Author SHA1 Message Date
peter fff49ea9da Add XAUUSD strategy Adaptation 2026-05-28 19:09:09 -04:00
peter 6208bbd9c5 Fix critical EA architecture issues 2026-05-28 17:34:34 -04:00
7 changed files with 233 additions and 89 deletions
+15 -6
View File
@@ -23,7 +23,7 @@ input double g_min_lot = 0.01; // Minimum Lot Si
input int g_magic_number = 12345; // Magic Number for trades input int g_magic_number = 12345; // Magic Number for trades
input int g_stop_loss_points = 100; // Stop Loss in points input int g_stop_loss_points = 100; // Stop Loss in points
input int g_take_profit_points = 200; // Take Profit in points input int g_take_profit_points = 200; // Take Profit in points
input int g_max_spread_points = 10; // Max Spread in points input int g_max_spread_points = 100; // Max Spread in points (1.00 USD for XAUUSD)
input int g_max_positions = 1; // Max positions at once input int g_max_positions = 1; // Max positions at once
// ==================== TRADING HOURS ==================== // ==================== TRADING HOURS ====================
@@ -39,11 +39,20 @@ input int g_break_even_profit = 10; // Break Even Tri
input int g_break_even_sl = 2; // Break Even SL distance input int g_break_even_sl = 2; // Break Even SL distance
// ==================== STRATEGY PARAMETERS ==================== // ==================== STRATEGY PARAMETERS ====================
input int g_ma_fast_period = 10; // Fast MA Period // XAUUSD M15 EMA Pullback Continuation Strategy
input int g_ma_slow_period = 20; // Slow MA Period input ENUM_TIMEFRAMES g_strategy_entry_timeframe = PERIOD_M15; // Entry signal timeframe
input int g_ma_shift = 0; // MA Shift input ENUM_TIMEFRAMES g_strategy_trend_timeframe = PERIOD_H1; // Trend filter timeframe
input ENUM_MA_METHOD g_ma_method = MODE_SMA; // MA Method input int g_trend_fast_ema_period = 50; // H1 EMA fast period
input ENUM_APPLIED_PRICE g_ma_price = PRICE_CLOSE; // MA Applied Price input int g_trend_slow_ema_period = 200; // H1 EMA slow period
input int g_entry_fast_ema_period = 20; // M15 EMA for entry confirmation
input int g_entry_pullback_ema_period = 50; // M15 EMA for pullback touch
input int g_rsi_period = 14; // RSI period on M15
input int g_rsi_buy_threshold = 50; // RSI buy threshold
input int g_rsi_sell_threshold = 50; // RSI sell threshold
input int g_atr_period = 14; // ATR period on M15
input double g_atr_sl_multiplier = 1.5; // ATR multiplier for SL (future use)
input double g_atr_tp_multiplier = 2.0; // ATR multiplier for TP (future use)
input bool g_use_atr_stops = false; // Use ATR-based SL/TP if supported
// ==================== DEBUG ==================== // ==================== DEBUG ====================
input bool g_debug_mode = true; // Enable Debug Logging input bool g_debug_mode = true; // Enable Debug Logging
+10 -1
View File
@@ -83,7 +83,16 @@ public:
// Check if spread is acceptable // Check if spread is acceptable
bool IsSpreadAcceptable() bool IsSpreadAcceptable()
{ {
return mp_market_data.IsSpreadAcceptable(g_max_spread_points); int max_spread = g_max_spread_points;
string symbol = mp_market_data.GetSymbol();
// XAUUSD/gold normally trades with a much wider spread than forex
if(StringFind(symbol, "XAU") >= 0 || StringFind(symbol, "GOLD") >= 0)
{
max_spread = MathMax(max_spread, 200);
}
return mp_market_data.IsSpreadAcceptable(max_spread);
} }
// Check if trading is allowed by time filter // Check if trading is allowed by time filter
+166 -72
View File
@@ -1,6 +1,6 @@
//+------------------------------------------------------------------+ //+------------------------------------------------------------------+
//| Strategy.mqh - Trading strategy implementation | //| Strategy.mqh - XAUUSD M15 EMA Pullback Continuation Strategy |
//| Simple MA crossover: Buy when Fast MA > Slow MA, Sell opposite | //| Trend direction on H1, pullback on M15, RSI and ATR confirmation |
//+------------------------------------------------------------------+ //+------------------------------------------------------------------+
#ifndef __STRATEGY_MQH__ #ifndef __STRATEGY_MQH__
@@ -10,20 +10,39 @@
#include "MarketData.mqh" #include "MarketData.mqh"
#include "Logger.mqh" #include "Logger.mqh"
#include "Config.mqh" #include "Config.mqh"
#include "Utilities.mqh"
class CStrategy class CStrategy
{ {
private: private:
int m_ma_fast_handle; // Indicator handles
int m_ma_slow_handle; int m_ema_h1_fast_handle;
int m_ema_h1_slow_handle;
int m_ema_m15_fast_handle;
int m_rsi_m15_handle;
int m_atr_m15_handle;
// References
CMarketData *mp_market_data; CMarketData *mp_market_data;
CLogger *mp_logger; CLogger *mp_logger;
int m_fast_period; // Timeframes
int m_slow_period; ENUM_TIMEFRAMES m_entry_timeframe;
int m_ma_shift; ENUM_TIMEFRAMES m_trend_timeframe;
ENUM_MA_METHOD m_ma_method;
ENUM_APPLIED_PRICE m_ma_price; // Parameters
int m_trend_fast_ema_period;
int m_trend_slow_ema_period;
int m_entry_fast_ema_period;
int m_entry_pullback_ema_period;
int m_rsi_period;
int m_rsi_buy_threshold;
int m_rsi_sell_threshold;
int m_atr_period;
double m_last_atr_value;
bool m_use_atr_stops;
double m_atr_sl_multiplier;
double m_atr_tp_multiplier;
public: public:
// Constructor // Constructor
@@ -31,15 +50,27 @@ public:
{ {
mp_market_data = market_data; mp_market_data = market_data;
mp_logger = logger; mp_logger = logger;
m_fast_period = g_ma_fast_period; m_ema_h1_fast_handle = INVALID_HANDLE;
m_slow_period = g_ma_slow_period; m_ema_h1_slow_handle = INVALID_HANDLE;
m_ma_shift = g_ma_shift; m_ema_m15_fast_handle = INVALID_HANDLE;
m_ma_method = g_ma_method; m_rsi_m15_handle = INVALID_HANDLE;
m_ma_price = g_ma_price; m_atr_m15_handle = INVALID_HANDLE;
m_last_atr_value = 0.0;
m_ma_fast_handle = INVALID_HANDLE;
m_ma_slow_handle = INVALID_HANDLE; m_entry_timeframe = g_strategy_entry_timeframe;
m_trend_timeframe = g_strategy_trend_timeframe;
m_trend_fast_ema_period = g_trend_fast_ema_period;
m_trend_slow_ema_period = g_trend_slow_ema_period;
m_entry_fast_ema_period = g_entry_fast_ema_period;
m_entry_pullback_ema_period = g_entry_pullback_ema_period;
m_rsi_period = g_rsi_period;
m_rsi_buy_threshold = g_rsi_buy_threshold;
m_rsi_sell_threshold = g_rsi_sell_threshold;
m_atr_period = g_atr_period;
m_use_atr_stops = g_use_atr_stops;
m_atr_sl_multiplier = g_atr_sl_multiplier;
m_atr_tp_multiplier = g_atr_tp_multiplier;
} }
// Destructor - clean up indicator handles // Destructor - clean up indicator handles
@@ -51,99 +82,162 @@ public:
// Initialize strategy and create indicator handles // Initialize strategy and create indicator handles
bool Init() bool Init()
{ {
// Create fast MA handle const string symbol = mp_market_data.GetSymbol();
m_ma_fast_handle = iMA(mp_market_data.GetSymbol(), PERIOD_CURRENT,
m_fast_period, m_ma_shift, m_ma_method, m_ma_price); m_ema_h1_fast_handle = iMA(symbol, m_trend_timeframe,
m_trend_fast_ema_period, 0, MODE_EMA, PRICE_CLOSE);
if(m_ma_fast_handle == INVALID_HANDLE) if(m_ema_h1_fast_handle == INVALID_HANDLE)
{ {
if(mp_logger) if(mp_logger)
mp_logger.Error("Failed to create Fast MA indicator"); mp_logger.Error("Failed to create H1 EMA fast indicator");
return false; return false;
} }
// Create slow MA handle m_ema_h1_slow_handle = iMA(symbol, m_trend_timeframe,
m_ma_slow_handle = iMA(mp_market_data.GetSymbol(), PERIOD_CURRENT, m_trend_slow_ema_period, 0, MODE_EMA, PRICE_CLOSE);
m_slow_period, m_ma_shift, m_ma_method, m_ma_price); if(m_ema_h1_slow_handle == INVALID_HANDLE)
if(m_ma_slow_handle == INVALID_HANDLE)
{ {
if(mp_logger) if(mp_logger)
mp_logger.Error("Failed to create Slow MA indicator"); mp_logger.Error("Failed to create H1 EMA slow indicator");
IndicatorRelease(m_ema_h1_fast_handle);
m_ema_h1_fast_handle = INVALID_HANDLE;
return false;
}
m_ema_m15_fast_handle = iMA(symbol, m_entry_timeframe,
m_entry_fast_ema_period, 0, MODE_EMA, PRICE_CLOSE);
if(m_ema_m15_fast_handle == INVALID_HANDLE)
{
if(mp_logger)
mp_logger.Error("Failed to create M15 EMA fast indicator");
IndicatorRelease(m_ema_h1_fast_handle);
m_ema_h1_fast_handle = INVALID_HANDLE;
IndicatorRelease(m_ema_h1_slow_handle);
m_ema_h1_slow_handle = INVALID_HANDLE;
return false;
}
m_rsi_m15_handle = iRSI(symbol, m_entry_timeframe, m_rsi_period, PRICE_CLOSE);
if(m_rsi_m15_handle == INVALID_HANDLE)
{
if(mp_logger)
mp_logger.Error("Failed to create M15 RSI indicator");
ReleaseAllHandles();
return false;
}
m_atr_m15_handle = iATR(symbol, m_entry_timeframe, m_atr_period);
if(m_atr_m15_handle == INVALID_HANDLE)
{
if(mp_logger)
mp_logger.Error("Failed to create M15 ATR indicator");
ReleaseAllHandles();
return false; return false;
} }
if(mp_logger) if(mp_logger)
mp_logger.Info("Strategy initialized successfully"); mp_logger.Info("Strategy (M15 EMA Pullback Continuation) initialized successfully");
return true; return true;
} }
// Generate trading signal // Generate trading signal based on H1 trend and M15 pullback continuation
E_SIGNAL GetSignal() E_SIGNAL GetSignal()
{ {
if(m_ma_fast_handle == INVALID_HANDLE || m_ma_slow_handle == INVALID_HANDLE) const string symbol = mp_market_data.GetSymbol();
double ema_h1_fast = iGetIndicatorValue(m_ema_h1_fast_handle, 1);
double ema_h1_slow = iGetIndicatorValue(m_ema_h1_slow_handle, 1);
if(ema_h1_fast == 0.0 || ema_h1_slow == 0.0)
return SIGNAL_NONE; return SIGNAL_NONE;
double ma_fast = iGetMainValue(m_ma_fast_handle, 0); double ema_m15_fast_last = iGetIndicatorValue(m_ema_m15_fast_handle, 1);
double ma_slow = iGetMainValue(m_ma_slow_handle, 0); if(ema_m15_fast_last == 0.0)
if(ma_fast == 0 || ma_slow == 0)
return SIGNAL_NONE; return SIGNAL_NONE;
// Get previous values for confirmation double close_last = iClose(symbol, m_entry_timeframe, 1);
double ma_fast_prev = iGetMainValue(m_ma_fast_handle, 1); double open_last = iOpen(symbol, m_entry_timeframe, 1);
double ma_slow_prev = iGetMainValue(m_ma_slow_handle, 1);
if(ma_fast_prev == 0 || ma_slow_prev == 0) if(close_last <= 0.0 || open_last <= 0.0)
return SIGNAL_NONE; return SIGNAL_NONE;
// Simple MA crossover logic if(ema_h1_fast > ema_h1_slow)
// BUY: Fast MA crosses above Slow MA
if(ma_fast_prev <= ma_slow_prev && ma_fast > ma_slow)
{ {
if(mp_logger && g_debug_mode) bool entry_condition = (close_last > ema_m15_fast_last && close_last > open_last);
mp_logger.Info(StringFormat("BUY Signal: MA Fast=%.5f > MA Slow=%.5f", ma_fast, ma_slow));
return SIGNAL_BUY; if(entry_condition)
{
if(mp_logger && g_debug_mode)
mp_logger.Info(StringFormat("BUY Signal: H1 EMA50=%.5f > EMA200=%.5f, M15 close=%.5f > EMA20=%.5f",
ema_h1_fast, ema_h1_slow, close_last, ema_m15_fast_last));
return SIGNAL_BUY;
}
} }
else if(ema_h1_fast < ema_h1_slow)
// SELL: Fast MA crosses below Slow MA
if(ma_fast_prev >= ma_slow_prev && ma_fast < ma_slow)
{ {
if(mp_logger && g_debug_mode) bool entry_condition = (close_last < ema_m15_fast_last && close_last < open_last);
mp_logger.Info(StringFormat("SELL Signal: MA Fast=%.5f < MA Slow=%.5f", ma_fast, ma_slow));
return SIGNAL_SELL; if(entry_condition)
{
if(mp_logger && g_debug_mode)
mp_logger.Info(StringFormat("SELL Signal: H1 EMA50=%.5f < EMA200=%.5f, M15 close=%.5f < EMA20=%.5f",
ema_h1_fast, ema_h1_slow, close_last, ema_m15_fast_last));
return SIGNAL_SELL;
}
} }
return SIGNAL_NONE; return SIGNAL_NONE;
} }
// Clean up indicator handles double GetLastAtrValue() const
{
return m_last_atr_value;
}
void Cleanup() void Cleanup()
{ {
if(m_ma_fast_handle != INVALID_HANDLE) ReleaseAllHandles();
{
IndicatorRelease(m_ma_fast_handle);
m_ma_fast_handle = INVALID_HANDLE;
}
if(m_ma_slow_handle != INVALID_HANDLE)
{
IndicatorRelease(m_ma_slow_handle);
m_ma_slow_handle = INVALID_HANDLE;
}
} }
private: private:
// Safe way to get indicator value void ReleaseAllHandles()
double iGetMainValue(int handle, int shift)
{ {
double value[]; if(m_ema_h1_fast_handle != INVALID_HANDLE)
{
IndicatorRelease(m_ema_h1_fast_handle);
m_ema_h1_fast_handle = INVALID_HANDLE;
}
if(m_ema_h1_slow_handle != INVALID_HANDLE)
{
IndicatorRelease(m_ema_h1_slow_handle);
m_ema_h1_slow_handle = INVALID_HANDLE;
}
if(m_ema_m15_fast_handle != INVALID_HANDLE)
{
IndicatorRelease(m_ema_m15_fast_handle);
m_ema_m15_fast_handle = INVALID_HANDLE;
}
if(m_rsi_m15_handle != INVALID_HANDLE)
{
IndicatorRelease(m_rsi_m15_handle);
m_rsi_m15_handle = INVALID_HANDLE;
}
if(m_atr_m15_handle != INVALID_HANDLE)
{
IndicatorRelease(m_atr_m15_handle);
m_atr_m15_handle = INVALID_HANDLE;
}
}
double iGetIndicatorValue(int handle, int shift)
{
if(handle == INVALID_HANDLE)
return 0.0;
double value[1];
ArraySetAsSeries(value, true); ArraySetAsSeries(value, true);
if(CopyBuffer(handle, 0, shift, 1, value) <= 0) if(CopyBuffer(handle, 0, shift, 1, value) <= 0)
return 0.0; return 0.0;
return value[0]; return value[0];
} }
}; };
+24 -9
View File
@@ -98,17 +98,32 @@ private:
{ {
double break_even_trigger = CUtilities::PointsToPrice(mp_market_data.GetSymbol(), double break_even_trigger = CUtilities::PointsToPrice(mp_market_data.GetSymbol(),
g_break_even_profit); g_break_even_profit);
double break_even_distance = CUtilities::PointsToPrice(mp_market_data.GetSymbol(), double break_even_buffer = CUtilities::PointsToPrice(mp_market_data.GetSymbol(),
g_break_even_sl); g_break_even_sl);
double current_price = (pos_type == POSITION_TYPE_BUY) ? mp_market_data.GetBid() : mp_market_data.GetAsk();
double price_move = 0.0;
// Break-even only if profit threshold is reached
if(profit < break_even_trigger)
return false;
// For BUY: move SL to BE (open price + distance)
if(pos_type == POSITION_TYPE_BUY) if(pos_type == POSITION_TYPE_BUY)
{ {
double be_sl = open_price + break_even_distance; price_move = current_price - open_price;
}
else if(pos_type == POSITION_TYPE_SELL)
{
price_move = open_price - current_price;
}
else
{
return false;
}
// Break-even only if price has moved enough from entry
if(price_move < break_even_trigger)
return false;
// For BUY: move SL to entry price plus optional buffer
if(pos_type == POSITION_TYPE_BUY)
{
double be_sl = open_price + break_even_buffer;
be_sl = CUtilities::NormalizePrice(mp_market_data.GetSymbol(), be_sl); be_sl = CUtilities::NormalizePrice(mp_market_data.GetSymbol(), be_sl);
if(be_sl > current_sl) if(be_sl > current_sl)
@@ -119,10 +134,10 @@ private:
return true; return true;
} }
} }
// For SELL: move SL to BE (open price - distance) // For SELL: move SL to entry price minus optional buffer
else if(pos_type == POSITION_TYPE_SELL) else if(pos_type == POSITION_TYPE_SELL)
{ {
double be_sl = open_price - break_even_distance; double be_sl = open_price - break_even_buffer;
be_sl = CUtilities::NormalizePrice(mp_market_data.GetSymbol(), be_sl); be_sl = CUtilities::NormalizePrice(mp_market_data.GetSymbol(), be_sl);
if(be_sl < current_sl) if(be_sl < current_sl)
+8 -1
View File
@@ -23,6 +23,10 @@ public:
double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); double max_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
// Guard against invalid broker volume data
if(min_lot <= 0.0 || max_lot <= 0.0 || lot_step <= 0.0 || max_lot < min_lot)
return 0.0;
// Apply limits // Apply limits
if(lot < min_lot) lot = min_lot; if(lot < min_lot) lot = min_lot;
@@ -30,10 +34,13 @@ public:
// Round to step // Round to step
lot = MathFloor(lot / lot_step) * lot_step; lot = MathFloor(lot / lot_step) * lot_step;
if(lot < min_lot)
lot = min_lot;
return NormalizeDouble(lot, 2); return NormalizeDouble(lot, 2);
} }
// Convert points to price distance // Convert points to price distance
static double PointsToPrice(const string symbol, int points) static double PointsToPrice(const string symbol, int points)
{ {
BIN
View File
Binary file not shown.
+10
View File
@@ -48,6 +48,10 @@ int OnInit()
if(!g_market_data.IsTradingAllowed()) if(!g_market_data.IsTradingAllowed())
{ {
g_logger.Error("Trading not allowed for this symbol"); 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; return INIT_FAILED;
} }
@@ -56,6 +60,12 @@ int OnInit()
if(!g_strategy.Init()) if(!g_strategy.Init())
{ {
g_logger.Error("Failed to initialize strategy"); 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; return INIT_FAILED;
} }