OPEN-SOURCE SCRIPT

Markov Forecaster PRO

1 123
🟦 Markov Forecaster PRO is a regime-classification and probability-forecasting engine built on a discrete-time Markov chain over three states — Bull, Bear, Sideways. Every bar is labelled from its rolling N-bar log return; the labels feed a 3×3 transition matrix that is power-iterated for the stationary distribution and exponentiated for forward-probability cones (P¹, P³, P⁵, P^horizon). Unlike the dozens of textbook Markov indicators on TradingView, this one layers four original refinements on top of the standard chain construction — each addressing a well-known weakness of the memoryless Markov assumption.

The indicator integrates seven analytical layers — adaptive regime classification, semi-Markov duration tracking, sample-size disclosure, pending-regime early warning, forward-probability forecasting, look-ahead-free backtesting with fees and slippage, and multi-timeframe confluence — each rendered on a single overlay chart through a regime ribbon, three-layer neon glow signals, and four theme-aware dashboard panels.

Built with statistical honesty in mind. The backtest charges configurable commission and slippage on every entry and exit, the transition matrix flags rows with insufficient data, the duration-conditional probabilities are shown alongside the unconditional ones, and the documentation is explicit about what the model can and cannot predict.


🟦 HOW THE CORE ENGINE WORKS

**Regime Classification**

Each bar, the engine measures the rolling N-bar log return:

logRet = log(close / close[N])


The bar is labelled by comparing this return against the configured boundary:

- `logRet > +threshold` → BULL
- `logRet < −threshold` → BEAR
- otherwise → SIDEWAYS

The classification runs every bar with no look-ahead. The choice of threshold determines how reactive the regime label is, and this is where the first refinement enters.

**Adaptive Threshold (k · σ · √N)**

Traditional Markov regime indicators use a fixed percentage cut — e.g. "±5 % over 20 bars". This collapses on real markets: the same 5 % is trivial in a 2017 mania and never reached in 2023 chop. The fix is to scale the boundary with realised volatility:

threshold_adaptive = k × σ × √N

where σ is the per-bar log-return standard deviation over a configurable window (default 100 bars). Under a random walk, k = 1.0 cuts at the 16th / 84th percentiles; k = 2.0 at the 2.5th / 97.5th percentiles. The default k = 1.5 reproduces classic ±1.5-sigma thresholds.

Fixed-percentage mode is still available for users who want to lock the threshold deliberately.

**Regime Confidence**

Once classified, the move's strength is normalised relative to the active boundary:

confidence = |logRet| / threshold

| Confidence | Tier | Visual |
|---|---|---|
| < 1.0× | weak | ▱▱▱ |
| 1.0× – 2.0× | moderate | ▰▱▱ |
| 2.0× – 3.0× | strong | ▰▰▱ |
| ≥ 3.0× | stretched | ▰▰▰ |

The confidence value drives the ribbon transparency (in Adaptive Intensity mode), feeds the High Confidence alert (≥ 2.5× trigger), and is reported in the Status dashboard.


🟦 SEMI-MARKOV DURATION BUCKETS

**The Memoryless Problem**

A standard Markov chain says: "Given I'm in Bull, the probability of staying Bull tomorrow is X — regardless of whether Bull started yesterday or 200 bars ago." This is the memoryless property, and on real markets it's wrong. A 200-day-old Bull regime carries different mean-reversion risk than a 5-day-old one.

**The Refinement**

Markov Forecaster PRO additionally builds two CONDITIONAL transition matrices:

- `P_young` — transitions counted when the source regime's age was below its empirical average duration
- `P_mature` — transitions counted when the source regime's age was at or above the average

Both matrices are constructed in parallel with the main P, using the same per-bar bucketing logic and updated continuously. The self-transition probabilities for the current regime are then surfaced in the Status dashboard:

P[stay] young / mature 91% / 64%

The user reads this as: "When this regime was young (under its avg duration), it continued 91 % of the time. When mature, only 64 %." On a long-running regime this is the canonical signal that mean-reversion risk is rising — without the rest of the chain math being polluted.

A minimum of 10 samples per bucket is required before a value is shown; below that the cell reports "—" rather than display an unreliable probability.


🟦 FORWARD PROBABILITY CONE

**Matrix Exponentiation**

The 3×3 transition matrix P encodes one-bar-ahead probabilities. To project further out, the matrix is multiplied by itself:

P¹ = P — next bar
P³ = P × P × P — 3 bars out
P⁵ = P × P × P × P × P — 5 bars out
P^h = repeated h times — user-configured horizon

The Forecast Cone panel renders all four horizons for each of the three destination regimes, conditioned on the current regime. A trader reading the row "BULL" sees the probability the market will be in Bull at each horizon, given the current regime.

**Stationary Distribution**

Power-iterating the matrix to convergence yields the stationary distribution — the long-run probability of being in each regime, independent of starting state. With 50 iterations (default), any well-behaved 3×3 stochastic matrix is essentially converged.

stat[Bull] + stat[Bear] + stat[Side] = 1.0

This is rendered as the "long-run" row in the Forecast panel and the "Long-run share" cell in the Status panel.

**Honest Limitation**

The cone uses the UNCONDITIONAL matrix (averaged over all regime ages). For duration-conditional probabilities, the Status panel's P[stay] cell is the relevant readout. This split is explicit in both the cone footer label and the Forecast input tooltip.


🟦 SAMPLE-SIZE DISCLOSURE

A probability is only as reliable as the data behind it. Markov Forecaster PRO surfaces sample size in three places:

**Per-row sample count in the Transition Matrix**

A fifth column "n" in the matrix panel reports the number of transitions from each source regime. The cell is colored by reliability tier:

| Sample N | Tier | Color |
|---|---|---|
| ≥ 100 | high | foreground |
| 30 – 99 | moderate | dim |
| < 30 | low | divergent (warning) |

A row with fewer than 30 transitions is flagged because three-decimal probabilities derived from sparse data are noise, not signal.

**Total Sample N in the Status panel**

The Status dashboard's "Sample N" cell sums all transition counts and reports a global reliability tier:

| Total N | Tier |
|---|---|
| ≥ 200 | high (full color) |
| 50 – 199 | moderate (foreground) |
| < 50 | low (divergent warning) |

**Matrix footer**

The matrix panel's footer also shows the total N in compact notation (e.g. "N = 1.8k") for at-a-glance check.

The goal of this layer is honesty: a freshly-loaded chart with 30 bars of history should NOT display the same matrix as a 10-year chart, and the reliability tier makes the difference obvious without the user having to inspect counts manually.


🟦 PENDING-REGIME EARLY WARNING

**The Lookback Lag**

Because the regime is classified from log(close / close[N]), the official regime label inherently lags — by the time the threshold is crossed, the move is already N bars old. This is a structural feature of the model, not a bug, but it can be partially mitigated.

**Pending Logic**

Inside Sideways, when the log return reaches 70 % of either boundary, the dashboard fires an early-warning cue:

distance_fraction = max(|logRet| / threshold, ...)
isPending = (regime == SIDE) AND (distance_fraction ≥ 0.70)

The Status panel's "Pending" cell displays the direction the return is leaning toward and the current fraction:

⚠ ▲ BULL 87%

Color matches the leaning regime. The Pending Regime alert (default OFF, opt-in) fires on the first bar a pending state is entered.

This is not a regime change signal — it's a "watch this" cue, triggered roughly 30 % before the official threshold is crossed. Used alongside the official regime change, it gives the user advance notice without compromising the threshold's strictness.


🟦 LOOK-AHEAD-FREE BACKTEST

**The Look-Ahead Trap**

`regime` is derived from `log(close / close[N])`, which contains today's close. Allocating today's return to today's regime is look-ahead bias — the strategy would "know" today's regime before today's close, which is impossible in real-time trading. Most published Markov backtests have this bug.

**The Fix**

Markov Forecaster PRO allocates positions on the PRIOR bar's confirmed regime:

regForAlloc = regime[1] // yesterday's confirmed regime

If yesterday's regime was Bull, we are long today. The strategy is realisable in real time because the previous bar's regime is known when the current bar opens.

This means the strategy is delayed by one bar relative to the regime label — and that's the correct, honest treatment. If a Bull→Bear flip happens on bar t, the strategy takes bar t's loss (still long from regime[t-1]=Bull) and exits at bar t+1.

**Fees and Slippage**

Every Bull entry and exit pays the configured per-fill cost:

costFrac = feesPct/100 + slippageBps/10000
costPerFill = log(1 − costFrac) // negative log-space cost

The cumulative cost is debited from the Bull log-return total:

Bull gross = exp(bullLogR) − 1
Bull net = exp(bullLogR + bullCostLogR) − 1

A round-trip pays the fee + slippage twice. With defaults (0.10 % fee, 5 bps slippage), each round-trip costs roughly 0.30 % of equity in log space.

**Display**

The Backtest panel renders:

| Field | Value |
|---|---|
| Per-regime rows | GROSS cumulative log return (no fees) |
| Strategy row | NET cumulative (fees applied) vs Buy-and-Hold |
| Methodology footer | trade count · fee % · slippage bps |

The headline strategy result is the NET number — the realistic outcome a trader would have experienced. The gross numbers are kept for diagnostic comparison.

**What This Is Not**

This is a diagnostic backtest, not a tradable strategy. There is no position sizing, no risk management, no overnight financing, no shorting. It tells you whether "long when prior bar was Bull, flat otherwise" would have beaten buy-and-hold after fees — nothing more.


🟦 MULTI-TIMEFRAME CONFLUENCE

The same regime logic runs on a user-configured higher timeframe via `request.security` with `lookahead = barmerge.lookahead_off` and `gaps = barmerge.gaps_off` (anti-repaint mandatory). The result is reported in the Status dashboard's HTF block:

| State | Display | Color |
|---|---|---|
| HTF regime matches LTF regime | ✓ ALIGNED | bull |
| HTF regime differs from LTF | ⚠ DIVERGENT | bear |
| Insufficient HTF data | — | foreground |

Divergent regimes are common at trend turns — the LTF flips before the HTF catches up. Aligned regimes carry higher conviction. A separate alert ("MTF Confluence") fires on regime entries only when the HTF agrees.

Recommended pairings:

| Chart | HTF |
|---|---|
| 1H | D |
| 4H | W |
| D | W |

Use at least 3× your chart timeframe — anything closer and the two regimes track each other with no information gain.


🟦 VISUAL LAYER

**Regime Ribbon**

The chart background is tinted to the current regime color with three style options:

| Style | Behaviour |
|---|---|
| Subtle | Fixed 92 % transparency (price stays hero) |
| Bold | Fixed 75 % transparency (easy to scan from far) |
| Adaptive Intensity | Transparency scales with confidence (60 % – 95 %) |

In Adaptive Intensity mode, a strong directional move (confidence ≥ 3×) renders the ribbon at full intensity; a weak move stays faint. The ribbon doubles as a visual confidence meter.

**Three-Layer Neon Glow Signals**

On every confirmed regime change (after the Min Hold filter), the indicator drops a three-layer halo on the chart:

| Layer | Size | Transparency | Purpose |
|---|---|---|---|
| Outer | size.large | 80 % | Soft halo |
| Middle | size.normal | 50 % | Mid-glow |
| Core | size.small | 0 % | Bright center |

Bull markers (▲) render below the bar; Bear (▼) and Sideways (◆) render above. The Min Hold input (default 4 bars) requires a new regime to persist before its flip is drawn — kills label spam in choppy zones without affecting the underlying transition counts.

**Confidence Tags (optional)**

An off-by-default toggle adds the confidence multiplier to each signal arrow ("BULL 2.3×"), useful for screen captures and analysis.


🟦 DASHBOARDS

Four theme-aware panels, each independently togglable and positionable:

**Status Panel** (default: Bottom Left)

Compact live readout — current regime, age, confidence, pending direction, average duration, young/mature bucket, P[stay] young vs mature, expected remaining bars, long-run share, sample size, and HTF alignment. 16 rows base, 19 with HTF block enabled.

**Transition Matrix Panel** (default: Top Right)

3×3 next-bar P matrix with diagonal-highlighted self-transition cells. The fifth column reports per-row sample size with reliability tier coloring. Matrix footer shows total N.

**Forecast Cone Panel** (default: Middle Right)

Forward probability for each destination regime at horizons +1, +3, +5, and +configured. Steady-state row shows the long-run distribution. Current regime is reported at the bottom for context.

**Backtest Panel** (default: Bottom Right)

Per-regime gross cumulative return, average per-bar, and the bar count. Strategy row shows NET return vs buy-and-hold. Methodology footer lists trade count, fee, and slippage.

All four panels share the same theme palette and adapt to Dark / Light display mode. Text size is independently configurable (Tiny / Small / Normal / Large).


🟦 COLOR THEMES

Ten cohesive palettes tuned to the Apex design system, each defining three regime axes (Bull, Bear, Sideways):

| Theme | Character | Bull | Bear | Sideways |
|---|---|---|---|---|
| Prism | Classic | Forest green | Crimson | Slate grey |
| Focus | Default | Cyan steel | Deep orange | Cool blue-grey |
| Solar | Warm | Amber | Indigo red | Lavender grey |
| Frost | Cool | Sky blue | Soft lavender | Pale steel |
| Laser | Neon | Lime green | Hot crimson | Charcoal grey |
| Aurora | Bright | Gold | Scarlet | Warm beige |
| Plasma | Electric | Aqua | Magenta | Slate teal |
| Bloom | Soft | Mint | Hot pink | Blue-grey |
| Eclipse | Deep | Navy | Dark crimson | Steel grey |
| Carbon | Minimal | Near-white | Mid-grey | Dark grey |

One theme selection drives every visual component: ribbon, glow signals, all four dashboard headers, regime-colored cells, diagonal matrix highlights, and HTF alignment color.

**Dark / Light Display Mode**

Dashboard chrome (background, foreground, borders, section dividers) flips between dark-on-bright and bright-on-dark. The regime axis colors remain consistent across modes — only the panel chrome changes.


🟦 ALERT SYSTEM

Six alert conditions, each independently togglable:

| Alert | Condition |
|---|---|
| Bull Regime Entry | Regime flipped to BULL (after Min Hold confirmation) |
| Bear Regime Entry | Regime flipped to BEAR |
| Sideways Regime Entry | Regime flipped to SIDEWAYS (default OFF) |
| High Confidence | confidence ≥ 2.5× threshold, first bar of crossing |
| MTF Confluence | Regime change + HTF agrees |
| Pending Regime | Inside Sideways, log return ≥ 70 % of either boundary (default OFF) |

All alerts fire on confirmed bar close and use the standard `alertcondition` mechanism. The Min Hold filter applies to entry alerts — a new regime must persist Min Hold bars before its entry alert fires, matching the on-chart glow markers.

The Sideways and Pending alerts are default-off because they can fire more frequently than the other types — opt-in by design.


🟦 SETTINGS REFERENCE

**Theme**
- Theme — One of 10 Apex palettes. Default: Focus
- Display Mode — Dark / Light. Default: Dark

**Regime Logic**
- Threshold Mode — Adaptive (k·σ·√N) / Fixed (%). Default: Adaptive
- Lookback Window — Bars for the rolling log return. Default: 20
- Adaptive k — Sigma multiplier. Default: 1.5
- Fixed Bull Threshold — Used only in Fixed mode. Default: 5.0 %
- Fixed Bear Threshold — Used only in Fixed mode. Default: 5.0 %
- Volatility Window — Bars for the per-bar stdev. Default: 100
- Min Hold — Bars a new regime must persist for label drawing. Default: 4

**Forecast**
- Forecast Horizon — Bars projected by the right-most cone column. Default: 10
- Stationary Power — Power-iteration count. Default: 50

**Regime Ribbon**
- Show Regime Ribbon — Toggle. Default: ON
- Ribbon Style — Subtle / Bold / Adaptive Intensity. Default: Adaptive Intensity

**Signal Labels**
- Show Regime Change Signals — Toggle. Default: ON
- Glow Effect — Three-layer halo toggle. Default: ON
- Show Confidence on Signal — Adds multiplier tag (e.g. "BULL 2.3×"). Default: OFF

**Multi-Timeframe**
- Enable HTF Confluence — Toggle. Default: ON
- HTF Resolution — Higher timeframe. Default: D

**Backtest**
- Trading Fee (% per fill) — Per-side commission. Default: 0.10 %
- Slippage (bps per fill) — Per-side slippage in basis points. Default: 5

**Dashboards**
- Show Status / Matrix / Forecast / Backtest — Independent toggles. Default: all ON
- Dashboard Size — Tiny / Small / Normal / Large. Default: Small

**Panel Positions**
- Status Panel — 9-position grid. Default: Bottom Left
- Matrix Panel — Default: Top Right
- Forecast Panel — Default: Middle Right
- Backtest Panel — Default: Bottom Right

**Alerts**
- Bull / Bear / Sideways Regime Entry — Independent toggles
- High Confidence — Default: ON
- MTF Confluence — Default: ON
- Pending Regime — Default: OFF


🟦 COMPATIBILITY

Works on all asset classes and all timeframes in TradingView Pine Script v6.

- Crypto: Spot, futures, perpetual contracts
- Forex: All pairs
- Equities: Stocks, ETFs, indices
- Commodities: Metals, energy, agriculture
- Timeframes: 1m through Monthly

The adaptive threshold normalises by per-bar realised volatility, making the regime classification volatility-agnostic across assets without manual recalibration. The same default settings work on BTCUSDT daily, SPY weekly, and EURUSD 4H — only the HTF resolution input should be adjusted to match the chart timeframe.


🟦 TECHNICAL NOTES

- Pine Script v6
- `max_labels_count = 500`, `max_lines_count = 100`, `max_bars_back = 5000`
- No repainting — all regime classifications are computed on confirmed bar close. The HTF request uses `lookahead = barmerge.lookahead_off` and `gaps = barmerge.gaps_off`
- Regime change debouncing uses `ta.barssince` to avoid runtime-indexed history reads (which can trip "cannot determine max_bars_back" in Pine v6)
- Heavy computation (matrix exponentiation, stationary distribution, dashboard rendering) is gated on `barstate.islast` to run once per chart render
- Transition counting uses `barstate.isconfirmed` to avoid double-counting the live bar
- Backtest accumulators charge fees at trade boundaries — entries and exits detected by `regForAlloc != regForAlloc[1]`
- Duration buckets use the SOURCE regime's age at the time of transition for classification; the threshold is the empirical average duration of that regime, computed continuously
- Matrix multiplication is implemented as an unrolled 3×3 flat-array routine for portability and speed
- Empty-row fallback to uniform 1/3 in the transition matrix prevents NaN propagation when a regime has not appeared in visible history


🟦 LIMITATIONS — READ THIS

This indicator is statistically honest about what it can and cannot do. Three known limitations:

1. **The Markov assumption is partially violated.** Markets are not memoryless. The duration buckets (Section: Semi-Markov Duration Buckets) mitigate this but do not eliminate it.

2. **Forward probabilities are not predictions.** They are conditional probabilities under the chain assumption. A "Bull 58 % at +10 bars" reading does not mean "58 % chance the next 10 bars are bullish" — it means "given a long-run sample of similar starting states, 58 % were in Bull at +10 bars". Use the cone as ONE input alongside other analysis.

3. **The regime label lags by N bars.** This is structural — the rolling log return necessarily looks back. The Pending early warning partially mitigates this but cannot eliminate the lag. Treat the official regime change as a confirmation, not a leading signal.


🟦 DISCLAIMER

This indicator is provided for educational and informational purposes only. It does not constitute financial advice. Past performance does not guarantee future results. The hypothetical backtest is a diagnostic tool — there is no position sizing, no risk management, and no consideration of overnight financing, dividends, or other real-world frictions beyond the configured fee and slippage. Always conduct your own analysis and apply proper risk management.

免責事項

これらの情報および投稿は、TradingViewが提供または承認する金融、投資、取引、またはその他の種類の助言もしくは推奨であることを意図したものではなく、またこれらに該当するものでもありません。詳細は利用規約をご覧ください。