2 Commits

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_stop_loss_points = 100; // Stop Loss 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
// ==================== 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
// ==================== STRATEGY PARAMETERS ====================
input int g_ma_fast_period = 10; // Fast MA Period
input int g_ma_slow_period = 20; // Slow MA Period
input int g_ma_shift = 0; // MA Shift
input ENUM_MA_METHOD g_ma_method = MODE_SMA; // MA Method
input ENUM_APPLIED_PRICE g_ma_price = PRICE_CLOSE; // MA Applied Price
// XAUUSD M15 EMA Pullback Continuation Strategy
input ENUM_TIMEFRAMES g_strategy_entry_timeframe = PERIOD_M15; // Entry signal timeframe
input ENUM_TIMEFRAMES g_strategy_trend_timeframe = PERIOD_H1; // Trend filter timeframe
input int g_trend_fast_ema_period = 50; // H1 EMA fast period
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 ====================
input bool g_debug_mode = true; // Enable Debug Logging
+10 -1
View File
@@ -83,7 +83,16 @@ public:
// Check if spread is acceptable
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
+166 -72
View File
@@ -1,6 +1,6 @@
//+------------------------------------------------------------------+
//| Strategy.mqh - Trading strategy implementation |
//| Simple MA crossover: Buy when Fast MA > Slow MA, Sell opposite |
//| Strategy.mqh - XAUUSD M15 EMA Pullback Continuation Strategy |
//| Trend direction on H1, pullback on M15, RSI and ATR confirmation |
//+------------------------------------------------------------------+
#ifndef __STRATEGY_MQH__
@@ -10,20 +10,39 @@
#include "MarketData.mqh"
#include "Logger.mqh"
#include "Config.mqh"
#include "Utilities.mqh"
class CStrategy
{
private:
int m_ma_fast_handle;
int m_ma_slow_handle;
// Indicator handles
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;
CLogger *mp_logger;
int m_fast_period;
int m_slow_period;
int m_ma_shift;
ENUM_MA_METHOD m_ma_method;
ENUM_APPLIED_PRICE m_ma_price;
// Timeframes
ENUM_TIMEFRAMES m_entry_timeframe;
ENUM_TIMEFRAMES m_trend_timeframe;
// 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:
// Constructor
@@ -31,15 +50,27 @@ public:
{
mp_market_data = market_data;
mp_logger = logger;
m_fast_period = g_ma_fast_period;
m_slow_period = g_ma_slow_period;
m_ma_shift = g_ma_shift;
m_ma_method = g_ma_method;
m_ma_price = g_ma_price;
m_ma_fast_handle = INVALID_HANDLE;
m_ma_slow_handle = INVALID_HANDLE;
m_ema_h1_fast_handle = INVALID_HANDLE;
m_ema_h1_slow_handle = INVALID_HANDLE;
m_ema_m15_fast_handle = INVALID_HANDLE;
m_rsi_m15_handle = INVALID_HANDLE;
m_atr_m15_handle = INVALID_HANDLE;
m_last_atr_value = 0.0;
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
@@ -51,99 +82,162 @@ public:
// Initialize strategy and create indicator handles
bool Init()
{
// Create fast MA handle
m_ma_fast_handle = iMA(mp_market_data.GetSymbol(), PERIOD_CURRENT,
m_fast_period, m_ma_shift, m_ma_method, m_ma_price);
if(m_ma_fast_handle == INVALID_HANDLE)
const string symbol = mp_market_data.GetSymbol();
m_ema_h1_fast_handle = iMA(symbol, m_trend_timeframe,
m_trend_fast_ema_period, 0, MODE_EMA, PRICE_CLOSE);
if(m_ema_h1_fast_handle == INVALID_HANDLE)
{
if(mp_logger)
mp_logger.Error("Failed to create Fast MA indicator");
mp_logger.Error("Failed to create H1 EMA fast indicator");
return false;
}
// Create slow MA handle
m_ma_slow_handle = iMA(mp_market_data.GetSymbol(), PERIOD_CURRENT,
m_slow_period, m_ma_shift, m_ma_method, m_ma_price);
if(m_ma_slow_handle == INVALID_HANDLE)
m_ema_h1_slow_handle = iMA(symbol, m_trend_timeframe,
m_trend_slow_ema_period, 0, MODE_EMA, PRICE_CLOSE);
if(m_ema_h1_slow_handle == INVALID_HANDLE)
{
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;
}
if(mp_logger)
mp_logger.Info("Strategy initialized successfully");
mp_logger.Info("Strategy (M15 EMA Pullback Continuation) initialized successfully");
return true;
}
// Generate trading signal
// Generate trading signal based on H1 trend and M15 pullback continuation
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;
double ma_fast = iGetMainValue(m_ma_fast_handle, 0);
double ma_slow = iGetMainValue(m_ma_slow_handle, 0);
if(ma_fast == 0 || ma_slow == 0)
double ema_m15_fast_last = iGetIndicatorValue(m_ema_m15_fast_handle, 1);
if(ema_m15_fast_last == 0.0)
return SIGNAL_NONE;
// Get previous values for confirmation
double ma_fast_prev = iGetMainValue(m_ma_fast_handle, 1);
double ma_slow_prev = iGetMainValue(m_ma_slow_handle, 1);
double close_last = iClose(symbol, m_entry_timeframe, 1);
double open_last = iOpen(symbol, m_entry_timeframe, 1);
if(ma_fast_prev == 0 || ma_slow_prev == 0)
if(close_last <= 0.0 || open_last <= 0.0)
return SIGNAL_NONE;
// Simple MA crossover logic
// BUY: Fast MA crosses above Slow MA
if(ma_fast_prev <= ma_slow_prev && ma_fast > ma_slow)
if(ema_h1_fast > ema_h1_slow)
{
if(mp_logger && g_debug_mode)
mp_logger.Info(StringFormat("BUY Signal: MA Fast=%.5f > MA Slow=%.5f", ma_fast, ma_slow));
return SIGNAL_BUY;
bool entry_condition = (close_last > ema_m15_fast_last && close_last > open_last);
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;
}
}
// SELL: Fast MA crosses below Slow MA
if(ma_fast_prev >= ma_slow_prev && ma_fast < ma_slow)
else if(ema_h1_fast < ema_h1_slow)
{
if(mp_logger && g_debug_mode)
mp_logger.Info(StringFormat("SELL Signal: MA Fast=%.5f < MA Slow=%.5f", ma_fast, ma_slow));
return SIGNAL_SELL;
bool entry_condition = (close_last < ema_m15_fast_last && close_last < open_last);
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;
}
// Clean up indicator handles
double GetLastAtrValue() const
{
return m_last_atr_value;
}
void Cleanup()
{
if(m_ma_fast_handle != INVALID_HANDLE)
{
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;
}
ReleaseAllHandles();
}
private:
// Safe way to get indicator value
double iGetMainValue(int handle, int shift)
void ReleaseAllHandles()
{
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);
if(CopyBuffer(handle, 0, shift, 1, value) <= 0)
return 0.0;
return value[0];
}
};
+24 -9
View File
@@ -98,17 +98,32 @@ private:
{
double break_even_trigger = CUtilities::PointsToPrice(mp_market_data.GetSymbol(),
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);
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)
{
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);
if(be_sl > current_sl)
@@ -119,10 +134,10 @@ private:
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)
{
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);
if(be_sl < current_sl)
+8 -1
View File
@@ -23,6 +23,10 @@ public:
double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
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
if(lot < min_lot) lot = min_lot;
@@ -30,10 +34,13 @@ public:
// Round to step
lot = MathFloor(lot / lot_step) * lot_step;
if(lot < min_lot)
lot = min_lot;
return NormalizeDouble(lot, 2);
}
// Convert points to price distance
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())
{
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;
}
@@ -56,6 +60,12 @@ int OnInit()
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;
}