8.1 KiB
Uncertainty and confidence intervals
A point forecast without a range invites overconfidence. This page covers model intervals and conformal intervals so you can size risk and set levels with eyes open.
Dense terms: Confidence interval · Conformal intervals · CI alpha · Horizon
Related: Forecasting · Barriers · Volatility · Glossary
Types of uncertainty
Model confidence intervals
Intervals from the model’s assumptions (for example normal errors).
Limitation: Markets often have fat tails and regime shifts, so these bands can be too narrow.
Conformal intervals
Bands calibrated from historical forecast errors — empirical residual-quantile coverage without strong distributional assumptions.
Advantage: More realistic bounds based on actual performance.
Model Confidence Intervals
Request intervals with --ci-alpha:
mtdata-cli forecast_generate EURUSD --timeframe H1 --horizon 12 \
--method analog --ci-alpha 0.1 --json
Parameters:
--ci-alpha 0.1→ 90% confidence interval--ci-alpha 0.05→ 95% confidence interval
Output includes:
{
"uncertainty": {
"status": "available",
"mode": "interval",
"alpha": 0.1,
"intervals": [
{"time": "2026-01-01T18:00Z", "forecast": 1.1755, "low": 1.1740, "high": 1.1770}
]
}
}
Interpretation:
- Each
uncertainty.intervals[]row is a forecast step.lowandhighare the model interval bounds for that row (price or return, matching the requested quantity). - If model assumptions hold, the true value will fall between
lowandhighabout 90% (or 95%) of the time.
Caution: Financial markets have fat tails. Model CIs often underestimate extreme moves.
forecast_generate does not promote a bullish or bearish point estimate to
direction unless the horizon interval excludes the last observed price. If a
method cannot supply the requested interval, the response reports
signal_status: not_actionable, direction_actionable: false, and preserves
the model-only label as point_estimate_direction for research diagnostics.
Conformal Intervals (forecast_conformal_intervals)
Residual-quantile calibration builds intervals from rolling backtest residuals, making no distributional assumptions.
How It Works
- Run a rolling-origin backtest on historical data
- Collect actual forecast errors at each horizon step
- Use error quantiles to set interval width for new forecasts
Usage
mtdata-cli forecast_conformal_intervals EURUSD --timeframe H1 \
--method theta --horizon 12 --steps 50 --spacing 20 --json
Parameters:
| Parameter | Description | Default |
|---|---|---|
--method |
Forecasting method | theta |
--horizon |
Forecast horizon | 12 |
--steps |
Number of calibration anchors (default 50 for stabler quantiles) | 50 |
--spacing |
Bars between calibration anchors | 20 |
--ci-alpha |
Miscoverage rate (0.05 = 95% interval; 0.1 = 90%) | 0.05 |
When
--steps > 1,--spacingmust be>= --horizonso calibration windows do not overlap; otherwise the request is rejected.
Output
{
"forecast": [
{"time": "2026-01-01T18:00Z", "value": 1.1755, "lower": 1.1740, "upper": 1.1770}
],
"conformal": {
"interval_method": "rolling_residual_quantiles",
"coverage_target": 0.95,
"empirical_coverage": 0.92
}
}
Interpretation:
- Each
forecast[]row has its pointvalueand empirically calibratedlower/upperprice bounds. - With the default alpha, the empirical target coverage is 95%. This is a calibration target, not a finite-sample guarantee.
Use --detail full when you need the raw lower_price / upper_price arrays
or calibration diagnostics such as conformal.per_step_q and per-step coverage.
When to Use
- When you don't trust model-based intervals
- For trading decisions where reliability matters
- When historical data shows fat tails or regime changes
Triple-Barrier Labeling (labels_triple_barrier)
A different approach to uncertainty: instead of predicting where price goes, label what happened historically.
Concept
For each historical bar, ask: "Within the next N bars, did price hit the take-profit level, stop-loss level, or neither?"
Labels:
+1(Win): TP hit first-1(Loss): SL hit first0(Neutral): Neither hit within horizon
In high_low mode, same_bar_policy resolves a bar that touches both
barriers. The default is conservatively sl_first; tp_first and neutral
are explicit alternatives.
When denoising is enabled, the resolved close series anchors each barrier.
high_low still uses raw intrabar highs and lows so an observed tradable touch
is not smoothed away; labeling_spec.hit_price_source reports this as
raw_high_low. Choose label_on=close to use only the resolved close series
for both anchors and hits.
Label preprocessing is causal by default. zero_phase filters use future bars
and are rejected unless --allow-noncausal-denoise is supplied. That override
is for exploratory offline analysis only: the response sets
lookahead_bias=true, suitable_for_backtest=false, and records the effective
method, causality, parameters, and entry column under preprocessing.denoise.
Usage
mtdata-cli labels_triple_barrier EURUSD --timeframe H1 --horizon 12 \
--barriers '{"unit":"pct","take_profit":0.5,"stop_loss":0.3}' --json
Parameters:
| Parameter | Description |
|---|---|
--horizon |
Maximum bars to wait |
--barriers |
JSON object with unit, take_profit, and stop_loss |
--allow-noncausal-denoise |
Explicitly permit look-ahead-contaminated zero-phase labels for offline exploration |
A conventional FX pip is not the same unit as an MT5 tick. Convert pips using the symbol's quote precision before supplying tick distances (for many five-digit FX quotes, one pip is 10 ticks).
Output
{
"entries": ["2025-12-18 17:00", "2025-12-18 18:00", ...],
"labels": [1, -1, 0, 0, 1, ...],
"holding_bars": [5, 3, 12, 12, 8, ...],
"summary": {
"counts": {"pos": 45, "neg": 32, "neut": 123}
}
}
Interpretation:
- Label distribution shows historical win/loss rates for these barrier levels
- Use this to evaluate signal quality or train ML models
Practical Applications
Conservative Position Sizing
Use conformal intervals instead of model CIs:
# Get conformal intervals
mtdata-cli forecast_conformal_intervals EURUSD --horizon 12 --ci-alpha 0.1
# Use the first forecast row's lower value as a stop-loss floor
# Size position so max loss (if that lower bound is hit) is within risk budget
Validating Signal Quality
Use triple-barrier labels to evaluate entry signals:
# Label historical entry points
mtdata-cli labels_triple_barrier EURUSD --horizon 12 --barriers '{"unit":"pct","take_profit":0.5,"stop_loss":0.3}'
# Check win rate: counts.pos / (counts.pos + counts.neg)
# If win rate < 50%, signal needs improvement
Comparing Forecast Methods
Backtest with conformal intervals to compare reliability:
# Method A
mtdata-cli forecast_conformal_intervals EURUSD --method theta --horizon 12 --steps 50
# Method B
mtdata-cli forecast_conformal_intervals EURUSD --method sf_autoarima --horizon 12 --steps 50
# Compare interval widths—narrower = more precise (if coverage is similar)
Quick Reference
| Task | Command |
|---|---|
| Method CI (90%) | mtdata-cli forecast_generate EURUSD --method analog --ci-alpha 0.1 |
| Conformal intervals | mtdata-cli forecast_conformal_intervals EURUSD --method theta --horizon 12 |
| Triple-barrier labels | mtdata-cli labels_triple_barrier EURUSD --horizon 12 --barriers '{"unit":"pct","take_profit":0.5,"stop_loss":0.3}' |
See Also
- FORECAST.md — Price forecasting
- BARRIER_FUNCTIONS.md — TP/SL probability analysis
- GLOSSARY.md — Term definitions