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 |
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 |