Options: live quotes & greeks
Live quotes & greeks
Stream per-contract quote deltas through the chain view — prices, implied volatility, open interest, volume and greeks — and merge them correctly on your side.
On this page
Subscribing
SubscribeQuotes takes the provider instrument ids of the contracts you want — ids
from this view's chain — and a handler. It returns an IDisposable; disposing
it tears down every venue-side subscription that call created.
Subscribeusing SabrTrader.Pipeline.Options;
// The front-expiry contracts within ~10 strikes of the money, say:
List<string> ids = pickedContracts.Select(c => c.ProviderInstrumentId).ToList();
IDisposable subscription = view.SubscribeQuotes(ids, OnQuote);
void OnQuote(OptionQuote q)
{
// q.ProviderInstrumentId tells you which contract this delta belongs to.
}
Updates are partial
Streaming venues send deltas: a quote update carries only the fields that changed, and a
null field means "unchanged" — never "gone". Keep one merged state per contract
and overwrite only the fields that are present:
Merging deltasprivate readonly Dictionary<string, OptionQuote> _latest = new();
private readonly object _gate = new();
void OnQuote(OptionQuote q)
{
lock (_gate)
{
_latest[q.ProviderInstrumentId] = _latest.TryGetValue(q.ProviderInstrumentId, out var prev)
? prev with
{
Bid = q.Bid ?? prev.Bid,
Ask = q.Ask ?? prev.Ask,
Mid = q.Mid ?? prev.Mid,
Last = q.Last ?? prev.Last,
Delta = q.Delta ?? prev.Delta,
Gamma = q.Gamma ?? prev.Gamma,
Theta = q.Theta ?? prev.Theta,
Vega = q.Vega ?? prev.Vega,
ImpliedVol = q.ImpliedVol ?? prev.ImpliedVol,
Volume = q.Volume ?? prev.Volume,
OpenInterest = q.OpenInterest ?? prev.OpenInterest,
NetChange = q.NetChange ?? prev.NetChange,
PercentChange= q.PercentChange?? prev.PercentChange,
AccessDenied = q.AccessDenied ?? prev.AccessDenied,
}
: q;
}
}
Greeks, activity and day-change fields are populated only when the venue serves them — that is entitlement- and field-group-dependent. Render blanks for fields that never arrive; don't invent zeros.
The quote fields
| Field | Meaning |
|---|---|
ProviderInstrumentId |
Which contract (or underlying) this delta belongs to — always present. |
Bid, Ask, Mid, Last
|
Prices, in the contract's quote currency. |
Delta, Gamma, Theta, Vega
|
Greeks, when the venue serves them. |
ImpliedVol |
Implied volatility as a fraction — 0.25m means 25%. Uniform across venues;
multiply by 100 only for display. |
Volume, OpenInterest
|
Day volume and open interest, in contracts. |
NetChange, PercentChange
|
Day change (absolute / percent) — mainly useful for the underlying header. |
AccessDenied |
true when the venue explicitly refused market data for this instrument
(entitlement) — show "not entitled" instead of silent blanks. Null means normal/unknown; it is
never inferred from absent fields. |
Quoting the underlying
The chain's UnderlyingProviderInstrumentId goes through the same
subscription surface — pass it alongside the contract ids and you get the spot price for ATM
centering, moneyness and exposure math from one stream:
Underlying + contractsvar ids = new List<string>(contractIds);
if (view.Chain.UnderlyingProviderInstrumentId.Length > 0)
ids.Add(view.Chain.UnderlyingProviderInstrumentId);
var sub = view.SubscribeQuotes(ids, OnQuote);
// In OnQuote, an update whose id equals UnderlyingProviderInstrumentId is the spot.
When the venue doesn't report an underlying id the property is empty — fall back to centering on the middle strike of the front expiry.
Threading
Teardown
Dispose the subscription handle when the composition changes (new expiry picked, indicator
removed); dispose the view and every subscription it created goes down with it. The safe
order in a plugin's dispose path: subscription handles first, then the view, then unhook
AvailabilityChanged.
OnDisposepublic override void OnDispose()
{
_subscription?.Dispose();
_view?.Dispose();
if (_options is not null) _options.AvailabilityChanged -= OnOptionsAvailability;
}