| # Execution Pipeline — DualEA System
|
|
|
| **Complete trade execution flow from signal to position**
|
|
|
| ---
|
|
|
| ## Pipeline Overview
|
|
|
| ```
|
| OnTick/OnTimer
|
| ↓
|
| [1. Early Safety & Filters] ← circuit breaker, memory guard, news filter (PaperEA); risk/spread/session/news/etc (LiveEA)
|
| ↓ PASS
|
| [2. Strategy Selection] ← selector scoring (PaperEA) or external orchestration (LiveEA)
|
| ↓ SELECTED
|
| [3. Signal Generation] ← PaperEA: indicator signal generator registry; LiveEA: strategy bridge is available but not called from OnTick
|
| ↓ SIGNAL
|
| [4. 8-Stage Gates] ← GateManager (PaperEA) / LiveEA early + risk4 gates
|
| ↓ PASS
|
| [5. Policy & Insights Gating] ← LiveEA: per-slice policy + insights gating + exploration; PaperEA: minimal policy load + min_conf gate
|
| ↓ PASS
|
| [6. Trade Execution] ← TradeManager.ExecuteOrder() (both EAs)
|
| ↓ SUCCESS
|
| [7. Post-Execution] ← KB logging, features export, telemetry, learning
|
| ```
|
|
|
| ---
|
|
|
| ## Stage 1: Signal Generation
|
|
|
| **Entry Points:**
|
| - `OnTick()`: Real-time tick processing
|
| - `OnTimer()`: Used today for maintenance and reload polling (PaperEA); LiveEA uses a timer for insights auto-reload polling
|
|
|
| **PaperEA_v2 Signal Generation (actual):**
|
| - Strategy selection chooses from a **23-strategy list** (e.g., `ADXStrategy`, `RSIStrategy`, `MACDStrategy`, `IchimokuStrategy`, etc.).
|
| - Signal generation is executed through `Include/StrategySignalGenerators.mqh` via `GenerateSignalFromStrategy()`.
|
| - Output is a `TradingSignal` (from `Include/GateManager.mqh`) with `price`, `sl`, `tp`, `volume`, and `confidence` seeded.
|
|
|
| **LiveEA Signal Generation (actual):**
|
| - `LiveEA.mq5` provides the gating and execution pipeline but **does not call the strategy bridge from `OnTick()`**.
|
| - Strategy generation exists as `LiveEA_StrategyBridge.mqh` (21 default `IStrategy` implementations) and can be used by an external orchestrator or future wiring.
|
|
|
| **Signal Structure:**
|
| - `strategy_name`, `direction` (1=Buy, -1=Sell)
|
| - `confidence` (0.0-1.0), `entry_price`, `stop_loss`, `take_profit`, `lot_size`
|
| - `reason` (human-readable), `timestamp`
|
| - Strategy-specific indicator values
|
|
|
| ---
|
|
|
| ## Stage 2: Early Phase Validation
|
|
|
| Quick rejection before expensive gate processing.
|
|
|
| **Checks:**
|
| 1. **Circuit breakers / risk limits**
|
| 2. **News blackout** (Common Files CSV `DualEA\news_blackouts.csv`)
|
| 3. **Session/trading hours caps** (where enabled)
|
| 4. **Spread and margin checks** (LiveEA)
|
|
|
| **NoConstraintsMode**:
|
| - PaperEA default is `true` and bypasses many constraints, but **circuit breaker + memory guard remain enforced**.
|
| - LiveEA default is `false`; when `true`, many gates become **shadow/diagnostic only** (it logs but does not block on early gates).
|
|
|
| ---
|
|
|
| ## Stage 3: 8-Stage Gate Processing
|
|
|
| Progressive filtering through configurable gates.
|
|
|
| **Important reality check:** In `Include/GateManager.mqh`, several gates are currently implemented as simplified/deterministic checks (for repeatable Strategy Tester runs), not a full production-grade market simulation.
|
|
|
| ### Gate 1: Signal Rinse
|
| - **Purpose**: Basic validation
|
| - **Checks**: `confidence ≥ G1_MinConfidence`, `strength ≥ G1_MinStrength`
|
|
|
| ### Gate 2: Market Soap
|
| - **Purpose**: Market context
|
| - **Checks**: ATR within `[G2_MinVolatility, G2_MaxVolatility]`, portfolio correlation ≤ `G2_MaxCorrelation`, regime tradeable
|
|
|
| ### Gate 3: Strategy Scrub
|
| - **Purpose**: Strategy-specific validation
|
| - **Checks**: History bars ≥ `G3_MinHistoryBars`, strategy.ValidateSignal()
|
|
|
| ### Gate 4: Risk Wash
|
| - **Purpose**: Risk validation
|
| - **Checks**: Risk/trade ≤ `G4_MaxRiskPct`, portfolio risk ≤ `G4_MaxPortfolioRisk`, RR ≥ `G4_MinRiskReward`
|
|
|
| ### Gate 5: Performance Wax
|
| - **Purpose**: Backtest performance
|
| - **Checks**: Backtest WR ≥ `G5_MinBacktestWinRate`, trades ≥ `G5_MinBacktestTrades`, R ≥ `G5_MinBacktestRMultiple`
|
|
|
| ### Gate 6: ML Polish
|
| - **Purpose**: ML confidence
|
| - **Checks**: ML confidence ≥ `G6_MinMLConfidence`, ML available if `G6_RequireMLAvailable`
|
|
|
| ### Gate 7: Live Clean
|
| - **Purpose**: Live market conditions
|
| - **Checks**: Spread, market open, liquidity
|
|
|
| ### Gate 8: Final Verify
|
| - **Purpose**: Pre-execution validation
|
| - **Checks**: Broker constraints (min/max lots), duplicate signals
|
|
|
| **Learning Integration**: Each gate decision recorded via `LearningBridge` for threshold optimization.
|
|
|
| ---
|
|
|
| ## Stage 4: Strategy Selection
|
|
|
| ### StrategySelector Scoring (PaperEA)
|
|
|
| ```cpp
|
| score = (winRate × W1) + (normRMultiple × W2) + (normSharpe × W3) + (normTrades × W4)
|
| if(SelectorUseRecency) score ×= recencyDecay^daysSinceLastTrade
|
| ```
|
|
|
| **Weights (actual PaperEA inputs):** `SelW_PF`, `SelW_Exp`, `SelW_WR`, `SelW_DD` with optional recency overlay.
|
|
|
| ### Insights Gating
|
|
|
| Load insights.json slice for `strategy|symbol|timeframe`:
|
|
|
| - **No slice found**: Enter exploration mode if enabled
|
| - **Slice exists**: Check `winRate ≥ InsightsMinWinRate`, `totalTrades ≥ InsightsMinTotalTrades`, `avgR ≥ InsightsMinAvgR`
|
|
|
| ### Exploration Mode
|
|
|
| **Triggered when**: No insights slice exists
|
|
|
| **Caps (current defaults in code):**
|
| - Daily: `ExploreMaxPerSlicePerDay` (default 100)
|
| - Weekly: `ExploreMaxPerSlice` (default 100)
|
|
|
| **Counters**: Persist in `explore_counts.csv` and `explore_counts_day.csv`
|
|
|
| **NoConstraintsMode**: Bypasses insights gating and exploration caps entirely.
|
|
|
| ---
|
|
|
| ## Stage 5: Policy Application
|
|
|
| ### Policy Loading
|
|
|
| **LiveEA (actual):**
|
| - Loads `DualEA\policy.json` from **Common Files**.
|
| - Parses per-slice fields including `p_win`, and optional scaling keys `sl_scale`, `tp_scale`, `trail_scale`.
|
| - Applies a slice lookup order:
|
| - Exact `strategy+symbol+timeframe`
|
| - Aggregate `strategy+symbol` with `timeframe=-1`
|
| - Aggregate `strategy` with `symbol="*"` and `timeframe=-1`
|
|
|
| **PaperEA_v2 (actual):**
|
| - Loads `DualEA\policy.json` from Common Files and treats the policy as “loaded” if it can find the string `min_confidence`.
|
| - `g_policy_min_conf` defaults to `0.5`.
|
| - Per-slice parsing/scaling is **not currently wired** in PaperEA’s `Policy_Load()`.
|
| - HTTP polling exists (see `PolicyServerUrl`, `PolicyHttpPollPercent`) but still only checks for `min_confidence`.
|
|
|
| ### Policy Gating Logic
|
|
|
| ```
|
| if policy.slices == 0:
|
| if FallbackWhenNoPolicy && (FallbackDemoOnly→demo OR !FallbackDemoOnly):
|
| → FALLBACK: neutral scaling, bypass insights/exploration
|
| else:
|
| → BLOCK
|
|
|
| if slice missing:
|
| if FallbackWhenSliceMissing && (demo check as above):
|
| → FALLBACK: neutral scaling
|
| else:
|
| → BLOCK
|
|
|
| if slice.probability < policy.min_confidence:
|
| → BLOCK
|
|
|
| else:
|
| → PASS: Apply scaling
|
| ```
|
|
|
| ### Policy Scaling
|
|
|
| **LiveEA (actual):**
|
| - Adjusts SL/TP distance from entry using `sl_scale` / `tp_scale`.
|
| - Adjusts trailing distance using `trail_scale`.
|
|
|
| **PaperEA_v2 (current):**
|
| - Policy gating is applied as a **minimum confidence check** (and the scaling path is currently neutral/placeholder in `ApplyPolicyGating()`).
|
|
|
| ---
|
|
|
| ## Stage 6: Risk Management
|
|
|
| ### Position Sizing
|
|
|
| **Base Sizing**:
|
| ```
|
| pipRisk = |entry - SL| / Point / 10
|
| riskAmount = Balance × (G4_MaxRiskPct / 100)
|
| lots = riskAmount / (pipRisk × tickValue)
|
| lots = NormalizeLots(lots) // Broker constraints
|
| ```
|
|
|
| **Correlation Adjustment** (LiveEA with PositionManager):
|
| ```
|
| avgCorr = CorrelationManager.GetAverageCorrelation(symbol)
|
| dampening = 1.0 - (avgCorr × 0.5)
|
| adjustedLots = lots × dampening
|
| adjustedLots = max(adjustedLots, max(lots × PM_CorrMinMult, PM_CorrMinLots))
|
| ```
|
|
|
| ### Circuit Breakers
|
|
|
| - **Daily Loss**: Block if daily PnL < `-MaxDailyLossPct`
|
| - **Drawdown**: Block if drawdown from peak > `MaxDrawdownPct`
|
| - **Consecutive Losses**: Block after `MaxConsecutiveLosses` losses
|
| - **Cooldown**: After breaker trips, wait `CircuitCooldownSec` seconds
|
|
|
| ---
|
|
|
| ## Stage 7: Trade Execution
|
|
|
| ### TradeManager.Execute()
|
|
|
| **PaperEA_v2 (actual):** Places **real MT5 orders on demo accounts** via `CTradeManager::ExecuteOrder()`.
|
|
|
| **LiveEA (actual):** Places **real MT5 orders** via `CTradeManager::ExecuteOrder()`.
|
| - Market orders: Immediate execution
|
| - Pending orders: BuyStop, SellStop, BuyLimit, SellLimit
|
| - SL/TP normalization to broker tick size
|
| - Volume normalization (min/max/step)
|
|
|
| **Trailing Stops**: Initialized if `TrailEnabled=true`
|
| - Fixed-points: `TrailActivationPoints`, `TrailDistancePoints`, `TrailStepPoints`
|
| - ATR-based: `TrailATRMultiplier × ATR` for distance
|
|
|
| ---
|
|
|
| ## Stage 8: Post-Execution
|
|
|
| ### Knowledge Base Logging
|
|
|
| **knowledge_base.csv**:
|
| ```
|
| timestamp,symbol,type,entry_price,stop_loss,take_profit,close_price,profit,strategy_id
|
| ```
|
|
|
| **knowledge_base_events.csv**:
|
| ```
|
| timestamp,strategy,retcode,deal,order
|
| ```
|
|
|
| ### Features Export
|
|
|
| **features.csv** (long format):
|
| ```
|
| timestamp,symbol,timeframe,strategy,direction,confidence,...,indicator_1,indicator_2,...
|
| ```
|
| - 50+ features per trade
|
| - Automatic ~100MB rotation (rename to `*.bak`); no compression is performed in MQL5
|
|
|
| ### Telemetry & Logging
|
|
|
| **Telemetry Events**:
|
| - `gate_decision`: Pass/block with reason, latency
|
| - `trade_execution`: Order placement, retcode
|
| - `policy_load`, `insights_rebuild`, `threshold_adjust`
|
| - `risk4_*`: Risk gate events (spread, margin, drawdown)
|
| - `pm_*`: PositionManager events (correlation sizing, scale-ins)
|
| - `p5_*`: Selector events (refresh, rescore, tune)
|
|
|
| **UnifiedTradeLogger**: Daily JSON logs with full lifecycle
|
| ```json
|
| {
|
| "timestamp": "2025-01-15T10:30:00Z",
|
| "strategy": "ADXStrategy",
|
| "symbol": "EURUSD",
|
| "timeframe": 60,
|
| "direction": 1,
|
| "entry": 1.0850,
|
| "sl": 1.0800,
|
| "tp": 1.0950,
|
| "lots": 0.10,
|
| "gates": ["signal_rinse:pass", "market_soap:pass", ...],
|
| "policy": {"confidence": 0.75, "sl_mult": 1.0, "tp_mult": 1.2},
|
| "outcome": "closed",
|
| "profit": 15.50,
|
| "r_multiple": 1.2
|
| }
|
| ```
|
|
|
| ### Learning System Update
|
|
|
| **LearningBridge**: Records `CSignalDecision` with:
|
| - All 8 gate decisions (pass/fail, reason, latency)
|
| - Final outcome (executed, blocked, reason)
|
| - Trade result (if executed): profit, R-multiple, duration
|
|
|
| **GateLearningSystem**: Updates gate thresholds
|
| - Hybrid learning: immediate (5% rate) + batch (every 20 trades)
|
| - Target success rate per gate configurable
|
| - Adjusts thresholds to optimize pass rate vs trade quality
|
|
|
| ---
|
|
|
| ## Live Trading Flow (LiveEA)
|
|
|
| **Differences from PaperEA_v2 (as wired today):**
|
| 1. **Insights auto-reload loop**: LiveEA requests rebuild by creating `DualEA\insights.reload` and waits for `DualEA\insights.ready`.
|
| 2. **Per-slice policy scaling**: LiveEA parses per-slice policy entries and applies SL/TP/trailing scaling.
|
| 3. **Risk gates**: LiveEA includes spread/session/margin/consecutive-loss gating in the EA itself.
|
|
|
| **Safety Features**:
|
| - Shadow mode possible (log decisions, don't execute)
|
| - Policy fallback with demo-only restriction
|
| - Multiple circuit breakers
|
| - One-execution-per-slice-per-minute de-dup (Phase 6 - planned)
|
|
|
| ---
|
|
|
| ## Error Handling
|
|
|
| ### Broker Errors
|
|
|
| **Retcode Handling**:
|
| - `TRADE_RETCODE_DONE`: Success → proceed to post-execution
|
| - `TRADE_RETCODE_REQUOTE`: Retry with new price (max 3 attempts)
|
| - `TRADE_RETCODE_REJECT`: Log + telemetry, don't retry
|
| - `TRADE_RETCODE_NO_MONEY`: Trip circuit breaker, halt trading
|
| - Other errors: Log, telemetry, optional cooldown
|
|
|
| ### File I/O Errors
|
|
|
| **KB Write Failures**:
|
| - Retry with exponential backoff (3 attempts)
|
| - Create directory if missing
|
| - Log to Journal on persistent failure
|
| - Continue execution (non-fatal)
|
|
|
| **Policy/Insights Load Failures**:
|
| - Use last-known-good cached version
|
| - Log warning + telemetry event
|
| - Fallback to neutral scaling if allowed
|
| - Reload attempt on next timer cycle
|
|
|
| ### Learning System Failures
|
|
|
| - Non-fatal: Log errors, continue trading
|
| - Disable learning if consecutive failures > 5
|
| - Telemetry event for monitoring
|
|
|
| ---
|
|
|
| ## Performance Characteristics
|
|
|
| ### Latency Targets
|
|
|
| - **Signal → Gate entry**: <5ms
|
| - **8-stage gates**: <50ms total (target <10ms per gate)
|
| - **Policy/insights lookup**: <2ms (in-memory cache)
|
| - **KB write**: <10ms (non-blocking preferred)
|
| - **Telemetry emit**: <1ms
|
| - **Total pipeline**: <100ms (OnTick → OrderSend)
|
|
|
| ### Memory Footprint
|
|
|
| - **Strategies (21)**: ~500KB
|
| - **Gate Manager**: ~100KB
|
| - **Policy cache**: ~50KB
|
| - **Insights cache**: ~200KB
|
| - **Telemetry buffer**: ~500KB
|
| - **Total**: ~1.5MB per EA instance
|
|
|
| ### File I/O
|
|
|
| - **KB writes**: Append-only, buffered every 10 trades
|
| - **Features export**: Buffered every 50 trades
|
| - **Telemetry flush**: Every 5 minutes or 1000 events
|
| - **Policy/insights reload**: On timer (every 60 minutes) or .reload trigger
|
|
|
| ---
|
|
|
| **See Also:**
|
| - [Configuration-Reference.md](Configuration-Reference.md) - All input parameters
|
| - [Observability-Guide.md](Observability-Guide.md) - Telemetry and monitoring
|
| - [Policy-Exploration-Guide.md](Policy-Exploration-Guide.md) - Policy fallback and exploration details
|