Zenith-FX/MQL5/Experts/ForexTrader_v3.2_MultiStrategy_Production.mq5

2306 lines
76 KiB
MQL5
Raw Permalink Normal View History

//+------------------------------------------------------------------+
//| ForexTrader_v3.2_MultiStrategy_Production.mq5 |
//| Enhanced Multi-Strategy Multi-Symbol Portfolio EA |
//| With Advanced Risk & Position Management |
//+------------------------------------------------------------------+
#property copyright "ForexTrader EA v3.2 Multi-Strategy - Enhanced Production"
#property link "https://github.com/simonokwundue-ops/Experienced-FX-Trader"
#property version "3.20"
#property description "Enhanced multi-strategy EA with multi-symbol portfolio trading"
#property description "Implements MA, RSI, BB, MACD with aggressive signal detection"
#property description "Supports multiple currency pairs from single chart"
#property description "Per-symbol position limits and global risk management"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\AccountInfo.mqh>
//--- Global Objects
CTrade trade;
CPositionInfo positionInfo;
CAccountInfo accountInfo;
//+------------------------------------------------------------------+
//| Strategy Enumeration |
//+------------------------------------------------------------------+
enum ENUM_STRATEGY_TYPE
{
STRATEGY_MA = 0, // Moving Average Crossover
STRATEGY_RSI = 1, // RSI Reversal
STRATEGY_BB = 2, // Bollinger Bands
STRATEGY_MACD = 3 // MACD
};
//+------------------------------------------------------------------+
//| Input Parameters - Multi-Symbol Trading |
//+------------------------------------------------------------------+
input group "=== Multi-Symbol Trading (v3.2) ==="
input bool TradeMultipleSymbols = true; // Enable Multi-Symbol Trading
input string TradingSymbols = "EURUSD,GBPUSD,USDJPY,AUDUSD,USDCAD"; // Trading Symbols (comma-separated)
input int MaxPositionsPerSymbol = 2; // Max Positions Per Symbol
input bool AllowHedging = true; // Allow Opposite Directions Per Symbol
input int MaxTotalPositions = 5; // Max Total Positions (All Symbols)
input double MaxFloatingEquityPercent = 5.0; // Max Floating Equity Risk (%)
//+------------------------------------------------------------------+
//| Input Parameters - Strategy Selection |
//+------------------------------------------------------------------+
input group "=== Strategy Selection ==="
input bool UseMAStrategy = true; // Use MA Crossover Strategy
input bool UseRSIStrategy = true; // Use RSI Strategy
input bool UseBBStrategy = true; // Use Bollinger Bands Strategy
input bool UseMACDStrategy = true; // Use MACD Strategy
input int MinSignalScore = 35; // Minimum Signal Score (0-100) - Lower for more trades
//+------------------------------------------------------------------+
//| Input Parameters - Multi-Timeframe |
//+------------------------------------------------------------------+
input group "=== Multi-Timeframe Analysis ==="
input bool UseMultiTimeframe = true; // Enable Multi-Timeframe Analysis
input ENUM_TIMEFRAMES TimeFrame1 = PERIOD_M15; // Fast Timeframe
input ENUM_TIMEFRAMES TimeFrame2 = PERIOD_M30; // Medium Timeframe
input ENUM_TIMEFRAMES TimeFrame3 = PERIOD_H1; // Slow Timeframe
input int MinTFConfirmation = 2; // Min Timeframes for Signal (1-3)
//+------------------------------------------------------------------+
//| Input Parameters - MA Strategy |
//+------------------------------------------------------------------+
input group "=== Moving Average Strategy ==="
input int FastMA_Period = 10; // Fast MA Period
input int SlowMA_Period = 50; // Slow MA Period
input ENUM_MA_METHOD MA_Method = MODE_EMA; // MA Method
input ENUM_APPLIED_PRICE MA_Price = PRICE_CLOSE; // MA Applied Price
input double MA_SlopeMinimum = 5.0; // Min MA Slope (Pips)
//+------------------------------------------------------------------+
//| Input Parameters - RSI Strategy |
//+------------------------------------------------------------------+
input group "=== RSI Strategy ==="
input int RSI_Period = 14; // RSI Period
input double RSI_Oversold = 30.0; // RSI Oversold Level
input double RSI_Overbought = 70.0; // RSI Overbought Level
input ENUM_APPLIED_PRICE RSI_Price = PRICE_CLOSE; // RSI Applied Price
//+------------------------------------------------------------------+
//| Input Parameters - Bollinger Bands Strategy |
//+------------------------------------------------------------------+
input group "=== Bollinger Bands Strategy ==="
input int BB_Period = 20; // BB Period
input double BB_Deviation = 2.0; // BB Standard Deviation
input ENUM_APPLIED_PRICE BB_AppliedPrice = PRICE_CLOSE; // BB Applied Price
//+------------------------------------------------------------------+
//| Input Parameters - MACD Strategy |
//+------------------------------------------------------------------+
input group "=== MACD Strategy ==="
input int MACD_FastEMA = 12; // MACD Fast EMA
input int MACD_SlowEMA = 26; // MACD Slow EMA
input int MACD_Signal = 9; // MACD Signal Period
input ENUM_APPLIED_PRICE MACD_Price = PRICE_CLOSE; // MACD Applied Price
//+------------------------------------------------------------------+
//| Input Parameters - Filters |
//+------------------------------------------------------------------+
input group "=== Signal Filters ==="
input bool UseADXFilter = true; // Use ADX Trend Filter
input int ADX_Period = 14; // ADX Period
input double ADX_Minimum = 20.0; // Minimum ADX Level
input bool UseATRFilter = true; // Use ATR Volatility Filter
input int ATR_Period = 14; // ATR Period
input double ATR_MinimumPips = 10.0; // Minimum ATR (Pips)
input double ATR_MaximumPips = 100.0; // Maximum ATR (Pips)
//+------------------------------------------------------------------+
//| Input Parameters - Portfolio Risk Management |
//+------------------------------------------------------------------+
input group "=== Portfolio Risk Management ==="
input double MaxPortfolioRisk = 10.0; // Max Portfolio Risk (% of Balance)
input double BaseRiskPercent = 1.5; // Base Risk Per Trade (%)
input bool UseDynamicRisk = true; // Use Win Rate Based Risk
input double MinRiskPercent = 0.5; // Minimum Risk Per Trade (%)
input double MaxRiskPercent = 3.0; // Maximum Risk Per Trade (%)
input double MaxDrawdownPercent = 30.0; // Max Drawdown % (halt trading)
input double DailyDrawdownPercent = 5.0; // Daily Drawdown Limit %
//+------------------------------------------------------------------+
//| Input Parameters - Position Sizing |
//+------------------------------------------------------------------+
input group "=== Position Sizing ==="
input double StopLossPips = 40.0; // Stop Loss (Pips)
input double TakeProfitPips = 80.0; // Take Profit (Pips)
input bool UseATRSizing = true; // Use ATR for SL/TP
input double ATR_SL_Multiplier = 2.0; // ATR Multiplier for SL
input double ATR_TP_Multiplier = 4.0; // ATR Multiplier for TP
input double MaxSpreadPips = 5.0; // Max Spread (Pips)
//+------------------------------------------------------------------+
//| Input Parameters - Position Management |
//+------------------------------------------------------------------+
input group "=== Position Management ==="
input bool UseBreakeven = true; // Move to Breakeven
input double BreakevenTriggerPips = 20.0; // Breakeven Trigger (Pips)
input double BreakevenOffsetPips = 2.0; // Breakeven Offset (Pips)
input bool UsePartialTP = true; // Use Partial Take Profit
input double PartialTP_Pips = 40.0; // Partial TP Level (Pips)
input double PartialTP_Percent = 50.0; // Partial Close % (0-100)
input bool UseTrailingStop = true; // Enable Trailing Stop
input double TrailingStopPips = 30.0; // Trailing Distance (Pips)
input double TrailingStepPips = 5.0; // Trailing Step (Pips)
input double TrailingActivationPips = 20.0; // Trailing Activation (Pips)
//+------------------------------------------------------------------+
//| Input Parameters - Trading Controls |
//+------------------------------------------------------------------+
input group "=== Trading Controls ==="
input int MaxDailyTrades = 50; // Max Trades Per Day (increased for more activity)
input int MaxConcurrentPositions = 5; // Max Concurrent Positions
input int CooldownMinutes = 3; // Cooldown Between Trades (Min) - Reduced
input bool SeparateCooldownByDirection = true; // Separate Buy/Sell Cooldown
input int PostCloseCooldownMinutes = 2; // Cooldown After Position Close (Min)
//+------------------------------------------------------------------+
//| Input Parameters - Session Filters |
//+------------------------------------------------------------------+
input group "=== Session Filters ==="
input bool UseSessionFilter = false; // Enable Session Filter - Disabled for 24/7 trading
input bool TradeAsianSession = true; // Trade Asian Session
input bool TradeLondonSession = true; // Trade London Session
input bool TradeNYSession = true; // Trade New York Session
input int AsianStartHour = 0; // Asian Start Hour (GMT)
input int AsianEndHour = 8; // Asian End Hour (GMT)
input int LondonStartHour = 7; // London Start Hour (GMT)
input int LondonEndHour = 16; // London End Hour (GMT)
input int NYStartHour = 12; // NY Start Hour (GMT)
input int NYEndHour = 21; // NY End Hour (GMT)
//+------------------------------------------------------------------+
//| Input Parameters - Money Management |
//+------------------------------------------------------------------+
input group "=== Money Management ==="
input double MaxLotSize = 5.0; // Maximum Lot Size
input double MinLotSize = 0.01; // Minimum Lot Size
input bool UseCompounding = true; // Use Compounding
input bool UseFixedLot = false; // Use Fixed Lot
input double FixedLotSize = 0.1; // Fixed Lot Size
//+------------------------------------------------------------------+
//| Input Parameters - Advanced Settings |
//+------------------------------------------------------------------+
input group "=== Advanced Settings ==="
input int MagicNumber = 234567; // Magic Number
input string TradeComment = "ForexTrader_v3.2_Multi"; // Trade Comment
input int Slippage = 30; // Max Slippage (Points)
input int MaxRetries = 3; // Max Order Retries
input int RetryDelayMs = 1000; // Retry Delay (Ms)
//+------------------------------------------------------------------+
//| Multi-Symbol Data Structures |
//+------------------------------------------------------------------+
struct SymbolData
{
string symbol;
double point;
double tickSize;
double tickValue;
double lotStep;
double minLot;
double maxLot;
double pipSize;
int symbolDigits;
// Indicator handles
int handleFastMA;
int handleSlowMA;
int handleADX;
int handleATR;
int handleRSI;
int handleBB;
int handleMACD;
// Trading state
datetime lastBuyTime;
datetime lastSellTime;
datetime lastCloseTime;
int positionCount;
};
SymbolData symbolDataArray[];
int totalSymbols = 0;
//+------------------------------------------------------------------+
//| Global Variables |
//+------------------------------------------------------------------+
// Indicator handles for current timeframe
int handleFastMA, handleSlowMA, handleADX, handleATR;
int handleRSI, handleBBUpper, handleBBMiddle, handleBBLower, handleMACD;
// Multi-timeframe indicator handles
int handleFastMA_TF1, handleSlowMA_TF1, handleFastMA_TF2, handleSlowMA_TF2;
int handleFastMA_TF3, handleSlowMA_TF3;
// Indicator buffers
double fastMA[], slowMA[], adxMain[], atrBuffer[];
double rsiBuffer[], bbUpper[], bbMiddle[], bbLower[];
double macdMain[], macdSignal[];
// Symbol properties
double point, tickSize, tickValue, lotStep, minLot, maxLot;
double pipSize;
int symbolDigits;
// Daily tracking
datetime lastTradeDate = 0;
int dailyTradeCount = 0;
double startOfDayBalance = 0;
double startOfDayEquity = 0;
// Cooldown tracking (single-symbol mode)
datetime lastBuyTime = 0;
datetime lastSellTime = 0;
// Strategy performance tracking
struct StrategyStats
{
int totalTrades;
int winningTrades;
double totalProfit;
double winRate;
};
StrategyStats maStats, rsiStats, bbStats, macdStats;
// Position tracking for partial TP
struct PositionTracker
{
ulong ticket;
bool partialTPDone;
};
PositionTracker trackedPositions[100];
int trackedPositionCount = 0;
//+------------------------------------------------------------------+
//| Expert Initialization |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Initialize trade object
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(Slippage);
trade.SetTypeFilling(ORDER_FILLING_IOC);
trade.SetAsyncMode(false);
//--- Get symbol properties
point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
symbolDigits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
//--- Calculate pip size correctly
if(symbolDigits == 3 || symbolDigits == 5)
pipSize = point * 10;
else
pipSize = point;
//--- Get trading properties
tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_PROFIT);
if(tickValue == 0)
{
double contractSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
tickValue = tickSize * contractSize;
}
lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
//--- Validate inputs
if(!ValidateInputs())
return INIT_PARAMETERS_INCORRECT;
//--- Create indicators for main timeframe
if(UseMAStrategy)
{
handleFastMA = iMA(_Symbol, PERIOD_CURRENT, FastMA_Period, 0, MA_Method, MA_Price);
handleSlowMA = iMA(_Symbol, PERIOD_CURRENT, SlowMA_Period, 0, MA_Method, MA_Price);
if(handleFastMA == INVALID_HANDLE || handleSlowMA == INVALID_HANDLE)
{
Print("Error: Failed to create MA indicators");
return INIT_FAILED;
}
}
if(UseRSIStrategy)
{
handleRSI = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, RSI_Price);
if(handleRSI == INVALID_HANDLE)
{
Print("Error: Failed to create RSI indicator");
return INIT_FAILED;
}
}
if(UseBBStrategy)
{
int bbHandle = iBands(_Symbol, PERIOD_CURRENT, BB_Period, 0, BB_Deviation, BB_AppliedPrice);
if(bbHandle == INVALID_HANDLE)
{
Print("Error: Failed to create BB indicator");
return INIT_FAILED;
}
handleBBUpper = bbHandle;
handleBBMiddle = bbHandle;
handleBBLower = bbHandle;
}
if(UseMACDStrategy)
{
handleMACD = iMACD(_Symbol, PERIOD_CURRENT, MACD_FastEMA, MACD_SlowEMA, MACD_Signal, MACD_Price);
if(handleMACD == INVALID_HANDLE)
{
Print("Error: Failed to create MACD indicator");
return INIT_FAILED;
}
}
if(UseADXFilter)
{
handleADX = iADX(_Symbol, PERIOD_CURRENT, ADX_Period);
if(handleADX == INVALID_HANDLE)
{
Print("Error: Failed to create ADX indicator");
return INIT_FAILED;
}
}
if(UseATRFilter || UseATRSizing)
{
handleATR = iATR(_Symbol, PERIOD_CURRENT, ATR_Period);
if(handleATR == INVALID_HANDLE)
{
Print("Error: Failed to create ATR indicator");
return INIT_FAILED;
}
}
//--- Create multi-timeframe MA indicators if enabled
if(UseMultiTimeframe && UseMAStrategy)
{
handleFastMA_TF1 = iMA(_Symbol, TimeFrame1, FastMA_Period, 0, MA_Method, MA_Price);
handleSlowMA_TF1 = iMA(_Symbol, TimeFrame1, SlowMA_Period, 0, MA_Method, MA_Price);
handleFastMA_TF2 = iMA(_Symbol, TimeFrame2, FastMA_Period, 0, MA_Method, MA_Price);
handleSlowMA_TF2 = iMA(_Symbol, TimeFrame2, SlowMA_Period, 0, MA_Method, MA_Price);
handleFastMA_TF3 = iMA(_Symbol, TimeFrame3, FastMA_Period, 0, MA_Method, MA_Price);
handleSlowMA_TF3 = iMA(_Symbol, TimeFrame3, SlowMA_Period, 0, MA_Method, MA_Price);
}
//--- Set arrays as series
ArraySetAsSeries(fastMA, true);
ArraySetAsSeries(slowMA, true);
ArraySetAsSeries(adxMain, true);
ArraySetAsSeries(atrBuffer, true);
ArraySetAsSeries(rsiBuffer, true);
ArraySetAsSeries(bbUpper, true);
ArraySetAsSeries(bbMiddle, true);
ArraySetAsSeries(bbLower, true);
ArraySetAsSeries(macdMain, true);
ArraySetAsSeries(macdSignal, true);
//--- Initialize strategy stats
InitializeStrategyStats();
//--- Initialize multi-symbol trading
if(TradeMultipleSymbols)
{
if(!InitializeMultiSymbolTrading())
{
Print("Error: Failed to initialize multi-symbol trading");
return INIT_FAILED;
}
}
//--- Initialize daily tracking
startOfDayBalance = accountInfo.Balance();
startOfDayEquity = accountInfo.Equity();
//--- Print initialization
Print("========================================");
Print("ForexTrader v3.2 Multi-Strategy - Initialized");
Print("Chart Symbol: ", _Symbol);
if(TradeMultipleSymbols)
{
Print("Multi-Symbol Trading: ENABLED");
Print("Trading Symbols: ", totalSymbols);
Print("Max Positions Per Symbol: ", MaxPositionsPerSymbol);
Print("Max Total Positions: ", MaxTotalPositions);
}
else
{
Print("Single Symbol Trading: ", _Symbol);
}
Print("Active Strategies: ", CountActiveStrategies());
Print("Multi-Timeframe: ", UseMultiTimeframe ? "Yes" : "No");
Print("Min Signal Score: ", MinSignalScore);
Print("Daily Drawdown Limit: ", DailyDrawdownPercent, "%");
Print("Max Floating Equity: ", MaxFloatingEquityPercent, "%");
Print("========================================");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert Deinitialization |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Release all indicator handles for chart symbol
if(handleFastMA != INVALID_HANDLE) IndicatorRelease(handleFastMA);
if(handleSlowMA != INVALID_HANDLE) IndicatorRelease(handleSlowMA);
if(handleADX != INVALID_HANDLE) IndicatorRelease(handleADX);
if(handleATR != INVALID_HANDLE) IndicatorRelease(handleATR);
if(handleRSI != INVALID_HANDLE) IndicatorRelease(handleRSI);
if(handleBBUpper != INVALID_HANDLE) IndicatorRelease(handleBBUpper);
if(handleMACD != INVALID_HANDLE) IndicatorRelease(handleMACD);
//--- Release multi-timeframe handles
if(handleFastMA_TF1 != INVALID_HANDLE) IndicatorRelease(handleFastMA_TF1);
if(handleSlowMA_TF1 != INVALID_HANDLE) IndicatorRelease(handleSlowMA_TF1);
if(handleFastMA_TF2 != INVALID_HANDLE) IndicatorRelease(handleFastMA_TF2);
if(handleSlowMA_TF2 != INVALID_HANDLE) IndicatorRelease(handleSlowMA_TF2);
if(handleFastMA_TF3 != INVALID_HANDLE) IndicatorRelease(handleFastMA_TF3);
if(handleSlowMA_TF3 != INVALID_HANDLE) IndicatorRelease(handleSlowMA_TF3);
//--- Release multi-symbol indicator handles
if(TradeMultipleSymbols)
{
for(int i = 0; i < totalSymbols; i++)
{
if(symbolDataArray[i].handleFastMA != INVALID_HANDLE)
IndicatorRelease(symbolDataArray[i].handleFastMA);
if(symbolDataArray[i].handleSlowMA != INVALID_HANDLE)
IndicatorRelease(symbolDataArray[i].handleSlowMA);
if(symbolDataArray[i].handleADX != INVALID_HANDLE)
IndicatorRelease(symbolDataArray[i].handleADX);
if(symbolDataArray[i].handleATR != INVALID_HANDLE)
IndicatorRelease(symbolDataArray[i].handleATR);
if(symbolDataArray[i].handleRSI != INVALID_HANDLE)
IndicatorRelease(symbolDataArray[i].handleRSI);
if(symbolDataArray[i].handleBB != INVALID_HANDLE)
IndicatorRelease(symbolDataArray[i].handleBB);
if(symbolDataArray[i].handleMACD != INVALID_HANDLE)
IndicatorRelease(symbolDataArray[i].handleMACD);
}
}
//--- Print final stats
PrintStrategyPerformance();
Print("ForexTrader v3.2 Multi-Strategy - Deinitialized");
}
//+------------------------------------------------------------------+
//| Expert Tick Function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Check for new bar on chart symbol
static datetime lastBar = 0;
datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBar == lastBar)
return;
lastBar = currentBar;
//--- Reset daily counter and check daily drawdown
ResetDailyTradeCount();
//--- Check daily drawdown limit
if(!CheckDailyDrawdownLimit())
{
Print("Daily drawdown limit exceeded. Trading halted for today.");
return;
}
//--- Check max drawdown limit
if(!CheckDrawdownLimit())
{
Print("Max drawdown exceeded. Trading halted.");
return;
}
//--- Check floating equity limit
if(!CheckFloatingEquityLimit())
{
Print("Floating equity limit exceeded. No new trades.");
return;
}
//--- Process trading for all symbols
if(TradeMultipleSymbols)
{
for(int i = 0; i < totalSymbols; i++)
{
ProcessSymbolTrading(i);
}
}
else
{
//--- Single symbol trading (legacy mode)
if(!UpdateIndicators())
return;
ManagePositions();
if(CanOpenNewTrade())
CheckEntrySignals();
}
}
//+------------------------------------------------------------------+
//| Trade Transaction Event |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
{
ulong dealTicket = trans.deal;
if(dealTicket > 0 && HistoryDealSelect(dealTicket))
{
long dealMagic = HistoryDealGetInteger(dealTicket, DEAL_MAGIC);
if(dealMagic == MagicNumber)
{
ENUM_DEAL_TYPE dealType = (ENUM_DEAL_TYPE)HistoryDealGetInteger(dealTicket, DEAL_TYPE);
double dealProfit = HistoryDealGetDouble(dealTicket, DEAL_PROFIT);
if(dealProfit != 0) // Trade closed
{
UpdateStrategyStats(trans.position, dealProfit);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Validate Input Parameters |
//+------------------------------------------------------------------+
bool ValidateInputs()
{
if(!UseMAStrategy && !UseRSIStrategy && !UseBBStrategy && !UseMACDStrategy)
{
Print("Error: At least one strategy must be enabled");
return false;
}
if(MinSignalScore < 0 || MinSignalScore > 100)
{
Print("Error: MinSignalScore must be between 0 and 100");
return false;
}
if(BaseRiskPercent <= 0 || BaseRiskPercent > 100)
{
Print("Error: Risk percent must be between 0 and 100");
return false;
}
if(MaxPortfolioRisk <= 0 || MaxPortfolioRisk > 100)
{
Print("Error: Max portfolio risk must be between 0 and 100");
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Update Indicator Values |
//+------------------------------------------------------------------+
bool UpdateIndicators()
{
//--- Update MA indicators
if(UseMAStrategy)
{
if(CopyBuffer(handleFastMA, 0, 0, 3, fastMA) < 3) return false;
if(CopyBuffer(handleSlowMA, 0, 0, 3, slowMA) < 3) return false;
}
//--- Update RSI
if(UseRSIStrategy)
{
if(CopyBuffer(handleRSI, 0, 0, 3, rsiBuffer) < 3) return false;
}
//--- Update BB
if(UseBBStrategy)
{
if(CopyBuffer(handleBBUpper, 1, 0, 2, bbUpper) < 2) return false;
if(CopyBuffer(handleBBMiddle, 0, 0, 2, bbMiddle) < 2) return false;
if(CopyBuffer(handleBBLower, 2, 0, 2, bbLower) < 2) return false;
}
//--- Update MACD
if(UseMACDStrategy)
{
if(CopyBuffer(handleMACD, 0, 0, 3, macdMain) < 3) return false;
if(CopyBuffer(handleMACD, 1, 0, 3, macdSignal) < 3) return false;
}
//--- Update ADX
if(UseADXFilter)
{
if(CopyBuffer(handleADX, 0, 0, 1, adxMain) < 1) return false;
}
//--- Update ATR
if(UseATRFilter || UseATRSizing)
{
if(CopyBuffer(handleATR, 0, 0, 1, atrBuffer) < 1) return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Check Drawdown Limit |
//+------------------------------------------------------------------+
bool CheckDrawdownLimit()
{
double equity = accountInfo.Equity();
double balance = accountInfo.Balance();
if(balance <= 0) return false;
double drawdownPercent = ((balance - equity) / balance) * 100.0;
return (drawdownPercent <= MaxDrawdownPercent);
}
//+------------------------------------------------------------------+
//| Reset Daily Trade Count |
//+------------------------------------------------------------------+
void ResetDailyTradeCount()
{
MqlDateTime currentTime, lastTime;
TimeToStruct(TimeCurrent(), currentTime);
TimeToStruct(lastTradeDate, lastTime);
if(currentTime.day != lastTime.day || currentTime.mon != lastTime.mon ||
currentTime.year != lastTime.year)
{
dailyTradeCount = 0;
lastTradeDate = TimeCurrent();
startOfDayBalance = accountInfo.Balance();
startOfDayEquity = accountInfo.Equity();
Print("New trading day started. Daily counters reset.");
}
}
//+------------------------------------------------------------------+
//| Can Open New Trade |
//+------------------------------------------------------------------+
bool CanOpenNewTrade()
{
//--- Check daily limit
if(dailyTradeCount >= MaxDailyTrades)
return false;
//--- Check concurrent positions
if(CountPositions() >= MaxConcurrentPositions)
return false;
//--- Check portfolio risk
if(!CheckPortfolioRisk())
return false;
//--- Check session filter
if(UseSessionFilter && !IsWithinTradingSession())
return false;
return true;
}
//+------------------------------------------------------------------+
//| Check Portfolio Risk |
//+------------------------------------------------------------------+
bool CheckPortfolioRisk()
{
double totalRisk = 0;
double balance = accountInfo.Balance();
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(positionInfo.SelectByIndex(i))
{
if(positionInfo.Magic() == MagicNumber)
{
double posRisk = CalculatePositionRisk(positionInfo.Ticket());
totalRisk += posRisk;
}
}
}
double riskPercent = (totalRisk / balance) * 100.0;
return (riskPercent < MaxPortfolioRisk);
}
//+------------------------------------------------------------------+
//| Calculate Position Risk |
//+------------------------------------------------------------------+
double CalculatePositionRisk(ulong ticket)
{
if(!positionInfo.SelectByTicket(ticket))
return 0;
double openPrice = positionInfo.PriceOpen();
double sl = positionInfo.StopLoss();
double volume = positionInfo.Volume();
if(sl == 0) return 0;
double slDistance = MathAbs(openPrice - sl);
double ticksDistance = slDistance / tickSize;
return ticksDistance * tickValue * volume;
}
//+------------------------------------------------------------------+
//| Check if Within Trading Session |
//+------------------------------------------------------------------+
bool IsWithinTradingSession()
{
MqlDateTime tm;
TimeToStruct(TimeCurrent(), tm);
int hour = tm.hour;
bool inAsian = (hour >= AsianStartHour && hour < AsianEndHour);
bool inLondon = (hour >= LondonStartHour && hour < LondonEndHour);
bool inNY = (hour >= NYStartHour && hour < NYEndHour);
if(TradeAsianSession && inAsian) return true;
if(TradeLondonSession && inLondon) return true;
if(TradeNYSession && inNY) return true;
return false;
}
//+------------------------------------------------------------------+
//| Check if Cooldown Elapsed |
//+------------------------------------------------------------------+
bool IsCooldownElapsed(bool isBuySignal)
{
datetime relevantTime = SeparateCooldownByDirection ?
(isBuySignal ? lastBuyTime : lastSellTime) :
MathMax(lastBuyTime, lastSellTime);
return (TimeCurrent() - relevantTime) >= (CooldownMinutes * 60);
}
//+------------------------------------------------------------------+
//| Check Entry Signals |
//+------------------------------------------------------------------+
void CheckEntrySignals()
{
//--- Check basic filters
if(!IsSpreadAcceptable()) return;
if(UseATRFilter && !IsVolatilityAcceptable()) return;
if(UseADXFilter && !IsTrendStrong()) return;
//--- Generate signals from each strategy
int buySignalScore = 0;
int sellSignalScore = 0;
string buyReasons = "";
string sellReasons = "";
//--- MA Strategy
if(UseMAStrategy)
{
int maScore = AnalyzeMAStrategy(buyReasons, sellReasons);
if(maScore > 0) buySignalScore += maScore;
if(maScore < 0) sellSignalScore += MathAbs(maScore);
}
//--- RSI Strategy
if(UseRSIStrategy)
{
int rsiScore = AnalyzeRSIStrategy(buyReasons, sellReasons);
if(rsiScore > 0) buySignalScore += rsiScore;
if(rsiScore < 0) sellSignalScore += MathAbs(rsiScore);
}
//--- BB Strategy
if(UseBBStrategy)
{
int bbScore = AnalyzeBBStrategy(buyReasons, sellReasons);
if(bbScore > 0) buySignalScore += bbScore;
if(bbScore < 0) sellSignalScore += MathAbs(bbScore);
}
//--- MACD Strategy
if(UseMACDStrategy)
{
int macdScore = AnalyzeMACDStrategy(buyReasons, sellReasons);
if(macdScore > 0) buySignalScore += macdScore;
if(macdScore < 0) sellSignalScore += MathAbs(macdScore);
}
//--- Apply multi-timeframe confirmation if enabled
if(UseMultiTimeframe && UseMAStrategy)
{
int mtfScore = AnalyzeMultiTimeframe();
if(mtfScore > 0)
{
buySignalScore += 20;
buyReasons += "MTF Bullish | ";
}
else if(mtfScore < 0)
{
sellSignalScore += 20;
sellReasons += "MTF Bearish | ";
}
}
//--- Process buy signal
if(buySignalScore >= MinSignalScore && IsCooldownElapsed(true))
{
Print("=== BUY SIGNAL | Score: ", buySignalScore, " ===");
Print("Reasons: ", buyReasons);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(OpenPosition(ORDER_TYPE_BUY, ask, STRATEGY_MA))
{
lastBuyTime = TimeCurrent();
dailyTradeCount++;
}
}
//--- Process sell signal
if(sellSignalScore >= MinSignalScore && IsCooldownElapsed(false))
{
Print("=== SELL SIGNAL | Score: ", sellSignalScore, " ===");
Print("Reasons: ", sellReasons);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(OpenPosition(ORDER_TYPE_SELL, bid, STRATEGY_MA))
{
lastSellTime = TimeCurrent();
dailyTradeCount++;
}
}
}
//+------------------------------------------------------------------+
//| Analyze MA Strategy |
//+------------------------------------------------------------------+
int AnalyzeMAStrategy(string &buyReasons, string &sellReasons)
{
//--- Check for crossover
bool bullishCross = fastMA[1] > slowMA[1] && fastMA[2] <= slowMA[2];
bool bearishCross = fastMA[1] < slowMA[1] && fastMA[2] >= slowMA[2];
if(!bullishCross && !bearishCross)
return 0;
//--- Check MA slope
double maSlope = MathAbs(fastMA[1] - fastMA[2]) / pipSize;
if(maSlope < MA_SlopeMinimum)
return 0;
//--- Calculate score
int score = 30; // Base score
if(bullishCross)
{
buyReasons += "MA Bullish Cross | ";
return score;
}
else if(bearishCross)
{
sellReasons += "MA Bearish Cross | ";
return -score;
}
return 0;
}
//+------------------------------------------------------------------+
//| Analyze RSI Strategy |
//+------------------------------------------------------------------+
int AnalyzeRSIStrategy(string &buyReasons, string &sellReasons)
{
double rsi = rsiBuffer[0];
double rsiPrev = rsiBuffer[1];
int score = 0;
//--- Oversold bounce
if(rsiPrev < RSI_Oversold && rsi > RSI_Oversold)
{
score = 25;
buyReasons += "RSI Oversold Bounce | ";
}
//--- Overbought fall
if(rsiPrev > RSI_Overbought && rsi < RSI_Overbought)
{
score = -25;
sellReasons += "RSI Overbought Fall | ";
}
return score;
}
//+------------------------------------------------------------------+
//| Analyze Bollinger Bands Strategy |
//+------------------------------------------------------------------+
int AnalyzeBBStrategy(string &buyReasons, string &sellReasons)
{
double close = iClose(_Symbol, PERIOD_CURRENT, 0);
double closePrev = iClose(_Symbol, PERIOD_CURRENT, 1);
int score = 0;
//--- Bounce from lower band
if(closePrev <= bbLower[1] && close > bbLower[0])
{
score = 25;
buyReasons += "BB Lower Bounce | ";
}
//--- Fall from upper band
if(closePrev >= bbUpper[1] && close < bbUpper[0])
{
score = -25;
sellReasons += "BB Upper Fall | ";
}
return score;
}
//+------------------------------------------------------------------+
//| Analyze MACD Strategy |
//+------------------------------------------------------------------+
int AnalyzeMACDStrategy(string &buyReasons, string &sellReasons)
{
int score = 0;
//--- Bullish MACD crossover
if(macdMain[1] > macdSignal[1] && macdMain[2] <= macdSignal[2])
{
score = 20;
buyReasons += "MACD Bullish Cross | ";
}
//--- Bearish MACD crossover
if(macdMain[1] < macdSignal[1] && macdMain[2] >= macdSignal[2])
{
score = -20;
sellReasons += "MACD Bearish Cross | ";
}
return score;
}
//+------------------------------------------------------------------+
//| Analyze Multi-Timeframe |
//+------------------------------------------------------------------+
int AnalyzeMultiTimeframe()
{
double fastMA_TF1[], slowMA_TF1[];
double fastMA_TF2[], slowMA_TF2[];
double fastMA_TF3[], slowMA_TF3[];
ArraySetAsSeries(fastMA_TF1, true);
ArraySetAsSeries(slowMA_TF1, true);
ArraySetAsSeries(fastMA_TF2, true);
ArraySetAsSeries(slowMA_TF2, true);
ArraySetAsSeries(fastMA_TF3, true);
ArraySetAsSeries(slowMA_TF3, true);
if(CopyBuffer(handleFastMA_TF1, 0, 0, 1, fastMA_TF1) < 1) return 0;
if(CopyBuffer(handleSlowMA_TF1, 0, 0, 1, slowMA_TF1) < 1) return 0;
if(CopyBuffer(handleFastMA_TF2, 0, 0, 1, fastMA_TF2) < 1) return 0;
if(CopyBuffer(handleSlowMA_TF2, 0, 0, 1, slowMA_TF2) < 1) return 0;
if(CopyBuffer(handleFastMA_TF3, 0, 0, 1, fastMA_TF3) < 1) return 0;
if(CopyBuffer(handleSlowMA_TF3, 0, 0, 1, slowMA_TF3) < 1) return 0;
int bullish = 0;
int bearish = 0;
if(fastMA_TF1[0] > slowMA_TF1[0]) bullish++; else bearish++;
if(fastMA_TF2[0] > slowMA_TF2[0]) bullish++; else bearish++;
if(fastMA_TF3[0] > slowMA_TF3[0]) bullish++; else bearish++;
if(bullish >= MinTFConfirmation) return 1;
if(bearish >= MinTFConfirmation) return -1;
return 0;
}
//+------------------------------------------------------------------+
//| Check if Spread Acceptable |
//+------------------------------------------------------------------+
bool IsSpreadAcceptable()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double spread = (ask - bid) / pipSize;
if(spread > MaxSpreadPips)
{
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Check if Volatility Acceptable |
//+------------------------------------------------------------------+
bool IsVolatilityAcceptable()
{
if(ArraySize(atrBuffer) == 0) return true;
double atr = atrBuffer[0];
double atrPips = atr / pipSize;
if(atrPips < ATR_MinimumPips || atrPips > ATR_MaximumPips)
return false;
return true;
}
//+------------------------------------------------------------------+
//| Check if Trend Strong |
//+------------------------------------------------------------------+
bool IsTrendStrong()
{
if(ArraySize(adxMain) == 0) return true;
return (adxMain[0] >= ADX_Minimum);
}
//+------------------------------------------------------------------+
//| Open Position (with all fixes) |
//+------------------------------------------------------------------+
bool OpenPosition(ENUM_ORDER_TYPE orderType, double price, ENUM_STRATEGY_TYPE strategy)
{
//--- Calculate SL and TP
double sl = CalculateStopLoss(orderType, price);
double tp = CalculateTakeProfit(orderType, price);
//--- Validate trade
if(!ValidateTrade(orderType, price, sl, tp))
return false;
//--- Calculate lot size with dynamic risk
double riskPercent = CalculateDynamicRisk();
double lots = CalculateLotSize(price, sl, riskPercent);
//--- Normalize
sl = NormalizeDouble(sl, symbolDigits);
tp = NormalizeDouble(tp, symbolDigits);
lots = NormalizeLot(lots);
if(lots < minLot || lots > maxLot)
return false;
//--- Open with retries
bool success = false;
int attempts = 0;
while(attempts < MaxRetries && !success)
{
ResetLastError();
if(orderType == ORDER_TYPE_BUY)
success = trade.Buy(lots, _Symbol, 0, sl, tp, TradeComment);
else
success = trade.Sell(lots, _Symbol, 0, sl, tp, TradeComment);
if(success)
{
Print("Position opened: ", EnumToString(orderType), " | Lots: ", lots,
" | SL: ", sl, " | TP: ", tp);
//--- Track position for partial TP
if(UsePartialTP)
{
trackedPositions[trackedPositionCount].ticket = trade.ResultOrder();
trackedPositions[trackedPositionCount].partialTPDone = false;
trackedPositionCount++;
}
return true;
}
else
{
int error = GetLastError();
if(IsTransientError(error))
{
Sleep(RetryDelayMs);
attempts++;
}
else
{
break;
}
}
}
return false;
}
//+------------------------------------------------------------------+
//| Calculate Dynamic Risk Based on Win Rate |
//+------------------------------------------------------------------+
double CalculateDynamicRisk()
{
if(!UseDynamicRisk)
return BaseRiskPercent;
//--- Calculate overall win rate
int totalTrades = maStats.totalTrades + rsiStats.totalTrades +
bbStats.totalTrades + macdStats.totalTrades;
int totalWins = maStats.winningTrades + rsiStats.winningTrades +
bbStats.winningTrades + macdStats.winningTrades;
if(totalTrades < 10)
return BaseRiskPercent;
double winRate = (double)totalWins / totalTrades;
//--- Adjust risk based on win rate
double risk = BaseRiskPercent;
if(winRate > 0.6)
risk = BaseRiskPercent * 1.2;
else if(winRate < 0.4)
risk = BaseRiskPercent * 0.8;
//--- Apply limits
if(risk < MinRiskPercent) risk = MinRiskPercent;
if(risk > MaxRiskPercent) risk = MaxRiskPercent;
return risk;
}
//+------------------------------------------------------------------+
//| Calculate Stop Loss |
//+------------------------------------------------------------------+
double CalculateStopLoss(ENUM_ORDER_TYPE orderType, double price)
{
double slDistance;
if(UseATRSizing && ArraySize(atrBuffer) > 0)
{
slDistance = atrBuffer[0] * ATR_SL_Multiplier;
}
else
{
slDistance = StopLossPips * pipSize;
}
double sl = (orderType == ORDER_TYPE_BUY) ?
price - slDistance : price + slDistance;
return sl;
}
//+------------------------------------------------------------------+
//| Calculate Take Profit |
//+------------------------------------------------------------------+
double CalculateTakeProfit(ENUM_ORDER_TYPE orderType, double price)
{
double tpDistance;
if(UseATRSizing && ArraySize(atrBuffer) > 0)
{
tpDistance = atrBuffer[0] * ATR_TP_Multiplier;
}
else
{
tpDistance = TakeProfitPips * pipSize;
}
double tp = (orderType == ORDER_TYPE_BUY) ?
price + tpDistance : price - tpDistance;
return tp;
}
//+------------------------------------------------------------------+
//| Validate Trade |
//+------------------------------------------------------------------+
bool ValidateTrade(ENUM_ORDER_TYPE orderType, double price, double sl, double tp)
{
long stopsLevelPoints = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
double stopsLevel = stopsLevelPoints * point;
if(MathAbs(price - sl) < stopsLevel) return false;
if(MathAbs(price - tp) < stopsLevel) return false;
return true;
}
//+------------------------------------------------------------------+
//| Calculate Lot Size |
//+------------------------------------------------------------------+
double CalculateLotSize(double entryPrice, double stopLoss, double riskPercent)
{
if(UseFixedLot)
return FixedLotSize;
double balance = UseCompounding ? accountInfo.Equity() : accountInfo.Balance();
double riskAmount = balance * (riskPercent / 100.0);
double slDistance = MathAbs(entryPrice - stopLoss);
if(slDistance <= 0) return minLot;
double ticksDistance = slDistance / tickSize;
double riskPerLot = ticksDistance * tickValue;
if(riskPerLot <= 0) return minLot;
double lots = riskAmount / riskPerLot;
if(lots > MaxLotSize) lots = MaxLotSize;
if(lots < MinLotSize) lots = MinLotSize;
return lots;
}
//+------------------------------------------------------------------+
//| Normalize Lot Size |
//+------------------------------------------------------------------+
double NormalizeLot(double lots)
{
lots = MathRound(lots / lotStep) * lotStep;
if(lots < minLot) lots = minLot;
if(lots > maxLot) lots = maxLot;
return NormalizeDouble(lots, 2);
}
//+------------------------------------------------------------------+
//| Count Positions |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(positionInfo.SelectByIndex(i))
{
if(positionInfo.Symbol() == _Symbol && positionInfo.Magic() == MagicNumber)
{
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| Manage Positions |
//+------------------------------------------------------------------+
void ManagePositions()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!positionInfo.SelectByIndex(i))
continue;
if(positionInfo.Symbol() != _Symbol || positionInfo.Magic() != MagicNumber)
continue;
ulong ticket = positionInfo.Ticket();
double openPrice = positionInfo.PriceOpen();
double currentSL = positionInfo.StopLoss();
double currentTP = positionInfo.TakeProfit();
ENUM_POSITION_TYPE posType = positionInfo.PositionType();
double volume = positionInfo.Volume();
double currentPrice = (posType == POSITION_TYPE_BUY) ?
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double profitPips = 0;
if(posType == POSITION_TYPE_BUY)
profitPips = (currentPrice - openPrice) / pipSize;
else
profitPips = (openPrice - currentPrice) / pipSize;
//--- Partial TP
if(UsePartialTP && profitPips >= PartialTP_Pips)
{
bool alreadyDone = false;
for(int j = 0; j < trackedPositionCount; j++)
{
if(trackedPositions[j].ticket == ticket && trackedPositions[j].partialTPDone)
{
alreadyDone = true;
break;
}
}
if(!alreadyDone)
{
double closeVolume = NormalizeLot(volume * PartialTP_Percent / 100.0);
if(closeVolume >= minLot && closeVolume < volume)
{
if(trade.PositionClosePartial(ticket, closeVolume))
{
Print("Partial TP executed: Ticket ", ticket, " | Closed: ", closeVolume);
for(int j = 0; j < trackedPositionCount; j++)
{
if(trackedPositions[j].ticket == ticket)
{
trackedPositions[j].partialTPDone = true;
break;
}
}
}
}
}
}
//--- Breakeven
if(UseBreakeven && profitPips >= BreakevenTriggerPips)
{
double bePrice = openPrice + (BreakevenOffsetPips * pipSize *
(posType == POSITION_TYPE_BUY ? 1 : -1));
bool needsUpdate = false;
if(posType == POSITION_TYPE_BUY && (currentSL < bePrice || currentSL == 0))
needsUpdate = true;
else if(posType == POSITION_TYPE_SELL && (currentSL > bePrice || currentSL == 0))
needsUpdate = true;
if(needsUpdate)
{
bePrice = NormalizeDouble(bePrice, symbolDigits);
if(trade.PositionModify(ticket, bePrice, currentTP))
{
Print("Breakeven: Ticket ", ticket, " | SL: ", bePrice);
}
continue;
}
}
//--- Trailing stop
if(UseTrailingStop && profitPips >= TrailingActivationPips)
{
double trailDistance = TrailingStopPips * pipSize;
double trailStep = TrailingStepPips * pipSize;
double newSL = 0;
if(posType == POSITION_TYPE_BUY)
{
newSL = currentPrice - trailDistance;
if(newSL > currentSL + trailStep || currentSL == 0)
{
newSL = NormalizeDouble(newSL, symbolDigits);
if(newSL > openPrice)
{
trade.PositionModify(ticket, newSL, currentTP);
}
}
}
else if(posType == POSITION_TYPE_SELL)
{
newSL = currentPrice + trailDistance;
if(newSL < currentSL - trailStep || currentSL == 0)
{
newSL = NormalizeDouble(newSL, symbolDigits);
if(newSL < openPrice)
{
trade.PositionModify(ticket, newSL, currentTP);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Check if Error is Transient |
//+------------------------------------------------------------------+
bool IsTransientError(int error)
{
switch(error)
{
case 10004: case 10006: case 10007: case 10018:
case 10021: case 10025: case 10027:
return true;
default:
return false;
}
}
//+------------------------------------------------------------------+
//| Initialize Strategy Stats |
//+------------------------------------------------------------------+
void InitializeStrategyStats()
{
maStats.totalTrades = 0;
maStats.winningTrades = 0;
maStats.totalProfit = 0;
maStats.winRate = 0;
rsiStats = maStats;
bbStats = maStats;
macdStats = maStats;
}
//+------------------------------------------------------------------+
//| Update Strategy Stats |
//+------------------------------------------------------------------+
void UpdateStrategyStats(ulong positionID, double profit)
{
//--- For simplicity, attribute to MA strategy
//--- In a real implementation, you'd track which strategy opened each position
maStats.totalTrades++;
if(profit > 0) maStats.winningTrades++;
maStats.totalProfit += profit;
if(maStats.totalTrades > 0)
maStats.winRate = (double)maStats.winningTrades / maStats.totalTrades * 100.0;
}
//+------------------------------------------------------------------+
//| Print Strategy Performance |
//+------------------------------------------------------------------+
void PrintStrategyPerformance()
{
Print("========================================");
Print("Strategy Performance Summary");
Print("MA: Trades=", maStats.totalTrades, " WinRate=", maStats.winRate,
"% Profit=", maStats.totalProfit);
Print("========================================");
}
//+------------------------------------------------------------------+
//| Count Active Strategies |
//+------------------------------------------------------------------+
int CountActiveStrategies()
{
int count = 0;
if(UseMAStrategy) count++;
if(UseRSIStrategy) count++;
if(UseBBStrategy) count++;
if(UseMACDStrategy) count++;
return count;
}
//+------------------------------------------------------------------+
//| Initialize Multi-Symbol Trading |
//+------------------------------------------------------------------+
bool InitializeMultiSymbolTrading()
{
//--- Parse trading symbols
string symbols[];
int count = StringSplit(TradingSymbols, ',', symbols);
if(count <= 0)
{
Print("Error: No valid symbols found");
return false;
}
ArrayResize(symbolDataArray, count);
totalSymbols = 0;
//--- Initialize each symbol
for(int i = 0; i < count; i++)
{
string symbol = symbols[i];
StringTrimLeft(symbol);
StringTrimRight(symbol);
//--- Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("Warning: Symbol ", symbol, " not found or cannot be selected");
continue;
}
//--- Initialize symbol data
SymbolData data;
data.symbol = symbol;
data.point = SymbolInfoDouble(symbol, SYMBOL_POINT);
data.symbolDigits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
//--- Calculate pip size
if(data.symbolDigits == 3 || data.symbolDigits == 5)
data.pipSize = data.point * 10;
else
data.pipSize = data.point;
//--- Get trading properties
data.tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
data.tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE_PROFIT);
if(data.tickValue == 0)
{
double contractSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_CONTRACT_SIZE);
data.tickValue = data.tickSize * contractSize;
}
data.lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
data.minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
data.maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
//--- Initialize trading state
data.lastBuyTime = 0;
data.lastSellTime = 0;
data.lastCloseTime = 0;
data.positionCount = 0;
//--- Create indicators for this symbol
data.handleFastMA = INVALID_HANDLE;
data.handleSlowMA = INVALID_HANDLE;
data.handleADX = INVALID_HANDLE;
data.handleATR = INVALID_HANDLE;
data.handleRSI = INVALID_HANDLE;
data.handleBB = INVALID_HANDLE;
data.handleMACD = INVALID_HANDLE;
if(UseMAStrategy)
{
data.handleFastMA = iMA(symbol, PERIOD_CURRENT, FastMA_Period, 0, MA_Method, MA_Price);
data.handleSlowMA = iMA(symbol, PERIOD_CURRENT, SlowMA_Period, 0, MA_Method, MA_Price);
}
if(UseADXFilter)
{
data.handleADX = iADX(symbol, PERIOD_CURRENT, ADX_Period);
}
if(UseATRFilter)
{
data.handleATR = iATR(symbol, PERIOD_CURRENT, ATR_Period);
}
if(UseRSIStrategy)
{
data.handleRSI = iRSI(symbol, PERIOD_CURRENT, RSI_Period, RSI_Price);
}
if(UseBBStrategy)
{
data.handleBB = iBands(symbol, PERIOD_CURRENT, BB_Period, 0, BB_Deviation, BB_AppliedPrice);
}
if(UseMACDStrategy)
{
data.handleMACD = iMACD(symbol, PERIOD_CURRENT, MACD_FastEMA, MACD_SlowEMA, MACD_Signal, MACD_Price);
}
symbolDataArray[totalSymbols] = data;
totalSymbols++;
Print("Initialized symbol: ", symbol);
}
Print("Total symbols initialized: ", totalSymbols);
return (totalSymbols > 0);
}
//+------------------------------------------------------------------+
//| Process Trading for a Symbol |
//+------------------------------------------------------------------+
void ProcessSymbolTrading(int symbolIndex)
{
if(symbolIndex < 0 || symbolIndex >= totalSymbols)
return;
SymbolData data = symbolDataArray[symbolIndex];
string symbol = data.symbol;
//--- Count positions for this symbol
int symbolPositions = CountSymbolPositions(symbol);
data.positionCount = symbolPositions;
symbolDataArray[symbolIndex] = data;
//--- Check symbol position limit
if(symbolPositions >= MaxPositionsPerSymbol)
return;
//--- Check global position limit
if(CountAllPositions() >= MaxTotalPositions)
return;
//--- Manage existing positions for this symbol
ManageSymbolPositions(symbol);
//--- Check if can open new trade
if(!CanOpenNewTradeForSymbol(symbolIndex))
return;
//--- Check for entry signals
CheckSymbolEntrySignals(symbolIndex);
}
//+------------------------------------------------------------------+
//| Check Entry Signals for Symbol |
//+------------------------------------------------------------------+
void CheckSymbolEntrySignals(int symbolIndex)
{
SymbolData data = symbolDataArray[symbolIndex];
string symbol = data.symbol;
//--- Update indicator buffers (local variables with distinct names)
double localFastMA[], localSlowMA[], atrBuf[], adxBuf[], rsiBuf[];
double localBBUpper[], localBBMiddle[], localBBLower[];
double localMACDMain[], localMACDSignal[];
ArraySetAsSeries(localFastMA, true);
ArraySetAsSeries(localSlowMA, true);
ArraySetAsSeries(atrBuf, true);
ArraySetAsSeries(adxBuf, true);
ArraySetAsSeries(rsiBuf, true);
ArraySetAsSeries(localBBUpper, true);
ArraySetAsSeries(localBBMiddle, true);
ArraySetAsSeries(localBBLower, true);
ArraySetAsSeries(localMACDMain, true);
ArraySetAsSeries(localMACDSignal, true);
//--- Copy indicator data
if(UseMAStrategy)
{
if(CopyBuffer(data.handleFastMA, 0, 0, 3, localFastMA) < 3) return;
if(CopyBuffer(data.handleSlowMA, 0, 0, 3, localSlowMA) < 3) return;
}
if(UseATRFilter)
{
if(CopyBuffer(data.handleATR, 0, 0, 1, atrBuf) < 1) return;
}
if(UseADXFilter)
{
if(CopyBuffer(data.handleADX, 0, 0, 1, adxBuf) < 1) return;
}
if(UseRSIStrategy)
{
if(CopyBuffer(data.handleRSI, 0, 0, 2, rsiBuf) < 2) return;
}
if(UseBBStrategy)
{
if(CopyBuffer(data.handleBB, 0, 0, 2, localBBUpper) < 2) return;
if(CopyBuffer(data.handleBB, 1, 0, 2, localBBMiddle) < 2) return;
if(CopyBuffer(data.handleBB, 2, 0, 2, localBBLower) < 2) return;
}
if(UseMACDStrategy)
{
if(CopyBuffer(data.handleMACD, 0, 0, 3, localMACDMain) < 3) return;
if(CopyBuffer(data.handleMACD, 1, 0, 3, localMACDSignal) < 3) return;
}
//--- Check basic filters
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
double spread = (ask - bid) / data.pipSize;
if(spread > MaxSpreadPips) return;
if(UseATRFilter && ArraySize(atrBuf) > 0)
{
double atrPips = atrBuf[0] / data.pipSize;
if(atrPips < ATR_MinimumPips || atrPips > ATR_MaximumPips) return;
}
if(UseADXFilter && ArraySize(adxBuf) > 0)
{
if(adxBuf[0] < ADX_Minimum) return;
}
//--- Generate signals
int buySignalScore = 0;
int sellSignalScore = 0;
string buyReasons = "";
string sellReasons = "";
//--- MA Strategy
if(UseMAStrategy && ArraySize(localFastMA) >= 3)
{
bool bullishCross = localFastMA[1] > localSlowMA[1] && localFastMA[2] <= localSlowMA[2];
bool bearishCross = localFastMA[1] < localSlowMA[1] && localFastMA[2] >= localSlowMA[2];
if(bullishCross)
{
double maSlope = MathAbs(localFastMA[1] - localFastMA[2]) / data.pipSize;
if(maSlope >= MA_SlopeMinimum)
{
buySignalScore += 30;
buyReasons += "MA Bullish | ";
}
}
else if(bearishCross)
{
double maSlope = MathAbs(localFastMA[1] - localFastMA[2]) / data.pipSize;
if(maSlope >= MA_SlopeMinimum)
{
sellSignalScore += 30;
sellReasons += "MA Bearish | ";
}
}
}
//--- RSI Strategy
if(UseRSIStrategy && ArraySize(rsiBuf) >= 2)
{
if(rsiBuf[1] < RSI_Oversold && rsiBuf[0] > RSI_Oversold)
{
buySignalScore += 25;
buyReasons += "RSI Oversold Bounce | ";
}
if(rsiBuf[1] > RSI_Overbought && rsiBuf[0] < RSI_Overbought)
{
sellSignalScore += 25;
sellReasons += "RSI Overbought Fall | ";
}
}
//--- BB Strategy
if(UseBBStrategy && ArraySize(localBBLower) >= 2)
{
double close = iClose(symbol, PERIOD_CURRENT, 0);
double closePrev = iClose(symbol, PERIOD_CURRENT, 1);
if(closePrev <= localBBLower[1] && close > localBBLower[0])
{
buySignalScore += 25;
buyReasons += "BB Lower Bounce | ";
}
if(closePrev >= localBBUpper[1] && close < localBBUpper[0])
{
sellSignalScore += 25;
sellReasons += "BB Upper Fall | ";
}
}
//--- MACD Strategy
if(UseMACDStrategy && ArraySize(localMACDMain) >= 3)
{
if(localMACDMain[1] > localMACDSignal[1] && localMACDMain[2] <= localMACDSignal[2])
{
buySignalScore += 20;
buyReasons += "MACD Bullish | ";
}
if(localMACDMain[1] < localMACDSignal[1] && localMACDMain[2] >= localMACDSignal[2])
{
sellSignalScore += 20;
sellReasons += "MACD Bearish | ";
}
}
//--- Process buy signal
if(buySignalScore >= MinSignalScore)
{
Print("=== BUY SIGNAL [", symbol, "] | Score: ", buySignalScore, " ===");
Print("Reasons: ", buyReasons);
if(OpenSymbolPosition(symbolIndex, ORDER_TYPE_BUY))
{
data.lastBuyTime = TimeCurrent();
symbolDataArray[symbolIndex] = data;
dailyTradeCount++;
}
}
//--- Process sell signal
if(sellSignalScore >= MinSignalScore)
{
Print("=== SELL SIGNAL [", symbol, "] | Score: ", sellSignalScore, " ===");
Print("Reasons: ", sellReasons);
if(OpenSymbolPosition(symbolIndex, ORDER_TYPE_SELL))
{
data.lastSellTime = TimeCurrent();
symbolDataArray[symbolIndex] = data;
dailyTradeCount++;
}
}
}
//+------------------------------------------------------------------+
//| Open Position for Symbol |
//+------------------------------------------------------------------+
bool OpenSymbolPosition(int symbolIndex, ENUM_ORDER_TYPE orderType)
{
SymbolData data = symbolDataArray[symbolIndex];
string symbol = data.symbol;
double price = (orderType == ORDER_TYPE_BUY) ?
SymbolInfoDouble(symbol, SYMBOL_ASK) :
SymbolInfoDouble(symbol, SYMBOL_BID);
//--- Calculate SL and TP
double sl = CalculateSymbolStopLoss(symbolIndex, orderType, price);
double tp = CalculateSymbolTakeProfit(symbolIndex, orderType, price);
//--- Validate trade
if(!ValidateSymbolTrade(symbolIndex, orderType, price, sl, tp))
return false;
//--- Calculate lot size
double riskPercent = CalculateDynamicRisk();
double lots = CalculateSymbolLotSize(symbolIndex, price, sl, riskPercent);
//--- Normalize
sl = NormalizeDouble(sl, data.symbolDigits);
tp = NormalizeDouble(tp, data.symbolDigits);
lots = NormalizeSymbolLot(symbolIndex, lots);
if(lots < data.minLot || lots > data.maxLot)
return false;
//--- Open with retries
bool success = false;
int attempts = 0;
while(attempts < MaxRetries && !success)
{
ResetLastError();
if(orderType == ORDER_TYPE_BUY)
success = trade.Buy(lots, symbol, 0, sl, tp, TradeComment);
else
success = trade.Sell(lots, symbol, 0, sl, tp, TradeComment);
if(success)
{
Print("Position opened: ", symbol, " ", EnumToString(orderType), " | Lots: ", lots);
//--- Track for partial TP
if(UsePartialTP && trackedPositionCount < 100)
{
trackedPositions[trackedPositionCount].ticket = trade.ResultOrder();
trackedPositions[trackedPositionCount].partialTPDone = false;
trackedPositionCount++;
}
return true;
}
else
{
int error = GetLastError();
if(IsTransientError(error))
{
Sleep(RetryDelayMs);
attempts++;
}
else
{
break;
}
}
}
return false;
}
//+------------------------------------------------------------------+
//| Calculate Stop Loss for Symbol |
//+------------------------------------------------------------------+
double CalculateSymbolStopLoss(int symbolIndex, ENUM_ORDER_TYPE orderType, double price)
{
SymbolData data = symbolDataArray[symbolIndex];
double slDistance;
if(UseATRSizing && data.handleATR != INVALID_HANDLE)
{
double atrBuf[];
ArraySetAsSeries(atrBuf, true);
if(CopyBuffer(data.handleATR, 0, 0, 1, atrBuf) >= 1)
slDistance = atrBuf[0] * ATR_SL_Multiplier;
else
slDistance = StopLossPips * data.pipSize;
}
else
{
slDistance = StopLossPips * data.pipSize;
}
double sl = (orderType == ORDER_TYPE_BUY) ?
price - slDistance : price + slDistance;
return sl;
}
//+------------------------------------------------------------------+
//| Calculate Take Profit for Symbol |
//+------------------------------------------------------------------+
double CalculateSymbolTakeProfit(int symbolIndex, ENUM_ORDER_TYPE orderType, double price)
{
SymbolData data = symbolDataArray[symbolIndex];
double tpDistance;
if(UseATRSizing && data.handleATR != INVALID_HANDLE)
{
double atrBuf[];
ArraySetAsSeries(atrBuf, true);
if(CopyBuffer(data.handleATR, 0, 0, 1, atrBuf) >= 1)
tpDistance = atrBuf[0] * ATR_TP_Multiplier;
else
tpDistance = TakeProfitPips * data.pipSize;
}
else
{
tpDistance = TakeProfitPips * data.pipSize;
}
double tp = (orderType == ORDER_TYPE_BUY) ?
price + tpDistance : price - tpDistance;
return tp;
}
//+------------------------------------------------------------------+
//| Validate Trade for Symbol |
//+------------------------------------------------------------------+
bool ValidateSymbolTrade(int symbolIndex, ENUM_ORDER_TYPE orderType, double price, double sl, double tp)
{
SymbolData data = symbolDataArray[symbolIndex];
long stopsLevelPoints = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_STOPS_LEVEL);
double stopsLevel = stopsLevelPoints * data.point;
if(MathAbs(price - sl) < stopsLevel) return false;
if(MathAbs(price - tp) < stopsLevel) return false;
return true;
}
//+------------------------------------------------------------------+
//| Calculate Lot Size for Symbol |
//+------------------------------------------------------------------+
double CalculateSymbolLotSize(int symbolIndex, double entryPrice, double stopLoss, double riskPercent)
{
SymbolData data = symbolDataArray[symbolIndex];
if(UseFixedLot)
return FixedLotSize;
double balance = UseCompounding ? accountInfo.Equity() : accountInfo.Balance();
double riskAmount = balance * (riskPercent / 100.0);
double slDistance = MathAbs(entryPrice - stopLoss);
if(slDistance <= 0) return data.minLot;
double ticksDistance = slDistance / data.tickSize;
double riskPerLot = ticksDistance * data.tickValue;
if(riskPerLot <= 0) return data.minLot;
double lots = riskAmount / riskPerLot;
if(lots > MaxLotSize) lots = MaxLotSize;
if(lots < MinLotSize) lots = MinLotSize;
return lots;
}
//+------------------------------------------------------------------+
//| Normalize Lot Size for Symbol |
//+------------------------------------------------------------------+
double NormalizeSymbolLot(int symbolIndex, double lots)
{
SymbolData data = symbolDataArray[symbolIndex];
lots = MathRound(lots / data.lotStep) * data.lotStep;
if(lots < data.minLot) lots = data.minLot;
if(lots > data.maxLot) lots = data.maxLot;
return NormalizeDouble(lots, 2);
}
//+------------------------------------------------------------------+
//| Count Positions for Symbol |
//+------------------------------------------------------------------+
int CountSymbolPositions(string symbol)
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(positionInfo.SelectByIndex(i))
{
if(positionInfo.Symbol() == symbol && positionInfo.Magic() == MagicNumber)
{
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| Count All Positions |
//+------------------------------------------------------------------+
int CountAllPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(positionInfo.SelectByIndex(i))
{
if(positionInfo.Magic() == MagicNumber)
{
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| Manage Positions for Symbol |
//+------------------------------------------------------------------+
void ManageSymbolPositions(string symbol)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!positionInfo.SelectByIndex(i))
continue;
if(positionInfo.Symbol() != symbol || positionInfo.Magic() != MagicNumber)
continue;
ulong ticket = positionInfo.Ticket();
double openPrice = positionInfo.PriceOpen();
double currentSL = positionInfo.StopLoss();
double currentTP = positionInfo.TakeProfit();
ENUM_POSITION_TYPE posType = positionInfo.PositionType();
double volume = positionInfo.Volume();
double currentPrice = (posType == POSITION_TYPE_BUY) ?
SymbolInfoDouble(symbol, SYMBOL_BID) :
SymbolInfoDouble(symbol, SYMBOL_ASK);
double symbolPipSize = GetSymbolPipSize(symbol);
double profitPips = 0;
if(posType == POSITION_TYPE_BUY)
profitPips = (currentPrice - openPrice) / symbolPipSize;
else
profitPips = (openPrice - currentPrice) / symbolPipSize;
//--- Partial TP
if(UsePartialTP && profitPips >= PartialTP_Pips)
{
bool alreadyDone = false;
for(int j = 0; j < trackedPositionCount; j++)
{
if(trackedPositions[j].ticket == ticket && trackedPositions[j].partialTPDone)
{
alreadyDone = true;
break;
}
}
if(!alreadyDone)
{
double symbolMinLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double closeVolume = MathRound((volume * PartialTP_Percent / 100.0) / symbolMinLot) * symbolMinLot;
if(closeVolume >= symbolMinLot && closeVolume < volume)
{
if(trade.PositionClosePartial(ticket, closeVolume))
{
Print("Partial TP: ", symbol, " Ticket ", ticket, " | Closed: ", closeVolume);
for(int j = 0; j < trackedPositionCount; j++)
{
if(trackedPositions[j].ticket == ticket)
{
trackedPositions[j].partialTPDone = true;
break;
}
}
}
}
}
}
//--- Breakeven
if(UseBreakeven && profitPips >= BreakevenTriggerPips)
{
double bePrice = openPrice + (BreakevenOffsetPips * pipSize *
(posType == POSITION_TYPE_BUY ? 1 : -1));
bool needsUpdate = false;
if(posType == POSITION_TYPE_BUY && (currentSL < bePrice || currentSL == 0))
needsUpdate = true;
else if(posType == POSITION_TYPE_SELL && (currentSL > bePrice || currentSL == 0))
needsUpdate = true;
if(needsUpdate)
{
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
bePrice = NormalizeDouble(bePrice, digits);
if(trade.PositionModify(ticket, bePrice, currentTP))
{
Print("Breakeven: ", symbol, " Ticket ", ticket, " | SL: ", bePrice);
}
continue;
}
}
//--- Trailing stop
if(UseTrailingStop && profitPips >= TrailingActivationPips)
{
double trailDistance = TrailingStopPips * pipSize;
double trailStep = TrailingStepPips * pipSize;
double newSL = 0;
if(posType == POSITION_TYPE_BUY)
{
newSL = currentPrice - trailDistance;
if(newSL > currentSL + trailStep || currentSL == 0)
{
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
newSL = NormalizeDouble(newSL, digits);
if(newSL > openPrice)
{
trade.PositionModify(ticket, newSL, currentTP);
}
}
}
else if(posType == POSITION_TYPE_SELL)
{
newSL = currentPrice + trailDistance;
if(newSL < currentSL - trailStep || currentSL == 0)
{
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
newSL = NormalizeDouble(newSL, digits);
if(newSL < openPrice)
{
trade.PositionModify(ticket, newSL, currentTP);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Get Symbol Pip Size |
//+------------------------------------------------------------------+
double GetSymbolPipSize(string symbol)
{
double symbolPoint = SymbolInfoDouble(symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
if(digits == 3 || digits == 5)
return symbolPoint * 10;
else
return symbolPoint;
}
//+------------------------------------------------------------------+
//| Can Open New Trade for Symbol |
//+------------------------------------------------------------------+
bool CanOpenNewTradeForSymbol(int symbolIndex)
{
SymbolData data = symbolDataArray[symbolIndex];
//--- Check daily trade limit
if(dailyTradeCount >= MaxDailyTrades)
return false;
//--- Check cooldown
bool isBuySignal = true; // Will be determined by signal
datetime relevantTime = SeparateCooldownByDirection ?
(isBuySignal ? data.lastBuyTime : data.lastSellTime) :
MathMax(data.lastBuyTime, data.lastSellTime);
if((TimeCurrent() - relevantTime) < (CooldownMinutes * 60))
return false;
//--- Check post-close cooldown
if((TimeCurrent() - data.lastCloseTime) < (PostCloseCooldownMinutes * 60))
return false;
//--- Check session filter
if(UseSessionFilter && !IsWithinTradingSession())
return false;
return true;
}
//+------------------------------------------------------------------+
//| Check Daily Drawdown Limit |
//+------------------------------------------------------------------+
bool CheckDailyDrawdownLimit()
{
double currentEquity = accountInfo.Equity();
if(startOfDayBalance <= 0)
return true;
double dailyLoss = startOfDayBalance - currentEquity;
double dailyLossPercent = (dailyLoss / startOfDayBalance) * 100.0;
if(dailyLossPercent > DailyDrawdownPercent)
{
Print("Daily drawdown limit hit: ", dailyLossPercent, "% (limit: ", DailyDrawdownPercent, "%)");
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Check Floating Equity Limit |
//+------------------------------------------------------------------+
bool CheckFloatingEquityLimit()
{
double balance = accountInfo.Balance();
double equity = accountInfo.Equity();
if(balance <= 0)
return true;
double floatingLoss = balance - equity;
double floatingLossPercent = (floatingLoss / balance) * 100.0;
if(floatingLossPercent > MaxFloatingEquityPercent)
{
Print("Floating equity limit hit: ", floatingLossPercent, "% (limit: ", MaxFloatingEquityPercent, "%)");
return false;
}
return true;
}
//+------------------------------------------------------------------+