Options data & trading
Overview
One venue-agnostic surface for 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. Your plugin never names 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 methods on the strategy. Together they cover the full loop: discover
contracts, read their market data, and trade them as one order.
🔗 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 updates: 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 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. The same plugin runs unchanged for a user connected to any options-capable broker or data feed.
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 and the underlying id) is only meaningful
within that same view, so ids from different venues cannot be mixed. 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", not the reason. Individual contracts
a venue refuses to quote carry OptionQuote.AccessDenied.
Getting the reader
The host installs one reader process-wide and both plugin contexts expose it; you never construct
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 or an "unsupported" result. Your
strategy runs identically in live, backtest and test harnesses and finds no options data where
none is served.
Availability is dynamic
Venues connect after charts load, disconnect mid-session, and reconnect. Hold the reader
and watch AvailabilityChanged instead of sampling availability once in
OnInit:
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, because a 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. Pair every += in OnInit with a
-= in OnDispose.
Where the types live
The options data model ships in SabrTrader.Pipeline.Contracts, namespace
SabrTrader.Pipeline.Options, and comes with the SabrTrader.Sdk
meta-package. The chain SPI a venue plugin implements lives in
SabrTrader.Pipeline.Venues.Options (package
SabrTrader.Pipeline.Venues.Contracts) and returns the same records, so a consumer
and a venue speak one data model. 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, ImpliedMoveMath
|
| Multi-leg trading | SabrTrader.Pipeline.Venues.Trading |
MultiLegOrderRequest, OptionLeg, PositionEffect, NetPriceDirection, MultiLegPlacement, MultiLegPrecheckResult
|
| Chain SPI (venue plugins) | SabrTrader.Pipeline.Venues.Options |
IOptionChainSource, IOptionChainSnapshotSource, OptionQuoteCapacity
|
| Context access |
SabrTrader.Pipeline.Indicators / SabrTrader.Pipeline.Strategies
|
IIndicatorContext.OptionChains, IStrategyContext.OptionChains and the multi-leg methods 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, a worked net-GEX example and the expected-move helpers.
- Multi-leg orders & exercise: building legs from chain data, validation rules, placement semantics and failure handling.
- Reference: Options: the full type surface, terse.