Realized P&L on the FIFO basis#

Realized P&L is computed by keeping track of each buy and sell in the chronological order and updating the total using the oldest matching execution from the opposite side (FIFO). We’ll use the following sequence of trades for illustration.

import onetick.py as otp

trades = otp.Ticks(
    SIDE=['B', 'B', 'B', 'S', 'S', 'S', 'S', 'S', 'B', 'B', 'S'],
    PRICE=[1.0, 2.0, 3.0, 2.5, 4.0, 5.0, 6.0, 7.0, 3.0, 4.0, 1.0],
    SIZE=[700, 20, 570, 600, 100, 100, 100, 100, 150, 10, 100],
)

otp.run(trades)
Time SIDE PRICE SIZE
0 2003-12-01 00:00:00.000 B 1.0 700
1 2003-12-01 00:00:00.001 B 2.0 20
2 2003-12-01 00:00:00.002 B 3.0 570
3 2003-12-01 00:00:00.003 S 2.5 600
4 2003-12-01 00:00:00.004 S 4.0 100
5 2003-12-01 00:00:00.005 S 5.0 100
6 2003-12-01 00:00:00.006 S 6.0 100
7 2003-12-01 00:00:00.007 S 7.0 100
8 2003-12-01 00:00:00.008 B 3.0 150
9 2003-12-01 00:00:00.009 B 4.0 10
10 2003-12-01 00:00:00.010 S 1.0 100

We define the deque variables to keep the buy and sell trades as they arrive. Note that the variables are associated with the trades time series.

trades.state_vars['BUY_DEQUE'] = otp.state.tick_deque()
trades.state_vars['SELL_DEQUE'] = otp.state.tick_deque()

As every trade arrives, we apply the following function, which updates the deques and computes the realized profit from the trade.

def fifo_computation_of_realized_profit(tick):
    buy_tick = otp.tick_deque_tick()
    sell_tick = otp.tick_deque_tick()
    tick['PROFIT'] = 0.0

    if tick['SIDE'] == 'B':
        tick.state_vars['BUY_DEQUE'].push_back(tick)
    else:
        tick.state_vars['SELL_DEQUE'].push_back(tick)

    while tick.state_vars['BUY_DEQUE'].get_size() > 0 and tick.state_vars['SELL_DEQUE'].get_size() > 0:
        tick.state_vars['BUY_DEQUE'].get_tick(0, buy_tick)
        tick.state_vars['SELL_DEQUE'].get_tick(0, sell_tick)
        if buy_tick['SIZE'] > sell_tick['SIZE']:
            tick['PROFIT'] += sell_tick['SIZE'] * (sell_tick['PRICE'] - buy_tick['PRICE'])
            buy_tick['SIZE'] -= sell_tick['SIZE']
            sell_tick['SIZE'] = 0
        else:
            tick['PROFIT'] += buy_tick['SIZE'] * (sell_tick['PRICE'] - buy_tick['PRICE'])
            sell_tick['SIZE'] -= buy_tick['SIZE']
            buy_tick['SIZE'] = 0

        if buy_tick['SIZE'] == 0:
            tick.state_vars['BUY_DEQUE'].pop_front()
        if sell_tick['SIZE'] == 0:
            tick.state_vars['SELL_DEQUE'].pop_front()

Total realized profit can now be calculated by applying the function to every trade.

trades = trades.script(fifo_computation_of_realized_profit)
trades = trades.agg({'TOTAL_PROFIT': otp.agg.sum('PROFIT')}, running=True, all_fields=True)
otp.run(trades)
Time SIDE PRICE SIZE PROFIT TOTAL_PROFIT
0 2003-12-01 00:00:00.000 B 1.0 700 0.0 0.0
1 2003-12-01 00:00:00.001 B 2.0 20 0.0 0.0
2 2003-12-01 00:00:00.002 B 3.0 570 0.0 0.0
3 2003-12-01 00:00:00.003 S 2.5 600 900.0 900.0
4 2003-12-01 00:00:00.004 S 4.0 100 300.0 1200.0
5 2003-12-01 00:00:00.005 S 5.0 100 220.0 1420.0
6 2003-12-01 00:00:00.006 S 6.0 100 300.0 1720.0
7 2003-12-01 00:00:00.007 S 7.0 100 400.0 2120.0
8 2003-12-01 00:00:00.008 B 3.0 150 0.0 2120.0
9 2003-12-01 00:00:00.009 B 4.0 10 0.0 2120.0
10 2003-12-01 00:00:00.010 S 1.0 100 -200.0 1920.0