flow alerts multiple tickers v3
import os
import httpx
import polars as pl
from pathlib import Path
from datetime import datetime
from lets_plot import *
LetsPlot.setup_html()
uw_token = os.environ['UW_TOKEN']
headers = {'Accept': 'application/json, text/plain', 'Authorization': uw_token}
pl.Config.set_tbl_cols(40)
pl.Config.set_fmt_str_lengths(40)
polars.config.Config
Get recent Flow Alert-type trades for single-name equities
- Data from option-trades/flow alerts endpoint: https://api.unusualwhales.com/docs#/operations/PublicApi.OptionTradeController.flow_alerts
-
GET:
https://api.unusualwhales.com/api/option-trades/flow-alerts
-
We use a
params
dictionary to send some additional criteria to the endpoint: -
issue_types[]
=['Common Stock', 'ADR']
to skip ETFs and indexes -
min_dte
= 1, which means we will always skip 0dte trades -
min_premium
= 200000, which means only trades involving $200K or more will be returned -
min_volume_oi_ratio
= 1.0, which helps us narrow down the universe to potentially opening trades -
rule_name[]
=['RepeatedHits', 'RepeatedHitsAscendingFill', 'RepeatedHitsDescendingFill']
to focus specifically on "urgent" multi-exchange screen trades
We execute the API call in the next cell:
url = 'https://api.unusualwhales.com/api/option-trades/flow-alerts'
params = {
'issue_types[]': ['Common Stock', 'ADR'],
'min_dte': 1,
'min_premium': 200000,
'min_volume_oi_ratio': 1.0,
'rule_name[]': ['RepeatedHits', 'RepeatedHitsAscendingFill', 'RepeatedHitsDescendingFill'],
'limit': 250,
}
rsp = httpx.get(url, headers=headers, params=params)
Use a DataFrame package (like polars
) to process
the response
-
First step is to ingest the
json
response into apolars
DataFrame - Then we will convert some columns that are expressed as strings into floats
- Finally we will display these converted columns to visually confirm a handful of results
Let's have at it:
raw_df = pl.DataFrame(rsp.json()['data'])
clean_df = (
raw_df
.with_columns(
pl.col('price').cast(pl.Float64),
pl.col('total_ask_side_prem').cast(pl.Float64),
pl.col('total_bid_side_prem').cast(pl.Float64),
pl.col('total_premium').cast(pl.Float64),
pl.col('underlying_price').cast(pl.Float64),
pl.col('volume_oi_ratio').cast(pl.Float64),
)
)
(
clean_df
.select(
[
'option_chain', 'price', 'total_ask_side_prem',
'total_bid_side_prem', 'total_premium',
'underlying_price', 'volume_oi_ratio'
]
)
)
option_chain | price | total_ask_side_prem | total_bid_side_prem | total_premium | underlying_price | volume_oi_ratio |
---|---|---|---|---|---|---|
str | f64 | f64 | f64 | f64 | f64 | f64 |
"TSLA250509P00280000" | 1.9 | 381524.0 | 0.0 | 396724.0 | 285.065 | 13.253292 |
"TSLA250516C00285000" | 10.9 | 498130.0 | 0.0 | 498130.0 | 285.41 | 2.340311 |
"TSLA250606P00290000" | 21.1 | 0.0 | 122380.0 | 552820.0 | 285.53 | 1.121166 |
"TSLA250516C00300000" | 5.35 | 267500.0 | 0.0 | 267500.0 | 286.5 | 2.000326 |
"TSLA250516C00305000" | 4.0 | 5200.0 | 802400.0 | 808000.0 | 286.33 | 2.004885 |
… | … | … | … | … | … | … |
"NVDA250530C00110000" | 11.5 | 1.034959e6 | 0.0 | 1.034959e6 | 117.7 | 1.138147 |
"APP250509P00325000" | 5.2 | 136240.0 | 49920.0 | 260000.0 | 343.0 | 53.878788 |
"APP250509P00325000" | 5.0 | 28500.0 | 80500.0 | 200000.0 | 342.9 | 12.121212 |
"AFRM270115P00040000" | 10.0 | 124000.0 | 376000.0 | 500000.0 | 53.94 | 2.55102 |
"BAC251121C00042000" | 3.4 | 0.0 | 340000.0 | 340000.0 | 41.435 | 1.960784 |
Use our knowledge of total_size
vs.
open_interest
for more filtering
- We can be a LOT more certain that a trade was an opener if the size of the trade itself is greater than the open interest on that contract
- So with that in mind, let's filter out trades where the size of the trade is less than the open interest
Filtering a DataFrame in polars
is pretty easy so
let's knock that out next:
opener_df = (
clean_df.filter(pl.col('total_size') > pl.col('open_interest'))
)
opener_df.drop(['id', 'rule_id']) # no need to see ID columns
end_time | has_multileg | option_chain | total_size | strike | iv_end | volume_oi_ratio | er_time | has_floor | volume | next_earnings_date | open_interest | all_opening_trades | total_ask_side_prem | alert_rule | sector | expiry | has_singleleg | ask | underlying_price | total_premium | marketcap | iv_start | expiry_count | price | start_time | has_sweep | created_at | type | bid | ticker | total_bid_side_prem | trade_count |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | bool | str | i64 | str | str | f64 | str | bool | i64 | str | i64 | bool | f64 | str | str | str | bool | str | f64 | f64 | str | str | i64 | f64 | i64 | bool | str | str | str | str | f64 | i64 |
1746733742305 | false | "LYFT250509C00015000" | 12000 | "15" | "2.7499258716579" | 4.430876 | "postmarket" | false | 13461 | "2025-05-08" | 3038 | false | 0.0 | "RepeatedHits" | "Technology" | "2025-05-09" | true | "0.2" | 13.06 | 216000.0 | "5155327101" | "2.7499258716579" | 1 | 0.18 | 1746733742304 | false | "2025-05-08T19:49:11.468550Z" | "call" | "0.18" | "LYFT" | 0.0 | 5 |
1746733742381 | false | "LYFT250509C00013500" | 7000 | "13.5" | "2.85380892701926" | 5.363529 | "postmarket" | false | 9059 | "2025-05-08" | 1689 | false | 0.0 | "RepeatedHits" | "Technology" | "2025-05-09" | true | "0.63" | 13.06 | 413000.0 | "5155327101" | "2.85380892701926" | 1 | 0.59 | 1746733742304 | false | "2025-05-08T19:49:11.468550Z" | "call" | "0.59" | "LYFT" | 0.0 | 5 |
1746733295343 | false | "CRWD250718P00410000" | 479 | "410" | "0.456593234079975" | 5.514851 | "unknown" | false | 557 | "2025-06-03" | 101 | false | 1.15439e6 | "RepeatedHits" | "Technology" | "2025-07-18" | true | "24.1" | 431.23 | 1.15439e6 | "104731475306" | "0.456593234079975" | 1 | 24.1 | 1746733295337 | false | "2025-05-08T19:41:45.871525Z" | "put" | "22.6" | "CRWD" | 0.0 | 34 |
1746733295350 | false | "CRWD250718P00430000" | 277 | "430" | "0.442852946127175" | 3.654762 | "unknown" | false | 307 | "2025-06-03" | 84 | false | 908534.0 | "RepeatedHitsDescendingFill" | "Technology" | "2025-07-18" | true | "32.8" | 431.23 | 908534.0 | "104731475306" | "0.442852946127175" | 1 | 32.8 | 1746733295337 | false | "2025-05-08T19:41:45.871525Z" | "put" | "31.35" | "CRWD" | 0.0 | 24 |
1746732668480 | false | "NFLX250523C01125000" | 105 | "1125" | "0.355169066554319" | 2.386364 | "unknown" | false | 105 | "2025-07-17" | 44 | false | 492450.0 | "RepeatedHits" | "Communication Services" | "2025-05-23" | true | "46.9" | 1150.3 | 492450.0 | "491838637666" | "0.355169066554319" | 1 | 46.9 | 1746732668477 | false | "2025-05-08T19:31:15.744289Z" | "call" | "46.85" | "NFLX" | 0.0 | 11 |
… | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … |
1746711412822 | false | "CNR251219P00070000" | 531 | "70" | "0.411152476000258" | 3.8 | "premarket" | false | 532 | "2025-05-08" | 140 | false | 301210.0 | "RepeatedHitsAscendingFill" | "Industrials" | "2025-12-19" | true | "11.1" | 70.72 | 451665.0 | "3963197320" | "0.406548902174686" | 1 | 8.51 | 1746711412789 | true | "2025-05-08T13:36:59.128171Z" | "put" | "7.4" | "CNR" | 150455.0 | 19 |
1746711319276 | false | "APP250509P00325000" | 500 | "325" | "1.74103393631172" | 53.878788 | "unknown" | false | 1778 | "2025-08-06" | 33 | false | 136240.0 | "RepeatedHits" | "Technology" | "2025-05-09" | true | "5.7" | 343.0 | 260000.0 | "93374131884" | "1.74103393631172" | 1 | 5.2 | 1746711319263 | true | "2025-05-08T13:35:26.589864Z" | "put" | "5" | "APP" | 49920.0 | 12 |
1746711041414 | false | "APP250509P00325000" | 400 | "325" | "1.70225198351738" | 12.121212 | "unknown" | false | 400 | "2025-08-06" | 33 | false | 28500.0 | "RepeatedHits" | "Technology" | "2025-05-09" | true | "9.4" | 342.9 | 200000.0 | "93374131884" | "1.70225198351738" | 1 | 5.0 | 1746711041370 | true | "2025-05-08T13:30:48.415109Z" | "put" | "2.3" | "APP" | 80500.0 | 22 |
1746711035288 | false | "AFRM270115P00040000" | 500 | "40" | "0.689067001480113" | 2.55102 | "postmarket" | false | 500 | "2025-05-08" | 196 | false | 124000.0 | "RepeatedHits" | "Technology" | "2027-01-15" | true | "11.4" | 53.94 | 500000.0 | "14289710073" | "0.689067001480113" | 1 | 10.0 | 1746711035249 | false | "2025-05-08T13:30:42.056040Z" | "put" | "9.8" | "AFRM" | 376000.0 | 14 |
1746711021305 | false | "BAC251121C00042000" | 1000 | "42" | "0.298968693127278" | 1.960784 | "premarket" | false | 1000 | "2025-07-14" | 510 | false | 0.0 | "RepeatedHits" | "Financial Services" | "2025-11-21" | true | "3.45" | 41.435 | 340000.0 | "308279688896" | "0.298968693127278" | 1 | 3.4 | 1746711021298 | true | "2025-05-08T13:30:27.667608Z" | "call" | "3.4" | "BAC" | 340000.0 | 29 |
Can we add some informative and convenient calculations for display?
- Execution time (eastern time zone)
- % Bid and % Ask
- Bullish, Bearish, or Mixed (we'll use 80% as our side threshold)
- Days To Expiration
- Strike and Type Combined
Lots of column manipulation in the polars
DataFrame
in the next cell, but hopefully the "clustering" of these tasks
makes it relatively easy to see what is happening each step of
the way:
display_df = (
opener_df
.with_columns(
pl.col('created_at').str.strptime(pl.Datetime, '%Y-%m-%dT%H:%M:%S%.fZ').alias('created_at_dt')
)
.with_columns(
pl.col('created_at_dt').dt.convert_time_zone('America/New_York').alias('created_at_dt_east')
)
.with_columns(
pl.col('created_at_dt_east').dt.strftime('%Y-%m-%d %I:%M:%S %p').alias('created_at_str_east')
)
.with_columns(
(pl.col('total_ask_side_prem') / pl.col('total_premium')).round(4).alias('pct_ask'),
(pl.col('total_bid_side_prem') / pl.col('total_premium')).round(4).alias('pct_bid')
)
.with_columns(
pl.when(pl.col('pct_ask') > 0.80).then(pl.lit('ask'))
.when(pl.col('pct_bid') > 0.80).then(pl.lit('bid'))
.otherwise(pl.lit('mixed')).alias('side')
)
.with_columns(
(
((pl.col('pct_ask').round(2) * 100).cast(pl.Utf8)) +
'% Ask / ' +
((pl.col('pct_bid').round(2) * 100).cast(pl.Utf8)) +
'% Bid'
).str.replace_all('.0%', '%').alias('pct_ask_bid_str')
)
.with_columns(
pl.when((pl.col('side') == 'ask') & (pl.col('type') == 'call')).then(pl.lit('bullish'))
.when((pl.col('side') == 'ask') & (pl.col('type') == 'put')).then(pl.lit('bearish'))
.when((pl.col('side') == 'bid') & (pl.col('type') == 'call')).then(pl.lit('bearish'))
.when((pl.col('side') == 'bid') & (pl.col('type') == 'put')).then(pl.lit('bullish'))
.otherwise(pl.lit('mixed')).alias('direction')
)
.with_columns(
pl.col('expiry').str.strptime(pl.Date, '%Y-%m-%d').alias('expiry_date')
)
.with_columns(
(pl.col('expiry_date') - pl.lit(datetime.today().date())).alias('dte')
)
.with_columns(
(pl.col('dte').dt.total_milliseconds() / 86_400_000).alias('dte_days')
)
.with_columns(
pl.col('dte_days').cast(pl.Int64).alias('dte_days_int')
)
.with_columns(
(
pl.when(pl.col('type') == 'call')
.then(pl.col('strike') + 'C')
.otherwise(pl.col('strike') + 'P')
).alias('strike_type')
)
.with_columns(
(
pl.col('ticker') + ' ' +
pl.col('strike_type') + ' ' +
(pl.col('expiry_date').dt.strftime('%m/%d/%Y')).alias('expiry_date_str')
).alias('contract_look_up_format')
)
)
display_df.drop(['id', 'rule_id']) # no need to see ID columns
end_time | has_multileg | option_chain | total_size | strike | iv_end | volume_oi_ratio | er_time | has_floor | volume | next_earnings_date | open_interest | all_opening_trades | total_ask_side_prem | alert_rule | sector | expiry | has_singleleg | ask | underlying_price | … | created_at | type | bid | ticker | total_bid_side_prem | trade_count | created_at_dt | created_at_dt_east | created_at_str_east | pct_ask | pct_bid | side | pct_ask_bid_str | direction | expiry_date | dte | dte_days | dte_days_int | strike_type | contract_look_up_format |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | bool | str | i64 | str | str | f64 | str | bool | i64 | str | i64 | bool | f64 | str | str | str | bool | str | f64 | … | str | str | str | str | f64 | i64 | datetime[ns] | datetime[ns, America/New_York] | str | f64 | f64 | str | str | str | date | duration[ms] | f64 | i64 | str | str |
1746733742305 | false | "LYFT250509C00015000" | 12000 | "15" | "2.7499258716579" | 4.430876 | "postmarket" | false | 13461 | "2025-05-08" | 3038 | false | 0.0 | "RepeatedHits" | "Technology" | "2025-05-09" | true | "0.2" | 13.06 | … | "2025-05-08T19:49:11.468550Z" | "call" | "0.18" | "LYFT" | 0.0 | 5 | 2025-05-08 19:49:11.468550 | 2025-05-08 15:49:11.468550 EDT | "2025-05-08 03:49:11 PM" | 0.0 | 0.0 | "mixed" | "0% Ask / 0% Bid" | "mixed" | 2025-05-09 | 1d | 1.0 | 1 | "15C" | "LYFT 15C 05/09/2025" |
1746733742381 | false | "LYFT250509C00013500" | 7000 | "13.5" | "2.85380892701926" | 5.363529 | "postmarket" | false | 9059 | "2025-05-08" | 1689 | false | 0.0 | "RepeatedHits" | "Technology" | "2025-05-09" | true | "0.63" | 13.06 | … | "2025-05-08T19:49:11.468550Z" | "call" | "0.59" | "LYFT" | 0.0 | 5 | 2025-05-08 19:49:11.468550 | 2025-05-08 15:49:11.468550 EDT | "2025-05-08 03:49:11 PM" | 0.0 | 0.0 | "mixed" | "0% Ask / 0% Bid" | "mixed" | 2025-05-09 | 1d | 1.0 | 1 | "13.5C" | "LYFT 13.5C 05/09/2025" |
1746733295343 | false | "CRWD250718P00410000" | 479 | "410" | "0.456593234079975" | 5.514851 | "unknown" | false | 557 | "2025-06-03" | 101 | false | 1.15439e6 | "RepeatedHits" | "Technology" | "2025-07-18" | true | "24.1" | 431.23 | … | "2025-05-08T19:41:45.871525Z" | "put" | "22.6" | "CRWD" | 0.0 | 34 | 2025-05-08 19:41:45.871525 | 2025-05-08 15:41:45.871525 EDT | "2025-05-08 03:41:45 PM" | 1.0 | 0.0 | "ask" | "100% Ask / 0% Bid" | "bearish" | 2025-07-18 | 71d | 71.0 | 71 | "410P" | "CRWD 410P 07/18/2025" |
1746733295350 | false | "CRWD250718P00430000" | 277 | "430" | "0.442852946127175" | 3.654762 | "unknown" | false | 307 | "2025-06-03" | 84 | false | 908534.0 | "RepeatedHitsDescendingFill" | "Technology" | "2025-07-18" | true | "32.8" | 431.23 | … | "2025-05-08T19:41:45.871525Z" | "put" | "31.35" | "CRWD" | 0.0 | 24 | 2025-05-08 19:41:45.871525 | 2025-05-08 15:41:45.871525 EDT | "2025-05-08 03:41:45 PM" | 1.0 | 0.0 | "ask" | "100% Ask / 0% Bid" | "bearish" | 2025-07-18 | 71d | 71.0 | 71 | "430P" | "CRWD 430P 07/18/2025" |
1746732668480 | false | "NFLX250523C01125000" | 105 | "1125" | "0.355169066554319" | 2.386364 | "unknown" | false | 105 | "2025-07-17" | 44 | false | 492450.0 | "RepeatedHits" | "Communication Services" | "2025-05-23" | true | "46.9" | 1150.3 | … | "2025-05-08T19:31:15.744289Z" | "call" | "46.85" | "NFLX" | 0.0 | 11 | 2025-05-08 19:31:15.744289 | 2025-05-08 15:31:15.744289 EDT | "2025-05-08 03:31:15 PM" | 1.0 | 0.0 | "ask" | "100% Ask / 0% Bid" | "bullish" | 2025-05-23 | 15d | 15.0 | 15 | "1125C" | "NFLX 1125C 05/23/2025" |
… | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … |
1746711412822 | false | "CNR251219P00070000" | 531 | "70" | "0.411152476000258" | 3.8 | "premarket" | false | 532 | "2025-05-08" | 140 | false | 301210.0 | "RepeatedHitsAscendingFill" | "Industrials" | "2025-12-19" | true | "11.1" | 70.72 | … | "2025-05-08T13:36:59.128171Z" | "put" | "7.4" | "CNR" | 150455.0 | 19 | 2025-05-08 13:36:59.128171 | 2025-05-08 09:36:59.128171 EDT | "2025-05-08 09:36:59 AM" | 0.6669 | 0.3331 | "mixed" | "67% Ask / 33% Bid" | "mixed" | 2025-12-19 | 225d | 225.0 | 225 | "70P" | "CNR 70P 12/19/2025" |
1746711319276 | false | "APP250509P00325000" | 500 | "325" | "1.74103393631172" | 53.878788 | "unknown" | false | 1778 | "2025-08-06" | 33 | false | 136240.0 | "RepeatedHits" | "Technology" | "2025-05-09" | true | "5.7" | 343.0 | … | "2025-05-08T13:35:26.589864Z" | "put" | "5" | "APP" | 49920.0 | 12 | 2025-05-08 13:35:26.589864 | 2025-05-08 09:35:26.589864 EDT | "2025-05-08 09:35:26 AM" | 0.524 | 0.192 | "mixed" | "52% Ask / 19% Bid" | "mixed" | 2025-05-09 | 1d | 1.0 | 1 | "325P" | "APP 325P 05/09/2025" |
1746711041414 | false | "APP250509P00325000" | 400 | "325" | "1.70225198351738" | 12.121212 | "unknown" | false | 400 | "2025-08-06" | 33 | false | 28500.0 | "RepeatedHits" | "Technology" | "2025-05-09" | true | "9.4" | 342.9 | … | "2025-05-08T13:30:48.415109Z" | "put" | "2.3" | "APP" | 80500.0 | 22 | 2025-05-08 13:30:48.415109 | 2025-05-08 09:30:48.415109 EDT | "2025-05-08 09:30:48 AM" | 0.1425 | 0.4025 | "mixed" | "14.000000000000002% Ask / 40% Bid" | "mixed" | 2025-05-09 | 1d | 1.0 | 1 | "325P" | "APP 325P 05/09/2025" |
1746711035288 | false | "AFRM270115P00040000" | 500 | "40" | "0.689067001480113" | 2.55102 | "postmarket" | false | 500 | "2025-05-08" | 196 | false | 124000.0 | "RepeatedHits" | "Technology" | "2027-01-15" | true | "11.4" | 53.94 | … | "2025-05-08T13:30:42.056040Z" | "put" | "9.8" | "AFRM" | 376000.0 | 14 | 2025-05-08 13:30:42.056040 | 2025-05-08 09:30:42.056040 EDT | "2025-05-08 09:30:42 AM" | 0.248 | 0.752 | "mixed" | "25% Ask / 75% Bid" | "mixed" | 2027-01-15 | 617d | 617.0 | 617 | "40P" | "AFRM 40P 01/15/2027" |
1746711021305 | false | "BAC251121C00042000" | 1000 | "42" | "0.298968693127278" | 1.960784 | "premarket" | false | 1000 | "2025-07-14" | 510 | false | 0.0 | "RepeatedHits" | "Financial Services" | "2025-11-21" | true | "3.45" | 41.435 | … | "2025-05-08T13:30:27.667608Z" | "call" | "3.4" | "BAC" | 340000.0 | 29 | 2025-05-08 13:30:27.667608 | 2025-05-08 09:30:27.667608 EDT | "2025-05-08 09:30:27 AM" | 0.0 | 1.0 | "bid" | "0% Ask / 100% Bid" | "bearish" | 2025-11-21 | 197d | 197.0 | 197 | "42C" | "BAC 42C 11/21/2025" |
Select specific columns for display to the end user
- We've got all the data a trader might want to see at-a-glance, let's cut down this polars DataFrame to fewer columns so it's easier to read
- Then let's export the DataFrame to a list of lists for easy display
Final manipulation before we are finished here! Let's go:
final_df = (
display_df
.select(
[
'created_at_str_east', 'contract_look_up_format',
'total_size', 'price', 'total_premium', 'pct_ask_bid_str',
'direction', 'underlying_price', 'dte_days_int'
]
)
)
final_df
created_at_str_east | contract_look_up_format | total_size | price | total_premium | pct_ask_bid_str | direction | underlying_price | dte_days_int |
---|---|---|---|---|---|---|---|---|
str | str | i64 | f64 | f64 | str | str | f64 | i64 |
"2025-05-08 03:49:11 PM" | "LYFT 15C 05/09/2025" | 12000 | 0.18 | 216000.0 | "0% Ask / 0% Bid" | "mixed" | 13.06 | 1 |
"2025-05-08 03:49:11 PM" | "LYFT 13.5C 05/09/2025" | 7000 | 0.59 | 413000.0 | "0% Ask / 0% Bid" | "mixed" | 13.06 | 1 |
"2025-05-08 03:41:45 PM" | "CRWD 410P 07/18/2025" | 479 | 24.1 | 1.15439e6 | "100% Ask / 0% Bid" | "bearish" | 431.23 | 71 |
"2025-05-08 03:41:45 PM" | "CRWD 430P 07/18/2025" | 277 | 32.8 | 908534.0 | "100% Ask / 0% Bid" | "bearish" | 431.23 | 71 |
"2025-05-08 03:31:15 PM" | "NFLX 1125C 05/23/2025" | 105 | 46.9 | 492450.0 | "100% Ask / 0% Bid" | "bullish" | 1150.3 | 15 |
… | … | … | … | … | … | … | … | … |
"2025-05-08 09:36:59 AM" | "CNR 70P 12/19/2025" | 531 | 8.51 | 451665.0 | "67% Ask / 33% Bid" | "mixed" | 70.72 | 225 |
"2025-05-08 09:35:26 AM" | "APP 325P 05/09/2025" | 500 | 5.2 | 260000.0 | "52% Ask / 19% Bid" | "mixed" | 343.0 | 1 |
"2025-05-08 09:30:48 AM" | "APP 325P 05/09/2025" | 400 | 5.0 | 200000.0 | "14.000000000000002% Ask / 40% Bid" | "mixed" | 342.9 | 1 |
"2025-05-08 09:30:42 AM" | "AFRM 40P 01/15/2027" | 500 | 10.0 | 500000.0 | "25% Ask / 75% Bid" | "mixed" | 53.94 | 617 |
"2025-05-08 09:30:27 AM" | "BAC 42C 11/21/2025" | 1000 | 3.4 | 340000.0 | "0% Ask / 100% Bid" | "bearish" | 41.435 | 197 |
# Export the results to CSV (for human review if interested)
filename = datetime.today().date().strftime('%Y-%m-%d') + '-flow-alert-openers.csv'
output_path = Path('raw_data') / filename
final_df.write_csv(output_path)