Sample Header Ad - 728x90

LIFO with stock splits

4 votes
0 answers
220 views
## Background I am managing a database containing my users' trading of financial products, stocks bonds, ETFs etc. The system offers simple bookkeeping functionality (create portfolio, create instrument, book a trade, book a cash flow ...) for my users, and some bulk features for the data admin user. The system keeps track of trade / position performance ("pnl", profit-and-loss) and values sell-trades using the FIFO method, i.e. outgoing stock is valued at 'oldest' prices. ## Problem statement Thru so-called corporate actions, a company manipulates their quantity of outstanding stock. An N-for-1 stock split, for example, results in N times the number of stock outstanding (at a price of 1/N of the pre-Split price), and the same holds for the number of stock in a given portfolio of a user, e.g. their quantities double during at a 2-for-1-split. Thus, the meaning of quantity may change over time, complicating the FIFO method in my database. I want to reconcile the ability to value a sale trade using the **FIFO method** with the concept of stock splits. ## Constraints - hard constraint: Historical entries must be recoverable 'as is': Users want to see their historical trades' units as of that date. - soft constraint: Data changes should be as local as possible, and be reversible: There should be no additional effect of adjusting for the 'same' stock split twice. This is very a small project with <10 users and no interaction between the users' trades, concurrency is not an issue, I guess. ## Status Quo For simplicity, I have dropped some entities such as portfolio, instrument etc. below. In order to be able to calculate the FIFO value for the position in a financial asset (think: stocks), my system contains a table called
that holds the history of each trade with trade quantity qty, unit price prcand a field qty_allotable that tells me how many units of each *buy* trade have not yet been sold off (i.e. not yet **allotted** to a sale). The relevant fields for my problem are:
lang-sql
CREATE TABLE db.trade (
   ,valuedate       DATE                    NOT NULL
   ,qty             DECIMAL(16,6)           NOT NULL
   ,prc             DECIMAL(12,6) UNSIGNED  NOT NULL
  ,qty_allotable    DECIMAL(16,6) UNSIGNED  NOT NULL
);
For example, let's buy 6 units of some stock on one day and another 4 units the next day (via some stored procedure not depicted here) valuedate qty prc qty_allotable 2021-08-02 6 10.00 6 2021-08-03 4 11.00 4 On day 3, we sell 7 units. Via FIFO, we value the outgoing units at 6 x 10 + 1 x 11 = 71.00 where the FIFO algorithm is implemented in some function, and we **deplete** the allotable quantity after the sale: valuedate qty prc qty_allotable 2021-08-02 5 10.00 0 2021-08-03 5 11.00 3 2021-08-04 -7 12.00 0 -- a sale is not allotable itself where the table update algorithm is performed by some stored procedure. At each point in time, the total invested volume is a simple SELECT(prc * qty_allotable) FROM .... NB.: The setup requires trades to be entered (truly) in sequential order. If a user 'forgets' to enter a historical trade, I rebuild the whole trade table for the affected combination of user and instrument... ## Increased complexity due to stock splits My question now is how to properly treat events that change the significance of a stock's quantity, e.g. a stock split. To make it tangible, say there's a 2-for-1 stock split on 2021-08-05, i.e. my remaining three units become six units (and the trading price halves), i.e. my **position** now is qty unit_prc 6 5.50 -- doubling the total qty, halving the prices. ## Perceived solutions How should I incorporate this effect in the database? I think that I have identified a couple of options to follow, #### 1. Fire and forget WITH SYSTEM VERSIONING Introduce versioning to the trade table and simply update the **historical** prc and qty_allotable fields by the split factor. Although I can retrieve original trades with this setup, I cannot 'roll back' to an earlier state without risking loss of trade data entered in the mean time. #### 2. Introduce a table corporate_actions and adjusted business logic. By introducing a table with an appropriate uniqueness index
lang-sql
CREATE TABLE db.corporate_action(
   ,valuedate       DATE                    NOT NULL
   ,split_factor    DECIMAL(12,6) UNSIGNED  NOT NULL
);
CREATE UNIQUE INDEX idx_ca_unq ON db.corporate_action(value_date);
In our example, the table could look like this: valuedate split_factor 1970-01-01 1.000000 -- auto entry per instrument 2021-08-05 2.000000 2099-12-31 1.000000 -- auto entry per instrument This way, I make sure that a 'double entry' cannot take place (thru the uniqueness index). In order to apply the FIFO method, I must adjust the historical quantities and prices through some view on the corporate_actions table, e.g.
lang-sql
SELECT 
     c1.valuedate
    ,EXP(-SUM(LOG(c2.split_factor))) as compound_factor
FROM       corporate_action c1 
INNER JOIN corporate_action c2 
        ON c1.valuedate <= c2.valuedate 
GROUP BY   c1.valuedate;
resulting in valuedate compound_factor 1970-01-01 0.5 2021-08-05 0.5 2099-12-31 1 Then, when calculating the FIFO price, I'd calculate prc*compound_factor and qty/compound_factor in order to properly calculate the outgoing value in a sell trade. ## Question Do you think that either of the two perceived solutions is sensible, or can you propose another solution?
Asked by Kermittfrog (141 rep)
Aug 28, 2021, 12:12 PM
Last activity: Aug 28, 2021, 06:11 PM