Options data & trading
Overview
One venue-agnostic surface for everything options: search underlyings, open chains, stream per-contract quotes with IV, open interest and greeks, capture bounded snapshots for exposure analytics, and place multi-leg orders from a strategy — without your plugin ever naming a venue.
On this page
What you can build
The options surface has two halves. The data half is IOptionChainReader —
a read-only seam your indicator or strategy obtains from its context. The trading half is a
set of multi-leg order verbs on the strategy context. Together they cover the full loop: discover
contracts, read their market data, and trade them atomically.
🔗 Underlyings & chains
Search option-capable underlyings, open a chain, walk its expiries and fetch the call/put contracts per expiry — strike, right, style, multiplier and the ids you trade with.
📡 Live quotes & greeks
Stream per-contract quote deltas: bid/ask/last, implied volatility, open interest, volume and the greeks — plus the underlying itself through the same subscription.
📸 Chain snapshots
One call captures a bounded, point-in-time slice of the chain with market data — the bulk input for GEX/DEX/VEX exposure profiles, IV-rank and screeners.
🧾 Multi-leg trading
Place verticals, straddles and iron condors as one atomic net-limit order from a strategy, pre-check margin, and exercise held positions.
One reader, no venues
Your plugin asks for "the option chain of SPY" — never "the Schwab chain of SPY". The host routes every call to whichever connected, options-capable venue serves that underlying, and nothing in the results tells you which one answered. This is deliberate: the same plugin runs unchanged for a user connected to any options-capable brokerage or data feed, today's and tomorrow's.
The key mechanism is the chain view. OpenChainAsync("SPY") resolves the symbol
and returns an IOptionChainView bound to the venue that resolved it. Every
provider instrument id inside that view — contract ids, the underlying id — is only meaningful
within that same view, so ids from different venues can never be mixed by construction. Quotes,
extra expiries and snapshots are all requested through the view, and disposing the view
releases the venue-side resources it holds.
graph LR;
A[Your plugin]-->B[IOptionChainReader];
B-->C[IOptionChainView per underlying];
C-->D[Chain listing];
C-->E[Quote stream];
C-->F[Bounded snapshot];
IsAvailable == false and calls return empty results or
null views — your plugin sees "no options data", never why. Individual contracts a
venue refuses to quote carry OptionQuote.AccessDenied instead of throwing.
Getting the reader
You never construct the reader — the host installs one process-wide and both plugin contexts expose
it. It is null when the host serves no options data at all (for example a bare backtest
harness), so the access pattern is a null-check, then use:
From an indicatorusing SabrTrader.Pipeline.Options;
// IIndicatorContext.OptionChains — available anywhere you hold the context.
IOptionChainReader? options = Context.OptionChains;
if (options is null || !options.IsAvailable) return; // no options-capable venue right now
var view = await options.OpenChainAsync("SPY");
From a strategyusing SabrTrader.Pipeline.Options;
// Strategy.OptionChains — a protected shortcut for Context.OptionChains.
IOptionChainReader? options = OptionChains;
if (options is null || !options.IsAvailable) return;
var view = await options.OpenChainAsync(underlyingSymbol);
null / "unsupported" results instead of
throwing. Your strategy runs identically in live, backtest and test harnesses — it just finds no
options data where none is served.
Availability is dynamic
Venues connect after charts load, disconnect mid-session, and reconnect. Don't sample
availability once in OnInit and give up — hold the reader and watch
AvailabilityChanged:
Availability patternprivate IOptionChainReader? _options;
private IOptionChainView? _view;
public override void OnInit(IIndicatorContext ctx)
{
_options = ctx.OptionChains;
if (_options is null) return; // this host never serves options data
_options.AvailabilityChanged += OnOptionsAvailability;
if (_options.IsAvailable) _ = OpenAsync();
}
private void OnOptionsAvailability()
{
// May fire on ANY thread. A venue (re)connected or dropped:
// reopen the view — an old view does not outlive its venue.
if (_options!.IsAvailable) _ = OpenAsync();
}
public override void OnDispose()
{
if (_options is not null) _options.AvailabilityChanged -= OnOptionsAvailability;
_view?.Dispose();
}
AvailabilityChanged subscription that outlives your plugin keeps its assembly load
context alive across hot reloads — the classic plugin memory leak. Pair every
+= in OnInit with a -= in Dispose.
Where the types live
The options data model ships inside SabrTrader.Pipeline.Contracts, namespace
SabrTrader.Pipeline.Options, bundled by the SabrTrader.Sdk meta-package
— if you installed the SDK, you already have it. The venue-side chain SPI a venue plugin implements
lives in SabrTrader.Pipeline.Venues.Options (package
SabrTrader.Pipeline.Venues.Contracts). Like every contract assembly they hold only
interfaces, records and enums; the engine that answers the calls lives in the host.
| Concern | Namespace | Key types |
|---|---|---|
| Chain data (read) | SabrTrader.Pipeline.Options |
IOptionChainReader, IOptionChainView, OptionChain, OptionContract, OptionQuote, OptionChainSnapshot, OptionChainSnapshotRequest, OccOptionSymbol
|
| Multi-leg trading | SabrTrader.Pipeline.Venues.Trading |
MultiLegOrderRequest, OptionLeg, PositionEffect, NetPriceDirection, MultiLegPlacement, MultiLegPrecheckResult
|
| Context access |
SabrTrader.Pipeline.Indicators / SabrTrader.Pipeline.Strategies
|
IIndicatorContext.OptionChains, IStrategyContext.OptionChains + the multi-leg verbs on Strategy
|
SabrTrader.Pipeline.Levels and are covered
in Options levels. This chapter is the raw chain data those vendors
compute from — with it you can build your own exposure analytics.
The chapter, page by page
- Underlyings & chains — search, open a view, walk expiries and contracts, and the OCC symbol helper.
- Live quotes & greeks — the quote stream, partial updates, threading and teardown.
- Chain snapshots & analytics — bounded captures, greek coverage, and a worked net-GEX example.
- Multi-leg orders & exercise — building legs from chain data, validation rules, placement semantics and failure handling.
- Reference: Options — the full type surface, terse.