FIFO сопоставляет первые покупки акций с первыми продажами sql

У меня есть данные, которые выглядят так:

Покупка и продажа акций

Мне нужен запрос, чтобы применить метод FIFO к покупкам и продажам, поэтому я получаю таблицу, которая выглядит следующим образом: Покупка и продажа FIFO

Я хочу иметь возможность сопоставить первую покупку с первой продажей с покупками слева и продажами справа. Если продажи нет, то нули должны применяться справа, а если нет покупки, то нули должны применяться слева. Ключ брокерской транзакции можно использовать в качестве порядка совершения сделок. Это то, что я пробовал до сих пор. Любая помощь приветствуется!

SELECT  a.ACCT_ID, a.Trade_Date_Key, a.Brokerage_Transaction_Key, a.Buy_Sell_Code, a.Principal_Amt, a.Security_Quantity
     , (a.Security_Quantity + b.Security_Quantity) CUMULATIVE_POSITION
     , a.SHARE_PRICE
     , (A.Principal_Amt + B.Principal_Amt) CUMULATIVE_VALUE
from #TRANSACTIONS_WITH_RANK a
     left join #TRANSACTIONS_WITH_RANK b 
     on a.acct_id = b.acct_id and a.rank = b.rank + 1
ORDER BY BROKERAGE_TRANSACTION_KEY

person A.nn    schedule 13.02.2016    source источник


Ответы (1)


В своем вопросе вы упоминаете, что первая покупка (ы) соответствует первой продаже (ям), но ваш пример вывода, похоже, игнорирует эту часть. Вот пример того, как это сделать, если вы хотите сопоставить первую покупку(и) с первой продажей на основе Acct_ID и Trade_Date

SELECT buy.*, sell.*
FROM #TRANSACTIONS_WITH_RANK buy
    INNER JOIN (
        SELECT MIN(Trade_Date) Trade_Date
        FROM #TRANSACTIONS_WITH_RANK
        WHERE Buy_Sell_Code = 'B'
        GROUP BY Acct_ID
    ) TDateBuy
    ON buy.Trade_Date = TDateBuy.Trade_Date
    FULL OUTER JOIN #TRANSACTIONS_WITH_RANK sell
        INNER JOIN (
            SELECT MIN(Trade_Date) Trade_Date
            FROM #TRANSACTIONS_WITH_RANK
            WHERE Buy_Sell_Code = 'S'
            GROUP BY Acct_ID
        ) TDateSell
        ON sell.Trade_Date = TDateSell.Trade_Date
    ON buy.Acct_ID = sell.Acct_ID

EDIT: увидев комментарий OP, я изменил запрос

SELECT 
    buy.Acct_ID, buy.Trade_Date, buy.Brokerage_Transaction_Key, buy.Buy_Sell_Code, buy.Principal_Amt, buy.Security_Quantity, 
    sell.Acct_ID, sell.Trade_Date, sell.Brokerage_Transaction_Key, sell.Buy_Sell_Code, sell.Principal_Amt, sell.Security_Quantity
FROM (
        SELECT wr.*, MIN(TransKey) TransKey -- This is the value of the Sell to be joined
        FROM #TRANSACTIONS_WITH_RANK wr
            LEFT OUTER JOIN (
                SELECT MIN(Brokerage_Transaction_Key) TransKey, Acct_ID
                FROM (
                        SELECT 
                            tr.*, 
                            (
                                SELECT MAX(Brokerage_Transaction_Key) --Purpose is to give outer query value to GROUP on
                                FROM #TRANSACTIONS_WITH_RANK 
                                WHERE Buy_Sell_Code = 'B' 
                                    AND Acct_ID = tr.Acct_ID
                                    AND Brokerage_Transaction_Key < tr.Brokerage_Transaction_Key
                            ) MaxLesserKey
                        FROM #TRANSACTIONS_WITH_RANK tr
                    ) data
                WHERE Buy_Sell_Code = 'S'
                GROUP BY Acct_ID, MaxLesserKey
            ) MinSell
            ON wr.Acct_ID = MinSell.Acct_ID
                AND wr.Brokerage_Transaction_Key < MinSell.TransKey
        WHERE Buy_Sell_Code = 'B'
        GROUP BY wr.Acct_ID, Trade_Date, Brokerage_Transaction_Key, Buy_Sell_Code, Principal_Amt, Security_Quantity
    ) buy
    FULL OUTER JOIN (
        SELECT wr.*, MIN(MinBuy.TransKey) TransKey -- This is the value of the Buy to be joined
        FROM #TRANSACTIONS_WITH_RANK wr
            LEFT OUTER JOIN (
                SELECT MIN(Brokerage_Transaction_Key) TransKey, Acct_ID
                FROM (
                        SELECT 
                            tr.*,
                            (
                                SELECT MAX(Brokerage_Transaction_Key) --Purpose is to give outer query a value to GROUP on
                                FROM #TRANSACTIONS_WITH_RANK
                                WHERE Buy_Sell_Code = 'S'
                                    AND Brokerage_Transaction_Key < tr.Brokerage_Transaction_Key
                            ) MaxLesserKey
                        FROM #TRANSACTIONS_WITH_RANK tr
                    ) data
                WHERE Buy_Sell_Code = 'B'
                GROUP BY Acct_ID, MaxLesserKey
            ) MinBuy
            ON wr.Acct_ID = MinBuy.Acct_ID
                AND wr.Brokerage_Transaction_Key < MinBuy.TransKey
        WHERE Buy_Sell_Code = 'S'
        GROUP BY wr.Acct_ID, Trade_Date, Brokerage_Transaction_Key, Buy_Sell_Code, Principal_Amt, Security_Quantity
    ) sell
    ON buy.TransKey = sell.Brokerage_Transaction_Key
        OR sell.TransKey = buy.Brokerage_Transaction_Key

По сути, это захватывает все покупки и их соответствующие продажи Brokerage_Transaction_Key (TransKey) и выполняет FULL OUTER JOIN (NULLs вне стороны покупки или продажи, когда нет противоположных совпадающих транзакций) к набору продаж и их соответствия Купить Brokerage_Transaction_Key (TransKey). TransKey — это наименьшее Brokerage_Transaction_Key из противоположных Buy_Sell_Code для каждой группы покупок/продаж. Это даст вам первую продажу для первой покупки или первую покупку для первой продажи для группы транзакций для определенного Acct_ID. Поле MaxLesserKey предназначено только для того, чтобы присвоить запросу TransKey значение GROUP на

person Kidiskidvogingogin    schedule 13.02.2016
comment
Извините, я должен был быть более ясным, Brokerage_transaction_key — это уникальное поле, которое указывает порядок сделки. Таким образом, его необходимо использовать, чтобы указать, какая покупка предшествовала какой продаже в каждом счете. - person A.nn; 13.02.2016
comment
Теперь я понимаю. Это только для разыскиваемого за первый набор покупки/продажи? т. е. у учетной записи есть 3 покупки, совпадающие с одной продажей (назовем это set1), а позже у него есть еще 2 покупки, прежде чем они продадут (назовем это set2). Должен ли set2 отображаться в данных как еще один набор покупок/продаж или он должен показывать только set1? - person Kidiskidvogingogin; 14.02.2016
comment
Вы правы, это должно быть установлено 2. Количество обеспечения на стороне покупки должно соответствовать количеству обеспечения на стороне продажи, если это имеет смысл. - person A.nn; 14.02.2016
comment
Чтобы уточнить, должен ли это быть только set2 или оба набора? - person Kidiskidvogingogin; 14.02.2016
comment
Оба набора. Таким образом, если первые 3 покупки составляют 100, 200, 100, а первая продажа — 400, это будет set1, а если следующая покупка — 100, а следующие две продажи — 50 и 50, это будет set2. - person A.nn; 14.02.2016
comment
Я отредактировал свой ответ. Поле MaxLesserKey предназначено для сопоставления ключей покупки/продажи с их фактическим набором покупок/продаж, а не со всеми наборами покупки/продажи для конкретного Acct_ID. - person Kidiskidvogingogin; 14.02.2016