Bars & position state
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.
On this page
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). |
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();
}
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.
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.