考虑到不同金融证券的一些头寸,我想在给定日期之前对他们进行交易。在这样做的过程中,我想跟踪哪些交易平仓并开启新的交易。在交易应用后,当头寸数量变为0时,头寸为CLOSED
。当“位置数量”从0更改为某个位置时,该位置将被视为OPENED
。
我们说我有以下表格:
CREATE TABLE tPosition
(
SecNum INT,
Position INT
)
CREATE TABLE tTrade
(
TradeID INT IDENTITY(1,1),
TradeDate DATETIME,
SecNum INT,
Quantity INT
)
以及一些示例数据:
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;
没有任何交易,我的结果就是(即具有2个额外字段的位置表,以后会有用):
SecNum, Position, OpenedByTradeID, ClosedByTradeID
1, 100, NULL, NULL
2, 200, NULL, NULL
3, -300, NULL, NULL
所以,让我们说我将交易应用到1/1/2016
之前的位置。此TradeID
:1的交易将影响securityID
:1的位置为100 +( - 50)= 50,因此我的结果应为:
SecNum, Position, OpenedByTradeID, ClosedByTradeID
1, 50, NULL, NULL
2, 200, NULL, NULL
3, -300, NULL, NULL
OpenedByTradeID
和ClosedTradeID
仍为NULL
,因为该位置尚未越过0。
如果我将交易应用到1/2/2016
,我应该得到:
SecNum, Position, OpenedByTradeID, ClosedByTradeID
1, 0, NULL, 2
2, 200, NULL, NULL
3, -300, NULL, NULL
请注意,该位置已变为0,因此我们会记录ClosedByTradeID
,并tradeID
关闭此位置。
最多并包括1/3/2016
我应该得到:
SecNum, Position, OpenedByTradeID, ClosedByTradeID
1, 0, NULL, 2
1, -50, 3, NULL
2, 200, NULL, NULL
3, -300, NULL, NULL
请注意,最新securityID
:tradeID
:1已在OpenedByTradeID
:1中打开了新位置,因此我们将TradeID
列标记为1/4/2016
:3
最多并包括 SecNum, Position, OpenedByTradeID, ClosedByTradeID
1, 0, NULL, 2
1, 0, 3, 4
2, 200, NULL, NULL
3, -300, NULL, NULL
我应该得到:
ClosedByTradeID
请注意,该位置已变为0,因此我们将tradeID
与关闭此位置的TradeID
进行记录 - 1/5/2016
:4
最多并包含tradeID
是边缘情况。这里发生了两件事:交易的应用程序越过0标记,因此需要形成新的头寸并且在同一天发生两笔交易。
申请:
securityID
:6 tradeID
:3会将位置调整为-100(-300 + 200 = -100)。 tradeID
:7但是它会越过0(-100 + 200 = 100从neg - > pos过渡)所以我们需要在越过0之前应用该部分来关闭位置然后以剩余金额开始新的头寸。 ClosedByTradeID
的一部分:7 [100]会将位置调整为0(-100 + 100 = 0)并关闭它(将在tradeID
列中注明)然后部分{{ 1}}:7 [剩下的100]将位置调整为100(0 + 100 = 100)[也会在OpenedByTradeID
列中注明]。 因此,我应该得到:
SecNum, Position, OpenedByTradeID, ClosedByTradeID
1, 0, NULL, 2
1, 0, 3, 4
2, 200, NULL, NULL
3, 0, NULL, 7
3, 100, 7, NULL
这听起来像岛屿问题,但我似乎无法弄清楚如何写这个。
答案 0 :(得分:1)
这只是使用递归的部分工作。我无法完成,但也许其他人可以有想法。
我开始只使用SecNum = 1
WITH DirectReports (SecNum, Position, OpenedByTradeID, ClosedByTradeID, level_id, TradeID)
AS (
SELECT SecNum, Position, NULL as OpenedByTradeID, NULL as ClosedByTradeID, 1, null as TradeID
FROM tPosition
WHERE SecNum = 1
UNION ALL
SELECT D.SecNum,
Position + Quantity as Position,
CASE WHEN Position = 0 and Position + Quantity <> 0
THEN T.TradeID
ELSE NULL
END as OpenedByTradeID,
CASE WHEN Position <> 0 and Position + Quantity = 0
THEN T.TradeID
ELSE NULL
END as ClosedByTradeID,
level_id + 1 as level_id,
T.TradeID
FROM DirectReports D
JOIN (SELECT *,
ROW_NUMBER() OVER (partition by SecNum ORDER BY TradeDate) as rn
FROM tTrade
) T
ON D.SecNum = T.SecNum
AND D.level_id = T.rn
)
SELECT *
FROM DirectReports
<强>输出强>
答案 1 :(得分:0)
我认为我使用WHILE
循环方法让它工作。也许有一种更有效的方式,但我认为这有效...
DECLARE @result TABLE
(
SecNum INT,
Position INT,
OpenedByTradeID INT,
ClosedByTradeID INT
)
INSERT INTO @result(SecNum, Position)
SELECT SecNum, Position
FROM dbo.tPosition
SELECT *
FROM @result
ORDER BY SecNum
DECLARE @CurTradeID INT
SELECT @CurTradeID = MIN(TradeID) FROM dbo.tTrade
WHILE(@CurTradeID IS NOT NULL)
BEGIN
DECLARE @TradeQty INT, @TradeSecNum INT
SELECT @TradeQty = Quantity, @TradeSecNum = SecNum FROM dbo.tTrade WHERE TradeID = @CurTradeID
DECLARE @OldPos INT = (SELECT Position FROM @result WHERE ClosedByTradeID IS NULL AND SecNum = @TradeSecNum)
-- IF THERE IS NO POSITION
IF (@OldPos IS NULL)
BEGIN
INSERT INTO @result(SecNum, Position, OpenedByTradeID, ClosedByTradeID)
SELECT @TradeSecNum, @TradeQty, @CurTradeID, NULL
END
-- IF THIS TRADE CLOSES THE POSITION
ELSE IF (@OldPos + @TradeQty = 0)
BEGIN
UPDATE @result
SET ClosedByTradeID = @CurTradeID, Position = Position + @TradeQty
WHERE SecNum = @TradeSecNum AND ClosedByTradeID IS NULL
END
-- IF THIS TRADE MAKES THE POSITION CROSS THROUGH 0 i.e. IF TRADE MAKES POSITION CHANGE SIGN
ELSE IF (SIGN(@OldPos + @TradeQty) <> SIGN(@OldPos))
BEGIN
DECLARE @RemainingAmt INT = @TradeQty + @OldPos
UPDATE @result
SET ClosedByTradeID = @CurTradeID, Position = 0
WHERE SecNum = @TradeSecNum AND ClosedByTradeID IS NULL
INSERT INTO @result(SecNum, Position, OpenedByTradeID, ClosedByTradeID)
SELECT @TradeSecNum, @RemainingAmt, @CurTradeID, NULL
END
-- JUST UPDATE THE ACTIVE POSITION
ELSE
BEGIN
UPDATE @result
SET Position = Position + @TradeQty
WHERE SecNum = @TradeSecNum AND ClosedByTradeID IS NULL
END
SELECT @CurTradeID = MIN(TradeID) FROM dbo.tTrade WHERE TradeID > @CurTradeID
END
SELECT *
FROM @result
ORDER BY SecNum
PS:我认为我可以用我之前需要的交易创建一个临时表,我可以在上面的查询中使用该表而不是tTrade所以我不必继续处理选择交易到特定日期。