Files

291 lines
10 KiB
Markdown
Raw Permalink Normal View History

2026-07-13 17:15:01 -05:00
# Uncertainty and confidence intervals
2026-01-01 17:26:37 -06: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 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-07-13 17:15:01 -05:00
**Limitation:** Markets often have fat tails and regime shifts, so these bands can be **too narrow**.
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.
**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
mtdata-cli forecast_generate EURUSD --timeframe H1 --horizon 12 --method analog --ci-alpha 0.1 --json
2026-01-01 17:26:37 -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
**Output includes:**
```json
{
"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 17:26:37 -06:00
**Interpretation:**
- 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
**Caution:** Financial markets have fat tails. Model CIs often underestimate extreme moves.
2026-01-01 17:26:37 -06: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.
---
## 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.
### 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
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
```
**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 |
| `--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.
### Output
2026-01-01 17:26:37 -06:00
```json
{
"forecast": [
{"time": "2026-01-01T18:00Z", "value": 1.1755, "lower": 1.1740, "upper": 1.1770}
],
2026-08-01 17:11:00 -05:00
"conformal": {
"interval_method": "rolling_residual_quantiles",
"calibration_anchor_tests_planned": 50,
"calibration_anchor_tests_succeeded": 50,
"calibration_anchor_tests_failed": 0,
"calibration_complete": true,
"coverage_target": 0.95,
"empirical_coverage": 0.92
2026-08-01 17:11:00 -05:00
}
}
```
2026-01-01 17:26:37 -06:00
**Interpretation:**
- Each `forecast[]` row has its point `value` and empirically calibrated
`lower` / `upper` price bounds.
- 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.
- `ci_available=true` requires at least 30 calibration residuals for every
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
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
2026-01-01 17:26:37 -06:00
---
2026-01-01 17:26:37 -06:00
## Triple-Barrier Labeling (`labels_triple_barrier`)
2026-01-01 17:26:37 -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
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
`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-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.
### Usage
2026-01-01 17:26:37 -06:00
```bash
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
```
**Parameters:**
| Parameter | Description |
|-----------|-------------|
| `--horizon` | Maximum bars to wait |
| `--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. |
| `--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. Use `unit=pips` for
forex pip distances. `unit=ticks` remains the broker trade tick/point (for many
five-digit FX quotes, one pip is 10 ticks).
### 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
}
],
"summary": {
2026-08-18 13:38:24 -05:00
"counts": {"tp": 45, "sl": 32, "neutral": 123}
}
}
```
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
**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
# 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:
```bash
# Label historical entry points
mtdata-cli labels_triple_barrier EURUSD --horizon 12 --barrier '{"unit":"pct","take_profit":0.5,"stop_loss":0.3}'
2026-08-18 13:38:24 -05:00
# Check resolved win rate: counts.tp / (counts.tp + counts.sl)
# 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
# Method B
2026-03-06 22:20:53 -06:00
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` |
2026-03-06 22:20:53 -06:00
| Conformal intervals | `mtdata-cli forecast_conformal_intervals EURUSD --method theta --horizon 12` |
| Triple-barrier labels | `mtdata-cli labels_triple_barrier EURUSD --horizon 12 --barrier '{"unit":"pct","take_profit":0.5,"stop_loss":0.3}'` |
---
## See Also
- [FORECAST.md](../FORECAST.md) — Price forecasting
- [BARRIER_FUNCTIONS.md](../BARRIER_FUNCTIONS.md) — TP/SL probability analysis
- [GLOSSARY.md](../GLOSSARY.md) — Term definitions