Bars & position state
Bars & position state
The read-only state your OnBar logic works from: price history, the current
bar, where you stand in the market, and the trading session.
Bars
Your decision logic needs the price series up to now. BarCount tells you how much history
exists (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 it, and so on (O(1)). Use the non-throwing TryGetBar
when you are not sure the bar exists. A Bar is an OHLCV value struct with
StartUtc, EndUtc, Open, High, Low,
Close, Volume and TickCount.
C#int n = BarCount; // total bars (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 safe to use ... */ }
bool closed = IsBarClosed; // false during an intra-bar callback (see Calculation cadence)
double range = cur.High - cur.Low;
The history available before the run started is read directly via GetBar for warm-up, but it
does not trigger OnBar — only bars that close after the run subscribes drive a
callback. By the time your first OnBar fires, at least one bar is guaranteed, so reading
CurrentBar there is always safe.
Position & account
The base class exposes your exposure three ways — IsFlat, IsLong,
IsShort — which is what makes the edge-triggered entry/exit pattern read so cleanly. For
finer detail, Position carries the signed Quantity (positive long, negative
short), AverageEntryPrice, LastPrice, UnrealizedPnL and
RealizedPnLSession; AccountSnapshot gives CashValue,
Equity, RealizedPnL, BuyingPower and more. Both are nullable, so
guard before dereferencing in paths that can run before any position or account exists.
C#if (IsFlat) { /* nothing on */ }
Position? pos = Position; // null when flat / unreported
if (pos is { } p)
Log($"qty={p.Quantity} entry={p.AverageEntryPrice} uPnL={p.UnrealizedPnL}");
Account? acc = AccountSnapshot; // null when the account isn't reported yet
decimal? equity = acc?.Equity;
Session, identity & risk state
Beyond bars and position, the base class surfaces the rest of the run's context as read-only properties. Reach for these to gate entries by session, label logs, or respect account-level risk state.
| Property | What it is |
|---|---|
Instrument |
The traded symbol (string). |
Account |
The AccountId the run is bound to. |
TickSize / PointValue
|
Instrument tick size and per-point currency value, from the instrument catalog (double?, null when the host could not resolve one). |
State / StrategyName
|
The StrategyState (Created→…→Stopped/Faulted) and the run's display name. |
WorkingOrders |
The run's live (non-terminal) orders, as IReadOnlyList<Order>. |
Calculate |
The CalculationMode driving OnBar (see Calculation cadence). |
IsFirstTickOfBar / IsFirstBarOfSession
|
Edge flags for rolling per-bar / per-session accumulators. |
InSession / Session
|
Whether CurrentBar is inside trading hours, and the ISessionIterator for richer session queries. |
TradingHours |
The resolved TradingHoursTemplate (sessions, holidays, exchange time zone), or null. |
RiskStateRegistry |
An IAccountRiskStateRegistry? — the account's live running-tally risk state (e.g. consecutive losers), shared with manual orders and other strategies. Null when the host wired none (e.g. a backtest). |
Session-anchored logic
For "trade RTH only" or "flatten at the close" logic, Session returns a stateful
ISessionIterator (NinjaTrader's SessionIterator) created once on first access.
IsFirstBarOfSession is the place to reset per-session state; InSession gates
entries to inside trading hours.
C#protected override void OnBar()
{
if (IsFirstBarOfSession) ResetDailyCounters(); // new trading day
if (!InSession) return; // RTH-only gate
// flatten everything when this bar opens a new session
if (Session.IsNewSession(CurrentBar.EndUtc, includesEndTimestamp: true) && !IsFlat)
Flatten();
}
NotifyEntryBlocked(reason) instead of silently returning. The default routes the reason
through the logger with an "Entry blocked: " prefix; the live runtime also raises a structured event the
Strategies tab can show, so a user understands why a run looks idle.