mirror of
https://github.com/dinethlive/dbasket-EA.git
synced 2026-08-17 12:48:16 +00:00
Initial commit: D-Basket EA v2.0 Pro
This commit is contained in:
@@ -0,0 +1,546 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DBasketEA.mq5 |
|
||||
//| D-Basket Correlation Hedging EA |
|
||||
//| Three-Pair Correlation Strategy for AUDCAD/NZDCAD/AUDNZD |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "D-Basket EA"
|
||||
#property version "1.00"
|
||||
#property description "Three-pair correlation hedging EA exploiting temporary divergences between AUDCAD, NZDCAD, and AUDNZD"
|
||||
#property strict
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Include Files |
|
||||
//+------------------------------------------------------------------+
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "..\Include\DBasket\DBasket_Defines.mqh"
|
||||
#include "..\Include\DBasket\DBasket_Structures.mqh"
|
||||
#include "..\Include\DBasket\DBasket_Logger.mqh"
|
||||
#include "..\Include\DBasket\DBasket_CorrelationEngine.mqh"
|
||||
#include "..\Include\DBasket\DBasket_SignalEngine.mqh"
|
||||
#include "..\Include\DBasket\DBasket_TradeWrapper.mqh"
|
||||
#include "..\Include\DBasket\DBasket_PositionManager.mqh"
|
||||
#include "..\Include\DBasket\DBasket_RiskManager.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Input Parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
// --- Symbol Configuration ---
|
||||
input group "Symbol Settings"
|
||||
input string InpSymbolSuffix = ""; // Symbol suffix (e.g., ".m", "_sb")
|
||||
|
||||
// --- Correlation Engine ---
|
||||
input group "Correlation Engine"
|
||||
input int InpLookbackPeriod = 250; // Lookback period (bars)
|
||||
input int InpCacheUpdateSec = 30; // Cache update interval (seconds)
|
||||
|
||||
// --- Signal Generation ---
|
||||
input group "Signal Generation"
|
||||
input double InpZScoreEntry = 2.5; // Entry Z-Score threshold
|
||||
input double InpZScoreExit = 0.5; // Exit Z-Score threshold
|
||||
input double InpMinCorrelation = 0.75; // Minimum correlation threshold
|
||||
input double InpMaxSpreadPips = 3.0; // Maximum spread (pips per symbol)
|
||||
|
||||
// --- Risk Management ---
|
||||
input group "Risk Management"
|
||||
input double InpFixedLotSize = 0.01; // Fixed lot size per leg
|
||||
input double InpRiskPercent = 1.0; // Risk % per basket (if dynamic sizing)
|
||||
input bool InpUseFixedLots = true; // Use fixed lot size
|
||||
input double InpMaxDrawdownPct = 15.0; // Max drawdown % before halt
|
||||
input double InpDailyLossLimit = 100.0; // Daily loss limit ($)
|
||||
input double InpDailyLossPct = 5.0; // Daily loss limit (%)
|
||||
input int InpMaxHoldingHours = 24; // Maximum basket holding hours
|
||||
input double InpTakeProfitAmount = 10.0; // Take profit per basket ($)
|
||||
input double InpStopLossAmount = 15.0; // Stop loss per basket ($)
|
||||
|
||||
// --- Trading Hours ---
|
||||
input group "Trading Hours"
|
||||
input int InpTradingStartHour = 0; // Trading start hour (broker time)
|
||||
input int InpTradingStartMin = 0; // Trading start minute
|
||||
input int InpTradingEndHour = 23; // Trading end hour (broker time)
|
||||
input int InpTradingEndMin = 59; // Trading end minute
|
||||
input bool InpAvoidRollover = true; // Avoid rollover period
|
||||
|
||||
// --- Technical Settings ---
|
||||
input group "Technical Settings"
|
||||
input int InpMagicNumber = 100000; // Magic number
|
||||
input int InpSlippagePoints = 10; // Maximum slippage (points)
|
||||
input ENUM_LOG_LEVEL InpLogLevel = LOG_LEVEL_INFO; // Log level
|
||||
input bool InpLogToFile = false; // Log to file
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables |
|
||||
//+------------------------------------------------------------------+
|
||||
// Configuration
|
||||
EAConfig g_config;
|
||||
|
||||
// Module instances
|
||||
CCorrelationEngine g_correlationEngine;
|
||||
CSignalEngine g_signalEngine;
|
||||
CTradeWrapper g_tradeWrapper;
|
||||
CPositionManager g_positionManager;
|
||||
CRiskManager g_riskManager;
|
||||
|
||||
// State
|
||||
bool g_isInitialized = false;
|
||||
bool g_tradingEnabled = true;
|
||||
datetime g_lastTickProcessed = 0;
|
||||
int g_tickCount = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Build configuration from inputs |
|
||||
//+------------------------------------------------------------------+
|
||||
void BuildConfiguration()
|
||||
{
|
||||
g_config.SetDefaults();
|
||||
|
||||
// Symbol configuration
|
||||
g_config.symbols[SYMBOL_AUDCAD] = DEFAULT_SYMBOL_AUDCAD + InpSymbolSuffix;
|
||||
g_config.symbols[SYMBOL_NZDCAD] = DEFAULT_SYMBOL_NZDCAD + InpSymbolSuffix;
|
||||
g_config.symbols[SYMBOL_AUDNZD] = DEFAULT_SYMBOL_AUDNZD + InpSymbolSuffix;
|
||||
g_config.timeframe = Period();
|
||||
|
||||
// Correlation engine
|
||||
g_config.lookbackPeriod = InpLookbackPeriod;
|
||||
g_config.updateIntervalSeconds = InpCacheUpdateSec;
|
||||
|
||||
// Signal generation
|
||||
g_config.zScoreEntryThreshold = InpZScoreEntry;
|
||||
g_config.zScoreExitThreshold = InpZScoreExit;
|
||||
g_config.minCorrelation = InpMinCorrelation;
|
||||
g_config.maxSpreadPips = InpMaxSpreadPips;
|
||||
|
||||
// Risk management
|
||||
g_config.baseLotSize = InpFixedLotSize;
|
||||
g_config.riskPercentPerBasket = InpRiskPercent;
|
||||
g_config.sizingMode = InpUseFixedLots ? SIZING_FIXED : SIZING_RISK_BASED;
|
||||
g_config.maxDrawdownPercent = InpMaxDrawdownPct;
|
||||
g_config.maxDailyLossPercent = InpDailyLossPct;
|
||||
g_config.maxDailyLossAmount = InpDailyLossLimit;
|
||||
g_config.maxHoldingHours = InpMaxHoldingHours;
|
||||
|
||||
// Trading hours
|
||||
g_config.tradingStartHour = InpTradingStartHour;
|
||||
g_config.tradingStartMinute = InpTradingStartMin;
|
||||
g_config.tradingEndHour = InpTradingEndHour;
|
||||
g_config.tradingEndMinute = InpTradingEndMin;
|
||||
g_config.avoidRollover = InpAvoidRollover;
|
||||
|
||||
// Technical
|
||||
g_config.magicNumber = InpMagicNumber;
|
||||
g_config.slippagePoints = InpSlippagePoints;
|
||||
g_config.logLevel = InpLogLevel;
|
||||
g_config.logToFile = InpLogToFile;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Validate input parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ValidateInputs()
|
||||
{
|
||||
// Lookback period
|
||||
if(InpLookbackPeriod < MIN_LOOKBACK_PERIOD || InpLookbackPeriod > MAX_LOOKBACK_PERIOD)
|
||||
{
|
||||
Logger.Error("Invalid lookback period. Must be " + IntegerToString(MIN_LOOKBACK_PERIOD) +
|
||||
"-" + IntegerToString(MAX_LOOKBACK_PERIOD));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Z-score thresholds
|
||||
if(InpZScoreEntry <= 0 || InpZScoreEntry > 5.0)
|
||||
{
|
||||
Logger.Error("Invalid entry Z-score. Must be 0-5.0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(InpZScoreExit < 0 || InpZScoreExit >= InpZScoreEntry)
|
||||
{
|
||||
Logger.Error("Invalid exit Z-score. Must be 0 to less than entry threshold");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Correlation
|
||||
if(InpMinCorrelation < 0.5 || InpMinCorrelation > 0.95)
|
||||
{
|
||||
Logger.Error("Invalid minimum correlation. Must be 0.5-0.95");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Risk parameters
|
||||
if(InpRiskPercent < 0.1 || InpRiskPercent > 10.0)
|
||||
{
|
||||
Logger.Error("Invalid risk percent. Must be 0.1-10.0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(InpMaxDrawdownPct < 5.0 || InpMaxDrawdownPct > 50.0)
|
||||
{
|
||||
Logger.Error("Invalid max drawdown. Must be 5-50%");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Validate trading environment |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ValidateEnvironment()
|
||||
{
|
||||
// Check account type (must be hedging)
|
||||
ENUM_ACCOUNT_MARGIN_MODE marginMode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
|
||||
if(marginMode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
|
||||
{
|
||||
Logger.Error("FATAL: Hedging account required. Current mode: " + EnumToString(marginMode));
|
||||
Logger.Error("This EA requires a hedging account to open opposite positions.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if trading allowed
|
||||
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
|
||||
{
|
||||
Logger.Error("Trading is not allowed in terminal settings");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!MQLInfoInteger(MQL_TRADE_ALLOWED))
|
||||
{
|
||||
Logger.Error("Automated trading is not allowed for this EA");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check connection
|
||||
if(!TerminalInfoInteger(TERMINAL_CONNECTED))
|
||||
{
|
||||
Logger.Warning("Terminal is not connected to server");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Validate symbols |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ValidateSymbols()
|
||||
{
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
{
|
||||
string symbol = g_config.symbols[i];
|
||||
|
||||
// Try to select symbol
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Logger.Error("Symbol not available: " + symbol);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check trade mode
|
||||
ENUM_SYMBOL_TRADE_MODE tradeMode = (ENUM_SYMBOL_TRADE_MODE)SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE);
|
||||
if(tradeMode != SYMBOL_TRADE_MODE_FULL)
|
||||
{
|
||||
Logger.Error("Trading not fully allowed on " + symbol + ": " + EnumToString(tradeMode));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log symbol info
|
||||
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
||||
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
||||
double spread = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
|
||||
Logger.Info("Symbol " + symbol + " - MinLot: " + DoubleToString(minLot, 2) +
|
||||
", Step: " + DoubleToString(lotStep, 2) +
|
||||
", Spread: " + DoubleToString(spread * 10000, 1) + " pips");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Initialize logger first
|
||||
Logger.Initialize(InpLogLevel, InpLogToFile);
|
||||
|
||||
// Log startup
|
||||
Logger.LogInitSummary(EA_NAME, EA_VERSION,
|
||||
AccountInfoDouble(ACCOUNT_BALANCE),
|
||||
(int)AccountInfoInteger(ACCOUNT_LEVERAGE),
|
||||
AccountInfoString(ACCOUNT_SERVER));
|
||||
|
||||
// Build configuration from inputs
|
||||
BuildConfiguration();
|
||||
|
||||
// Validate inputs
|
||||
if(!ValidateInputs())
|
||||
{
|
||||
return INIT_PARAMETERS_INCORRECT;
|
||||
}
|
||||
|
||||
// Validate environment
|
||||
if(!ValidateEnvironment())
|
||||
{
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Validate symbols
|
||||
if(!ValidateSymbols())
|
||||
{
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Initialize modules
|
||||
Logger.Info("Initializing EA modules...");
|
||||
|
||||
// Trade wrapper
|
||||
if(!g_tradeWrapper.Initialize(g_config.magicNumber, g_config.slippagePoints))
|
||||
{
|
||||
Logger.Error("Failed to initialize Trade Wrapper");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Correlation engine
|
||||
if(!g_correlationEngine.Initialize(g_config.symbols, g_config.lookbackPeriod,
|
||||
g_config.timeframe, g_config.updateIntervalSeconds))
|
||||
{
|
||||
Logger.Error("Failed to initialize Correlation Engine");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Signal engine
|
||||
if(!g_signalEngine.Initialize(g_config))
|
||||
{
|
||||
Logger.Error("Failed to initialize Signal Engine");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Position manager
|
||||
if(!g_positionManager.Initialize(g_config, &g_tradeWrapper))
|
||||
{
|
||||
Logger.Error("Failed to initialize Position Manager");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Set TP/SL
|
||||
g_positionManager.SetTPSL(InpTakeProfitAmount, InpStopLossAmount);
|
||||
|
||||
// Risk manager
|
||||
if(!g_riskManager.Initialize(g_config))
|
||||
{
|
||||
Logger.Error("Failed to initialize Risk Manager");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Recover any existing positions from previous session
|
||||
g_positionManager.RecoverFromOpenPositions();
|
||||
|
||||
g_isInitialized = true;
|
||||
g_tradingEnabled = true;
|
||||
|
||||
Logger.Info("EA initialization complete. Ready for trading.");
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
string reasonStr;
|
||||
switch(reason)
|
||||
{
|
||||
case REASON_PROGRAM: reasonStr = "Program"; break;
|
||||
case REASON_REMOVE: reasonStr = "Remove"; break;
|
||||
case REASON_RECOMPILE: reasonStr = "Recompile"; break;
|
||||
case REASON_CHARTCHANGE: reasonStr = "Chart changed"; break;
|
||||
case REASON_CHARTCLOSE: reasonStr = "Chart closed"; break;
|
||||
case REASON_PARAMETERS: reasonStr = "Parameters changed"; break;
|
||||
case REASON_ACCOUNT: reasonStr = "Account changed"; break;
|
||||
case REASON_TEMPLATE: reasonStr = "Template applied"; break;
|
||||
case REASON_INITFAILED: reasonStr = "Init failed"; break;
|
||||
case REASON_CLOSE: reasonStr = "Terminal closed"; break;
|
||||
default: reasonStr = "Unknown (" + IntegerToString(reason) + ")";
|
||||
}
|
||||
|
||||
Logger.Info("EA shutdown - Reason: " + reasonStr);
|
||||
|
||||
// Log final statistics
|
||||
PerformanceMetrics metrics;
|
||||
g_riskManager.GetMetrics(metrics);
|
||||
|
||||
Logger.Info("Final Statistics:");
|
||||
Logger.Info(" Total Baskets: " + IntegerToString(metrics.totalBaskets));
|
||||
Logger.Info(" Closed: " + IntegerToString(metrics.closedBaskets) +
|
||||
" (Win: " + IntegerToString(metrics.winningBaskets) +
|
||||
", Loss: " + IntegerToString(metrics.losingBaskets) + ")");
|
||||
Logger.Info(" Win Rate: " + DoubleToString(metrics.winRate * 100, 1) + "%");
|
||||
Logger.Info(" Realized P/L: $" + DoubleToString(metrics.realizedPL, 2));
|
||||
Logger.Info(" Max Drawdown: " + DoubleToString(metrics.maxDrawdownPercent, 2) + "%");
|
||||
|
||||
// Clear chart
|
||||
Comment("");
|
||||
|
||||
// Deinitialize logger
|
||||
Logger.Deinitialize();
|
||||
|
||||
g_isInitialized = false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
if(!g_isInitialized)
|
||||
return;
|
||||
|
||||
g_tickCount++;
|
||||
|
||||
// === Phase 1: Risk Management Check ===
|
||||
string riskReason;
|
||||
if(!g_riskManager.CheckRiskLimits(riskReason))
|
||||
{
|
||||
// Check for emergency exit
|
||||
string emergencyReason;
|
||||
if(g_riskManager.CheckEmergencyExit(emergencyReason))
|
||||
{
|
||||
Logger.Error("EMERGENCY EXIT: " + emergencyReason);
|
||||
if(g_positionManager.HasOpenBasket())
|
||||
{
|
||||
g_positionManager.CloseBasket(EXIT_EMERGENCY);
|
||||
}
|
||||
}
|
||||
|
||||
g_tradingEnabled = false;
|
||||
|
||||
// Update display
|
||||
if(g_tickCount % 100 == 0)
|
||||
g_riskManager.DisplayMetricsOnChart();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
g_tradingEnabled = true;
|
||||
|
||||
// === Phase 2: Update Price Buffers ===
|
||||
g_correlationEngine.UpdatePriceBuffers();
|
||||
|
||||
// === Phase 3: Update Correlation Cache ===
|
||||
if(!g_correlationEngine.UpdateCorrelationCache())
|
||||
{
|
||||
// Not ready or data invalid - skip this tick
|
||||
return;
|
||||
}
|
||||
|
||||
// Get correlation data
|
||||
CorrelationData corrData;
|
||||
g_correlationEngine.GetCorrelationData(corrData);
|
||||
|
||||
// === Phase 4: Position Management ===
|
||||
if(g_positionManager.HasOpenBasket())
|
||||
{
|
||||
// Update basket state
|
||||
g_positionManager.UpdateBasketState();
|
||||
|
||||
// Check exit signals
|
||||
BasketState basket;
|
||||
g_positionManager.GetBasketState(basket);
|
||||
|
||||
ENUM_EXIT_REASON exitReason;
|
||||
if(g_signalEngine.CheckExitSignal(corrData, basket,
|
||||
g_positionManager.GetTakeProfitAmount(),
|
||||
g_positionManager.GetStopLossAmount(),
|
||||
g_positionManager.GetMaxHoldingHours(),
|
||||
exitReason))
|
||||
{
|
||||
// Close basket
|
||||
double pl = g_positionManager.GetBasketPL();
|
||||
g_positionManager.CloseBasket(exitReason);
|
||||
|
||||
// Record result
|
||||
g_riskManager.RecordBasketClose(pl, pl >= 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// === Phase 5: Signal Generation ===
|
||||
string signalFailReason;
|
||||
ENUM_BASKET_SIGNAL signal = g_signalEngine.CheckEntrySignal(corrData, false, signalFailReason);
|
||||
|
||||
if(signal != SIGNAL_NONE)
|
||||
{
|
||||
g_riskManager.RecordSignal(true);
|
||||
|
||||
// Attempt to open basket
|
||||
if(g_positionManager.OpenBasket(signal, corrData.spreadZScore, corrData.corrAUDCAD_NZDCAD))
|
||||
{
|
||||
g_riskManager.RecordBasketOpen();
|
||||
}
|
||||
}
|
||||
else if(signalFailReason != "")
|
||||
{
|
||||
// Signal was blocked by filter
|
||||
g_riskManager.RecordSignal(false);
|
||||
|
||||
// Log filter reason periodically (every 1000 ticks)
|
||||
if(g_tickCount % 1000 == 0)
|
||||
{
|
||||
Logger.Debug("Signal blocked: " + signalFailReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === Phase 6: Display Update ===
|
||||
if(g_tickCount % 50 == 0)
|
||||
{
|
||||
g_riskManager.DisplayMetricsOnChart();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Tester function for custom optimization criterion |
|
||||
//+------------------------------------------------------------------+
|
||||
double OnTester()
|
||||
{
|
||||
// Get statistics
|
||||
double profit = TesterStatistics(STAT_PROFIT);
|
||||
double maxDD = TesterStatistics(STAT_EQUITY_DD);
|
||||
double profitFactor = TesterStatistics(STAT_PROFIT_FACTOR);
|
||||
int totalTrades = (int)TesterStatistics(STAT_TRADES);
|
||||
int winTrades = (int)TesterStatistics(STAT_PROFIT_TRADES);
|
||||
|
||||
// Calculate win rate
|
||||
double winRate = totalTrades > 0 ? (double)winTrades / totalTrades : 0;
|
||||
|
||||
// Custom optimization criterion
|
||||
// Prioritize: profit/drawdown ratio, win rate > 65%, profit factor > 1.3
|
||||
if(winRate < 0.65 || profitFactor < 1.3 || totalTrades < 20)
|
||||
return 0; // Reject parameters that don't meet minimum criteria
|
||||
|
||||
// Risk-adjusted return
|
||||
double riskAdjustedReturn = maxDD > 0 ? profit / maxDD : 0;
|
||||
|
||||
// Combine metrics
|
||||
double score = riskAdjustedReturn * profitFactor * winRate;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trade event handler |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTrade()
|
||||
{
|
||||
// Handle trade events if needed
|
||||
// (Position state is already updated in OnTick via UpdateBasketState)
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Timer function (if using timer) |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTimer()
|
||||
{
|
||||
// Can be used for periodic tasks independent of ticks
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,735 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DBasketEA_v2.mq5 |
|
||||
//| D-Basket Correlation Hedging EA |
|
||||
//| Version 2.0 - Advanced Optimized |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "D-Basket EA"
|
||||
#property version "2.00"
|
||||
#property description "Three-pair correlation hedging EA v2.0 with Cointegration, Half-Life, and ATR Balancing"
|
||||
#property strict
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Include Files |
|
||||
//+------------------------------------------------------------------+
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "..\Include\DBasket\DBasket_Defines.mqh"
|
||||
#include "..\Include\DBasket\DBasket_Structures.mqh"
|
||||
#include "..\Include\DBasket\DBasket_Logger.mqh"
|
||||
#include "..\Include\DBasket\DBasket_CorrelationEngine.mqh"
|
||||
#include "..\Include\DBasket\DBasket_SignalEngine.mqh"
|
||||
#include "..\Include\DBasket\DBasket_TradeWrapper.mqh"
|
||||
#include "..\Include\DBasket\DBasket_PositionManager.mqh"
|
||||
#include "..\Include\DBasket\DBasket_RiskManager.mqh"
|
||||
// v2.0 Optimization Modules
|
||||
#include "..\Include\DBasket\DBasket_CointegrationEngine.mqh"
|
||||
#include "..\Include\DBasket\DBasket_HalfLifeEngine.mqh"
|
||||
#include "..\Include\DBasket\DBasket_VolatilityBalancer.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Input Parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
// --- Symbol Configuration ---
|
||||
input group "Symbol Settings"
|
||||
input string InpSymbolSuffix = ""; // Symbol suffix (e.g., ".m", "_sb")
|
||||
|
||||
// --- Correlation Engine ---
|
||||
input group "Correlation Engine"
|
||||
input int InpLookbackPeriod = 250; // Lookback period (bars)
|
||||
input int InpCacheUpdateSec = 30; // Cache update interval (seconds)
|
||||
|
||||
// --- Signal Generation ---
|
||||
input group "Signal Generation"
|
||||
input double InpZScoreEntry = 2.5; // Entry Z-Score threshold
|
||||
input double InpZScoreExit = 0.5; // Exit Z-Score threshold
|
||||
input double InpMinCorrelation = 0.75; // Minimum correlation threshold
|
||||
input double InpMaxSpreadPips = 3.0; // Maximum spread (pips per symbol)
|
||||
|
||||
// --- Risk Management ---
|
||||
input group "Risk Management"
|
||||
input double InpFixedLotSize = 0.01; // Fixed lot size per leg
|
||||
input double InpRiskPercent = 1.0; // Risk % per basket (if dynamic sizing)
|
||||
input bool InpUseFixedLots = true; // Use fixed lot size
|
||||
input double InpMaxDrawdownPct = 15.0; // Max drawdown % before halt
|
||||
input double InpDailyLossLimit = 100.0; // Daily loss limit ($)
|
||||
input double InpDailyLossPct = 5.0; // Daily loss limit (%)
|
||||
input int InpMaxHoldingHours = 24; // Maximum basket holding hours
|
||||
input double InpTakeProfitAmount = 10.0; // Take profit per basket ($)
|
||||
input double InpStopLossAmount = 15.0; // Stop loss per basket ($)
|
||||
|
||||
// === v2.0 OPTIMIZATION SETTINGS ===
|
||||
|
||||
// --- Cointegration Settings ---
|
||||
input group "=== Cointegration Filter (v2.0) ==="
|
||||
input bool InpCointEnabled = true; // Enable Cointegration Filter?
|
||||
input double InpCointPValue = 0.05; // P-Value Threshold (0.01-0.10)
|
||||
input int InpCointUpdateBars = 50; // Update Interval (bars)
|
||||
input int InpCointADFLags = 1; // ADF Regression Lags
|
||||
|
||||
// --- Half-Life Settings ---
|
||||
input group "=== Half-Life Exits (v2.0) ==="
|
||||
input bool InpHLEnabled = true; // Enable Half-Life Exits?
|
||||
input int InpHLUpdateBars = 20; // Update Interval (bars)
|
||||
input int InpHLMinValue = 10; // Minimum Half-Life (bars)
|
||||
input int InpHLMaxValue = 500; // Maximum Half-Life (bars)
|
||||
input double InpHLExitMultiplier = 2.0; // Max Hold = Multiplier × HalfLife
|
||||
input double InpHLStopLossSigma = 1.5; // Stop-Loss Distance (sigma)
|
||||
|
||||
// --- ATR Position Sizing ---
|
||||
input group "=== ATR Position Sizing (v2.0) ==="
|
||||
input bool InpATREnabled = true; // Enable ATR Sizing?
|
||||
input int InpATRPeriod = 14; // ATR Period
|
||||
input double InpATRMinWeight = 0.15; // Minimum Weight per Symbol
|
||||
input double InpATRMaxWeight = 0.50; // Maximum Weight per Symbol
|
||||
|
||||
// --- Trading Hours ---
|
||||
input group "Trading Hours"
|
||||
input int InpTradingStartHour = 0; // Trading start hour (broker time)
|
||||
input int InpTradingStartMin = 0; // Trading start minute
|
||||
input int InpTradingEndHour = 23; // Trading end hour (broker time)
|
||||
input int InpTradingEndMin = 59; // Trading end minute
|
||||
input bool InpAvoidRollover = true; // Avoid rollover period
|
||||
|
||||
// --- Technical Settings ---
|
||||
input group "Technical Settings"
|
||||
input int InpMagicNumber = 200000; // Magic number (v2.0)
|
||||
input int InpSlippagePoints = 10; // Maximum slippage (points)
|
||||
input ENUM_LOG_LEVEL InpLogLevel = LOG_LEVEL_INFO; // Log level
|
||||
input bool InpLogToFile = false; // Log to file
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables |
|
||||
//+------------------------------------------------------------------+
|
||||
// Configuration
|
||||
EAConfig g_config;
|
||||
|
||||
// Core modules
|
||||
CCorrelationEngine g_correlationEngine;
|
||||
CSignalEngine g_signalEngine;
|
||||
CTradeWrapper g_tradeWrapper;
|
||||
CPositionManager g_positionManager;
|
||||
CRiskManager g_riskManager;
|
||||
|
||||
// v2.0 Optimization modules
|
||||
CCointegrationEngine g_cointegrationEngine;
|
||||
CHalfLifeEngine g_halfLifeEngine;
|
||||
CVolatilityBalancer g_volatilityBalancer;
|
||||
|
||||
// State
|
||||
bool g_isInitialized = false;
|
||||
bool g_tradingEnabled = true;
|
||||
datetime g_lastTickProcessed = 0;
|
||||
int g_tickCount = 0;
|
||||
int g_barCount = 0;
|
||||
datetime g_lastBarTime = 0;
|
||||
|
||||
// v2.0 basket tracking
|
||||
double g_entryZScore = 0;
|
||||
int g_barsOpenCount = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Build configuration from inputs |
|
||||
//+------------------------------------------------------------------+
|
||||
void BuildConfiguration()
|
||||
{
|
||||
g_config.SetDefaults();
|
||||
|
||||
// Symbol configuration
|
||||
g_config.symbols[SYMBOL_AUDCAD] = DEFAULT_SYMBOL_AUDCAD + InpSymbolSuffix;
|
||||
g_config.symbols[SYMBOL_NZDCAD] = DEFAULT_SYMBOL_NZDCAD + InpSymbolSuffix;
|
||||
g_config.symbols[SYMBOL_AUDNZD] = DEFAULT_SYMBOL_AUDNZD + InpSymbolSuffix;
|
||||
g_config.timeframe = Period();
|
||||
|
||||
// Correlation engine
|
||||
g_config.lookbackPeriod = InpLookbackPeriod;
|
||||
g_config.updateIntervalSeconds = InpCacheUpdateSec;
|
||||
|
||||
// Signal generation
|
||||
g_config.zScoreEntryThreshold = InpZScoreEntry;
|
||||
g_config.zScoreExitThreshold = InpZScoreExit;
|
||||
g_config.minCorrelation = InpMinCorrelation;
|
||||
g_config.maxSpreadPips = InpMaxSpreadPips;
|
||||
|
||||
// Risk management
|
||||
g_config.baseLotSize = InpFixedLotSize;
|
||||
g_config.riskPercentPerBasket = InpRiskPercent;
|
||||
g_config.sizingMode = InpUseFixedLots ? SIZING_FIXED : SIZING_RISK_BASED;
|
||||
g_config.maxDrawdownPercent = InpMaxDrawdownPct;
|
||||
g_config.maxDailyLossPercent = InpDailyLossPct;
|
||||
g_config.maxDailyLossAmount = InpDailyLossLimit;
|
||||
g_config.maxHoldingHours = InpMaxHoldingHours;
|
||||
|
||||
// Trading hours
|
||||
g_config.tradingStartHour = InpTradingStartHour;
|
||||
g_config.tradingStartMinute = InpTradingStartMin;
|
||||
g_config.tradingEndHour = InpTradingEndHour;
|
||||
g_config.tradingEndMinute = InpTradingEndMin;
|
||||
g_config.avoidRollover = InpAvoidRollover;
|
||||
|
||||
// Technical
|
||||
g_config.magicNumber = InpMagicNumber;
|
||||
g_config.slippagePoints = InpSlippagePoints;
|
||||
g_config.logLevel = InpLogLevel;
|
||||
g_config.logToFile = InpLogToFile;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Validate input parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ValidateInputs()
|
||||
{
|
||||
// Lookback period
|
||||
if(InpLookbackPeriod < MIN_LOOKBACK_PERIOD || InpLookbackPeriod > MAX_LOOKBACK_PERIOD)
|
||||
{
|
||||
Logger.Error("Invalid lookback period. Must be " + IntegerToString(MIN_LOOKBACK_PERIOD) +
|
||||
"-" + IntegerToString(MAX_LOOKBACK_PERIOD));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Z-score thresholds
|
||||
if(InpZScoreEntry <= 0 || InpZScoreEntry > 5.0)
|
||||
{
|
||||
Logger.Error("Invalid entry Z-score. Must be 0-5.0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(InpZScoreExit < 0 || InpZScoreExit >= InpZScoreEntry)
|
||||
{
|
||||
Logger.Error("Invalid exit Z-score. Must be 0 to less than entry threshold");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Correlation
|
||||
if(InpMinCorrelation < 0.5 || InpMinCorrelation > 0.95)
|
||||
{
|
||||
Logger.Error("Invalid minimum correlation. Must be 0.5-0.95");
|
||||
return false;
|
||||
}
|
||||
|
||||
// v2.0 Cointegration validation
|
||||
if(InpCointPValue < 0.01 || InpCointPValue > 0.20)
|
||||
{
|
||||
Logger.Error("Invalid cointegration p-value. Must be 0.01-0.20");
|
||||
return false;
|
||||
}
|
||||
|
||||
// v2.0 Half-life validation
|
||||
if(InpHLMinValue < 1 || InpHLMinValue > InpHLMaxValue)
|
||||
{
|
||||
Logger.Error("Invalid half-life range");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Validate trading environment |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ValidateEnvironment()
|
||||
{
|
||||
// Check account type (must be hedging)
|
||||
ENUM_ACCOUNT_MARGIN_MODE marginMode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
|
||||
if(marginMode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
|
||||
{
|
||||
Logger.Error("FATAL: Hedging account required. Current mode: " + EnumToString(marginMode));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if trading allowed
|
||||
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
|
||||
{
|
||||
Logger.Error("Trading is not allowed in terminal settings");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!MQLInfoInteger(MQL_TRADE_ALLOWED))
|
||||
{
|
||||
Logger.Error("Automated trading is not allowed for this EA");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Validate symbols |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ValidateSymbols()
|
||||
{
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
{
|
||||
string symbol = g_config.symbols[i];
|
||||
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Logger.Error("Symbol not available: " + symbol);
|
||||
return false;
|
||||
}
|
||||
|
||||
ENUM_SYMBOL_TRADE_MODE tradeMode = (ENUM_SYMBOL_TRADE_MODE)SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE);
|
||||
if(tradeMode != SYMBOL_TRADE_MODE_FULL)
|
||||
{
|
||||
Logger.Error("Trading not fully allowed on " + symbol);
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger.Info("Symbol validated: " + symbol);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if new bar formed |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime currentBarTime = iTime(g_config.symbols[0], Period(), 0);
|
||||
if(currentBarTime != g_lastBarTime)
|
||||
{
|
||||
g_lastBarTime = currentBarTime;
|
||||
g_barCount++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate spread series for statistical tests |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CalculateSpreadSeries(double &spreadSeries[], double &syntheticRatio[], double &audnzd[], int count)
|
||||
{
|
||||
// Get price data
|
||||
double audcadClose[], nzdcadClose[], audnzdClose[];
|
||||
|
||||
if(CopyClose(g_config.symbols[SYMBOL_AUDCAD], Period(), 0, count, audcadClose) != count)
|
||||
return false;
|
||||
if(CopyClose(g_config.symbols[SYMBOL_NZDCAD], Period(), 0, count, nzdcadClose) != count)
|
||||
return false;
|
||||
if(CopyClose(g_config.symbols[SYMBOL_AUDNZD], Period(), 0, count, audnzdClose) != count)
|
||||
return false;
|
||||
|
||||
// Resize output arrays
|
||||
ArrayResize(spreadSeries, count);
|
||||
ArrayResize(syntheticRatio, count);
|
||||
ArrayResize(audnzd, count);
|
||||
|
||||
// Calculate spread and synthetic ratio
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
syntheticRatio[i] = (nzdcadClose[i] > 0) ? audcadClose[i] / nzdcadClose[i] : 0;
|
||||
audnzd[i] = audnzdClose[i];
|
||||
spreadSeries[i] = audnzdClose[i] - syntheticRatio[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Initialize logger first
|
||||
Logger.Initialize(InpLogLevel, InpLogToFile);
|
||||
|
||||
Logger.Info("=== D-Basket EA v2.0 OPTIMIZED ===");
|
||||
Logger.Info("Features: Cointegration + Half-Life + ATR Balancing");
|
||||
|
||||
// Build configuration from inputs
|
||||
BuildConfiguration();
|
||||
|
||||
// Validate inputs
|
||||
if(!ValidateInputs())
|
||||
return INIT_PARAMETERS_INCORRECT;
|
||||
|
||||
// Validate environment
|
||||
if(!ValidateEnvironment())
|
||||
return INIT_FAILED;
|
||||
|
||||
// Validate symbols
|
||||
if(!ValidateSymbols())
|
||||
return INIT_FAILED;
|
||||
|
||||
// Initialize core modules
|
||||
Logger.Info("Initializing core modules...");
|
||||
|
||||
if(!g_tradeWrapper.Initialize(g_config.magicNumber, g_config.slippagePoints))
|
||||
{
|
||||
Logger.Error("Failed to initialize Trade Wrapper");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
if(!g_correlationEngine.Initialize(g_config.symbols, g_config.lookbackPeriod,
|
||||
g_config.timeframe, g_config.updateIntervalSeconds))
|
||||
{
|
||||
Logger.Error("Failed to initialize Correlation Engine");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
if(!g_signalEngine.Initialize(g_config))
|
||||
{
|
||||
Logger.Error("Failed to initialize Signal Engine");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
if(!g_positionManager.Initialize(g_config, &g_tradeWrapper))
|
||||
{
|
||||
Logger.Error("Failed to initialize Position Manager");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
g_positionManager.SetTPSL(InpTakeProfitAmount, InpStopLossAmount);
|
||||
|
||||
if(!g_riskManager.Initialize(g_config))
|
||||
{
|
||||
Logger.Error("Failed to initialize Risk Manager");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
// === Initialize v2.0 Optimization Modules ===
|
||||
Logger.Info("Initializing v2.0 optimization modules...");
|
||||
|
||||
// Cointegration Engine
|
||||
if(InpCointEnabled)
|
||||
{
|
||||
if(!g_cointegrationEngine.Initialize(InpLookbackPeriod, InpCointPValue,
|
||||
InpCointUpdateBars, InpCointADFLags))
|
||||
{
|
||||
Logger.Error("Failed to initialize Cointegration Engine");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
Logger.Info("Cointegration Filter ENABLED (p < " + DoubleToString(InpCointPValue, 2) + ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("Cointegration Filter DISABLED");
|
||||
}
|
||||
|
||||
// Half-Life Engine
|
||||
if(InpHLEnabled)
|
||||
{
|
||||
if(!g_halfLifeEngine.Initialize(InpLookbackPeriod, InpHLUpdateBars,
|
||||
InpHLMinValue, InpHLMaxValue,
|
||||
InpHLExitMultiplier, InpHLStopLossSigma))
|
||||
{
|
||||
Logger.Error("Failed to initialize Half-Life Engine");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
Logger.Info("Half-Life Exits ENABLED (max hold = " + DoubleToString(InpHLExitMultiplier, 1) + " × halflife)");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("Half-Life Exits DISABLED");
|
||||
}
|
||||
|
||||
// Volatility Balancer
|
||||
if(InpATREnabled)
|
||||
{
|
||||
if(!g_volatilityBalancer.Initialize(g_config.symbols, InpATRPeriod,
|
||||
InpATRMinWeight, InpATRMaxWeight, true))
|
||||
{
|
||||
Logger.Error("Failed to initialize Volatility Balancer");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
Logger.Info("ATR Position Sizing ENABLED (period = " + IntegerToString(InpATRPeriod) + ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("ATR Position Sizing DISABLED");
|
||||
}
|
||||
|
||||
// Recover existing positions
|
||||
g_positionManager.RecoverFromOpenPositions();
|
||||
|
||||
g_isInitialized = true;
|
||||
g_tradingEnabled = true;
|
||||
g_lastBarTime = iTime(g_config.symbols[0], Period(), 0);
|
||||
|
||||
Logger.Info("EA v2.0 initialization complete. Ready for optimized trading.");
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
Logger.Info("EA v2.0 shutdown - Reason: " + IntegerToString(reason));
|
||||
|
||||
// Log final statistics
|
||||
PerformanceMetrics metrics;
|
||||
g_riskManager.GetMetrics(metrics);
|
||||
|
||||
Logger.Info("Final Statistics:");
|
||||
Logger.Info(" Total Baskets: " + IntegerToString(metrics.totalBaskets));
|
||||
Logger.Info(" Closed: " + IntegerToString(metrics.closedBaskets) +
|
||||
" (Win: " + IntegerToString(metrics.winningBaskets) +
|
||||
", Loss: " + IntegerToString(metrics.losingBaskets) + ")");
|
||||
Logger.Info(" Win Rate: " + DoubleToString(metrics.winRate * 100, 1) + "%");
|
||||
Logger.Info(" Realized P/L: $" + DoubleToString(metrics.realizedPL, 2));
|
||||
Logger.Info(" Max Drawdown: " + DoubleToString(metrics.maxDrawdownPercent, 2) + "%");
|
||||
|
||||
Comment("");
|
||||
Logger.Deinitialize();
|
||||
g_isInitialized = false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
if(!g_isInitialized)
|
||||
return;
|
||||
|
||||
g_tickCount++;
|
||||
bool isNewBar = IsNewBar();
|
||||
|
||||
// === Phase 1: Risk Management Check ===
|
||||
string riskReason;
|
||||
if(!g_riskManager.CheckRiskLimits(riskReason))
|
||||
{
|
||||
string emergencyReason;
|
||||
if(g_riskManager.CheckEmergencyExit(emergencyReason))
|
||||
{
|
||||
Logger.Error("EMERGENCY EXIT: " + emergencyReason);
|
||||
if(g_positionManager.HasOpenBasket())
|
||||
g_positionManager.CloseBasket(EXIT_EMERGENCY);
|
||||
}
|
||||
|
||||
g_tradingEnabled = false;
|
||||
if(g_tickCount % 100 == 0)
|
||||
g_riskManager.DisplayMetricsOnChart();
|
||||
return;
|
||||
}
|
||||
|
||||
g_tradingEnabled = true;
|
||||
|
||||
// === Phase 2: Update Price Buffers ===
|
||||
g_correlationEngine.UpdatePriceBuffers();
|
||||
|
||||
// === Phase 3: Update Correlation Cache ===
|
||||
if(!g_correlationEngine.UpdateCorrelationCache())
|
||||
return;
|
||||
|
||||
CorrelationData corrData;
|
||||
g_correlationEngine.GetCorrelationData(corrData);
|
||||
|
||||
// === Phase 4: Update v2.0 Optimization Modules (on new bar) ===
|
||||
if(isNewBar)
|
||||
{
|
||||
// Update basket bar counter if open
|
||||
if(g_positionManager.HasOpenBasket())
|
||||
g_barsOpenCount++;
|
||||
|
||||
// Calculate spread series for statistical tests
|
||||
double spreadSeries[], syntheticRatio[], audnzd[];
|
||||
int dataCount = InpLookbackPeriod;
|
||||
|
||||
if(CalculateSpreadSeries(spreadSeries, syntheticRatio, audnzd, dataCount))
|
||||
{
|
||||
// Update Cointegration
|
||||
if(InpCointEnabled)
|
||||
{
|
||||
g_cointegrationEngine.Update(syntheticRatio, audnzd, dataCount);
|
||||
}
|
||||
|
||||
// Update Half-Life
|
||||
if(InpHLEnabled)
|
||||
{
|
||||
g_halfLifeEngine.Update(spreadSeries, dataCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Update ATR weights
|
||||
if(InpATREnabled)
|
||||
{
|
||||
g_volatilityBalancer.Update();
|
||||
}
|
||||
}
|
||||
|
||||
// === Phase 5: Position Management ===
|
||||
if(g_positionManager.HasOpenBasket())
|
||||
{
|
||||
g_positionManager.UpdateBasketState();
|
||||
|
||||
BasketState basket;
|
||||
g_positionManager.GetBasketState(basket);
|
||||
|
||||
ENUM_EXIT_REASON exitReason = EXIT_NONE;
|
||||
|
||||
// Check standard exit signals first
|
||||
if(g_signalEngine.CheckExitSignal(corrData, basket,
|
||||
g_positionManager.GetTakeProfitAmount(),
|
||||
g_positionManager.GetStopLossAmount(),
|
||||
g_positionManager.GetMaxHoldingHours(),
|
||||
exitReason))
|
||||
{
|
||||
// Standard exit triggered
|
||||
}
|
||||
// v2.0: Check half-life based exits
|
||||
else if(InpHLEnabled && g_halfLifeEngine.IsValid())
|
||||
{
|
||||
// Check time-based exit
|
||||
if(g_halfLifeEngine.IsTimeExitTriggered(g_barsOpenCount))
|
||||
{
|
||||
exitReason = EXIT_MAX_TIME;
|
||||
Logger.Info("Half-Life time exit triggered (bars: " + IntegerToString(g_barsOpenCount) +
|
||||
", max: " + IntegerToString(g_halfLifeEngine.GetMaxHoldingBars()) + ")");
|
||||
}
|
||||
// Check variance-based stop loss
|
||||
else if(g_halfLifeEngine.IsStopLossTriggered(g_entryZScore, corrData.spreadZScore))
|
||||
{
|
||||
exitReason = EXIT_STOP_LOSS;
|
||||
Logger.Info("Half-Life variance stop triggered (entry z: " + DoubleToString(g_entryZScore, 2) +
|
||||
", current z: " + DoubleToString(corrData.spreadZScore, 2) + ")");
|
||||
}
|
||||
}
|
||||
// v2.0: Check cointegration breakdown
|
||||
else if(InpCointEnabled && g_cointegrationEngine.IsValid())
|
||||
{
|
||||
if(!g_cointegrationEngine.IsCointegrated())
|
||||
{
|
||||
// Cointegration broke down - consider exiting
|
||||
if(g_cointegrationEngine.GetPValue() > 0.10)
|
||||
{
|
||||
exitReason = EXIT_CORRELATION_BREAK;
|
||||
Logger.Warning("Cointegration breakdown - p-value: " +
|
||||
DoubleToString(g_cointegrationEngine.GetPValue(), 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute exit if triggered
|
||||
if(exitReason != EXIT_NONE)
|
||||
{
|
||||
double pl = g_positionManager.GetBasketPL();
|
||||
g_positionManager.CloseBasket(exitReason);
|
||||
g_riskManager.RecordBasketClose(pl, pl >= 0);
|
||||
g_barsOpenCount = 0;
|
||||
g_entryZScore = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// === Phase 6: Signal Generation with v2.0 Filters ===
|
||||
string signalFailReason;
|
||||
ENUM_BASKET_SIGNAL signal = SIGNAL_NONE;
|
||||
|
||||
// v2.0 Pre-filter: Check cointegration before expensive signal calculation
|
||||
bool cointValid = true;
|
||||
if(InpCointEnabled)
|
||||
{
|
||||
if(!g_cointegrationEngine.IsCointegrated())
|
||||
{
|
||||
cointValid = false;
|
||||
signalFailReason = "Not cointegrated (p=" +
|
||||
DoubleToString(g_cointegrationEngine.GetPValue(), 2) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
// v2.0 Pre-filter: Check half-life validity
|
||||
bool hlValid = true;
|
||||
if(InpHLEnabled)
|
||||
{
|
||||
if(!g_halfLifeEngine.IsHalfLifeValid())
|
||||
{
|
||||
hlValid = false;
|
||||
if(signalFailReason == "")
|
||||
signalFailReason = "Invalid half-life (" +
|
||||
DoubleToString(g_halfLifeEngine.GetHalfLife(), 1) + " bars)";
|
||||
}
|
||||
}
|
||||
|
||||
// Only check entry signal if v2.0 pre-filters pass
|
||||
if(cointValid && hlValid)
|
||||
{
|
||||
signal = g_signalEngine.CheckEntrySignal(corrData, false, signalFailReason);
|
||||
}
|
||||
|
||||
if(signal != SIGNAL_NONE)
|
||||
{
|
||||
g_riskManager.RecordSignal(true);
|
||||
|
||||
// v2.0: Calculate ATR-weighted lot sizes
|
||||
double lots[NUM_SYMBOLS];
|
||||
if(InpATREnabled && g_volatilityBalancer.IsValid())
|
||||
{
|
||||
g_volatilityBalancer.CalculateWeightedLots(InpFixedLotSize, lots);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
lots[i] = InpFixedLotSize;
|
||||
}
|
||||
|
||||
// Attempt to open basket (using standard method for now)
|
||||
if(g_positionManager.OpenBasket(signal, corrData.spreadZScore, corrData.corrAUDCAD_NZDCAD))
|
||||
{
|
||||
g_riskManager.RecordBasketOpen();
|
||||
g_barsOpenCount = 0;
|
||||
g_entryZScore = corrData.spreadZScore;
|
||||
|
||||
// Log v2.0 entry stats
|
||||
if(InpCointEnabled)
|
||||
Logger.Info("Entry cointegration p-value: " + DoubleToString(g_cointegrationEngine.GetPValue(), 3));
|
||||
if(InpHLEnabled)
|
||||
Logger.Info("Entry half-life: " + DoubleToString(g_halfLifeEngine.GetHalfLife(), 1) +
|
||||
" bars (max hold: " + IntegerToString(g_halfLifeEngine.GetMaxHoldingBars()) + ")");
|
||||
}
|
||||
}
|
||||
else if(signalFailReason != "" && g_tickCount % 1000 == 0)
|
||||
{
|
||||
g_riskManager.RecordSignal(false);
|
||||
Logger.Debug("Signal blocked: " + signalFailReason);
|
||||
}
|
||||
}
|
||||
|
||||
// === Phase 7: Display Update ===
|
||||
if(g_tickCount % 50 == 0)
|
||||
{
|
||||
g_riskManager.DisplayMetricsOnChart();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Tester function for custom optimization criterion |
|
||||
//+------------------------------------------------------------------+
|
||||
double OnTester()
|
||||
{
|
||||
double profit = TesterStatistics(STAT_PROFIT);
|
||||
double maxDD = TesterStatistics(STAT_EQUITY_DD);
|
||||
double profitFactor = TesterStatistics(STAT_PROFIT_FACTOR);
|
||||
int totalTrades = (int)TesterStatistics(STAT_TRADES);
|
||||
int winTrades = (int)TesterStatistics(STAT_PROFIT_TRADES);
|
||||
|
||||
double winRate = totalTrades > 0 ? (double)winTrades / totalTrades : 0;
|
||||
|
||||
// v2.0: Stricter criteria
|
||||
if(winRate < 0.70 || profitFactor < 1.5 || totalTrades < 30)
|
||||
return 0;
|
||||
|
||||
double riskAdjustedReturn = maxDD > 0 ? profit / maxDD : 0;
|
||||
double score = riskAdjustedReturn * profitFactor * winRate;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trade event handler |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTrade()
|
||||
{
|
||||
// Handle trade events if needed
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Timer function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTimer()
|
||||
{
|
||||
// Can be used for periodic tasks independent of ticks
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Reference in New Issue
Block a user