2026-07-13 17:15:01 -05:00
# Uncertainty and confidence intervals
2026-01-01 17:26:37 -06:00
2026-08-13 13:05:04 -05:00
**Audience:** User
2026-07-13 17:15:01 -05:00
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.
2026-01-01 17:26:37 -06:00
2026-07-13 17:15:01 -05:00
**Dense terms:** [Confidence interval ](../GLOSSARY.md#confidence-interval ) · [Conformal intervals ](../GLOSSARY.md#conformal-intervals ) · [CI alpha ](../GLOSSARY.md#ci-alpha ) · [Horizon ](../GLOSSARY.md#horizon )
**Related:** [Forecasting ](../FORECAST.md ) · [Barriers ](../BARRIER_FUNCTIONS.md ) · [Volatility ](VOLATILITY.md ) · [Glossary ](../GLOSSARY.md )
2026-01-01 17:26:37 -06:00
2026-01-01 18:36:41 -06:00
---
2026-01-01 17:26:37 -06:00
2026-07-13 17:15:01 -05:00
## Types of uncertainty
2026-01-01 17:26:37 -06:00
2026-07-13 17:15:01 -05:00
### Model confidence intervals
Intervals from the model’ s assumptions (for example normal errors).
2026-01-01 18:36:41 -06:00
2026-07-13 17:15:01 -05:00
**Limitation:** Markets often have fat tails and regime shifts, so these bands can be **too narrow** .
2026-01-01 18:36:41 -06:00
2026-07-13 17:15:01 -05:00
### Conformal intervals
2026-08-01 17:11:00 -05:00
Bands calibrated from **historical forecast errors** — empirical residual-quantile coverage without strong distributional assumptions.
2026-01-01 18:36:41 -06:00
**Advantage:** More realistic bounds based on actual performance.
---
## Model Confidence Intervals
Request intervals with `--ci-alpha` :
2026-01-01 17:26:37 -06:00
```bash
2026-08-24 20:28:50 -05:00
mtdata-cli forecast_generate EURUSD --timeframe H1 --horizon 12 --method analog --ci-alpha 0.1 --json
2026-01-01 17:26:37 -06:00
```
2026-01-01 18:36:41 -06:00
**Parameters:**
- `--ci-alpha 0.1` → 90% confidence interval
- `--ci-alpha 0.05` → 95% confidence interval
2026-01-01 17:26:37 -06:00
2026-01-01 18:36:41 -06:00
**Output includes:**
```json
{
2026-08-06 21:00:58 -05:00
"uncertainty" : {
"status" : "available" ,
"mode" : "interval" ,
"alpha" : 0.1 ,
"intervals" : [
{ "time" : "2026-01-01T18:00Z" , "forecast" : 1.1755 , "low" : 1.1740 , "high" : 1.1770 }
]
}
2026-01-01 18:36:41 -06:00
}
```
2026-01-01 17:26:37 -06:00
2026-01-01 18:36:41 -06:00
**Interpretation:**
2026-08-06 21:00:58 -05:00
- Each `uncertainty.intervals[]` row is a forecast step. `low` and `high` are the
model interval bounds for that row (price or return, matching the requested
quantity).
- If model assumptions hold, the true value will fall between `low` and `high`
about 90% (or 95%) of the time.
2026-01-01 17:26:37 -06:00
2026-01-01 18:36:41 -06:00
**Caution:** Financial markets have fat tails. Model CIs often underestimate extreme moves.
2026-01-01 17:26:37 -06:00
2026-08-06 17:34:50 -05:00
`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.
2026-01-01 18:36:41 -06:00
---
## Conformal Intervals (`forecast_conformal_intervals`)
2026-08-01 17:11:00 -05:00
Residual-quantile calibration builds intervals from rolling backtest residuals, making no distributional assumptions.
2026-01-01 18:36:41 -06:00
### How It Works
1. Run a rolling-origin backtest on historical data
2. Collect actual forecast errors at each horizon step
3. Use error quantiles to set interval width for new forecasts
### Usage
2026-01-01 17:26:37 -06:00
```bash
2026-08-24 20:28:50 -05:00
mtdata-cli forecast_conformal_intervals EURUSD --timeframe H1 --method theta --horizon 12 --steps 50 --spacing 20 --json
2026-01-01 17:26:37 -06:00
```
2026-01-01 18:36:41 -06:00
**Parameters:**
| Parameter | Description | Default |
|-----------|-------------|---------|
| `--method` | Forecasting method | theta |
| `--horizon` | Forecast horizon | 12 |
2026-06-07 00:19:23 -05:00
| `--steps` | Number of calibration anchors (default 50 for stabler quantiles) | 50 |
| `--spacing` | Bars between calibration anchors | 20 |
2026-07-17 19:29:26 -05:00
| `--ci-alpha` | Miscoverage rate (0.05 = 95% interval; 0.1 = 90%) | 0.05 |
2026-01-01 17:26:37 -06:00
2026-06-07 00:19:23 -05:00
> When `--steps > 1`, `--spacing` must be `>= --horizon` so calibration windows do not overlap; otherwise the request is rejected.
2026-01-01 18:36:41 -06:00
### Output
2026-01-01 17:26:37 -06:00
2026-01-01 18:36:41 -06:00
```json
{
2026-08-06 21:00:58 -05:00
"forecast" : [
{ "time" : "2026-01-01T18:00Z" , "value" : 1.1755 , "lower" : 1.1740 , "upper" : 1.1770 }
],
2026-08-01 17:11:00 -05:00
"conformal" : {
2026-08-06 21:00:58 -05:00
"interval_method" : "rolling_residual_quantiles" ,
2026-08-27 16:21:25 -05:00
"calibration_anchor_tests_planned" : 50 ,
"calibration_anchor_tests_succeeded" : 50 ,
"calibration_anchor_tests_failed" : 0 ,
"calibration_complete" : true ,
2026-08-06 21:00:58 -05:00
"coverage_target" : 0.95 ,
"empirical_coverage" : 0.92
2026-08-01 17:11:00 -05:00
}
2026-01-01 18:36:41 -06:00
}
```
2026-01-01 17:26:37 -06:00
2026-01-01 18:36:41 -06:00
**Interpretation:**
2026-08-06 21:00:58 -05:00
- Each `forecast[]` row has its point `value` and empirically calibrated
`lower` / `upper` price bounds.
2026-08-12 18:02:39 -05:00
- With the default alpha, `nominal_confidence_level` is 95%. This is a
calibration target, not a finite-sample guarantee. Compare it with the
top-level `empirical_coverage` and `coverage_status` fields before using the
interval as evidence of historical calibration quality.
2026-08-19 09:39:37 -05:00
- `ci_available=true` requires at least 30 calibration residuals for every
2026-08-27 16:21:25 -05:00
forecast step and complete forecast paths from every planned anchor. Smaller
samples return `ci_status=insufficient_calibration` . A failed or incomplete
anchor returns `ci_status=incomplete_anchor_coverage` . Both cases set
`ci_available=false` and keep any bounds diagnostic-only.
- Check `calibration_anchor_tests_failed=0` and `calibration_complete=true` .
Empirical coverage from only the anchors where a model happened to fit can
be biased, so mtdata does not promote such a subset to a decision-use band.
2026-01-01 17:26:37 -06:00
2026-08-06 21:00:58 -05:00
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.
2026-01-01 18:36:41 -06:00
### 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
2026-01-01 17:26:37 -06:00
2026-01-01 18:36:41 -06:00
---
2026-01-01 17:26:37 -06:00
2026-01-01 18:36:41 -06:00
## Triple-Barrier Labeling (`labels_triple_barrier`)
2026-01-01 17:26:37 -06:00
2026-01-01 18:36:41 -06:00
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 first
- `0` (Neutral): Neither hit within horizon
2026-07-11 20:46:39 -05:00
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.
2026-03-27 19:44:01 -05:00
2026-08-01 05:20:42 -05:00
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.
2026-08-12 12:09:05 -05:00
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
2026-08-13 22:17:52 -05:00
`denoise_lookahead_bias=true` , `suitable_as_training_target=false` , and records
the effective method, causality, parameters, and entry column under
`preprocessing.denoise` .
Every triple-barrier outcome uses later bars by design. Responses therefore set
`label_uses_future_path=true` , `suitable_as_live_feature=false` , and separately
state whether the result remains a valid historical training target. Join these
labels as targets, never as same-timestamp live features.
2026-08-12 12:09:05 -05:00
2026-08-20 09:12:35 -05:00
The label row is keyed by `entry_bar_open_time` , but its `entry_price` is the
source bar's close. Use `entry_price_available_at` as the earliest decision time
and only join features that were available by that instant. For example, an H1
row opened at `05:00Z` has a close-derived entry price available at `06:00Z` .
`tp_hit_bar_open_time` and `sl_hit_bar_open_time` identify the candle containing
the first observed touch; OHLC data cannot reveal the exact intrabar touch time.
The machine-readable `timestamp_contract` carries the same rules in compact and
full responses. Daily, weekly, and monthly availability uses broker-calendar
bar boundaries, including configured daylight-saving changes.
2026-01-01 18:36:41 -06:00
### Usage
2026-01-01 17:26:37 -06:00
```bash
2026-08-31 15:45:54 -05:00
mtdata-cli labels_triple_barrier EURUSD --timeframe H1 --horizon 12 --barrier '{"unit":"pct","take_profit":0.5,"stop_loss":0.3}' --json
2026-01-01 17:26:37 -06:00
```
2026-01-01 18:36:41 -06:00
**Parameters:**
| Parameter | Description |
|-----------|-------------|
| `--horizon` | Maximum bars to wait |
2026-08-31 15:45:54 -05:00
| `--barrier` | JSON object with `unit` , `take_profit` , and `stop_loss` . `ticks` is the broker trade tick/point, not FX pips; use `unit=pips` for forex pips. |
2026-08-12 12:09:05 -05:00
| `--allow-noncausal-denoise` | Explicitly permit look-ahead-contaminated zero-phase labels for offline exploration |
2026-08-01 05:20:42 -05:00
2026-08-31 15:45:54 -05:00
A conventional FX pip is not the same unit as an MT5 tick. Use `unit=pips` for
forex pip distances. `unit=ticks` remains the broker trade tick/point (for many
2026-08-01 05:20:42 -05:00
five-digit FX quotes, one pip is 10 ticks).
2026-01-01 18:36:41 -06:00
### Output
```json
{
2026-08-18 13:38:24 -05:00
"data" : [
{
2026-08-20 09:12:35 -05:00
"entry_bar_open_time" : "2025-12-18T17:00Z" ,
"entry_price_available_at" : "2025-12-18T18:00Z" ,
2026-08-18 13:38:24 -05:00
"label" : 1 ,
"outcome" : "tp_first" ,
2026-08-20 09:12:35 -05:00
"holding_bars" : 5 ,
"tp_hit_bar_open_time" : "2025-12-18T22:00Z"
2026-08-18 13:38:24 -05:00
}
],
2026-01-01 18:36:41 -06:00
"summary" : {
2026-08-18 13:38:24 -05:00
"counts" : { "tp" : 45 , "sl" : 32 , "neutral" : 123 }
2026-01-01 18:36:41 -06:00
}
}
```
2026-08-18 13:38:24 -05:00
Compact output keeps at most 10 representative rows in `data` ; summary counts
cover the full requested lookback. Use `--detail full` when you need the
2026-08-20 09:12:35 -05:00
parallel `entry_bar_open_times` , `entry_price_available_at` , `labels` , and
`holding_bars` arrays for model training.
2026-08-18 13:38:24 -05:00
2026-01-01 18:36:41 -06:00
**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:
```bash
# Get conformal intervals
2026-04-04 17:05:23 -05:00
mtdata-cli forecast_conformal_intervals EURUSD --horizon 12 --ci-alpha 0.1
2026-01-01 18:36:41 -06:00
2026-08-06 21:00:58 -05:00
# 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
2026-01-01 18:36:41 -06:00
```
### Validating Signal Quality
Use triple-barrier labels to evaluate entry signals:
```bash
# Label historical entry points
2026-08-31 15:45:54 -05:00
mtdata-cli labels_triple_barrier EURUSD --horizon 12 --barrier '{"unit":"pct","take_profit":0.5,"stop_loss":0.3}'
2026-01-01 18:36:41 -06:00
2026-08-18 13:38:24 -05:00
# Check resolved win rate: counts.tp / (counts.tp + counts.sl)
2026-01-01 18:36:41 -06:00
# If win rate < 50%, signal needs improvement
```
### Comparing Forecast Methods
Backtest with conformal intervals to compare reliability:
```bash
# Method A
2026-03-06 22:20:53 -06:00
mtdata-cli forecast_conformal_intervals EURUSD --method theta --horizon 12 --steps 50
2026-01-01 18:36:41 -06:00
# Method B
2026-03-06 22:20:53 -06:00
mtdata-cli forecast_conformal_intervals EURUSD --method sf_autoarima --horizon 12 --steps 50
2026-01-01 18:36:41 -06:00
# Compare interval widths—narrower = more precise (if coverage is similar)
```
---
## Quick Reference
| Task | Command |
|------|---------|
2026-07-16 17:25:00 -05:00
| Method CI (90%) | `mtdata-cli forecast_generate EURUSD --method analog --ci-alpha 0.1` |
2026-03-06 22:20:53 -06:00
| Conformal intervals | `mtdata-cli forecast_conformal_intervals EURUSD --method theta --horizon 12` |
2026-08-31 15:45:54 -05:00
| Triple-barrier labels | `mtdata-cli labels_triple_barrier EURUSD --horizon 12 --barrier '{"unit":"pct","take_profit":0.5,"stop_loss":0.3}'` |
2026-01-01 18:36:41 -06:00
---
## See Also
- [FORECAST.md ](../FORECAST.md ) — Price forecasting
- [BARRIER_FUNCTIONS.md ](../BARRIER_FUNCTIONS.md ) — TP/SL probability analysis
- [GLOSSARY.md ](../GLOSSARY.md ) — Term definitions