Bars & position state

Strategies

Bars & position state

The read-only state your OnBar logic works from: price history, the current bar, your position, the account, the session and the instrument's trading metadata.

Bars

BarCount is the number of bars available (use it to skip warm-up), CurrentBar is the most recently closed bar, and GetBar(barsAgo) walks back: GetBar(0) is the current bar, GetBar(1) the one before, in O(1). GetBar throws ArgumentOutOfRangeException past the available history; TryGetBar returns false instead. A Bar is an OHLCV value struct: Open, High, Low, Close (all double), Volume, TickCount, the true-UTC StartUtc / EndUtc, the chart-zone StartDisplay / EndDisplay, and Start / End (the chart timeline).

C#int  n   = BarCount;            // history + bars closed since start
Bar  cur = CurrentBar;          // most recent closed bar
Bar  ago = GetBar(3);           // 3 bars ago
if (TryGetBar(10, out Bar b)) { /* b exists */ }

bool closed = IsBarClosed;      // false during an intra-bar callback (see Calculation cadence)
double range = cur.High - cur.Low;

History that closed before the run started is readable through GetBar from OnStart on, and drives no OnBar. Each bar that closes after the run starts drives one closing callback. At least one bar exists by the first OnBar, so reading CurrentBar there is always safe; elsewhere CurrentBar throws InvalidOperationException while BarCount is 0.

Position & account

IsFlat, IsLong and IsShort answer the common question directly and make edge-triggered entries read cleanly. Position carries the signed Quantity (positive long, negative short), AverageEntryPrice, LastPrice, UnrealizedPnL and RealizedPnLSession. AccountSnapshot carries CashValue, Equity, RealizedPnL, BuyingPower, Currency and more. Both are null until the platform has reported them, so guard before dereferencing.

C#if (IsFlat) { /* no position */ }

Position? pos = Position;            // null when flat or not yet reported
if (pos is { } p)
    Log($"qty={p.Quantity} entry={p.AverageEntryPrice} uPnL={p.UnrealizedPnL}");

Account? acc = AccountSnapshot;      // null until the account is reported
decimal? equity = acc?.Equity;

WorkingOrders lists the run's non-terminal orders for its account and instrument.

Instrument metadata & sizing

The host fills the instrument's trading metadata from its instrument catalog, so a strategy reads it instead of asking the user for it. Each value is null when the host could not resolve it.

Property What it is
TickSize Minimum price increment (double?).
PointValue Currency value of a one-point move (double?): 50 for ES, 2 for MNQ.
QuantityStep Smallest tradable size increment in the unit the entry helpers take (double?): 1 contract, 1 share, 0.0001 BTC, 0.01 lot. Size onto this grid; a computed 0.4 BTC floored to whole units becomes 0.
MinQuantity The venue's smallest accepted order size, in the same unit (double?). Stand down rather than send a smaller order.
WireUnitsPerQuantity Venue units per trading unit (decimal): 1 for contracts, shares and coins, 100 000 on a forex venue that counts base currency units while strategies size in lots.
ToWireQuantity(quantity) Converts a size in trading units to venue units (multiplies by WireUnitsPerQuantity).
Two quantity units. EnterLong, EnterShort, Position, QuantityStep and MinQuantity use trading units (lots on a base-unit forex venue). PlaceOrder, WorkingOrders, and the Order, Fill and Position passed to OnOrderUpdate, OnFill and OnPositionUpdate use venue units. On every venue where WireUnitsPerQuantity is 1 the two are identical; elsewhere pass raw order sizes through ToWireQuantity.

Session, identity & risk state

Property What it is
Instrument The traded symbol (string).
Account The AccountId the run's orders route to.
State / StrategyName The run's StrategyState and its configured name.
Calculate The CalculationMode driving OnBar (see Calculation cadence).
IsFirstTickOfBar / IsFirstBarOfSession Edge flags for resetting per-bar and per-session accumulators.
InSession / Session Whether CurrentBar starts inside trading hours, and the run's ISessionIterator for richer session queries.
CreateSessionIterator(template) A new ISessionIterator over another TradingHoursTemplate, for example to trade the RTH session on a 24-hour series. Create it once and keep it; null when the host supplies no calendar.
TradingHours The run's TradingHoursTemplate (sessions, holidays, exchange time zone), or null.
DataConnection / IsDataConnected The state of the market-data connection feeding the run's instrument as a DataConnectionStatus, and whether it is Connected. NotApplicable in a backtest. Changes arrive in OnDataConnectionChanged(DataConnectionStatus status); see Live data connection.
Feed-recovery pause While the host pauses a live run for feed recovery, the managed entries (EnterLong / EnterShort) are held and report through NotifyEntryBlocked; exits and protective orders still go out, so an open position stays guarded. PlaceOrder sends what you give it, so gate raw entries yourself: send them while DataConnection is Connected or NotApplicable (a backtest).
RiskStateRegistry An IAccountRiskStateRegistry? with the platform's running risk tallies per account (for example consecutive losers), shared with manual trading and other strategies. GetState(Account) returns this run's account. Null when the host provides none, for example in a backtest.

Session-anchored logic

For "trade RTH only" or "flatten at the close" logic, Session returns the run's stateful ISessionIterator, created once on first access. IsFirstBarOfSession is the place to reset per-session state; InSession gates entries to trading hours.

C#protected override void OnBar()
{
    if (IsFirstBarOfSession) ResetDailyCounters();   // new trading session
    if (!InSession) return;                          // RTH-only gate

    // flatten when this bar opens a new session
    if (Session.IsNewSession(CurrentBar.EndUtc, includesEndTimestamp: true) && !IsFlat)
        Flatten();
}
Strategy session clock. The strategy's iterator works in UTC, so pass true-UTC times: CurrentBar.StartUtc / EndUtc. A chart indicator's iterator uses the chart's display zone and takes Bar.Start / Bar.End, so code copied from an indicator needs that change.
Blocked entries. When a risk or session rule makes you skip an entry, call NotifyEntryBlocked(reason). The reason is logged with an "Entry blocked: " prefix, and a live run also shows it on the Strategies tab, so the user can see why a run is idle.

Order flow and the order book

TryGetOrderFlowBar(barsAgo, out OrderFlowBar orderFlow) returns the per-bar order-flow snapshot (bid and ask volume per price, delta, point of control) for a closed bar, with the same indexing as GetBar. It returns false when the run has no order flow, when the index is out of range, and for the developing bar of an intra-bar callback. The host loads order flow for a run only when the strategy asks for it: implement IOrderFlowAwareStrategy, or declare an indicator marked [RequiresOrderFlow].

C#using System.Collections.Generic;
using SabrTrader.Pipeline.OrderFlow;
using SabrTrader.Pipeline.Strategies;

public sealed class DeltaStrategy : Strategy, IOrderFlowAwareStrategy
{
    // Called before OnInitialize with the run's parameter overrides; return true to load order flow.
    public bool RequiresOrderFlow(IReadOnlyDictionary<string, object>? parameterValues) => true;

    protected override void OnBar()
    {
        if (TryGetOrderFlowBar(0, out OrderFlowBar flow) && flow.Delta > 500 && IsFlat)
            EnterLong(1);
    }
}

RequiresOrderFlow must be free of side effects and must not throw; return false for a configuration that needs no order flow, because loading it is expensive. TryGetOrderBook(out IOrderBook? book) returns the run's order book in a backtest that replays a recorded MBO or L2 tape; see Recorded order book. RecordedDepth opens the recorded tapes directly, and OptionChains gives read access to option chains; see Multi-leg orders & exercise for options trading from a strategy.