Loading...
Searching...
No Matches
ATAS.DataFeedsCore.OptionRequestGuard Class Reference

Connector-level limit on option requests that come past the budgeted options API. More...

Classes

struct  BudgetedScope
 Ends the budgeted scope entered by EnterBudgetedScope. More...
 

Public Member Functions

 OptionRequestGuard (ILoggerSource logger, SubscriptionStatistics statistics, TimeProvider timeProvider)
 
bool TryAdmitSubscribe (Security option, SubscriptionType subscriptionTypes, bool sendsRequest, out Exception? error)
 Decides whether the current caller may subscribe the option to the types and, when it may, records the holding under the caller's origin.
 
void Remove (Security option, SubscriptionType subscriptionTypes)
 The current caller unsubscribed the option from the types. The holding is taken from the caller's origin; types the origin does not hold are taken from the other one (a caller that subscribed and unsubscribes under different scopes).
 
void RemoveRejected (Security option, SubscriptionType subscriptionTypes)
 The broker rejected the registration of the types, reported on whatever thread: the holding is taken from the budgeted side first. When the options API and a direct caller share the option, the rejected request is the options API's more often than not, and a direct place is never given back on a guess.
 
bool TryAdmitResubscribe (Security option, SubscriptionType countedTypes, out Exception? error)
 Decides whether an option still counted as subscribed after a reconnect may be searched and subscribed at the broker again. An option held only inside budgeted scopes passes; an option a direct caller holds costs a direct token. A refused option is forgotten: the connector drops its subscription.
 
bool TryAdmitLookup (string request, out Exception? error)
 Decides whether an option series or chain lookup may reach the broker.
 
void Release (Security option)
 The option has no active subscription left at the connector: every holding of it is forgotten and a direct place is given back.
 

Static Public Member Functions

static BudgetedScope EnterBudgetedScope ()
 Marks the option connector calls of the current thread as already budgeted by the caller until the returned scope is disposed. Only for code that keeps its own per-connector budget (the options API) or whose volume is bounded by the account (position subscriptions). The scope must be entered and disposed on the same thread around the synchronous part of the call, for subscribes and unsubscribes alike.
 

Static Public Attributes

const int MaxDirectOptions = 200
 Options a direct caller may hold subscribed at once per connector.
 
const int DirectSubscribeBurst = 200
 Largest burst of direct option subscribe requests per connector.
 
const double DirectSubscribeRefillPerSecond = 0.5
 Sustained direct option subscribe requests per second per connector (30 per minute).
 
const int DirectLookupBurst = 10
 Largest burst of direct option series/chain lookups per connector.
 
const double DirectLookupRefillPerSecond = 1.0 / 30
 Sustained direct option series/chain lookups per second per connector (two per minute).
 

Properties

static bool IsBudgeted [get]
 The calls of the current thread are budgeted by their caller and pass unlimited.
 
int DirectOptions [get]
 Options held by direct callers at the moment.
 

Detailed Description

Connector-level limit on option requests that come past the budgeted options API.

The options API (OptionsSubscriptionService behind IOptionsDataProvider and the Options Board, PLAT-5011) budgets its own subscriptions and lookups per connector and marks its connector calls with EnterBudgetedScope. Everything else that reaches a connector for options - a chart or DOM opened on an option, or a third-party indicator that took the connector itself instead of the provider - is a direct caller. Direct callers get a small allowance meant for a person opening option charts, not for a program walking a whole option chain: at most MaxDirectOptions options held at once (an option shared with the options API counts too), a token bucket for the subscribe requests they send to the broker (a subscription restored after a reconnect costs a token too) and one for series/chain lookups. A request over the allowance is refused locally and never reaches the broker. The unattended server hosts (IDataFeedConnector.ReconnectOnFirstConnect) are our own infrastructure; the base connector does not consult the guard for them.

The holdings are counted per origin and per subscription type, mirroring the connector's subscription counter: the budgeted and the direct holders of one option are tracked apart, so a direct caller cannot ride on a subscription of the options API and keep it after the API released it. The connector serialises the guard with its own counter (see BaseConnector); the guard's own lock only protects its state.

Background: on 2026-09-28 Rithmic blocked clients of 8.0.15.271 that ran an indicator subscribing ~4 000 option strikes straight through the connector, around the budget of the options API, and re-subscribing all of them after every reconnect.

The scope is a guard against bugs, not a security boundary: indicators run in-process with full trust.

Constructor & Destructor Documentation

◆ OptionRequestGuard()

ATAS.DataFeedsCore.OptionRequestGuard.OptionRequestGuard ( ILoggerSource  logger,
SubscriptionStatistics  statistics,
TimeProvider  timeProvider 
)

Member Function Documentation

◆ EnterBudgetedScope()

static BudgetedScope ATAS.DataFeedsCore.OptionRequestGuard.EnterBudgetedScope ( )
static

Marks the option connector calls of the current thread as already budgeted by the caller until the returned scope is disposed. Only for code that keeps its own per-connector budget (the options API) or whose volume is bounded by the account (position subscriptions). The scope must be entered and disposed on the same thread around the synchronous part of the call, for subscribes and unsubscribes alike.

◆ Release()

void ATAS.DataFeedsCore.OptionRequestGuard.Release ( Security  option)

The option has no active subscription left at the connector: every holding of it is forgotten and a direct place is given back.

◆ Remove()

void ATAS.DataFeedsCore.OptionRequestGuard.Remove ( Security  option,
SubscriptionType  subscriptionTypes 
)

The current caller unsubscribed the option from the types. The holding is taken from the caller's origin; types the origin does not hold are taken from the other one (a caller that subscribed and unsubscribes under different scopes).

◆ RemoveRejected()

void ATAS.DataFeedsCore.OptionRequestGuard.RemoveRejected ( Security  option,
SubscriptionType  subscriptionTypes 
)

The broker rejected the registration of the types, reported on whatever thread: the holding is taken from the budgeted side first. When the options API and a direct caller share the option, the rejected request is the options API's more often than not, and a direct place is never given back on a guess.

◆ TryAdmitLookup()

bool ATAS.DataFeedsCore.OptionRequestGuard.TryAdmitLookup ( string  request,
out Exception?  error 
)

Decides whether an option series or chain lookup may reach the broker.

◆ TryAdmitResubscribe()

bool ATAS.DataFeedsCore.OptionRequestGuard.TryAdmitResubscribe ( Security  option,
SubscriptionType  countedTypes,
out Exception?  error 
)

Decides whether an option still counted as subscribed after a reconnect may be searched and subscribed at the broker again. An option held only inside budgeted scopes passes; an option a direct caller holds costs a direct token. A refused option is forgotten: the connector drops its subscription.

Parameters
optionThe option.
countedTypesThe types the connector counts for the option; recorded as a direct holding when the guard does not know the option.
errorThe reason of a refusal.

◆ TryAdmitSubscribe()

bool ATAS.DataFeedsCore.OptionRequestGuard.TryAdmitSubscribe ( Security  option,
SubscriptionType  subscriptionTypes,
bool  sendsRequest,
out Exception?  error 
)

Decides whether the current caller may subscribe the option to the types and, when it may, records the holding under the caller's origin.

Parameters
optionThe option.
subscriptionTypesThe types the caller subscribes to.
sendsRequestThe subscription sends a request to the broker (it adds a type nobody holds yet and the connector is connected); only such a subscription costs a token.
errorThe reason of a refusal.

Member Data Documentation

◆ DirectLookupBurst

const int ATAS.DataFeedsCore.OptionRequestGuard.DirectLookupBurst = 10
static

Largest burst of direct option series/chain lookups per connector.

◆ DirectLookupRefillPerSecond

const double ATAS.DataFeedsCore.OptionRequestGuard.DirectLookupRefillPerSecond = 1.0 / 30
static

Sustained direct option series/chain lookups per second per connector (two per minute).

◆ DirectSubscribeBurst

const int ATAS.DataFeedsCore.OptionRequestGuard.DirectSubscribeBurst = 200
static

Largest burst of direct option subscribe requests per connector.

◆ DirectSubscribeRefillPerSecond

const double ATAS.DataFeedsCore.OptionRequestGuard.DirectSubscribeRefillPerSecond = 0.5
static

Sustained direct option subscribe requests per second per connector (30 per minute).

◆ MaxDirectOptions

const int ATAS.DataFeedsCore.OptionRequestGuard.MaxDirectOptions = 200
static

Options a direct caller may hold subscribed at once per connector.

Property Documentation

◆ DirectOptions

int ATAS.DataFeedsCore.OptionRequestGuard.DirectOptions
get

Options held by direct callers at the moment.

◆ IsBudgeted

bool ATAS.DataFeedsCore.OptionRequestGuard.IsBudgeted
staticget

The calls of the current thread are budgeted by their caller and pass unlimited.


The documentation for this class was generated from the following file: