Bars & series

Reference

Bars & series

The OHLCV bar value type, the bar-specification records that identify a series, the builder interfaces that construct bars from ticks, per-instrument metadata, and the barsAgo-indexed series you read in OnBarUpdate. Namespaces TradeStrike.Pipeline.Bars and TradeStrike.Pipeline.Series.

Bar

TradeStrike.Pipeline.Bars · a readonly record struct (64 bytes, passed by value through pipeline callbacks). For non-time bars there is no fixed period — EndUtc is the timestamp of the closing tick.

Bar.cspublic readonly record struct Bar(
    DateTime StartUtc,
    DateTime EndUtc,
    double Open,
    double High,
    double Low,
    double Close,
    long Volume,
    long TickCount);
Member Meaning
StartUtc Bar open time.
EndUtc Moment the bar closed (closing-tick timestamp for non-time bars).
Open / High / Low / Close OHLC prices.
Volume Total traded volume in the bar.
TickCount Number of underlying ticks that formed the bar. Optional metadata for time bars; load-bearing for order-flow / tick bars.

BarSpecification

An abstract record that is the identity of a series — comparable by value, hashable, serialisable. Two consumers asking for the same spec share one builder and one output series. Adding a bar type is one new record plus one builder; no core changes.

BarSpecification.cspublic abstract record BarSpecification(string InstrumentId)
{
    public string? ContinuousRoot { get; init; }
    public BarAdjustmentMode ContinuousAdjustment { get; init; } = BarAdjustmentMode.BackAdjusted;
    public string? TradingHoursTemplateName { get; init; }
    public bool ResetOnNewTradingDay { get; init; } = true;

    public abstract string DisplayName { get; }

    public virtual BarSpecification WithInstrumentId(string newInstrumentId) => this;
    public BarSpecification WithContinuous(string? continuousRoot, BarAdjustmentMode adjustment);
    public BarSpecification WithTradingHours(string? templateName, bool resetOnNewTradingDay = true);
}
Member Meaning
InstrumentId The venue-native symbol the series is built from. Stays the front-month wire symbol even for continuous futures.
ContinuousRoot When non-null, the continuous-futures root (e.g. "ES") the data layer stitches dated contracts behind. Null for a literal / dated / equity / forex / crypto series.
ContinuousAdjustment How rollover gaps are bridged when ContinuousRoot is set. Default BackAdjusted. Ignored when ContinuousRoot is null.
TradingHoursTemplateName Resolved trading-hours template the series is bound to (part of identity, so RTH vs ETH series don't share a builder). Null = no session filtering.
ResetOnNewTradingDay Whether bar construction restarts the grid at each session boundary ("Break at EOD"). Only meaningful when a template is set. Default true.
DisplayName Abstract; each concrete spec overrides for logs / UI (e.g. "MNQ Time 5m").
WithInstrumentId(string) Copy with a replaced instrument id (symbol-translation step). Default returns this; built-ins and plugin specs override.
WithContinuous(string?, BarAdjustmentMode) Copy carrying continuous-futures intent.
WithTradingHours(string?, bool) Copy bound to a resolved trading-hours template.

Concrete bar specs

All sealed record in TradeStrike.Pipeline.Bars. Each overrides DisplayName and WithInstrumentId.

BarSpecification.cspublic sealed record TimeBarSpec(string InstrumentId, TimeSpan Period) : BarSpecification(InstrumentId);
public sealed record TickBarSpec(string InstrumentId, int Ticks) : BarSpecification(InstrumentId);
public sealed record VolumeBarSpec(string InstrumentId, long Volume,
    OversizedTradePolicy OversizedTrades = OversizedTradePolicy.KeepAtomic) : BarSpecification(InstrumentId);
public sealed record RangeBarSpec(string InstrumentId, int RangeTicks) : BarSpecification(InstrumentId);
public sealed record RenkoBarSpec(string InstrumentId, int BrickTicks, bool WithWicks = false) : BarSpecification(InstrumentId);
public sealed record MedianRenkoBarSpec(string InstrumentId, int BrickTicks, int TrendThresholdTicks) : BarSpecification(InstrumentId);
public sealed record KagiBarSpec(string InstrumentId, int ReversalTicks) : BarSpecification(InstrumentId);
public sealed record PointAndFigureBarSpec(string InstrumentId, int BoxTicks, int Reversal) : BarSpecification(InstrumentId);
public sealed record LineBreakBarSpec(string InstrumentId, int LineCount) : BarSpecification(InstrumentId);
public sealed record HeikinAshiBarSpec(string InstrumentId, BarSpecification Underlying) : BarSpecification(InstrumentId);
Spec Closes when…
TimeBarSpec(Period) Every Period (seconds/minutes/hours/days).
TickBarSpec(Ticks) Every N ticks (N positive).
VolumeBarSpec(Volume, OversizedTrades) Accumulated traded volume reaches Volume; OversizedTrades controls a print larger than remaining capacity.
RangeBarSpec(RangeTicks) Price moves RangeTicks ticks from the bar's open; the breaking tick opens the next bar at the close (no gap).
RenkoBarSpec(BrickTicks, WithWicks) Price moves one brick; reversal needs 2 bricks. WithWicks exposes intra-brick excursions (default false = classical wickless).
MedianRenkoBarSpec(BrickTicks, TrendThresholdTicks) "Median Renko" variant: always-wicked, body height BrickTicks, continuation/reversal driven by TrendThresholdTicks.
KagiBarSpec(ReversalTicks) Price reverses ReversalTicks from the active extreme; line closes and a new one opens opposite.
PointAndFigureBarSpec(BoxTicks, Reversal) One column = one bar. BoxTicks box height; Reversal boxes (classically 3) to start a new column.
LineBreakBarSpec(LineCount) Price closes above the highest high / below the lowest low of the last LineCount bars.
HeikinAshiBarSpec(Underlying) Derived from another BarSpecification; OHLC computed from the underlying bars. WithInstrumentId propagates into Underlying.

BarAdjustmentMode · OversizedTradePolicy

BarAdjustmentMode.cspublic enum BarAdjustmentMode { None, BackAdjusted, RatioAdjusted }
public enum OversizedTradePolicy { KeepAtomic, SplitAcrossBars }
Value Meaning
BarAdjustmentMode.None Raw stitched bars; rollover gaps visible.
BarAdjustmentMode.BackAdjusted Additive back-adjustment (default). Standard for TA — absolute price distances stay meaningful.
BarAdjustmentMode.RatioAdjusted Multiplicative back-adjustment. Standard for returns analysis; absolute distances drift.
OversizedTradePolicy.KeepAtomic Default. A single print is atomic — the bar can exceed the threshold. Required for order-flow correctness.
OversizedTradePolicy.SplitAcrossBars Split a print across bars so each equals the threshold. Pure-volume backtests only; do not use with order-flow.

IBarBuilder

Shared metadata every builder exposes. HasCurrentBar is false until the first bar starts; reading CurrentBar while false throws.

IBarBuilder.cspublic interface IBarBuilder
{
    BarSpecification Spec { get; }
    bool HasCurrentBar { get; }
    Bar CurrentBar { get; }
}

ITickBarBuilder

Tick-driven builders (Time, Tick, Volume, Range, Renko, …). They consume only TickFlags.Trade ticks. OnTick writes newly-closed bars into a caller-supplied span and returns the count (0 mid-bar, 1 typical, N ≥ 2 on a gap).

IBarBuilder.cspublic interface ITickBarBuilder : IBarBuilder
{
    int OnTick(in Tick tick, Span<Bar> closedBars);

    bool ClosingTickBelongsToNextBar => false;
    bool ResetsOnSessionBoundary => true;
    bool ExposesFormingBar => true;

    int ForceCloseCurrentBar(Span<Bar> closedBars) => 0;
    int ForceCloseAtSessionEnd(DateTime sessionEndUtc, Span<Bar> closedBars) => ForceCloseCurrentBar(closedBars);
    void BeginSession(DateTime sessionBeginUtc) { }
}
Member Meaning
OnTick(in Tick, Span<Bar>) Process one tick; writes closed bars chronologically, returns the count written.
ClosingTickBelongsToNextBar false (default): closing tick credited to the closing bar (Volume / Tick). true: it opens the next bar (Time / Range / Renko). Order-flow uses this to finalize the snapshot before/after crediting the tick.
ResetsOnSessionBoundary Default true (time/tick/volume/range force-close at the boundary). false for continuous price-pattern builders (Renko, Median-Renko, Kagi, P&F, Line-Break).
ExposesFormingBar Default trueCurrentBar is a continuously-forming tail bar. false for classical Renko (returns the last completed brick).
ForceCloseCurrentBar(Span<Bar>) Force-close at a session boundary; returns count written.
ForceCloseAtSessionEnd(DateTime, Span<Bar>) Force-close clamping the bar end to the exact session end.
BeginSession(DateTime) Notify a new session open so a time-grid builder anchors its bars.

IDerivedBarBuilder

Builders whose input is another series' closed-bar stream (e.g. Heikin Ashi).

IBarBuilder.cspublic interface IDerivedBarBuilder : IBarBuilder
{
    int OnSourceBarClosed(in Bar sourceBar, Span<Bar> closedBars);
    void PreviewSourceBar(in Bar developingSourceBar);
}

IBarBuilderFactory

Resolves a spec to a freshly-constructed builder. Plugins register a custom spec + builder via the host's DefaultBarBuilderFactory.Register<TSpec>, or supply their own factory.

IBarBuilderFactory.cspublic interface IBarBuilderFactory
{
    IBarBuilder Create(BarSpecification spec);
    bool Supports(BarSpecification spec);
}

Seeding capabilities

Opt-in interfaces the historical→live handoff probes so the first live tick continues the series seamlessly. A builder implements whichever applies.

ISeedableBarBuilder.cspublic readonly record struct BarSeedResult(bool Seeded, DateTime Cutoff, bool LastSeriesBarIsInProgress)
{
    public static readonly BarSeedResult NotSeeded;
}

public interface ISeedableBarBuilder
{
    BarSeedResult SeedFromLastBar(in Bar lastBar, DateTime nowUtc);
}

public interface ISeedableDerivedBarBuilder
{
    void SeedFromLastDerivedBar(in Bar lastDerivedBar);
}

public interface IHistorySeedableBarBuilder
{
    BarSeedResult SeedFromHistory(IBarSeries series, DateTime nowUtc);
}
Interface For
ISeedableBarBuilder Builders that chain from the last single bar (Time re-opens the period; Range opens at the previous close). Volume / tick bars don't chain and aren't seedable.
ISeedableDerivedBarBuilder A derived builder (e.g. Heikin Ashi) whose output depends on its own previous emitted bar.
IHistorySeedableBarBuilder Builders needing more than one bar to continue (e.g. Line-Break's N-line window). Preferred over ISeedableBarBuilder when both are present.

IInstrumentMetadata

Per-instrument static metadata the runtime needs — most importantly GetTickSize, used by range / Renko builders. InMemoryInstrumentMetadata is the public thread-safe fixture/registry.

IInstrumentMetadata.cspublic interface IInstrumentMetadata
{
    double GetTickSize(string instrumentId);
    bool IsRegistered(string instrumentId);

    double? TryGetPointValue(string instrumentId) => null;
    QuantityUnit GetQuantityUnit(string instrumentId) => QuantityUnit.Contract;
    double? TryGetLotSize(string instrumentId) => null;
    double? TryGetQuantityStep(string instrumentId) => null;
    double? TryGetMinQuantity(string instrumentId) => null;
    string? TryGetQuoteCurrency(string instrumentId) => null;
    string? TryGetAdjustmentVersion(string instrumentId) => null;
    InstrumentCategory? TryGetCategory(string instrumentId) => null;
}
Member Meaning
GetTickSize(string) Minimum price increment (MNQ = 0.25, CL = 0.01). Throws if the instrument isn't registered.
IsRegistered(string) True when GetTickSize would return a real value.
TryGetPointValue(string) Currency value of one full point (ES = 50), or null. Non-throwing.
GetQuantityUnit(string) Unit the quantity is counted in. Default Contract.
TryGetLotSize(string) For Lot instruments, base-currency units per lot (forex standard 100 000), or null.
TryGetQuantityStep(string) Smallest tradable quantity increment, in the instrument's unit, or null.
TryGetMinQuantity(string) Smallest order quantity the venue accepts, or null.
TryGetQuoteCurrency(string) ISO-4217 quote/settlement currency, or null.
TryGetAdjustmentVersion(string) Opaque corporate-action token for split-adjusted equity series, or null. Flows into the cache key.
TryGetCategory(string) The venue's explicit instrument category, or null when none reported.

InMemoryInstrumentMetadata.Register(instrumentId, tickSize, pointValue?, quantityUnit?, lotSize?, quantityStep?, minQuantity?, quoteCurrency?, adjustmentVersion?, category?) populates the registry; only tickSize is required.

QuantityUnit

QuantityUnit.cspublic enum QuantityUnit { Contract = 0, Share, Lot, Unit, Coin }
Value Meaning
Contract Futures / derivatives — whole contracts. The default.
Share Equities — shares (whole or fractional).
Lot Forex — standard lots (one lot = TryGetLotSize base-currency units).
Unit Forex — raw base-currency units.
Coin Spot crypto — fractional coin amounts.

InstrumentCategory

A venue's explicit asset-class signal, used when QuantityUnit alone is ambiguous (a forex pair and a metal/index CFD are both Lot/Unit). Carried nullable everywhere; null is the single canonical "no category reported" — there is deliberately no Unknown.

InstrumentCategory.cspublic enum InstrumentCategory { Forex, Equity, Crypto, Index, Commodity, Metal, Bond }

IBarSeries

TradeStrike.Pipeline.Series · read-only bar series with NinjaTrader-style barsAgo indexing: this[0] is the current bar, this[1] the one before. Backed by a power-of-2 ring (struct-of-arrays); Count is monotonic, the largest legal barsAgo is Math.Min(Count, Capacity) - 1.

ISeries.cspublic interface IBarSeries
{
    BarSpecification Spec { get; }
    int SeriesIndex { get; }
    int Count { get; }
    int Capacity { get; }

    Bar this[int barsAgo] { get; }
    bool TryGet(int barsAgo, out Bar bar);

    bool TryReadCloseSpan (int fromBarsAgo, int count, out ReadOnlySpan<double> firstSlice, out ReadOnlySpan<double> secondSlice);
    bool TryReadOpenSpan  (int fromBarsAgo, int count, out ReadOnlySpan<double> firstSlice, out ReadOnlySpan<double> secondSlice);
    bool TryReadHighSpan  (int fromBarsAgo, int count, out ReadOnlySpan<double> firstSlice, out ReadOnlySpan<double> secondSlice);
    bool TryReadLowSpan   (int fromBarsAgo, int count, out ReadOnlySpan<double> firstSlice, out ReadOnlySpan<double> secondSlice);
    bool TryReadVolumeSpan(int fromBarsAgo, int count, out ReadOnlySpan<long>   firstSlice, out ReadOnlySpan<long>   secondSlice);

    ISeries<double> Close { get; }
    ISeries<double> High { get; }
    ISeries<double> Low { get; }
    ISeries<double> Open { get; }
    ISeries<double> Volume { get; }
    ISeries<double> Typical { get; }   // (H+L+C)/3
    ISeries<double> Median { get; }    // (H+L)/2
    ISeries<double> Weighted { get; }  // (H+L+2C)/4
    ISeries<double> OHLC4 { get; }     // (O+H+L+C)/4
}
Member Meaning
Spec Spec the series was constructed from. Stable for its lifetime.
SeriesIndex Position in the runtime's series table. 0 = primary; 1, 2, … = series added via AddDataSeries. Routes per-bar callbacks (matches NT's BarsInProgress).
Count / Capacity Total bars ever observed (monotonic) / ring size (power of 2).
this[int barsAgo] O(1) random-access read; this[0] = most recent. Materialises a Bar per call.
TryGet(int, out Bar) Safe variant — returns false during warmup instead of throwing.
TryRead*Span(…) Zero-copy contiguous column slices in chronological order; up to two spans when the slice wraps the ring. False when the range exceeds bars in the ring.
CloseOHLC4 Cached price-series projections (single instance per component, so indicator dedup relies on reference equality).

ISeries<T>

Read-only generic series — the shape indicator output plots expose. Warmup positions hold double.NaN for double-typed series.

ISeries.cspublic interface ISeries<T> where T : struct
{
    int Count { get; }
    int Capacity { get; }
    T this[int barsAgo] { get; }
    bool TryGet(int barsAgo, out T value);
}

IBarSourcedSeries

Marker on an ISeries<double> that knows the IBarSeries it was derived from — the indicator framework uses it to resolve a child's primary bars in indicator-of-indicator chains.

IBarSourcedSeries.cspublic interface IBarSourcedSeries
{
    IBarSeries SourceBars { get; }
}

ChunkedSeries<T>

The public writable backing for indicator output. ISeries<T> + IClearableSeries; grows without bound (4096-slot chunks), single-writer / multi-reader, NaN-aware for warmup. this[0] is the newest value.

ChunkedSeries.cspublic sealed class ChunkedSeries<T> : ISeries<T>, IClearableSeries where T : struct
{
    public int Count { get; }
    public int Capacity { get; }            // int.MaxValue (unbounded)
    public T this[int barsAgo] { get; }
    public bool TryGet(int barsAgo, out T value);

    public void Append(T value);            // becomes the new this[0]
    public void UpdateLast(T value);        // rewrite the newest value (intra-bar)
    public void Set(int barsAgo, T value);  // rewrite at an arbitrary offset (swing/pivot backfill)
    public void Clear();                    // drop all values, Count -> 0
}

IClearableSeries

Opt-in: a mutable series that can be reset to empty in place — the recalc path clears values without reallocating, so the chart's IPlot.Values references never dangle.

IClearableSeries.cspublic interface IClearableSeries
{
    void Clear();
}