mirror of
https://github.com/lucasmos/GoldRegime_X.git
synced 2026-11-02 06:26:15 +00:00
- Introduced `test_lifecycle_parity.py` to verify event timelines between live and backtest evaluations, ensuring consistent activation, partial close, scalp exit, and regime shift behaviors across different timeframes. - Added `test_lifecycle_units.py` for unit testing of lifecycle configurations, including cent normalization, activation thresholds, and the absence of R-multiple fields. - Created `test_three_state_contract.py` to enforce and validate the three-state regime (TREND, MEAN_REVERSION, VOLATILITY_SHOCK) across various components, ensuring that any attempt to use more than three states raises appropriate errors.
1298 lines
59 KiB
Markdown
1298 lines
59 KiB
Markdown
# Gold Regime X
|
||
|
||
A hybrid machine learning trading system for **XAUUSD (Gold)** that combines Hidden Markov Models for regime detection, XGBoost for signal classification, and a stateful **Regime-Confirmation Signal Engine** that gates live trades on regime persistence, XGBoost probability, synth-VIX Z-score, and dynamic ATR band position. Designed for live execution through MetaTrader 5 on both **Headway Cent** (micro) and **Standard** accounts, with full Telegram remote control and health monitoring.
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
1. [How It Works](#how-it-works)
|
||
2. [Architecture](#architecture)
|
||
3. [Account Types — Cent vs Standard](#account-types--cent-vs-standard)
|
||
4. [Prerequisites](#prerequisites)
|
||
5. [Installation](#installation)
|
||
6. [Data Setup](#data-setup)
|
||
7. [Configuration](#configuration)
|
||
8. [Complete Workflow — Start to Live Trading](#complete-workflow--start-to-live-trading)
|
||
9. [Command Reference](#command-reference)
|
||
10. [Signal Logic — Regime-Confirmation](#signal-logic--regime-confirmation)
|
||
11. [Automatic Data Updates](#automatic-data-updates)
|
||
12. [Sensitivity Analysis](#sensitivity-analysis)
|
||
13. [Interactive Research Notebook](#interactive-research-notebook)
|
||
14. [Risk Management](#risk-management)
|
||
15. [Timeframe Configurations](#timeframe-configurations)
|
||
16. [Performance Metrics & Scoring](#performance-metrics--scoring)
|
||
17. [Optimizer Anti-Overfitting Rules](#optimizer-anti-overfitting-rules)
|
||
18. [Telegram Remote Control](#telegram-remote-control)
|
||
19. [Multi-TF Live Trading](#multi-tf-live-trading)
|
||
20. [MQL5 EA (Alternative Execution)](#mql5-ea-alternative-execution)
|
||
21. [Combinatorial Purged CV (CPCV)](#combinatorial-purged-cv-cpcv)
|
||
22. [Walk-Forward Analysis & Staleness Gate](#walk-forward-analysis--staleness-gate)
|
||
23. [Troubleshooting](#troubleshooting)
|
||
24. [Security Notes](#security-notes)
|
||
|
||
---
|
||
|
||
## How It Works
|
||
|
||
```
|
||
Raw OHLCV CSV
|
||
│
|
||
▼
|
||
Kalman Filter → Smoothed log returns
|
||
│
|
||
▼
|
||
GaussianHMM (k-means prior init, Baum-Welch EM)
|
||
│ Observation features: [kalman_return, volatility, rsi_slope]
|
||
│ All StandardScaler-normalised before fit
|
||
▼
|
||
Regime Labels Bull=0 Bear=1 Chop=2 [Chop_High=3] (n_states 3–4, optimised per TF)
|
||
│ Median filter smoothing applied post-prediction
|
||
│ kernel: H1=3 bars, M15=5 bars, M5=5 bars
|
||
│ prevents rapid state oscillation between consecutive bars
|
||
│
|
||
▼
|
||
Session Filter → London/NY overlap only 08:00–17:59 UTC
|
||
│ Applied after all rolling indicators are computed
|
||
│ Off-session bars excluded from training and inference
|
||
│
|
||
▼
|
||
GMM Volatility Cluster → quiet / normal / volatile label
|
||
│
|
||
▼
|
||
XGBoost Three-Model Volatility Ensemble
|
||
│ Base features (always):
|
||
│ [hmm_0, hmm_1, hmm_2 [, hmm_3] ← OHE, one column per HMM state
|
||
│ gmm_vol_cluster, rsi_slope,
|
||
│ atr_normalized, prev_log_return]
|
||
│ External features (optional, added when masters exist):
|
||
│ usdchf_log_return, xagusd_log_return,
|
||
│ xtiusd_log_return, synth_vix_zscore*, atr_band_position*
|
||
│ Low / Med / High ATR bucket → separate XGBoost model
|
||
│ * computed from XAUUSD price — no external CSV needed
|
||
│
|
||
▼
|
||
▼
|
||
SignalEngine — Regime Confirmation
|
||
│ update_regime() tracks consecutive bars in current state
|
||
│ should_enter(): bars ≥ 2, XGBoost prob ≥ TF threshold, P(stay) ≥ TF floor
|
||
│ MR entry: ATR band < 0.10 (buy) or ATR band > 0.90 (sell) in Chop state
|
||
│
|
||
▼
|
||
TCN Confidence Scorer — **removed**
|
||
│ (XGBoost probability gate is the direct signal quality filter)
|
||
│
|
||
▼
|
||
IS / OOS Backtest
|
||
│ Bar-by-bar SignalEngine loop — floating drawdown, Sharpe, Recovery Factor,
|
||
│ Profit Factor, MR attribution, MT5-style equity curve
|
||
│
|
||
▼
|
||
Complex Criterion Score = RF_c×0.35 + Sharpe_c×0.35 + (PF−1)×0.20 + Edge_c×0.10
|
||
│ (+return_consistency×0.30 bonus cap for M5/M15)
|
||
│ All terms symmetrically clamped — see Scoring section for details
|
||
│
|
||
▼
|
||
Live Bridge → MT5 Market Orders
|
||
│ IOC fill, ATR-based SL, staged TPs, ATR trail
|
||
│ Hybrid Scalp Protection on M5 (between-bar, every 5 s)
|
||
│ TF-specific magic numbers: H1=123456, M15=123457, M5=123458
|
||
```
|
||
|
||
The **Optuna optimizer** searches Kalman parameters, HMM state count, and XGBoost hyperparameters (including L1 and L2 regularisation), scoring every trial on **OOS Complex Criterion only** using **Combinatorial Purged Cross-Validation (CPCV)** with `C(4,2) = 6` combinatorial train/test paths. The dataset is split into 4 chronological blocks; each trial evaluates all 6 path combinations with a TF-specific purge gap (H1: 24 bars, M15: 96 bars, M5: 288 bars) to prevent leakage between adjacent train and test blocks. Stage 1 (`--stage xgb`) uses a fast single IS/OOS hold-out (~5× faster, no CPCV); Stage 2 (`--stage trading`) runs full CPCV warm-started from Stage 1 params. Before each optimization run, `ensure_data_updated()` auto-fetches any missing bars from MT5 so the model always trains on the most recent data.
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
| File | Purpose |
|
||
|------|---------|
|
||
| `src/processor.py` | Kalman filter, log returns, RSI, ATR, GMM vol cluster, per-TF config; **London/NY session gating** (08:00–17:59 UTC, applied after all rolling indicators) |
|
||
| `src/engine_hmm.py` | GaussianHMM with k-means prior init; TF persistence boost; **median-filter state smoothing** (kernel H1=3, M15/M5=5) to prevent rapid state oscillation |
|
||
| `src/engine_xgb.py` | XGBoost volatility ensemble; three vol-bucket models (Low/Med/High ATR); **`hmm_state` OHE** (`hmm_0`/`hmm_1`/`hmm_2`[`/hmm_3`]) in `prepare_features`; **`atr_band_position`** feature; `compute_regime_stats()` for metadata; ONNX export |
|
||
| `src/signal_engine.py` | **Stateful signal engine** — `SignalEngine`; regime-confirmation entry (persistence + XGBoost prob + synth-VIX Z-score + ATR band); exit on regime reversal, persistence collapse, profit erosion, or max hold |
|
||
| `src/sensitivity.py` | Z-Score sensitivity sweep — Bull/Bear cutoffs 1.5–3.0σ; outputs ranked table + `reports/sensitivity_<TF>_<broker>.csv/json` |
|
||
| `src/backtester.py` | Bar-by-bar backtest via `SignalEngine` — IS/OOS split, broker costs, floating drawdown, MT5 equity curve, MR attribution |
|
||
| `src/optimizer.py` | CPCV optimizer — C(4,2)=6 combinatorial purged paths; Calmar-dominant composite scoring (Calmar×0.45 + Sharpe×0.35 + PF×0.15 + Edge×0.05); `CPCV_N_BLOCKS=4`, `CPCV_K_TEST=2`, `CPCV_PURGE_BARS` per TF; two-stage: `--stage xgb` fast hold-out → `--stage trading` full CPCV; `WFO_PARAMS`/`WFO_PARAMS_FAST` retained for backward-compat (notebook); per-broker SQLite resume; RAM guard; Telegram heartbeat |
|
||
| `notebooks/GoldRegimeX_Explorer.ipynb` | Interactive research notebook — equity explorer, WFO window analysis (standard/fast mode), feature/regime explorer, parameter sensitivity with WFO comparison, CV Path Inspector (Section 6) |
|
||
| `src/risk_manager.py` | AdaptiveRiskManager, CentConverter, DailyEquityGate, broker cost configs |
|
||
| `src/visualizer.py` | 6-chart visual report: regime overlay, equity curve, features, transition matrix, dashboard, MT5 balance/equity |
|
||
| `src/mt5_sync.py` | MT5 data downloader; **`ensure_data_updated()`** — auto-fetches missing bars before each optimization run |
|
||
| `src/validator.py` | Pre-live validation gate — SignalEngine inference + Sharpe threshold + spread-payoff erosion warning |
|
||
| `src/mt5_trader.py` | Live execution loop: bar detection, XGBoost inference, `SignalEngine` regime-confirmation entry/exit, order placement, ATR trailing exits, M5 scalp recycling |
|
||
| `src/notifier.py` | Telegram message sender |
|
||
| `src/auditor.py` | MT5 deal history report |
|
||
| `src/data_updater.py` | **Weekly MT5 data pull** — `WeeklyDataUpdater` appends fresh XAUUSD bars to raw CSVs every Sunday |
|
||
| `src/guardian.py` | Multi-TF rolling health monitor — fires Telegram alert when rolling Sharpe drops below threshold |
|
||
| `src/remote_control.py` | Telegram long-polling bot for remote commands |
|
||
| `src/data_consolidator.py` | USDCHF master file builder |
|
||
| `src/monte_carlo.py` | Monte Carlo simulation on trained models — distribution of expected return/drawdown outcomes |
|
||
| `main.py` | CLI entry point for all modes |
|
||
| `mql5/GoldRegimeX.mq5` | MT5 Expert Advisor with ONNX inference (alternative to Python bridge) |
|
||
|
||
---
|
||
|
||
## Account Types — Cent vs Standard
|
||
|
||
Choose your account type before running any command. The `--broker` flag controls lot sizing, P&L conversion, spread guards, and signal calibration throughout the entire pipeline.
|
||
|
||
### Headway Cent Account (`--broker headway_cent`)
|
||
|
||
| Item | Cent Account | Example |
|
||
|------|-------------|---------|
|
||
| Real deposit | $15 USD | Wired to Headway |
|
||
| MT5 balance display | 1 500.00 USC | real USD × 100 |
|
||
| Minimum lot | 0.01 | = 0.01 oz gold |
|
||
| P&L per $1 gold move at 0.01 lot | 0.01 USC | = **$0.0001 real USD** |
|
||
| MT5 history shows `+15.00` | ÷ 100 | = **$0.15 real USD** |
|
||
| Bridge balance handling | Divides raw balance by 100 | Pass `--balance 15` (real USD) |
|
||
| Spread viability guard (M5) | TP1 ≥ 1.5× spread | |
|
||
|
||
**Best for:** Learning the system and verifying signals with minimal capital at risk.
|
||
|
||
### Headway Standard Account (`--broker standard`)
|
||
|
||
| Item | Standard Account | Example |
|
||
|------|-----------------|---------|
|
||
| Minimum recommended | $500+ USD | |
|
||
| Minimum lot | 0.01 | = 1 oz gold |
|
||
| P&L per $1 gold move at 0.01 lot | **$1.00 real USD** | 100× more than cent |
|
||
| Bridge balance handling | Uses raw balance directly | Pass `--balance 500` |
|
||
| Spread viability guard | TP1 ≥ 3.0× spread | All timeframes |
|
||
|
||
> **Key difference:** On cent, `+$2.40` in MT5 history = `$0.024` real USD. On standard it is exactly `$2.40`. The bridge always logs P&L in real USD regardless of account type.
|
||
|
||
---
|
||
|
||
## Prerequisites
|
||
|
||
- **Windows** — MetaTrader5 Python package is Windows-only
|
||
- **Python 3.11**
|
||
- **MetaTrader5 terminal** open and logged into your broker account
|
||
- **XAUUSD** visible in Market Watch
|
||
- **Algorithmic trading enabled** in MT5: Tools → Options → Expert Advisors → Allow Algorithmic Trading
|
||
- A **Telegram bot** (optional but strongly recommended for remote control and health alerts)
|
||
|
||
---
|
||
|
||
## Installation
|
||
|
||
```bash
|
||
# 1. Clone the repo
|
||
git clone https://github.com/lucasmos/GoldRegime_X.git
|
||
cd GoldRegime_X
|
||
|
||
# 2. Create a virtual environment
|
||
python -m venv venv
|
||
venv\Scripts\activate
|
||
|
||
# 3. Install dependencies
|
||
pip install -r requirements.txt
|
||
|
||
# 4. Copy the environment template
|
||
copy .env.example .env
|
||
```
|
||
|
||
---
|
||
|
||
## Data Setup
|
||
|
||
Export historical OHLCV CSVs from MetaTrader5 (History Center — F2 → XAUUSD → your TF → Export):
|
||
|
||
| Timeframe | Expected filename |
|
||
|-----------|-------------------|
|
||
| H1 | `data/raw/XAU_1h_data.csv` |
|
||
| M15 | `data/raw/XAU_15m_data.csv` |
|
||
| M5 | `data/raw/XAU_5m_data.csv` |
|
||
|
||
**Format:** semicolon-delimited, columns: `Date;Open;High;Low;Close;Volume`
|
||
|
||
> The system filters to the **last 10 years** anchored at the end of your CSV. Export fresh data before each full pipeline re-run.
|
||
|
||
### Getting deep history from MT5
|
||
|
||
1. Tools → Options → Charts → Max bars: `99,999,999`
|
||
2. View → Symbols (`Ctrl+U`) → XAUUSD → Bars tab → Request (repeat until start year is reached)
|
||
3. Export Bars → save as `.csv`
|
||
|
||
### External Asset Features (optional, recommended)
|
||
|
||
The processor enriches each XAUUSD bar with cross-asset log returns. Export each asset from MT5 and run `--mode consolidate` to build the master files.
|
||
|
||
| Asset | Role | Source files (`data/raw/`) | Master produced |
|
||
|-------|------|---------------------------|-----------------|
|
||
| USDCHF | Intraday DXY proxy (~0.85 correlation) | `USDCHF_H1.csv` / `USDCHF_M15_*.csv` / `USDCHF_M5_*.csv` | `USDCHF_master[_M15/_M5].csv` |
|
||
| XAGUSD | Silver — cross-commodity regime signal | `XAGUSD_H1.csv` / `XAGUSD_M15_*.csv` / `XAGUSD_M5_*.csv` | `XAGUSD_master[_M15/_M5].csv` |
|
||
| XTIUSD | WTI crude — macro risk-on/off signal | `XTIUSD_H1.csv` / `XTIUSD_M15_*.csv` / `XTIUSD_M5_*.csv` | `XTIUSD_master[_M15/_M5].csv` |
|
||
| US500 | S&P 500 — equity/gold correlation | `US500_H1.csv` / `US500_M15_*.csv` / `US500_M5_*.csv` | `US500_master[_M15/_M5].csv` |
|
||
| USDJPY | JPY safe-haven proxy | `USDJPY_H1.csv` / `USDJPY_M15_*.csv` / `USDJPY_M5_*.csv` | `USDJPY_master[_M15/_M5].csv` |
|
||
|
||
All masters live in `data/processed/`. The `synth_vix_zscore` feature (Williams VIX Fix) and `atr_band_position` feature (dynamic ATR band position, 0 = lower band, 1 = upper band) are both computed directly from XAUUSD price — no external file is needed for either.
|
||
|
||
```bash
|
||
python main.py --mode consolidate
|
||
```
|
||
|
||
The pipeline degrades gracefully: any asset whose master file is absent is simply omitted from the feature set. XGBoost's `get_feature_cols()` only includes a column when it is >50% non-null.
|
||
|
||
---
|
||
|
||
## Configuration
|
||
|
||
### 1. Create a Telegram Bot (optional but recommended)
|
||
|
||
1. Open Telegram → search **@BotFather** → `/newbot`
|
||
2. Copy the **Bot Token**
|
||
3. Find your **Chat ID** via `https://api.telegram.org/bot<TOKEN>/getUpdates`
|
||
4. Find your **User ID** via [@userinfobot](https://t.me/userinfobot)
|
||
|
||
### 2. Edit your `.env` file
|
||
|
||
```env
|
||
TELEGRAM_BOT_TOKEN=your_bot_token_here
|
||
TELEGRAM_CHAT_ID=your_chat_id_here
|
||
ALLOWED_USER_ID=your_telegram_user_id_here
|
||
|
||
# Defaults used by the Telegram START TRADING button
|
||
LIVE_TF=H1
|
||
LIVE_BROKER=headway_cent
|
||
LIVE_BALANCE=15
|
||
```
|
||
|
||
---
|
||
|
||
## Complete Workflow — Start to Live Trading
|
||
|
||
This is the full sequence for a first-time setup on a **Headway Cent account, H1 timeframe**. Adapt TF and broker flags as needed.
|
||
|
||
### Phase 0 — One-time setup
|
||
|
||
```bash
|
||
# Consolidate USDCHF CSV exports into per-TF master files
|
||
python main.py --mode consolidate
|
||
```
|
||
|
||
### Phase 1 — Build and optimise the model
|
||
|
||
Model optimisation uses a **unified single-stage workflow**. A single
|
||
exploration (∼5× faster, no CPCV) that saves the best params to `models/stage1_{tf}_{broker}.json`.
|
||
Stage 2 runs the full CPCV optimization warm-started from those params. Both stages are safe to
|
||
interrupt and resume. Run `--mode train` after both stages to commit the final model to disk.
|
||
|
||
```bash
|
||
# --- Preparation ---
|
||
python main.py --mode process --tf H1
|
||
|
||
# NOTE: ensure_data_updated() runs automatically at the start of every optimize run
|
||
# to fetch any bars missing since your last CSV export — MT5 must be connected.
|
||
|
||
# === STAGE 1: Fast XGB Exploration (no CPCV, ~5x faster) ===
|
||
# Explores the hyperparameter landscape with a single IS/OOS split.
|
||
# Saves best params to models/stage1_h1_headway_cent.json.
|
||
python main.py --mode optimize --tf H1 --broker headway_cent --balance 15 --trials 100
|
||
|
||
# === STAGE 2: Full CPCV Optimization (warm-started from Stage 1) ===
|
||
# Runs C(4,2)=6 CPCV path evaluation, seeded from Stage 1 params.
|
||
# Saves all trials to models/study_headway_cent.db.
|
||
python main.py --mode optimize --tf H1 --broker headway_cent --balance 15 --stage trading --trials 130
|
||
|
||
# === COMMIT: Train Final Model ===
|
||
# Reads best params from study DB, trains GaussianHMM + XGBoost 3-vol-bucket ensemble.
|
||
# Saves: hmm_model_H1_headway_cent.pkl + xgb_ensemble_H1_headway_cent.pkl
|
||
python main.py --mode train --tf H1 --broker headway_cent --balance 15
|
||
|
||
# --- Optional post-training checks ---
|
||
python main.py --mode sensitivity --tf H1 --broker headway_cent --balance 15
|
||
python main.py --mode montecarlo --tf H1 --broker headway_cent --balance 15
|
||
python main.py --mode report --tf H1 --broker headway_cent --balance 15
|
||
python main.py --mode compare --tf H1,M15,M5 --broker headway_cent --balance 15
|
||
python main.py --mode export --tf H1 --broker headway_cent
|
||
```
|
||
|
||
|
||
### Phase 2 — Validate before going live
|
||
|
||
```bash
|
||
# Download live MT5 data and check rolling Sharpe against trained model.
|
||
# Use --period 6m for H1 and M15; --period 3m for M5.
|
||
# --period 12m available for a deeper H1 look-back.
|
||
python main.py --mode sync_validate --tf H1 --broker headway_cent --balance 15 --period 6m
|
||
```
|
||
|
||
| Status | H1 Sharpe | M15 Sharpe | M5 Sharpe | Action |
|
||
|--------|-----------|------------|-----------|--------|
|
||
| **PASS** | ≥ 0.25 | ≥ 0.50 | ≥ 0.70 | Proceed |
|
||
| **WARN** | 0.05–0.25 | 0.20–0.50 | 0.40–0.70 | Proceed with caution or re-optimise |
|
||
| **FAIL** | < 0.05 | < 0.20 | < 0.40 | Re-optimise + retrain before going live |
|
||
|
||
FAIL exits with code 1 and blocks the live script.
|
||
|
||
### Phase 3 — Walk-Forward Analysis (optional but recommended)
|
||
|
||
```bash
|
||
python main.py --mode wfa --tf H1 --broker headway_cent --balance 15
|
||
```
|
||
|
||
Checks that the model generalises across time periods. WFE ≥ 60% = robust.
|
||
|
||
### Phase 4 — Demo test
|
||
|
||
Connect MT5 to a **demo account** and run:
|
||
|
||
```bash
|
||
python main.py --mode demo --tf H1 --broker headway_cent --balance 15
|
||
```
|
||
|
||
This sends **real orders** to MT5 (no simulation). Use it to verify lot sizes, TP/SL placement, and session limits before risking real money.
|
||
|
||
### Phase 5 — Go live
|
||
|
||
Connect MT5 to your **live account** and run:
|
||
|
||
```bash
|
||
python main.py --mode live --tf H1 --broker headway_cent --balance 15
|
||
```
|
||
|
||
Type `YES` when prompted. The live loop:
|
||
|
||
1. Detects each newly completed bar (polls every 5 s)
|
||
2. Fetches 200 bars for Kalman / HMM warm-up
|
||
3. Runs Kalman → GMM → HMM → XGBoost inference
|
||
4. `SignalEngine.update_regime()` + `should_enter()` — checks persistence, XGBoost prob, synth-VIX Z-score, ATR band position
|
||
5. Applies session limits (08:00–17:59 UTC), margin check, spread viability guard
|
||
6. Places IOC market orders with ATR-based SL and staged TPs (ATR multiples)
|
||
7. Logs closed P&L in real USD after every trade
|
||
8. Every Sunday: appends fresh bars to raw CSVs (WeeklyDataUpdater)
|
||
|
||
> **Important:** Remove the GoldRegimeX.mq5 EA from any XAUUSD chart before starting the same-TF Python bridge — they share the same magic number.
|
||
|
||
### Phase 6 — Ongoing maintenance
|
||
|
||
```bash
|
||
# Weekly (M5) / monthly (M15, H1):
|
||
python main.py --mode sync_validate --tf H1 --broker headway_cent --balance 15 --period 6m
|
||
python main.py --mode wfa --tf H1 --broker headway_cent --balance 15
|
||
|
||
# If sync_validate fails or WFE < 50% — re-run the two-stage optimize + retrain:
|
||
# Stage 1: re-run fast XGB exploration (resumes existing study, adds trials on top)
|
||
python main.py --mode optimize --tf H1 --broker headway_cent --balance 15 --trials 100
|
||
# Stage 2: re-run full CPCV (warm-started from updated Stage 1 params)
|
||
python main.py --mode optimize --tf H1 --broker headway_cent --balance 15 --stage trading --trials 130
|
||
# Commit: retrain final model with new best params
|
||
python main.py --mode train --tf H1 --broker headway_cent --balance 15
|
||
```
|
||
|
||
The [Guardian](#telegram-remote-control) runs a continuous rolling Sharpe health check and fires Telegram alerts if performance degrades.
|
||
|
||
---
|
||
|
||
## Command Reference
|
||
|
||
```
|
||
python main.py --mode <MODE> [OPTIONS]
|
||
```
|
||
|
||
### Modes
|
||
|
||
| Mode | Description |
|
||
|------|-------------|
|
||
| `consolidate` | Merge `*USDCHF*.csv` files in `data/raw/` into per-TF USDCHF masters |
|
||
| `process` | Process raw CSV → features (Kalman, log returns, RSI, ATR, GMM cluster) |
|
||
| `optimize` | Run / resume Optuna hyperparameter search (OOS Complex Criterion scoring). Supports `--stage xgb` (fast XGB exploration) → `--stage trading` (full CPCV, warm-started) two-stage workflow |
|
||
| `train` | Train HMM + XGBoost; show IS/OOS breakdown |
|
||
| `sensitivity` | Z-Score sensitivity sweep (Bull/Bear cutoffs 1.5–3.0) on trained models |
|
||
| `compare` | Side-by-side OOS comparison across TFs ranked by Complex Criterion |
|
||
| `export` | Export XGBoost ensemble → ONNX for MQL5 EA |
|
||
| `report` | Generate 6-chart visual report → `reports/<TF>_<broker>/` |
|
||
| `wfa` | Walk-Forward Analysis — per-fold Complex Criterion scores and WFE ratio |
|
||
| `sync_validate` | Download live MT5 data + validate model health + cost audit |
|
||
| `demo` | Run live execution loop on MT5 demo account (no YES prompt) |
|
||
| `live` | Run live execution loop on MT5 live account (requires YES confirmation) |
|
||
| `audit` | Generate and Telegram-send daily MT5 deal report |
|
||
| `guardian` | Continuous rolling Sharpe health monitor + Telegram alerts |
|
||
| `listen` | Start Telegram remote control bot (with nightly 23:55 summary) |
|
||
| `montecarlo` | Monte Carlo simulation on trained models — return/drawdown outcome distribution |
|
||
| `extract_consensus` | Extract consensus best parameters across Optuna trials |
|
||
|
||
### All Options
|
||
|
||
| Option | Type | Default | Description |
|
||
|--------|------|---------|-------------|
|
||
| `--tf` | str | `H1` | Timeframe: `H1`, `M15`, `M5` (or comma-separated for `compare`/`guardian`) |
|
||
| `--broker` | str | `standard` | `headway_cent` or `standard` |
|
||
| `--balance` | float | 15 | Account size in **real USD** |
|
||
| `--trials` | int | 250 | Total Optuna trial target. Recommended: H1=400, M15=600, M5=1000. |
|
||
| `--stage` | str | `None` | Two-stage optimize: `xgb` = Stage 1 fast XGB exploration (no CPCV, ∼5× faster, saves `stage1_{tf}_{broker}.json`); `trading` = Stage 2 full CPCV warm-started from Stage 1. Omit to run standard joint optimization |
|
||
| `--period` | str | `3m` | Lookback for MT5 sync: `3m`, `6m`, `12m` |
|
||
| `--interval` | int | 3600 | Guardian check interval in seconds |
|
||
| `--profit_target` | float | 4.0 on M5 | Quick-profit close in USD per position. Pass `0` to disable on M5 |
|
||
| `--skip_stale_check` | flag | off | Bypass model-staleness gate on `live`/`demo` |
|
||
| `--train_days` | int | TF default | WFA IS window in calendar days (H1=365, M15=180, M5=90) |
|
||
| `--test_days` | int | TF default | WFA OOS step in calendar days (H1=90, M15=60, M5=30) |
|
||
| `--n_jobs` | int | 1 | Parallel Optuna workers (advanced) |
|
||
|
||
> **Note:** `--balance` is always in **real USD**. For a Headway Cent account with $15, pass `--balance 15` — the bridge handles the ×100 display conversion internally.
|
||
|
||
### Per-Timeframe Recommended Workflows
|
||
|
||
**H1 — Headway Cent (first time):**
|
||
```bash
|
||
# One-time setup
|
||
python main.py --mode consolidate
|
||
python main.py --mode process --tf H1
|
||
|
||
# Stage 1: Fast XGB exploration (no CPCV) — saves stage1_h1_headway_cent.json
|
||
python main.py --mode optimize --tf H1 --broker headway_cent --balance 15 --stage xgb --trials 60
|
||
|
||
# Stage 2: Full CPCV optimization (warm-started from Stage 1)
|
||
python main.py --mode optimize --tf H1 --broker headway_cent --balance 15 --stage trading --trials 130
|
||
|
||
# Commit: Train final model with best params
|
||
python main.py --mode train --tf H1 --broker headway_cent --balance 15
|
||
|
||
# Optional checks
|
||
python main.py --mode sensitivity --tf H1 --broker headway_cent --balance 15
|
||
python main.py --mode montecarlo --tf H1 --broker headway_cent --balance 15
|
||
python main.py --mode report --tf H1 --broker headway_cent --balance 15
|
||
|
||
# Validate + go live
|
||
python main.py --mode sync_validate --tf H1 --broker headway_cent --balance 15 --period 6m
|
||
python main.py --mode wfa --tf H1 --broker headway_cent --balance 15
|
||
python main.py --mode demo --tf H1 --broker headway_cent --balance 15
|
||
python main.py --mode live --tf H1 --broker headway_cent --balance 15
|
||
```
|
||
|
||
**M15 — Headway Cent:**
|
||
```bash
|
||
python main.py --mode process --tf M15
|
||
|
||
# Stage 1: Fast XGB exploration — saves stage1_m15_headway_cent.json
|
||
python main.py --mode optimize --tf M15 --broker headway_cent --balance 15 --stage xgb --trials 100
|
||
|
||
# Stage 2: Full CPCV optimization (warm-started from Stage 1)
|
||
python main.py --mode optimize --tf M15 --broker headway_cent --balance 15 --stage trading --trials 200
|
||
|
||
# Commit: Train final model with best params
|
||
python main.py --mode train --tf M15 --broker headway_cent --balance 15
|
||
|
||
# Optional checks
|
||
python main.py --mode sensitivity --tf M15 --broker headway_cent --balance 15
|
||
python main.py --mode montecarlo --tf M15 --broker headway_cent --balance 15
|
||
python main.py --mode report --tf M15 --broker headway_cent --balance 15
|
||
|
||
# Validate + go live
|
||
python main.py --mode sync_validate --tf M15 --broker headway_cent --balance 15 --period 6m
|
||
python main.py --mode wfa --tf M15 --broker headway_cent --balance 15
|
||
python main.py --mode demo --tf M15 --broker headway_cent --balance 15
|
||
python main.py --mode live --tf M15 --broker headway_cent --balance 15
|
||
```
|
||
|
||
**M5 — Headway Cent:**
|
||
```bash
|
||
python main.py --mode process --tf M5
|
||
|
||
# Stage 1: Fast XGB exploration — saves stage1_m5_headway_cent.json
|
||
python main.py --mode optimize --tf M5 --broker headway_cent --balance 15 --stage xgb --trials 150
|
||
|
||
# Stage 2: Full CPCV optimization (warm-started from Stage 1)
|
||
python main.py --mode optimize --tf M5 --broker headway_cent --balance 15 --stage trading --trials 300
|
||
|
||
# Commit: Train final model with best params
|
||
python main.py --mode train --tf M5 --broker headway_cent --balance 15
|
||
|
||
# Optional checks
|
||
python main.py --mode sensitivity --tf M5 --broker headway_cent --balance 15
|
||
python main.py --mode montecarlo --tf M5 --broker headway_cent --balance 15
|
||
python main.py --mode report --tf M5 --broker headway_cent --balance 15
|
||
|
||
# Validate + go live
|
||
python main.py --mode sync_validate --tf M5 --broker headway_cent --balance 15 --period 3m
|
||
python main.py --mode wfa --tf M5 --broker headway_cent --balance 15
|
||
python main.py --mode demo --tf M5 --broker headway_cent --balance 15
|
||
python main.py --mode live --tf M5 --broker headway_cent --balance 15
|
||
```
|
||
|
||
---
|
||
|
||
## Signal Logic — Regime-Confirmation
|
||
|
||
### Stateful Regime-Confirmation Engine
|
||
|
||
The live signal gate is the `SignalEngine` class (`src/signal_engine.py`). It is stateful — it tracks how many consecutive bars the current HMM regime has persisted, whether a trade is open, and what regime the trade was entered in.
|
||
|
||
> **Note on HMM state encoding:** XGBoost receives the HMM regime as one-hot encoded columns (`hmm_0`, `hmm_1`, `hmm_2` for n_states=3; `hmm_0`–`hmm_3` for n_states=4) rather than a single integer. This prevents the model from treating states as ordinal (0 < 1 < 2) and allows each regime to have an independent feature weight.
|
||
|
||
**Trend entry conditions (all must pass):**
|
||
|
||
1. **Regime has persisted** — at least 2 consecutive bars in the same regime (Bull or Bear)
|
||
2. **synth_vix_zscore** ≥ `MIN_TREND_ZSCORE` for this TF (volatility expansion confirmation)
|
||
3. **XGBoost probability** ≥ TF threshold (H1 only; M15/M5 bypass the XGB gate)
|
||
4. **ATR band position** within valid range (trend: < 0.80 for BUY, > 0.20 for SELL)
|
||
5. **HMM self-transition probability** ≥ TF persistence floor
|
||
|
||
| TF | XGBoost threshold | Persistence floor | `MIN_TREND_ZSCORE` | `MIN_MR_ZSCORE` |
|
||
|----|-------------------|-------------------|--------------------|-----------------|
|
||
| H1 | 0.575 | 0.57 | 0.5 | 1.0 |
|
||
| M15 | bypass (0.0) | 0.55 | 1.0 | 1.5 |
|
||
| M5 | bypass (0.0) | 0.45 | 1.5 | 2.0 |
|
||
|
||
#### Mean Reversion entries (Chop state ≥ 2)
|
||
|
||
MR entries require ATR band position at an extreme: ATR band < 0.10 → MR_BUY; ATR band > 0.90 → MR_SELL. MR positions use 75% of the standard lot size. `synth_vix_zscore ≥ MIN_MR_ZSCORE` is also required.
|
||
|
||
#### Exit conditions
|
||
|
||
The engine exits an open trade when **any** of the following fires:
|
||
|
||
| Exit reason | Condition |
|
||
|-------------|-----------|
|
||
| `regime_reversal` | HMM state changes from the entry regime |
|
||
| `persistence_collapse` | P(stay in regime) drops below the TF persistence floor |
|
||
| `profit_erosion` | Current P&L < 40% of peak P&L for this trade |
|
||
| `max_hold` | Trade open for H1: 24 bars / M15: 32 bars / M5: 48 bars |
|
||
|
||
#### Trade gate requirements
|
||
|
||
A trade fires when **all** of the following pass:
|
||
|
||
| Gate | Requirement |
|
||
|------|-------------|
|
||
| Regime confirmed | ≥ 2 consecutive bars in regime |
|
||
| synth_vix_zscore | ≥ `MIN_TREND_ZSCORE` for this TF |
|
||
| XGBoost prob | ≥ TF threshold (H1 only; M15/M5 bypass) |
|
||
| ATR band | trend: position < 0.80 (BUY) or > 0.20 (SELL) |
|
||
| Persistence | P(stay in state) ≥ TF floor |
|
||
| Direction | BUY only in Bull (0); SELL only in Bear (1); Chop → MR only |
|
||
| ATR band (MR) | MR_BUY: ATR band < 0.10; MR_SELL: ATR band > 0.90 |
|
||
| Session limit | Under daily trade cap for this TF |
|
||
| Margin check | Sufficient free margin for the lot size |
|
||
| Spread viability | TP1 ≥ spread × ratio (1.5× cent / 3.0× standard) |
|
||
| DailyEquityGate | Floating loss < 5% AND day gain < profit-lock threshold |
|
||
| Global Guard | Fewer than 4 GRX positions open across all TFs |
|
||
|
||
### Mean Reversion in Chop — Live Safety Gates
|
||
|
||
MR signals are gated by the engine's persistence check (P(stay in Chop state) ≥ TF floor), ATR band extremity (< 0.10 / > 0.90), and `synth_vix_zscore ≥ MIN_MR_ZSCORE`, matching the backtester exactly.
|
||
|
||
### Logic Audit
|
||
|
||
Every bar that does not fire a trade, the bridge logs a structured reason:
|
||
|
||
`[LOGIC AUDIT] BULL | state=0 prob=0.510 bars=1 P(stay)=0.82 vix_z=0.87 atr_band=0.44`
|
||
|
||
### Spread Viability Guard
|
||
|
||
| Broker | Applied on | Minimum ratio |
|
||
|--------|------------|---------------|
|
||
| `headway_cent` | M5 only | TP1 ≥ 1.5× spread |
|
||
| `standard` | All timeframes | TP1 ≥ 3.0× spread |
|
||
|
||
### Two-Sided DailyEquityGate
|
||
|
||
**Loss gate:** Floating equity drops ≥ 5% → gate locks, all GRX positions closed. No new signals for the rest of the day.
|
||
|
||
**Profit lock:** Day gain reaches TF-specific threshold → no new entries.
|
||
|
||
| TF | Profit Lock | Loss Gate |
|
||
|----|-------------|-----------|
|
||
| M5 | **20%** day gain | 5% loss |
|
||
| M15 | **10%** day gain | 5% loss |
|
||
| H1 | **10%** day gain | 5% loss |
|
||
|
||
Both gates reset at UTC midnight.
|
||
|
||
### ATR Trailing Exits
|
||
|
||
| Phase | Trigger | Action |
|
||
|-------|---------|--------|
|
||
| Phase 1 (break-even) | Floating P&L ≥ activation amount | SL → entry + 2×spread |
|
||
| Phase 2 (trail) | Activated | Trailing SL at ATR × trail multiplier |
|
||
|
||
**Activation amounts and trail multipliers by TF:**
|
||
|
||
| TF | Activation P&L | Trail Multiplier |
|
||
|----|----------------|-----------------|
|
||
| H1 | **$1.50** | 2.5× ATR |
|
||
| M15 | **$1.50** | 1.5× ATR |
|
||
| M5 | **$1.00** | 1.5× ATR |
|
||
|
||
### Staged Take-Profits
|
||
|
||
| Regime | TF | TP1 | TP2 (Runner) | SL ATR mult |
|
||
|--------|----|-----|--------------|-------------|
|
||
| Bull / Bear | M5 | 0.8× ATR | 1.5× ATR | 1.5× |
|
||
| Bull / Bear | M15 | 1.2× ATR | 2.5× ATR | 2.0× |
|
||
| Bull / Bear | H1 | 1.5× ATR | 3.0× ATR | 2.0× |
|
||
| Chop (MR) | M5 | 0.5× ATR | — | 1.05× |
|
||
| Chop (MR) | M15 | 0.8× ATR | — | 1.4× |
|
||
| Chop (MR) | H1 | 1.0× ATR | — | 1.4× |
|
||
|
||
**Full profit protection chain (all TFs):**
|
||
|
||
1. **Profit guard** — SL → entry + 2×spread when price reaches 70% of TP1 distance
|
||
2. **Break-even** — runner SL → entry when TP1 fills
|
||
3. **ATR trail** — activates when floating P&L ≥ TF activation amount
|
||
|
||
**M5-only Hybrid Scalp Protection** (runs every 5 s between bars):
|
||
|
||
4. **Fixed scalp target** — position closed when floating P&L ≥ $4.00
|
||
5. **Trailing guard** — if peak P&L ≥ $2.00, close when P&L falls to ≤ 50% of peak
|
||
6. **Chop-exit** — all positions closed at market if HMM shifts to Chop mid-trade
|
||
7. **M5 recycle** — after scalp target close, re-entry is allowed on the same bar if regime unchanged and daily cap not hit
|
||
|
||
## Automatic Data Updates
|
||
|
||
### Pre-Optimisation Sync (`ensure_data_updated`)
|
||
|
||
`src/mt5_sync.py` exposes `ensure_data_updated(tf, symbol)`, which is called automatically at the **start of every `--mode optimize` run** for each requested timeframe. It:
|
||
|
||
1. Reads the last timestamp from the corresponding raw CSV file (`data/raw/XAU_*_data.csv`)
|
||
2. Calculates how many bars are missing up to the current time
|
||
3. Fetches only the missing bars from MT5 using `copy_rates_range`
|
||
4. Appends new rows in semicolon-delimited format, matching the existing CSV structure
|
||
5. Skips silently if MT5 is unavailable — optimization continues with existing data
|
||
|
||
This ensures the model always trains on the most recent bars without requiring a manual data export before each optimization cycle.
|
||
|
||
```bash
|
||
# Sync happens automatically — just run:
|
||
python main.py --mode optimize --tf H1 --broker headway_cent --balance 15 --trials 400
|
||
```
|
||
|
||
### Weekly Data Updater (`WeeklyDataUpdater`)
|
||
|
||
`src/data_updater.py` runs automatically inside the live trading loop every **Sunday**. It:
|
||
|
||
1. Fetches the last ~2 weeks of XAUUSD bars from MT5 for each active TF (H1: 336 bars, M15: 1344, M5: 4032)
|
||
2. Deduplicates by timestamp and appends only new rows to the raw CSV training files
|
||
3. Writes a `data/raw/.last_auto_update` marker
|
||
|
||
No manual action is required. The updater is a no-op on non-Sunday days and won't re-run within the same week.
|
||
|
||
### Guardian daily check
|
||
|
||
When \--mode guardian\ is running it performs a **daily model health check**. If rolling Sharpe degrades or models appear stale, the guardian fires an alert via Telegram.
|
||
|
||
---
|
||
|
||
## Sensitivity Analysis
|
||
|
||
The sensitivity analysis sweeps Bull/Bear Z-Score cutoffs across a range on your **already-trained model** and shows how trades, Sharpe, drawdown, and profit factor change at each level. Note: the live signal path now uses `SignalEngine` — sensitivity results are informational only.
|
||
|
||
```bash
|
||
python main.py --mode sensitivity --tf H1 --broker headway_cent --balance 15
|
||
```
|
||
|
||
**What it tests:** Bull (`Z_CUTOFF_BULL`) and Bear (`Z_CUTOFF_BEAR`) cutoffs from **1.5σ to 3.0σ** in steps of 0.25σ. MR (Chop) cutoffs are held constant throughout.
|
||
|
||
**Output — printed table:**
|
||
|
||
```
|
||
Z OOS Trades Win% Sharpe MaxDD% PF RF Return% Status
|
||
1.50 312 52.3 0.81 14.2 1.43 1.92 18.4
|
||
1.75 241 55.1 0.97 11.8 1.61 2.34 22.1 ← current
|
||
2.00 188 58.2 1.12 10.1 1.79 2.87 26.3 ← BEST
|
||
2.25 134 60.4 1.04 9.8 1.74 2.61 24.1
|
||
...
|
||
```
|
||
|
||
**Output files** (written to `reports/`):
|
||
|
||
```
|
||
reports/sensitivity_H1_headway_cent.csv
|
||
reports/sensitivity_H1_headway_cent.json
|
||
```
|
||
|
||
The JSON includes `current_z`, `best_z`, and `best_sharpe` for automated comparison.
|
||
|
||
**When to run sensitivity analysis:**
|
||
- After training a new model — confirm the hardcoded TF Z cutoff is near-optimal
|
||
- When validation Sharpe is borderline — determine whether a lower cutoff would pass
|
||
|
||
---
|
||
|
||
## Interactive Research Notebook
|
||
|
||
`notebooks/GoldRegimeX_Explorer.ipynb` is a fully interactive Jupyter notebook for exploring, tweaking, and diagnosing the system without touching any source files. Run it from the repo root:
|
||
|
||
```bash
|
||
jupyter notebook notebooks/GoldRegimeX_Explorer.ipynb
|
||
```
|
||
|
||
### Sections
|
||
|
||
| Section | Feature | Typical runtime |
|
||
|---------|---------|-----------------|
|
||
| **1 — Data & Model Loader** | Select TF, broker, balance, and **WFO mode** (`standard` / `fast`). Loads processed data, best Optuna params, and trained HMM + XGB models. | ~5 s |
|
||
| **2 — Equity Curve Explorer** | Interactive equity curve with IS/OOS split line, drawdown panel, and full metric table. Re-runs instantly on balance/broker change. | instant |
|
||
| **3 — WFO Window Analysis** | Runs `_run_wfo` using the loaded best params. Plots per-window OOS score bars. Drill into any window's equity curve with the **Window #** slider. Shows **Walk-Forward Efficiency (WFE) interpretation** label. | ~2–3 min |
|
||
| **4 — Feature & Regime Explorer** | Price + HMM regime overlay, regime distribution pie, GMM volatility cluster histogram, feature distributions per regime, XGB feature importance. | instant |
|
||
| **5 — Parameter Sensitivity** | Sliders for `n_states`, `max_depth`, `learning_rate`, `reg_alpha`, **`reg_lambda`**, and **`min_child_weight`**. **Run Comparison** re-trains and shows a side-by-side metric bar chart. **Run WFO Score Comparison** runs proper rolling WFO (not just a full-dataset backtest) on both baseline and modified configs. | ~30–60 s / ~4–8 min WFO |
|
||
| **6 — CV Path Inspector** | Runs either **WFO IS CV** (per-window inner fold scores) or **CPCV** (C(6,2)=15 paths). Displays a per-path boxplot, per-path score bar chart, and a **consistency score** = `n_profitable_paths / total × 100%`. | ~2–5 min |
|
||
|
||
### WFO Mode (`standard` vs `fast`)
|
||
|
||
Selected via the **WFO Mode** dropdown in Section 1. Stored in `_CACHE` and used by Sections 3, 5, and 6.
|
||
|
||
| Mode | IS window | OOS window | Use case |
|
||
|------|-----------|------------|----------|
|
||
| `standard` | H1: 1 yr / M15: 1 yr / M5: 1 yr | H1: 90d / M15: 90d / M5: 90d | Default; thorough evaluation |
|
||
| `fast` | H1: 6 mo / M15: 6 mo / M5: 6 mo | H1: 45d / M15: 45d / M5: 30d | Quicker feedback; suits intraday regime shifts |
|
||
|
||
### Walk-Forward Efficiency (WFE) Labels
|
||
|
||
Section 3 automatically labels the WFE ratio after each run:
|
||
|
||
| WFE | Label |
|
||
|-----|-------|
|
||
| ≥ 0.80 | ✅ Excellent (OOS ≥ 80% of IS CV performance) |
|
||
| ≥ 0.50 | 🟡 Acceptable (OOS ≥ 50% of IS CV performance) |
|
||
| ≥ 0.20 | 🟠 Marginal — consider broader regularization |
|
||
| < 0.20 | 🔴 Poor — strong overfitting signal |
|
||
|
||
---
|
||
|
||
## Risk Management
|
||
|
||
### Position Sizing — 1% Risk Rule
|
||
|
||
```
|
||
lot_size = (1% × account_balance_USD) / (ATR(14) × SL_multiplier)
|
||
```
|
||
|
||
Minimum lot is always **0.01**. All lots rounded to 2 decimal places.
|
||
|
||
### Daily Exposure Limits
|
||
|
||
| Account Balance | TF | Positions per Signal | Max Trades/Day |
|
||
|----------------|----|----------------------|----------------|
|
||
| ≤ $50 | H1 / M15 | **2** (cent) / **1** (standard) | 2 |
|
||
| ≤ $50 | M5 | **2** (cent) / **1** (standard) | 4 |
|
||
| > $50 | Any | **3** | 2–3 depending on regime |
|
||
|
||
**Global Guard:** skip any signal if ≥ 4 GRX positions are open across all TFs.
|
||
|
||
### Broker Cost Configs
|
||
|
||
| Broker | Spread fraction | Commission fraction | Total round-trip |
|
||
|--------|----------------|--------------------|--------------------|
|
||
| `headway_cent` | 0.02% | 0.02% | **0.04%** |
|
||
| `standard` | 0.02% | 0.01% | **0.03%** |
|
||
|
||
---
|
||
|
||
## Timeframe Configurations
|
||
|
||
| Parameter | M5 | M15 | H1 |
|
||
|-----------|-----|-----|-----|
|
||
| Kalman `obs_cov` default | 0.05 | 4.0 | 1.0 |
|
||
| Bars/day (annualisation) | 288 | 96 | 24 |
|
||
| HMM `n_states` search space | 3–4 | 3–4 | 3–4 |
|
||
| HMM persistence gate (training) | ≥ 0.65 | ≥ 0.65 | ≥ 0.65 |
|
||
| IS/OOS split | 65% / 35% | 65% / 35% | **70% / 30%** |
|
||
| Min OOS trades (hard floor) | **120** | **60** | 20 |
|
||
| Min OOS trades (penalty threshold) | 350 | 140 | 25 |
|
||
| SL ATR multiplier | 1.5× | 2.0× | 2.0× |
|
||
| TP1 multiplier (trend) | 0.8× SL | 1.0× SL | 1.5× SL |
|
||
| TP2 multiplier (runner) | 1.5× SL | 2.0× SL | 3.0× SL |
|
||
| Hybrid Scalp Protection | **On (5 s)** | Off | Off |
|
||
| Fixed scalp target | **$4 USD** | Off | Off |
|
||
| Trailing guard activation | **$2 peak** | Off | Off |
|
||
| DailyEquityGate loss | 5% | 5% | 5% |
|
||
| DailyEquityGate profit lock | **20%** | **10%** | **10%** |
|
||
| Model staleness gate | 14 days | 30 days | 30 days |
|
||
| M5 optimisation freshness gate | **120 h** | — | — |
|
||
| Recommended `--trials` | 1000 | 600 | 400 |
|
||
|
||
> **`n_states` restriction (all TFs):** All timeframes search `{3, 4}`. n_states=2 is banned on all TFs — with only Bull/Bear states every bar is signal-eligible, creating excessive counter-trend noise. n_states=3 gives Bull/Bear/Chop; n_states=4 adds a `Chop_High` state for volatile chop regimes. The optimal count is selected by Optuna per study.
|
||
|
||
---
|
||
|
||
## Performance Metrics & Scoring
|
||
|
||
### Complex Criterion Score
|
||
|
||
```
|
||
Score = clamp(RF, -5, 5) × 0.35
|
||
+ clamp(Sharpe, -3, 3) × 0.35
|
||
+ clamp(PF − 1, -2, 2) × 0.20 ← normalised: breakeven = 0
|
||
+ clamp(avg_payoff / $0.035, 0, 2) × 0.10
|
||
+ [M5/M15 only] return_consistency × 0.30 (cap +0.30)
|
||
```
|
||
|
||
**Score range:** approximately −5 to +5. A score > 0.5 is considered a worthwhile configuration.
|
||
|
||
| Component | Weight | Clamp | Measures |
|
||
|-----------|--------|-------|---------|
|
||
| Recovery Factor (RF) | **0.35** | [−5, 5] | Capital preservation — net profit / floating max DD |
|
||
| Sharpe Ratio | **0.35** | [−3, 3] | Return smoothness — prevents outlier-driven strategies |
|
||
| Profit Factor − 1 (normalised) | **0.20** | [−2, 2] | Trade quality — breakeven = 0, loss-only = −1 |
|
||
| Edge / Spread ratio | **0.10** | [0, 2] | avg payoff vs $0.035 spread proxy |
|
||
| Return Consistency | +0.30 bonus cap | — | Weekly P&L stability (M5/M15 only) |
|
||
|
||
### WFO Variance Penalty
|
||
|
||
The final WFO score aggregates per-window scores with an increased variance penalty:
|
||
|
||
```
|
||
wfo_score = median(window_scores) − 0.20 × std(window_scores)
|
||
```
|
||
|
||
The 0.20 multiplier (up from 0.15) aligns with the wider score range (−5 to +5) to meaningfully penalise inconsistent strategies.
|
||
|
||
### All Reported Metrics
|
||
|
||
| Metric | Description |
|
||
|--------|-------------|
|
||
| **Score** | Complex Criterion (see above) |
|
||
| **Sharpe Ratio** | Annualised return / annualised volatility |
|
||
| **Recovery Factor** | Net profit / floating max drawdown (capped 20× for display) |
|
||
| **Profit Factor** | Gross wins / gross losses (capped 10× for display) |
|
||
| **Expected Payoff** | Mean per-trade return × account_size |
|
||
| **Max Drawdown** | Peak-to-trough closed-bar drawdown |
|
||
| **Floating Max DD** | Intra-bar adverse excursion using bar High/Low |
|
||
| **Win Rate** | Fraction of trades that closed profitable |
|
||
| **Trade Count** | Total closed trades in the window |
|
||
| **Avg Efficiency** | Mean ATR / spread on active-trade bars |
|
||
| **Cost Efficiency** | `1 - (total_costs / gross_profit)` |
|
||
| **Total Payout** | `total_return × account_size` in broker currency |
|
||
| **Return Consistency** | Weekly P&L stability: `1 - std / (std + |mean|)` |
|
||
| **mr_trades** | Count of MR trades (Chop-state signals) |
|
||
| **mr_win_rate** | Win rate of MR trades |
|
||
| **mr_pnl** | Cumulative log-return from MR trades only |
|
||
|
||
### IS/OOS Split Ratios
|
||
|
||
| TF | IS | OOS |
|
||
|----|----|-----|
|
||
| H1 | 70% | 30% |
|
||
| M15 | 65% | 35% |
|
||
| M5 | 65% | 35% |
|
||
|
||
---
|
||
|
||
## Optimizer Anti-Overfitting Rules
|
||
|
||
| Rule | Detail |
|
||
|------|--------|
|
||
| OOS-only scoring | All scoring uses OOS data only — IS is never evaluated in the objective |
|
||
| Complex Criterion | `RF_c×0.35 + Sharpe_c×0.35 + (PF−1)×0.20 + Edge_c×0.10` — all terms clamped symmetrically |
|
||
| Hard trade floor | OOS trades < hard minimum → score **−50.0** (trial discarded) |
|
||
| Progressive trade penalty | OOS trades below soft threshold → score × 0.1 |
|
||
| Payoff floor | OOS average edge < $0.035 → score × 0.1 |
|
||
| Max drawdown gate | OOS floating DD > 20% → score **−50.0** |
|
||
| H1 DD guard | OOS floating DD > 5% → score × 0.5 |
|
||
| IS/OOS generalisation | If IS Sharpe > 0.1 and OOS/IS Sharpe < 0.35 → score **−50.0** |
|
||
| HMM persistence gate | Any self-transition < 0.65 → score **−100.0** |
|
||
| M5 activity bonus | OOS trades > 300 → score × 1.2; OOS trades < 150 → score × 0.5 |
|
||
| n_states restriction | All TFs: `{3, 4}` |
|
||
| No threshold search | SignalEngine thresholds are hardcoded TF constants — not Optuna parameters |
|
||
| Per-broker study isolation | All TF studies for a broker are colocated in `study_{broker}.db` (e.g. `study_headway_cent.db`). Use `optuna-dashboard sqlite:///models/study_headway_cent.db` to view all active studies. |
|
||
|
||
---
|
||
|
||
## Canonical Regime Contract
|
||
|
||
All timeframes (H1, M15, M5) use **exactly 3 HMM states**. This is a hard architectural invariant, not a tunable parameter.
|
||
|
||
| ID | Label | Trading Allowed |
|
||
|----|-------|----------------|
|
||
| 0 | `TREND` | Yes |
|
||
| 1 | `MEAN_REVERSION` | No (strict no-trade) |
|
||
| 2 | `VOLATILITY_SHOCK` | Yes |
|
||
|
||
### Enforcement layers
|
||
|
||
1. **`optimizer.py`** — `n_states` is never suggested by Optuna. It is pinned to `3` unconditionally for H1, M15, and M5. `SEARCH_SPACES[tf]["n_states"]` records `(3, 3, "int")`. `_enforce_three_states(params, context)` raises `ValueError` at every `fit_hmm` call site.
|
||
2. **`main.py` / `optimizer.py` `resolve_n_states`** — any call with `n_states != 3` raises `ValueError` immediately with a descriptive message.
|
||
3. **`engine_hmm.py`** — `_assert_canonical_states(states, context)` is called after `fit_hmm` and `predict_states`. If any state id outside `{0,1,2}` is found a `RuntimeError` is raised. `_log_transition_matrix` always renders a 3×3 matrix with semantic labels (never numeric fallback columns).
|
||
4. **`engine_xgb.py`** — `_validate_state_feature_schema(df, tf)` is called inside `prepare_features`. For H1 it requires `hmm_state` and forbids `state_3`. For M15/M5 it requires `state_0`, `state_1`, `state_2` and forbids `state_3`.
|
||
|
||
### Rejected inputs
|
||
|
||
- `n_states=4` or `n_states=5`: `ValueError` at `resolve_n_states` / `_enforce_three_states`
|
||
- Any HMM producing state id 3+: `RuntimeError` at `_assert_canonical_states`
|
||
- Feature matrix with `state_3` column: `ValueError` at `_validate_state_feature_schema`
|
||
|
||
### Legacy note
|
||
|
||
Legacy 4/5-state trials may exist in a resumed Optuna study from before this patch. They do not affect new trials (all new fits use `n_states=3`). The `_assert_canonical_states` guard ensures that if a legacy model file is loaded and somehow predicts state 3, it is caught immediately at inference time.
|
||
|
||
---
|
||
|
||
## H1 Profitability Safeguards
|
||
|
||
H1 is scored with additional hard-floor penalties and a tunable entry-gate to reduce persistent negative Sharpe trials while preserving CPCV robustness.
|
||
|
||
### Hard-Floor Penalties
|
||
|
||
Three floors are checked per CPCV path aggregation. A breach applies `penalty = 10.0 + (floor − metric) × 10.0`:
|
||
|
||
| Param | Default | Meaning |
|
||
|-------|---------|---------|
|
||
| `h1_min_median_sharpe` | `0.10` | Median CPCV Sharpe must exceed this |
|
||
| `h1_min_median_expectancy` | `0.0` | Median per-trade expectancy ($) must exceed this |
|
||
| `h1_min_median_pf` | `1.02` | Median profit factor must exceed this |
|
||
|
||
Reason codes such as `floor_sharpe:0.032<0.100` appear in the structured trial log:
|
||
|
||
```
|
||
trial=7 tf=H1 base=0.1812 p_var=0.0000 p_inst=0.0000 p_dd=0.0000 p_mr=0.0000 p_floor=12.3200 p_turn=0.0000 final=-12.1388 reasons=floor_sharpe:0.032<0.100|floor_pf:0.98<1.02
|
||
```
|
||
|
||
### Turnover Penalty
|
||
|
||
Excessive H1 trade churn (measured as trades per 100 bars across CPCV test windows) is penalised:
|
||
|
||
| Param | Default | Meaning |
|
||
|-------|---------|---------|
|
||
| `h1_max_trades_per_100` | `4.0` | Trades per 100 bars before penalty fires |
|
||
|
||
Penalty scales linearly: `(trades_per_100 − max) × 2.0`. Not applied on M15/M5.
|
||
|
||
### Tunable Entry Gate
|
||
|
||
H1 entry uses a tunable probability threshold for the XGBoost regime classifier. During optimization, `h1_entry_prob` is an Optuna parameter (range `[0.57, 0.66]`). In live/demo mode it can be overridden at the CLI:
|
||
|
||
```bash
|
||
# Run optimization with custom floor overrides
|
||
python main.py --mode optimize --tf H1 --broker headway_cent --trials 200 \
|
||
--h1_min_median_sharpe 0.12 \
|
||
--h1_min_median_pf 1.05 \
|
||
--h1_max_trades_per_100 3.5
|
||
|
||
# Run demo with fixed entry gate override
|
||
python main.py --mode demo --tf H1 --broker headway_cent \
|
||
--h1_entry_prob 0.62
|
||
```
|
||
|
||
### CLI Reference
|
||
|
||
| Flag | Type | Default | Description |
|
||
|------|------|---------|-------------|
|
||
| `--h1_entry_prob` | float | `0.575` | XGBoost entry probability gate for H1. Range `[0.50, 0.90]`. |
|
||
| `--h1_min_median_sharpe` | float | `0.10` | H1 Sharpe floor. Range `[-1.0, 2.0]`. |
|
||
| `--h1_min_median_pf` | float | `1.02` | H1 profit-factor floor. Range `[0.5, 3.0]`. |
|
||
| `--h1_max_trades_per_100` | float | `4.0` | H1 max trades/100 bars. Range `[0.5, 20.0]`. |
|
||
|
||
All flags are validated at startup — out-of-range values exit immediately with a descriptive message.
|
||
|
||
### Asymmetric Buy/Sell Thresholds (advanced)
|
||
|
||
For programmatic use (not CLI), pass `h1_entry_prob_buy` and `h1_entry_prob_sell` separately via `evaluator_config` in `vectorized_backtest`. Both are clamped to `[0.50, 0.90]`.
|
||
|
||
---
|
||
| CPCV per trial | Every Optuna trial evaluates all C(4,2)=6 combinatorial purged paths — not a single IS/OOS split; `CPCV_MAX_FLOAT_DD=0.20` prunes paths with > 20% floating DD |
|
||
| OOS scaler consistency | OOS features are always scaled with the IS-fitted scaler (no data leakage) |
|
||
| State alignment | `states_oos` and `states_is_cv` are re-aligned to `df_aligned.index` after NaN drops |
|
||
| L2 regularisation | `reg_lambda` is searched for all TFs — XGBoost default of 1.0 is no longer forced |
|
||
| Pruner per TF | M5: `HyperbandPruner` (early-stops expensive trials); H1/M15: `MedianPruner(startup=10, warmup=5)` |
|
||
|
||
```python
|
||
# Hard trade floors (trials below these return -50.0 immediately):
|
||
MIN_OOS_TRADES_HARD = {"M5": 120, "M15": 60, "H1": 20}
|
||
|
||
# Soft trade floors (trials below these have score × 0.1):
|
||
TF_MIN_OOS_TRADES = {"M5": 350, "M15": 140, "H1": 25}
|
||
|
||
# CPCV trial budgets (Stage 2 / joint optimization):
|
||
CPCV_TRIALS = {"H1": 80, "M15": 120, "M5": 200}
|
||
# Stage-1 trial budgets (fast hold-out, no CPCV):
|
||
STAGE1_TRIALS = {"H1": 60, "M15": 100, "M5": 150}
|
||
```
|
||
|
||
### Optuna Search Space per Timeframe
|
||
|
||
| Parameter | H1 | M15 | M5 |
|
||
|-----------|-----|------|-----|
|
||
| `learning_rate` | 0.005 – 0.15 | 0.005 – 0.20 | 0.01 – 0.15 |
|
||
| `n_estimators` | 50 – 400 (step 50) | 100 – 500 (step 50) | 200 – 600 (step 50) |
|
||
| `max_depth` | 3 – 7 | 3 – 7 | 2 – 4 |
|
||
| `min_child_weight` | 5 – 100 | 3 – 30 | 5 – 25 |
|
||
| `reg_alpha` (L1) | 1e-6 – 0.5 | 1e-6 – 0.1 | 1e-6 – 0.1 |
|
||
| `reg_lambda` (L2) | **0.01 – 2.0** | **1e-6 – 0.1** | **1e-6 – 0.1** |
|
||
| `subsample` | 0.5 – 0.9 | 0.5 – 0.9 | 0.55 – 0.85 |
|
||
| `colsample_bytree` | 0.4 – 0.9 | 0.5 – 1.0 | 0.4 – 0.8 |
|
||
|
||
> ⚠️ **Delete the study DB** whenever a fundamental parameter changes (e.g. after adding a new feature, changing n_states search space, altering the score function, enabling OHE for `hmm_state`, or adding session gating). Old trials bias the surrogate model.
|
||
>
|
||
> ```bash
|
||
> del models\study_headway_cent.db
|
||
> del models\study_headway_cent_h1.db # legacy per-TF DB if it exists
|
||
> del models\study_standard.db
|
||
> ```
|
||
|
||
---
|
||
|
||
## Telegram Remote Control
|
||
|
||
Start the listener alongside your trading session:
|
||
|
||
```bash
|
||
python main.py --mode listen --broker headway_cent --balance 15
|
||
```
|
||
|
||
| Button | Action |
|
||
|--------|--------|
|
||
| 🚀 START TRADING | Launches `--mode live` using `.env` TF/broker/balance defaults |
|
||
| 🛑 STOP TRADING | Terminates the running live process |
|
||
| 📉 START OPTIMIZE (M5) | Starts / resumes M5 Optuna study |
|
||
| 📊 BOT STATUS | Last 24 h P&L, win rate, floating positions |
|
||
|
||
A nightly summary is sent at **23:55 UTC** while the listener runs.
|
||
|
||
### Guardian — Continuous health monitor
|
||
|
||
```bash
|
||
python main.py --mode guardian --tf M5,M15,H1 --period 3m \
|
||
--interval 3600 --broker headway_cent --balance 15
|
||
```
|
||
|
||
Every hour: validates rolling Sharpe for each TF — fires Telegram alert if below 0.6.
|
||
|
||
### Audit — On-demand deal report
|
||
|
||
```bash
|
||
python main.py --mode audit --broker headway_cent --balance 15
|
||
```
|
||
|
||
### Parallel Optimisation
|
||
|
||
```bash
|
||
# Open multiple terminals — each one runs an independent Optuna worker:
|
||
Terminal 1: python main.py --mode optimize --tf M5 --broker headway_cent --trials 1000
|
||
Terminal 2: python main.py --mode optimize --tf M5 --broker headway_cent --trials 1000
|
||
```
|
||
|
||
Workers share the same SQLite study safely via locking.
|
||
|
||
---
|
||
|
||
## Multi-TF Live Trading
|
||
|
||
Each timeframe runs as an **independent Python process** with its own magic number:
|
||
|
||
| TF | Magic Number | Comment format |
|
||
|----|-------------|----------------|
|
||
| H1 | `123456` | `GRX_H1_TREND_BUY_s0_tp1` |
|
||
| M15 | `123457` | `GRX_M15_TREND_SELL_s1_tp2` |
|
||
| M5 | `123458` | `GRX_M5_MR_BUY_s2_tp1` |
|
||
|
||
### Global Exposure Guard
|
||
|
||
Before any signal, the bridge counts all open GRX positions across all TFs. If **≥ 4 positions** are open, the signal is skipped.
|
||
|
||
### Starting multiple TFs
|
||
|
||
```bash
|
||
# Terminal 1
|
||
python main.py --mode live --tf H1 --broker headway_cent --balance 15
|
||
|
||
# Terminal 2
|
||
python main.py --mode live --tf M15 --broker headway_cent --balance 15
|
||
|
||
# Terminal 3
|
||
python main.py --mode live --tf M5 --broker headway_cent --balance 15
|
||
```
|
||
|
||
At UTC midnight each bridge sends a daily P&L audit to Telegram.
|
||
|
||
---
|
||
|
||
## MQL5 EA (Alternative Execution)
|
||
|
||
`mql5/GoldRegimeX.mq5` is a self-contained MT5 Expert Advisor that loads the ONNX model directly inside MT5 and replicates the same regime → signal → risk logic in MQL5.
|
||
|
||
**Do not run the EA and the Python bridge for the same TF simultaneously.**
|
||
|
||
To use the EA:
|
||
1. `python main.py --mode export --tf H1 --broker headway_cent`
|
||
2. Copy `mql5/GoldRegimeX.mq5` and the `.onnx` file to `MQL5/Experts/`
|
||
3. Compile in MetaEditor (F7) and attach to the XAUUSD chart
|
||
|
||
---
|
||
|
||
## Combinatorial Purged CV (CPCV)
|
||
|
||
Both the Optuna optimizer (`--mode optimize`) and the post-training diagnostic (`--mode wfa`) evaluate XGBoost performance using **Combinatorial Purged Cross-Validation (CPCV)** — not rolling Walk-Forward windows. CPCV eliminates serial-correlation leakage by inserting a purge gap at every train/test boundary and exhausts all combinatorial path orderings rather than relying on a single rolling split.
|
||
|
||
> **Note:** `_run_wfo`, `WFO_PARAMS`, and `WFO_PARAMS_FAST` are retained in `src/optimizer.py` for backward compatibility with the research notebook only. They are not used by the optimizer or `--mode wfa`.
|
||
|
||
### How it works
|
||
|
||
```
|
||
Full dataset (e.g., 10 years of H1 bars)
|
||
│
|
||
Split into N_BLOCKS = 4 equal chronological blocks
|
||
│
|
||
Generate all C(4,2) = 6 train/test path combinations
|
||
│
|
||
├── Path 1: Train=[B1,B2] purge_gap Test=[B3,B4]
|
||
├── Path 2: Train=[B1,B3] purge_gap Test=[B2,B4]
|
||
├── Path 3: Train=[B1,B4] purge_gap Test=[B2,B3]
|
||
├── Path 4: Train=[B2,B3] purge_gap Test=[B1,B4]
|
||
├── Path 5: Train=[B2,B4] purge_gap Test=[B1,B3]
|
||
└── Path 6: Train=[B3,B4] purge_gap Test=[B1,B2]
|
||
│
|
||
For each path:
|
||
1. HMM fitted strictly on purged training blocks (no lookahead)
|
||
2. HMM applied to full dataset → predict_states
|
||
3. StandardScaler fitted on training rows → applied to full dataset
|
||
4. XGBoost ensemble trained on training path (train_ratio=1.0)
|
||
5. Vectorized backtest on test path blocks only
|
||
6. Path score = Calmar-dominant composite (Calmar×0.45 + Sharpe×0.35 + PF×0.15 + Edge×0.05)
|
||
7. Paths pruned if floating_max_drawdown > 20% OR n_trades < hard floor
|
||
│
|
||
cpcv_score = median(path_scores) − 0.20 × std(path_sharpes)
|
||
```
|
||
|
||
### CPCV Parameters
|
||
|
||
| Parameter | H1 | M15 | M5 |
|
||
|-----------|-----|------|-----|
|
||
| `CPCV_N_BLOCKS` | 4 | 4 | 4 |
|
||
| `CPCV_K_TEST` | 2 | 2 | 2 |
|
||
| Paths per trial `C(4,2)` | 6 | 6 | 6 |
|
||
| `CPCV_PURGE_BARS` (embargo) | 24 bars | 96 bars | 288 bars |
|
||
| `CPCV_MAX_FLOAT_DD` | 20% | 20% | 20% |
|
||
| `MIN_TRADES_PER_PATH` | 15 | 60 | 100 |
|
||
| Stage-2 / joint trials | 80 | 120 | 200 |
|
||
| Stage-1 trials (hold-out) | 60 | 100 | 150 |
|
||
|
||
### Two-stage optimization flow
|
||
|
||
| Stage | CLI flag | Method | Speed | Output |
|
||
|-------|----------|--------|-------|--------|
|
||
| Stage 1 | `--stage xgb` | Fast single IS/OOS hold-out — no CPCV | ~5× faster | `models/stage1_{tf}_{broker}.json` |
|
||
| Stage 2 | `--stage trading` | Full CPCV — C(4,2)=6 paths, warm-started from Stage 1 | Full | `models/study_{broker}.db` |
|
||
| Joint | *(omit `--stage`)* | Full CPCV from scratch | Full | `models/study_{broker}.db` |
|
||
|
||
### CPCV score interpretation
|
||
|
||
| cpcv_score | Interpretation |
|
||
|------------|----------------|
|
||
| ≥ +1.0 | ✅ Excellent — strong consistent performance across all 6 paths |
|
||
| ≥ +0.3 | 🟡 Acceptable — robust enough for live deployment |
|
||
| ≥ 0.0 | 🟠 Marginal — consider additional regularisation |
|
||
| < 0.0 | 🔴 Poor — inconsistent paths; re-optimise with more trials |
|
||
|
||
---
|
||
|
||
## Walk-Forward Analysis & Staleness Gate
|
||
|
||
### Walk-Forward Analysis
|
||
|
||
```
|
||
Full dataset
|
||
├── Window 1: Train [Y1–Y2] → Test [Y2 Q3]
|
||
├── Window 2: Train [Y1 Q3–Y2 Q3] → Test [Y3 Q1]
|
||
└── Aggregate: WFE = mean(OOS Sharpe) / mean(IS Sharpe)
|
||
```
|
||
|
||
| WFE | Interpretation |
|
||
|-----|----------------|
|
||
| ≥ 60% | Robust — safe to go live |
|
||
| 50–60% | Acceptable — monitor closely |
|
||
| < 50% | Fragile — model curve-fits specific years |
|
||
|
||
```bash
|
||
python main.py --mode wfa --tf H1 --broker headway_cent --balance 15
|
||
```
|
||
|
||
### Model Staleness Gate
|
||
|
||
| TF | Max HMM/XGB model age |
|
||
|----|-----------------------|
|
||
| M5 | 14 days |
|
||
| M15 | 30 days |
|
||
| H1 | 30 days |
|
||
|
||
M5 has an additional **5-day optimisation freshness gate**: `m5_meta_<broker>.json` must exist and be < 120 hours old before going live.
|
||
|
||
**Bypass the gate for demo testing:**
|
||
```bash
|
||
python main.py --mode demo --tf M5 --broker headway_cent --balance 15 --skip_stale_check
|
||
```
|
||
|
||
### Recommended Maintenance Schedule
|
||
|
||
```bash
|
||
# Weekly (M5):
|
||
python main.py --mode sync_validate --tf M5 --period 3m --broker headway_cent --balance 15
|
||
python main.py --mode wfa --tf M5 --broker headway_cent --balance 15
|
||
# If needed:
|
||
python main.py --mode optimize --tf M5 --broker headway_cent --balance 15 --trials 1000
|
||
python main.py --mode train --tf M5 --broker headway_cent --balance 15
|
||
|
||
# Monthly (H1 / M15):
|
||
python main.py --mode wfa --tf H1 --broker headway_cent --balance 15
|
||
python main.py --mode optimize --tf H1 --broker headway_cent --balance 15 --trials 400
|
||
python main.py --mode train --tf H1 --broker headway_cent --balance 15
|
||
```
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
| Problem | Cause | Fix |
|
||
|---------|-------|-----|
|
||
| `ConnectionError: Could not connect to MT5` | Terminal not running | Open MT5 and log in first |
|
||
| `FileNotFoundError: hmm_model_H1_headway_cent.pkl` | Models not trained | `--mode train --tf H1 --broker headway_cent` |
|
||
| `FileNotFoundError: m5_meta_headway_cent.json` | M5 not optimised | `--mode optimize --tf M5 --broker headway_cent` |
|
||
| `WARNING: No Optuna study found` | Wrong broker or deleted DB | `--mode optimize` with matching `--broker` |
|
||
| `ERROR: Degenerate HMM` during train | Stale params | Delete study DB, re-optimise, then train |
|
||
| Validation FAIL every day | Model stale or data ends before sync window | Export fresh CSV from MT5, re-run full pipeline |
|
||
| No signals firing | XGBoost prob or persistence below thresholds | Check `[LOGIC AUDIT]` logs for `prob=` and `P(stay)=` values; consider re-training |
|
||
| No entry after regime change | `bars_in_regime` < 2 | Engine waits for confirmation — normal on first bar of a new regime |
|
||
| WFA shows many ❌ folds | Model curve-fits specific years | Loosen regularisation, increase trials, add more training data |
|
||
| `Order failed: retcode=10006` | No broker connection | Check MT5 connection indicator |
|
||
| `Order failed: retcode=10015` | Price moved past deviation | Will retry next bar; elevated deviation auto-applies on high-vol |
|
||
| `[CONFLICT]` warning at startup | GRX positions from EA already open | Stop EA / other process before starting bridge |
|
||
| Double positions | MQL5 EA running alongside Python bridge | Remove GoldRegimeX.mq5 EA from chart |
|
||
| Telegram errors in log | Wrong token or unconfigured | Check `.env`; regenerate via @BotFather |
|
||
|
||
**Emergency stop:** Press **Ctrl+C**. Open positions remain open — close them manually from the MT5 Trade tab.
|
||
|
||
---
|
||
|
||
## Security Notes
|
||
|
||
- **Never commit `.env`** — it is in `.gitignore`
|
||
- `.env.example` contains only placeholders
|
||
- `ALLOWED_USER_ID` is the single security gate for all Telegram commands
|
||
- If credentials are accidentally committed, immediately revoke the bot token via @BotFather `/revoke`
|
||
- The listener uses Telegram long-polling — no public webhook or open port required
|
||
|
||
---
|
||
|
||
## State Labels
|
||
|
||
Hardcoded across all Python modules and the MQL5 EA:
|
||
|
||
| Label | Integer | Signal eligibility |
|
||
|-------|---------|-------------------|
|
||
| Bull | 0 | BUY (trend) |
|
||
| Bear | 1 | SELL (trend) |
|
||
| Chop | 2 | MR_BUY / MR_SELL only (`n_states = 3`) |
|
||
| Chop_Low | 2 | MR_BUY / MR_SELL only (`n_states = 4`) |
|
||
| Chop_High | 3 | MR_SELL only with higher cutoff (`n_states = 4`) |
|
||
|
||
## Model Files Reference
|
||
|
||
```
|
||
models/
|
||
├── hmm_model_H1_headway_cent.pkl
|
||
├── hmm_model_H1_standard.pkl
|
||
├── hmm_model_M15_headway_cent.pkl
|
||
├── hmm_model_M5_headway_cent.pkl
|
||
├── xgb_ensemble_H1_headway_cent.pkl
|
||
├── xgb_ensemble_M15_headway_cent.pkl
|
||
├── xgb_ensemble_M5_headway_cent.pkl
|
||
├── xgb_model_H1_headway_cent.onnx ← MQL5 EA uses this
|
||
├── study_headway_cent.db ← Optuna trials (per-broker, never shared)
|
||
├── study_standard.db
|
||
└── m5_meta_headway_cent.json ← M5 optimisation freshness gate
|
||
```
|
||
|
||
Without this fix, the notebook's loaded probabilities came from a single global XGBoost model instead of the three vol-bucket models used by the optimizer and live trader, making Section 2 equity curves and Section 3 WFO scores inconsistent with production results.
|