Variables and Data Structures#

import onetick.py as otp

Variables can be used to keep track of state across ticks. The example below shows how we may keep track of P&L.

trd = otp.Ticks({'PRICE': [13.5, 13.6, 13.3, 14.0],
                 'SIZE': [200, 100, 150, 200],
                 'SIDE': ['B', 'S', 'B', 'S']})

trd.state_vars['PROFIT'] = 0
trd.state_vars['PROFIT'] += trd.apply(
    lambda t: t['PRICE'] * t['SIZE'] if t['SIDE'] == 'S' else -(t['PRICE'] * t['SIZE'])
)

trd['PROFIT'] = trd.state_vars['PROFIT']

otp.run(trd)
Time PRICE SIZE SIDE PROFIT
0 2003-12-01 00:00:00.000 13.5 200 B -2700
1 2003-12-01 00:00:00.001 13.6 100 S -1340
2 2003-12-01 00:00:00.002 13.3 150 B -3335
3 2003-12-01 00:00:00.003 14.0 200 S -535

The variable ‘PROFIT’ keeps a running total. In other words, it aggregates state across trd.

Note that the same can be accomplished without variables by keeping the running total in a separate column.

trd = otp.Ticks({'PRICE': [13.5, 13.6, 13.3, 14.0],
                 'SIZE': [200, 100, 150, 200],
                 'SIDE': ['B', 'S', 'B', 'S']})
trd['VALUE'] = trd.apply(
    lambda t: t['PRICE'] * t['SIZE'] if t['SIDE'] == 'S' else -(t['PRICE'] * t['SIZE'])
)
trd = trd.agg({'PROFIT': otp.agg.sum('VALUE')}, running=True)
otp.run(trd)
Time PROFIT
0 2003-12-01 00:00:00.000 -2700.0
1 2003-12-01 00:00:00.001 -1340.0
2 2003-12-01 00:00:00.002 -3335.0
3 2003-12-01 00:00:00.003 -535.0

Another use case is to store the value from the last tick during aggregation / grouping.

q = otp.Ticks(X=[-1, 3, -3, 4, 2], Y=[0, 1, 1, 0, 3])
q.state_vars['S'] = 0
q.state_vars['S'] = q['X']
q = q.high('X', group_by=['Y'])
q['S'] = q.state_vars['S']
otp.run(q)
Time X Y S
0 2003-12-01 00:00:00.001 3 1 2
1 2003-12-01 00:00:00.003 4 0 2
2 2003-12-01 00:00:00.004 2 3 2

Dictionaries / Maps#

A map can be created with keys taken from one or more columns and holding entire ticks as values.

q = otp.DataSource('NYSE_TAQ', tick_type='TRD')
q = q[['PRICE','SIZE','COND','EXCHANGE']]
exchanges = otp.Ticks(EXCHANGE=['A', 'B', 'C', 'D', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
          NAME=['NYSE American (Amex)', 'Nasdaq BX', 'NYSE National (NSX)', 'FINRA ADF + NYSE/Nasdaq TRFs', 'MIAX Pearl', 'International Securities Exchange', 'Cboe EDGA', 'Cboe EDGX', 'Long-Term Stock Exchange (LTSE)', 'NYSE Chicago', 'New York Stock Exchange', 'NYSE Arca', 'Nasdaq (Tape C securities)', 'Consolidated Tape System (CTS)', 'Nasdaq (Tape A,B securities)', 'Members Exchange (MEMX)', "The Investors' Exchange (IEX)", 'CBOE Stock Exchange (CBSX)', 'Nasdaq PSX', 'Cboe BYX', 'Cboe BZX'])

q.state_vars['exchanges'] = otp.state.tick_set('latest', 'EXCHANGE', otp.eval(exchanges))
d = q.state_vars['exchanges'].dump()
q['exchange_name'] = q.state_vars['exchanges'].find('NAME', 'unknown')

otp.run(q, start=otp.dt(2023,3,29,9,30), end=otp.dt(2023,3,29,10), symbols=['SPY'])
Time PRICE SIZE COND EXCHANGE exchange_name
0 2023-03-29 09:30:00.000877568 399.9200 400 T P NYSE Arca
1 2023-03-29 09:30:00.001151232 399.9200 1000 T T Nasdaq (Tape A,B securities)
2 2023-03-29 09:30:00.001154304 399.9200 1000 T T Nasdaq (Tape A,B securities)
3 2023-03-29 09:30:00.001921280 399.9300 657 T T Nasdaq (Tape A,B securities)
4 2023-03-29 09:30:00.010831360 399.9250 100 F Z Cboe BZX
... ... ... ... ... ... ...
73249 2023-03-29 09:59:59.690966784 399.8582 3 I D FINRA ADF + NYSE/Nasdaq TRFs
73250 2023-03-29 09:59:59.697699840 399.8424 5 I D FINRA ADF + NYSE/Nasdaq TRFs
73251 2023-03-29 09:59:59.707425024 399.8600 1 I D FINRA ADF + NYSE/Nasdaq TRFs
73252 2023-03-29 09:59:59.928770304 399.8600 1 I D FINRA ADF + NYSE/Nasdaq TRFs
73253 2023-03-29 09:59:59.949504768 399.8500 35 I D FINRA ADF + NYSE/Nasdaq TRFs

73254 rows × 6 columns