Use Cases#

import onetick.py as otp
from datetime import datetime
otp.config['default_start_time'] = datetime(2003, 12, 1, 0, 0, 0)
otp.config['default_end_time'] = datetime(2003, 12, 4, 0, 0, 0)
otp.config['default_db'] = 'NYSE_TAQ'
otp.config['default_symbol'] = 'AAPL'
otp.config['tz'] = 'EST5EDT'

Retrieving Tick Data#

trd = otp.DataSource('NYSE_TAQ', tick_type='TRD')
otp.run(trd, start=otp.dt(2023,4,20,9,30), end=otp.dt(2023,4,20,9,35), 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-04-20 09:30:00.000650240 Z N C 0 SPY 411.2100 1969-12-31 19:00:00 0 231 0 78367 52983525042744 2023-04-20 09:30:00.000331000 1969-12-31 19:00:00.000000000 0
1 2023-04-20 09:30:00.000656384 T T N C 0 SPY 411.2000 1969-12-31 19:00:00 0 100 0 78368 62879146921029 2023-04-20 09:30:00.000273283 1969-12-31 19:00:00.000000000 1
2 2023-04-20 09:30:00.002436608 K N C 0 SPY 411.2000 1969-12-31 19:00:00 0 400 0 78373 52983525173819 2023-04-20 09:30:00.002089000 1969-12-31 19:00:00.000000000 0
3 2023-04-20 09:30:00.002562816 Z F I N C 1 SPY 411.2100 1969-12-31 19:00:00 0 50 0 78374 52983525042745 2023-04-20 09:30:00.002325000 1969-12-31 19:00:00.000000000 1
4 2023-04-20 09:30:00.002568960 Z F I N C 1 SPY 411.2200 1969-12-31 19:00:00 0 50 0 78375 52983525042746 2023-04-20 09:30:00.002325000 1969-12-31 19:00:00.000000000 2
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
17115 2023-04-20 09:34:59.781732352 P F N C 1 SPY 411.7400 1969-12-31 19:00:00 0 100 0 119251 52983525623877 2023-04-20 09:34:59.781707776 1969-12-31 19:00:00.000000000 0
17116 2023-04-20 09:34:59.781930752 K F N C 1 SPY 411.7400 1969-12-31 19:00:00 0 100 0 119252 52983525236544 2023-04-20 09:34:59.781680000 1969-12-31 19:00:00.000000000 1
17117 2023-04-20 09:34:59.892375040 Z I N C 0 SPY 411.7400 1969-12-31 19:00:00 0 40 0 119268 52983525106510 2023-04-20 09:34:59.892138000 1969-12-31 19:00:00.000000000 0
17118 2023-04-20 09:34:59.930526464 D N C T 0 SPY 411.7425 1969-12-31 19:00:00 0 200 0 119270 71675308169546 2023-04-20 09:34:59.780763000 2023-04-20 09:34:59.930170219 0
17119 2023-04-20 09:34:59.958385152 Z N C 0 SPY 411.7400 1969-12-31 19:00:00 0 160 0 119271 52983525106514 2023-04-20 09:34:59.958147000 1969-12-31 19:00:00.000000000 0

17120 rows × 18 columns

Creating Bars#

We create 10-minute bars (bucket_interval=600 seconds) below.

trd = otp.DataSource('NYSE_TAQ', tick_type='TRD')
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=600)
otp.run(bars, start=otp.dt(2022,11,28,9,30), end=otp.dt(2022,11,30,16), symbols=['AA'], apply_times_daily=True)
Time VOLUME HIGH LOW OPEN COUNT CLOSE
0 2022-11-28 09:40:00 256335 47.3400 46.4110 46.9200 1958 47.2000
1 2022-11-28 09:50:00 103989 47.3100 46.9050 47.1800 1360 47.1399
2 2022-11-28 10:00:00 96622 47.2400 46.9600 47.0900 1102 47.0900
3 2022-11-28 10:10:00 169139 47.1600 46.7400 47.1000 1770 46.9113
4 2022-11-28 10:20:00 115764 47.3000 46.7800 46.9100 1230 47.1250
... ... ... ... ... ... ... ...
112 2022-11-30 15:20:00 215528 50.1100 49.7319 49.7716 2118 49.9711
113 2022-11-30 15:30:00 146682 50.3100 49.9400 49.9750 2127 50.2800
114 2022-11-30 15:40:00 188976 50.5000 50.2300 50.2700 2624 50.2300
115 2022-11-30 15:50:00 200605 50.3561 50.0100 50.2300 3231 50.0700
116 2022-11-30 16:00:00 581342 50.1900 49.8300 50.0700 6493 50.1400

117 rows × 7 columns

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.

bars = otp.DataSource('NYSE_TAQ_BARS', tick_type='TRD_1M')
bars = bars[['FIRST','HIGH','LOW','LAST','VOLUME']]
otp.run(bars, start=otp.dt(2022,11,28,9,31), end=otp.dt(2022,11,30,16,1), symbols=['SPY'], apply_times_daily=True)
Time FIRST HIGH LOW LAST VOLUME
0 2022-11-28 09:31:00 399.09 399.750 399.000 399.6500 289791
1 2022-11-28 09:32:00 399.69 400.000 399.600 399.8900 295441
2 2022-11-28 09:33:00 399.89 400.175 399.840 400.1750 233032
3 2022-11-28 09:34:00 400.16 400.380 400.000 400.1600 208954
4 2022-11-28 09:35:00 400.13 400.360 400.120 400.3493 112015
... ... ... ... ... ... ...
1165 2022-11-30 15:56:00 406.68 406.830 406.460 406.8100 1311316
1166 2022-11-30 15:57:00 406.80 406.830 406.525 406.6000 1319798
1167 2022-11-30 15:58:00 406.60 406.850 406.550 406.7300 1691348
1168 2022-11-30 15:59:00 406.73 406.950 406.680 406.7100 2592503
1169 2022-11-30 16:00:00 406.71 407.550 406.680 407.4700 6700757

1170 rows × 6 columns

Prevailing quote at the time of a trade#

import onetick.py as otp

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=otp.dt(2023,4,20,9,30), end=otp.dt(2023,4,20,10), symbols='SPY')
Time PRICE SIZE ASK_PRICE BID_PRICE quote_time
0 2023-04-20 09:30:00.000650240 411.21 231 411.22 411.20 2023-04-20 09:30:00.000636672
1 2023-04-20 09:30:00.000656384 411.20 100 411.22 411.20 2023-04-20 09:30:00.000636672
2 2023-04-20 09:30:00.002436608 411.20 400 411.22 411.20 2023-04-20 09:30:00.002411008
3 2023-04-20 09:30:00.002562816 411.21 50 411.22 411.20 2023-04-20 09:30:00.002486016
4 2023-04-20 09:30:00.002568960 411.22 50 411.22 411.20 2023-04-20 09:30:00.002566912
... ... ... ... ... ... ...
63773 2023-04-20 09:59:59.967479808 411.66 100 411.67 411.65 2023-04-20 09:59:59.967476224
63774 2023-04-20 09:59:59.971563520 411.66 100 411.67 411.66 2023-04-20 09:59:59.971560960
63775 2023-04-20 09:59:59.971569408 411.66 100 411.67 411.66 2023-04-20 09:59:59.971565824
63776 2023-04-20 09:59:59.971674624 411.66 100 411.67 411.66 2023-04-20 09:59:59.971671296
63777 2023-04-20 09:59:59.972700160 411.68 1 411.67 411.65 2023-04-20 09:59:59.971976960

63778 rows × 6 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=otp.dt(2023,4,10,10,25,59), end=otp.dt(2023,4,10,10,27), symbols='AAPL')
Time market_vwap
0 2023-04-10 10:27:00 160.477922

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

orders = otp.Ticks(arrival=[otp.dt(2023,4,10,10,25,59), otp.dt(2023,4,10,10,26,9)], 
                      exit=[otp.dt(2023,4,10,10,26,7), otp.dt(2023,4,10,10,27,48)],
                      sym=['AAPL', 'MSFT'])
otp.run(orders)
Time arrival exit sym
0 2003-12-01 00:00:00.000 2023-04-10 10:25:59 2023-04-10 10:26:07 AAPL
1 2003-12-01 00:00:00.001 2023-04-10 10:26:09 2023-04-10 10:27:48 MSFT
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=[otp.dt(2023,4,10,10,25,59), otp.dt(2023,4,10,10,26,9)], 
                      exit=[otp.dt(2023,4,10,10,26,7), otp.dt(2023,4,10,10,27,48)],
                      sym=['AAPL', 'MSFT'])
orders = orders.join_with_query(vwap, start=orders['arrival'], end=orders['exit'], symbol=orders['sym'])
otp.run(orders)
Time market_vwap arrival exit sym
0 2003-12-01 00:00:00.000 160.548890 2023-04-10 10:25:59 2023-04-10 10:26:07 AAPL
1 2003-12-01 00:00:00.001 286.136779 2023-04-10 10:26:09 2023-04-10 10:27:48 MSFT

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

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

import onetick.query as otq

s = otp.dt(2023, 3, 8, 10)
e = otp.dt(2023, 3, 9, 10, 0, 1)
markouts = [-1, 1, 5, 10, 60] 

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

qte_by_markout = []
for m in markouts:
    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_{m}', 
                      'BID_PRICE': f'BID_PRICE_{m}'})
    qte[f'quote_time_{m}'] = qte['Time']
    
    # shift the data by m seconds
    # change the timestamp so it fits into original time range (required by OneTick)
    qte.sink(otq.ModifyQueryTimes(start_time=f'_START_TIME + {m * 1000}',
                                  output_timestamp=f'TIMESTAMP - {m * 1000}',
                                  end_time=f'_END_TIME + {m * 1000}'))
    qte_by_markout.append(qte)

trd = otp.join_by_time([trd] + qte_by_markout)
# trd.render()
otp.run(trd, start=s, end=e, symbols='TSLA', apply_times_daily=True)
Time PRICE SIZE ASK_PRICE_-1 BID_PRICE_-1 quote_time_-1 ASK_PRICE_1 BID_PRICE_1 quote_time_1 ASK_PRICE_5 BID_PRICE_5 quote_time_5 ASK_PRICE_10 BID_PRICE_10 quote_time_10 ASK_PRICE_60 BID_PRICE_60 quote_time_60
0 2023-03-08 10:00:00.014762069 181.4250 300 181.47 181.41 2023-03-08 09:59:59.002564435 181.49 181.48 2023-03-08 10:00:01.000000000 181.15 181.04 2023-03-08 10:00:05.012557243 181.16 181.08 2023-03-08 10:00:10.000000000 180.51 180.45 2023-03-08 10:01:00.012332497
1 2023-03-08 10:00:00.014764168 181.4400 12 181.47 181.41 2023-03-08 09:59:59.002564435 181.49 181.48 2023-03-08 10:00:01.000000000 181.15 181.04 2023-03-08 10:00:05.012557243 181.16 181.08 2023-03-08 10:00:10.000000000 180.51 180.45 2023-03-08 10:01:00.012332497
2 2023-03-08 10:00:00.014767570 181.4400 200 181.47 181.41 2023-03-08 09:59:59.002564435 181.49 181.48 2023-03-08 10:00:01.000000000 181.15 181.04 2023-03-08 10:00:05.012557243 181.16 181.08 2023-03-08 10:00:10.000000000 180.51 180.45 2023-03-08 10:01:00.012332497
3 2023-03-08 10:00:00.014799259 181.4500 2 181.47 181.41 2023-03-08 09:59:59.002564435 181.49 181.48 2023-03-08 10:00:01.000000000 181.15 181.04 2023-03-08 10:00:05.012557243 181.16 181.08 2023-03-08 10:00:10.000000000 180.51 180.45 2023-03-08 10:01:00.012332497
4 2023-03-08 10:00:00.015374686 181.4400 300 181.47 181.41 2023-03-08 09:59:59.002564435 181.49 181.48 2023-03-08 10:00:01.000000000 181.15 181.04 2023-03-08 10:00:05.012557243 181.16 181.08 2023-03-08 10:00:10.000000000 180.51 180.45 2023-03-08 10:01:00.012332497
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
705 2023-03-09 10:00:00.956041264 183.5899 500 183.46 183.43 2023-03-09 09:59:59.911620614 183.59 183.54 2023-03-09 10:00:01.945769144 183.65 183.57 2023-03-09 10:00:05.954673686 183.68 183.65 2023-03-09 10:00:10.872345446 183.47 183.45 2023-03-09 10:01:00.941018616
706 2023-03-09 10:00:00.967881686 183.5800 25 183.46 183.43 2023-03-09 09:59:59.963372673 183.59 183.54 2023-03-09 10:00:01.963256371 183.66 183.57 2023-03-09 10:00:05.967652794 183.68 183.65 2023-03-09 10:00:10.872345446 183.47 183.45 2023-03-09 10:01:00.941018616
707 2023-03-09 10:00:00.978963708 183.4718 136 183.46 183.42 2023-03-09 09:59:59.971349853 183.59 183.54 2023-03-09 10:00:01.963256371 183.66 183.57 2023-03-09 10:00:05.967652794 183.68 183.65 2023-03-09 10:00:10.872345446 183.47 183.45 2023-03-09 10:01:00.941018616
708 2023-03-09 10:00:00.983358396 183.5500 20 183.46 183.41 2023-03-09 09:59:59.983288366 183.59 183.54 2023-03-09 10:00:01.963256371 183.66 183.57 2023-03-09 10:00:05.967652794 183.68 183.65 2023-03-09 10:00:10.872345446 183.47 183.45 2023-03-09 10:01:00.941018616
709 2023-03-09 10:00:00.990945922 183.5650 200 183.44 183.41 2023-03-09 09:59:59.988781014 183.59 183.54 2023-03-09 10:00:01.963256371 183.66 183.57 2023-03-09 10:00:05.967652794 183.68 183.65 2023-03-09 10:00:10.872345446 183.47 183.45 2023-03-09 10:01:00.941018616

710 rows × 18 columns

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='AMZN',
        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:01:00.702788 {'PRICE': 102.685, 'short': 102.55571394019401, 'long': 102.55571526781988, 'buy': 0.0, 'sell': 1.0}
SELL



2023-03-31 14:01:00.715928 {'PRICE': 102.69, 'short': 102.55577296703349, 'long': 102.5557718802702, 'buy': 1.0, 'sell': 0.0}
BUY

..

2023-03-31 14:01:00.859880 {'PRICE': 102.685, 'short': 102.55593452066896, 'long': 102.55593932631618, 'buy': 0.0, 'sell': 1.0}
SELL

..

2023-03-31 14:01:01.362596 {'PRICE': 102.6888, 'short': 102.55619792035449, 'long': 102.5561062237178, 'buy': 1.0, 'sell': 0.0}
BUY

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

2023-03-31 14:02:37.925538 {'PRICE': 102.51, 'short': 102.58265328102567, 'long': 102.58265371464864, '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=otp.dt(2023,4,20,9,30), end=otp.dt(2023,4,20,9,35), symbols=['SPY'])
Time PRICE UPTICK
0 2023-04-20 09:30:00.000650240 411.2100 NaN
1 2023-04-20 09:30:00.000656384 411.2000 -1.0
2 2023-04-20 09:30:00.002436608 411.2000 0.0
3 2023-04-20 09:30:00.002562816 411.2100 1.0
4 2023-04-20 09:30:00.002568960 411.2200 1.0
... ... ... ...
17115 2023-04-20 09:34:59.781732352 411.7400 0.0
17116 2023-04-20 09:34:59.781930752 411.7400 0.0
17117 2023-04-20 09:34:59.892375040 411.7400 0.0
17118 2023-04-20 09:34:59.930526464 411.7425 1.0
17119 2023-04-20 09:34:59.958385152 411.7400 -1.0

17120 rows × 3 columns