Appearance
SPAN Margin
Family: Risk
What it computes
Emits SpanMarginTick ticks carrying scanning_risk, total_initial_margin off contract + spot + IV.
Available on the live, historical, and replay drives.
Methodology
OCC SPAN 16-scenario grid.
See the methodology overview for the citation index.
Inputs
Contract + spot + IV.
Key outputs
Scanning_risk, total_initial_margin. The full field set is in the tick table below.
Output schema (SpanMarginTick)
The field / type / description table below is regenerated from the SpanMarginTick Rust source by docs-site/scripts/inject-doc-tables.ts on every npm run docs:build. Do not hand-edit between the sentinels.
| Field | Type | Description |
|---|---|---|
contract | Contract | Resolved option contract metadata (symbol, expiration, side, strike). Subscribers identify the option by these fields — the wire-level contract id is engine-internal and is not surfaced here. |
date | i32 | Trading session date (YYYYMMDD). |
ms_of_day | i32 | Milliseconds since midnight at emission time. |
root | Arc<str> | Underlying symbol (interned). Provided alongside the contract projection so downstream consumers can group margin telemetry by root without unpacking Contract. |
expiration | i32 | Option expiration date (YYYYMMDD) projected off the contract. |
right | &'static str | Option right — "C" for calls, "P" for puts. |
strike | f64 | Strike price in dollars. |
current_price | f64 | Current option mid ((bid + ask) / 2) used as the baseline price for the scenario comparison. |
scanning_risk_per_contract | f64 | SPAN scanning risk — the maximum loss (in dollars per contract) across the sixteen-scenario grid. Positive when at least one scenario produces a loss; zero when every scenario improves the position's value. |
short_option_min_per_contract | f64 | Short-option-minimum floor (in dollars per contract). Equal to [SpanMarginParams::short_option_min] for every option, surfaced explicitly so margin-engine consumers can audit the floor. |
total_initial_margin_per_contract | f64 | Total per-contract initial margin estimate (max(scanning_risk, 0) + short_option_min). |
worst_scenario | Arc<str> | Human-readable label for the worst scenario, formatted as "spot{±N}% vol{±M}" (e.g. "spot-3% vol+1"). Treasury teams reconcile this against the broker's printed margin call to confirm the scan grid is targeting the same tail. |
Configuration (SpanMarginParams)
Regenerated from the SpanMarginParams Rust source — see the note above.
| Field | Type | Description |
|---|---|---|
contracts | SecurityFilter | Contracts the subscription tracks. Include the underlying stock contract so the per-symbol spot cache seats from underlying trades. |
conditions | ConditionPolicy | Trade-condition admission policy applied to admitted option NBBO quotes (the bid-condition byte is gated through the same OPRA admission policy the per-trade conditions use). |
venues | ExchangeFilter | Exchange / venue admission policy. |
rate | Arc<dyn RateService> | Risk-free rate provider. Looked up per emission with (event_date, expiry_date). Production deployments inject the curvekit-backed CurvekitRateService. |
annual_dividend | Option<f64> | Optional override of the implied dividend yield (annual decimal). None falls back to the hosted convention of zero dividend yield. |
dividend_cache | `std::sync::Arc<crate::reference_data::dividend_yield:: | |
| DividendYieldCache>` | Engine-wired per-symbol dividend-yield cache. Seated at subscribe time by DefaultSpec via wire_dividend_yield. Consulted on the dispatch path only when annual_dividend is None — the per-tick lookup resolves the continuous-dividend carry yield q = D / S from the live underlying spot the analytic already holds (O(1), lock-light, non-blocking). An explicit annual_dividend override always wins. | |
calendar | Arc<dyn MarketCalendar> | Market calendar provider — derives the per-session option-trading cutoff used by the time-to-expiration helper. |
spot_cache | SpotPriceCache | Shared spot price cache, populated by the analytic from underlying stock trades and read by every option contract on the same subscription. Build via [new_spot_price_cache] and clone the returned Arc into the spec. |
price_scan_pct | f64 | Underlying price-scan range as a fraction of spot (e.g. 0.05 = ±5%). Multiplied against {-3, -2, -1, 0, +1, +2, +3} to produce the seven price-shock arms of the SPAN scan grid. Defaults to [DEFAULT_PRICE_SCAN_PCT]. |
vol_scan_pct | f64 | Volatility-scan range as a fraction of the seated IV (e.g. 0.25 = ±25%). Multiplied against {-1, 0, +1} to produce the three vol-shock arms of the SPAN scan grid. Defaults to [DEFAULT_VOL_SCAN_PCT]. |
short_option_min | f64 | Short-option minimum charge per contract, in dollars. Defaults to [DEFAULT_SHORT_OPTION_MIN_USD] ($25). |
contract_multiplier | f64 | Contract multiplier — shares per contract. Defaults to [DEFAULT_CONTRACT_MULTIPLIER] (100 for US equity options). |
min_emit_interval_ms | i32 | Minimum interval between emissions per contract, in milliseconds. Defaults to 1_000 (one snapshot per second). |
Example
Python
python
import kairos_thetadata as kt
client = kt.Client.connect(kt.Credentials.from_env())
def on_event(row):
print(row)
sub = client.live().span_margin(["QQQ"]).on_event(on_event)
sub.wait(timeout_seconds=60.0)TypeScript
typescript
import { Client, Credentials } from "kairos-thetadata";
const client = await Client.connect(Credentials.fromEnv());
await client
.live()
.spanMargin(["QQQ"])
.onEvent((tick) => {
console.log(tick);
});Rust
rust
// Cargo.toml:
// kairos = "0.1"
use kairos::{Client, SpanMarginRow};
# fn run() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::connect(("me@example.com", "secret"))?;
let sub = client
.live()
.span_margin(["QQQ"])
.on_event(|row: &SpanMarginRow| println!("scanning_risk={} total_initial_margin={}", row.scanning_risk_per_contract, row.total_initial_margin_per_contract))?;
// ... later ...
sub.unsubscribe();
# Ok(())
# }