<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: tomasz dobrowolski</title>
    <description>The latest articles on DEV Community by tomasz dobrowolski (@tomasz_dobrowolski_35d32c).</description>
    <link>https://dev.to/tomasz_dobrowolski_35d32c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3827438%2F3874bebe-b7b5-43b5-a0e5-53b9c79dbe7b.png</url>
      <title>DEV Community: tomasz dobrowolski</title>
      <link>https://dev.to/tomasz_dobrowolski_35d32c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tomasz_dobrowolski_35d32c"/>
    <language>en</language>
    <item>
      <title>Quant Options Backtesting on Point-in-Time Data: The Complete Guide</title>
      <dc:creator>tomasz dobrowolski</dc:creator>
      <pubDate>Mon, 08 Jun 2026 18:45:07 +0000</pubDate>
      <link>https://dev.to/tomasz_dobrowolski_35d32c/quant-options-backtesting-on-point-in-time-data-the-complete-guide-3cea</link>
      <guid>https://dev.to/tomasz_dobrowolski_35d32c/quant-options-backtesting-on-point-in-time-data-the-complete-guide-3cea</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally published on the &lt;a href="https://flashalpha.com/articles/complete-guide-quant-options-backtesting" rel="noopener noreferrer"&gt;FlashAlpha Research blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Backtesting options strategies is structurally harder than backtesting equity momentum or fixed-income carry. The payoff is path-dependent. The instrument set re-generates every week. Fills are wide and asymmetric. And the data problem (point-in-time chains, computed dealer exposure, arbitrage-free surfaces) is usually bigger than the signal problem.&lt;/p&gt;

&lt;p&gt;This guide is written for a quant or systematic developer who wants to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Research a dealer-positioning, VRP, or dispersion signal on real historical data.&lt;/li&gt;
&lt;li&gt;Build a candidate set across the options universe, not just one ticker.&lt;/li&gt;
&lt;li&gt;Run a fill model that reflects what you would have actually paid.&lt;/li&gt;
&lt;li&gt;Avoid the ten systematic mistakes that inflate backtested Sharpe before live trading erases the edge.&lt;/li&gt;
&lt;li&gt;Connect the same code to a live production signal without rewriting the data layer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The historical endpoint set covers: GEX/DEX/VEX/CHEX exposure replay (&lt;code&gt;/v1/exposure/*?date=&lt;/code&gt;), VRP time-series and percentiles (&lt;code&gt;/v1/vrp/{t}/history&lt;/code&gt;), full option chains with greeks and IV at minute resolution (&lt;code&gt;/v1/options/{t}?date=&lt;/code&gt;), IV surfaces and SVI parameters (&lt;code&gt;/v1/adv_volatility&lt;/code&gt;), and the universe screener (&lt;code&gt;POST /v1/screener&lt;/code&gt;). Everything replays since 2018. The response shape is identical to the live endpoints: add or remove the &lt;code&gt;date&lt;/code&gt; parameter to switch modes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What systematic options research actually needs
&lt;/h2&gt;

&lt;p&gt;Three requirements separate a backtest you can trust from a backtest that lies to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Point-in-time data
&lt;/h3&gt;

&lt;p&gt;A request for &lt;code&gt;/v1/exposure/gex/SPY?date=2020-03-16T14:30:00Z&lt;/code&gt; must return the state of the book exactly as it existed at that minute. Open interest, greeks, and computed gamma exposure as of 14:30:00, not end-of-day OI stamped back, not a forward fill from the prior session.&lt;/p&gt;

&lt;p&gt;This matters for two concrete reasons. First, dealer-positioning signals use OI to compute GEX, and end-of-day OI is not known at 14:30. Second, option greeks depend on implied vol, which moves continuously, so a 15:30 vol observation is not the same as a 14:30 vol observation on a volatile day. Any historical dataset that stamps a single daily value and calls it "intraday" is baking lookahead bias into every signal that uses it.&lt;/p&gt;

&lt;p&gt;Gamma exposure is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GEX(t) = Σ_k OI_k(t) · Γ_k(S_t, σ_k(t), T_k − t)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each term depends on spot S_t, the per-strike IV σ_k(t), and time to expiry at exactly time t. Substituting end-of-day values for any of these introduces forward-looking information.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. No lookahead bias
&lt;/h3&gt;

&lt;p&gt;Lookahead is not only about timestamps. It also lives in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OI revision.&lt;/strong&gt; Exchanges publish preliminary OI and revise it the next morning. A historical dataset assembled from the revised numbers makes yesterday's GEX look different from what any live system would have computed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Methodology drift.&lt;/strong&gt; If the analytics vendor updates the GEX formula, historical replays under the new formula produce different numbers than a live system running the old formula would have produced. Archive raw responses if you need bit-exact reproducibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Surface parameterization.&lt;/strong&gt; SVI calibration parameters (a, b, ρ, m, σ) are an end-of-day fit in this dataset. An intraday request returns the most recent prior EOD SVI fit. If your signal uses the SVI latent at minute resolution, you have one observation per trading day, not per minute. Use the minute-level surface grid for intraday surface features, and SVI params for daily cross-sectional work.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Same schema, research to production
&lt;/h3&gt;

&lt;p&gt;The single most underrated property of a historical API: research and production should call the same function. If your backtest calls &lt;code&gt;hx.exposure_summary("SPY", at="2022-06-15T14:30:00")&lt;/code&gt; and your live signal calls &lt;code&gt;fa.exposure_summary("SPY")&lt;/code&gt; and the response shapes differ, you will discover the discrepancy the first time the live system hits a field the backtest parser never saw. Every undocumented difference between historical and live response shapes is a production incident waiting to happen.&lt;/p&gt;

&lt;p&gt;The FlashAlpha API uses the same JSON schema for both modes. The &lt;code&gt;date&lt;/code&gt; parameter controls replay; omit it for live. One exception: the historical option-quote endpoint returns a flat array with renamed fields (&lt;code&gt;implied_vol&lt;/code&gt; instead of &lt;code&gt;iv&lt;/code&gt;, &lt;code&gt;open_interest&lt;/code&gt; instead of &lt;code&gt;oi&lt;/code&gt;) and historical-only fields (&lt;code&gt;iv_bid&lt;/code&gt;, &lt;code&gt;iv_ask&lt;/code&gt;, &lt;code&gt;vanna&lt;/code&gt;, &lt;code&gt;charm&lt;/code&gt;, &lt;code&gt;rho&lt;/code&gt;). Write a thin adapter and test it against both modes before you backtest at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data: what the historical API covers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dealer exposure replay (GEX/DEX/VEX/CHEX)
&lt;/h3&gt;

&lt;p&gt;All four exposures replay at minute resolution from 2018, available via their own endpoints and via the unified exposure summary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Historical exposure summary, the one-call option&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://lab.flashalpha.com/v1/exposure/summary/SPY?date=2020-03-16T14:30:00Z"&lt;/span&gt;

&lt;span class="c"&gt;# Or per-metric:&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://lab.flashalpha.com/v1/exposure/gex/SPY?date=2020-03-16T14:30:00Z"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flashalpha_historical&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FlashAlphaHistorical&lt;/span&gt;

&lt;span class="n"&gt;hx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FlashAlphaHistorical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;snap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exposure_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2020-03-16T14:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Regime: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;regime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Net GEX: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;net_gex&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Gamma flip: $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;regime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gamma_flip&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Call wall: $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;levels&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;call_wall&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Put wall:  $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;levels&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;put_wall&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response includes &lt;code&gt;as_of&lt;/code&gt;, so you can confirm the timestamp is what you asked for. On March 16 2020 the net GEX was deeply negative; the COVID-crash dealer-positioning replay is the canonical stress-test for any strategy that conditions on GEX regime.&lt;/p&gt;

&lt;h3&gt;
  
  
  VRP time series and percentiles
&lt;/h3&gt;

&lt;p&gt;The volatility risk premium (VRP) measures implied minus realized vol. The history endpoint returns a daily time series of VRP, z-score, and percentile rank against the trailing window, all point-in-time: the percentile for day T is computed using only data through day T.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://lab.flashalpha.com/v1/vrp/SPY/history?lookback=252"&lt;/span&gt;

curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://lab.flashalpha.com/v1/vrp/SPY?date=2022-10-14"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;vrp_percentile&lt;/code&gt; field is the fraction of days in the trailing window where VRP was below the current reading, computed with only prior observations. This is the operative number for a premium-selling trigger: if it is 85 or above, implied vol has been richer than this 85% of the time in the lookback window, conditioning only on past data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full option chains at minute resolution
&lt;/h3&gt;

&lt;p&gt;The full chain endpoint returns every listed contract (bid, ask, IV, delta, gamma, theta, vega, vanna, charm, rho, OI) for a symbol at any minute since 2018.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://lab.flashalpha.com/v1/options/SPY?date=2020-03-16T14:30:00Z"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Honest resolution table for the chain endpoint:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Resolution in history&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bid, ask, IV, delta, gamma, theta, vega&lt;/td&gt;
&lt;td&gt;Minute-level (9:30 to 16:00 ET)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vanna, charm, rho (historical-only)&lt;/td&gt;
&lt;td&gt;Minute-level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open interest&lt;/td&gt;
&lt;td&gt;EOD-stamped (one value per trading day)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Volume&lt;/td&gt;
&lt;td&gt;Always 0; use OI for liquidity proxy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SVI-smoothed vol (&lt;code&gt;svi_vol&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Always null (&lt;code&gt;svi_vol_gated: "backtest_mode"&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your GEX calculation uses OI and you replay at minute resolution, you are using that day's opening OI for every minute of the session. That is what any live system would have done, since OI is published once per morning. So the EOD stamp is correct for intraday GEX replay, not a limitation.&lt;/p&gt;

&lt;h3&gt;
  
  
  IV surface and SVI parameters
&lt;/h3&gt;

&lt;p&gt;The 50x50 implied vol surface grid (moneyness by maturity) evolves at minute resolution, driven by per-contract quotes. The SVI calibration parameters are EOD-stamped.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;surface&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;surface&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2022-06-15T14:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;adv&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;adv_volatility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2022-06-15T14:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# surface: minute-level 50x50 grid
# adv: EOD-stamped SVI params {a, b, rho, m, sigma} +
#      arbitrage-free flags + variance-swap strike
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The universe screener: building a candidate set
&lt;/h3&gt;

&lt;p&gt;Every cross-sectional strategy starts with a candidate set. The screener endpoint ranks and filters across the full symbol universe on GEX, VRP, IV rank, 0DTE contribution, and custom score formulas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://lab.flashalpha.com/v1/screener"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "filters": [
      {"field": "vrp_percentile", "op": "gte", "value": 75},
      {"field": "iv_rank",        "op": "gte", "value": 50},
      {"field": "regime",         "op": "eq",  "value": "positive_gamma"}
    ],
    "sort": {"field": "vrp_zscore", "direction": "desc"},
    "limit": 30
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a backtest, run the screener at the decision timestamp on each rebalance date, then pull per-name history for those candidates only. This is both computationally efficient and methodologically correct: you only see names the screener would have surfaced on that date, not names that survived to the end of your sample.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a backtest: the full stack
&lt;/h2&gt;

&lt;p&gt;The canonical loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Signal(t) → [screener] → Candidates(t) → [history] → Per-name data(t)
          → [fill model] → Fills(t) → [accum] → Metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Signal design
&lt;/h3&gt;

&lt;p&gt;A signal is a function from the point-in-time state of one or more endpoints to a trade decision. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dealer-positioning (GEX):&lt;/strong&gt; Enter a momentum position when net GEX is negative and the 5-day average delta of net GEX is declining.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VRP premium-selling:&lt;/strong&gt; Sell a put credit spread when VRP percentile is at least 80 and IV rank is at least 50, exit when VRP z-score reverts below its median.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dispersion:&lt;/strong&gt; Long single-name straddle, short index straddle, when the ratio of single-name IV to index IV is below its 6-month median.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Signals must be computable from endpoint fields available point-in-time. GEX is available at minute resolution. SVI parameters in a time series are EOD, so your signal updates once per day. OI trajectory is EOD, so the "trajectory" is one observation per day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Candidate set via screener
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;HEADERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;screener_candidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date_str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vrp_pctile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Run the screener point-in-time on a rebalance date.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;date_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;op&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;conditions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;field&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vrp_percentile&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;operator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gte&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vrp_pctile&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;field&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iv_rank&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;operator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gte&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;field&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;regime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;operator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;positive_gamma&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;neutral&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;field&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vrp_zscore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;direction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;desc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/screener&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

&lt;span class="n"&gt;candidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;screener_candidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2022-10-03&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Per-name history retrieval
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flashalpha_historical&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FlashAlphaHistorical&lt;/span&gt;

&lt;span class="n"&gt;hx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FlashAlphaHistorical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_signal_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fetch point-in-time VRP + exposure for a candidate.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;vrp&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vrp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;summ&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exposure_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;vol&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;volatility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;as_of&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vrp_pctile&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vrp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vrp_percentile&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vrp_zscore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vrp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vrp_zscore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vrp_regime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vrp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;regime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gex_regime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;summ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;regime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gamma_flip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;summ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;regime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gamma_flip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;net_gex&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;summ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;net_gex&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iv_rank&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;vol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iv_rank&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iv_atm_30d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;atm_iv_30d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The fill model is the edge
&lt;/h3&gt;

&lt;p&gt;This is where most backtests lie. The naive fill model uses the midpoint price at decision time. That model is wrong in every direction that matters for premium sellers and spread traders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You sell at the bid, not the mid.&lt;/strong&gt; For a short put spread with a mid of $1.20 and a $0.10 wide bid-ask, your actual fill is closer to $1.10 than $1.20. At 250 trades per year, that $0.10 slip compounds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The bid-ask widens before earnings and into events.&lt;/strong&gt; Exactly when VRP looks richest, spreads are widest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volume is zero in historical chains.&lt;/strong&gt; Use OI as a liquidity proxy and apply a conservative fill model for low-OI strikes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A tractable fill model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fill_sell = Mid(t) − α · (Ask(t) − Bid(t)) / 2
Fill_buy  = Mid(t) + α · (Ask(t) − Bid(t)) / 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where α in [0.3, 0.7] is a market-impact parameter. Use α = 0.5 as a baseline; test sensitivity before claiming edge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fill_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bid&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;half_spread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ask&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sell&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;half_spread&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;half_spread&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fill_spread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leg1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;leg1_side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;leg2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;leg2_side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fill_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leg1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;leg1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ask&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;leg1_side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;f2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fill_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leg2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;leg2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ask&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;leg2_side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;f1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;f2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Metrics
&lt;/h3&gt;

&lt;p&gt;Sharpe assumes normal returns; options premium-selling has negative skewness and excess kurtosis. Report Sharpe as a directional indicator, but lean on Sortino, Calmar, and maximum drawdown for sizing. A 70% win rate with a 3:1 loss-to-win payoff is a losing strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example strategies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dealer-positioning momentum
&lt;/h3&gt;

&lt;p&gt;When dealers are net short gamma, they buy into rallies and sell into drops to stay delta-neutral, amplifying directional moves. A momentum signal conditioned on negative-gamma regime should outperform an unconditional one.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;At 9:45 ET, pull &lt;code&gt;/v1/exposure/summary/{t}&lt;/code&gt; to determine the regime.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;net_gex &amp;lt; 0&lt;/code&gt; and the 5-day EMA of &lt;code&gt;net_gex&lt;/code&gt; is declining, the regime is "amplifying."&lt;/li&gt;
&lt;li&gt;Compute the opening 15-minute return. If positive in an amplifying regime, go long a 1-week ATM call debit spread; if negative, a 1-week ATM put debit spread.&lt;/li&gt;
&lt;li&gt;Exit at 14:30 or 2x the initial mid, whichever comes first.&lt;/li&gt;
&lt;li&gt;Flat when regime is positive gamma or undefined.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  VRP premium-selling
&lt;/h3&gt;

&lt;p&gt;Implied vol systematically overstates realized vol on average. When VRP is in the upper tercile, selling premium has historically had positive expected value.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Daily, gate on &lt;code&gt;vrp_percentile ≥ 75&lt;/code&gt; and &lt;code&gt;vrp_regime == "rich"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Sell a 30-delta put credit spread 21 DTE, short put at the put wall, long put $5 further OTM.&lt;/li&gt;
&lt;li&gt;Exit at 50% of initial credit or 7 DTE.&lt;/li&gt;
&lt;li&gt;Apply the fill model with α = 0.5, cap at 2% notional per trade.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Realized-vol dispersion
&lt;/h3&gt;

&lt;p&gt;The implied vol of an index exceeds the weighted implied vol of its constituents; the excess is the correlation risk premium. Long single-name straddles, short index straddle, vega-neutral. Pull &lt;code&gt;/v1/adv_volatility/{name}&lt;/code&gt; per constituent and for the index on the same timestamp.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation and pitfalls
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Walk-forward, not in-sample optimization.&lt;/strong&gt; Fit on a training window, evaluate on the next held-out window, roll forward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Out-of-sample periods must include stress events.&lt;/strong&gt; A backtest that excludes March 2020, August 2024, and Q4 2018 is an in-sample fit on calm markets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Costs must be large enough to matter.&lt;/strong&gt; If removing slippage changes Sharpe by more than 0.3, the edge is in the cost model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regime sensitivity test.&lt;/strong&gt; Split by VIX tercile. If it only works in one regime, say so.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The kinks and common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Kink 1: Survivorship bias.&lt;/strong&gt; A fixed ticker list (today's S&amp;amp;P 500) excludes names delisted or merged before today but present at the time. Construct the universe from point-in-time index membership.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kink 2: Lookahead in computed analytics.&lt;/strong&gt; VRP percentile computed over the full sample ranks a 2019 reading against observations that had not happened yet. Verify the window is trailing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kink 3: Restatement and non-determinism.&lt;/strong&gt; Recomputed analytics change if methodology updates. Archive raw responses for bit-exact reproducibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kink 4: Ignoring fills and slippage.&lt;/strong&gt; A 1-DTE short straddle mid-priced at $3.20 with a $0.25 spread costs $0.50 round-trip, 15.6% of premium. Apply the fill model and vary α from 0.3 to 0.7.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kink 5: Overfitting strike, expiry, and threshold.&lt;/strong&gt; Many free parameters. Treat threshold as a hyper-parameter, fit on training only, test out-of-sample with it locked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kink 6: Regime dependence without disclosure.&lt;/strong&gt; VRP selling wins slowly in calm markets, loses quickly in spikes. Report Calmar, worst drawdown, and drawdown duration. Backtest any drawdown breaker as a system parameter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kink 7: Point-in-time OI vs revised OI.&lt;/strong&gt; Using finalized OI for a 14:30 signal is lookahead. Verify you use the preliminary morning publication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kink 8: Train/test leakage via shared normalization.&lt;/strong&gt; Fit all preprocessors on training data only, then transform both train and test with frozen parameters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kink 9: Research vs production data mismatch.&lt;/strong&gt; The historical option-quote shape differs from live (flat array, renamed fields). Write one parser that handles both, test against both before deploying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kink 10: Assignment and pin risk near expiry.&lt;/strong&gt; A short put ITM within 2 to 3 DTE carries early-assignment risk. For credit spreads, pin risk leaves a naked overnight position with gap risk. Flag any position at 1 DTE with the short strike within 1% of spot, and apply a conservative exit fill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worked example: a VRP backtest sketch
&lt;/h2&gt;

&lt;p&gt;A condensed but complete sketch of a 30-delta put credit spread backtest on SPY using the VRP signal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flashalpha_historical&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FlashAlphaHistorical&lt;/span&gt;

&lt;span class="n"&gt;API_BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;API_KEY&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;hx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FlashAlphaHistorical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;SIGNAL_OPEN_HOUR&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;15:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;VRP_PCTILE_THRESH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;
&lt;span class="n"&gt;IV_RANK_THRESH&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;
&lt;span class="n"&gt;ALPHA_FILL&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;
&lt;span class="n"&gt;DTE_OPEN&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;

&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cal_days&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bdate_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;rebalance_dates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cal_days&lt;/span&gt;&lt;span class="p"&gt;[::&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# weekly
&lt;/span&gt;
&lt;span class="n"&gt;trades&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rd&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rebalance_dates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;T&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SIGNAL_OPEN_HOUR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;vrp_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vrp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;vol_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;volatility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;exp_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exposure_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;

    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vrp_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vrp_percentile&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;VRP_PCTILE_THRESH&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt;
            &lt;span class="n"&gt;vol_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iv_rank&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;IV_RANK_THRESH&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt;
            &lt;span class="n"&gt;exp_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;regime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;positive_gamma&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;neutral&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;

    &lt;span class="n"&gt;put_wall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exp_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;levels&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;put_wall&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API_BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/options/SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;short_put&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;option_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;P&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
         &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;strike&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;put_wall&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;
         &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;DTE_OPEN&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;days_to_expiry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;DTE_OPEN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;short_put&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="n"&gt;long_put&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;option_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;P&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
         &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;strike&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;short_put&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;strike&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
         &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;days_to_expiry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;short_put&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;days_to_expiry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;long_put&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;

    &lt;span class="n"&gt;short_fill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fill_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;short_put&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;short_put&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ask&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sell&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ALPHA_FILL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;long_fill&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fill_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;long_put&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="n"&gt;long_put&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ask&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;ALPHA_FILL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;net_credit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;short_fill&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;long_fill&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;net_credit&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;

    &lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;rd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;short_strike&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;short_put&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;strike&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;long_strike&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;short_put&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;strike&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;net_credit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;net_credit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vrp_pctile&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;vrp_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vrp_percentile&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Trades: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this sketch leaves out, deliberately, as kink-avoidance exercises: the exit loop, the assignment model, the drawdown breaker, and walk-forward parameter selection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tooling: endpoints and the MCP connector
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /v1/exposure/gex/{t}?date=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Net GEX by strike, gamma flip, walls&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /v1/exposure/summary/{t}?date=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full dealer-positioning state&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /v1/options/{t}?date=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full option chain: IV, greeks, OI, bid/ask&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /v1/vrp/{t}/history&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;VRP series, z-score, percentile&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /v1/volatility/{t}?date=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IV, realized vol, skew, term structure&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /v1/surface/{t}?date=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;50x50 IV surface grid (minute-level)&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /v1/adv_volatility/{t}?date=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SVI params (EOD), variance surface, arb flags&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST /v1/screener&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Universe filter on GEX, VRP, IV rank, regime&lt;/td&gt;
&lt;td&gt;Growth&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Historical replay
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flashalpha_historical&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FlashAlphaHistorical&lt;/span&gt;
&lt;span class="n"&gt;hx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FlashAlphaHistorical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;snap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exposure_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2022-06-15T14:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Live production, same method names, no at=
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flashalpha&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FlashAlpha&lt;/span&gt;
&lt;span class="n"&gt;fa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FlashAlpha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;snap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exposure_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For LLM-augmented research, the quant MCP connector exposes the full Historical API as callable tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"flashalpha-quant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://lab.flashalpha.com/mcp-oauth/quant"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The systematic options research pipeline has three honest bottlenecks: point-in-time data, a realistic fill model, and discipline against the ten kinks that inflate backtested Sharpe before live trading reveals the truth.&lt;/p&gt;

&lt;p&gt;Once the backtest is validated, the production migration is a one-parameter change: remove &lt;code&gt;at=&lt;/code&gt; from each call and the same code runs live. That is the architectural reason to use a pre-computed analytics layer rather than building a raw-chain pipeline from scratch.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://flashalpha.com/articles/complete-guide-quant-options-backtesting" rel="noopener noreferrer"&gt;FlashAlpha&lt;/a&gt;. FlashAlpha provides pre-computed options analytics (GEX, DEX, VEX, CHEX, SVI surfaces, VRP) for 6,000+ US equities and ETFs via &lt;a href="https://flashalpha.com/docs/lab-api-overview" rel="noopener noreferrer"&gt;API&lt;/a&gt;. &lt;a href="https://flashalpha.com/pricing" rel="noopener noreferrer"&gt;Free API key, no credit card&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>python</category>
      <category>trading</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Volatility Relative-Value Trading: The Complete Guide</title>
      <dc:creator>tomasz dobrowolski</dc:creator>
      <pubDate>Mon, 08 Jun 2026 07:40:35 +0000</pubDate>
      <link>https://dev.to/tomasz_dobrowolski_35d32c/volatility-relative-value-trading-the-complete-guide-421g</link>
      <guid>https://dev.to/tomasz_dobrowolski_35d32c/volatility-relative-value-trading-the-complete-guide-421g</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally published on the &lt;a href="https://flashalpha.com/articles/complete-guide-volatility-relative-value-trading" rel="noopener noreferrer"&gt;FlashAlpha Research blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What relative-value volatility trading is
&lt;/h2&gt;

&lt;p&gt;A directional equity trader has a view on &lt;em&gt;where&lt;/em&gt; the stock goes. A volatility relative-value trader has a view on &lt;em&gt;how much&lt;/em&gt; it should cost to insure it, and on whether that cost is mispriced relative to history, relative to a model, or relative to another asset. The underlying price is not the primary variable: implied volatility is.&lt;/p&gt;

&lt;p&gt;Relative-value vol trading exploits dislocations in the implied volatility surface: one tenor trading too rich versus another (calendar trades), the put wing too expensive versus fair value (skew trades), index implied correlation diverging from single-name vols (dispersion), or the market pricing future variance above what realised variance will deliver (variance or volatility risk premium). The common thread is that every trade is structural and model-referenced, not a directional bet on the S&amp;amp;P going up or down.&lt;/p&gt;

&lt;p&gt;This matters because the risk profile is different. A well-constructed vol-arb trade is delta-hedged and, when also vega-hedged across legs, is primarily exposed to the &lt;em&gt;shape&lt;/em&gt; of the surface rather than its level. That shape exposure (the relative cheapness or richness of one part of the surface versus another) is more persistent and mean-reverting than the level of implied vol itself. That is the edge.&lt;/p&gt;

&lt;h2&gt;
  
  
  The toolkit
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SVI surfaces and arbitrage-free constraints
&lt;/h3&gt;

&lt;p&gt;The Stochastic Volatility Inspired (SVI) parameterisation, due to Gatheral (2004), maps log-moneyness k to total implied variance w for a single expiry slice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;w(k) = a + b·[ ρ(k − m) + sqrt((k − m)² + σ²) ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where k = ln(K/F) is log-moneyness relative to the forward F, and the five parameters carry clear economic content:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;a&lt;/strong&gt; is the overall level of variance (vertical shift)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;b&lt;/strong&gt; is the angle of the smile wings (controls vol-of-vol)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ρ&lt;/strong&gt; is put/call asymmetry (negative in equity markets due to the leverage effect)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;m&lt;/strong&gt; is the moneyness at which the smile is most symmetric&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;σ&lt;/strong&gt; is the curvature of the ATM region&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Five parameters describe a liquid equity smile. The real difficulty is not the fitting; it is ensuring the fitted surface is arbitrage-free. Two static arbitrage conditions must hold. No butterfly arbitrage means the risk-neutral density must be non-negative at every strike, which translates to a non-negative second derivative of total variance in log-moneyness. No calendar arbitrage means total variance must be non-decreasing across expiries at every moneyness. If either fails, you have a surface that implies free money, and any trade priced off it is wrong in an unbounded way. Arbitrage detection is the first deliverable to check, not an afterthought.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variance swap replication and the var-swap strike
&lt;/h3&gt;

&lt;p&gt;A variance swap pays the difference between realised variance and a fixed strike agreed at inception. The fair strike is the replication-theoretic value (Demeterfi et al. 1999):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;K_var = (2/T) · [ ∫₀ᶠ P(K)/K² dK + ∫ᶠ^∞ C(K)/K² dK ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the integrand spans all strikes, you need a complete smile, which is exactly what the SVI fit provides. The numerical integral over the smile gives a clean, arb-consistent fair value for realised variance that goes beyond ATM implied vol. The convexity adjustment (fair vol minus ATM IV) measures how much the wings contribute; when the put wing is rich, the fair var-swap vol sits above ATM IV, and selling vol via variance swaps captures that richness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variance risk premium versus volatility risk premium
&lt;/h3&gt;

&lt;p&gt;This is one of the most conflated concepts in practice. The variance risk premium (VaRP) is the expected profit from selling a variance swap and delta-hedging: it operates on &lt;em&gt;squared&lt;/em&gt; vol. The volatility risk premium (VolRP) is the P&amp;amp;L from selling a vega-neutral straddle and continuously delta-hedging: it operates on &lt;em&gt;vol itself&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VaRP = K_var − E[σ_R²]      VolRP = σ_IV,ATM − E[σ_R]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These diverge because of the nonlinear relationship between variance and vol. In the tails, variance grows quadratically while vol grows linearly, so fat-tail regimes inflate VaRP far more than VolRP. A strategy that looks like it is selling VaRP may actually be mostly selling VolRP with a highly convex residual tail exposure, and vice versa. Knowing which you are trading, and sizing accordingly, is critical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skew and term structure
&lt;/h3&gt;

&lt;p&gt;The surface has two primary shape dimensions beyond the ATM level. Skew is the slope of implied vol across strikes at a fixed expiry; in equity markets the put wing is systematically richer than the call wing. Term structure is the slope of ATM implied vol across expiries: contango (front below back) is normal, backwardation occurs around events that concentrate near-term uncertainty. Calendar trades buy cheap tenors and sell rich ones.&lt;/p&gt;

&lt;p&gt;Spot-vol correlation is a further shape measure. When spot falls, vol typically rises (negative spot-vol correlation in equities). A surface with very steep downside skew reflects a high-magnitude negative spot-vol correlation priced into the smile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dispersion and implied correlation
&lt;/h3&gt;

&lt;p&gt;Implied correlation is extracted by comparing index implied vol to single-name implied vols:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ρ_implied = (σ_index² − Σ wᵢ²σᵢ²) / (2 Σ_{i&amp;lt;j} wᵢ wⱼ σᵢ σⱼ)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implied correlation is typically higher than realised because the index put skew is structurally bid: investors pay up to hedge the index as a unit rather than buying individual-name puts. A dispersion trade sells index vol and buys single-name vols (or vice versa), monetising the spread. When implied correlation exceeds realised, the index is rich relative to constituents: sell the index, buy the names.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the data
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SVI parameters: what the numbers tell you
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com/v1/adv_volatility/SPX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;svi_parameters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;expiry&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;days_to_expiry&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;d): &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  b=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rho=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rho&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  m=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;m&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  sigma=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sigma&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ATM IV=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;atm_iv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%  arb_free=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arb_free&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interpretation per parameter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;rho more negative than −0.7:&lt;/strong&gt; extremely steep put skew. Often indicates event risk or structural demand for downside protection. Check whether it is a single-name earnings effect or a broad index regime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;b above 0.15:&lt;/strong&gt; wide wings, large moves priced at both extremes. High b with a normal rho means symmetric wing richness, which is unusual and may indicate a vol-of-vol dislocation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sigma near zero:&lt;/strong&gt; a nearly V-shaped smile with minimal ATM curvature. Numerically fragile; arb violations are more likely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cross-slice rho flattening:&lt;/strong&gt; near-term slices are usually steeper than long-dated ones. If the long end suddenly steepens to match, the long end is pricing a structural change.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Arbitrage flags: non-negotiable checks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arbitrage_flags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;] &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;expiry&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; moneyness=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;moneyness&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;n/a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Surface is arbitrage-free&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A butterfly violation means the risk-neutral density goes negative at that moneyness, so any price referencing that region is undefined. A calendar violation means a calendar spread there is a free lunch. Do not trade off a surface with active flags.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variance surface and var-swap strikes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;variance_swap_fair_values&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;K_var&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;K_var&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;            &lt;span class="c1"&gt;# fair variance strike (annualised)
&lt;/span&gt;    &lt;span class="n"&gt;fair_vol&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fair_vol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;         &lt;span class="c1"&gt;# sqrt(K_var), vol units
&lt;/span&gt;    &lt;span class="n"&gt;convexity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fair_vol&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;atm_iv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;vs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;expiry&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: fair_vol=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fair_vol&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ATM=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;vs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;atm_iv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%  convexity_adj=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;convexity&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;pp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The convexity adjustment directly measures how much the wings cost relative to ATM. Above +1.5pp for SPX is historically elevated, indicating the market is paying a premium for tail coverage beyond what ATM vol implies. Track the time series and you have a clean signal for when variance swaps are cheap or rich relative to straddles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dispersion and spot-vol correlation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;disp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com/v1/dispersion&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;symbols&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AAPL,MSFT,NVDA,AMZN,GOOGL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.07,0.07,0.06,0.05,0.05&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;horizon_days&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Implied correlation:  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;disp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;implied_correlation&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Realised correlation: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;disp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;realized_correlation&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Corr premium:         &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;disp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;correlation_premium&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The trades
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Calendar / term-structure trades
&lt;/h3&gt;

&lt;p&gt;A calendar spread buys total variance at one expiry and sells it at another. Compute the ATM IV ratio between front and back via &lt;code&gt;/v1/volatility/skew-term&lt;/code&gt;. If the ratio is in the top decile of its history, the front is rich: sell front variance, buy back variance. Size to vega-neutrality across the two legs, not notional-neutrality.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com/v1/volatility/skew-term/SPX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;front&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;term_structure&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;back&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;term_structure&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Front/back ratio: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;front&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;atm_iv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;atm_iv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Regime: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;term_structure_regime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Event distortions.&lt;/strong&gt; The front ratio inflates mechanically before FOMC, CPI, and earnings because the near-term expiry prices the event premium. A top-decile ratio during a FOMC week is not a calendar opportunity; it is the options market correctly pricing the event. Check the term-structure endpoint's event flags first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skew / risk-reversal trades
&lt;/h3&gt;

&lt;p&gt;A risk reversal sells an OTM put and buys an OTM call at the same delta, delta-hedged: a pure bet on the skew level.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RR25 = σ_IV(Δput = −0.25) − σ_IV(Δcall = +0.25)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 25-delta RR in the bottom decile (puts cheap relative to calls, rare in equities) is a long-skew opportunity. A top-decile RR (puts extremely rich) is a short-skew opportunity, but timing matters enormously because equity skew is structurally bid and can stay rich for months.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dispersion
&lt;/h3&gt;

&lt;p&gt;The canonical dispersion trade sells index variance and buys single-name variance in proportion to index weights. The P&amp;amp;L is driven by the off-diagonal cross-terms: each constituent pair contributes its weighted vol product weighted by the correlation gap. When implied correlation exceeds realised, selling the index and buying the names earns positive carry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regime risk.&lt;/strong&gt; Correlation is regime-switching. In a broad risk-off event (March 2020, the 2022 rate shock), realised correlation spikes above 0.9 and the dispersion trade loses catastrophically because single-name vol cannot offset the index move. Sizing requires explicit correlation-scenario stress testing, not just historical mean-reversion assumptions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variance vs vol RP trades
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;vrp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com/v1/vrp/SPX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VRP (variance basis): &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;vrp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;variance_risk_premium&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;pp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VolRP (vol basis):    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;vrp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;volatility_risk_premium&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;pp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# VRP z &amp;gt;&amp;gt; VolRP z: wings rich, convexity elevated
# VolRP z &amp;gt;&amp;gt; VRP z: wings cheap, ATM richness dominates
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the convexity adjustment widens, the wings are expensive relative to ATM; a ratio spread long ATM and short the wings monetises it. When it compresses, butterfly buyers and long vol-of-vol positions benefit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Risk and sizing
&lt;/h2&gt;

&lt;p&gt;Every vol-RV trade should be vega-hedged at the trade level: the book earns from surface-shape dislocations, not from the level of vol. Net vega across all legs should sit close to zero, rebalanced at least daily. Gamma neutrality is harder to hold simultaneously, since in a calendar the short front leg has far higher gamma than the long back leg, so you will carry net gamma. For a multi-name dispersion book, report risk in vega-weighted terms per name, normalising by vol-of-vol (proxy via b from the SVI fit), because 10,000 vega in NVDA carries more vol-of-vol risk than 10,000 vega in MSFT.&lt;/p&gt;

&lt;p&gt;The residual risk in a delta- and vega-neutral book is correlation risk, which is fat-tailed and regime-switching. Size as a fraction of the risk budget assuming realised correlation can spike 0.3 to 0.5 above its long-run mean in a single macro shock, and confirm the resulting P&amp;amp;L stays inside your drawdown limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The kinks and common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Trading off a non-arbitrage-free surface.&lt;/strong&gt; A butterfly failure implies negative probability density. The symptom is subtle: a calendar that should pay positive carry bleeds theta because the fit is locally inconsistent. Check &lt;code&gt;arb_free&lt;/code&gt; per slice before placing any trade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Conflating VaRP with VolRP.&lt;/strong&gt; A high VolRP z-score says ATM straddles are rich. A high VaRP z-score says the wings are rich too. Selling a straddle when VaRP is rich but VolRP is flat earns no premium; the wing richness lives in the variance-swap convexity adjustment. The correct trade there is a ratio spread, not an outright straddle sale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Liquidity filtering of SVI fits.&lt;/strong&gt; Stale, zero-OI, or wide-market strikes distort the fit. Rich/cheap signals in the far wings are often artefacts of liquidity gaps, not real dislocations. Cross-check wing signals against raw chain liquidity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Dispersion correlation regime shifts.&lt;/strong&gt; The implied-exceeds-realised argument is valid in low-correlation regimes. During a macro sell-off, realised correlation jumps toward 1.0 and the trade loses on both legs at once. Require hard stop-losses on realised correlation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Term-structure event distortions.&lt;/strong&gt; A calendar that looks rich on the ATM ratio alone may be fully justified by an event in the front expiry. Check event flags on both legs before interpreting front-month richness as a calendar opportunity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Realised vol estimator choice.&lt;/strong&gt; Close-to-close is biased by overnight gaps and noisy for short windows. Yang-Zhang uses overnight, open-to-close, and close-to-close returns simultaneously for lower variance. The choice can shift apparent richness by 0.5 to 2pp, enough to flip a weak z-score signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worked example: an SPX surface snapshot
&lt;/h2&gt;

&lt;p&gt;Representative figures, illustrative rather than live:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Front (21d)&lt;/th&gt;
&lt;th&gt;Back (49d)&lt;/th&gt;
&lt;th&gt;Read&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ATM IV&lt;/td&gt;
&lt;td&gt;17.4%&lt;/td&gt;
&lt;td&gt;15.8%&lt;/td&gt;
&lt;td&gt;Ratio 1.10, mild backwardation, front modestly rich&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25Δ RR&lt;/td&gt;
&lt;td&gt;−4.2pp&lt;/td&gt;
&lt;td&gt;−3.1pp&lt;/td&gt;
&lt;td&gt;Front put wing steeper, within normal range (~60th pctile)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Var-swap fair vol&lt;/td&gt;
&lt;td&gt;19.1%&lt;/td&gt;
&lt;td&gt;17.2%&lt;/td&gt;
&lt;td&gt;Convexity +1.7pp and +1.4pp above ATM, wings elevated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Butterfly (25Δ)&lt;/td&gt;
&lt;td&gt;1.8pp&lt;/td&gt;
&lt;td&gt;1.5pp&lt;/td&gt;
&lt;td&gt;Wing curvature above ATM, consistent with convexity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arb flags&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Trade-ready on both slices&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The front convexity adjustment of +1.7pp is elevated versus the historical mean near 1.0pp for SPX, so variance swaps are expensive relative to straddles. A ratio spread (long ATM, short wings) or an outright var-swap sell is more attractive than a simple straddle sale. The skew signal is neutral at the 60th percentile, so no standalone RR trade is justified.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;adv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com/v1/adv_volatility/SPX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com/v1/surface/history/SPX?lookback_days=90&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;skew&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com/v1/volatility/skew-term/SPX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;rr_today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skew&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;term_structure&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;risk_reversal_25d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;rr_hist&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;risk_reversal_25d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;skew_history&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="n"&gt;rr_pctile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rr_hist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;rr_today&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;front_arb_free&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;adv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;svi_parameters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arb_free&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rr_pctile&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;front_arb_free&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PUT WING RICH: short skew / short RR candidate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;rr_pctile&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;front_arb_free&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PUT WING CHEAP: long skew / long RR candidate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No skew signal: RR at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rr_pctile&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;th percentile&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Endpoints for vol-arb
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Delivers&lt;/th&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/v1/adv_volatility/{t}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SVI params, variance surface, arb flags, var-swap fair values&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/v1/surface/{t}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full IV surface grid (historical replay available)&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/v1/surface/svi/{t}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Per-bucket IV with rolling mean and z-score&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/v1/vrp/{t}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Variance RP, vol RP, z-scores&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/v1/dispersion?index=...&amp;amp;symbols=...&amp;amp;weights=...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Implied/realised correlation, premium&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/v1/volatility/skew-term/{t}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ATM IV, 25Δ RR, butterfly, regime, event flags&lt;/td&gt;
&lt;td&gt;Growth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/v1/volatility/spot-vol-correlation/{t}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30d, 90d, 6m spot-vol correlation&lt;/td&gt;
&lt;td&gt;Growth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/v1/volatility/{t}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ATM IV, Yang-Zhang and close-to-close realised vol, IV rank&lt;/td&gt;
&lt;td&gt;Growth&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a Claude-based research or signal-generation agent, the vol-arb MCP connector is at &lt;code&gt;https://lab.flashalpha.com/mcp-oauth/volarb&lt;/code&gt;, exposing the advanced volatility, VRP, surface, dispersion, and term-structure tools as Claude-callable functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The edge in relative-value volatility is exposure to the shape of the surface, not its level, and shape is more persistent and mean-reverting than level. Capturing it cleanly depends on an arbitrage-free fit, a clear separation between variance and volatility risk premium, and explicit correlation-scenario sizing for anything dispersion-shaped. Get the surface right first; the trades follow from it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://flashalpha.com/articles/complete-guide-volatility-relative-value-trading" rel="noopener noreferrer"&gt;FlashAlpha&lt;/a&gt;. FlashAlpha provides SVI surfaces, var-swap strikes, VRP decomposition, dispersion, and term structure as structured JSON for 6,000+ US equities and ETFs. &lt;a href="https://flashalpha.com/pricing" rel="noopener noreferrer"&gt;Free API key, no credit card.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>python</category>
      <category>trading</category>
      <category>finance</category>
    </item>
    <item>
      <title>Best Options Data APIs 2026: 7 Compared (Pricing &amp; Free Tiers)</title>
      <dc:creator>tomasz dobrowolski</dc:creator>
      <pubDate>Sun, 07 Jun 2026 07:00:40 +0000</pubDate>
      <link>https://dev.to/tomasz_dobrowolski_35d32c/best-options-data-apis-2026-7-compared-pricing-free-tiers-1lf5</link>
      <guid>https://dev.to/tomasz_dobrowolski_35d32c/best-options-data-apis-2026-7-compared-pricing-free-tiers-1lf5</guid>
      <description>&lt;p&gt;&lt;strong&gt;Full disclosure:&lt;/strong&gt; I built FlashAlpha. I am going to be honest about where every other provider beats us, because lying in a comparison guide gets you exactly one click and zero customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The seven that actually matter
&lt;/h2&gt;

&lt;p&gt;The "options data API" search returns dozens of providers. Most are repackaged Polygon, dead startups, or enterprise tools nobody under $1M revenue can afford. These seven are the ones real quants and developers actually choose between:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Starting price&lt;/th&gt;
&lt;th&gt;Free tier&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Polygon.io&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Raw market data infrastructure, tick-level history&lt;/td&gt;
&lt;td&gt;$29/mo (stocks) + $79/mo (options)&lt;/td&gt;
&lt;td&gt;5 calls/min, no options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ORATS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Historical data, backtesting, 25 years of history&lt;/td&gt;
&lt;td&gt;$99/mo&lt;/td&gt;
&lt;td&gt;14-day trial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tradier&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Brokerage-attached API, retail traders, sandbox&lt;/td&gt;
&lt;td&gt;$10/mo (data) or free with brokerage account&lt;/td&gt;
&lt;td&gt;Sandbox account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Intrinio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Institutional fundamental + options data combo&lt;/td&gt;
&lt;td&gt;~$1,000/mo+&lt;/td&gt;
&lt;td&gt;Limited demo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ThetaData&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cheap historical options tick data for quants&lt;/td&gt;
&lt;td&gt;$80/mo&lt;/td&gt;
&lt;td&gt;None (paid only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unusual Whales&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Flow alerts, dark pool prints, congressional trades&lt;/td&gt;
&lt;td&gt;$48/mo retail / API on request&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FlashAlpha&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pre-computed exposure analytics (GEX/DEX/VEX/CHEX), max pain, vol surfaces&lt;/td&gt;
&lt;td&gt;Free / $79 / $299 / $1,499/mo&lt;/td&gt;
&lt;td&gt;5 req/day, no time limit, no card&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That is the table you came for. Now let me explain why the choice matters and which one fits which job.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are you actually building?
&lt;/h2&gt;

&lt;p&gt;Most developers waste their first month picking an API on price or feature lists without asking what data they actually need to fetch. Five common project types and the right starting point for each:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. A backtesting engine.&lt;/strong&gt; Start with ORATS or ThetaData. Both have deep historical options data with greeks attached. ORATS gives you a hosted backtesting engine (300M+ pre-computed backtests) and 98 proprietary indicators if you do not want to build the strategy logic yourself. ThetaData is cheaper per gigabyte if you are comfortable writing your own framework and pulling raw end-of-day data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. A real-time GEX dashboard.&lt;/strong&gt; Start with FlashAlpha. Real-time per-strike gamma exposure, gamma flip levels, call/put walls, and dealer regime classification, pre-computed in a single API call. Polygon and Tradier give you the raw chain, but you will need to compute the greeks yourself, then aggregate by strike, then label the regime. That is 2,000+ lines of code before your first chart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. API access for a brokerage workflow.&lt;/strong&gt; Start with Tradier. If you already trade with Tradier, the data is bundled with the account, the API is free for active traders, and the sandbox lets you build before funding. No pre-computed analytics, though. Good for execution-adjacent automation, bad for analytical research.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. A SaaS product needing every contract on every US underlying.&lt;/strong&gt; Start with Polygon.io. They are the infrastructure layer: tick-level history, full chains, websocket streaming, real-time consolidated quotes. Expensive at scale but the most complete raw feed in the retail-accessible range. Use Polygon for the data, build (or rent) analytics on top.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Following flow, dark pool prints, unusual activity, congressional trades.&lt;/strong&gt; Start with Unusual Whales. They built a product around the "follow the smart money" thesis. The data is opinion-laden in a way the others are not. You are getting their interpretation of "unusual" baked in, which is fine if you trust the definition and a problem if you want to define your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three categories nobody names
&lt;/h2&gt;

&lt;p&gt;Once you have narrowed by use case, the seven fall into three categories none of their marketing pages name out loud. Knowing the category is more useful than any feature list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Category A: Raw data infrastructure (Polygon, Tradier, ThetaData).&lt;/strong&gt; They give you the chain - bid, ask, IV, OI, volume, sometimes greeks - and you bring the analytics layer. Best for teams that want full control of the pipeline. Trade-off: 4-8 weeks building the analytics layer before your first useful screen. Cheapest long-term if you have engineers and time; fastest path to a delayed launch if you have a deadline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Category B: Pre-computed analytics (ORATS, FlashAlpha).&lt;/strong&gt; These ship analytics with the data - regime labels, GEX, vol surfaces, strategy scores, VRP. You pay for the math to be done for you. Best for teams whose value-add is the strategy, not the data engineering. Trade-off: you are locked into someone else's definitions. The category splits on the time axis - ORATS optimises for EOD depth (25 years back to 2007), FlashAlpha for minute-level dealer-positioning analytics (live, plus a historical API replaying every analytics endpoint at any minute since April 2018 in the same response shape). ORATS goes wider, FlashAlpha goes deeper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Category C: Flow and sentiment products (Unusual Whales, SpotGamma, Intrinio).&lt;/strong&gt; These wrap data in a UX or a thesis. You pay for opinion as much as data. Best for traders who want a commentary or alert stream rather than building from raw inputs. Trade-off: less programmatic flexibility - the best content often lives behind a web dashboard, not an API.&lt;/p&gt;

&lt;h2&gt;
  
  
  What each one does best, in one sentence
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polygon.io&lt;/strong&gt; - Best raw options data infrastructure in the retail-accessible price range. Buy if your team will build the analytics layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ORATS&lt;/strong&gt; - Best for historical backtesting and 25 years of consistently-computed indicators. Buy if your strategy needs to backtest across multiple regimes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tradier&lt;/strong&gt; - Best brokerage-attached API. Buy if you are already a Tradier customer or want bundled execution and data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intrinio&lt;/strong&gt; - Best for combining fundamentals with options in one bill. Buy if you have $1K+/mo budget and need both.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ThetaData&lt;/strong&gt; - Best cheap historical options tick data. Buy if you are a price-sensitive quant comfortable with raw data engineering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unusual Whales&lt;/strong&gt; - Best flow alerts and "follow the smart money" product. Buy if you want opinion-laden alerts rather than building from raw data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FlashAlpha&lt;/strong&gt; - Best pre-computed dealer-positioning analytics (GEX, DEX, VEX, CHEX, max pain, regime, VRP) with a matched live + historical API. Buy if you want to ship a GEX dashboard or premium-selling scanner this week instead of next quarter.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The stacks that actually work
&lt;/h2&gt;

&lt;p&gt;Most serious teams combine two providers. The three I see most often:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backtest + live execution.&lt;/strong&gt; If you need pre-2018 EOD depth or ORATS's proprietary indicators, use ORATS for deep-history validation and FlashAlpha for live signals plus intraday research since 2018. If your strategy is dealer-positioning-driven, FlashAlpha alone works - the historical API replays every live endpoint at any minute since April 2018 in the same shape, so backtest and production run the same code path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Raw data + custom analytics.&lt;/strong&gt; Polygon for the raw chain, in-house Python for everything else. Full methodology control, slowest to first value, cheapest at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow alerts + positioning context.&lt;/strong&gt; Unusual Whales for the alerts, FlashAlpha for the positioning. When the alert fires "TSLA unusual call activity," check whether TSLA is in positive or negative gamma, where the call wall is, and what the VRP looks like. The alert tells you something is happening; the positioning tells you whether to fade or follow it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide in under five minutes
&lt;/h2&gt;

&lt;p&gt;Work down this list and stop at the first match:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;EOD backtesting engine needing pre-2018 data → ORATS&lt;/li&gt;
&lt;li&gt;Backtesting a dealer-positioning or intraday strategy since 2018 → FlashAlpha (Alpha tier, historical API)&lt;/li&gt;
&lt;li&gt;Every options contract on every US underlying with full tick history → Polygon.io&lt;/li&gt;
&lt;li&gt;Retail trader already on Tradier → Tradier API (free for you)&lt;/li&gt;
&lt;li&gt;Ship a GEX dashboard or VRP scanner this week → FlashAlpha (free tier, no card)&lt;/li&gt;
&lt;li&gt;Flow alerts and "what are the smart traders doing today" → Unusual Whales&lt;/li&gt;
&lt;li&gt;Fundamentals + options in one bill, $1K+/mo budget → Intrinio&lt;/li&gt;
&lt;li&gt;Cheap quant who will write your own pipeline against raw tick data → ThetaData&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If two or more match, read the dedicated pairwise comparison - the headline difference is rarely the actual decision driver.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed in 2026
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free tiers are table stakes.&lt;/strong&gt; The "no credit card" free tier used to be a differentiator. Now Polygon, Tradier sandbox, and newer entrants all offer one. The bar for "show me before you charge me" is permanently higher.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-computed analytics is a category.&lt;/strong&gt; Until 2024, most providers shipped raw data and expected you to build analytics (ORATS aside). Buyers now ask "what do you compute for me?" not just "what data do you ship?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Point-in-time analytics replay is new.&lt;/strong&gt; A historical API mirroring every live analytics endpoint at minute resolution since April 2018, in the same response shape, is a shape that did not exist before. Raw historical data is still a commodity; the minute-level computed-analytics replay is the new part.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP servers are an unlock.&lt;/strong&gt; Model Context Protocol lets LLM assistants (Claude, Cursor, Windsurf) query providers directly. If your dev workflow involves an LLM - and most do in 2026 - MCP support is a real differentiator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0DTE is its own product.&lt;/strong&gt; Same-day expiry now drives enough volume that providers ship dedicated 0DTE endpoints returning pin risk, expected move, and gamma acceleration in real time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The TL;DR
&lt;/h2&gt;

&lt;p&gt;For the most common case - a developer who wants to build something useful with options data this week without getting locked into a $299/mo bill before validating the idea - start with a free tier (5 calls/day, no card, no time limit) covering GEX, IV, BSM greeks, key levels, and stock quotes for 6,000+ symbols. Prototype a whole premium-selling screener before deciding whether to scale up.&lt;/p&gt;

&lt;p&gt;Then pair it with the right second provider. ORATS for backtesting depth. Polygon for raw data scale. Unusual Whales for flow context. The single-vendor stack rarely wins; the two-vendor stack does.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://flashalpha.com/articles/best-options-data-apis-2026" rel="noopener noreferrer"&gt;flashalpha.com&lt;/a&gt;. FlashAlpha provides pre-computed options analytics - GEX, DEX, VEX, CHEX, SVI surfaces, max pain, VRP, and dealer positioning - as a live and historical API for 6,000+ US equities and ETFs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>finance</category>
      <category>trading</category>
      <category>quant</category>
    </item>
    <item>
      <title>Alpha Decay in Options Analytics: Which Endpoints Erode With Crowding, and Why the Edge Has a Price</title>
      <dc:creator>tomasz dobrowolski</dc:creator>
      <pubDate>Sat, 06 Jun 2026 20:31:07 +0000</pubDate>
      <link>https://dev.to/tomasz_dobrowolski_35d32c/alpha-decay-in-options-analytics-which-endpoints-erode-with-crowding-and-why-the-edge-has-a-price-3pn0</link>
      <guid>https://dev.to/tomasz_dobrowolski_35d32c/alpha-decay-in-options-analytics-which-endpoints-erode-with-crowding-and-why-the-edge-has-a-price-3pn0</guid>
      <description>&lt;p&gt;If you sell options analytics to prop desks and funds, the first honest question a quant buyer asks is: "If you sell this to everyone, doesn't the edge disappear?" It is the right question, and the lazy answer ("our moat is data depth") does not survive contact with someone who has read the crowding literature. The accurate answer requires separating what actually decays from what does not, because the two are mixed together in any analytics product.&lt;/p&gt;

&lt;p&gt;Full disclosure: I build this stack. I am going to be specific about which of our own endpoints carry no edge at all, because the credibility of the parts that &lt;em&gt;do&lt;/em&gt; matter depends on not overselling the parts that do not.&lt;/p&gt;

&lt;h2&gt;
  
  
  What alpha decay actually is
&lt;/h2&gt;

&lt;p&gt;Alpha decay is the erosion of a strategy's excess return over time. It has three distinct drivers, and conflating them is the most common mistake:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data-mining decay.&lt;/strong&gt; A backtested edge was partly noise; it shrinks out-of-sample even if nobody else ever sees it. McLean and Pontiff bound this at roughly the 26% out-of-sample decline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crowding / publication decay.&lt;/strong&gt; Once others learn the signal and commit capital, they arbitrage it toward fair value. This is the additional drop from 26% to 58% - the part caused by awareness, not noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capacity decay.&lt;/strong&gt; A specific case of crowding: the strategy has finite room before the participants' own trades move the price against them. Capacity is a function of &lt;em&gt;liquidity and holding period&lt;/em&gt;, not of how clever the signal is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is the one most relevant to a data vendor, and the one most often abused in marketing. The crowding literature is consistent that decay magnitude scales with capacity constraints: slower, less liquid strategies decay more because they attract more arbitrage capital relative to the liquidity available to absorb it (&lt;a href="https://arxiv.org/pdf/2105.01380" rel="noopener noreferrer"&gt;Why and how systematic strategies decay, 2021&lt;/a&gt;). The classic worked example is pairs trading, whose returns compressed materially as hedge funds crowded it after the 1990s.&lt;/p&gt;

&lt;p&gt;The corollary matters for honesty. A signal on the most liquid instruments on earth - SPX, SPY, 0DTE index options - has very high capacity, so the marginal participant erodes it slowly. A niche, low-capacity signal erodes fast. Any vendor claiming "we limit subscribers so the edge survives" is only telling the truth for the low-capacity case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not every endpoint is an edge
&lt;/h2&gt;

&lt;p&gt;Here is the part that gets skipped. An analytics API is a mix of measurements, risk premia, mechanical effects, and genuine timing signals. Only one of those four classes decays the way the popular usage of "alpha decay" implies.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Endpoints&lt;/th&gt;
&lt;th&gt;Decay behaviour&lt;/th&gt;
&lt;th&gt;Mechanism / evidence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;1. Measurement&lt;/strong&gt; (was never alpha)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;option_chain&lt;/code&gt;, &lt;code&gt;option_quote&lt;/code&gt;, &lt;code&gt;stock_quote&lt;/code&gt;, &lt;code&gt;greeks&lt;/code&gt;, &lt;code&gt;solve_iv&lt;/code&gt;, &lt;code&gt;volatility&lt;/code&gt;, &lt;code&gt;surface&lt;/code&gt;, &lt;code&gt;dex&lt;/code&gt;, &lt;code&gt;vex&lt;/code&gt;, &lt;code&gt;chex&lt;/code&gt;, &lt;code&gt;kelly&lt;/code&gt;, &lt;code&gt;tickers&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;None. You cannot decay what was never an edge.&lt;/td&gt;
&lt;td&gt;Reports consensus market state or is pure math. Any edge lives in &lt;em&gt;your model of them&lt;/em&gt;, not in the number.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;2. Risk premia&lt;/strong&gt; (persistent, time-varying)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;vrp&lt;/code&gt; (the premium itself)&lt;/td&gt;
&lt;td&gt;Compresses under crowding but is not eliminated; can invert in stress.&lt;/td&gt;
&lt;td&gt;Compensation for bearing variance risk (Carr and Wu, 2009). Paid because short-vol periodically blows up.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;3. Mechanical flow&lt;/strong&gt; (structural)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;max_pain&lt;/code&gt; / pinning, the dealer-hedging component of &lt;code&gt;gex&lt;/code&gt; and &lt;code&gt;levels&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Resists crowding decay; erodes instead via front-running and manipulation.&lt;/td&gt;
&lt;td&gt;Driven by dealer delta-hedging, not by how many people believe it (Ni, Pearson and Poteshman, 2005).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;4. Crowdable timing signals&lt;/strong&gt; (genuine decay)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;narrative&lt;/code&gt;, the directional read of &lt;code&gt;exposure_summary&lt;/code&gt;, &lt;code&gt;zero_dte&lt;/code&gt; timing, front-running a gamma-flip &lt;em&gt;level&lt;/em&gt;, VRP strategy scoring, the live &lt;code&gt;screener&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Real decay with awareness and capital, scaled by capacity.&lt;/td&gt;
&lt;td&gt;Publication / crowding effect (McLean and Pontiff, 2016).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Class 1 - measurement endpoints carry no alpha to lose
&lt;/h3&gt;

&lt;p&gt;A real-time options chain, a Black-Scholes greek, a solved implied vol, an SVI-fitted surface, a DEX/VEX/CHEX exposure - these are descriptions of the market's current state. The vol surface is the market's &lt;em&gt;consensus&lt;/em&gt;; it cannot be arbitraged away by being widely distributed because it is not a prediction. If you have an edge here, it is in your model spotting that the surface is mispriced relative to your own forecast, and that edge is yours, not the endpoint's. Distributing this data to a thousand desks does not degrade it. This is the bulk of what most buyers actually need, and it is honest to say it does not decay.&lt;/p&gt;

&lt;h3&gt;
  
  
  Class 2 - the variance risk premium is a premium, not an anomaly
&lt;/h3&gt;

&lt;p&gt;VRP is frequently mis-sold as a decaying "edge." It is not an anomaly; it is compensation for selling volatility insurance, and it is pervasive across assets and regions precisely because someone has to be paid to be short volatility into the next crash. Crowding compresses it - more sellers, thinner premium - but it does not vanish, because the risk it pays for is real and occasionally catastrophic (February 2018's short-vol unwind is the standing reminder). VRP is persistent &lt;em&gt;in expectation&lt;/em&gt;, time-varying, and capable of going deeply negative in stress. What decays is the &lt;em&gt;timing overlay&lt;/em&gt; on top of it - knowing when to lean in - not the premium itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Class 3 - pinning is mechanical, so it resists belief-crowding
&lt;/h3&gt;

&lt;p&gt;Max pain and strike pinning are the most misunderstood case. Ni, Pearson and Poteshman (2005) showed that on expiration dates, optionable stock closes cluster at strike prices, shifting returns by an average of at least 16.5 basis points, with no equivalent effect in non-optionable stocks. The driver is &lt;em&gt;dealer delta-hedging&lt;/em&gt;: hedgers of net-long option positions sell above the strike and buy below it, pushing price toward the strike. Because the effect is a mechanical consequence of hedging flow rather than a belief traders can pile into, it does not decay the way a sentiment signal does.&lt;/p&gt;

&lt;p&gt;Two honest caveats keep this factual. First, the original evidence covers 1996-2002; market structure has changed materially since, and the rise of 0DTE has compressed the relevant hedging horizon, so the modern magnitude is not guaranteed to match the original study. Second, the same paper found proprietary-trader manipulation contributing to clustering, which means the erosion channel here is front-running and gaming, not crowding-toward-fair-value. The signal is structural, but it is not immortal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Class 4 - the signals that genuinely decay
&lt;/h3&gt;

&lt;p&gt;This is where "alpha decay" actually applies. A packaged directional narrative, a gamma-flip &lt;em&gt;level traded as a setup&lt;/em&gt;, a 0DTE expected-move timing call, a ranked screener of "best" setups - these are predictions that work better when fewer people act on them, and McLean-Pontiff is the empirical anchor: awareness erodes returns.&lt;/p&gt;

&lt;p&gt;The crucial distinction is the &lt;strong&gt;two-layer nature of a dealer-positioning level&lt;/strong&gt;. The gamma-flip level &lt;em&gt;as a number&lt;/em&gt; is mechanical (Class 3) and persistent. The &lt;em&gt;trade of front-running that level&lt;/em&gt; is a Class 4 timing signal and does decay with crowding. Same endpoint, two layers, opposite decay behaviour.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the edge has a price
&lt;/h2&gt;

&lt;p&gt;For Class 4 signals - and only those - distribution is the decay variable. The mechanism is a congestion externality: each additional participant acting on a capacity-constrained signal imposes a small cost on everyone already holding it, by moving the price before they can. This is standard limits-to-arbitrage reasoning.&lt;/p&gt;

&lt;p&gt;The factual scoping matters: this externality is large for low-capacity signals and small for high-capacity ones. So the correct version of the rationing argument is not "we cap subscribers or the edge dies on everything." It is narrower and defensible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For genuinely capacity-constrained signals, the value to each holder is a decreasing function of how many other holders there are. The economically efficient price for such a feed is therefore &lt;em&gt;high by construction&lt;/em&gt; - the price is the rationing mechanism that keeps per-holder capacity intact.&lt;/li&gt;
&lt;li&gt;Mass retail distribution of a niche, low-capacity signal is the fastest way to destroy it.&lt;/li&gt;
&lt;li&gt;This is observable in the tiering itself. Measurement endpoints (Class 1, no decay) are cheap or free. The crowdable signals (Class 4) sit in the higher tiers. The price gradient tracks the decay gradient, which is what you would expect if the pricing is doing real work rather than segmenting on willingness-to-pay alone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The signals worth protecting are protected by the fact that a crowd cannot afford them, and the things a crowd &lt;em&gt;can&lt;/em&gt; afford are the ones that do not decay anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Offshoring the quant stack
&lt;/h2&gt;

&lt;p&gt;Strip away the Class 4 signals and look at what is left: Class 1 measurement infrastructure. That is the durable, non-decaying core, and it is also the most expensive thing to build in-house. Replicating it means standing up three functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A quant function&lt;/strong&gt; to build and validate the calculators - greeks hydration, SVI surface fitting with arbitrage constraints, exposure math, VRP estimation. Months of work before the first correct number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A data function&lt;/strong&gt; to ingest, store, and serve options data at scale - the minute-level history behind a single liquid name runs into billions of rows - with gap detection and daily reconciliation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A devops / on-call function&lt;/strong&gt; to keep the pipeline running every trading day.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a desk whose actual edge is its trading, not its data infrastructure, the build-versus-buy maths is one-sided. What you are really paying for is the non-decaying measurement layer - the part that stays valuable no matter how many people have it. The Class 4 signals on top stay useful precisely because the price keeps their distribution narrow.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;McLean, R.D. and Pontiff, J. (2016). "Does Academic Research Destroy Stock Return Predictability?" &lt;em&gt;Journal of Finance&lt;/em&gt; 71(1): 5-32.&lt;/li&gt;
&lt;li&gt;Ni, S.X., Pearson, N.D. and Poteshman, A.M. (2005). "Stock Price Clustering on Option Expiration Dates." &lt;em&gt;Journal of Financial Economics&lt;/em&gt; 78(1): 49-87.&lt;/li&gt;
&lt;li&gt;Carr, P. and Wu, L. (2009). "Variance Risk Premiums." &lt;em&gt;Review of Financial Studies&lt;/em&gt; 22(3): 1311-1341.&lt;/li&gt;
&lt;li&gt;"Why and how systematic strategies decay" (2021). arXiv:2105.01380.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://flashalpha.com/articles/alpha-decay-options-signals-which-endpoints-erode" rel="noopener noreferrer"&gt;flashalpha.com&lt;/a&gt;. FlashAlpha provides pre-computed options analytics - GEX, DEX, VEX, CHEX, SVI surfaces, VRP, and dealer positioning - as a live and historical API for 6,000+ US equities and ETFs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>options</category>
      <category>trading</category>
      <category>finance</category>
    </item>
    <item>
      <title>Machine Learning on Options Data: An Honest Quant ML Guide</title>
      <dc:creator>tomasz dobrowolski</dc:creator>
      <pubDate>Sat, 30 May 2026 07:53:06 +0000</pubDate>
      <link>https://dev.to/tomasz_dobrowolski_35d32c/machine-learning-on-options-data-an-honest-quant-ml-guide-49hk</link>
      <guid>https://dev.to/tomasz_dobrowolski_35d32c/machine-learning-on-options-data-an-honest-quant-ml-guide-49hk</guid>
      <description>&lt;p&gt;Options data is one of the densest public-markets modalities and one of the worst-served by ML toolchains. This is a direct tour of eight ML methodologies that have published research or plausible workflows on options, grouped by maturity, with the data shape each one needs, where historical replay actually delivers minute-level signal versus end-of-day, and the honest list of what none of this solves.&lt;/p&gt;

&lt;p&gt;I maintain the calculator stack this is written against, so I'll be upfront about the bias: I sell pre-computed analytics on top of historical options data. For a trader the pitch has always been "you don't have to build the calculator yourself." For an ML engineer the pitch is sharper: you don't have to build the training corpus yourself. Most of the exposure summaries, volatility analytics, and quote streams that power the live API are also available point-in-time across the history window. Some pieces (SVI parameters, open interest, macro overlays) are EOD-stamped rather than minute-level, and I'll flag exactly where that matters per methodology.&lt;/p&gt;

&lt;p&gt;If you want the case against before the survey, skip to the failures section at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline problem (why this field is small)
&lt;/h2&gt;

&lt;p&gt;Before any model trains, the data has to be shaped, and options data is uniquely punishing here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Raw chains aren't features.&lt;/strong&gt; A quote feed gives you bids and asks per strike. You still need spot, the forward curve, dividends, and a consistent Greeks pass before any of those numbers are usable as model inputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Surface fits are not optional.&lt;/strong&gt; Without an arbitrage-free fit, your "implied volatility" feature is whatever Newton-Raphson converged to per strike, which means your skew, term, and butterfly features are noise plus signal. Models trained on that overfit to fit instability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-year minute-level coverage is large and hard to join.&lt;/strong&gt; Spot bars, option quotes per strike per expiration, open interest, macro overlays (VIX, VVIX, SKEW, MOVE), event calendars. Assembling all of this from raw tick providers is measured in months, and then you own the maintenance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leakage hides in EOD.&lt;/strong&gt; Most academic options datasets are end-of-day. Intraday strategies trained on EOD labels are quietly using settlement information that wouldn't have been known mid-session. Backtest looks great, live looks terrible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Greeks consistency.&lt;/strong&gt; If your training data has Greeks from one IV-surface assumption and your inference path uses another, you have silent training-serving skew. The feature drifts and you blame the model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why most published ML-on-options papers use a tiny strike subset and a short window. Every methodology below assumes the data is shaped right. If it isn't, your research timeline is consumed before you ever read a paper.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two SDKs, one wire format
&lt;/h2&gt;

&lt;p&gt;There are two FlashAlpha Python packages, and conflating them is a common mistake.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;flashalpha&lt;/code&gt; wraps the live service. High-level methods do not accept an &lt;code&gt;at=&lt;/code&gt; kwarg.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flashalpha-historical&lt;/code&gt; wraps the historical service. Every high-level method requires &lt;code&gt;at=&lt;/code&gt; for point-in-time replay.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Recommended: dedicated historical SDK
# pip install flashalpha-historical
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flashalpha_historical&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FlashAlphaHistorical&lt;/span&gt;

&lt;span class="n"&gt;hx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FlashAlphaHistorical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;snap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exposure_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2020-03-16T15:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vol&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;volatility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2024-06-03T14:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fallback: hit historical REST directly with the same X-Api-Key
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://historical.flashalpha.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;HEADERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;vol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/volatility/SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2024-06-03T14:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What historical actually ships: minute-level vs EOD
&lt;/h2&gt;

&lt;p&gt;"Same response shape as live" is true for the analytics surface. It is materially misleading for a handful of endpoints, and the resolution truth matters more than the schema.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Resolution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Per-contract bid, ask, IV, Greeks, spot&lt;/td&gt;
&lt;td&gt;Minute, 9:30 to 16:00 ET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50x50 surface grid values&lt;/td&gt;
&lt;td&gt;Minute (driven by quotes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SVI calibration parameters {a, b, ρ, m, σ}&lt;/td&gt;
&lt;td&gt;EOD-stamped (one fit per trading day)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open interest&lt;/td&gt;
&lt;td&gt;EOD-stamped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Macro (VIX, VVIX, SKEW, MOVE)&lt;/td&gt;
&lt;td&gt;EOD-stamped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forward prices&lt;/td&gt;
&lt;td&gt;EOD-stamped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-contract &lt;code&gt;svi_vol&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Always null in historical (&lt;code&gt;backtest_mode&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-contract &lt;code&gt;volume&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Always 0 in historical (use &lt;code&gt;open_interest&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That table dictates which methodology survives at minute resolution and which is bounded to EOD.&lt;/p&gt;

&lt;h2&gt;
  
  
  The maturity map
&lt;/h2&gt;

&lt;p&gt;The eight are not equally mature.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Production-proven:&lt;/strong&gt; realised-vol regression (1), deep hedging (4) for market makers and option desks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Research-backed:&lt;/strong&gt; sequence models on surfaces (3), vol-surface anomaly detection (5), causal inference and event studies (8).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plausible but not production:&lt;/strong&gt; regime classification on pre-computed dealer labels (2), generative path augmentation (6), GNNs on chains (7).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I include all eight because the data shape supports all eight. I am not selling all eight as alpha.&lt;/p&gt;

&lt;p&gt;If you read nothing else, read this table. It pulls the rest of the article into one place: what each method is mature enough to ship, the resolution constraint that actually bites it, and the tier that unblocks it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Methodology&lt;/th&gt;
&lt;th&gt;Maturity&lt;/th&gt;
&lt;th&gt;Resolution constraint&lt;/th&gt;
&lt;th&gt;Tier to run it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Realised-vol regression&lt;/td&gt;
&lt;td&gt;Production&lt;/td&gt;
&lt;td&gt;Macro overlay is EOD; features otherwise minute&lt;/td&gt;
&lt;td&gt;Growth live / Alpha replay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Regime classification&lt;/td&gt;
&lt;td&gt;Plausible&lt;/td&gt;
&lt;td&gt;Labels minute-level; classifier methodology drifts&lt;/td&gt;
&lt;td&gt;Growth live / Alpha replay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Sequence models on surfaces&lt;/td&gt;
&lt;td&gt;Research&lt;/td&gt;
&lt;td&gt;Grid minute, SVI latent EOD&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Deep hedging / RL&lt;/td&gt;
&lt;td&gt;Production (desks)&lt;/td&gt;
&lt;td&gt;Greeks minute, OI EOD, full chain per step&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Surface anomaly detection&lt;/td&gt;
&lt;td&gt;Research&lt;/td&gt;
&lt;td&gt;Grid minute, SVI reference EOD&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Generative path augmentation&lt;/td&gt;
&lt;td&gt;Plausible&lt;/td&gt;
&lt;td&gt;Grid minute; can't synthesise unseen tails&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;GNNs on chains&lt;/td&gt;
&lt;td&gt;Speculative&lt;/td&gt;
&lt;td&gt;Chain minute; no known PnL profile&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Causal inference / event studies&lt;/td&gt;
&lt;td&gt;Research&lt;/td&gt;
&lt;td&gt;Surface minute; statistical power is the limit&lt;/td&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern is blunt: live feature engineering starts at Growth, and every method that needs point-in-time history is Alpha. The free tier inspects response shapes on a single non-index equity and nothing more.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Realised-volatility forecasting (regression)
&lt;/h2&gt;

&lt;p&gt;The simplest, most-published, most reliably useful-at-the-margin target: predict realised vol at a forward horizon (1d, 5d, 21d) given the current implied surface and recent realised history.&lt;/p&gt;

&lt;p&gt;Realised vol over horizon h is RV(t, h) = sqrt((252/h) · Σ r²). The vol risk premium is VRP = σ_IV − σ_RV. The forecasting target is the realised side; the implied side is a feature, because traders pricing protection are betting on the future return distribution and that signal leaks into level, skew, and term structure.&lt;/p&gt;

&lt;p&gt;Tree ensembles (XGBoost, LightGBM) and small recurrent models exploit this with a feature set that is easy to assemble: realised vol at multiple lookbacks, ATM 30-day IV, 25-delta risk reversal (skew), 30d-vs-90d term slope, 25-delta strangle vs ATM (butterfly), and the macro overlay (EOD-stamped in history).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;vol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;volatility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2024-06-14T15:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# realised-vol ladders, ATM IV, skew, term structure
# field names follow the live /v1/volatility response
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What it doesn't solve: regime change (the model always lags) and event-driven shocks (FOMC and earnings follow a different conditional distribution; treat them as a separate feature regime). This is the Kaggle-shaped end of options ML. Useful as a building block, not institutional alpha standalone.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Regime classification (dealer gamma, VRP)
&lt;/h2&gt;

&lt;p&gt;Conditional strategies are where most options alpha actually lives: sell premium when VRP is rich, buy gamma when dealers are short, fade rallies in positive-gamma regimes, ride them in negative-gamma. Every one needs a label, and labels are where most ML projects on options quietly fail, either look-ahead-biased or arbitrary.&lt;/p&gt;

&lt;p&gt;The exposure summary endpoint returns a categorical regime label per minute alongside net GEX, gamma flip, 0DTE contribution, and dealer-positioning interpretations, with the same classifier running across the replay window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exposure_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2024-06-14T15:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# net GEX/DEX/VEX/CHEX, gamma flip, 0DTE contribution,
# dealer-hedging narrative, regime label
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Honest caveat: the classifier evolves as bugs are fixed, so replay reflects current methodology, not a frozen-at-the-time snapshot. If you need bit-exact reproducibility against an older pull, archive your responses. The labels are descriptive, not causal: knowing the market is in a negative-gamma regime doesn't tell you when it ends. Pair with a separate transition-timing model. That's why this sits in "plausible but not production."&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Sequence models on IV surfaces (LSTM, transformer)
&lt;/h2&gt;

&lt;p&gt;The surface at time t is an (n_strikes by n_expirations) tensor; the sequence over a day is a tensor time series. This is squarely in the modality patch-based transformers, TCNs, and dilated convolutions handle well. The canonical reference is Horvath, Muguruza, and Tomas, "Deep Learning Volatility" (2019), which uses neural nets to price options under rough vol; the inverse problem of predicting surface dynamics uses the same input shape.&lt;/p&gt;

&lt;p&gt;SVI fits each expiration slice's total variance w(k) = a + b{ρ(k − m) + sqrt((k − m)² + σ²)}: five parameters per slice, arbitrage-free under explicit constraints.&lt;/p&gt;

&lt;p&gt;Feature engineering, in increasing dimensional efficiency: raw IV grid (most expressive, hardest to train), SVI parameters per slice, or whole-surface SVI parameters as global state (lowest-dimensional latent).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolution caveat that bites here.&lt;/strong&gt; The 50x50 grid evolves at minute resolution because it's driven by quotes. The SVI calibration parameters are EOD-stamped. An intraday &lt;code&gt;at=&lt;/code&gt; returns the most recent EOD SVI parameters, not a fresh intraday calibration. If your model forecasts the SVI latent, you have one observation per trading day, not per minute. If you use the raw grid, the intraday tensor is real.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;surface&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;surface&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2024-06-14T15:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# minute-level grid
&lt;/span&gt;&lt;span class="n"&gt;adv&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;adv_volatility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2024-06-14T15:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# SVI (EOD) + arb checks
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What it doesn't solve: sub-second mid-quote prediction. Market makers see flow you don't. Stay at multi-second horizons or longer.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Deep hedging and RL hedging
&lt;/h2&gt;

&lt;p&gt;This is where the academic literature is strongest and industrial deployment most mature, inside market-maker and exotic-desk shops. You hold a path-dependent position and want a hedging policy that minimises transaction-cost-aware variance, CVaR, or another risk measure. Analytical delta hedging is provably suboptimal under transaction costs and jumps; neural-network policies trained on simulated and real paths beat it.&lt;/p&gt;

&lt;p&gt;The objective: min over policy π of ρ(V_T − C_T), where V_T = V_0 + Σ π_t ΔS_t − Σ TC(π_t) and ρ is a convex risk measure. References: Buehler, Gonon, Teichmann, Wood, "Deep Hedging" (2018); Kolm and Ritter (2019); Cao, Chen, Hull, Poulos (2021).&lt;/p&gt;

&lt;p&gt;The data requirement is the hardest in this article: the full option chain at every step of every rollout, with full Greeks, so the policy can choose its hedge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option_quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2024-06-14T15:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# flat array, renamed fields: implied_vol (not iv), open_interest (not oi),
# lastUpdate (camelCase). Historical-only: iv_bid, iv_ask, vanna, charm, rho.
# volume always 0 (use open_interest); svi_vol always null (backtest_mode);
# open_interest EOD-stamped.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The minute-level Greeks, bid/ask, and spot anchor are real and usable; EOD OI and the null intraday smoother are the honest limits. Deep hedging is a hedging technology, not an alpha. PnL comes from being a market maker; deep hedging shrinks the residual variance. If you're a directional trader, this isn't your methodology.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Vol-surface anomaly detection (unsupervised)
&lt;/h2&gt;

&lt;p&gt;Mispriced surfaces produce structural arbitrage (butterfly, calendar, sticky-strike vs sticky-delta) and unusual structural shifts that precede directional moves. Both are unsupervised: you learn what normal looks like and flag deviations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Autoencoder on the surface grid.&lt;/strong&gt; Reconstruction error is the anomaly score. The 50x50 grid is minute-level historically, so this works at minute resolution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quote-vs-fit residual analysis.&lt;/strong&gt; The deviation between minute-level quotes and the EOD SVI fit is itself a signal; a persistent large deviation often precedes a quote correction or flags a stale fit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reference: Ackerer, Tagasovska, Vatter, "Deep Smoothing of the Implied Volatility Surface" (2020). The residuals of any smoother are usable as anomaly signals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;adv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;adv_volatility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2024-06-14T15:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# SVI (EOD), variance grid, butterfly/calendar arb flags,
# variance-swap fair values, higher-order Greeks surfaces
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What it doesn't solve: anomalies on illiquid strikes are mostly fit noise. Use the liquidity-weighted version.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Generative models for vol path augmentation
&lt;/h2&gt;

&lt;p&gt;The 8-year intraday history sounds long until you condition on a joint state: negative dealer gamma, VRP above its 90th percentile, earnings week, VIX between 18 and 22. The sample count drops to single digits. Generative models synthesise realistic-but-novel paths to augment training in those undersampled regions.&lt;/p&gt;

&lt;p&gt;Reference: Wiese, Knobloch, Korn, Kretschmer, "Quant GANs" (2020); the architecture has since extended to TimeGAN, conditional GANs on surface tensors, and diffusion models on financial series.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;iter_market_minutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2018-04-16&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-01-01&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;grid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;surface&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;surface_grid_to_tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What it doesn't solve: synthetic paths can't introduce tail behaviour that wasn't in the training set. A model trained on calm regimes will not invent a COVID-style crash. Generative augmentation expands the interior of your distribution, not its tails. Plan stress tests separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Graph neural nets on option chains
&lt;/h2&gt;

&lt;p&gt;The most speculative section. I include it because the data shape is right, not because industry adoption is strong. Contracts on the same underlying form a natural graph: strikes connect via butterflies, expirations via calendars, related underlyings (SPX/SPY, sector ETFs and constituents) via vol-of-vol relationships. A GNN with these structural priors can handle the whole surface jointly rather than slice by slice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option_quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2024-06-14T15:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# nodes = per-strike contracts; edges via moneyness/expiry proximity;
# messages carry IV and Greek information
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worth reaching for cross-chain mispricing detection (SPX vs SPY drift, sector ETFs vs constituents) or research where the joint surface structure matters. It's a research direction, not a deployed methodology with a known PnL profile. Calibrate expectations accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Causal inference and event studies
&lt;/h2&gt;

&lt;p&gt;The most under-rated angle. Classical finance has decades of event-study methodology; modern ML adds heterogeneous treatment effect estimation (causal forests, X-learners, doubly-robust estimators give per-firm CATE rather than a single average), counterfactual surface construction (synthetic control on the surface tensor), and IV-crush prediction.&lt;/p&gt;

&lt;p&gt;A concrete flow: pull the surface around every earnings announcement in the window. Use trading-day offsets, not calendar-day, and align to the actual announcement timestamp (most issuers report before-open or after-close).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas_market_calendars&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;mcal&lt;/span&gt;
&lt;span class="n"&gt;nyse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mcal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_calendar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;XNYS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trading_days_before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;sched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nyse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;end_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sched&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_pydatetime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pull_pre_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;announce_ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when_announced&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pre_day&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;trading_days_before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;announce_ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;last_day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;trading_days_before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;announce_ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;when_announced&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;before_open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;post_day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;announce_ts&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;post_day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nyse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valid_days&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;announce_ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;announce_ts&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;))[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;pre&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;volatility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pre_day&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;T15:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;volatility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;last_day&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;T15:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;volatility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;post_day&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;T15:30:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Earnings and FOMC dates are well known; the matched surface state isn't, and that's the bottleneck. What it doesn't solve: rare-event causal estimates have wide error bars regardless of ML sophistication. The framework cleans up the analysis; it doesn't manufacture statistical power.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leakage and point-in-time correctness
&lt;/h2&gt;

&lt;p&gt;The single most common reason ML-on-options papers don't replicate live: training labels built from end-of-day or settlement data that wouldn't have been known at the decision time. The model looks fine in cross-validation and loses money in paper trading. The cause is upstream of the model.&lt;/p&gt;

&lt;p&gt;The primitive against this is point-in-time replay. Every historical endpoint accepts &lt;code&gt;at=YYYY-MM-DDTHH:mm:ss&lt;/code&gt; and returns the response as it would have been computed at that minute. Feature construction is leak-free by default if you ask for features at t and labels at t+h using &lt;code&gt;at&lt;/code&gt; for both.&lt;/p&gt;

&lt;p&gt;Checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are your features as-of t or as-of t-1? Decide explicitly and verify.&lt;/li&gt;
&lt;li&gt;Are your labels computed using only data timestamped ≤ t + horizon, not back-revised?&lt;/li&gt;
&lt;li&gt;Are your regime labels from the same classifier across history, or did methodology drift?&lt;/li&gt;
&lt;li&gt;Does your hold-out split respect time (chronological), not random?&lt;/li&gt;
&lt;li&gt;Are event dates aligned to the announcement timestamp (before-open vs after-close)?&lt;/li&gt;
&lt;li&gt;Are you treating EOD-stamped fields (SVI, OI, macro) as as-of-most-recent-close, not as-of-minute t?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What ML on options won't solve
&lt;/h2&gt;

&lt;p&gt;The credibility section. Every quant ML engineer has met a vendor who claimed everything.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sub-second mid-quote prediction.&lt;/strong&gt; Market makers see the flow; you don't. The information asymmetry is structural.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regime change prediction.&lt;/strong&gt; Detection works. Change prediction does not, robustly. Detection plus position sizing is the honest play.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-asset macro.&lt;/strong&gt; Options data tells you about this underlying. Rates, credit, FX, commodities come from elsewhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Survivorship bias.&lt;/strong&gt; Cross-sectional ML on single-name options is biased toward winners that didn't get delisted. Index ETFs largely sidestep it; single-name work needs explicit handling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intraday SVI / OI / macro evolution.&lt;/strong&gt; EOD-stamped here. If your architecture requires minute-level SVI dynamics, this dataset is not it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Same response shape" for everything.&lt;/strong&gt; True for most analytics endpoints; not for optionquote, maxpain, or stock-summary macro objects. Write your client with awareness.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your project requires any of the above, this stack is part of the answer, not the whole answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The substrate, not the strategy
&lt;/h2&gt;

&lt;p&gt;Eight methodologies, grouped by maturity, mapped to verified endpoints. The honest pitch: this is the data substrate that compresses ML wall-clock from quarters to weeks. It does not manufacture alpha; it removes the pipeline tax. Pick the methodology that fits your research question, recognise that almost all require historical replay, and budget accordingly. For a single non-index equity sniff test, the free tier inspects response shapes. For real work you need the historical replay tier.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://flashalpha.com/articles/machine-learning-options-data-quant-strategies-guide" rel="noopener noreferrer"&gt;FlashAlpha Research&lt;/a&gt;. Free API key, no credit card: &lt;a href="https://flashalpha.com/pricing" rel="noopener noreferrer"&gt;flashalpha.com/pricing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>quant</category>
      <category>python</category>
      <category>api</category>
    </item>
    <item>
      <title>Connect Claude to a live options-analytics API over MCP (the 2-endpoint gotcha)</title>
      <dc:creator>tomasz dobrowolski</dc:creator>
      <pubDate>Fri, 29 May 2026 13:10:21 +0000</pubDate>
      <link>https://dev.to/tomasz_dobrowolski_35d32c/connect-claude-to-a-live-options-analytics-api-over-mcp-the-2-endpoint-gotcha-3e00</link>
      <guid>https://dev.to/tomasz_dobrowolski_35d32c/connect-claude-to-a-live-options-analytics-api-over-mcp-the-2-endpoint-gotcha-3e00</guid>
      <description>&lt;p&gt;FlashAlpha ships &lt;strong&gt;two&lt;/strong&gt; MCP endpoints with the same tool catalogue but different auth, and picking the wrong one is the single most common setup failure. This walks both paths and the errors you will actually hit.&lt;/p&gt;

&lt;p&gt;FlashAlpha exposes 40 tools over the Model Context Protocol (23 live, 17 historical-replay), so you can ask Claude about gamma exposure, dealer positioning, volatility surfaces and Greeks in plain English instead of hand-rolling REST calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which endpoint?
&lt;/h2&gt;

&lt;p&gt;Same tools on both. Different authentication. Match the row to the client you are configuring.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Client&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;th&gt;Auth&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;claude.ai (web)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://lab.flashalpha.com/mcp-oauth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OAuth (one-time sign-in)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Desktop&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;https://lab.flashalpha.com/mcp-oauth&lt;/code&gt; (current builds) or &lt;code&gt;https://lab.flashalpha.com/mcp&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;OAuth, or API key on older builds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code (CLI)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://lab.flashalpha.com/mcp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;API key per tool call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor / Windsurf / VS Code&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://lab.flashalpha.com/mcp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;API key per tool call&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The one rule: &lt;strong&gt;claude.ai web and current Claude Desktop builds use the &lt;code&gt;-oauth&lt;/code&gt; endpoint and OAuth sign-in.&lt;/strong&gt; The CLI and editor clients (Claude Code, Cursor, Windsurf, VS Code) use the plain &lt;code&gt;/mcp&lt;/code&gt; endpoint and you hand Claude the key in your first message. If your Desktop build is old enough that OAuth fails, fall back to &lt;code&gt;/mcp&lt;/code&gt; and the apiKey path below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: OAuth (claude.ai web or Claude Desktop)
&lt;/h2&gt;

&lt;p&gt;Cleanest setup. Sign in once, click Allow, and Claude calls tools on your behalf with no key handling. Works on Free, Pro, Max, Team and Enterprise.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://claude.ai" rel="noopener noreferrer"&gt;claude.ai&lt;/a&gt; and sign in.&lt;/li&gt;
&lt;li&gt;Your name (bottom-left) then &lt;strong&gt;Settings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customize&lt;/strong&gt; then &lt;strong&gt;Connectors&lt;/strong&gt;. On Team/Enterprise the workspace owner adds connectors under &lt;em&gt;Organization settings then Connectors&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;+&lt;/strong&gt; then &lt;strong&gt;Add custom connector&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Fill in the dialog:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;FlashAlpha&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote MCP server URL:&lt;/strong&gt; &lt;code&gt;https://lab.flashalpha.com/mcp-oauth&lt;/code&gt;. The &lt;code&gt;-oauth&lt;/code&gt; suffix is required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced settings:&lt;/strong&gt; leave empty. FlashAlpha uses Dynamic Client Registration, so Claude registers itself. No client ID or secret to paste.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add&lt;/strong&gt;, then &lt;strong&gt;Connect&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Sign in on the FlashAlpha page and click &lt;strong&gt;Allow&lt;/strong&gt;. If you are already signed in to flashalpha.com in the same browser, you skip straight to consent.&lt;/li&gt;
&lt;li&gt;You land back in Claude and the connector flips to &lt;strong&gt;Connected&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On Desktop the only difference is the callback: you get an "Open Claude?" browser dialog that deep-links back into the app. Click &lt;strong&gt;Open Claude&lt;/strong&gt;. That is expected, not an error.&lt;/p&gt;

&lt;p&gt;Verify with a new chat: &lt;em&gt;"What is my FlashAlpha account?"&lt;/em&gt; Claude calls &lt;code&gt;get_account&lt;/code&gt; and returns your email, tier and remaining quota.&lt;/p&gt;

&lt;p&gt;Your API key never appears in Claude on this path. The OAuth resource server resolves your account from the bearer token and forwards your stored key on the internal upstream call only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: apiKey (Desktop, Cursor, Windsurf, VS Code, Claude Code CLI)
&lt;/h2&gt;

&lt;p&gt;Here each tool takes an &lt;code&gt;apiKey&lt;/code&gt; argument. Two steps: register the URL, then tell Claude the key once per chat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude Desktop config:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Windows: &lt;code&gt;%APPDATA%\Claude\claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"flashalpha"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://lab.flashalpha.com/mcp"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save, then &lt;strong&gt;fully quit and reopen&lt;/strong&gt; Claude Desktop. A reload is not enough; the config is read once at startup.&lt;/p&gt;

&lt;p&gt;First message in a fresh chat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;My FlashAlpha API key is fa_xxxxxxxxxxxxxxxxxxxxxxxx.
Use it on every FlashAlpha tool call.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude holds the key for the session and passes it as &lt;code&gt;apiKey&lt;/code&gt; on every call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Other clients, same &lt;code&gt;/mcp&lt;/code&gt; URL and same chat-paste pattern:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Claude Code CLI&lt;/span&gt;
claude mcp add flashalpha &lt;span class="nt"&gt;--transport&lt;/span&gt; http https://lab.flashalpha.com/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Cursor: &lt;em&gt;Settings then MCP Servers then Add Server&lt;/em&gt;. Name &lt;code&gt;flashalpha&lt;/code&gt;, URL &lt;code&gt;https://lab.flashalpha.com/mcp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Windsurf: &lt;em&gt;Cascade then MCP then Add Server&lt;/em&gt;, paste the same URL.&lt;/li&gt;
&lt;li&gt;VS Code (Copilot / Continue): add &lt;code&gt;{ "url": "https://lab.flashalpha.com/mcp" }&lt;/code&gt; to your MCP settings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why is the key not in the config? Claude Desktop's HTTP-transport MCP config only supports a URL. There is no &lt;code&gt;env&lt;/code&gt; or &lt;code&gt;headers&lt;/code&gt; pass-through that maps to a tool argument, and the key is a per-tool argument. So it comes from the conversation, or you skip it entirely with the OAuth endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"MCP Server not responding" on claude.ai.&lt;/strong&gt; You pasted &lt;code&gt;/mcp&lt;/code&gt; into a custom-connector dialog that needs OAuth. The &lt;code&gt;/mcp&lt;/code&gt; endpoint expects an &lt;code&gt;apiKey&lt;/code&gt; argument the browser flow never sends, so the first call hangs. Edit the connector URL to exactly &lt;code&gt;https://lab.flashalpha.com/mcp-oauth&lt;/code&gt; and reconnect. This is the one nearly everyone hits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"A server with this URL already exists" but nothing shows in your Connectors list.&lt;/strong&gt; Claude.ai recorded the URL during an earlier handshake that failed mid-flow. Try, in order: hard-refresh (Ctrl/Cmd+Shift+R) and retry Add; open claude.ai in an incognito window and Add from there; sign out and back in. If all three still error, send Anthropic support the &lt;code&gt;ofid_...&lt;/code&gt; reference from the original failure and ask them to clear the orphaned record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"A server with this URL already exists" and FlashAlpha is visible.&lt;/strong&gt; Close the dialog, click the existing entry, use &lt;strong&gt;Connect&lt;/strong&gt; / &lt;strong&gt;Reconnect&lt;/strong&gt;. If stuck, delete from the &lt;strong&gt;...&lt;/strong&gt; menu and re-add.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"apiKey is required" on Desktop.&lt;/strong&gt; Config is fine, Claude just has not been told the key in this chat. Paste it as your first message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Unauthorized" after hours of working.&lt;/strong&gt; Token expired before refresh. Settings then Connectors then FlashAlpha then &lt;strong&gt;Reconnect&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTP 429.&lt;/strong&gt; Daily quota hit (Free 5/day, Basic 100, Growth 2,500, Alpha unlimited). Reset is midnight UTC, or upgrade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"no_data" on &lt;code&gt;get_gex&lt;/code&gt;, &lt;code&gt;get_levels&lt;/code&gt;, &lt;code&gt;get_volatility&lt;/code&gt;.&lt;/strong&gt; Markets are closed. Not a connector bug. Retry during US market hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Desktop ignores the new config.&lt;/strong&gt; You reloaded instead of quitting. Cmd-Q on macOS, tray then Quit on Windows, then relaunch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get once it is green
&lt;/h2&gt;

&lt;p&gt;All 23 live tools work immediately. Highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exposure:&lt;/strong&gt; &lt;code&gt;get_gex&lt;/code&gt;, &lt;code&gt;get_dex&lt;/code&gt;, &lt;code&gt;get_vex&lt;/code&gt;, &lt;code&gt;get_levels&lt;/code&gt;, &lt;code&gt;get_exposure_summary&lt;/code&gt;, &lt;code&gt;get_narrative&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volatility and pricing:&lt;/strong&gt; &lt;code&gt;get_volatility&lt;/code&gt;, &lt;code&gt;get_stock_summary&lt;/code&gt;, &lt;code&gt;calculate_greeks&lt;/code&gt;, &lt;code&gt;solve_iv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Market data:&lt;/strong&gt; &lt;code&gt;get_stock_quote&lt;/code&gt;, &lt;code&gt;get_tickers&lt;/code&gt;, &lt;code&gt;get_option_chain&lt;/code&gt;, &lt;code&gt;get_account&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Historical replay&lt;/strong&gt; (Alpha tier): 17 tools that mirror the live ones with an &lt;code&gt;at=&amp;lt;timestamp&amp;gt;&lt;/code&gt; argument back to April 2018&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Four one-click workflow prompts also surface in the client UI: &lt;code&gt;analyze_exposure(symbol)&lt;/code&gt;, &lt;code&gt;vrp_regime_check(symbol)&lt;/code&gt;, &lt;code&gt;historical_comparison(symbol, reference_date)&lt;/code&gt; and &lt;code&gt;zero_dte_brief(symbol)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A good first prompt: &lt;em&gt;"Show me the gamma exposure for SPY and tell me where the gamma flip is."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Free tier needs only an email and password, no card: &lt;a href="https://flashalpha.com/pricing" rel="noopener noreferrer"&gt;flashalpha.com/pricing&lt;/a&gt;. The same key works on both endpoints.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>claude</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>FlashAlpha vs Quant Data: What an AI Agent Can Actually Reason Over</title>
      <dc:creator>tomasz dobrowolski</dc:creator>
      <pubDate>Wed, 27 May 2026 13:39:00 +0000</pubDate>
      <link>https://dev.to/tomasz_dobrowolski_35d32c/flashalpha-vs-quant-data-what-an-ai-agent-can-actually-reason-over-39ki</link>
      <guid>https://dev.to/tomasz_dobrowolski_35d32c/flashalpha-vs-quant-data-what-an-ai-agent-can-actually-reason-over-39ki</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclosure up front: I work on FlashAlpha. The factual claims are checkable against &lt;a href="https://quantdata.us/api/docs" rel="noopener noreferrer"&gt;quantdata.us/api/docs&lt;/a&gt; and &lt;a href="https://lab.flashalpha.com/swagger" rel="noopener noreferrer"&gt;lab.flashalpha.com/swagger&lt;/a&gt; as of 2026-05-27. The framing is opinion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Quant Data and FlashAlpha both publish themselves as real-time options analytics platforms with REST APIs and native MCP servers. From a feature-list distance they look like they compete head-to-head. They do not. They were architected for different classes of user and they expose fundamentally different data layers.&lt;/p&gt;

&lt;p&gt;This is the comparison the way a senior engineer would explain it to a colleague evaluating both.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-sentence framing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Quant Data is a trader workstation with an API attached. FlashAlpha is an API-first quant analytics engine with a UI attached.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That sentence does most of the work. Both platforms read the same underlying market data and both expose a REST surface plus an MCP server, so a checklist will produce overlap. The interesting comparison is not which checkbox each one ticks. It is which class of problem each platform was built to solve, and what that decision implies for everything downstream.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Quant Data&lt;/th&gt;
&lt;th&gt;FlashAlpha&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary product&lt;/td&gt;
&lt;td&gt;Hosted dashboard (web, iOS, Android) with 30+ tools; REST API on top&lt;/td&gt;
&lt;td&gt;REST API + MCP server; companion per-ticker pages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Design philosophy&lt;/td&gt;
&lt;td&gt;Trader workflow platform&lt;/td&gt;
&lt;td&gt;Quantitative analytics infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Greeks coverage&lt;/td&gt;
&lt;td&gt;First-order Greek-weighted exposures (delta, gamma, vega)&lt;/td&gt;
&lt;td&gt;First-order plus second-order (VEX, CHEX) with published 8-year backtest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Volatility surfaces&lt;/td&gt;
&lt;td&gt;Vol skew, term structure, IV rank&lt;/td&gt;
&lt;td&gt;Full SVI calibration, arbitrage-free constraints, sampleable smiles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flow scoring&lt;/td&gt;
&lt;td&gt;Flow analytics endpoints (Net Drift, Net Flow)&lt;/td&gt;
&lt;td&gt;Six-component composite with &lt;code&gt;score_breakdown&lt;/code&gt; in every response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open/close inference&lt;/td&gt;
&lt;td&gt;OI snapshots and changes&lt;/td&gt;
&lt;td&gt;Per-contract OI simulator, 0.43 confidence weight, daily calibration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dealer positioning&lt;/td&gt;
&lt;td&gt;Greek-weighted exposure series&lt;/td&gt;
&lt;td&gt;Positive vs negative gamma regime, flip-level computation, regime-conditioned VRP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing entry&lt;/td&gt;
&lt;td&gt;$124.99/mo annual, non-pro, personal use only&lt;/td&gt;
&lt;td&gt;Free 5 req/day; Basic $63/mo annual; commercial use allowed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Discretionary traders who want a polished UI + mobile&lt;/td&gt;
&lt;td&gt;Quants, devs, fintech apps, AI agents consuming derived state&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What each platform actually is
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quant Data
&lt;/h3&gt;

&lt;p&gt;A real-time options market intelligence platform built around a hosted dashboard. Web app plus native iOS and Android with drag-and-drop layouts and 30+ trading tools. A REST API exposes the same feeds programmatically, and an MCP server lets ChatGPT, Claude, Cursor, Gemini, and custom agents call it as a typed tool. The standard plan is gated to non-professionals with "personal use only" and no external redistribution.&lt;/p&gt;

&lt;p&gt;What you get: a polished dashboard product, 23 options endpoints, 6 equity endpoints, real-time alerts, and an MCP server. The data is the polished, finished feed; the API mirrors what the dashboard shows.&lt;/p&gt;

&lt;h3&gt;
  
  
  FlashAlpha
&lt;/h3&gt;

&lt;p&gt;An options quantitative analytics engine that ships as a REST API and an MCP server, with a companion site that surfaces the same analytics visually. The endpoints cover the same raw-flow territory Quant Data does, but the centre of gravity is one layer up: derived market state. Dealer-positioning regimes, second-order exposures, full SVI-calibrated arbitrage-free volatility surfaces, VRP series, an OI simulator that emits effective open interest in near real time, 0DTE analytics keyed on intraday gamma regime, and a six-component scored flow signal with the formula breakdown returned in every response.&lt;/p&gt;

&lt;p&gt;What you get: a documented REST API, SDKs in Python, JavaScript/TypeScript, C#, Go, and Java, an MCP server at &lt;code&gt;lab.flashalpha.com/mcp&lt;/code&gt; with typed tools for every endpoint, an OpenAPI playground at &lt;code&gt;lab.flashalpha.com/swagger&lt;/code&gt;, and methodology articles covering every derived value. No native mobile app, no flagship dashboard. The API is the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Quant Data does genuinely well
&lt;/h2&gt;

&lt;p&gt;This is the section that earns the article credibility. If a vendor-vs-vendor piece only critiques the competitor, the reader correctly discounts it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polished trader workflow.&lt;/strong&gt; Drag-and-drop layouts, iOS and Android apps, real-time alerts, exchange notifications, a clean UI for flow monitoring. Serious product surface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dark pool visibility.&lt;/strong&gt; Dark Flow, Dark Pool Levels, Equity Prints, and exchange notifications are first-class endpoints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API ergonomics.&lt;/strong&gt; The "many shapes, one operation" filter design (case-insensitive field names, shorthand and full-name operators, scalar or array values interchangeably) is a developer-experience win.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solid infrastructure positioning.&lt;/strong&gt; Published 240 req/min rate limit, 99.99% uptime SLA, 365+ day historical lookback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native MCP support.&lt;/strong&gt; They shipped MCP-as-typed-tools early and deserve credit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discretionary-trader fit.&lt;/strong&gt; The product is honestly built for a human in the loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is a hedge. Quant Data is a well-built trader platform. The question is whether it is the right product for a different class of problem, the one FlashAlpha was built to solve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flow data vs derived intelligence: the centerpiece distinction
&lt;/h2&gt;

&lt;p&gt;Most options APIs expose what an options market &lt;em&gt;did&lt;/em&gt;: trades, chains, sweeps, exposure snapshots, raw activity. This is necessary and useful. It is also one layer below where systematic strategies actually need to operate.&lt;/p&gt;

&lt;p&gt;FlashAlpha exposes that same raw flow, but also surfaces a layer above it: what the options market &lt;em&gt;is&lt;/em&gt;, in structural terms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical options API surface (activity):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order flow prints, options chains with first-order Greeks&lt;/li&gt;
&lt;li&gt;Sweep and block tags&lt;/li&gt;
&lt;li&gt;Exposure by strike and expiration&lt;/li&gt;
&lt;li&gt;Max pain, IV rank, vol skew snapshots&lt;/li&gt;
&lt;li&gt;Dark pool prints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;FlashAlpha's additional layer (structure):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dealer-positioning regime classification (positive vs negative gamma)&lt;/li&gt;
&lt;li&gt;VEX (vanna exposure) and CHEX (charm exposure) series&lt;/li&gt;
&lt;li&gt;SVI-calibrated arbitrage-free volatility surfaces&lt;/li&gt;
&lt;li&gt;VRP, regime-conditioned and directional&lt;/li&gt;
&lt;li&gt;Effective open interest from an intraday OI simulator&lt;/li&gt;
&lt;li&gt;Six-component scored flow signals with &lt;code&gt;score_breakdown&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Gamma flip levels and exposure concentration zones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Activity tells you what just happened. Structural data tells you what regime the market is in. Both matter; they are not substitutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quantitative differentiators
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Second-order exposures: VEX and CHEX
&lt;/h3&gt;

&lt;p&gt;Quant Data's exposure endpoints are Greek-weighted by first-order Greeks (delta, gamma, sometimes vega). Standard treatment, correct for most flow-monitoring use cases.&lt;/p&gt;

&lt;p&gt;FlashAlpha additionally surfaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VEX (vanna exposure):&lt;/strong&gt; sensitivity of dealer delta to implied volatility changes. Useful for vol-crush behaviour around earnings, dealer hedging shifts when vol moves, and regime transitions where the second-order term dominates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CHEX (charm exposure):&lt;/strong&gt; time-decay impact on dealer delta positioning. Useful for intraday drift, expiry effects (OPEX, quarterly), 0DTE dynamics, overnight roll-over.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FlashAlpha published an &lt;a href="https://flashalpha.com/articles/gex-dex-vex-chex-8-year-backtest-spy-vix-control" rel="noopener noreferrer"&gt;8-year backtest of GEX/DEX/VEX/CHEX as predictors of SPY returns and VIX changes&lt;/a&gt;. Most retail-focused platforms do not surface these in a usable form.&lt;/p&gt;

&lt;h3&gt;
  
  
  Arbitrage-free volatility surfaces (SVI)
&lt;/h3&gt;

&lt;p&gt;Quant Data exposes vol skew, term structure, IV rank, IV percentile. Standard.&lt;/p&gt;

&lt;p&gt;FlashAlpha exposes the full underlying surface infrastructure. SVI (Stochastic Volatility Inspired) calibrations per expiration with arbitrage-free constraints (no calendar arbitrage, no butterfly arbitrage), smile parameterisation that can be sampled at any strike or moneyness, term-structure interpolation, and the underlying liquidity-filtered fit data.&lt;/p&gt;

&lt;p&gt;This matters for any workflow that consumes IV as a primary input: backtesting, signal generation off skew dynamics, options pricing models, variance trading, ML features built from surface shape rather than snapshot points.&lt;/p&gt;

&lt;h3&gt;
  
  
  Volatility risk premium (VRP)
&lt;/h3&gt;

&lt;p&gt;VRP is the gap between implied volatility and realised volatility. It is the central premium that systematic options strategies harvest. Quant Data does not surface a published VRP endpoint in their API documentation as of writing.&lt;/p&gt;

&lt;p&gt;FlashAlpha publishes VRP as a first-class derived value in several conditioned forms: base VRP across the universe, GEX-conditioned (segmented by dealer-positioning regime), directional (put vs call decomposition), and a VRP-driven strategy scoring endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  OI simulator and effective open interest
&lt;/h3&gt;

&lt;p&gt;The OPRA tape carries the side of every trade but not whether it opens or closes a position. That information lives at the clearing firm and reaches the tape the next morning as a settled OI broadcast.&lt;/p&gt;

&lt;p&gt;Quant Data exposes the standard daily OI snapshots and changes. Same data the rest of the industry consumes.&lt;/p&gt;

&lt;p&gt;FlashAlpha runs a per-contract OI simulator that maintains a running signed intraday delta against the OPRA broadcast. The simulator's per-trade confidence weight is &lt;strong&gt;0.43&lt;/strong&gt;, calibrated daily against next-morning settled OI residuals. The output is an effective open interest field that updates intraday, which is what lets FlashAlpha compute live GEX from flow rather than only end-of-day settled GEX.&lt;/p&gt;

&lt;p&gt;For systematic strategies that care about intraday regime shifts, this is the difference between operating on yesterday's positioning and today's.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scoring transparency
&lt;/h3&gt;

&lt;p&gt;FlashAlpha's Flow Signals endpoint returns a six-component composite score with the full &lt;code&gt;score_breakdown&lt;/code&gt; in every response. Components: premium (log-normalised), size-vs-OI (ratio), aggressor strength (NBBO-position + side), sweep structure (sweep/block/single), opening bias (OI simulator output, 0.43 weight), tenor (linear decay to 45 DTE). Default weights and formulas are documented. The breakdown reconstructs the composite within rounding.&lt;/p&gt;

&lt;p&gt;Quant Data's order flow endpoints surface Net Drift, Net Flow, and Contract Statistics without a documented composite formula. Fine for a finished-signal product; a different choice from audit-trail-by-default.&lt;/p&gt;

&lt;h2&gt;
  
  
  The MCP / agent angle
&lt;/h2&gt;

&lt;p&gt;Both platforms ship native MCP servers. The interesting question is not whether a platform has an MCP surface but what shape the data is in when an agent consumes it.&lt;/p&gt;

&lt;p&gt;LLMs reason well over compact, structured, derived values and poorly over voluminous raw data. Handing an agent a 3,000-row chain JSON and asking it to compute exposures, fit a surface, or derive a regime is a misuse of the tool. There is no clean closed-form path from "here is the chain" to "this market is in a negative-gamma regime with VRP three standard deviations above the GEX-conditioned mean."&lt;/p&gt;

&lt;p&gt;An agent &lt;em&gt;can&lt;/em&gt; reason cleanly over precomputed derived state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What is the current gamma regime for SPX?" → one field, one value, one decision branch.&lt;/li&gt;
&lt;li&gt;"Is VRP elevated relative to its GEX-conditioned distribution?" → one signal, segmented by regime.&lt;/li&gt;
&lt;li&gt;"List the top-5 scored unusual flow signals on NVDA today, with &lt;code&gt;score_breakdown&lt;/code&gt;." → structured, bounded, auditable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FlashAlpha was designed assuming a non-trivial fraction of consumers would be machines rather than humans. The derived analytics layer is the surface area an AI agent actually wants. The raw chain is available, but it is not the primary product.&lt;/p&gt;

&lt;p&gt;Quant Data's MCP server exposes the same data layer as the dashboard: flow feeds, exposure snapshots, dark pool prints. For a workflow that wants an agent to surface what a trader would see, this is well-suited. For a workflow that wants an agent to reason over structural market state, the derived layer matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ideal use cases
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Better fit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Discretionary options flow trading from a dashboard&lt;/td&gt;
&lt;td&gt;Quant Data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile options flow monitoring (iOS, Android)&lt;/td&gt;
&lt;td&gt;Quant Data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dark pool prints and exchange notification feeds&lt;/td&gt;
&lt;td&gt;Quant Data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quantitative research on dealer positioning&lt;/td&gt;
&lt;td&gt;FlashAlpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Systematic volatility strategies (VRP harvest, variance, dispersion)&lt;/td&gt;
&lt;td&gt;FlashAlpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backtesting on second-order exposures (VEX, CHEX)&lt;/td&gt;
&lt;td&gt;FlashAlpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Building ML features from surface shape&lt;/td&gt;
&lt;td&gt;FlashAlpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live GEX/DEX from flow (intraday regime detection)&lt;/td&gt;
&lt;td&gt;FlashAlpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0DTE intraday gamma regime modelling&lt;/td&gt;
&lt;td&gt;FlashAlpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI / LLM agents reasoning over derived market state&lt;/td&gt;
&lt;td&gt;FlashAlpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API-first integration into a fintech app&lt;/td&gt;
&lt;td&gt;FlashAlpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit-trail scored unusual flow signals&lt;/td&gt;
&lt;td&gt;FlashAlpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free tier with no credit card for evaluation&lt;/td&gt;
&lt;td&gt;FlashAlpha (5 req/day)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your workflow centres on visual flow monitoring in a polished dashboard, Quant Data is the better fit and there is no shame in it. If it centres on systematic strategies, derived market state, or agent-driven automation, FlashAlpha is the better fit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing (2026-05-27)
&lt;/h2&gt;

&lt;p&gt;Quant Data's standard plan is explicitly marked "Non-professionals only" and "Personal use only. Not for external redistribution." If you are building a commercial product, a fintech app, or anything that redistributes data downstream, you need their enterprise tier (contact sales).&lt;/p&gt;

&lt;p&gt;FlashAlpha's paid tiers allow commercial use directly. Free (5 req/day, no card), Basic $63/mo annual (100 req/day), Growth $239/mo annual (2,500 req/day), Alpha $1,199/mo annual (unlimited, full derived analytics + historical replay).&lt;/p&gt;

&lt;p&gt;The right comparison is per-workflow, not per-dollar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the two genuinely overlap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Raw flow ingest, sweep/block tagging&lt;/li&gt;
&lt;li&gt;First-order Greek-weighted exposure series&lt;/li&gt;
&lt;li&gt;IV rank, percentile, vol skew&lt;/li&gt;
&lt;li&gt;Max pain&lt;/li&gt;
&lt;li&gt;Native MCP for agents&lt;/li&gt;
&lt;li&gt;Well-documented REST surfaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your workflow only uses the overlap surface, the two are closer to substitutes than this article suggests, and the choice should come down to pricing, ergonomics, and UI. The thesis here is that workflows that &lt;em&gt;only&lt;/em&gt; use the overlap surface are leaving the more interesting half of options analytics on the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one sentence
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;FlashAlpha is designed to expose quantitative market structure, not just market activity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If that sentence describes the layer your strategy or product needs, FlashAlpha is the right fit. If it doesn't, Quant Data may genuinely be the better-fit product for what you are doing, and that is a legitimate outcome.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Full article with FAQ, deeper methodology, and links to the published backtests on the &lt;a href="https://flashalpha.com/articles/flashalpha-vs-quantdata-quant-infrastructure-vs-trader-platform" rel="noopener noreferrer"&gt;canonical version&lt;/a&gt;. Free API key (5 req/day, no card) at &lt;a href="https://flashalpha.com/pricing" rel="noopener noreferrer"&gt;flashalpha.com/pricing&lt;/a&gt;. OpenAPI playground at &lt;a href="https://lab.flashalpha.com/swagger" rel="noopener noreferrer"&gt;lab.flashalpha.com/swagger&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>mcp</category>
      <category>ai</category>
      <category>options</category>
    </item>
    <item>
      <title>How to Trade a Long 0DTE Straddle, Read From a Single API Endpoint</title>
      <dc:creator>tomasz dobrowolski</dc:creator>
      <pubDate>Sun, 24 May 2026 08:13:35 +0000</pubDate>
      <link>https://dev.to/tomasz_dobrowolski_35d32c/how-to-trade-a-long-0dte-straddle-read-from-a-single-api-endpoint-480d</link>
      <guid>https://dev.to/tomasz_dobrowolski_35d32c/how-to-trade-a-long-0dte-straddle-read-from-a-single-api-endpoint-480d</guid>
      <description>&lt;p&gt;A long 0DTE straddle is a bet that today's remaining move will exceed what the market is currently pricing in.&lt;/p&gt;

&lt;p&gt;It's a small-edge trade. The default outcome on an unfiltered session is a loss. The structural forces working against the buyer (theta acceleration, pin risk, vol crush, slippage) are large, well-documented, and almost always at least one is dominating any given afternoon.&lt;/p&gt;

&lt;p&gt;The article that follows is the structural read I do before considering one. The data comes from a single API endpoint, &lt;code&gt;/v1/exposure/zero-dte/{symbol}&lt;/code&gt;, which returns regime, pin score, expected move, IV ratio and execution score in one call. The decision logic stays on your side. The endpoint does not give you a signal; it gives you the data to form one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you're paying for
&lt;/h2&gt;

&lt;p&gt;A long straddle is two long options at the same strike and same expiry: one call and one put, both at the at-the-money strike. The naïve breakevens sit symmetrically around the strike:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BE_upper = K + (C + P)
BE_lower = K - (C + P)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;K&lt;/code&gt; is the ATM strike and &lt;code&gt;C + P&lt;/code&gt; is the combined call + put mid. FlashAlpha exposes that combined mid directly at &lt;code&gt;expected_move.straddle_price&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two important framings before the structural read:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The straddle mid is not the strict 1-sigma.&lt;/strong&gt; Under the lognormal Black-Scholes convention the ATM straddle prices to roughly &lt;code&gt;0.8 × σ × S × √t&lt;/code&gt;, so it sits modestly below the true 1-sigma. The endpoint reports the strict statistical 1-sigma separately at &lt;code&gt;expected_move.implied_1sd_dollars&lt;/code&gt; (full session) and &lt;code&gt;expected_move.remaining_1sd_dollars&lt;/code&gt; (rest of session). Use the straddle mid for breakeven math and the 1-sigma fields for probability comparisons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Terminal payoff depends on the close, not the range.&lt;/strong&gt; A move that touches the upper breakeven at noon and reverts to the strike by 4 PM pays nothing if you hold to expiration. Realised range matters only if you exit at the moment of the move. Both views matter; they are not the same trade.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real breakeven includes round-trip slippage
&lt;/h2&gt;

&lt;p&gt;The naïve breakeven assumes you fill at mid on both entry and exit. You will not. On 0DTE you pay roughly half the bid-ask spread on each leg on entry, and again on exit, on both the call and the put. That is one full spread round-trip per leg, summed across two legs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BE_upper_real ≈ K + (C + P) + (S_C + S_P)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The FlashAlpha &lt;code&gt;liquidity.atm_spread_pct&lt;/code&gt; field is defined as the &lt;em&gt;average&lt;/em&gt; of the call and put spread percentages at the ATM strike. With that definition, round-trip slippage as a percentage of combined premium works out to roughly &lt;code&gt;atm_spread_pct&lt;/code&gt; itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(S_C + S_P) / (C + P) ≈ atm_spread_pct
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A SPY session with a 1% ATM spread costs roughly 1% of premium in round-trip slippage. A less-liquid name with a 3% spread costs roughly 3%. Linear in the spread, not the multiplicative blow-up retail traders sometimes assume. On a small-edge trade, even 1-3% slippage eats a meaningful share of the move you need to recoup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four forces against the buyer
&lt;/h2&gt;

&lt;p&gt;On a typical session, the buyer is fighting at least one of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Theta acceleration.&lt;/strong&gt; 0DTE gamma is several times larger than equivalent 7DTE gamma at the same strike (the endpoint reports the ratio at &lt;code&gt;decay.gamma_acceleration&lt;/code&gt;), and theta scales with gamma. The hourly bleed is captured at &lt;code&gt;decay.theta_per_hour_remaining&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pin risk.&lt;/strong&gt; When dealers are net long 0DTE gamma, hedging flow biases price toward the highest-OI strike into close. If the ATM strike is also the magnet, the position decays to near-zero on a session that closes near the strike. Captured at &lt;code&gt;pin_risk.pin_score&lt;/code&gt; (the endpoint labels 82 as a "strong pin").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vol crush.&lt;/strong&gt; A long straddle is long vega. If implied volatility declines through the session, the position loses on vega even when the underlying moves. The endpoint's &lt;code&gt;vol_context.iv_ratio_0dte_7dte&lt;/code&gt; compares 0DTE ATM IV against the next weekly. Ratios above 1.0 carry an event premium that will compress regardless of direction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slippage.&lt;/strong&gt; Covered above. The most underweighted force.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The buyer needs at least one of these to invert, or needs realised vol to outpace all of them combined. The default expectation for an unfiltered 0DTE straddle is a loss.&lt;/p&gt;

&lt;h2&gt;
  
  
  The negative-gamma timing trap
&lt;/h2&gt;

&lt;p&gt;"Negative gamma" sounds bullish for a long straddle because dealer hedging amplifies moves. The trap is that the regime label is a present-tense observation, not a forward-looking signal. Dealers typically &lt;em&gt;become&lt;/em&gt; short gamma after a sharp move has already pushed spot through the flip level. By the time &lt;code&gt;regime.label == "negative_gamma"&lt;/code&gt;, much of the move you needed may have already happened.&lt;/p&gt;

&lt;p&gt;Three sub-cases worth distinguishing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Already-realised vol.&lt;/strong&gt; Spot has gapped through the flip overnight or moved sharply pre-noon. The negative-gamma label is now true but the implied move is already partly spent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stable negative-gamma session.&lt;/strong&gt; Spot opened below the flip and is consolidating. The amplification force is present but not yet engaged. The cleanest backdrop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;About-to-cross.&lt;/strong&gt; &lt;code&gt;regime.label == "positive_gamma"&lt;/code&gt; but &lt;code&gt;regime.distance_to_flip_sigmas&lt;/code&gt; is well below 1.0. The straddle benefits from the cross during the session.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The endpoint cannot distinguish "already moved" from "about to move" on its own. Compare &lt;code&gt;expected_move.implied_1sd_dollars&lt;/code&gt; to the realised move so far. If the day's high-low range already exceeds the implied 1-sigma at the time of read, the easy part of the move is over.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five structural fields
&lt;/h2&gt;

&lt;p&gt;Five fields capture most of the structural picture and come back in a single response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regime and distance to flip:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;regime.label                          # "negative_gamma" or "positive_gamma"
regime.distance_to_flip_sigmas        # &amp;lt;1.0 = flip within a normal move
exposures.pct_of_total_gex            # &amp;gt;50% = 0DTE dominates intraday flow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pin risk and level clustering:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pin_risk.pin_score                    # 0-100; 82 = "strong pin" per API
pin_risk.oi_concentration_top3_pct    # higher = stronger magnet
levels.level_cluster_score            # 0-100; high = levels stacked at one strike
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;IV ratio (cost, not edge):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vol_context.iv_ratio_0dte_7dte        # &amp;lt;1.0 = cheap vs term; &amp;gt;1.0 = event premium
vol_context.zero_dte_atm_iv
vol_context.skew_25d                  # 25-delta risk reversal in IV points
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A common mistake: treating "cheap IV" as a payoff-probability signal. It is not. The IV ratio tells you about the &lt;em&gt;cost&lt;/em&gt; of the position, not the likelihood of payoff. A 0.85 ratio means you're paying a relatively low premium; it does not mean the move is more likely to happen. Cheap IV reduces gravity pulling the position toward zero; it does not push it toward profit.&lt;/p&gt;

&lt;p&gt;An IV ratio above 1.0 is an event premium. If you cannot name the event, assume the market knows something you don't and skip.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Liquidity and execution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;liquidity.atm_spread_pct
liquidity.weighted_spread_pct         # OI-weighted across the window
liquidity.execution_score             # 0-100 composite; directional read
metadata.snapshot_age_seconds         # &amp;lt;30s for trade decisions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;execution_score&lt;/code&gt; is reliably high on SPY and SPXW during regular hours. On smaller-cap names with 0DTE listings, a low score combined with a wide ATM spread is a hard veto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catalyst alignment
&lt;/h2&gt;

&lt;p&gt;Structural read tells you what dealer positioning looks like. It does not tell you whether the day has a reason to move. The most powerful predictor of realised 0DTE vol is whether the session contains a scheduled catalyst: CPI, PCE, NFP, retail sales, ISM, FOMC, central bank meetings outside the Fed, treasury auctions (especially 30Y).&lt;/p&gt;

&lt;p&gt;For single-name 0DTE straddles, post-close vs pre-open earnings matters because the 0DTE expiry usually does not capture the move if the report is after-hours.&lt;/p&gt;

&lt;p&gt;A catalyst-empty day with hostile structure is the average session that drains straddle premium.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sizing
&lt;/h2&gt;

&lt;p&gt;The maximum loss is the combined premium paid. Every US equity, ETF and index option contract carries a 100x multiplier. A SPY straddle quoted at $1.62 is &lt;strong&gt;$162 of risk per contract&lt;/strong&gt;, not $1.62. Sized at 5 contracts: $810 of risk before fees.&lt;/p&gt;

&lt;p&gt;Size as if the position goes to zero, because that is the modal outcome on sessions that don't deliver realised vol.&lt;/p&gt;

&lt;h2&gt;
  
  
  Settlement and exercise risk
&lt;/h2&gt;

&lt;p&gt;The most common way a 0DTE straddle trader ends up surprised on Monday morning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cash-settled index options&lt;/strong&gt; (SPXW, XSP, NDX, RUT): the ITM amount settles to cash. No stock position is created, no overnight risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Physically-settled equity and ETF options&lt;/strong&gt; (SPY, QQQ, IWM, NVDA, TSLA, single names): any leg ITM at expiration is auto-exercised by the OCC under the standard $0.01 threshold. For a long straddle, the call leg buys 100 shares per contract at the strike, or the put leg shorts 100 shares per contract. If the account cannot finance the long-stock buy or borrow for the short, the broker liquidates at Monday's opening print, which is often gapped against the trader.&lt;/p&gt;

&lt;p&gt;The rule: close both legs before 4:00 PM ET on physically-settled expiries, even the worthless one. Letting a near-zero leg expire OTM is fine; letting a profitable leg expire ITM creates a stock position you did not size for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exit rules tied to live fields
&lt;/h2&gt;

&lt;p&gt;Time-based exits ("exit at noon") work only because the average straddle decays predictably. A more robust ruleset references the same live fields used on entry:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Implied-move compression stop.&lt;/strong&gt; Compare entry &lt;code&gt;expected_move.remaining_1sd_dollars&lt;/code&gt; to current. If remaining implied move has compressed to a fraction of entry, the trade has been outwaited by the clock. Exit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regime-flip stop.&lt;/strong&gt; If &lt;code&gt;regime.label&lt;/code&gt; transitions to the opposite regime and spot moves back toward the straddle strike, the structural reason has reversed. Exit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pin-score escalation stop.&lt;/strong&gt; If &lt;code&gt;pin_risk.pin_score&lt;/code&gt; climbs into the "strong pin" range while the underlying is near the strike, exit before the magnet finishes its work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Profit lock.&lt;/strong&gt; When one leg has gained substantially and the other is approaching worthless, close the winning leg. Letting it ride is a directional bet you did not size for.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Three recurring traps
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Morning IV crush.&lt;/strong&gt; Buying at 9:31 AM, when overnight IV is still elevated and the day's range is unknown. The 0DTE IV typically declines through the first 30-60 minutes. Wait for the ratio to stabilise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mid-day pin trap.&lt;/strong&gt; Buying at 1:30 PM with the pin score already elevated, hoping for an afternoon breakout the gamma profile is actively resisting. The breakout occasionally happens; the more common outcome is a slow drift to the magnet and a near-total premium loss into close.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "move already happened" entry.&lt;/strong&gt; Buying after the underlying has already realised a meaningful fraction of the morning's implied 1-sigma. Front-of-day IV has typically crushed (the vol uncertainty that priced the morning straddle is partly resolved), and the session has less time remaining. You're buying the residual of a move someone else captured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extract and label
&lt;/h2&gt;

&lt;p&gt;The script below pulls the relevant fields and labels each as buyer-friendly or buyer-hostile based on the API's documented anchors. It does &lt;em&gt;not&lt;/em&gt; emit a buy/sell signal, because the right entry depends on calibration, catalysts and your own risk tolerance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com/v1/exposure/zero-dte/SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;regime&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;regime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;flip_sigmas&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;regime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;distance_to_flip_sigmas&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;zdte_share&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exposures&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pct_of_total_gex&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;pin&lt;/span&gt;           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pin_risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pin_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;cluster&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;levels&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level_cluster_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;iv_ratio&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vol_context&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iv_ratio_0dte_7dte&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;skew&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vol_context&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;skew_25d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;exec_score&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;liquidity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;execution_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;atm_spread&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;liquidity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;atm_spread_pct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;straddle_mid&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected_move&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;straddle_price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;implied_1sd&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected_move&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;implied_1sd_dollars&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;remaining_1sd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected_move&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;remaining_1sd_dollars&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# atm_spread_pct is the avg of call+put spread %.
# Round-trip slippage as % of premium ≈ atm_spread_pct.
&lt;/span&gt;&lt;span class="n"&gt;est_slippage_pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;atm_spread&lt;/span&gt;

&lt;span class="c1"&gt;# remaining_1sd / implied_1sd ≈ sqrt(t_remain / t_full).
# This is the time-decay scaling of the implied move,
# NOT realised price movement.
&lt;/span&gt;&lt;span class="n"&gt;implied_time_decay_pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;remaining_1sd&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;implied_1sd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;friendly&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;friendly&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;WARN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Regime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;regime&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;negative_gamma&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flip_sigmas&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;flip_sigmas&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0DTE dominates flow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zdte_share&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;IV not event-priced&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;iv_ratio&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;iv_ratio&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pin not forming&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Levels scattered&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Straddle mid:           $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;straddle_mid&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Implied 1sd (full):     $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;implied_1sd&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Remaining 1sd:          $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;remaining_1sd&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Implied-move time decay:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;implied_time_decay_pct&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%  (time only)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ATM spread:             &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;atm_spread&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Approx round-trip slip: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;est_slippage_pct&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;% of premium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Execution score:        &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exec_score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;25d skew:               &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;skew&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Note: realised range must be checked separately.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fetch today&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s OHLC and compare day&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s high-low to implied_1sd.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is a structured read, not a verdict.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;A long 0DTE straddle is a small-edge trade fighting four structural forces. The setups where the buyer wins are sessions where the structural read is friendly (negative gamma not yet realised, low pin, IV not event-priced, levels scattered, execution clean) &lt;strong&gt;and&lt;/strong&gt; a catalyst gives the day a reason to move. The endpoint returns the structural picture in one call. It does not give you a signal; it gives you the data to form one. The discipline is in the filter, the catalyst check, and the willingness to skip the trade on the majority of sessions where the structure doesn't align.&lt;/p&gt;




&lt;p&gt;Full guide with formulas typeset, the strangle alternative as a cheaper expression, and the curl version on the canonical: &lt;a href="https://flashalpha.com/articles/long-straddle-0dte-flashalpha-live-entry-signal" rel="noopener noreferrer"&gt;Long 0DTE Straddle on FlashAlpha&lt;/a&gt;. Free API key, no card, on the &lt;a href="https://flashalpha.com/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>api</category>
      <category>finance</category>
      <category>programming</category>
    </item>
    <item>
      <title>Inside an Unusual Options Activity Score: Six Components, One Audit Trail</title>
      <dc:creator>tomasz dobrowolski</dc:creator>
      <pubDate>Sat, 23 May 2026 19:24:33 +0000</pubDate>
      <link>https://dev.to/tomasz_dobrowolski_35d32c/inside-an-unusual-options-activity-score-six-components-one-audit-trail-5fo0</link>
      <guid>https://dev.to/tomasz_dobrowolski_35d32c/inside-an-unusual-options-activity-score-six-components-one-audit-trail-5fo0</guid>
      <description>&lt;p&gt;The unusual-options-flow market is full of black boxes. Vendors publish "scored" feeds without specifying what the score means: which trades qualify, how the score is computed, what threshold separates a meaningful signal from background noise.&lt;/p&gt;

&lt;p&gt;For a developer building a screener, this is unworkable. You cannot calibrate against a number whose derivation is hidden, and you cannot ignore false positives whose drivers you cannot see.&lt;/p&gt;

&lt;p&gt;This article is the methodology paper for the FlashAlpha Flow Signals API. The score is the deterministic, weighted average of six normalised components. Every component is returned in the response under &lt;code&gt;score_breakdown&lt;/code&gt;. The classification of every trade as a sweep or block, an opening or closing flow, a bullish or bearish bet, follows rules documented in code and reproduced here.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the feed actually is
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;GET /v1/flow/signals/{symbol}&lt;/code&gt; takes the raw block-sized prints from the OPRA tape, groups same-side prints that arrived within 500ms of each other on the same contract into single execution intents, and produces one scored, classified &lt;code&gt;FlowSignal&lt;/code&gt; per group.&lt;/p&gt;

&lt;p&gt;The output is the input layer for any system that needs to react to unusual activity: a trading dashboard, an alert ruleset, a backtest universe selector, or an LLM that needs structured options-flow context.&lt;/p&gt;

&lt;p&gt;The pipeline is split across pure-static components so every step is independently testable on synthetic tapes. No clock, no HTTP, no global state outside the inputs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. FlowDataClient.NotableTradesAsync  - windowed pull from notable-trade ring
2. FlowAnalyticsService.LoadAsync     - greek snapshots + OI simulator state
3. UnusualFlowScorer.Score            - pure classification and scoring
4. UnusualFlowEnricher.Enrich         - chain overlay (greeks, IV vs ATM, etc)
5. Controller                         - tier gate, filters, JSON shaping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sweep coalescing: 500ms, same side, same contract
&lt;/h2&gt;

&lt;p&gt;The single most important pre-processing step is sweep coalescing. A real institutional execution often crosses multiple venues in rapid succession: one bid lifted on PHLX, another at the same instant on CBOE, a third on AMEX, because that is how exchanges route a multi-leg sweep. On the tape, that single intent looks like three prints.&lt;/p&gt;

&lt;p&gt;Without coalescing, the score-it-once-per-print approach would either double-count the intent or under-rate it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;group(t_i, t_{i+1}) iff
    side(t_i) == side(t_{i+1})
    AND (t_{i+1} - t_i) &amp;lt;= 500ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any group with two or more prints is classified as a &lt;code&gt;sweep&lt;/code&gt;; a singleton block-sized print is a &lt;code&gt;block&lt;/code&gt;. The size used for scoring is the sum across the group; the price is the size-weighted average; the timestamp is the last print in the group.&lt;/p&gt;

&lt;p&gt;The 500ms window is deliberately conservative. Genuine cross-venue sweeps usually finish in under 100ms; the extra headroom catches venues that route via slower paths without false-coalescing prints that are merely temporally adjacent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The six scoring components
&lt;/h2&gt;

&lt;p&gt;Every coalesced group is scored along six axes. Each component is normalised to &lt;code&gt;[0,1]&lt;/code&gt; before weighting, the components are combined as a weighted average, and the result is scaled to a 0-100 integer.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Premium (weight 1.0)
&lt;/h3&gt;

&lt;p&gt;The notional dollar value of the trade (price × size × 100), log-normalised against a $10M cap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n_premium = clamp( log10(1 + P) / log10(1 + P_cap), 0, 1 )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A $1M trade scores about 0.86, a $100K trade about 0.71, a $10M trade saturates at 1.00. The log normalisation is deliberate: linear scaling would push every $100K-$1M print to noise and only spotlight the largest tape.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Size vs Open Interest (weight 1.0)
&lt;/h3&gt;

&lt;p&gt;The ratio of trade size to the contract's settled OI, capped at 1.0.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n_size_vs_oi = clamp( size / max(1, settled_oi), 0, 1 )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the unusual-activity classic. A 50,000-lot SPY 0DTE print on a strike with 800,000 OI scores 0.0625; the same 50,000-lot on a single-name back-month strike with 30,000 OI saturates at 1.0. That is intended behaviour: relative significance, not absolute size. For absolute-size ranking, sort by &lt;code&gt;premium&lt;/code&gt; instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Aggressor Strength (weight 0.8)
&lt;/h3&gt;

&lt;p&gt;Where the print landed inside the NBBO, side-aware.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n_aggressor =
    0.4                        if side == mid
    0.5                        if spread &amp;lt;= 0 (crossed/locked)
    (price - bid) / spread     if side == buy
    (ask - price) / spread     if side == sell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A buyer who lifts the offer scores near 1.0; a buyer paying the midpoint scores around 0.5. Mid prints get a flat 0.4 because they cannot be classified as aggressive in either direction with confidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Sweep Structure (weight 1.0)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n_sweep =
    1.00   coalesced group, &amp;gt;= 2 prints (sweep)
    0.55   singleton block-sized print (block)
    0.20   single print below block size (rare)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sweeps signal urgency. A standalone block is meaningful but compatible with patient, single-venue execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Opening Bias (weight 1.2, but capped at 0.43)
&lt;/h3&gt;

&lt;p&gt;Whether the trade opens new positions or closes existing ones, from the OI simulator's signed intraday delta.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;n_opening&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bias_score&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;oi_confidence&lt;/span&gt;

&lt;span class="n"&gt;bias_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="mf"&gt;1.0&lt;/span&gt;   &lt;span class="n"&gt;net&lt;/span&gt; &lt;span class="n"&gt;OI&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OpeningBias&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="mf"&gt;0.3&lt;/span&gt;   &lt;span class="n"&gt;net&lt;/span&gt; &lt;span class="n"&gt;OI&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClosingBias&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="mf"&gt;0.5&lt;/span&gt;   &lt;span class="n"&gt;net&lt;/span&gt; &lt;span class="n"&gt;OI&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;oi_confidence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.43&lt;/span&gt;   &lt;span class="c1"&gt;# calibrated against next-morning settled OI
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the most important design choice in the entire scorer.&lt;/p&gt;

&lt;p&gt;Even a "perfect" opening signal contributes at most &lt;strong&gt;0.43&lt;/strong&gt; to the normalised component, not 1.0. With the default weight of 1.2 out of total 5.6, the opening-bias bucket can contribute at most &lt;code&gt;100 * 1.2 * 0.43 / 5.6&lt;/code&gt; ≈ &lt;strong&gt;9.2 points&lt;/strong&gt; to the composite score.&lt;/p&gt;

&lt;p&gt;That ceiling is intentional: the simulator's calibrated confidence is 0.43, and a scoring model that gave full credit to opening signals would be overstating what the underlying inference can actually prove. Honest in, honest out.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Tenor (weight 0.6)
&lt;/h3&gt;

&lt;p&gt;Days to expiration, normalised so short-dated trades score higher.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;n_tenor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;dte&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;T_cap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# T_cap = 45 default
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 0DTE print scores 1.0, a 22-DTE print scores 0.51, a 45+-DTE print scores 0. LEAPS get zero credit for tenor. Shorter-dated flow has more time-discounted information value.&lt;/p&gt;

&lt;h2&gt;
  
  
  The composite
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;contribution_i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;w_i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n_i&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w_j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contributions&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Default weights (sum 5.6): &lt;code&gt;premium=1.0&lt;/code&gt;, &lt;code&gt;size_vs_oi=1.0&lt;/code&gt;, &lt;code&gt;aggressor=0.8&lt;/code&gt;, &lt;code&gt;sweep=1.0&lt;/code&gt;, &lt;code&gt;opening_bias=1.2&lt;/code&gt;, &lt;code&gt;tenor=0.6&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The breakdown lives in the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"score_breakdown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"premium"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"size_vs_oi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"aggressor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sweep"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"opening_bias"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tenor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the buckets and you get the score, within rounding. Any score in the feed can be reconstructed from the trade + OI context using the formulas above. There are no hidden adjustments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intent classification
&lt;/h2&gt;

&lt;p&gt;Once scored, the scorer assigns a directional intent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;intent =
    Neutral   if bias == ClosingBias        # unwind, direction unknown
    Neutral   if side == Mid
    Bullish   if BuyCall  or SellPut
    Bearish   if SellCall or BuyPut
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The closing-bias-collapses-to-Neutral rule is load-bearing. A closing trade tells you what is being unwound, but the direction the unwinder originally bet is generally unknown to the tape. Calling a closing trade "Bullish" or "Bearish" would over-claim.&lt;/p&gt;

&lt;p&gt;The consumer can still read &lt;code&gt;open_close_bias&lt;/code&gt; directly if they want to count closing flow separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conviction labels and the "golden" tag
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Conviction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;80-100&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;60-79&lt;/td&gt;
&lt;td&gt;medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;40-59&lt;/td&gt;
&lt;td&gt;low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0-39&lt;/td&gt;
&lt;td&gt;minimal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A signal is tagged &lt;code&gt;golden&lt;/code&gt; if its score is in the top decile of the current result set &lt;strong&gt;and&lt;/strong&gt; meets the absolute floor of 70. The dual gate stops a quiet session from promoting a mediocre signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Greeks enrichment overlay
&lt;/h2&gt;

&lt;p&gt;After scoring, the enricher joins the signal with the settled greek snapshot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;iv&lt;/code&gt;, &lt;code&gt;delta&lt;/code&gt;, &lt;code&gt;gamma&lt;/code&gt; from the snapshot (null if just-listed)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iv_vs_atm&lt;/code&gt;: the contract's IV minus the ATM IV on its expiry&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;moneyness&lt;/code&gt;: &lt;code&gt;ITM&lt;/code&gt; if &lt;code&gt;|δ| &amp;gt;= 0.65&lt;/code&gt;, &lt;code&gt;OTM&lt;/code&gt; if &lt;code&gt;&amp;lt;= 0.35&lt;/code&gt;, else &lt;code&gt;ATM&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;estimated_delta_notional = size * 100 * δ * S&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hypothetical_gex_impact_if_opening = size * 100 * Γ * S² * 0.01&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The hypothetical GEX field is named for a reason. The live chain on the flow surface already folds intraday OI deltas in, so adding the per-signal impact on top would double-count. Use it to size individual trades, not to recompute chain GEX.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick example: top bullish sweeps on SPY
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com/v1/flow/signals/SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;windowMinutes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bullish&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;structure&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sweep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;minScore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signals&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;expiry&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;strike&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  size=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;size&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;premium&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  score=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;conviction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tags&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What it isn't
&lt;/h2&gt;

&lt;p&gt;Three honest scope limits:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not a full tape replay.&lt;/strong&gt; The notable ring is capped at 512 prints; this is a windowed scan, not exhaustive history.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not single-print precision intent.&lt;/strong&gt; Multi-leg structures (verticals, butterflies, calendars) aren't detected at this layer. A bear-call-spread leg that buys the lower call still reads as a bullish single leg here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not a replacement for per-strike GEX.&lt;/strong&gt; The chain-level live GEX number lives at &lt;code&gt;/v1/flow/gex&lt;/code&gt;, computed against effective OI. Use both: signals for intent and significance, the GEX endpoint for chain-level dealer exposure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the score breakdown matters
&lt;/h2&gt;

&lt;p&gt;Every unusual-activity vendor publishes a score. Almost none of them tell you what is inside it. The buyer is forced to trust the number, the implicit weighting, and the (undisclosed) calibration choices the vendor made years ago and may or may not have revisited.&lt;/p&gt;

&lt;p&gt;The default weights (1.0 / 1.0 / 0.8 / 1.0 / 1.2 / 0.6) were set by eye after looking at a representative range of SPY, NVDA, and TSLA sessions. There is no claim that these weights are statistically optimal. They are documented defaults you can override per-request. The audit trail in &lt;code&gt;score_breakdown&lt;/code&gt; exists partly so customers can build their own re-weighting on top of the normalised components.&lt;/p&gt;

&lt;p&gt;The score is the product. The methodology is the warranty.&lt;/p&gt;




&lt;p&gt;Full methodology paper with all formulas typeset, FAQ, and worked examples: &lt;a href="https://flashalpha.com/articles/flow-signals-api-scoring-unusual-options-activity" rel="noopener noreferrer"&gt;Flow Signals API on FlashAlpha&lt;/a&gt;. Free API key, no card required, on the &lt;a href="https://flashalpha.com/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>api</category>
      <category>finance</category>
      <category>data</category>
    </item>
    <item>
      <title>Build a Working Unusual Options Activity Scanner in Python (Under 150 Lines)</title>
      <dc:creator>tomasz dobrowolski</dc:creator>
      <pubDate>Sat, 23 May 2026 19:24:09 +0000</pubDate>
      <link>https://dev.to/tomasz_dobrowolski_35d32c/build-a-working-unusual-options-activity-scanner-in-python-under-150-lines-oeb</link>
      <guid>https://dev.to/tomasz_dobrowolski_35d32c/build-a-working-unusual-options-activity-scanner-in-python-under-150-lines-oeb</guid>
      <description>&lt;p&gt;If you want to build an unusual options activity scanner, you have two choices.&lt;/p&gt;

&lt;p&gt;You can buy a packaged alert product from a vendor that picks the strategy for you and charges per ticker. Or you can call a transparent flow API yourself and write the scanner you actually want.&lt;/p&gt;

&lt;p&gt;This article walks through the second route end to end: every line of Python, every parameter, every alert format. By the end you'll have a single-file scanner you can run locally, schedule with cron, or wire into Slack.&lt;/p&gt;

&lt;p&gt;The underlying feed used here is the FlashAlpha Flow Signals API, which scores every block-sized print on a 0-100 scale with six documented components (premium, size-vs-OI, aggressor, sweep, opening bias, tenor). The full methodology is a separate article; the short version is that every score in the response carries a &lt;code&gt;score_breakdown&lt;/code&gt; object you can audit. We'll filter, rank, render, and alert on top of that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FLASHALPHA_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need Python 3.9+ and an Alpha-tier API key. The signals endpoints are gated to Alpha. &lt;code&gt;requests&lt;/code&gt; is the only dependency for the scanner itself; if you want fancier terminal output, &lt;code&gt;rich&lt;/code&gt; is a drop-in addition but the article doesn't use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: The signals call
&lt;/h2&gt;

&lt;p&gt;One function, one HTTP call, one parsed JSON response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;API_BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FLASHALPHA_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_signals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;min_score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;structure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;expiry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;One call to /v1/flow/signals/{symbol}.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;windowMinutes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;minScore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;min_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;structure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;structure&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;structure&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;expiry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expiry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expiry&lt;/span&gt;

    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API_BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/flow/signals/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is a top-level metadata wrapper plus a &lt;code&gt;signals&lt;/code&gt; array. Each signal carries the trade fields, classification, score, breakdown, and the chain context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: A pretty terminal renderer
&lt;/h2&gt;

&lt;p&gt;The point of a transparent feed is that you can read the per-component score, not just the composite. Render the breakdown alongside the signal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render_signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromisoformat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Z&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;+00:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;bd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score_breakdown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;bd_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;expiry&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;strike&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;side&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;size=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;size&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;premium&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;conviction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;open_close_bias&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bd_str&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;]  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scan_and_print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch_signals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;=== &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;count&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; signals ===&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chain: call_wall=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;chain&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;call_wall&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;put_wall=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;chain&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;put_wall&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flip=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;chain&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gamma_flip&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signals&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;render_signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;scan_and_print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;structure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sweep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One line per qualifying signal with every score component, the intent, the open/close bias, the chain context, and the tags. No interpretation layer, no opaque rank.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: A watchlist scanner
&lt;/h2&gt;

&lt;p&gt;The summary endpoint returns a directional roll-up in one call per ticker. Concurrent fetches let you sweep a watchlist in under a second.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;concurrent.futures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;

&lt;span class="n"&gt;WATCHLIST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QQQ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;IWM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NVDA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TSLA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AAPL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;META&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AMZN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API_BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/flow/signals/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;windowMinutes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;watchlist_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;watchlist&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;WATCHLIST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;=== Watchlist Direction Card (window=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;m) ===&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SYM&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DIR&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;NET PREMIUM&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OPEN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CLOSE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SIGNALS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;fetch_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;watchlist&lt;/span&gt;
        &lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;net&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;net_directional_premium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BULLISH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BEARISH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NEUTRAL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;opening_premium&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;closing_premium&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;signal_count&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output on a busy session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=== Watchlist Direction Card (window=60m) ===
SYM    DIR           NET PREMIUM           OPEN          CLOSE  SIGNALS
SPY    BULLISH    $   4,820,000   $  6,420,000   $  1,600,000        42
QQQ    BULLISH    $   1,140,000   $  2,810,000   $  1,670,000        28
NVDA   BEARISH    $  -2,250,000   $    410,000   $  2,660,000        17
TSLA   BULLISH    $   3,100,000   $  3,420,000   $    320,000        12
AAPL   NEUTRAL    $     -45,000   $     80,000   $    125,000         3
AMZN   NEUTRAL    $          0    $          0   $          0         0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SPY is bullish with mostly fresh positioning (open &amp;gt; close). NVDA looks bearish at the net but closing premium dominates opening, so it's mostly position unwinding, not fresh bearish conviction. The opening/closing split is doing real work here. Most UOA feeds publish only the net.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Custom filters - whale sweeps only
&lt;/h2&gt;

&lt;p&gt;Compose the filter parameters for the specific pattern you care about. Whale (premium ≥ $1M) sweeps with high conviction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;whale_sweeps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch_signals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;structure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sweep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;min_score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signals&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;whale&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sym&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;WATCHLIST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;whale_sweeps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;render_signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;whale&lt;/code&gt; tag fires automatically at $1,000,000+ premium.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Golden signals only
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;golden&lt;/code&gt; tag is a dual gate: top decile of the current result set &lt;strong&gt;and&lt;/strong&gt; at or above 70 score. A quiet session doesn't promote weak signals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;golden_signals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch_signals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signals&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;golden&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Opening-only bullish scan
&lt;/h2&gt;

&lt;p&gt;Most directional bets worth surfacing are opening flows, not closing flows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;opening_bullish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch_signals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bullish&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;min_score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;min_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signals&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open_close_bias&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opening_bias&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;intent=bullish&lt;/code&gt; already drops every closing trade (closing flows collapse to Neutral). The client-side check is the explicit double-gate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Slack alerts
&lt;/h2&gt;

&lt;p&gt;Wire any filter into a Slack incoming webhook. Send the score breakdown in the alert body so the human reading it can see why it fired without leaving Slack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;SLACK_WEBHOOK_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;slack_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score_breakdown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#22c55e&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bullish&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#ef4444&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bearish&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#a1a1aa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attachments&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;color&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;expiry&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;strike&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;side&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  score=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;conviction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;size: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;size&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;premium: $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;premium&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bias: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;open_close_bias&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;structure: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;structure&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tags&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;breakdown: prem=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;premium&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; sz=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;size_vs_oi&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aggr=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;aggressor&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; swp=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sweep&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;opening_bias&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ten=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tenor&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;slack_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;slack_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 8: De-duplication on a schedule
&lt;/h2&gt;

&lt;p&gt;Polling every minute, the same signal appears in consecutive responses until it ages out of the window. Hash the natural key and remember.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;

&lt;span class="n"&gt;_alerted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;signal_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;expiry&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;strike&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;size&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()[:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;alert_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_alerted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="n"&gt;_alerted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;slack_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Coalesced sweeps come through with their group's last-print timestamp and combined size, so the key is stable for the duration of the window. For long-running daemons, periodically prune entries older than the window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9: The single-file daemon
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;loop_forever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;watchlist&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;WATCHLIST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;poll_seconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;min_score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UOA daemon starting on &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;watchlist&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, polling every &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;poll_seconds&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sym&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;watchlist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;opening_bullish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_score&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;golden&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                        &lt;span class="nf"&gt;alert_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timespec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  HTTP &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - retrying next tick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timespec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - retrying next tick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poll_seconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;loop_forever&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Under 150 lines, one dependency, alerts to Slack with full breakdown, deterministic de-dup, swappable filters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you avoid by building it yourself
&lt;/h2&gt;

&lt;p&gt;Packaged UOA product:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Black-box score with no per-component breakdown&lt;/li&gt;
&lt;li&gt;Alert thresholds the vendor picks, not you&lt;/li&gt;
&lt;li&gt;Per-ticker pricing that gets expensive fast&lt;/li&gt;
&lt;li&gt;Cannot inspect why an alert fired (or did not)&lt;/li&gt;
&lt;li&gt;Wire format is whatever the vendor provides&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;API + 150 lines of Python:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every component of every score in the response&lt;/li&gt;
&lt;li&gt;You pick filters, thresholds, watchlist&lt;/li&gt;
&lt;li&gt;One key, unlimited symbols, no per-ticker fees&lt;/li&gt;
&lt;li&gt;Every alert carries its audit trail&lt;/li&gt;
&lt;li&gt;Slack, Discord, Telegram, terminal, whatever you want&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Five natural extensions
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;0DTE-only filter.&lt;/strong&gt; Pass &lt;code&gt;expiry=&amp;lt;today's ET date&amp;gt;&lt;/code&gt;. Combine with &lt;code&gt;structure=sweep&lt;/code&gt; for intraday momentum.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-reference with chain context.&lt;/strong&gt; Compare each signal's strike against &lt;code&gt;chain.call_wall&lt;/code&gt;, &lt;code&gt;chain.put_wall&lt;/code&gt;, &lt;code&gt;chain.gamma_flip&lt;/code&gt;. A sweep through a wall is structurally different from one in no-man's-land.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Score-weighted leaderboard.&lt;/strong&gt; Aggregate by ticker, sum premium weighted by score. Daily top-5 at 4:05 PM ET.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross with live GEX direction.&lt;/strong&gt; Alert only when intent agrees with the live GEX regime shift.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persist to a database.&lt;/strong&gt; Drop alerts into Postgres or DuckDB keyed by &lt;code&gt;signal_key&lt;/code&gt;. Build your own residual analysis: how often did "golden bullish opening sweep" precede a meaningful move? The breakdown makes per-component backtests trivial.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Full tutorial with worked examples and FAQ on the canonical: &lt;a href="https://flashalpha.com/articles/build-unusual-options-activity-scanner-python-api" rel="noopener noreferrer"&gt;Build an Unusual Options Activity Scanner with Python&lt;/a&gt;. The methodology pillar that explains every field in the response: &lt;a href="https://flashalpha.com/articles/flow-signals-api-scoring-unusual-options-activity" rel="noopener noreferrer"&gt;Flow Signals API methodology&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>api</category>
      <category>programming</category>
    </item>
    <item>
      <title>VRP Short Put Spreads: An Honest 7-Year Backtest Across 5 Symbols</title>
      <dc:creator>tomasz dobrowolski</dc:creator>
      <pubDate>Thu, 21 May 2026 10:19:18 +0000</pubDate>
      <link>https://dev.to/tomasz_dobrowolski_35d32c/vrp-short-put-spreads-an-honest-7-year-backtest-across-5-symbols-5gpl</link>
      <guid>https://dev.to/tomasz_dobrowolski_35d32c/vrp-short-put-spreads-an-honest-7-year-backtest-across-5-symbols-5gpl</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Framing correction (read first).&lt;/strong&gt; The thesis of this article ("the edge does not survive honest execution") is &lt;strong&gt;overstated&lt;/strong&gt;. A controlled follow-up, &lt;a href="https://flashalpha.com/articles/vrp-backtest-fill-model-is-the-edge" rel="noopener noreferrer"&gt;VRP Backtest: The Fill Model Is the Edge&lt;/a&gt;, shows the result is &lt;strong&gt;bounded by the fill-model assumption&lt;/strong&gt;: honest fills give breakeven to negative (these numbers); idealized mid-fills (the universal public-backtest default) give strongly positive. The correct conclusion is &lt;em&gt;execution-fragility and a result-range straddling zero&lt;/em&gt;, not "VRP is dead." Read this piece as the pessimistic bound of that range.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Search "volatility risk premium strategy" and you will find a hundred posts with an equity curve going up and to the right and a win rate north of 80%. They are almost all built on three quiet lies: mid-price fills, clean profit-target exits, and parameters chosen on the same symbol they are advertised on. This study removes all three.&lt;/p&gt;

&lt;p&gt;We took a single short-put-spread VRP harvest, tuned it &lt;strong&gt;once&lt;/strong&gt; on SPY in prior work, &lt;strong&gt;froze every parameter&lt;/strong&gt;, and ran it unchanged across QQQ, IWM, AMZN, and NVDA from 2019 to 2026. SPXW was added as a flagship out-of-sample check via a completely separate daily-resolution data path. Execution was modeled the way a real account experiences it: post-and-wait limit orders, exits that have to cross the spread, a stale-quote guard, and a signal that can only see the past.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;Trades&lt;/th&gt;
&lt;th&gt;Win rate&lt;/th&gt;
&lt;th&gt;Profit factor&lt;/th&gt;
&lt;th&gt;Sharpe&lt;/th&gt;
&lt;th&gt;CAGR %&lt;/th&gt;
&lt;th&gt;MaxDD %&lt;/th&gt;
&lt;th&gt;Fill rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;SPY&lt;/strong&gt; &lt;em&gt;(tuned)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;335&lt;/td&gt;
&lt;td&gt;71.6%&lt;/td&gt;
&lt;td&gt;1.10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.23&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.05&lt;/td&gt;
&lt;td&gt;6.48&lt;/td&gt;
&lt;td&gt;13.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NVDA&lt;/td&gt;
&lt;td&gt;201&lt;/td&gt;
&lt;td&gt;72.1%&lt;/td&gt;
&lt;td&gt;1.12&lt;/td&gt;
&lt;td&gt;0.22&lt;/td&gt;
&lt;td&gt;0.84&lt;/td&gt;
&lt;td&gt;6.87&lt;/td&gt;
&lt;td&gt;6.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AMZN&lt;/td&gt;
&lt;td&gt;139&lt;/td&gt;
&lt;td&gt;71.2%&lt;/td&gt;
&lt;td&gt;1.08&lt;/td&gt;
&lt;td&gt;0.13&lt;/td&gt;
&lt;td&gt;0.42&lt;/td&gt;
&lt;td&gt;7.38&lt;/td&gt;
&lt;td&gt;3.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QQQ&lt;/td&gt;
&lt;td&gt;162&lt;/td&gt;
&lt;td&gt;69.8%&lt;/td&gt;
&lt;td&gt;0.96&lt;/td&gt;
&lt;td&gt;−0.08&lt;/td&gt;
&lt;td&gt;−0.23&lt;/td&gt;
&lt;td&gt;7.76&lt;/td&gt;
&lt;td&gt;7.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IWM&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;td&gt;63.8%&lt;/td&gt;
&lt;td&gt;0.72&lt;/td&gt;
&lt;td&gt;−0.35&lt;/td&gt;
&lt;td&gt;−0.11&lt;/td&gt;
&lt;td&gt;1.24&lt;/td&gt;
&lt;td&gt;3.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SPXW †&lt;/td&gt;
&lt;td&gt;112&lt;/td&gt;
&lt;td&gt;61.6%&lt;/td&gt;
&lt;td&gt;0.37&lt;/td&gt;
&lt;td&gt;−0.39&lt;/td&gt;
&lt;td&gt;−0.66&lt;/td&gt;
&lt;td&gt;5.81&lt;/td&gt;
&lt;td&gt;100% †&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Period: 2019-02-01 to 2026-04-02 (7.16 years). $100k notional, unlevered Half-Kelly.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;† SPXW is sourced at daily-close resolution from the Historical API; fill rate is 100% by construction. Direction check only.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Win rate clusters at &lt;strong&gt;64-72%&lt;/strong&gt; across every symbol. The VRP probability edge is real and travels. Sharpe ranges from +0.23 to −0.39. &lt;strong&gt;Two of four out-of-sample symbols lost money. SPXW lost money. The premium is real; the tradeable edge is not.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The strategy (frozen specification)
&lt;/h2&gt;

&lt;p&gt;A short put &lt;strong&gt;credit spread&lt;/strong&gt; on the underlying, gated by a market-regime VRP signal. Every number below was fixed before the cross-sectional run and never changed per symbol.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structure&lt;/strong&gt;: short put vertical spread (defined risk)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short leg target&lt;/strong&gt;: 0.10 delta (far-wing premium)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DTE target&lt;/strong&gt;: 14 days (±2)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PT / SL&lt;/strong&gt;: 50% / 100% of credit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spread widths&lt;/strong&gt;: 5 / 10 / 15 / 20 / 25 / 30 points (EV-ranked at entry from observable greeks only)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sizing&lt;/strong&gt;: Half-Kelly, default 0.05, cap 0.25 (unlevered)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entry time&lt;/strong&gt;: 10:05 ET&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regime gate&lt;/strong&gt;: one SPY/VIX market-regime signal applied to all five symbols (this is what makes the four extra symbols a genuine OOS test rather than four fitted strategies)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Circuit breaker&lt;/strong&gt;: 30% peak-to-trough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The regime signal is built from VIX, VIX9D, VVIX, the VIX term structure, a realized-vs-implied VRP proxy and its 252-day percentile, HY spreads, and stress z-scores. It outputs &lt;code&gt;risk_on&lt;/code&gt; / &lt;code&gt;neutral&lt;/code&gt; / &lt;code&gt;reduce&lt;/code&gt; / &lt;code&gt;risk_off&lt;/code&gt; with a continuous Kelly multiplier. &lt;code&gt;risk_off&lt;/code&gt; means no trade. Construction is leak-free; methodology in &lt;a href="https://flashalpha.com/articles/historical-vrp-percentiles-no-lookahead-bias-backtesting" rel="noopener noreferrer"&gt;Historical VRP Percentiles Without Lookahead Bias&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The honesty controls
&lt;/h2&gt;

&lt;p&gt;Each control below removes a specific way backtests lie. This is the entire point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Leak-free signal.&lt;/strong&gt; The signal CSV is shifted forward one calendar day with a 7-day walk-back lookup. Any trading day's decision uses only data fully observable before that day's open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Honest fills (post-and-wait limits).&lt;/strong&gt; We do not fill at mid. We post a limit at &lt;code&gt;ask_edge&lt;/code&gt; and wait for someone else to cross our price. If nobody does, the order is cancelled. That is why fill rates are 3-14% and not 100%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Stale-quote guard.&lt;/strong&gt; When a cross is detected, we re-check that the mid has not moved through our limit by more than a floor. A one-tick bid blip during a vol spike does not get to manufacture a phantom fill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Patient-then-cross exits.&lt;/strong&gt; Profit-target and stop-loss exits are not free. We post a buy-to-close limit at the trigger and wait. If it does not fill, we cross the spread and pay the offer. Those exits are tagged &lt;code&gt;pt_x&lt;/code&gt; / &lt;code&gt;sl_x&lt;/code&gt; so the execution tax is auditable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. EV-blind tiebreak.&lt;/strong&gt; When multiple candidate spreads cross on the same bar, the winner is chosen by a timestamp-seeded random shuffle, never by best EV. Any EV-aware tiebreak is a look-ahead oracle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Frozen, never re-fit.&lt;/strong&gt; Tuned once on SPY in prior work. Then frozen. QQQ, IWM, AMZN, NVDA, SPXW received zero per-symbol optimization.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why a 70% win rate nets to nothing
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;Avg win&lt;/th&gt;
&lt;th&gt;Avg loss&lt;/th&gt;
&lt;th&gt;Win:loss&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SPY&lt;/td&gt;
&lt;td&gt;$369&lt;/td&gt;
&lt;td&gt;−$850&lt;/td&gt;
&lt;td&gt;1 : 2.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QQQ&lt;/td&gt;
&lt;td&gt;$311&lt;/td&gt;
&lt;td&gt;−$751&lt;/td&gt;
&lt;td&gt;1 : 2.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AMZN&lt;/td&gt;
&lt;td&gt;$407&lt;/td&gt;
&lt;td&gt;−$933&lt;/td&gt;
&lt;td&gt;1 : 2.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NVDA&lt;/td&gt;
&lt;td&gt;$385&lt;/td&gt;
&lt;td&gt;−$888&lt;/td&gt;
&lt;td&gt;1 : 2.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IWM&lt;/td&gt;
&lt;td&gt;$53&lt;/td&gt;
&lt;td&gt;−$130&lt;/td&gt;
&lt;td&gt;1 : 2.4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A 70% win rate at a 1:2.3 payoff has expected value &lt;code&gt;0.70 × 1 − 0.30 × 2.3 ≈ 0&lt;/code&gt;. By arithmetic, it is a coin flip. This is the defining feature of short-vol: you win small, often, and lose big, occasionally. The win rate is the wrong number to anchor on.&lt;/p&gt;
&lt;h2&gt;
  
  
  Only 3-14% of orders filled, and the fills were adversely selected
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;Proposed&lt;/th&gt;
&lt;th&gt;Filled&lt;/th&gt;
&lt;th&gt;Fill rate&lt;/th&gt;
&lt;th&gt;Avg edge captured&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SPY&lt;/td&gt;
&lt;td&gt;2,438&lt;/td&gt;
&lt;td&gt;335&lt;/td&gt;
&lt;td&gt;13.7%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−$0.037&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QQQ&lt;/td&gt;
&lt;td&gt;2,055&lt;/td&gt;
&lt;td&gt;162&lt;/td&gt;
&lt;td&gt;7.9%&lt;/td&gt;
&lt;td&gt;−$0.042&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NVDA&lt;/td&gt;
&lt;td&gt;3,072&lt;/td&gt;
&lt;td&gt;201&lt;/td&gt;
&lt;td&gt;6.5%&lt;/td&gt;
&lt;td&gt;−$0.044&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AMZN&lt;/td&gt;
&lt;td&gt;4,114&lt;/td&gt;
&lt;td&gt;139&lt;/td&gt;
&lt;td&gt;3.4%&lt;/td&gt;
&lt;td&gt;−$0.040&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IWM&lt;/td&gt;
&lt;td&gt;1,896&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;td&gt;3.1%&lt;/td&gt;
&lt;td&gt;−$0.039&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A mid-fill backtest books &lt;strong&gt;all&lt;/strong&gt; of the proposed trades at a &lt;em&gt;better&lt;/em&gt; price. Reality books a single-digit-to-low-teens percentage of them and the ones that do fill cross at ~$0.04 worse than mid. The orders that fill are disproportionately the ones the market is running through (adverse selection). On 100-multiplier contracts that is a structural ~$4 headwind per fill before the trade begins.&lt;/p&gt;
&lt;h2&gt;
  
  
  The "profit target" is mostly a forced spread cross
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pt&lt;/code&gt; = clean limit fill at target. &lt;code&gt;pt_x&lt;/code&gt; = target hit but had to cross the spread.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;pt (clean)&lt;/th&gt;
&lt;th&gt;pt_x (crossed)&lt;/th&gt;
&lt;th&gt;sl&lt;/th&gt;
&lt;th&gt;sl_x&lt;/th&gt;
&lt;th&gt;expiry&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SPY&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;133&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QQQ&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;76&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AMZN&lt;/td&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NVDA&lt;/td&gt;
&lt;td&gt;68&lt;/td&gt;
&lt;td&gt;71&lt;/td&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IWM&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On every symbol, &lt;strong&gt;more profit-target exits required crossing the spread than filled cleanly at the limit&lt;/strong&gt;. The idealized "close at 50% target" that naive backtests book is, in practice, the minority outcome. This single effect is a primary driver of the gap between the win rate and the Sharpe.&lt;/p&gt;
&lt;h2&gt;
  
  
  The high-conviction regime did not earn its name
&lt;/h2&gt;

&lt;p&gt;The signal's &lt;code&gt;risk_on&lt;/code&gt; ("deploy full size") regime underperformed &lt;code&gt;neutral&lt;/code&gt; on four of five 1-minute symbols:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;neutral&lt;/code&gt; P&amp;amp;L (win%)&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;risk_on&lt;/code&gt; P&amp;amp;L (win%)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SPY&lt;/td&gt;
&lt;td&gt;+$5,604 (73%)&lt;/td&gt;
&lt;td&gt;+$2,174 (70%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QQQ&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;−$3,000&lt;/strong&gt; (69%)&lt;/td&gt;
&lt;td&gt;+$1,364 (71%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NVDA&lt;/td&gt;
&lt;td&gt;+$5,918 (75%)&lt;/td&gt;
&lt;td&gt;+$266 (69%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AMZN&lt;/td&gt;
&lt;td&gt;+$4,669 (70%)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;−$1,644&lt;/strong&gt; (73%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IWM&lt;/td&gt;
&lt;td&gt;+$145 (72%)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;−$910&lt;/strong&gt; (60%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A signal that sizes up into its highest-conviction state and earns less there is a signal whose conviction is, at best, uncorrelated with forward edge. The same pattern reappears on SPXW from a completely separate data feed (95 &lt;code&gt;risk_on&lt;/code&gt; trades, −$5,266; 17 &lt;code&gt;neutral&lt;/code&gt; trades, +$625). This is the precise mechanism by which leveraged-VRP posts blow up.&lt;/p&gt;
&lt;h2&gt;
  
  
  SPXW: the flagship out-of-sample check
&lt;/h2&gt;

&lt;p&gt;SPXW is the canonical home of the VRP trade. The strategy was never tuned on it, and it had to be sourced from a different data path entirely (Historical API, daily-close resolution).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;SPXW (daily, OOS)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trades&lt;/td&gt;
&lt;td&gt;112 over 7.16 yr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Win rate&lt;/td&gt;
&lt;td&gt;61.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Profit factor&lt;/td&gt;
&lt;td&gt;0.37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sharpe&lt;/td&gt;
&lt;td&gt;−0.39&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg win / avg loss&lt;/td&gt;
&lt;td&gt;$39 / −$170 (&lt;strong&gt;1 : 4.4&lt;/strong&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;risk_on&lt;/code&gt; regime&lt;/td&gt;
&lt;td&gt;95 trades, &lt;strong&gt;−$5,266&lt;/strong&gt;, 60% win&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;neutral&lt;/code&gt; regime&lt;/td&gt;
&lt;td&gt;17 trades, +$625, 71% win&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It lost money too. The win/loss asymmetry is even worse (1:4.4) because narrow 5-point SPX spreads collect tiny credits against a wide max loss.&lt;/p&gt;

&lt;p&gt;Caveat: SPXW transacts at daily-close mid minus the −$0.04 haircut empirically measured on the five 1-minute symbols. Fill rate is 100% by construction and exits are all forced crosses, so its fill rate and Sharpe magnitude are not apples-to-apples with the 1-minute rows. It is cited as a directional confirmation, not a sixth execution-comparable Sharpe.&lt;/p&gt;
&lt;h2&gt;
  
  
  What this means
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VRP is real; the free lunch is not.&lt;/strong&gt; The premium shows up reliably as a high win rate. It does not reliably show up as money once you (a) pay realistic execution and (b) refuse to re-fit per symbol.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The win rate is the most misleading number in options content.&lt;/strong&gt; A 70% win rate with a 1:2.3 payoff is structurally breakeven. Any post leading with win rate and not showing the loss distribution is selling you the setup, not the result.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Execution is the strategy.&lt;/strong&gt; The single biggest gap between the hyped version and this one is fills, not signal. Mid-fill assumptions, clean-target exits, and 100% fill rates are where the fictional returns live.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Out-of-sample is brutal and necessary.&lt;/strong&gt; SPY (tuned) was the best result. Every symbol the strategy had never seen did worse. Honest deployment is the non-tuned result.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do not lever the confidence.&lt;/strong&gt; The &lt;code&gt;risk_on&lt;/code&gt; regime, the one a leveraged variant would size into hardest, was the weaker regime.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Reproduction
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash run_cross_symbol.sh                              &lt;span class="c"&gt;# 5 symbols, 1-min local chains&lt;/span&gt;
python spxw_api_backtest.py &lt;span class="nt"&gt;--start&lt;/span&gt; 2019-02-01 &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;--end&lt;/span&gt; 2026-04-02 &lt;span class="nt"&gt;--label&lt;/span&gt; vrpfrozen             &lt;span class="c"&gt;# SPXW, daily-close via API&lt;/span&gt;
python aggregate_cross_symbol.py                      &lt;span class="c"&gt;# cross_symbol_summary.{csv,json}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Per-symbol engine invocation (identical except &lt;code&gt;--symbol&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python intraday_bt_ev_rank.py &lt;span class="nt"&gt;--symbol&lt;/span&gt; &amp;lt;SYM&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--start&lt;/span&gt; 2019-02-01 &lt;span class="nt"&gt;--end&lt;/span&gt; 2026-04-02 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--delta&lt;/span&gt; 0.10 &lt;span class="nt"&gt;--dte&lt;/span&gt; 14 &lt;span class="nt"&gt;--pt&lt;/span&gt; 0.50 &lt;span class="nt"&gt;--sl&lt;/span&gt; 1.0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vrp-signal&lt;/span&gt; vrp_signal_v2.csv &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--kelly-default&lt;/span&gt; 0.05 &lt;span class="nt"&gt;--kelly-mult&lt;/span&gt; 0.5 &lt;span class="nt"&gt;--vrp-on-mult&lt;/span&gt; 1.0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--kelly-max&lt;/span&gt; 0.25 &lt;span class="nt"&gt;--max-drawdown&lt;/span&gt; 0.30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SPXW row consumed the same Historical API surface a paying user would call. Full study, full appendix table, year-by-year breakdowns, and the limitations section live in the &lt;a href="https://flashalpha.com/articles/vrp-short-put-spreads-honest-7-year-backtest" rel="noopener noreferrer"&gt;original article&lt;/a&gt;. The companion fill-model sensitivity study is &lt;a href="https://flashalpha.com/articles/vrp-backtest-fill-model-is-the-edge" rel="noopener noreferrer"&gt;VRP Backtest: The Fill Model Is the Edge&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>backtesting</category>
      <category>options</category>
      <category>quant</category>
      <category>fintech</category>
    </item>
    <item>
      <title>Effective Open Interest: How to Estimate Live OI From OPRA Flow</title>
      <dc:creator>tomasz dobrowolski</dc:creator>
      <pubDate>Thu, 21 May 2026 09:39:32 +0000</pubDate>
      <link>https://dev.to/tomasz_dobrowolski_35d32c/effective-open-interest-how-to-estimate-live-oi-from-opra-flow-mb6</link>
      <guid>https://dev.to/tomasz_dobrowolski_35d32c/effective-open-interest-how-to-estimate-live-oi-from-opra-flow-mb6</guid>
      <description>&lt;p&gt;If you have ever wondered why one vendor's "live GEX" differs from another's even though both read the same OPRA feed, the answer is almost always how they handle open interest.&lt;/p&gt;

&lt;p&gt;OPRA broadcasts settled OI once per session, usually before the open. Everything past 09:30 ET is built on top of an OI &lt;em&gt;estimate&lt;/em&gt; derived from trade flow. The estimate is rarely disclosed. Most vendors mix settled OI with their own intraday adjustments and call the result "live data" without describing how the adjustment is made.&lt;/p&gt;

&lt;p&gt;The choice of estimator changes the GEX number. It changes the call wall. It sometimes changes the gamma flip. For anyone wiring this into a model, the methodology &lt;em&gt;is&lt;/em&gt; the product.&lt;/p&gt;

&lt;p&gt;This post walks through how we do it at FlashAlpha: the six OI values we expose, the flow simulator, the 0.43 confidence weight and how it was calibrated, and how the whole thing feeds live exposure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with official OI
&lt;/h2&gt;

&lt;p&gt;Open interest is the count of outstanding contracts at a strike and expiration. It moves when positions are opened or closed. Aggregate exposure (GEX, DEX, VEX, charm) is linear in OI: wrong OI, wrong exposure.&lt;/p&gt;

&lt;p&gt;Official OI comes from the OCC's nightly clearing run and is broadcast by OPRA once per ET trading day, typically before the open. It is the only OI that has cleared. It is also stale through every minute of the trading day after the morning broadcast.&lt;/p&gt;

&lt;p&gt;For a sleepy index in a quiet week, that approximation is fine. For SPY on a Fed afternoon, QQQ on an NVDA-news day, or any single name on earnings, official OI is hours behind the actual book.&lt;/p&gt;

&lt;p&gt;0DTE is the extreme case: the entire OI lifecycle from creation to expiration happens inside one trading day. By the time tomorrow's settled OI arrives, those contracts have already cleared. If you want any GEX signal from 0DTE flow, you have to estimate OI from the tape in real time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The six OI values we expose
&lt;/h2&gt;

&lt;p&gt;Every Exposure API response includes six named OI fields per chain. Mixing them up produces silently wrong analytics, so each one has a precise meaning.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;official_oi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Last settled OI from OPRA (StatType=9). Conservative, accurate, stale.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;intraday_oi_delta&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Signed running estimate of position changes since the morning broadcast. Resets on a new ET trading day.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;intraday_oi_delta_x10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same delta in fixed-point form (one decimal preserved) for integer-only pipelines.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;simulated_oi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;official_oi + intraday_oi_delta&lt;/code&gt;, unclamped. May go negative on contracts where sell flow exceeds the morning baseline. Diagnostic only.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;effective_oi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;max(0, simulated_oi)&lt;/code&gt; per contract. The non-negative, intraday-updated OI surface that feeds live GEX/DEX/VEX/CHEX.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;oi_day&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The ET trading day this state belongs to. Critical for reconnect-replays and weekend boundaries.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you want the textbook official number, read &lt;code&gt;official_oi&lt;/code&gt;. If you want to see how aggressive intraday flow has been, watch &lt;code&gt;intraday_oi_delta&lt;/code&gt;. If you want the number that actually drives live GEX, read &lt;code&gt;effective_oi&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The side-classified flow simulator
&lt;/h2&gt;

&lt;p&gt;The intraday delta comes from a per-trade accumulator running continuously against OPRA. Each trade is side-classified (buy / sell / midpoint), weighted, and added to the running delta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ΔOI_t = size_t × Confidence × sign(side_t)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;sign(buy) = +1&lt;/code&gt;, &lt;code&gt;sign(sell) = -1&lt;/code&gt;, &lt;code&gt;sign(mid) = 0&lt;/code&gt;. Midpoint trades contribute zero because they cannot be reliably classified as opening or closing flow. That removes some signal but eliminates a large bias source in tight-spread names where midpoint prints are common.&lt;/p&gt;

&lt;p&gt;The aggregator runs at OPRA-trade resolution: a single contract's OI estimate updates the instant a print hits. Chain-level &lt;code&gt;effective_oi&lt;/code&gt; is the per-strike sum.&lt;/p&gt;

&lt;p&gt;Side classification is the upstream weakness. Tick-rule and quote-rule heuristics get most trades right, but rapid quote updates around news events can flip classification on individual prints. We monitor the aggregate impact in the residual reconcile rather than trying to make every classification perfect.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 0.43 confidence weight
&lt;/h2&gt;

&lt;p&gt;The confidence weight is the one knob that has to come from data, not theory. There is no first-principles derivation of "43% of volume opens new positions": the right value depends on participant mix, side-classification accuracy, time of day, and ticker. The only honest calibration is empirical.&lt;/p&gt;

&lt;p&gt;The reconcile loop runs daily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;16:30 ET snapshot.&lt;/strong&gt; After the MOC auction settles, the calibration service walks every contract with non-zero intraday delta and writes a prediction row: today's morning settled OI, the accumulated intraday delta, the predicted end-of-day OI, and the confidence weight in use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;09:35 ET reconcile.&lt;/strong&gt; The next morning, after the new day's settled-OI broadcast, the service looks up the actual settled value for each contract and writes a residual row: &lt;code&gt;actual - predicted&lt;/code&gt;. Predictions are matched to the &lt;em&gt;previous trading day&lt;/em&gt;: Monday's reconcile compares against Friday's predictions, not Sunday's.&lt;/p&gt;

&lt;p&gt;Aggregating residuals over a week or two tells you three things no single trade can: whether the weight is right on average, whether the bias is symmetric or tilted, and whether the error varies by ticker, side, or trade size.&lt;/p&gt;

&lt;p&gt;The current 0.43 came out of a calibration run in May 2026. The original setting was 0.40, calibrated when the pipeline first shipped. Residual analysis showed that 0.40 systematically under-predicted EOD OI by 4 to 10 percent on liquid names, with the bias largest on names with heavy retail flow. Raising the weight to 0.43 brought the median residual close to zero across the liquidity distribution.&lt;/p&gt;

&lt;p&gt;The tuning itself is not automated. The service writes residuals; humans look at the data and decide whether the heuristic needs adjusting. Automating the loop would risk over-fitting to a particular liquidity regime. Treating it as a quarterly engineering review preserves the option to ignore the data when something else is going on in the market.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feeding live GEX
&lt;/h2&gt;

&lt;p&gt;The aggregate GEX calculation does not care about the OI source. It multiplies gamma by OI by 100 by spot squared, summed across the chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GEX_live = Σ Γ_i × effective_oi_i × 100 × S²
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap the OI input and the GEX number changes mechanically. Using &lt;code&gt;official_oi&lt;/code&gt; gives a morning-stale GEX. Using &lt;code&gt;simulated_oi&lt;/code&gt; would feed a negative-OI artifact into the sum on contracts where the heuristic overshot. The per-contract &lt;code&gt;max(0, simulated)&lt;/code&gt; clamp is the small, important step that makes chain-level GEX sensible even when individual contract estimates are noisy.&lt;/p&gt;

&lt;p&gt;Same effective OI feeds DEX, VEX, charm exposure, and every per-strike profile. They are all linear in OI, so they all inherit the calibration of the underlying flow simulator.&lt;/p&gt;

&lt;h2&gt;
  
  
  A useful diagnostic
&lt;/h2&gt;

&lt;p&gt;Aggregate &lt;code&gt;intraday_oi_delta&lt;/code&gt; is the simulator's &lt;em&gt;direct&lt;/em&gt; signed sum, never reconstructed from &lt;code&gt;(effective - official)&lt;/code&gt;. Reconstructing from clamped values loses magnitude on contracts where the clamp fired. The accumulator is the source of truth for net intraday flow.&lt;/p&gt;

&lt;p&gt;A large gap between unclamped &lt;code&gt;simulated_oi&lt;/code&gt; and clamped &lt;code&gt;effective_oi&lt;/code&gt; on a chain means per-contract clamps fired somewhere and the heuristic is straining. That is your "trust the live GEX less right now" signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the heuristic breaks
&lt;/h2&gt;

&lt;p&gt;The simulator is a heuristic, not a position-tracking ledger. Three patterns produce predictable bias.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dealer-internalised crosses.&lt;/strong&gt; Large institutional trades that print through dealer-internal crossing networks show up on the tape but do not create new OI. The 0.43 weight accounts for some fraction of this through calibration, but bursts of internalised flow can locally inflate the estimate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Roll activity.&lt;/strong&gt; Around OPEX, large multi-leg rolls produce flurries of trades that side-classifiers read as one-directional. The simulator counts them as new positions when they are, in aggregate, position-neutral.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Midpoint density.&lt;/strong&gt; In tight-spread names, a large fraction of prints clear at the midpoint and contribute zero. Genuine positioning that prints at the midpoint is missed. Bias is toward under-estimating OI changes in liquid, tight-spread names.&lt;/p&gt;

&lt;p&gt;None of these break the methodology on aggregate. They show up as residual variance in the daily reconcile and inform the next calibration pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://lab.flashalpha.com/v1/exposure/gex/SPY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Official OI:    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;oi&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;official_oi&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Simulated OI:   &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;oi&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;simulated_oi&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Effective OI:   &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;oi&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;effective_oi&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Intraday Delta: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;oi&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;intraday_oi_delta&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Confidence:     &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;oi&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;oi_delta_confidence&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Live GEX:       &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;live_gex&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Free tier and docs at &lt;a href="https://flashalpha.com" rel="noopener noreferrer"&gt;flashalpha.com&lt;/a&gt;. Full methodology details, calibration history, and the four-question vendor checklist are in the &lt;a href="https://flashalpha.com/articles/effective-open-interest-methodology-live-gex-from-flow" rel="noopener noreferrer"&gt;original article&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>options</category>
      <category>api</category>
      <category>fintech</category>
      <category>quant</category>
    </item>
  </channel>
</rss>
