Initial MQL5 EA Project
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Config.mqh - Global configuration and input parameters |
|
||||
//| Central place for all EA settings |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#ifndef __CONFIG_MQH__
|
||||
#define __CONFIG_MQH__
|
||||
|
||||
// ==================== LOT SIZING ====================
|
||||
enum E_LOT_MODE
|
||||
{
|
||||
LOT_MODE_FIXED = 0, // Use fixed lot size
|
||||
LOT_MODE_RISK = 1 // Use risk percent method
|
||||
};
|
||||
|
||||
input E_LOT_MODE g_lot_mode = LOT_MODE_FIXED; // Lot Sizing Mode
|
||||
input double g_fixed_lot = 0.1; // Fixed Lot Size (if LOT_MODE_FIXED)
|
||||
input double g_risk_percent = 2.0; // Risk Percent (if LOT_MODE_RISK)
|
||||
input double g_max_lot = 10.0; // Maximum Lot Size
|
||||
input double g_min_lot = 0.01; // Minimum Lot Size
|
||||
|
||||
// ==================== TRADE MANAGEMENT ====================
|
||||
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_positions = 1; // Max positions at once
|
||||
|
||||
// ==================== TRADING HOURS ====================
|
||||
input bool g_use_trading_hours = false; // Enable trading hour filter
|
||||
input int g_trade_start_hour = 8; // Trading Start Hour (0-23)
|
||||
input int g_trade_end_hour = 20; // Trading End Hour (0-23)
|
||||
|
||||
// ==================== TRAILING STOP ====================
|
||||
input bool g_use_trailing_stop = true; // Use Trailing Stop
|
||||
input int g_trailing_stop_points= 50; // Trailing Stop Distance
|
||||
input bool g_use_break_even = true; // Use Break Even
|
||||
input int g_break_even_profit = 10; // Break Even Trigger profit
|
||||
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
|
||||
|
||||
// ==================== DEBUG ====================
|
||||
input bool g_debug_mode = true; // Enable Debug Logging
|
||||
|
||||
#endif //__CONFIG_MQH__
|
||||
@@ -0,0 +1,74 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Logger.mqh - Simple logging system |
|
||||
//| Provides info, warning, and error logging with formatting |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#ifndef __LOGGER_MQH__
|
||||
#define __LOGGER_MQH__
|
||||
|
||||
#include "Config.mqh"
|
||||
|
||||
class CLogger
|
||||
{
|
||||
private:
|
||||
bool m_debug_mode;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
CLogger(bool debug_mode = true)
|
||||
{
|
||||
m_debug_mode = debug_mode;
|
||||
}
|
||||
|
||||
// Log info level message
|
||||
void Info(const string message)
|
||||
{
|
||||
if(m_debug_mode)
|
||||
PrintFormat("[INFO] %s", message);
|
||||
}
|
||||
|
||||
// Log warning level message
|
||||
void Warning(const string message)
|
||||
{
|
||||
PrintFormat("[WARNING] %s", message);
|
||||
}
|
||||
|
||||
// Log error level message
|
||||
void Error(const string message)
|
||||
{
|
||||
PrintFormat("[ERROR] %s", message);
|
||||
}
|
||||
|
||||
// Log formatted message (info level)
|
||||
// Note: MQL5 does not support user-defined variadic functions, so
|
||||
// pass an already-formatted string to this method.
|
||||
void InfoFormat(const string formatted)
|
||||
{
|
||||
if(m_debug_mode)
|
||||
{
|
||||
PrintFormat("[INFO] %s", formatted);
|
||||
}
|
||||
}
|
||||
|
||||
// Log formatted message (error level)
|
||||
// Note: MQL5 does not support user-defined variadic functions, so
|
||||
// pass an already-formatted string to this method.
|
||||
void ErrorFormat(const string formatted)
|
||||
{
|
||||
PrintFormat("[ERROR] %s", formatted);
|
||||
}
|
||||
|
||||
// Set debug mode
|
||||
void SetDebugMode(bool debug_mode)
|
||||
{
|
||||
m_debug_mode = debug_mode;
|
||||
}
|
||||
|
||||
// Get debug mode
|
||||
bool GetDebugMode() const
|
||||
{
|
||||
return m_debug_mode;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__LOGGER_MQH__
|
||||
@@ -0,0 +1,186 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MarketData.mqh - Market data wrapper |
|
||||
//| Handles bid/ask, spread, symbol info, new bar detection |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#ifndef __MARKETDATA_MQH__
|
||||
#define __MARKETDATA_MQH__
|
||||
|
||||
#include "Config.mqh"
|
||||
#include "Logger.mqh"
|
||||
#include "Utilities.mqh"
|
||||
|
||||
class CMarketData
|
||||
{
|
||||
private:
|
||||
string m_symbol;
|
||||
CLogger *mp_logger;
|
||||
datetime m_last_bar_time;
|
||||
int m_digits;
|
||||
double m_point;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
CMarketData(const string symbol, CLogger *logger)
|
||||
{
|
||||
m_symbol = symbol;
|
||||
mp_logger = logger;
|
||||
m_last_bar_time = 0;
|
||||
m_digits = CUtilities::GetDigits(symbol);
|
||||
m_point = CUtilities::GetPoint(symbol);
|
||||
}
|
||||
|
||||
// Get current bid price
|
||||
double GetBid() const
|
||||
{
|
||||
return SymbolInfoDouble(m_symbol, SYMBOL_BID);
|
||||
}
|
||||
|
||||
// Get current ask price
|
||||
double GetAsk() const
|
||||
{
|
||||
return SymbolInfoDouble(m_symbol, SYMBOL_ASK);
|
||||
}
|
||||
|
||||
// Get current spread in points
|
||||
int GetSpreadPoints() const
|
||||
{
|
||||
double spread_price = GetAsk() - GetBid();
|
||||
return CUtilities::PriceToPoints(m_symbol, spread_price);
|
||||
}
|
||||
|
||||
// Get current spread in price
|
||||
double GetSpreadPrice() const
|
||||
{
|
||||
return GetAsk() - GetBid();
|
||||
}
|
||||
|
||||
// Check if spread is acceptable
|
||||
bool IsSpreadAcceptable(int max_spread_points) const
|
||||
{
|
||||
int current_spread = GetSpreadPoints();
|
||||
if(current_spread > max_spread_points)
|
||||
{
|
||||
if(mp_logger)
|
||||
{
|
||||
// Build message without using variadic StringFormat to avoid parser issues
|
||||
string msg = "Spread too wide: ";
|
||||
msg += IntegerToString(current_spread);
|
||||
msg += " > ";
|
||||
msg += IntegerToString(max_spread_points);
|
||||
mp_logger.Warning(msg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Detect new bar on current timeframe
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime bar_time = iTime(m_symbol, PERIOD_CURRENT, 0);
|
||||
|
||||
if(m_last_bar_time == 0)
|
||||
{
|
||||
m_last_bar_time = bar_time;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(bar_time != m_last_bar_time)
|
||||
{
|
||||
m_last_bar_time = bar_time;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get current close price
|
||||
double GetClose() const
|
||||
{
|
||||
return iClose(m_symbol, PERIOD_CURRENT, 0);
|
||||
}
|
||||
|
||||
// Get current open price
|
||||
double GetOpen() const
|
||||
{
|
||||
return iOpen(m_symbol, PERIOD_CURRENT, 0);
|
||||
}
|
||||
|
||||
// Get current high price
|
||||
double GetHigh() const
|
||||
{
|
||||
return iHigh(m_symbol, PERIOD_CURRENT, 0);
|
||||
}
|
||||
|
||||
// Get current low price
|
||||
double GetLow() const
|
||||
{
|
||||
return iLow(m_symbol, PERIOD_CURRENT, 0);
|
||||
}
|
||||
|
||||
// Get close price of bar N bars ago
|
||||
double GetCloseAt(int shift) const
|
||||
{
|
||||
return iClose(m_symbol, PERIOD_CURRENT, shift);
|
||||
}
|
||||
|
||||
// Get open price of bar N bars ago
|
||||
double GetOpenAt(int shift) const
|
||||
{
|
||||
return iOpen(m_symbol, PERIOD_CURRENT, shift);
|
||||
}
|
||||
|
||||
// Get symbol digits
|
||||
int GetDigits() const
|
||||
{
|
||||
return m_digits;
|
||||
}
|
||||
|
||||
// Get symbol point
|
||||
double GetPoint() const
|
||||
{
|
||||
return m_point;
|
||||
}
|
||||
|
||||
// Check if trading is allowed for this symbol
|
||||
bool IsTradingAllowed() const
|
||||
{
|
||||
// Ensure symbol allows trading (full trade mode)
|
||||
long trade_mode = (long)SymbolInfoInteger(m_symbol, SYMBOL_TRADE_MODE);
|
||||
if(trade_mode != SYMBOL_TRADE_MODE_FULL)
|
||||
return false;
|
||||
|
||||
// Basic check: ensure symbol is tradeable (has non-zero point and digits)
|
||||
if(SymbolInfoDouble(m_symbol, SYMBOL_POINT) <= 0.0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get symbol name
|
||||
string GetSymbol() const
|
||||
{
|
||||
return m_symbol;
|
||||
}
|
||||
|
||||
// Get minimum volume
|
||||
double GetMinVolume() const
|
||||
{
|
||||
return SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_MIN);
|
||||
}
|
||||
|
||||
// Get maximum volume
|
||||
double GetMaxVolume() const
|
||||
{
|
||||
return SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_MAX);
|
||||
}
|
||||
|
||||
// Get volume step
|
||||
double GetVolumeStep() const
|
||||
{
|
||||
return SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_STEP);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__MARKETDATA_MQH__
|
||||
@@ -0,0 +1,146 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| PositionManager.mqh - Position tracking and validation |
|
||||
//| Checks for existing positions, prevents duplicates |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#ifndef __POSITIONMANAGER_MQH__
|
||||
#define __POSITIONMANAGER_MQH__
|
||||
|
||||
#include "Config.mqh"
|
||||
#include "Logger.mqh"
|
||||
|
||||
class CPositionManager
|
||||
{
|
||||
private:
|
||||
CLogger *mp_logger;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
CPositionManager(CLogger *logger)
|
||||
{
|
||||
mp_logger = logger;
|
||||
}
|
||||
|
||||
// Check if there is already an open position for this symbol and magic
|
||||
bool HasOpenPosition(const string symbol)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(PositionSelectByTicket(PositionGetTicket(i)))
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == g_magic_number)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get current position count for this symbol and magic
|
||||
int GetPositionCount(const string symbol)
|
||||
{
|
||||
int count = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(PositionSelectByTicket(PositionGetTicket(i)))
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == g_magic_number)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Get current position type (OP_BUY, OP_SELL, or -1 if none)
|
||||
int GetPositionType(const string symbol)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(PositionSelectByTicket(PositionGetTicket(i)))
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == g_magic_number)
|
||||
{
|
||||
return (int)PositionGetInteger(POSITION_TYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get current position ticket (returns 0 if none)
|
||||
ulong GetPositionTicket(const string symbol)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(PositionSelectByTicket(ticket))
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == g_magic_number)
|
||||
{
|
||||
return ticket;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get current position profit
|
||||
double GetPositionProfit(const string symbol)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(PositionSelectByTicket(PositionGetTicket(i)))
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == g_magic_number)
|
||||
{
|
||||
return PositionGetDouble(POSITION_PROFIT);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Check if new position is allowed (not exceeding max positions)
|
||||
bool IsNewPositionAllowed(const string symbol)
|
||||
{
|
||||
int current_count = GetPositionCount(symbol);
|
||||
|
||||
if(current_count >= g_max_positions)
|
||||
{
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Max positions reached: %d >= %d",
|
||||
current_count, g_max_positions));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get total open lots for symbol and magic
|
||||
double GetTotalOpenLots(const string symbol)
|
||||
{
|
||||
double total_lots = 0.0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(PositionSelectByTicket(PositionGetTicket(i)))
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == g_magic_number)
|
||||
{
|
||||
total_lots += PositionGetDouble(POSITION_VOLUME);
|
||||
}
|
||||
}
|
||||
}
|
||||
return total_lots;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__POSITIONMANAGER_MQH__
|
||||
@@ -0,0 +1,158 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RiskManager.mqh - Risk and position sizing management |
|
||||
//| Calculates lot sizes, validates parameters, checks trading hours |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#ifndef __RISKMANAGER_MQH__
|
||||
#define __RISKMANAGER_MQH__
|
||||
|
||||
#include "Config.mqh"
|
||||
#include "Logger.mqh"
|
||||
#include "MarketData.mqh"
|
||||
#include "Utilities.mqh"
|
||||
|
||||
class CRiskManager
|
||||
{
|
||||
private:
|
||||
CMarketData *mp_market_data;
|
||||
CLogger *mp_logger;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
CRiskManager(CMarketData *market_data, CLogger *logger)
|
||||
{
|
||||
mp_market_data = market_data;
|
||||
mp_logger = logger;
|
||||
}
|
||||
|
||||
// Calculate lot size based on configuration
|
||||
double CalculateLotSize(int stop_loss_points)
|
||||
{
|
||||
double lot = 0.0;
|
||||
|
||||
if(g_lot_mode == LOT_MODE_FIXED)
|
||||
{
|
||||
lot = g_fixed_lot;
|
||||
}
|
||||
else if(g_lot_mode == LOT_MODE_RISK)
|
||||
{
|
||||
lot = CalculateLotByRisk(stop_loss_points);
|
||||
}
|
||||
|
||||
return ValidateLotSize(lot);
|
||||
}
|
||||
|
||||
// Calculate lot size based on risk percent
|
||||
double CalculateLotByRisk(int stop_loss_points)
|
||||
{
|
||||
double account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
double stop_loss_distance = CUtilities::PointsToPrice(mp_market_data.GetSymbol(), stop_loss_points);
|
||||
double contract_size = CUtilities::GetContractSize(mp_market_data.GetSymbol());
|
||||
|
||||
if(stop_loss_distance == 0 || contract_size == 0)
|
||||
return g_min_lot;
|
||||
|
||||
// Risk = Account Balance * Risk Percent / 100
|
||||
double risk_amount = account_balance * (g_risk_percent / 100.0);
|
||||
|
||||
// Lot = Risk Amount / (SL Distance * Contract Size * Point)
|
||||
double point = mp_market_data.GetPoint();
|
||||
double lot = risk_amount / (stop_loss_distance * contract_size);
|
||||
|
||||
return lot;
|
||||
}
|
||||
|
||||
// Validate and normalize lot size
|
||||
double ValidateLotSize(double lot)
|
||||
{
|
||||
// Apply global limits first
|
||||
if(lot < g_min_lot)
|
||||
lot = g_min_lot;
|
||||
if(lot > g_max_lot)
|
||||
lot = g_max_lot;
|
||||
|
||||
// Normalize to broker's lot step
|
||||
lot = CUtilities::NormalizeLot(mp_market_data.GetSymbol(), lot);
|
||||
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Lot size calculated: %.2f", lot));
|
||||
|
||||
return lot;
|
||||
}
|
||||
|
||||
// Check if spread is acceptable
|
||||
bool IsSpreadAcceptable()
|
||||
{
|
||||
return mp_market_data.IsSpreadAcceptable(g_max_spread_points);
|
||||
}
|
||||
|
||||
// Check if trading is allowed by time filter
|
||||
bool IsTradingHourValid()
|
||||
{
|
||||
if(!g_use_trading_hours)
|
||||
return true;
|
||||
|
||||
MqlDateTime time_struct;
|
||||
TimeToStruct(TimeCurrent(), time_struct);
|
||||
int current_hour = time_struct.hour;
|
||||
|
||||
if(g_trade_start_hour <= g_trade_end_hour)
|
||||
{
|
||||
// Normal case: e.g., 8:00 to 20:00
|
||||
if(current_hour < g_trade_start_hour || current_hour >= g_trade_end_hour)
|
||||
{
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Outside trading hours: %d (allowed: %d-%d)",
|
||||
current_hour, g_trade_start_hour, g_trade_end_hour));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Overnight case: e.g., 20:00 to 8:00
|
||||
if(current_hour < g_trade_start_hour && current_hour >= g_trade_end_hour)
|
||||
{
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Outside trading hours: %d (allowed: %d-%d)",
|
||||
current_hour, g_trade_start_hour, g_trade_end_hour));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculate stop loss price in absolute terms
|
||||
double CalculateStopLossPrice(bool buy)
|
||||
{
|
||||
double bid = mp_market_data.GetBid();
|
||||
double ask = mp_market_data.GetAsk();
|
||||
double entry_price = buy ? ask : bid;
|
||||
double sl_distance = CUtilities::PointsToPrice(mp_market_data.GetSymbol(), g_stop_loss_points);
|
||||
|
||||
double sl_price = buy ? (entry_price - sl_distance) : (entry_price + sl_distance);
|
||||
|
||||
return CUtilities::NormalizePrice(mp_market_data.GetSymbol(), sl_price);
|
||||
}
|
||||
|
||||
// Calculate take profit price in absolute terms
|
||||
double CalculateTakeProfitPrice(bool buy)
|
||||
{
|
||||
double bid = mp_market_data.GetBid();
|
||||
double ask = mp_market_data.GetAsk();
|
||||
double entry_price = buy ? ask : bid;
|
||||
double tp_distance = CUtilities::PointsToPrice(mp_market_data.GetSymbol(), g_take_profit_points);
|
||||
|
||||
double tp_price = buy ? (entry_price + tp_distance) : (entry_price - tp_distance);
|
||||
|
||||
return CUtilities::NormalizePrice(mp_market_data.GetSymbol(), tp_price);
|
||||
}
|
||||
|
||||
// Get market data reference
|
||||
CMarketData* GetMarketData()
|
||||
{
|
||||
return mp_market_data;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__RISKMANAGER_MQH__
|
||||
@@ -0,0 +1,16 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Signal.mqh - Trade signal enumeration |
|
||||
//| Defines possible signals from strategy |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#ifndef __SIGNAL_MQH__
|
||||
#define __SIGNAL_MQH__
|
||||
|
||||
enum E_SIGNAL
|
||||
{
|
||||
SIGNAL_NONE = 0, // No signal
|
||||
SIGNAL_BUY = 1, // Buy signal
|
||||
SIGNAL_SELL = -1 // Sell signal
|
||||
};
|
||||
|
||||
#endif //__SIGNAL_MQH__
|
||||
@@ -0,0 +1,151 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy.mqh - Trading strategy implementation |
|
||||
//| Simple MA crossover: Buy when Fast MA > Slow MA, Sell opposite |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#ifndef __STRATEGY_MQH__
|
||||
#define __STRATEGY_MQH__
|
||||
|
||||
#include "Signal.mqh"
|
||||
#include "MarketData.mqh"
|
||||
#include "Logger.mqh"
|
||||
#include "Config.mqh"
|
||||
|
||||
class CStrategy
|
||||
{
|
||||
private:
|
||||
int m_ma_fast_handle;
|
||||
int m_ma_slow_handle;
|
||||
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;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
CStrategy(CMarketData *market_data, CLogger *logger)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Destructor - clean up indicator handles
|
||||
~CStrategy()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if(mp_logger)
|
||||
mp_logger.Error("Failed to create Fast MA 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)
|
||||
{
|
||||
if(mp_logger)
|
||||
mp_logger.Error("Failed to create Slow MA indicator");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(mp_logger)
|
||||
mp_logger.Info("Strategy initialized successfully");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Generate trading signal
|
||||
E_SIGNAL GetSignal()
|
||||
{
|
||||
if(m_ma_fast_handle == INVALID_HANDLE || m_ma_slow_handle == INVALID_HANDLE)
|
||||
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)
|
||||
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);
|
||||
|
||||
if(ma_fast_prev == 0 || ma_slow_prev == 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(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("BUY Signal: MA Fast=%.5f > MA Slow=%.5f", ma_fast, ma_slow));
|
||||
return SIGNAL_BUY;
|
||||
}
|
||||
|
||||
// SELL: Fast MA crosses below Slow MA
|
||||
if(ma_fast_prev >= ma_slow_prev && ma_fast < ma_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;
|
||||
}
|
||||
|
||||
return SIGNAL_NONE;
|
||||
}
|
||||
|
||||
// Clean up indicator handles
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Safe way to get indicator value
|
||||
double iGetMainValue(int handle, int shift)
|
||||
{
|
||||
double value[];
|
||||
ArraySetAsSeries(value, true);
|
||||
|
||||
if(CopyBuffer(handle, 0, shift, 1, value) <= 0)
|
||||
return 0.0;
|
||||
|
||||
return value[0];
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__STRATEGY_MQH__
|
||||
@@ -0,0 +1,187 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| TradeManager.mqh - Trade execution using CTrade class |
|
||||
//| Handles buy/sell orders with SL/TP and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#ifndef __TRADEMANAGER_MQH__
|
||||
#define __TRADEMANAGER_MQH__
|
||||
|
||||
#include <Trade/Trade.mqh>
|
||||
#include "Config.mqh"
|
||||
#include "Logger.mqh"
|
||||
#include "RiskManager.mqh"
|
||||
|
||||
class CTradeManager
|
||||
{
|
||||
private:
|
||||
CTrade m_trade;
|
||||
CLogger *mp_logger;
|
||||
CRiskManager *mp_risk_manager;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
CTradeManager(CLogger *logger, CRiskManager *risk_manager)
|
||||
{
|
||||
mp_logger = logger;
|
||||
mp_risk_manager = risk_manager;
|
||||
|
||||
// Set magic number
|
||||
m_trade.SetExpertMagicNumber((ulong)g_magic_number);
|
||||
|
||||
// Set async/sync mode
|
||||
m_trade.SetAsyncMode(false);
|
||||
|
||||
// Set slippage
|
||||
m_trade.SetDeviationInPoints(10);
|
||||
}
|
||||
|
||||
// Destructor
|
||||
~CTradeManager()
|
||||
{
|
||||
}
|
||||
|
||||
// Open BUY trade
|
||||
bool OpenBuyTrade(const string symbol, double lot)
|
||||
{
|
||||
if(lot <= 0)
|
||||
{
|
||||
if(mp_logger)
|
||||
mp_logger.Error(StringFormat("Invalid lot size: %.2f", lot));
|
||||
return false;
|
||||
}
|
||||
|
||||
double ask = mp_risk_manager.GetMarketData().GetAsk();
|
||||
double sl = mp_risk_manager.CalculateStopLossPrice(true);
|
||||
double tp = mp_risk_manager.CalculateTakeProfitPrice(true);
|
||||
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Opening BUY: lot=%.2f, SL=%.5f, TP=%.5f", lot, sl, tp));
|
||||
|
||||
if(!m_trade.Buy(lot, symbol, ask, sl, tp))
|
||||
{
|
||||
if(mp_logger)
|
||||
{
|
||||
mp_logger.Error(StringFormat("Buy trade failed. Result code: %d, Error: %s",
|
||||
m_trade.ResultRetcode(), m_trade.ResultRetcodeDescription()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(mp_logger)
|
||||
{
|
||||
mp_logger.Info(StringFormat("Buy trade opened. Ticket: %I64d, Volume: %.2f",
|
||||
m_trade.ResultOrder(), lot));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Open SELL trade
|
||||
bool OpenSellTrade(const string symbol, double lot)
|
||||
{
|
||||
if(lot <= 0)
|
||||
{
|
||||
if(mp_logger)
|
||||
mp_logger.Error(StringFormat("Invalid lot size: %.2f", lot));
|
||||
return false;
|
||||
}
|
||||
|
||||
double bid = mp_risk_manager.GetMarketData().GetBid();
|
||||
double sl = mp_risk_manager.CalculateStopLossPrice(false);
|
||||
double tp = mp_risk_manager.CalculateTakeProfitPrice(false);
|
||||
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Opening SELL: lot=%.2f, SL=%.5f, TP=%.5f", lot, sl, tp));
|
||||
|
||||
if(!m_trade.Sell(lot, symbol, bid, sl, tp))
|
||||
{
|
||||
if(mp_logger)
|
||||
{
|
||||
mp_logger.Error(StringFormat("Sell trade failed. Result code: %d, Error: %s",
|
||||
m_trade.ResultRetcode(), m_trade.ResultRetcodeDescription()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(mp_logger)
|
||||
{
|
||||
mp_logger.Info(StringFormat("Sell trade opened. Ticket: %I64d, Volume: %.2f",
|
||||
m_trade.ResultOrder(), lot));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Close position by ticket
|
||||
bool ClosePosition(ulong ticket)
|
||||
{
|
||||
if(ticket == 0)
|
||||
return false;
|
||||
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
return false;
|
||||
|
||||
double volume = PositionGetDouble(POSITION_VOLUME);
|
||||
string symbol = PositionGetString(POSITION_SYMBOL);
|
||||
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Closing position ticket %I64d, volume %.2f", ticket, volume));
|
||||
|
||||
if(!m_trade.PositionClose(ticket))
|
||||
{
|
||||
if(mp_logger)
|
||||
{
|
||||
mp_logger.Error(StringFormat("Close position failed. Ticket: %I64d, Result: %d",
|
||||
ticket, m_trade.ResultRetcode()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(mp_logger)
|
||||
mp_logger.Info(StringFormat("Position closed. Ticket: %I64d", ticket));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Modify position SL and/or TP
|
||||
bool ModifyPosition(ulong ticket, double sl, double tp)
|
||||
{
|
||||
if(ticket == 0)
|
||||
return false;
|
||||
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
return false;
|
||||
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Modifying position %I64d: SL=%.5f, TP=%.5f", ticket, sl, tp));
|
||||
|
||||
if(!m_trade.PositionModify(ticket, sl, tp))
|
||||
{
|
||||
if(mp_logger)
|
||||
{
|
||||
mp_logger.Error(StringFormat("Position modify failed. Ticket: %I64d, Result: %d",
|
||||
ticket, m_trade.ResultRetcode()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Position modified. Ticket: %I64d", ticket));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get last result code
|
||||
uint GetResultRetcode() const
|
||||
{
|
||||
return m_trade.ResultRetcode();
|
||||
}
|
||||
|
||||
// Get CTrade instance
|
||||
CTrade* GetTradeObject()
|
||||
{
|
||||
return &m_trade;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__TRADEMANAGER_MQH__
|
||||
@@ -0,0 +1,183 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| TrailingStop.mqh - Trailing stop and break-even management |
|
||||
//| Modifies SL based on profit targets |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#ifndef __TRAILINGSTOP_MQH__
|
||||
#define __TRAILINGSTOP_MQH__
|
||||
|
||||
#include "Config.mqh"
|
||||
#include "Logger.mqh"
|
||||
#include "MarketData.mqh"
|
||||
#include "Utilities.mqh"
|
||||
#include "TradeManager.mqh"
|
||||
|
||||
class CTrailingStop
|
||||
{
|
||||
private:
|
||||
CMarketData *mp_market_data;
|
||||
CLogger *mp_logger;
|
||||
CTradeManager *mp_trade_manager;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
CTrailingStop(CMarketData *market_data, CLogger *logger, CTradeManager *trade_manager)
|
||||
{
|
||||
mp_market_data = market_data;
|
||||
mp_logger = logger;
|
||||
mp_trade_manager = trade_manager;
|
||||
}
|
||||
|
||||
// Update trailing stop for all positions
|
||||
void UpdateAllPositions()
|
||||
{
|
||||
if(!g_use_trailing_stop && !g_use_break_even)
|
||||
return;
|
||||
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(PositionSelectByTicket(PositionGetTicket(i)))
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == mp_market_data.GetSymbol() &&
|
||||
PositionGetInteger(POSITION_MAGIC) == g_magic_number)
|
||||
{
|
||||
UpdatePosition(PositionGetTicket(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update trailing stop for a single position
|
||||
void UpdatePosition(ulong ticket)
|
||||
{
|
||||
if(ticket == 0)
|
||||
return;
|
||||
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
return;
|
||||
|
||||
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
double current_sl = PositionGetDouble(POSITION_SL);
|
||||
double current_tp = PositionGetDouble(POSITION_TP);
|
||||
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double profit = PositionGetDouble(POSITION_PROFIT);
|
||||
|
||||
double new_sl = current_sl;
|
||||
double new_tp = current_tp;
|
||||
bool need_modify = false;
|
||||
|
||||
// Apply break-even logic first
|
||||
if(g_use_break_even)
|
||||
{
|
||||
if(ApplyBreakEven(pos_type, open_price, current_sl, profit, new_sl))
|
||||
{
|
||||
need_modify = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply trailing stop logic
|
||||
if(g_use_trailing_stop)
|
||||
{
|
||||
if(ApplyTrailingStop(pos_type, current_sl, new_sl))
|
||||
{
|
||||
need_modify = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Only modify if SL changed
|
||||
if(need_modify && new_sl != current_sl)
|
||||
{
|
||||
mp_trade_manager.ModifyPosition(ticket, new_sl, new_tp);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Apply break-even logic
|
||||
bool ApplyBreakEven(ENUM_POSITION_TYPE pos_type, double open_price,
|
||||
double current_sl, double profit, double &new_sl)
|
||||
{
|
||||
double break_even_trigger = CUtilities::PointsToPrice(mp_market_data.GetSymbol(),
|
||||
g_break_even_profit);
|
||||
double break_even_distance = CUtilities::PointsToPrice(mp_market_data.GetSymbol(),
|
||||
g_break_even_sl);
|
||||
|
||||
// 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;
|
||||
be_sl = CUtilities::NormalizePrice(mp_market_data.GetSymbol(), be_sl);
|
||||
|
||||
if(be_sl > current_sl)
|
||||
{
|
||||
new_sl = be_sl;
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Break-even applied: BUY SL moved to %.5f", be_sl));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// For SELL: move SL to BE (open price - distance)
|
||||
else if(pos_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
double be_sl = open_price - break_even_distance;
|
||||
be_sl = CUtilities::NormalizePrice(mp_market_data.GetSymbol(), be_sl);
|
||||
|
||||
if(be_sl < current_sl)
|
||||
{
|
||||
new_sl = be_sl;
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Break-even applied: SELL SL moved to %.5f", be_sl));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply trailing stop logic
|
||||
bool ApplyTrailingStop(ENUM_POSITION_TYPE pos_type, double current_sl, double &new_sl)
|
||||
{
|
||||
double bid = mp_market_data.GetBid();
|
||||
double ask = mp_market_data.GetAsk();
|
||||
double trail_distance = CUtilities::PointsToPrice(mp_market_data.GetSymbol(),
|
||||
g_trailing_stop_points);
|
||||
|
||||
// For BUY: trailing stop follows price from below
|
||||
if(pos_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
double candidate_sl = bid - trail_distance;
|
||||
candidate_sl = CUtilities::NormalizePrice(mp_market_data.GetSymbol(), candidate_sl);
|
||||
|
||||
// Only move SL up (never down)
|
||||
if(candidate_sl > current_sl)
|
||||
{
|
||||
new_sl = candidate_sl;
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Trailing stop applied: BUY SL moved to %.5f", candidate_sl));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// For SELL: trailing stop follows price from above
|
||||
else if(pos_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
double candidate_sl = ask + trail_distance;
|
||||
candidate_sl = CUtilities::NormalizePrice(mp_market_data.GetSymbol(), candidate_sl);
|
||||
|
||||
// Only move SL down (never up)
|
||||
if(candidate_sl < current_sl)
|
||||
{
|
||||
new_sl = candidate_sl;
|
||||
if(mp_logger && g_debug_mode)
|
||||
mp_logger.Info(StringFormat("Trailing stop applied: SELL SL moved to %.5f", candidate_sl));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__TRAILINGSTOP_MQH__
|
||||
@@ -0,0 +1,81 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Utilities.mqh - Helper utility functions |
|
||||
//| Price normalization, lot normalization, etc. |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#ifndef __UTILITIES_MQH__
|
||||
#define __UTILITIES_MQH__
|
||||
|
||||
class CUtilities
|
||||
{
|
||||
public:
|
||||
// Normalize price to bid/ask
|
||||
static double NormalizePrice(const string symbol, double price)
|
||||
{
|
||||
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
int digits = GetDigits(symbol);
|
||||
return NormalizeDouble(price, digits);
|
||||
}
|
||||
|
||||
// Normalize lot size to contract size step
|
||||
static double NormalizeLot(const string symbol, double lot)
|
||||
{
|
||||
double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
||||
double max_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
||||
double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
||||
|
||||
// Apply limits
|
||||
if(lot < min_lot) lot = min_lot;
|
||||
if(lot > max_lot) lot = max_lot;
|
||||
|
||||
// Round to step
|
||||
lot = MathFloor(lot / lot_step) * lot_step;
|
||||
|
||||
return NormalizeDouble(lot, 2);
|
||||
}
|
||||
|
||||
// Convert points to price distance
|
||||
static double PointsToPrice(const string symbol, int points)
|
||||
{
|
||||
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
return (double)points * point;
|
||||
}
|
||||
|
||||
// Convert price distance to points
|
||||
static int PriceToPoints(const string symbol, double price_distance)
|
||||
{
|
||||
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
return (int)(price_distance / point);
|
||||
}
|
||||
|
||||
// Get point value
|
||||
static double GetPoint(const string symbol)
|
||||
{
|
||||
return SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
}
|
||||
|
||||
// Get digits
|
||||
static int GetDigits(const string symbol)
|
||||
{
|
||||
long digits = SymbolInfoInteger(symbol, SYMBOL_DIGITS);
|
||||
if(digits < 0)
|
||||
digits = 0;
|
||||
if(digits > 255)
|
||||
digits = 255;
|
||||
return (int)digits;
|
||||
}
|
||||
|
||||
// Safe double comparison with precision
|
||||
static bool DoubleEquals(double a, double b, double tolerance = 0.00001)
|
||||
{
|
||||
return MathAbs(a - b) < tolerance;
|
||||
}
|
||||
|
||||
// Get contract size (lot multiplier)
|
||||
static double GetContractSize(const string symbol)
|
||||
{
|
||||
return SymbolInfoDouble(symbol, SYMBOL_TRADE_CONTRACT_SIZE);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__UTILITIES_MQH__
|
||||
BIN
Binary file not shown.
+242
@@ -0,0 +1,242 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
// 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");
|
||||
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");
|
||||
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("Timeframe: %s", EnumToString(PERIOD_CURRENT)));
|
||||
g_logger.Info(StringFormat("Lot Mode: %s",
|
||||
g_lot_mode == LOT_MODE_FIXED ? "FIXED" : "RISK"));
|
||||
g_logger.Info(StringFormat("Stop Loss: %d points, Take Profit: %d points",
|
||||
g_stop_loss_points, g_take_profit_points));
|
||||
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())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3: Check trading hours
|
||||
if(!g_risk_manager.IsTradingHourValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 4: Manage existing positions (trailing stop, break-even)
|
||||
g_trailing_stop.UpdateAllPositions();
|
||||
|
||||
// Step 5: Only process signals on new bar
|
||||
if(!g_market_data.IsNewBar())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 6: Get trading signal from strategy
|
||||
E_SIGNAL signal = g_strategy.GetSignal();
|
||||
|
||||
if(signal == SIGNAL_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 7: Check if we already have a position
|
||||
if(g_position_manager.HasOpenPosition(_Symbol))
|
||||
{
|
||||
if(g_debug_mode)
|
||||
g_logger.Info("Already have open position, skipping entry");
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 8: Check if new position is allowed
|
||||
if(!g_position_manager.IsNewPositionAllowed(_Symbol))
|
||||
{
|
||||
g_logger.Warning("New position not allowed (max positions reached)");
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 9: Calculate lot size
|
||||
double lot = g_risk_manager.CalculateLotSize(g_stop_loss_points);
|
||||
if(lot <= 0)
|
||||
{
|
||||
g_logger.Error("Invalid lot size calculated");
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 10: Execute trade based on signal
|
||||
bool trade_success = false;
|
||||
|
||||
if(signal == SIGNAL_BUY)
|
||||
{
|
||||
trade_success = g_trade_manager.OpenBuyTrade(_Symbol, lot);
|
||||
}
|
||||
else if(signal == SIGNAL_SELL)
|
||||
{
|
||||
trade_success = g_trade_manager.OpenSellTrade(_Symbol, lot);
|
||||
}
|
||||
|
||||
if(trade_success)
|
||||
{
|
||||
if(g_logger)
|
||||
g_logger.Info("Trade executed successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(g_logger)
|
||||
g_logger.Error("Trade execution failed");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user