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 |