Options#

This section contains 16 examples for Options using the onetick-py.
Each example is a self-contained script that can be run against the OneTick Cloud sample databases.

# onetick-py WebAPI configuration for OneTick Cloud
import os
os.environ['OTP_WEBAPI'] = '1'
os.environ['OTP_HTTP_ADDRESS'] = 'https://rest.cloud.onetick.com'
os.environ['OTP_ACCESS_TOKEN_URL'] = 'https://cloud-auth.parent.onetick.com/realms/OMD/protocol/openid-connect/token'
os.environ['OTP_CLIENT_ID'] = '__FILL_IN__'
os.environ['OTP_CLIENT_SECRET'] = '__FILL_IN__'

Options Greeks#

Retrieves end-of-day options data for AAPL contracts from the US Options EOD sample.
Includes Greeks (delta, gamma, theta, vega), implied volatility, open interest, volume, bid/ask close prices, and underlying price for a single trading day.

import onetick.py as otp

# Retrieve the end-of-day (DAY) records for all AAPL option contracts.
# Symbols are selected with a pattern against the US_OPTIONS_EOD_SAMPLE database.
options_day = otp.DataSource(db='US_OPTIONS_EOD_SAMPLE', tick_type='DAY')

# Merge every matching AAPL contract into a single stream.
merged = otp.merge(
    [options_day],
    symbols=otp.Symbols('US_OPTIONS_EOD_SAMPLE', pattern='AAPL%')
)

# Return first 1000 rows
merged = merged.limit(1000)
result = otp.run(
    merged,
    start=otp.dt(2025, 1, 3),
    end=otp.dt(2025, 1, 4),
    timezone='America/New_York'
)
result
Time ASK_CLOSE BID_CLOSE DELTA GAMMA IMP_VOLATILITY OMDSEQ OPEN_INT THETA UNDERLYING_PRICE VEGA VOLUME
0 2025-01-03 16:00:00 144.35 142.55 1.0000 0.0000 0.3000 3782 10.0 0.0000 243.36 0.0000 0
1 2025-01-03 16:00:00 0.01 NaN 0.0000 0.0000 0.3000 3783 80.0 0.0000 243.36 0.0000 10
2 2025-01-03 16:00:00 139.55 137.90 1.0000 0.0000 0.3000 3784 0.0 0.0000 243.36 0.0000 0
3 2025-01-03 16:00:00 0.01 NaN 0.0000 0.0000 0.3000 3785 0.0 0.0000 243.36 0.0000 0
4 2025-01-03 16:00:00 134.45 132.45 1.0000 0.0000 0.3000 3786 0.0 0.0000 243.36 0.0000 0
... ... ... ... ... ... ... ... ... ... ... ... ...
995 2025-01-03 16:00:00 12.55 12.00 -0.5370 0.0155 0.2301 4777 11541.0 -0.0448 243.36 0.4420 424
996 2025-01-03 16:00:00 5.65 5.50 0.3719 0.0165 0.2055 4778 4593.0 -0.0695 243.36 0.4209 1006
997 2025-01-03 16:00:00 15.55 15.00 -0.6113 0.0150 0.2291 4779 2039.0 -0.0390 243.36 0.4265 312
998 2025-01-03 16:00:00 4.05 3.90 0.2937 0.0153 0.2025 4780 23432.0 -0.0610 243.36 0.3832 887
999 2025-01-03 16:00:00 19.10 18.50 -0.6770 0.0139 0.2327 4781 1259.0 -0.0330 243.36 0.3995 24

1000 rows × 12 columns

Options Trades#

Retrieves options trade data for a specific AAPL contract from the US Options sample feed.
The symbol encodes the underlying, expiry, call/put indicator and strike price.

import onetick.py as otp

# Define the trade data source for a single option contract.
data = otp.DataSource(db='US_OPTIONS_SAMPLE', tick_type='TRD')

# Return first 1000 rows
data = data.limit(1000)

result = otp.run(
    data,
    start=otp.dt(2025, 1, 2),
    end=otp.dt(2025, 1, 4, 16),
    timezone='UTC',
    symbols='AAPL  250103C00155000'   # Symbol has Underlying, Expiry, Call/Put and Strike
)
result

Time

PRICE

SIZE

EXCHANGE

TRADE_TYPE

TRADE_TYPE_EXT

TRADE_ID

OMDSEQ

DELETED_TIME

TICK_STATUS

0

2025-01-03 16:09:05.053000

87.91

6

S

f

MLET

003988253e

147

1970-01-01 00:00:00

0

1

2025-01-03 16:09:05.153000

87.92

7

E

f

MLET

00beb5253e

89

1970-01-01 00:00:00

0

2

2025-01-03 16:09:05.153000

87.95

3

X

f

MLET

00c3b5253e

90

1970-01-01 00:00:00

0

3

2025-01-03 16:09:05.153000

87.92

7

W

f

MLET

00c4b5253e

164

1970-01-01 00:00:00

0

4

2025-01-03 16:09:05.286000

87.93

12

J

g

MLAT

000bdb253e

60

1970-01-01 00:00:00

0

5

2025-01-03 19:09:30.778000

87.84

2

C

g

MLAT

0001936f7d

33

1970-01-01 00:00:00

0

OPRA Call and Put Volume & Open Interest Summary by Strike Price#

OPRA Daily data is retrieved from the US_OPTIONS_EOD database.
Here the sample database US_OPTIONS_EOD_SAMPLE is used.
Daily Volume and Open Interest are split by Calls and Puts and grouped by Strike Price for a specified underlying.
The daily DAY records are joined by time to the static STAT records to obtain the Expiration Date, Strike Price and Call/Put Indicator.
A lookback of up to 1 day (86400s) is used on the static data so the prevailing static record is picked up.
Volume and Open Interest are divided into Call and Put buckets, then summed across the day, grouped by Underlying Symbol and Strike Price.

import onetick.py as otp

# Daily options data for all AAPL contracts.
day = otp.DataSource(db='US_OPTIONS_EOD_SAMPLE', tick_type='DAY')
day = day[['VOLUME', 'OPEN_INT']]

# Static (reference) data for the same contracts, looking back up to 1 day for the prevailing record.
stat = otp.DataSource(db='US_OPTIONS_EOD_SAMPLE', tick_type='STAT', back_to_first_tick=86400)
stat = stat[['UNDERLYING_SYMBOL', 'EXPIRATION_DATE', 'STRIKE_PRICE', 'CALL_PUT_IND']]

# Join each daily tick to the prevailing static record (asof join).
joined = otp.join_by_time([day, stat])

# Split Volume and Open Interest into Call and Put buckets based on the Call/Put Indicator.
joined['CALL_VOLUME'] = joined.apply(lambda t: t['VOLUME'] if t['CALL_PUT_IND'] == 'C' else 0)
joined['PUT_VOLUME'] = joined.apply(lambda t: t['VOLUME'] if t['CALL_PUT_IND'] == 'P' else 0)
joined['CALL_OPEN_INT'] = joined.apply(lambda t: t['OPEN_INT'] if t['CALL_PUT_IND'] == 'C' else 0)
joined['PUT_OPEN_INT'] = joined.apply(lambda t: t['OPEN_INT'] if t['CALL_PUT_IND'] == 'P' else 0)

# Sum across the day, grouped by Underlying Symbol and Strike Price.
summary = joined.agg(
    {
        'CONTRACT_COUNT': otp.agg.count(),
        'CALL_VOLUME': otp.agg.sum('CALL_VOLUME'),
        'PUT_VOLUME': otp.agg.sum('PUT_VOLUME'),
        'VOLUME': otp.agg.sum('VOLUME'),
        'CALL_OPEN_INT': otp.agg.sum('CALL_OPEN_INT'),
        'PUT_OPEN_INT': otp.agg.sum('PUT_OPEN_INT'),
        'OPEN_INT': otp.agg.sum('OPEN_INT'),
    },
    group_by=['UNDERLYING_SYMBOL', 'STRIKE_PRICE']
)

# Merge every matching AAPL contract into a single stream.
merged = otp.merge(
    [summary],
    symbols=otp.Symbols('US_OPTIONS_EOD_SAMPLE', pattern='AAPL %')
)

result = otp.run(
    merged,
    start=otp.dt(2025, 1, 3),
    end=otp.dt(2025, 1, 4),
    timezone='America/New_York'
)
result
Time UNDERLYING_SYMBOL STRIKE_PRICE CONTRACT_COUNT CALL_VOLUME PUT_VOLUME VOLUME CALL_OPEN_INT PUT_OPEN_INT OPEN_INT
0 2025-01-04 AAPL 100.0 1 0 0 0 10.0 0.0 10.0
1 2025-01-04 AAPL 105.0 1 0 0 0 0.0 0.0 0.0
2 2025-01-04 AAPL 110.0 1 0 0 0 0.0 0.0 0.0
3 2025-01-04 AAPL 115.0 1 0 0 0 0.0 0.0 0.0
4 2025-01-04 AAPL 120.0 1 0 0 0 0.0 0.0 0.0
... ... ... ... ... ... ... ... ... ... ...
2257 2025-01-04 AAPL 410.0 1 0 0 0 0.0 0.0 0.0
2258 2025-01-04 AAPL 420.0 1 0 0 0 0.0 0.0 0.0
2259 2025-01-04 AAPL 430.0 1 0 0 0 0.0 0.0 0.0
2260 2025-01-04 AAPL 440.0 1 0 0 0 0.0 0.0 0.0
2261 2025-01-04 AAPL 450.0 1 0 0 0 0.0 0.0 0.0

2262 rows × 10 columns

OPRA Call and Put Volume & Open Interest Summary by Expiration#

OPRA Daily data is retrieved from the US_OPTIONS_EOD database.
Here the sample database US_OPTIONS_EOD_SAMPLE is used.
Daily Volume and Open Interest are split by Calls and Puts and grouped by Expiration Date for a specified underlying.
The daily DAY records are joined by time to the static STAT records to obtain the Expiration Date, Strike Price and Call/Put Indicator.
A lookback of up to 1 day (86400s) is used on the static data so the prevailing static record is picked up.
Volume and Open Interest are divided into Call and Put buckets, then summed across the day, grouped by Underlying Symbol and Expiration Date.

import onetick.py as otp

# Daily options data for all AAPL contracts.
day = otp.DataSource(db='US_OPTIONS_EOD_SAMPLE', tick_type='DAY')
day = day[['VOLUME', 'OPEN_INT']]

# Static (reference) data for the same contracts, looking back up to 1 day for the prevailing record.
stat = otp.DataSource(db='US_OPTIONS_EOD_SAMPLE', tick_type='STAT', back_to_first_tick=86400)
stat = stat[['UNDERLYING_SYMBOL', 'EXPIRATION_DATE', 'STRIKE_PRICE', 'CALL_PUT_IND']]

# Join each daily tick to the prevailing static record (asof join).
joined = otp.join_by_time([day, stat])

# Split Volume and Open Interest into Call and Put buckets based on the Call/Put Indicator.
joined['CALL_VOLUME'] = joined.apply(lambda t: t['VOLUME'] if t['CALL_PUT_IND'] == 'C' else 0)
joined['PUT_VOLUME'] = joined.apply(lambda t: t['VOLUME'] if t['CALL_PUT_IND'] == 'P' else 0)
joined['CALL_OPEN_INT'] = joined.apply(lambda t: t['OPEN_INT'] if t['CALL_PUT_IND'] == 'C' else 0)
joined['PUT_OPEN_INT'] = joined.apply(lambda t: t['OPEN_INT'] if t['CALL_PUT_IND'] == 'P' else 0)

# Sum across the day, grouped by Underlying Symbol and Expiration Date.
summary = joined.agg(
    {
        'CONTRACT_COUNT': otp.agg.count(),
        'CALL_VOLUME': otp.agg.sum('CALL_VOLUME'),
        'PUT_VOLUME': otp.agg.sum('PUT_VOLUME'),
        'VOLUME': otp.agg.sum('VOLUME'),
        'CALL_OPEN_INT': otp.agg.sum('CALL_OPEN_INT'),
        'PUT_OPEN_INT': otp.agg.sum('PUT_OPEN_INT'),
        'OPEN_INT': otp.agg.sum('OPEN_INT'),
    },
    group_by=['UNDERLYING_SYMBOL', 'EXPIRATION_DATE']
)

# Merge every matching AAPL contract into a single stream.
merged = otp.merge(
    [summary],
    symbols=otp.Symbols('US_OPTIONS_EOD_SAMPLE', pattern='AAPL %')
)

result = otp.run(
    merged,
    start=otp.dt(2025, 1, 3),
    end=otp.dt(2025, 1, 4),
    timezone='America/New_York'
)
result
Time UNDERLYING_SYMBOL EXPIRATION_DATE CONTRACT_COUNT CALL_VOLUME PUT_VOLUME VOLUME CALL_OPEN_INT PUT_OPEN_INT OPEN_INT
0 2025-01-04 AAPL 20250103 1 0 0 0 10.0 0.0 10.0
1 2025-01-04 AAPL 20250103 1 0 0 0 0.0 0.0 0.0
2 2025-01-04 AAPL 20250103 1 0 0 0 0.0 0.0 0.0
3 2025-01-04 AAPL 20250103 1 0 0 0 0.0 0.0 0.0
4 2025-01-04 AAPL 20250103 1 0 0 0 0.0 0.0 0.0
... ... ... ... ... ... ... ... ... ... ...
2257 2025-01-04 AAPL 20270115 1 0 0 0 0.0 0.0 0.0
2258 2025-01-04 AAPL 20270115 1 0 0 0 0.0 0.0 0.0
2259 2025-01-04 AAPL 20270115 1 0 0 0 0.0 0.0 0.0
2260 2025-01-04 AAPL 20270115 1 0 0 0 0.0 0.0 0.0
2261 2025-01-04 AAPL 20270115 1 0 0 0 0.0 0.0 0.0

2262 rows × 10 columns

OPRA Call and Put Volume across an intra-day time range by Expiry#

OPRA Trade data is retrieved from the US_OPTIONS database.
Here the sample database US_OPTIONS_SAMPLE is used.
Period Volume is split by Calls and Puts and grouped by Expiration Date for a specified underlying.
Trades are joined by time to the static STAT records to obtain the Expiration Date, Strike Price and Call/Put Indicator.
A lookback of up to 1 day (86400s) is used on the static data so the prevailing static record is picked up.
Volume is aggregated from Trade Size and divided into Call and Put buckets, then summed across the period, grouped by Underlying Symbol and Expiration Date.

import onetick.py as otp

# Intraday options trades for all AAPL contracts.
trd = otp.DataSource(db='US_OPTIONS_SAMPLE', tick_type='TRD')
trd = trd[['SIZE']]

# Static (reference) data for the same contracts, looking back up to 1 day for the prevailing record.
stat = otp.DataSource(db='US_OPTIONS_SAMPLE', tick_type='STAT', back_to_first_tick=86400)
stat = stat[['UNDERLYING_SYMBOL', 'EXPIRATION_DATE', 'STRIKE_PRICE', 'CALL_PUT_IND']]

# Join each trade to the prevailing static record (asof join).
joined = otp.join_by_time([trd, stat])

# Trade Size is the traded volume; split it into Call and Put buckets.
joined['VOLUME'] = joined['SIZE']
joined['CALL_VOLUME'] = joined.apply(lambda t: t['SIZE'] if t['CALL_PUT_IND'] == 'C' else 0)
joined['PUT_VOLUME'] = joined.apply(lambda t: t['SIZE'] if t['CALL_PUT_IND'] == 'P' else 0)

# Sum across the period, grouped by Underlying Symbol and Expiration Date.
summary = joined.agg(
    {
        'CONTRACT_COUNT': otp.agg.count(),
        'CALL_VOLUME': otp.agg.sum('CALL_VOLUME'),
        'PUT_VOLUME': otp.agg.sum('PUT_VOLUME'),
        'VOLUME': otp.agg.sum('VOLUME'),
    },
    group_by=['UNDERLYING_SYMBOL', 'EXPIRATION_DATE']
)

# Merge every matching AAPL contract into a single stream.
merged = otp.merge(
    [summary],
    symbols=otp.Symbols('US_OPTIONS_SAMPLE', pattern='AAPL %')
)

result = otp.run(
    merged,
    start=otp.dt(2025, 1, 3, 10),
    end=otp.dt(2025, 1, 3, 12),
    timezone='America/New_York'
)
result

Time

UNDERLYING_SYMBOL

EXPIRATION_DATE

CONTRACT_COUNT

CALL_VOLUME

PUT_VOLUME

VOLUME

0

2025-01-03 12:00:00

AAPL

20250103

1

2

0

2

1

2025-01-03 12:00:00

AAPL

20250103

1

10

0

10

2

2025-01-03 12:00:00

AAPL

20250103

5

35

0

35

3

2025-01-03 12:00:00

AAPL

20250103

2

4

0

4

4

2025-01-03 12:00:00

AAPL

20250103

1

1

0

1

811

2025-01-03 12:00:00

AAPL

20270115

1

0

3

3

812

2025-01-03 12:00:00

AAPL

20270115

49

0

528

528

813

2025-01-03 12:00:00

AAPL

20270115

3

0

7

7

814

2025-01-03 12:00:00

AAPL

20270115

2

0

2

2

815

2025-01-03 12:00:00

AAPL

20270115

1

0

6

6

816 rows x 7 columns

OPRA Call and Put Volume across an intra-day time range by Strike Price#

OPRA Trade data is retrieved from the US_OPTIONS database.
Here the sample database US_OPTIONS_SAMPLE is used.
Period Volume is split by Calls and Puts and grouped by Strike Price for a specified underlying.
Trades are joined by time to the static STAT records to obtain the Expiration Date, Strike Price and Call/Put Indicator.
A lookback of up to 1 day (86400s) is used on the static data so the prevailing static record is picked up.
Volume is aggregated from Trade Size and divided into Call and Put buckets, then summed across the period, grouped by Underlying Symbol and Strike Price.

import onetick.py as otp

# Intraday options trades for all AAPL contracts.
trd = otp.DataSource(db='US_OPTIONS_SAMPLE', tick_type='TRD')
trd = trd[['SIZE']]

# Static (reference) data for the same contracts, looking back up to 1 day for the prevailing record.
stat = otp.DataSource(db='US_OPTIONS_SAMPLE', tick_type='STAT', back_to_first_tick=86400)
stat = stat[['UNDERLYING_SYMBOL', 'EXPIRATION_DATE', 'STRIKE_PRICE', 'CALL_PUT_IND']]

# Join each trade to the prevailing static record (asof join).
joined = otp.join_by_time([trd, stat])

# Trade Size is the traded volume; split it into Call and Put buckets.
joined['VOLUME'] = joined['SIZE']
joined['CALL_VOLUME'] = joined.apply(lambda t: t['SIZE'] if t['CALL_PUT_IND'] == 'C' else 0)
joined['PUT_VOLUME'] = joined.apply(lambda t: t['SIZE'] if t['CALL_PUT_IND'] == 'P' else 0)

# Sum across the period, grouped by Underlying Symbol and Strike Price.
summary = joined.agg(
    {
        'CONTRACT_COUNT': otp.agg.count(),
        'CALL_VOLUME': otp.agg.sum('CALL_VOLUME'),
        'PUT_VOLUME': otp.agg.sum('PUT_VOLUME'),
        'VOLUME': otp.agg.sum('VOLUME'),
    },
    group_by=['UNDERLYING_SYMBOL', 'STRIKE_PRICE']
)

# Merge every matching AAPL contract into a single stream.
merged = otp.merge(
    [summary],
    symbols=otp.Symbols('US_OPTIONS_SAMPLE', pattern='AAPL %')
)

result = otp.run(
    merged,
    start=otp.dt(2025, 1, 3, 10),
    end=otp.dt(2025, 1, 3, 12),
    timezone='America/New_York'
)
result

Time

UNDERLYING_SYMBOL

STRIKE_PRICE

CONTRACT_COUNT

CALL_VOLUME

PUT_VOLUME

VOLUME

0

2025-01-03 12:00:00

AAPL

140.0

1

2

0

2

1

2025-01-03 12:00:00

AAPL

150.0

1

10

0

10

2

2025-01-03 12:00:00

AAPL

155.0

5

35

0

35

3

2025-01-03 12:00:00

AAPL

160.0

2

4

0

4

4

2025-01-03 12:00:00

AAPL

170.0

1

1

0

1

811

2025-01-03 12:00:00

AAPL

185.0

1

0

3

3

812

2025-01-03 12:00:00

AAPL

200.0

49

0

528

528

813

2025-01-03 12:00:00

AAPL

210.0

3

0

7

7

814

2025-01-03 12:00:00

AAPL

240.0

2

0

2

2

815

2025-01-03 12:00:00

AAPL

250.0

1

0

6

6

816 rows x 7 columns

OPRA Daily Volume, Open Interest and Greeks for Selected Underlying and Expiry#

OPRA Daily data is retrieved from the US_OPTIONS_EOD database.
Here the sample database US_OPTIONS_EOD_SAMPLE is used.
Retrieves end-of-day pricing, Volume, Open Interest and Greeks (Implied Volatility, Delta, Gamma, Theta and Vega), together with static data including Underlying Symbol, Strike Price, Expiration Date and Call/Put Indicator.
Filtered for the AAPL underlying and expiration date 20270115.
The daily DAY records are joined by time to the static STAT records.
Because the static data may have been published at a different time to the daily data, a lookback of up to 1 day (86400s) is used so the prevailing static record is identified.

import onetick.py as otp

# Daily options data for all AAPL contracts.
day = otp.DataSource(db='US_OPTIONS_EOD_SAMPLE', tick_type='DAY')
day = day[['VOLUME', 'OPEN_INT', 'UNDERLYING_PRICE', 'BID_CLOSE', 'ASK_CLOSE',
           'IMP_VOLATILITY', 'DELTA', 'GAMMA', 'THETA', 'VEGA']]

# Static (reference) data for the same contracts, looking back up to 1 day for the prevailing record.
stat = otp.DataSource(db='US_OPTIONS_EOD_SAMPLE', tick_type='STAT', back_to_first_tick=86400)
stat = stat[['UNDERLYING_SYMBOL', 'EXPIRATION_DATE', 'STRIKE_PRICE', 'CALL_PUT_IND']]

# Restrict the static records to the selected expiration.
stat = stat.where(stat['EXPIRATION_DATE'] == '20270115')

# Join each daily tick to the prevailing static record (asof join).
joined = otp.join_by_time([day, stat])

# Merge every matching AAPL contract into a single stream.
merged = otp.merge(
    [joined],
    symbols=otp.Symbols('US_OPTIONS_EOD_SAMPLE', pattern='AAPL %')
)

result = otp.run(
    merged,
    start=otp.dt(2025, 1, 3),
    end=otp.dt(2025, 1, 4),
    timezone='America/New_York'
)
result
Time VOLUME OPEN_INT UNDERLYING_PRICE BID_CLOSE ASK_CLOSE IMP_VOLATILITY DELTA GAMMA THETA VEGA UNDERLYING_SYMBOL EXPIRATION_DATE STRIKE_PRICE CALL_PUT_IND
0 2025-01-03 16:00:00 0 10.0 243.36 142.55 144.35 0.3000 1.0000 0.0000 0.0000 0.0000 NaN
1 2025-01-03 16:00:00 0 0.0 243.36 137.90 139.55 0.3000 1.0000 0.0000 0.0000 0.0000 NaN
2 2025-01-03 16:00:00 0 0.0 243.36 132.45 134.45 0.3000 1.0000 0.0000 0.0000 0.0000 NaN
3 2025-01-03 16:00:00 0 0.0 243.36 127.65 128.80 0.3000 1.0000 0.0000 0.0000 0.0000 NaN
4 2025-01-03 16:00:00 0 0.0 243.36 122.45 123.80 0.3000 1.0000 0.0000 0.0000 0.0000 NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2257 2025-01-03 16:00:00 0 0.0 243.36 164.55 168.50 0.5541 -0.5489 0.0021 -0.0057 1.3732 AAPL 20270115 410.0 P
2258 2025-01-03 16:00:00 0 0.0 243.36 174.60 178.55 0.5541 -0.5610 0.0021 -0.0039 1.3674 AAPL 20270115 420.0 P
2259 2025-01-03 16:00:00 0 0.0 243.36 184.60 188.55 0.5541 -0.5727 0.0020 -0.0021 1.3605 AAPL 20270115 430.0 P
2260 2025-01-03 16:00:00 0 0.0 243.36 194.60 198.55 0.5541 -0.5841 0.0020 -0.0002 1.3527 AAPL 20270115 440.0 P
2261 2025-01-03 16:00:00 0 0.0 243.36 204.60 208.55 0.5541 -0.5952 0.0020 0.0016 1.3440 AAPL 20270115 450.0 P

2262 rows × 15 columns

OPRA Daily Volume, Open Interest and Greeks for Selected Underlying and Expiry returned as a straddle configuration#

OPRA Daily Volume, Open Interest and Greeks for Selected Underlying and Expiry returned as a straddle configuration.
OPRA Daily data is retrieved from the US_OPTIONS_EOD database.
Here the sample database US_OPTIONS_EOD_SAMPLE is used.
Retrieves end-of-day pricing, Volume, Open Interest and Greeks (Implied Volatility, Delta, Gamma, Theta and Vega), together with static data including Underlying Symbol, Strike Price, Expiration Date and Call/Put Indicator.
Filtered for the AAPL underlying and expiration date 20270115.
Data is displayed grouped by strike price, with Call and Put metrics associated with each strike.
The daily DAY records are joined by time to the static STAT records with a lookback of up to 1 day (86400s).
Call and Put metrics are created by testing CALL_PUT_IND (‘P’ for Put, ‘C’ for Call), using NaN for the other side so that averaging by strike leaves a single row per strike carrying both the Call and Put values.

import onetick.py as otp

# Daily options data for all AAPL contracts.
day = otp.DataSource(db='US_OPTIONS_EOD_SAMPLE', tick_type='DAY')
day = day[['VOLUME', 'OPEN_INT', 'BID_CLOSE', 'ASK_CLOSE',
           'IMP_VOLATILITY', 'DELTA', 'GAMMA', 'THETA', 'VEGA']]

# Static (reference) data for the same contracts, looking back up to 1 day for the prevailing record.
stat = otp.DataSource(db='US_OPTIONS_EOD_SAMPLE', tick_type='STAT', back_to_first_tick=86400)
stat = stat[['UNDERLYING_SYMBOL', 'EXPIRATION_DATE', 'STRIKE_PRICE', 'CALL_PUT_IND']]

# Restrict the static records to the selected expiration.
stat = stat.where(stat['EXPIRATION_DATE'] == '20270115')

# Join each daily tick to the prevailing static record (asof join).
joined = otp.join_by_time([day, stat])

# Build Call ('C') and Put ('P') columns per metric; the opposite side is NaN so it is ignored by the average.
# (per-tick apply takes a single-argument lambda referencing the tick's fields)
joined['C_VOLUME'] = joined.apply(lambda t: t['VOLUME'] if t['CALL_PUT_IND'] == 'C' else otp.nan)
joined['P_VOLUME'] = joined.apply(lambda t: t['VOLUME'] if t['CALL_PUT_IND'] == 'P' else otp.nan)
joined['C_OPEN_INT'] = joined.apply(lambda t: t['OPEN_INT'] if t['CALL_PUT_IND'] == 'C' else otp.nan)
joined['P_OPEN_INT'] = joined.apply(lambda t: t['OPEN_INT'] if t['CALL_PUT_IND'] == 'P' else otp.nan)
joined['C_BID'] = joined.apply(lambda t: t['BID_CLOSE'] if t['CALL_PUT_IND'] == 'C' else otp.nan)
joined['P_BID'] = joined.apply(lambda t: t['BID_CLOSE'] if t['CALL_PUT_IND'] == 'P' else otp.nan)
joined['C_ASK'] = joined.apply(lambda t: t['ASK_CLOSE'] if t['CALL_PUT_IND'] == 'C' else otp.nan)
joined['P_ASK'] = joined.apply(lambda t: t['ASK_CLOSE'] if t['CALL_PUT_IND'] == 'P' else otp.nan)
joined['C_IV'] = joined.apply(lambda t: t['IMP_VOLATILITY'] if t['CALL_PUT_IND'] == 'C' else otp.nan)
joined['P_IV'] = joined.apply(lambda t: t['IMP_VOLATILITY'] if t['CALL_PUT_IND'] == 'P' else otp.nan)
joined['C_DELTA'] = joined.apply(lambda t: t['DELTA'] if t['CALL_PUT_IND'] == 'C' else otp.nan)
joined['P_DELTA'] = joined.apply(lambda t: t['DELTA'] if t['CALL_PUT_IND'] == 'P' else otp.nan)
joined['C_GAMMA'] = joined.apply(lambda t: t['GAMMA'] if t['CALL_PUT_IND'] == 'C' else otp.nan)
joined['P_GAMMA'] = joined.apply(lambda t: t['GAMMA'] if t['CALL_PUT_IND'] == 'P' else otp.nan)
joined['C_THETA'] = joined.apply(lambda t: t['THETA'] if t['CALL_PUT_IND'] == 'C' else otp.nan)
joined['P_THETA'] = joined.apply(lambda t: t['THETA'] if t['CALL_PUT_IND'] == 'P' else otp.nan)
joined['C_VEGA'] = joined.apply(lambda t: t['VEGA'] if t['CALL_PUT_IND'] == 'C' else otp.nan)
joined['P_VEGA'] = joined.apply(lambda t: t['VEGA'] if t['CALL_PUT_IND'] == 'P' else otp.nan)

# Average each Call and Put metric, grouped by Strike Price, to return a single straddle row per strike.
metrics = ['VOLUME', 'OPEN_INT', 'BID', 'ASK', 'IV', 'DELTA', 'GAMMA', 'THETA', 'VEGA']
agg_spec = {}
for m in metrics:
    agg_spec['C_' + m] = otp.agg.average('C_' + m)
    agg_spec['P_' + m] = otp.agg.average('P_' + m)

straddle = joined.agg(agg_spec, group_by=['STRIKE_PRICE'])

# Merge every matching AAPL contract into a single stream.
merged = otp.merge(
    [straddle],
    symbols=otp.Symbols('US_OPTIONS_EOD_SAMPLE', pattern='AAPL %')
)

result = otp.run(
    merged,
    start=otp.dt(2025, 1, 3),
    end=otp.dt(2025, 1, 4),
    timezone='America/New_York'
)
result
Time STRIKE_PRICE C_VOLUME P_VOLUME C_OPEN_INT P_OPEN_INT C_BID P_BID C_ASK P_ASK C_IV P_IV C_DELTA P_DELTA C_GAMMA P_GAMMA C_THETA P_THETA C_VEGA P_VEGA
0 2025-01-04 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 2025-01-04 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 2025-01-04 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 2025-01-04 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 2025-01-04 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2257 2025-01-04 410.0 NaN 0.0 NaN 0.0 NaN 164.55 NaN 168.50 NaN 0.5541 NaN -0.5489 NaN 0.0021 NaN -0.0057 NaN 1.3732
2258 2025-01-04 420.0 NaN 0.0 NaN 0.0 NaN 174.60 NaN 178.55 NaN 0.5541 NaN -0.5610 NaN 0.0021 NaN -0.0039 NaN 1.3674
2259 2025-01-04 430.0 NaN 0.0 NaN 0.0 NaN 184.60 NaN 188.55 NaN 0.5541 NaN -0.5727 NaN 0.0020 NaN -0.0021 NaN 1.3605
2260 2025-01-04 440.0 NaN 0.0 NaN 0.0 NaN 194.60 NaN 198.55 NaN 0.5541 NaN -0.5841 NaN 0.0020 NaN -0.0002 NaN 1.3527
2261 2025-01-04 450.0 NaN 0.0 NaN 0.0 NaN 204.60 NaN 208.55 NaN 0.5541 NaN -0.5952 NaN 0.0020 NaN 0.0016 NaN 1.3440

2262 rows × 20 columns

OPRA Prevailing Pricing or Snapshot for the Selected Time, Underlying and Expiry#

OPRA Trade data is retrieved from the US_OPTIONS database (sample database US_OPTIONS_SAMPLE), together with static data including Underlying Symbol, Strike Price, Expiration Date and Call/Put Indicator.
Underlying equity pricing is retrieved from the US_COMP database (sample database US_COMP_SAMPLE).
Data is Filtered for the AAPL underlying (options via the AAPL % pattern, equity via SYMBOL_NAME AAPL) and for the expiration date 20270115.
The option trades are joined by time to the static records and to the prevailing underlying equity trade.
Because the data may have been published at different times before the target time, lookbacks are defined so the prevailing values are returned.
MONEYNESS is computed as Strike Price minus the underlying price.

import onetick.py as otp

# The snapshot time of interest.
snapshot_time = otp.dt(2025, 1, 3, 10)

# Prevailing options trades for all AAPL contracts, looking back up to 10 hours (36000s).
trd = otp.DataSource(db='US_OPTIONS_SAMPLE', tick_type='TRD', back_to_first_tick=36000)
trd = trd[['PRICE', 'SIZE']]

# Static (reference) data for the same contracts, looking back up to 1 day (86400s) for the prevailing record.
stat = otp.DataSource(db='US_OPTIONS_SAMPLE', tick_type='STAT', back_to_first_tick=86400)
stat = stat[['UNDERLYING_SYMBOL', 'EXPIRATION_DATE', 'STRIKE_PRICE', 'CALL_PUT_IND']]

# Restrict the static records to the selected expiration.
stat = stat.where(stat['EXPIRATION_DATE'] == '20270115')

# Join each prevailing option trade to the prevailing static record (asof join).
joined = otp.join_by_time([trd, stat])

# Keep only the last (prevailing) tick up to the snapshot time, per option contract.
joined = joined.last()

# Merge every matching AAPL option contract into a single stream.
options = otp.merge(
    [joined],
    symbols=otp.Symbols('US_OPTIONS_SAMPLE', pattern='AAPL %')
)

# Prevailing underlying equity trade for AAPL, looking back up to 10 hours (36000s).
underlying = otp.DataSource(db='US_COMP_SAMPLE', tick_type='TRD', back_to_first_tick=36000, symbols='AAPL')
underlying = underlying[['PRICE']]
underlying = underlying.rename({'PRICE': 'UNDERLYING_PRICE'})
underlying = underlying.last()

# Join the option snapshot to the prevailing underlying price (asof join).
result_src = otp.join_by_time([options, underlying])

# Moneyness is the strike price relative to the underlying price.
result_src['MONEYNESS'] = result_src['STRIKE_PRICE'] - result_src['UNDERLYING_PRICE']

# A zero-length window ending at the snapshot time returns the prevailing values as of that time.
result = otp.run(
    result_src,
    start=snapshot_time,
    end=snapshot_time,
    timezone='America/New_York'
)
result

OPRA Prevailing or Snapshot Options Prices at a specified time, for a specified underlying#

OPRA Trade data is retrieved from the US_OPTIONS database.
Here the sample database US_OPTIONS_SAMPLE is used.
Trades are joined by time to the static STAT records to obtain the Expiration Date, Strike Price and Call/Put Indicator.
The query returns the prevailing trade at the specified time; lookbacks are defined for both the Trades (up to 10 hours) and the Static Data (up to 1 day) so the prevailing values up to the specified time are returned.

import onetick.py as otp

# The snapshot time of interest.
snapshot_time = otp.dt(2025, 1, 3, 10)

# Prevailing options trades for all AAPL contracts, looking back up to 10 hours (36000s).
trd = otp.DataSource(db='US_OPTIONS_SAMPLE', tick_type='TRD', back_to_first_tick=36000)
trd = trd[['PRICE', 'SIZE']]

# Static (reference) data for the same contracts, looking back up to 1 day (86400s) for the prevailing record.
stat = otp.DataSource(db='US_OPTIONS_SAMPLE', tick_type='STAT', back_to_first_tick=86400)
stat = stat[['UNDERLYING_SYMBOL', 'EXPIRATION_DATE', 'STRIKE_PRICE', 'CALL_PUT_IND']]

# Join each prevailing trade to the prevailing static record (asof join).
joined = otp.join_by_time([trd, stat])

# Keep only the last (prevailing) tick up to the snapshot time.
joined = joined.last()

# Merge every matching AAPL contract into a single stream.
merged = otp.merge(
    [joined],
    symbols=otp.Symbols('US_OPTIONS_SAMPLE', pattern='AAPL %')
)

# A zero-length window ending at the snapshot time returns the prevailing values as of that time.
result = otp.run(
    merged,
    start=snapshot_time,
    end=snapshot_time,
    timezone='America/New_York'
)
result

Time

PRICE

SIZE

UNDERLYING_SYMBOL

EXPIRATION_DATE

STRIKE_PRICE

CALL_PUT_IND

0

2025-01-03 10:00:00

68.35

10

AAPL

20250103

175.0

C

1

2025-01-03 10:00:00

63.71

20

AAPL

20250103

180.0

C

2

2025-01-03 10:00:00

38.76

1

AAPL

20250103

205.0

C

3

2025-01-03 10:00:00

32.40

1

AAPL

20250103

210.0

C

4

2025-01-03 10:00:00

30.65

1

AAPL

20250103

212.5

C

633

2025-01-03 10:00:00

9.60

15

AAPL

20270115

190.0

P

634

2025-01-03 10:00:00

11.73

2

AAPL

20270115

200.0

P

635

2025-01-03 10:00:00

24.45

1

AAPL

20270115

240.0

P

636

2025-01-03 10:00:00

28.65

50

AAPL

20270115

250.0

P

637

2025-01-03 10:00:00

34.00

1

AAPL

20270115

260.0

P

638 rows x 7 columns

OPRA Options Chain Symbol Universe Retrieval#

Return the Options Chain of contracts for an OPRA underlying from the Symbol Universe.
Filtering with US_OPTIONS Option selects the symbols that correspond to OPRA Options.
Additionally filtering on UNDERLYING_SYMBOL equal to AAPL returns the option contracts for AAPL.

import onetick.py as otp

# The Symbol Universe static records are stored in the SYMBOL_UNIVERSE database, STAT tick type.
# The SYMBOL_NAME 'US_OPTIONS Option' groups the OPRA option contracts.
data = otp.DataSource(db='SYMBOL_UNIVERSE', tick_type='STAT')

# Filter to the OPRA options universe for the AAPL underlying.
data = data.where(data['UNDERLYING_SYMBOL'] == 'AAPL')

# Select the descriptive fields of interest.
data = data[['DB_NAME', 'DB_SYMBOL', 'NAME', 'SEC_TYPE',
             'UNDERLYING_SYMBOL', 'CALL_PUT_IND', 'STRIKE_PRICE', 'EXPIRATION_DATE']]

# Return first 1000 rows
data = data.limit(1000)

result = otp.run(
    data,
    start=otp.dt(2026, 6, 11),
    end=otp.dt(2026, 6, 12),
    timezone='UTC',
    symbols='US_OPTIONS Option'
)
result
Time DB_NAME DB_SYMBOL NAME SEC_TYPE UNDERLYING_SYMBOL CALL_PUT_IND STRIKE_PRICE EXPIRATION_DATE
0 2026-06-11 00:00:00 US_OPTIONS AAPL 260610C00250000 Option AAPL C 250.0 20260610
1 2026-06-11 00:00:00 US_OPTIONS AAPL 260610P00250000 Option AAPL P 250.0 20260610
2 2026-06-11 00:00:00 US_OPTIONS AAPL 260610C00255000 Option AAPL C 255.0 20260610
3 2026-06-11 00:00:00 US_OPTIONS AAPL 260610P00255000 Option AAPL P 255.0 20260610
4 2026-06-11 00:00:00 US_OPTIONS AAPL 260610C00260000 Option AAPL C 260.0 20260610
... ... ... ... ... ... ... ... ... ...
995 2026-06-11 20:00:00 US_OPTIONS AAPL 260702P00170000 Option AAPL P 170.0 20260702
996 2026-06-11 20:00:00 US_OPTIONS AAPL 260702C00175000 Option AAPL C 175.0 20260702
997 2026-06-11 20:00:00 US_OPTIONS AAPL 260702P00175000 Option AAPL P 175.0 20260702
998 2026-06-11 20:00:00 US_OPTIONS AAPL 260702C00180000 Option AAPL C 180.0 20260702
999 2026-06-11 20:00:00 US_OPTIONS AAPL 260702P00180000 Option AAPL P 180.0 20260702

1000 rows × 9 columns

OPRA Options Chain Symbol Universe Summary Retrieval#

Return the Options Chain of contracts for an OPRA underlying from the Symbol Universe.
Filtering with US_OPTIONS Option selects the symbols that correspond to OPRA Options.
Additionally filtering on UNDERLYING_SYMBOL equal to AAPL returns the option contracts for AAPL.

import onetick.py as otp

# The Symbol Universe static records are stored in the SYMBOL_UNIVERSE database, STAT tick type.
# The SYMBOL_NAME 'US_OPTIONS Option' groups the OPRA option contracts.
data = otp.DataSource(db='SYMBOL_UNIVERSE', tick_type='STAT')

# Filter to the OPRA options universe for the AAPL underlying.
data = data.where(data['UNDERLYING_SYMBOL'] == 'AAPL')

# Select the descriptive fields of interest.
data = data[['DB_NAME', 'DB_SYMBOL', 'NAME', 'SEC_TYPE',
             'UNDERLYING_SYMBOL', 'CALL_PUT_IND', 'STRIKE_PRICE', 'EXPIRATION_DATE']]

# Return first 1000 rows
data = data.limit(1000)

result = otp.run(
    data,
    start=otp.dt(2026, 6, 11),
    end=otp.dt(2026, 6, 12),
    timezone='UTC',
    symbols='US_OPTIONS Option'
)
result
Time DB_NAME DB_SYMBOL NAME SEC_TYPE UNDERLYING_SYMBOL CALL_PUT_IND STRIKE_PRICE EXPIRATION_DATE
0 2026-06-11 00:00:00 US_OPTIONS AAPL 260610C00250000 Option AAPL C 250.0 20260610
1 2026-06-11 00:00:00 US_OPTIONS AAPL 260610P00250000 Option AAPL P 250.0 20260610
2 2026-06-11 00:00:00 US_OPTIONS AAPL 260610C00255000 Option AAPL C 255.0 20260610
3 2026-06-11 00:00:00 US_OPTIONS AAPL 260610P00255000 Option AAPL P 255.0 20260610
4 2026-06-11 00:00:00 US_OPTIONS AAPL 260610C00260000 Option AAPL C 260.0 20260610
... ... ... ... ... ... ... ... ... ...
995 2026-06-11 20:00:00 US_OPTIONS AAPL 260702P00170000 Option AAPL P 170.0 20260702
996 2026-06-11 20:00:00 US_OPTIONS AAPL 260702C00175000 Option AAPL C 175.0 20260702
997 2026-06-11 20:00:00 US_OPTIONS AAPL 260702P00175000 Option AAPL P 175.0 20260702
998 2026-06-11 20:00:00 US_OPTIONS AAPL 260702C00180000 Option AAPL C 180.0 20260702
999 2026-06-11 20:00:00 US_OPTIONS AAPL 260702P00180000 Option AAPL P 180.0 20260702

1000 rows × 9 columns

CME Options Symbol Universe Retrieval#

Return the number of Options contracts for CME from the Symbol Universe.
Filtering with CME Option selects the symbols that correspond to CME Options.
CME populates UNDERLYING_SEC_TYPE, allowing Products to be grouped.

import onetick.py as otp

# The Symbol Universe static records are stored in the SYMBOL_UNIVERSE database, STAT tick type.
# The SYMBOL_NAME 'CME Option' groups the CME option contracts.
data = otp.DataSource(db='SYMBOL_UNIVERSE', tick_type='STAT')

# Count the contracts, grouped by database, security type, underlying security type and product code.
summary = data.agg(
    {'CONTRACT_COUNT': otp.agg.count()},
    group_by=['DB_NAME', 'SEC_TYPE', 'UNDERLYING_SEC_TYPE', 'PRODUCT_CODE']
)

# Return first 1000 rows
summary = summary.limit(1000)

result = otp.run(
    summary,
    start=otp.dt(2026, 6, 11),
    end=otp.dt(2026, 6, 12),
    timezone='UTC',
    symbols='CME Option'
)
result
Time DB_NAME SEC_TYPE UNDERLYING_SEC_TYPE PRODUCT_CODE CONTRACT_COUNT
0 2026-06-12 CME Option Agriculture 0HO 64
1 2026-06-12 CME Option Agriculture 0OC 710
2 2026-06-12 CME Option Agriculture 12C 56
3 2026-06-12 CME Option Agriculture 12K 60
4 2026-06-12 CME Option Agriculture 12S 66
... ... ... ... ... ... ...
883 2026-06-12 CME Option Metal W2S 672
884 2026-06-12 CME Option Metal W3S 370
885 2026-06-12 CME Option Metal W4S 148
886 2026-06-12 CME Option Other 0CA 492
887 2026-06-12 CME Option Other ECO 404

888 rows × 6 columns

CME Treasury Options Symbol Universe Retrieval#

Return Interest Rate Options contracts for CME from the Symbol Universe.
Filtering with CME Option selects the symbols that correspond to CME Options.
Filtering with UNDERLYING_SEC_TYPE set to Interest Rate restricts the results to Treasury Options.

import onetick.py as otp

# The Symbol Universe static records are stored in the SYMBOL_UNIVERSE database, STAT tick type.
# The SYMBOL_NAME 'CME Option' groups the CME option contracts.
data = otp.DataSource(db='SYMBOL_UNIVERSE', tick_type='STAT')

# Filter to Interest Rate (Treasury) options.
data = data.where(data['UNDERLYING_SEC_TYPE'] == 'Interest Rate')

# Select the descriptive fields of interest.
data = data[['DB_NAME', 'DB_SYMBOL', 'NAME', 'SEC_TYPE', 'UNDERLYING_SEC_TYPE',
             'PRODUCT_CODE', 'CALL_PUT_IND', 'STRIKE_PRICE', 'EXPIRATION_DATE']]

# Return first 1000 rows
data = data.limit(1000)

result = otp.run(
    data,
    start=otp.dt(2026, 6, 11),
    end=otp.dt(2026, 6, 12),
    timezone='UTC',
    symbols='CME Option'
)
result
Time DB_NAME DB_SYMBOL NAME SEC_TYPE UNDERLYING_SEC_TYPE PRODUCT_CODE CALL_PUT_IND STRIKE_PRICE EXPIRATION_DATE
0 2026-06-11 00:00:00.000 CME GB2\F26\108 US Treasury Bond Tuesday Week 2 Jun26 108 Call Option Interest Rate GB2 C 108.000 20260609
1 2026-06-11 00:00:00.000 CME GB2\F26\118 US Treasury Bond Tuesday Week 2 Jun26 118 Call Option Interest Rate GB2 C 118.000 20260609
2 2026-06-11 00:00:00.000 CME GB2\F26\127 US Treasury Bond Tuesday Week 2 Jun26 127 Call Option Interest Rate GB2 C 127.000 20260609
3 2026-06-11 00:00:00.000 CME GB2\F26\128.5 US Treasury Bond Tuesday Week 2 Jun26 128.5 Call Option Interest Rate GB2 C 128.500 20260609
4 2026-06-11 00:00:00.000 CME GB2\F26\97.5 US Treasury Bond Tuesday Week 2 Jun26 97.5 Call Option Interest Rate GB2 C 97.500 20260609
... ... ... ... ... ... ... ... ... ... ...
995 2026-06-11 21:00:00.934 CME VT3\F26\102.125 2-Year T-Note Monday Week 3 Jun26 102.125 Call Option Interest Rate VT3 C 102.125 20260615
996 2026-06-11 21:00:00.934 CME VF4\F26\108.75 5-Year T-Note Monday Week 4 Jun26 108.75 Call Option Interest Rate VF4 C 108.750 20260622
997 2026-06-11 21:00:00.934 CME VF4\R26\104.75 5-Year T-Note Monday Week 4 Jun26 104.75 Put Option Interest Rate VF4 P 104.750 20260622
998 2026-06-11 21:00:00.934 CME VF4\F26\109.25 5-Year T-Note Monday Week 4 Jun26 109.25 Call Option Interest Rate VF4 C 109.250 20260622
999 2026-06-11 21:00:00.934 CME VF4\F26\109.75 5-Year T-Note Monday Week 4 Jun26 109.75 Call Option Interest Rate VF4 C 109.750 20260622

1000 rows × 10 columns

CME Options Chain Symbol Universe Retrieval#

Return the Options Chain of contracts for a CME Product from the Symbol Universe.
Filtering with CME Option selects the symbols that correspond to CME Options.
Additionally filtering on PRODUCT_CODE equal to LO, the CME Product Code for Crude Oil.

import onetick.py as otp

# The Symbol Universe static records are stored in the SYMBOL_UNIVERSE database, STAT tick type.
# The SYMBOL_NAME 'CME Option' groups the CME option contracts.
data = otp.DataSource(db='SYMBOL_UNIVERSE', tick_type='STAT')

# Filter to the CME Crude Oil product (PRODUCT_CODE 'LO').
data = data.where(data['PRODUCT_CODE'] == 'LO')

# Select the descriptive fields of interest.
data = data[['DB_NAME', 'DB_SYMBOL', 'NAME', 'SEC_TYPE', 'UNDERLYING_SEC_TYPE',
             'PRODUCT_CODE', 'CALL_PUT_IND', 'STRIKE_PRICE', 'EXPIRATION_DATE']]

# Return first 1000 rows
data = data.limit(1000)

result = otp.run(
    data,
    start=otp.dt(2026, 6, 11),
    end=otp.dt(2026, 6, 12),
    timezone='UTC',
    symbols='CME Option'
)
result
Time DB_NAME DB_SYMBOL NAME SEC_TYPE UNDERLYING_SEC_TYPE PRODUCT_CODE CALL_PUT_IND STRIKE_PRICE EXPIRATION_DATE
0 2026-06-11 21:00:00.809 CME LO\X29\113.5 Crude Oil Dec29 113.5 Put Option Energy LO P 113.5 20291114
1 2026-06-11 21:00:00.809 CME LO\R29\75 Crude Oil Jun29 75 Put Option Energy LO P 75.0 20290517
2 2026-06-11 21:00:00.809 CME LO\L31\4 Crude Oil Dec31 4 Call Option Energy LO C 4.0 20311117
3 2026-06-11 21:00:00.809 CME LO\X27\19.5 Crude Oil Dec27 19.5 Put Option Energy LO P 19.5 20271116
4 2026-06-11 21:00:00.809 CME LO\L29\90.5 Crude Oil Dec29 90.5 Call Option Energy LO C 90.5 20291114
... ... ... ... ... ... ... ... ... ... ...
995 2026-06-11 21:00:01.109 CME LO\R28\152.5 Crude Oil Jun28 152.5 Put Option Energy LO P 152.5 20280517
996 2026-06-11 21:00:01.109 CME LO\F27\66.5 Crude Oil Jun27 66.5 Call Option Energy LO C 66.5 20270517
997 2026-06-11 21:00:01.109 CME LO\L29\105.5 Crude Oil Dec29 105.5 Call Option Energy LO C 105.5 20291114
998 2026-06-11 21:00:01.109 CME LO\R30\24 Crude Oil Jun30 24 Put Option Energy LO P 24.0 20300516
999 2026-06-11 21:00:01.109 CME LO\X32\127.5 Crude Oil Dec32 127.5 Put Option Energy LO P 127.5 20321116

1000 rows × 10 columns

Query Options Volume Open Interest And Greeks Analysis#

Options Volume, Open Interest, and Greeks Analysis by Expiration and Call/Put.

import onetick.py as otp

# Define the time range
start_time = otp.dt(2025, 1, 20)
end_time = otp.dt(2025, 1, 28)

# Create a DataSource for all AAPL option contracts
options_day = otp.DataSource(
    db='US_OPTIONS_EOD_SAMPLE',
    tick_type='DAY'
)

# Merge all AAPL option contracts into a single stream
merged_options = otp.merge(
    [options_day],
    symbols=otp.Symbols('US_OPTIONS_EOD_SAMPLE', pattern='AAPL%')
)

# Aggregate metrics across all contracts
agg_result = merged_options.agg(
    {
        'TOTAL_OPEN_INTEREST': otp.agg.sum('OPEN_INT'),
        'TOTAL_VOLUME': otp.agg.sum('VOLUME'),
        'AVG_UNDERLYING_PRICE': otp.agg.average('UNDERLYING_PRICE'),
        'AVG_IMPLIED_VOL': otp.agg.average('IMP_VOLATILITY'),
        'AVG_DELTA': otp.agg.average('DELTA'),
        'AVG_GAMMA': otp.agg.average('GAMMA'),
        'AVG_THETA': otp.agg.average('THETA'),
        'AVG_VEGA': otp.agg.average('VEGA'),
        'CONTRACT_COUNT': otp.agg.count()
    }
)

# Run the query
df = otp.run(
    agg_result,
    start=start_time,
    end=end_time,
    timezone='UTC'
)

# The result is a single DataFrame with one row
df
Time TOTAL_OPEN_INTEREST TOTAL_VOLUME AVG_UNDERLYING_PRICE AVG_IMPLIED_VOL AVG_DELTA AVG_GAMMA AVG_THETA AVG_VEGA CONTRACT_COUNT
0 2025-01-28 21880286.0 5846439 224.641871 0.400747 0.087597 0.00411 -0.028074 0.260932 11460