The BarSpecification
The BarSpecification
The immutable, value-comparable identity record that names your bar type and carries its parameters — the half of a custom bar type the engine uses to deduplicate work across charts.
On this page
The base record
A BarSpecification is an abstract record, which is deliberate: records compare by
value, so two requests for the same instrument and the same settings hash and equate identically, and the
engine can hand both charts one shared builder and one output series. The base record carries the
instrument id, optional continuous-futures wiring, optional trading-hours (session) binding, and the
hooks the host needs for labels and symbol translation. You inherit from it and add your own settings.
BarSpecification.csnamespace TradeStrike.Pipeline.Bars;
public abstract record BarSpecification(string InstrumentId)
{
// Continuous-futures intent (null for a literal / dated contract).
public string? ContinuousRoot { get; init; }
public BarAdjustmentMode ContinuousAdjustment { get; init; } = BarAdjustmentMode.BackAdjusted;
// Trading-hours (session) binding. Null = no session filtering.
public string? TradingHoursTemplateName { get; init; }
public bool ResetOnNewTradingDay { get; init; } = true;
// Human-readable label for logs / UI. Each concrete spec overrides it.
public abstract string DisplayName { get; }
// Symbol-translation hook. Default returns this unchanged.
public virtual BarSpecification WithInstrumentId(string newInstrumentId) => this;
// Convenience clones (not overridable) the host's resolution chokepoints use.
public BarSpecification WithContinuous(string? continuousRoot, BarAdjustmentMode adjustment);
public BarSpecification WithTradingHours(string? templateName, bool resetOnNewTradingDay = true);
}
Members you override
A concrete spec is a sealed record that adds its settings as positional record parameters and
overrides two members the host calls into.
-
DisplayName— the abstract label shown in chart UI and logs. Build it fromInstrumentIdand your settings (e.g."MNQ Range 4t"). -
WithInstrumentId(string)— the symbol-translation hook. When the chart layer re-targets a series from a user-typed symbol ("ES") to the venue-native one ("/ESM26:XCME"), the host calls this to clone the spec onto the new instrument. The base default returnsthisunchanged — safe, but a chart on your spec would keep the user-typed symbol. Override it with a recordwith-expression so your settings copy across, and, for a composite spec, propagate the rewrite into any nested spec.
The base also exposes WithContinuous(...) and WithTradingHours(...). You do not
override these — they are non-virtual clone helpers the host's continuous-futures factory and
session-resolution chokepoint use to stamp intent onto any spec via the record with. Because
ContinuousRoot, ContinuousAdjustment, TradingHoursTemplateName, and
ResetOnNewTradingDay are part of the record's value, two otherwise-identical specs with
different sessions (one RTH, one ETH) correctly do not share a builder.
BarAdjustmentMode (in
TradeStrike.Pipeline.Bars) has three values — None (raw stitched bars),
BackAdjusted (additive, the TA-friendly default), and RatioAdjusted
(multiplicative, for returns analysis). It is honoured by the data layer that stitches contracts; your
builder never sees it.The built-in subtypes
All ship in BarSpecification.cs. Study them as templates — your custom type will look
like one of these.
| Record | Parameters |
|---|---|
TimeBarSpec |
(string InstrumentId, TimeSpan Period) |
TickBarSpec |
(string InstrumentId, int Ticks) |
VolumeBarSpec |
(string InstrumentId, long Volume, OversizedTradePolicy OversizedTrades = KeepAtomic) |
RangeBarSpec |
(string InstrumentId, int RangeTicks) |
RenkoBarSpec |
(string InstrumentId, int BrickTicks, bool WithWicks = false) |
MedianRenkoBarSpec |
(string InstrumentId, int BrickTicks, int TrendThresholdTicks) |
KagiBarSpec |
(string InstrumentId, int ReversalTicks) |
PointAndFigureBarSpec |
(string InstrumentId, int BoxTicks, int Reversal) |
LineBreakBarSpec |
(string InstrumentId, int LineCount) |
HeikinAshiBarSpec |
(string InstrumentId, BarSpecification Underlying) |
HeikinAshiBarSpec is the one composite: its Underlying is another
BarSpecification (usually a TimeBarSpec, but the framework does not restrict it).
Its WithInstrumentId override rewrites both itself and the nested underlying so the composed
spec stays self-consistent.
Defining your own spec
To define a bar type, add your settings as positional record parameters, override DisplayName,
and override WithInstrumentId with a with-expression that copies
InstrumentId across.
MomentumBarSpec.csusing TradeStrike.Pipeline.Bars;
public sealed record MomentumBarSpec(string InstrumentId, int Threshold)
: BarSpecification(InstrumentId)
{
public override string DisplayName => $"{InstrumentId} Momentum {Threshold}";
public override BarSpecification WithInstrumentId(string newInstrumentId)
=> this with { InstrumentId = newInstrumentId };
}
A setting worth knowing: OversizedTradePolicy
One built-in setting deserves a callout because its default is load-bearing for orderflow. The
VolumeBarSpec takes an OversizedTradePolicy alongside its volume threshold,
which decides what happens when a single trade is larger than the volume remaining in the forming bar.
-
KeepAtomic(default) — treat the trade as one indivisible event: the bar closes with its full traded volume even if that overshoots the threshold. This is required for orderflow correctness, because a single exchange print carries one bid/ask snapshot and one aggressor side; splitting it across bars would duplicate that state and corrupt volume-at-price and delta. -
SplitAcrossBars— divide the oversized trade across consecutive bars to hit the threshold exactly. Each synthetic bar is a single price point, with the tick-count credit on the first bar only. Acceptable for pure volume-only backtests, but wrong for footprint work.
If you author a custom volume-like bar type, mirror this choice and default to KeepAtomic
unless you have a specific reason not to — a footprint indicator built on a SplitAcrossBars
series will show corrupted delta.