使用sql生成所有已实现和未实现的交易?

时间:2016-12-01 21:06:46

标签: sql-server gaps-and-islands

问题:

考虑到不同金融证券的一些头寸,我想在给定日期之前对他们进行交易。在这样做的过程中,我想跟踪哪些交易平仓并开启新的交易。在交易应用后,当头寸数量变为0时,头寸为CLOSED。当“位置数量”从0更改为某个位置时,该位置将被视为OPENED

设置: SQL DEMO SOURCE

我们说我有以下表格:

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;

示例情景/案例: SQL DEMO SOURCE

没有任何交易,我的结果就是(即具有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

OpenedByTradeIDClosedTradeID仍为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

请注意,最新securityIDtradeID: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

这听起来像岛屿问题,但我似乎无法弄清楚如何写这个。

2 个答案:

答案 0 :(得分:1)

这只是使用递归的部分工作。我无法完成,但也许其他人可以有想法。

我开始只使用SecNum = 1

SQL DEMO

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

<强>输出

enter image description here

答案 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所以我不必继续处理选择交易到特定日期。