PROBLEM:
Given some positions in different financial securities, I would like to apply trades to them up to a given date. In doing so I would like to keep track which trades closed the position and opened a new one. The position is CLOSED
when the Position Quantity becomes 0 after a trade is applied. A position is considered OPENED
when the Position Quantity changes from 0 to something.
SETUP: SQL DEMO SOURCE
Let's say I have the following tables:
CREATE TABLE tPosition
(
SecNum INT,
Position INT
)
CREATE TABLE tTrade
(
TradeID INT IDENTITY(1,1),
TradeDate DATETIME,
SecNum INT,
Quantity INT
)
And some sample data:
INSERT INTO tPosition (SecNum, Position)
SELECT 1, 100
UNION
SELECT 2, 200
UNION
SELECT 3, -300
INSERT INTO tTrade (TradeID, TradeDate, SecNum, Quantity)
SELECT 1, '1/1/2016', 1, -50
UNION
SELECT 2, '1/2/2016', 1, -50
UNION
SELECT 3, '1/3/2016', 1, -50
UNION
SELECT 4, '1/4/2016', 1, 50
UNION
SELECT 6, '1/5/2016', 3, 200
UNION
SELECT 7, '1/5/2016', 3, 200;
SAMPLE SCENARIOS/CASES: SQL DEMO SOURCE
Without any trades my result would be (i.e. exactly the position table with 2 extra fields which will be useful later):
SecNum, Position, OpenedByTradeID, ClosedByTradeID
1, 100, NULL, NULL
2, 200, NULL, NULL
3, -300, NULL, NULL
So let's say I apply trades to the positions up to and including 1/1/2016
. This trade with TradeID
:1, will affect the position for securityID
: 1 as 100+(-50)=50 so my result should be:
SecNum, Position, OpenedByTradeID, ClosedByTradeID
1, 50, NULL, NULL
2, 200, NULL, NULL
3, -300, NULL, NULL
The OpenedByTradeID
and ClosedTradeID
are still NULL
because the position hasn't crossed 0 yet.
If I apply trades up to and including 1/2/2016
I should get:
SecNum, Position, OpenedByTradeID, ClosedByTradeID
1, 0, NULL, 2
2, 200, NULL, NULL
3, -300, NULL, NULL
Notice that the position has become 0 so we record the ClosedByTradeID
with the tradeID
that closed this position.
Up to and including 1/3/2016
I should get:
SecNum, Position, OpenedByTradeID, ClosedByTradeID
1, 0, NULL, 2
1, -50, 3, NULL
2, 200, NULL, NULL
3, -300, NULL, NULL
Notice that a new position was opened in securityID
:1 by the latest tradeID
:3 so we mark the OpenedByTradeID
column with TradeID
:3
Up to and including 1/4/2016
I should get:
SecNum, Position, OpenedByTradeID, ClosedByTradeID
1, 0, NULL, 2
1, 0, 3, 4
2, 200, NULL, NULL
3, -300, NULL, NULL
Notice that the position has become 0 so we record the ClosedByTradeID
with the tradeID
that closed this position -- TradeID
:4
Up to and including 1/5/2016
is an edge case. There are 2 things happening here: the application of the trade crosses the 0 mark so a new position needs to be formed AND 2 trades happen on the same day.
Applying:
tradeID
:6 forsecurityID
:3 would adjust the position to -100 (-300 + 200 = -100).- Then we need to apply
tradeID
:7 but that would cross 0 (-100 + 200 = 100 crossing from neg -> pos) so we need to apply the part before crossing 0 to close the position and then start a new position with the remaining amount. - Part of
tradeID
:7 [100] would adjust the position to 0 (-100 + 100 = 0) and close it (would be noted inClosedByTradeID
column) and then part oftradeID
:7 [the remaining 100] would adjust the position to 100 (0 + 100 = 100) [would also get noted inOpenedByTradeID
column].
Therefore, I should get:
SecNum, Position, OpenedByTradeID, ClosedByTradeID
1, 0, NULL, 2
1, 0, 3, 4
2, 200, NULL, NULL
3, 0, NULL, 7
3, 100, 7, NULL
This sounds like an islands problem but I just can't seem to figure out how to write this one.