Market Phases
Use market-phase information in strategy logic and research.
Default use
Register context.MarketPhases().Create(symbol) in OnInit, read handle.Current in OnBar, and require Status == Ready. This uses the shared tester/Launcher C# runtime. Defaults are a reproducible starting point, not universally correct settings.
Create declares history without an OnBar subscription. The primary key is already subscribed; extra callbacks need context.Runtime.SubscribeOnBar(key). Identical registrations reuse a handle; at most 32 distinct handles.
// Fields: MarketPhaseAnalyzer? _phase;
// OnInit:
_phase = context.MarketPhases().Create(context.Parameters.SymbolKey);
// OnBar:
var phase = _phase!.Current;
if (phase.Status != MarketPhaseStatus.Ready) return;
bool up = phase.Phase == MarketPhase.Up;
bool breakout = (phase.Events & MarketPhaseEvents.BreakoutUp) != MarketPhaseEvents.None;Three detail levels
Phase: Up, Down, Range, Transition. Regime: SteadyUp/SteadyDown, VolatileUp/VolatileDown, QuietRange, Range, VolatileRange, Transition. Events are coexisting flags: Compression/Expansion, BreakoutUp/Down, ImpulseUp/Down, PullbackUp/Down. All three levels are returned together.
PullbackUp means a downward retracement within Up; PullbackDown means upward within Down. Transition does not predict a reversal. Snapshot includes Features, Confidence, AsOfUtc, LastClosedBarUtc, AvailableBars, RequiredBars and MethodVersion. Confidence is rule agreement, not profit probability or accuracy.
Settings and independent timeframes
Lookback=32 and BaselinePeriod=128 accept 8..4096; VolatilityPeriod=14 accepts 2..4096. RequiredBars=max(Lookback+1,BaselinePeriod+VolatilityPeriod+1)+PhaseSmoothingBars-1: default 147, advanced example 217. Options are immutable, symbol keys copied; do not mutate the primary SymbolKey.
TrendEfficiencyThreshold=0.30 and RangeEfficiencyThreshold=0.25 require 0 < range < trend < 1. TrendFitThreshold=0.45 is in (0,1); TrendDisplacementAtr=1.5 and TrendSlopeAtr=0.02 are positive. PhaseSmoothingBars=5 accepts 1..32: the phase is the most frequent of the last five decisions (the most recent on a tie), which removes one- and two-bar flips at the cost of about two bars of lag.
CompressionRatio=0.75 is in (0,1); ExpansionRatio=1.50 >1. BreakoutBufferAtr=0.10 is nonnegative; ImpulseAtr=2.50 and PullbackAtr=0.50 are positive. Invalid options throw.
Create separate m5/h1 handles in OnInit; _fast and _slow below are MarketPhaseAnalyzer? fields. In OnBar filter m5, then require both handles to be Ready. H1 changes only after an h1 candle closes.
var source = context.Parameters.SymbolKey;
var fastKey = new SymbolKey(source.Exchange, source.Market, source.Symbol, TimeFrames.m5);
var slowKey = new SymbolKey(source.Exchange, source.Market, source.Symbol, TimeFrames.h1);
_fast = context.MarketPhases().Create(fastKey);
_slow = context.MarketPhases().Create(slowKey, new MarketPhaseOptions
{
Lookback = 48, BaselinePeriod = 192, VolatilityPeriod = 20,
TrendEfficiencyThreshold = 0.50m, TrendFitThreshold = 0.65m, ExpansionRatio = 1.75m
});
context.Runtime.SubscribeOnBar(fastKey);
// In OnBar: read both .Current snapshots and require both Status == Ready.Readiness and history
WarmingUp means insufficient bars; Stale means a full period elapsed after the last closed bar; InvalidData means bad OHLCV, duplicates or gaps; Unavailable means absent model time. Non-ready produces Unknown, never a substitute Range. Required=false permits WarmingUp but creates no data.
Global.Now is the clock, with no wall-clock fallback. Only OpenTime+duration <= cursor participates, including the exact close boundary. Current/future bars are excluded. Identical bars/settings/cursor give the same result; corrected bars are recomputed. Fixed-duration m1..w2 only; None, M1/M3/M6/Y1 and seconds are unsupported.
Prices must be positive, high/low must contain open/close and volume must be nonnegative. Gaps are not filled. Host aggregation requires every source m1 candle in the full window; coarse-only injected data is insufficient. Read Current on relevant closed bars: large-TF completeness checks scan RequiredBars × timeframe minutes.
Required phase dependencies join the Launcher history warmup before trading begins. Unknown gaps remain unavailable; confirmed no-trades do not create candles. Generated scenario provenance is validated against metadata and content hashes; arbitrary IKline inputs alone do not certify a source.
Method and limits
ER is absolute endpoint displacement/sum(abs(close changes)); regression r² and signed slope describe closes, with slope normalized by simple recent ATR. TR=max(high-low,abs(high-prevClose),abs(low-prevClose)). Recent TR/prevClose is compared with a disjoint preceding baseline. Neither Wilder smoothing nor RSI is used.
RelativeVolume=last volume/mean preceding Lookback volumes, null for a zero denominator. It does not choose Phase/Regime. Confidence caps: trend 0.85, range 0.75, transition 0.15, flat 0; missing previous volume or a dominant jump ≥65% of path caps it at 0.35. This is a heuristic, not a forecast.
PhaseProbability is the calibrated probability that the next 12 closed bars realize the reported phase (a trend: the close moves at least 0.5 ATR its way with path efficiency of at least 0.60; a range: path efficiency at most 0.30 inside the last Lookback bars’ high/low; otherwise a transition).
Using it as RiskSizingRequest.Confidence is an explicit strategy policy that needs separate A/B validation; no automatic sizing recommendation follows. It is the probability of the right phase, not of a profitable trade. Calibration phase-probability-v1: 14 Binance Futures pairs 2019–2024, checked on 2025–2026 (ECE 0.013).
The numbers stay close to how often each phase occurs: about 0.03–0.04 for Up/Down, 0.35–0.45 for Range, 0.46–0.55 for Transition. Null unless Ready, with the default options, on bars of 5 minutes to 1 day.
MethodVersion=ohlcv-rules-v2 (since 3 October 2026: softer trend defaults and smoothing; PhaseSmoothingBars=1 with thresholds 0.45/0.60/2 reproduces v1). Existing market-phase-demo ONNX is a separate example, not this service. Historical phase frequencies do not measure accuracy. Robustness checks use these phases for their episodes, and the chart shows them as bands (Charts → Market phases on the chart); risk integration, adaptive seconds and automated downloads remain separate work.
Agent and Copilot discovery
Use list_strategy_api, then get_strategy_api_doc(type=MarketPhaseService, member=Create) and get_strategy_api_doc(type=MarketPhaseOptions) for types and actual XML documentation/defaults, then compile generated C#. Current catalog and runtime reference work on existing installations; an old prebuilt RAG index does not guarantee new content and fallback knowledge files are not automatically seeded.
Offline: MarketPhaseClassifier.Analyze(bars, asOfUtc, barDuration, options), with IReadOnlyList<IKline> in chronological UTC order and a fixed step. The pure API loads no data. Advanced analysis uses the existing shared runtime language bridges; inspect runtime discovery for exact contracts.
Choosing an analyzer
Describe closed price structure: Native OHLCV rules or Gaussian. Confidence is heuristic; model posterior is uncalibrated.
Study H1/H4/D1 history from 2020 with past-only parameters: Explicit HistoricalExpanded, selecting the annual version at the closed decision time.
Reproduce an earlier experiment: Frozen ExpandedFeatures (available from 2024), or the exact retained ResearchExpert. These are distinct from HistoricalExpanded.
Compare entry timing with a larger background: Explicit MTF Context: working timeframe for decisions, last fully closed higher timeframe for context. Disagreement is allowed.
Assess an entry filter: A paired strategy A/B with frozen settings and equal risk and costs. A phase color or high score alone is no trading signal.
Native analysis is the starting point. Choose a retained research expert explicitly per instrument/handle through MarketStateResearchExperts.GetCatalog(); there is no universal winner. A ready Direction expert may abstain, while unavailable expert inputs preserve the native direction. Auxiliary event-risk/outcome scores are separate from direction. Retained negative research results remain limitations, not recommendations. Experts cannot currently be combined with Context or HistoricalExpanded.
Historical Expanded and chart details
Open the chart context menu → Phase analyzer → Historical expanded features (walk-forward, experimental). HistoricalExpanded contains 15 immutable annual models, H1/H4/D1 × application years 2020–2024. Runtime chooses the latest version available at the **closed decision**, not the computer date. Fit, selection and calibration end before application; future versions cannot change earlier results.
The latest version persists until a bank update. Unsupported timeframes, missing versions or insufficient history return explicit Unavailable/WarmingUp reasons; there is no silent fallback to the frozen 2024 Expanded model. W1 context is unsupported for this model. Choose supported H1/H4/D1 context or disable context.
D1 needs at least **147 full closed days** of measurements. With history starting on 2020-01-01, the early months of 2020 are not ready; replay/profile requirements can require more. A model availability date alone does not establish readiness.
CalibratedCurrentDirectionProbability uses a past class-conditional Beta(1,1) estimate of raw argmax agreement with an external current-price proxy: (correct+1)/(support+2). It is absent below 8 class decisions or with a custom DirectionFilter. This is separate from heuristic confidence, uncalibrated posterior, continuation and trade profitability. Training uses ETHBTC/LTCBTC/BNBBTC Spot; transfer to BTCUSDT is unconfirmed.
The bank reconstructs previously used research history, not a new independent accuracy test. Neutral displays as Transition and remains Uncertain/Unknown in CurrentRegime, not established Range. See [training and limits](../../../../docs/port/evidence/historical-expanded/README.md).
Register in OnInit with CreateAdvanced(symbol, new MarketStateOptions { Horizons = new[] { new MarketStateHorizon { TimeFrame = TimeFrames.h1 } }, Model = new MarketRegimeModelOptions { DirectionModel = MarketStateDirectionModel.HistoricalExpanded } }, required: false). Inspect each horizon's status and HistoricalModel version/cutoff. Chart settings do not alter an already registered strategy.
Details open only on the phase label at the bottom of a segment (help cursor), or by keyboard focus. Moving over candles does not open them. The scrollable tooltip describes the decision available at the visible segment start; Escape closes it. Local candle detail does not change the analysis timeframe.
Fair strategy A/B
The Oct 1–15 2026 check across four instruments and H1/H4 found four improvements, two deteriorations and two ties, with only 0–13 trades per arm. This is an offline reference strategy using the native phase core, not acceptance of the application's order engine. A reliable benefit is not established. Protocol and all results: docs/port/evidence/phase-strategy-ab/README.md.
Compare the same strategy without an entry gate (A) and with it (B). Gate only **new entries**; manage open positions, stops and exits independently at every analyzer status. Unknown, warmup and missing data are not Range. Freeze their entry policy before evaluating the test period.
Keep data, dates, capital, risk per trade, sizing, fees, slippage and execution equal. Select models, instruments, timeframes and thresholds on train/selection only, then freeze them before test and purge overlapping future targets at split boundaries. Selecting the best expert on test turns it into selection data.
Report net results after costs, drawdown, exposure, trade counts, blocked entries, gate availability/coverage, instrument/period breakdowns and configuration versions. Keep negative and unstable results. Fewer trades or better label accuracy do not establish strategy improvement. Established means operational criteria passed; it is not an accuracy or profit certificate.
Current regime and entry policy
Each horizon returns CurrentRegime for its working timeframe: Unavailable, Uncertain, Transition or Established. Established passes operational thresholds; it is not proven accuracy. Unknown or uncertain Neutral does not establish Range. Context may change the chart direction while CurrentRegime still describes the working timeframe.
MarketStatePolicy.Evaluate(snapshot, new MarketStateEntryPolicy { RequireEstablishedRegime = true }) returns AllowNewEntries and Reasons. The gate is opt-in and only controls new entries. MinimumCurrentDirectionProbability requires applicable current-direction calibration; posterior or confidence cannot replace it. Existing positions, stops, exits and orders remain strategy decisions.
Related commands
Command Palette reference
Find these actions by name in the Command Palette; availability depends on the current project and operation state.
xtester.chartConfigureMarketPhasesxtester.chartHideMarketPhasesxtester.chartShowMarketPhasesxtester.chartToggleMarketPhases