280 lines
8.6 KiB
Plaintext
280 lines
8.6 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| MyProEA.mq5 - Professional Expert Advisor |
|
|
//| Main EA file with clean lifecycle and separation of concerns |
|
|
//+------------------------------------------------------------------+
|
|
|
|
#property copyright "Educational EA - BackTesting Only"
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.00"
|
|
#property strict
|
|
#property description "Professional modular EA with MA crossover strategy"
|
|
#property description "For educational and backtesting purposes only"
|
|
|
|
// Include all class definitions
|
|
#include "Include/Config.mqh"
|
|
#include "Include/Logger.mqh"
|
|
#include "Include/Utilities.mqh"
|
|
#include "Include/MarketData.mqh"
|
|
#include "Include/Signal.mqh"
|
|
#include "Include/Strategy.mqh"
|
|
#include "Include/RiskManager.mqh"
|
|
#include "Include/PositionManager.mqh"
|
|
#include "Include/TradeManager.mqh"
|
|
#include "Include/TrailingStop.mqh"
|
|
|
|
//+------------------------------------------------------------------+
|
|
// Global class instances
|
|
//+------------------------------------------------------------------+
|
|
|
|
CLogger *g_logger = NULL;
|
|
CMarketData *g_market_data = NULL;
|
|
CStrategy *g_strategy = NULL;
|
|
CRiskManager *g_risk_manager = NULL;
|
|
CPositionManager *g_position_manager = NULL;
|
|
CTradeManager *g_trade_manager = NULL;
|
|
CTrailingStop *g_trailing_stop = NULL;
|
|
|
|
// Diagnostics counters for blocked entries
|
|
long g_blocked_existing_position = 0;
|
|
long g_blocked_spread = 0;
|
|
long g_blocked_hours = 0;
|
|
long g_blocked_max_positions = 0;
|
|
|
|
//+------------------------------------------------------------------+
|
|
// EA Initialization
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
// Create logger instance
|
|
g_logger = new CLogger(g_debug_mode);
|
|
g_logger.Info("===== EA INITIALIZATION START =====");
|
|
|
|
// Create market data handler
|
|
g_market_data = new CMarketData(_Symbol, g_logger);
|
|
if(!g_market_data.IsTradingAllowed())
|
|
{
|
|
g_logger.Error("Trading not allowed for this symbol");
|
|
delete g_market_data;
|
|
g_market_data = NULL;
|
|
delete g_logger;
|
|
g_logger = NULL;
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
// Create strategy
|
|
g_strategy = new CStrategy(g_market_data, g_logger);
|
|
if(!g_strategy.Init())
|
|
{
|
|
g_logger.Error("Failed to initialize strategy");
|
|
delete g_strategy;
|
|
g_strategy = NULL;
|
|
delete g_market_data;
|
|
g_market_data = NULL;
|
|
delete g_logger;
|
|
g_logger = NULL;
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
// Create risk manager
|
|
g_risk_manager = new CRiskManager(g_market_data, g_logger);
|
|
|
|
// Create position manager
|
|
g_position_manager = new CPositionManager(g_logger);
|
|
|
|
// Create trade manager
|
|
g_trade_manager = new CTradeManager(g_logger, g_risk_manager);
|
|
|
|
// Create trailing stop manager
|
|
g_trailing_stop = new CTrailingStop(g_market_data, g_logger, g_trade_manager);
|
|
|
|
// Log configuration
|
|
g_logger.Info(StringFormat("Magic Number: %d", g_magic_number));
|
|
g_logger.Info(StringFormat("Symbol: %s", _Symbol));
|
|
g_logger.Info(StringFormat("Chart Timeframe: %s", EnumToString(PERIOD_CURRENT)));
|
|
g_logger.Info(StringFormat("Strategy Evaluation: %s on %s",
|
|
g_evaluate_every_tick ? "Every tick" : (g_evaluate_on_new_bar ? "New bar" : "Every tick"),
|
|
EnumToString(g_strategy_entry_timeframe)));
|
|
g_logger.Info(StringFormat("Lot Mode: %s",
|
|
g_lot_mode == LOT_MODE_FIXED ? "FIXED" : "RISK"));
|
|
g_logger.Info("Initial SL/TP source: Strategy TradeSetup");
|
|
g_logger.Info(StringFormat("Max Positions: %d, Max Spread: %d points",
|
|
g_max_positions, g_max_spread_points));
|
|
|
|
g_logger.Info("===== EA INITIALIZATION COMPLETE =====");
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
// EA Deinitialization
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
g_logger.Info("===== EA DEINITIALIZATION START =====");
|
|
|
|
string reason_text = "";
|
|
switch(reason)
|
|
{
|
|
case REASON_ACCOUNT: reason_text = "Account changed"; break;
|
|
case REASON_CHARTCHANGE: reason_text = "Chart changed"; break;
|
|
case REASON_CHARTCLOSE: reason_text = "Chart closed"; break;
|
|
case REASON_PARAMETERS: reason_text = "Parameters changed"; break;
|
|
case REASON_RECOMPILE: reason_text = "EA recompiled"; break;
|
|
case REASON_REMOVE: reason_text = "EA removed"; break;
|
|
default: reason_text = "Unknown reason"; break;
|
|
}
|
|
|
|
g_logger.Info(StringFormat("Deinit reason: %s (%d)", reason_text, reason));
|
|
|
|
// Clean up strategy (release indicator handles)
|
|
if(g_strategy != NULL)
|
|
{
|
|
delete g_strategy;
|
|
g_strategy = NULL;
|
|
}
|
|
|
|
// Clean up all other objects
|
|
if(g_trailing_stop != NULL)
|
|
{
|
|
delete g_trailing_stop;
|
|
g_trailing_stop = NULL;
|
|
}
|
|
|
|
if(g_trade_manager != NULL)
|
|
{
|
|
delete g_trade_manager;
|
|
g_trade_manager = NULL;
|
|
}
|
|
|
|
if(g_position_manager != NULL)
|
|
{
|
|
delete g_position_manager;
|
|
g_position_manager = NULL;
|
|
}
|
|
|
|
if(g_risk_manager != NULL)
|
|
{
|
|
delete g_risk_manager;
|
|
g_risk_manager = NULL;
|
|
}
|
|
|
|
if(g_market_data != NULL)
|
|
{
|
|
delete g_market_data;
|
|
g_market_data = NULL;
|
|
}
|
|
|
|
if(g_logger != NULL)
|
|
{
|
|
g_logger.Info("===== EA DEINITIALIZATION COMPLETE =====");
|
|
delete g_logger;
|
|
g_logger = NULL;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
// Main EA Logic - OnTick()
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
// Step 1: Update market data (always)
|
|
if(!g_market_data.IsTradingAllowed())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Step 2: Check spread - if spread is too wide, don't trade
|
|
if(!g_risk_manager.IsSpreadAcceptable())
|
|
{
|
|
g_blocked_spread++;
|
|
if(g_debug_mode)
|
|
g_logger.Info(StringFormat("Blocked entry due to spread (count=%d)", g_blocked_spread));
|
|
return;
|
|
}
|
|
|
|
// Step 3: Check trading hours
|
|
if(!g_risk_manager.IsTradingHourValid())
|
|
{
|
|
g_blocked_hours++;
|
|
if(g_debug_mode)
|
|
g_logger.Info(StringFormat("Blocked entry due to trading hours (count=%d)", g_blocked_hours));
|
|
return;
|
|
}
|
|
|
|
// Step 4: Manage existing positions (trailing stop, break-even)
|
|
g_trailing_stop.UpdateAllPositions();
|
|
|
|
// Step 5: Evaluate strategy on configured entry timeframe unless every-tick mode is enabled
|
|
bool should_evaluate_strategy = true;
|
|
if(!g_evaluate_every_tick && g_evaluate_on_new_bar)
|
|
should_evaluate_strategy = g_market_data.IsNewBar(g_strategy_entry_timeframe);
|
|
|
|
if(!should_evaluate_strategy)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Log diagnostics on each new entry bar
|
|
if(g_strategy != NULL)
|
|
g_strategy.LogDiagnostics();
|
|
if(g_logger != NULL && g_debug_mode)
|
|
g_logger.Info(StringFormat("Blocked totals: existing=%d spread=%d hours=%d maxpos=%d", g_blocked_existing_position, g_blocked_spread, g_blocked_hours, g_blocked_max_positions));
|
|
|
|
// Step 6: Get the trade setup from strategy
|
|
TradeSetup setup = g_strategy.GetTradeSetup();
|
|
|
|
if(setup.signal == SIGNAL_NONE)
|
|
{
|
|
if(g_debug_mode && g_logger != NULL)
|
|
g_logger.Info(StringFormat("No trade setup: %s", setup.reason));
|
|
return;
|
|
}
|
|
|
|
// Step 7: Check if we already have a position
|
|
if(g_position_manager.HasOpenPosition(_Symbol))
|
|
{
|
|
g_blocked_existing_position++;
|
|
if(g_debug_mode)
|
|
g_logger.Info(StringFormat("Already have open position, skipping entry (count=%d)", g_blocked_existing_position));
|
|
return;
|
|
}
|
|
|
|
// Step 8: Check if new position is allowed
|
|
if(!g_position_manager.IsNewPositionAllowed(_Symbol))
|
|
{
|
|
g_blocked_max_positions++;
|
|
g_logger.Warning(StringFormat("New position not allowed (max positions reached) (count=%d)", g_blocked_max_positions));
|
|
return;
|
|
}
|
|
|
|
double lot = 0.0;
|
|
if(!g_risk_manager.ValidateTradeSetup(setup, lot))
|
|
{
|
|
if(g_logger)
|
|
g_logger.Warning(StringFormat("Trade setup rejected: %s", setup.reason));
|
|
return;
|
|
}
|
|
|
|
bool trade_success = false;
|
|
|
|
if(setup.signal == SIGNAL_BUY)
|
|
{
|
|
trade_success = g_trade_manager.OpenBuyTrade(_Symbol, lot, setup.stopLoss, setup.takeProfit, setup.entryPrice);
|
|
}
|
|
else if(setup.signal == SIGNAL_SELL)
|
|
{
|
|
trade_success = g_trade_manager.OpenSellTrade(_Symbol, lot, setup.stopLoss, setup.takeProfit, setup.entryPrice);
|
|
}
|
|
|
|
if(trade_success)
|
|
{
|
|
if(g_logger)
|
|
g_logger.Info("Trade executed successfully");
|
|
}
|
|
else
|
|
{
|
|
if(g_logger)
|
|
g_logger.Error("Trade execution failed");
|
|
}
|
|
}
|
|
|