Use Cases#

import onetick.py as otp

Retrieving Tick Data#

s = otp.dt(2023, 5, 15, 9, 30)
e = otp.dt(2023, 5, 15, 9, 30, 1)
trd = otp.DataSource('NYSE_TAQ', tick_type='TRD')
otp.run(trd, start=s, end=e, symbols=['SPY'])
Time EXCHANGE COND STOP_STOCK SOURCE TRF TTE TICKER PRICE DELETED_TIME TICK_STATUS SIZE CORR SEQ_NUM TRADE_ID PARTICIPANT_TIME TRF_TIME OMDSEQ
0 2023-05-15 09:30:00.000178688 P T N C 0 SPY 412.22 1969-12-31 19:00:00 0 100 0 39195 52983525167153 2023-05-15 09:30:00.000150016 1969-12-31 19:00:00.000000000 0
1 2023-05-15 09:30:00.000776704 Z N C 0 SPY 412.22 1969-12-31 19:00:00 0 247 0 39196 52983525035355 2023-05-15 09:30:00.000450000 1969-12-31 19:00:00.000000000 1
2 2023-05-15 09:30:00.003603456 T T N C 0 SPY 412.22 1969-12-31 19:00:00 0 100 0 39212 62879133157950 2023-05-15 09:30:00.003165122 1969-12-31 19:00:00.000000000 0
3 2023-05-15 09:30:00.006352128 K I N C 0 SPY 412.24 1969-12-31 19:00:00 0 1 0 39227 52983525098301 2023-05-15 09:30:00.006091000 1969-12-31 19:00:00.000000000 0
4 2023-05-15 09:30:00.007128064 K I N C 0 SPY 412.24 1969-12-31 19:00:00 0 3 0 39231 52983525098302 2023-05-15 09:30:00.006873000 1969-12-31 19:00:00.000000000 0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
310 2023-05-15 09:30:00.934032640 T T N C 0 SPY 412.27 1969-12-31 19:00:00 0 160 0 40517 62879133545538 2023-05-15 09:30:00.933678033 1969-12-31 19:00:00.000000000 1
311 2023-05-15 09:30:00.975609344 D I N C T 0 SPY 412.24 1969-12-31 19:00:00 0 2 0 40543 71675240595789 2023-05-15 09:30:00.661000000 2023-05-15 09:30:00.975241514 0
312 2023-05-15 09:30:00.980264448 D I N C T 0 SPY 412.27 1969-12-31 19:00:00 0 1 0 40545 71675240596294 2023-05-15 09:30:00.553501000 2023-05-15 09:30:00.979895700 0
313 2023-05-15 09:30:00.985391616 T N C 0 SPY 412.28 1969-12-31 19:00:00 0 100 0 40547 62879133552717 2023-05-15 09:30:00.984946104 1969-12-31 19:00:00.000000000 0
314 2023-05-15 09:30:00.985394944 T Q N C 0 SPY 412.28 1969-12-31 19:00:00 0 100 0 40549 62879133552719 2023-05-15 09:30:00.984958312 1969-12-31 19:00:00.000000000 1

315 rows × 18 columns

Creating Bars#

We create 1-minute bars (bucket_interval=60 seconds) below.

trd = otp.DataSource('NYSE_TAQ', tick_type='TRD')
trd, _ = trd[trd['COND'].str.match('^[^O6TUHILNRWZ47QMBCGPV]*$')]
bars = trd.agg({'VOLUME': otp.agg.sum('SIZE'),
                'HIGH': otp.agg.max('PRICE'),
                'LOW': otp.agg.min('PRICE'),
                'OPEN': otp.agg.first('PRICE'),
                'COUNT': otp.agg.count(),
                'CLOSE': otp.agg.last('PRICE')},
               bucket_interval=60)
otp.run(bars, start=otp.dt(2023, 5, 15, 9, 30), end=otp.dt(2023, 5, 15, 10), symbols=['SPY'])
Time VOLUME HIGH LOW OPEN COUNT CLOSE
0 2023-05-15 09:31:00 264719 412.2900 412.0350 412.2200 1446 412.0400
1 2023-05-15 09:32:00 218724 412.2600 412.0000 412.0500 1537 412.1600
2 2023-05-15 09:33:00 271364 412.2000 411.9400 412.1650 1649 412.0200
3 2023-05-15 09:34:00 312764 412.0200 411.6312 412.0100 1827 411.8200
4 2023-05-15 09:35:00 252569 411.8300 411.3600 411.8100 1241 411.3983
5 2023-05-15 09:36:00 202980 411.6250 411.3200 411.3900 1286 411.6250
6 2023-05-15 09:37:00 114774 411.6800 411.4400 411.6200 841 411.5900
7 2023-05-15 09:38:00 152180 411.6000 411.4100 411.5800 927 411.4300
8 2023-05-15 09:39:00 96940 411.5100 411.3600 411.4300 592 411.3600
9 2023-05-15 09:40:00 129613 411.4650 411.2300 411.3550 884 411.4471
10 2023-05-15 09:41:00 111231 411.6668 411.3600 411.4550 723 411.6300
11 2023-05-15 09:42:00 214502 411.7100 411.4300 411.6400 1395 411.4750
12 2023-05-15 09:43:00 262713 411.6600 411.4200 411.4700 1379 411.4700
13 2023-05-15 09:44:00 106379 411.5200 411.3520 411.4700 654 411.4400
14 2023-05-15 09:45:00 103743 411.5250 411.4000 411.4200 551 411.5016
15 2023-05-15 09:46:00 137092 411.6600 411.5200 411.5200 871 411.6300
16 2023-05-15 09:47:00 189016 411.6900 411.3600 411.6300 1201 411.3900
17 2023-05-15 09:48:00 121331 411.4780 411.3100 411.3900 612 411.3150
18 2023-05-15 09:49:00 108976 411.3900 411.1600 411.3200 664 411.2900
19 2023-05-15 09:50:00 112394 411.3000 411.1200 411.3000 637 411.1900
20 2023-05-15 09:51:00 94153 411.2700 411.0300 411.1900 469 411.0300
21 2023-05-15 09:52:00 112302 411.1900 410.9800 411.0300 687 410.9900
22 2023-05-15 09:53:00 114025 411.1179 410.9700 410.9850 599 410.9900
23 2023-05-15 09:54:00 80763 411.1299 410.9500 410.9850 458 411.0150
24 2023-05-15 09:55:00 66556 411.1099 411.0100 411.0190 435 411.0350
25 2023-05-15 09:56:00 131143 411.1100 410.9100 411.0350 790 410.9205
26 2023-05-15 09:57:00 128274 410.9300 410.8200 410.9205 679 410.8400
27 2023-05-15 09:58:00 162243 410.9500 410.8000 410.8200 953 410.8900
28 2023-05-15 09:59:00 105649 410.9300 410.8300 410.8800 572 410.8700
29 2023-05-15 10:00:00 145019 410.9100 410.7800 410.8600 911 410.8400
Note: OneTick Cloud has minute bars precomputed and available in *_BARS databases under the tick type TRD_1M.

Daily OHLCV data with the official closing prices is also available: see OHLCV.

Note the use of apply_times_daily to limit each day’s interval to 9:30-4:00pm (plus one minute is added as the minute bar for 9:30-9:31 has the timestamp of 9:31).

bars = otp.DataSource('NYSE_TAQ_BARS', tick_type='TRD_1M')
bars = bars[['FIRST', 'HIGH', 'LOW', 'LAST', 'VOLUME']]
otp.run(bars, start=otp.dt(2023, 5, 15, 9, 31), end=otp.dt(2023, 5, 19, 16, 1), symbols=['SPY'], apply_times_daily=True)
Time FIRST HIGH LOW LAST VOLUME
0 2023-05-15 09:31:00 412.220 412.290 412.0350 412.0400 264719
1 2023-05-15 09:32:00 412.050 412.260 412.0000 412.1600 218724
2 2023-05-15 09:33:00 412.165 412.200 411.9400 412.0200 271364
3 2023-05-15 09:34:00 412.010 412.020 411.6312 411.8200 312764
4 2023-05-15 09:35:00 411.810 411.830 411.3600 411.3983 252569
... ... ... ... ... ... ...
1945 2023-05-19 15:56:00 418.890 418.950 418.8501 418.9200 465446
1946 2023-05-19 15:57:00 418.910 418.915 418.7200 418.8700 618549
1947 2023-05-19 15:58:00 418.870 418.980 418.8400 418.9500 601161
1948 2023-05-19 15:59:00 418.950 418.990 418.9000 418.9600 711607
1949 2023-05-19 16:00:00 418.960 418.970 418.5700 418.6100 1565989

1950 rows × 6 columns

Prevailing quote at the time of a trade#

trd = otp.DataSource('NYSE_TAQ', tick_type='TRD')
trd = trd[['PRICE', 'SIZE']]

qte = otp.DataSource('TAQ_NBBO', tick_type='NBBO', back_to_first_tick=600)
qte = qte[['ASK_PRICE', 'BID_PRICE']]
qte['quote_time'] = qte['Time']

enriched_trades = otp.join_by_time([trd, qte])

otp.run(enriched_trades, start=s, end=e, symbols=['SPY'])
Time PRICE SIZE ASK_PRICE BID_PRICE quote_time
0 2023-05-15 09:30:00.000178688 412.22 100 412.25 412.22 2023-05-15 09:30:00.000174080
1 2023-05-15 09:30:00.000776704 412.22 247 412.24 412.21 2023-05-15 09:30:00.000715520
2 2023-05-15 09:30:00.003603456 412.22 100 412.24 412.22 2023-05-15 09:30:00.003562496
3 2023-05-15 09:30:00.006352128 412.24 1 412.25 412.22 2023-05-15 09:30:00.006343936
4 2023-05-15 09:30:00.007128064 412.24 3 412.25 412.22 2023-05-15 09:30:00.007110656
... ... ... ... ... ... ...
310 2023-05-15 09:30:00.934032640 412.27 160 412.28 412.26 2023-05-15 09:30:00.934030080
311 2023-05-15 09:30:00.975609344 412.24 2 412.28 412.27 2023-05-15 09:30:00.970691840
312 2023-05-15 09:30:00.980264448 412.27 1 412.28 412.27 2023-05-15 09:30:00.979763456
313 2023-05-15 09:30:00.985391616 412.28 100 412.28 412.27 2023-05-15 09:30:00.985296640
314 2023-05-15 09:30:00.985394944 412.28 100 412.28 412.27 2023-05-15 09:30:00.985296640

315 rows × 6 columns

Point-in-time benchmarks: BBO at different markouts#

Now let’s find the prevailing quote at different time intervals (markouts) before/after each trade.

markouts = [-1, 0, 1, 5, 60, 600] 

trd = otp.DataSource('NYSE_TAQ', tick_type='TRD')
trd = trd[['PRICE', 'SIZE']]

qte_by_markout = []
for m in markouts:
    mr = str(m).replace('-', 'm')
    qte = otp.DataSource('TAQ_NBBO', tick_type='NBBO', back_to_first_tick=86400)
    qte = qte[['ASK_PRICE', 'BID_PRICE']]
    qte = qte.rename({'ASK_PRICE': f'ASK_PRICE_{mr}',
                      'BID_PRICE': f'BID_PRICE_{mr}'})
    qte[f'quote_time_{mr}'] = qte['Time']

    # shift the data by m seconds
    qte = qte.time_interval_shift(m * 1000)
    qte_by_markout.append(qte)

trd = otp.join_by_time([trd] + qte_by_markout)
otp.run(trd, start=s, end=e, symbols=['SPY'], apply_times_daily=True)
Time PRICE SIZE ASK_PRICE_m1 BID_PRICE_m1 quote_time_m1 ASK_PRICE_0 BID_PRICE_0 quote_time_0 ASK_PRICE_1 BID_PRICE_1 quote_time_1 ASK_PRICE_5 BID_PRICE_5 quote_time_5 ASK_PRICE_60 BID_PRICE_60 quote_time_60 ASK_PRICE_600 BID_PRICE_600 quote_time_600
0 2023-05-15 09:30:00.000178688 412.22 100 412.26 412.23 2023-05-15 09:29:59.000000000 412.25 412.22 2023-05-15 09:30:00.000174080 412.28 412.27 2023-05-15 09:30:01.000000000 412.23 412.21 2023-05-15 09:30:05.000000000 412.06 412.05 2023-05-15 09:31:00.000105984 411.45 411.44 2023-05-15 09:40:00.000000000
1 2023-05-15 09:30:00.000776704 412.22 247 412.26 412.23 2023-05-15 09:29:59.000000000 412.24 412.21 2023-05-15 09:30:00.000715520 412.28 412.27 2023-05-15 09:30:01.000000000 412.23 412.21 2023-05-15 09:30:05.000000000 412.06 412.05 2023-05-15 09:31:00.000772608 411.45 411.44 2023-05-15 09:40:00.000000000
2 2023-05-15 09:30:00.003603456 412.22 100 412.26 412.23 2023-05-15 09:29:59.000000000 412.24 412.22 2023-05-15 09:30:00.003562496 412.28 412.27 2023-05-15 09:30:01.002818816 412.23 412.21 2023-05-15 09:30:05.000000000 412.06 412.05 2023-05-15 09:31:00.001634816 411.45 411.44 2023-05-15 09:40:00.000000000
3 2023-05-15 09:30:00.006352128 412.24 1 412.26 412.23 2023-05-15 09:29:59.005259520 412.25 412.22 2023-05-15 09:30:00.006343936 412.28 412.27 2023-05-15 09:30:01.004251904 412.23 412.21 2023-05-15 09:30:05.000000000 412.06 412.05 2023-05-15 09:31:00.005624320 411.45 411.44 2023-05-15 09:40:00.006328576
4 2023-05-15 09:30:00.007128064 412.24 3 412.26 412.23 2023-05-15 09:29:59.007053824 412.25 412.22 2023-05-15 09:30:00.007110656 412.28 412.27 2023-05-15 09:30:01.004251904 412.23 412.21 2023-05-15 09:30:05.000000000 412.05 412.04 2023-05-15 09:31:00.007117824 411.45 411.44 2023-05-15 09:40:00.007125760
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
310 2023-05-15 09:30:00.934032640 412.27 160 412.26 412.22 2023-05-15 09:29:59.837682688 412.28 412.26 2023-05-15 09:30:00.934030080 412.28 412.26 2023-05-15 09:30:01.931170560 412.25 412.23 2023-05-15 09:30:05.898093824 412.07 412.06 2023-05-15 09:31:00.927816448 411.44 411.43 2023-05-15 09:40:00.928655104
311 2023-05-15 09:30:00.975609344 412.24 2 412.25 412.22 2023-05-15 09:29:59.970543872 412.28 412.27 2023-05-15 09:30:00.970691840 412.29 412.26 2023-05-15 09:30:01.972468480 412.25 412.24 2023-05-15 09:30:05.973397760 412.07 412.06 2023-05-15 09:31:00.972016640 411.44 411.43 2023-05-15 09:40:00.954621952
312 2023-05-15 09:30:00.980264448 412.27 1 412.25 412.22 2023-05-15 09:29:59.970543872 412.28 412.27 2023-05-15 09:30:00.979763456 412.29 412.27 2023-05-15 09:30:01.978472704 412.26 412.24 2023-05-15 09:30:05.978229504 412.07 412.06 2023-05-15 09:31:00.972016640 411.44 411.43 2023-05-15 09:40:00.954621952
313 2023-05-15 09:30:00.985391616 412.28 100 412.25 412.22 2023-05-15 09:29:59.970543872 412.28 412.27 2023-05-15 09:30:00.985296640 412.29 412.27 2023-05-15 09:30:01.985324032 412.26 412.24 2023-05-15 09:30:05.978229504 412.07 412.06 2023-05-15 09:31:00.972016640 411.44 411.43 2023-05-15 09:40:00.954621952
314 2023-05-15 09:30:00.985394944 412.28 100 412.25 412.22 2023-05-15 09:29:59.970543872 412.28 412.27 2023-05-15 09:30:00.985296640 412.29 412.27 2023-05-15 09:30:01.985324032 412.26 412.24 2023-05-15 09:30:05.978229504 412.07 412.06 2023-05-15 09:31:00.972016640 411.44 411.43 2023-05-15 09:40:00.954621952

315 rows × 21 columns

Interval Metrics (e.g., VWAP)#

q = otp.DataSource('NYSE_TAQ', tick_type='TRD')
q = q.agg({'market_vwap': otp.agg.vwap('PRICE', 'SIZE')})
otp.run(q, start=s, end=e, symbols=['SPY'])
Time market_vwap
0 2023-05-15 09:30:01 412.212012

Computing market VWAP for every order’s arrival/exit interval#

orders = otp.Ticks(arrival=[s, s + otp.Milli(7934)],
                   exit=[e, e + otp.Milli(2556)],
                   sym=['SPY', 'QQQ'])
otp.run(orders, start=s, end=s + otp.Day(1))
Time arrival exit sym
0 2023-05-15 09:30:00.000 2023-05-15 09:30:00.000 2023-05-15 09:30:01.000 SPY
1 2023-05-15 09:30:00.001 2023-05-15 09:30:07.934 2023-05-15 09:30:03.556 QQQ
def vwap(symbol):
    q = otp.DataSource('NYSE_TAQ', tick_type='TRD')
    q = q.agg({'market_vwap': otp.agg.vwap('PRICE','SIZE')})
    return q

orders = otp.Ticks(arrival=[s, s + otp.Milli(7934)],
                   exit=[e, e + otp.Milli(9556)],
                   sym=['SPY', 'QQQ'])
orders = orders.join_with_query(vwap, start=orders['arrival'], end=orders['exit'], symbol=orders['sym'])
otp.run(orders, start=s, end=s + otp.Day(1))
Time market_vwap arrival exit sym
0 2023-05-15 09:30:00.000 412.212012 2023-05-15 09:30:00.000 2023-05-15 09:30:01.000 SPY
1 2023-05-15 09:30:00.001 325.318988 2023-05-15 09:30:07.934 2023-05-15 09:30:10.556 QQQ

A more efficient implementation is also available with symbol parameters.

Real-time processing: Signal Generation#

We’ll compute golden cross signals using 50-second and 200-second moving averages

  • ‘Entries’ is set to 1 when the short-term moving average goes above the long term (i.e., a signal to buy)

  • ‘Exits’ is set to 1 on when the short-term moving average goes below the long term (i.e., a signal to sell)

trd = otp.DataSource('NYSE_TAQ', tick_type='TRD')
trd = trd[['PRICE']]

trd = trd.agg({'short': otp.agg.mean('PRICE')}, bucket_interval=60, running=True, all_fields=True)
trd = trd.agg({'long': otp.agg.mean('PRICE')}, bucket_interval=60 * 5, running=True, all_fields=True)

trd['buy'] = (trd['short'][-1] < trd['long'][-1]) & (trd['short'] > trd['long']) 
trd['sell'] = (trd['short'][-1] > trd['long'][-1]) & (trd['short'] < trd['long']) 

We define a callback that for every tick (i.e., on every trade) will

  • print a ‘.’ if there is no signal

  • print out the tick followed by ‘BUY’ on an entry signal

  • print out the tick followed by ‘SELL’ on an exit signal

class GoldenCrossCallback(otp.CallbackBase):
    def process_tick(self, tick, time):
        if not tick['buy'] and not tick['sell']:
            print('.', end='')
            return
        print()
        print()
        print(time, tick)
        if tick['buy']:
            print('BUY')
        if tick['sell']:
            print('SELL')
        print()

The query will run continuously with the output printed as the events happen if you set start/end times accordingly (see the commented out line).

# timestamps appear in GMT
cb = GoldenCrossCallback()
otp.run(trd, symbols=['SPY'],
        callback=cb, running=True,
        # start=otp.dt.now(), end=otp.dt.now() + otp.Day(1),
        start=otp.dt(2023, 3, 31, 10), end=otp.dt(2023, 3, 31, 10, 5),
)
...............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

2023-03-31 14:02:16.717255 {'PRICE': 405.54, 'short': 405.70794973568997, 'long': 405.70836495181135, 'buy': 0.0, 'sell': 1.0}
SELL

.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

Upticks / Downticks#

Let’s mark each trade as an uptick if its price is above the last trade’s price and as a downtick if it’s below.

def uptick(t):
    if t['PRICE'] == otp.nan or t['PRICE'][-1] == otp.nan:
        return otp.nan
    if t['PRICE'] > t['PRICE'][-1]:
        return 1
    elif t['PRICE'] < t['PRICE'][-1]:
        return -1
    else:
        return 0

trd = otp.DataSource('NYSE_TAQ', tick_type='TRD')
trd = trd[['PRICE']]
trd['UPTICK'] = trd.apply(uptick)
otp.run(trd, start=s, end=e, symbols=['SPY'])
Time PRICE UPTICK
0 2023-05-15 09:30:00.000178688 412.22 NaN
1 2023-05-15 09:30:00.000776704 412.22 0.0
2 2023-05-15 09:30:00.003603456 412.22 0.0
3 2023-05-15 09:30:00.006352128 412.24 1.0
4 2023-05-15 09:30:00.007128064 412.24 0.0
... ... ... ...
310 2023-05-15 09:30:00.934032640 412.27 0.0
311 2023-05-15 09:30:00.975609344 412.24 -1.0
312 2023-05-15 09:30:00.980264448 412.27 1.0
313 2023-05-15 09:30:00.985391616 412.28 1.0
314 2023-05-15 09:30:00.985394944 412.28 0.0

315 rows × 3 columns

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