diff --git a/Experts/test.mq5 b/Experts/test.mq5 new file mode 100644 index 00000000..d9cb3d33 --- /dev/null +++ b/Experts/test.mq5 @@ -0,0 +1,816 @@ +//+------------------------------------------------------------------+ +//| X121_Strategy_EA.mq5 | +//| Copyright 2023, SaherElm IT | +//| https://saherelm.ir/ | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2023, SaherElm IT Center" +#property link "https://saherelm.ir" +#property version "1.00" +#property description "EA based on X121 XCA and X3MA indicators" +#property strict + +// Include Common Library +#include "../Libraries/x-saherelm.common.lib.mq5" +#include + +// Indicator handles +int xcaHandle = INVALID_HANDLE; +int x3maHandle = INVALID_HANDLE; + +// Indicator buffers for XCA +double xcaTrendBuffer[]; +double xcaTrendColorBuffer[]; +double xcaKIBuffer[]; +double xcaKIColorBuffer[]; +double xcaVidyaBuffer[]; +double xcaVidyaColorBuffer[]; +double xcaSwingLowBuffer[]; +double xcaSwingHighBuffer[]; +double xcaSupportBuffer[]; +double xcaResistanceBuffer[]; +double xcaSLBuffer[]; // Short Cycle Low +double xcaSHBuffer[]; // Short Cycle High +double xcaMLBuffer[]; // Medium Cycle Low +double xcaMHBuffer[]; // Medium Cycle High +double xcaFiboLevel2Buffer[]; // 50% Fibo level + +// Indicator buffers for X3MA +double x3maFastBuffer[]; +double x3maFastColorBuffer[]; +double x3maMediumBuffer[]; +double x3maMediumColorBuffer[]; +double x3maSlowBuffer[]; +double x3maSlowColorBuffer[]; + +// Trade parameters +input group "Trade Settings" input double LotSize = 0.1; // Fixed Lot Size +input bool UseAutoLotSize = false; // Use Auto Lot Size +input double RiskPercent = 1.0; // Risk Percent (for Auto Lot) +input int StopLoss = 100; // Stop Loss in points +input int TakeProfit = 200; // Take Profit in points +input bool UseTrailingStop = true; // Use Trailing Stop +input int TrailingStop = 50; // Trailing Stop in points +input int TrailingStep = 10; // Trailing Step in points + +input group "Strategy Parameters" input bool UseSwingPoints = true; // Use Swing Points for Entry +input bool UseFiboLevels = true; // Use Fibonacci Levels +input bool UseMAAlignment = true; // Require All MAs Alignment +input bool UseKIConfirmation = true; // Require KI Confirmation +input bool UseVidyaConfirmation = true; // Require VIDYA Confirmation +input bool UseTrendConfirmation = true; // Require Trend Confirmation + +input group "Money Management" input bool UseMultipleTargets = true; // Use Multiple Profit Targets +input double Target1Percent = 33.0; // First Target Percentage +input double Target2Percent = 33.0; // Second Target Percentage +input double Target3Percent = 34.0; // Third Target Percentage + +input group "Time Filter" input bool UseTimeFilter = false; // Use Time Filter +input string StartTradeTime = "08:00"; // Start Trading Time (Server Time) +input string EndTradeTime = "16:00"; // End Trading Time (Server Time) + +// Global variables +int magicNumber = 12121; +bool isTradingAllowed = true; +datetime lastBarTime = 0; +int barsTotalPrev = 0; + +// Trade objects +CTrade trade; + +//+------------------------------------------------------------------+ +//| Expert initialization function | +//+------------------------------------------------------------------+ +int OnInit() +{ + // Initialize trade object + trade.SetExpertMagicNumber(magicNumber); + + // Initialize XCA indicator + xcaHandle = iCustom(_Symbol, _Period, "x-saherelm.x121.xca"); + if (xcaHandle == INVALID_HANDLE) + { + Print("Failed to create handle of the XCA indicator"); + return (INIT_FAILED); + } + + // Initialize X3MA indicator + x3maHandle = iCustom(_Symbol, _Period, "x-saherelm.x121.x3ma"); + if (x3maHandle == INVALID_HANDLE) + { + Print("Failed to create handle of the X3MA indicator"); + return (INIT_FAILED); + } + + // Set up arrays as series + ArraySetAsSeries(xcaTrendBuffer, true); + ArraySetAsSeries(xcaTrendColorBuffer, true); + ArraySetAsSeries(xcaKIBuffer, true); + ArraySetAsSeries(xcaKIColorBuffer, true); + ArraySetAsSeries(xcaVidyaBuffer, true); + ArraySetAsSeries(xcaVidyaColorBuffer, true); + ArraySetAsSeries(xcaSwingLowBuffer, true); + ArraySetAsSeries(xcaSwingHighBuffer, true); + ArraySetAsSeries(xcaSupportBuffer, true); + ArraySetAsSeries(xcaResistanceBuffer, true); + ArraySetAsSeries(xcaSLBuffer, true); + ArraySetAsSeries(xcaSHBuffer, true); + ArraySetAsSeries(xcaMLBuffer, true); + ArraySetAsSeries(xcaMHBuffer, true); + ArraySetAsSeries(xcaFiboLevel2Buffer, true); + + ArraySetAsSeries(x3maFastBuffer, true); + ArraySetAsSeries(x3maFastColorBuffer, true); + ArraySetAsSeries(x3maMediumBuffer, true); + ArraySetAsSeries(x3maMediumColorBuffer, true); + ArraySetAsSeries(x3maSlowBuffer, true); + ArraySetAsSeries(x3maSlowColorBuffer, true); + + return (INIT_SUCCEEDED); +} + +//+------------------------------------------------------------------+ +//| Expert deinitialization function | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) +{ + // Release indicator handles + if (xcaHandle != INVALID_HANDLE) + IndicatorRelease(xcaHandle); + + if (x3maHandle != INVALID_HANDLE) + IndicatorRelease(x3maHandle); +} + +//+------------------------------------------------------------------+ +//| Expert tick function | +//+------------------------------------------------------------------+ +void OnTick() +{ + // Check if we're allowed to trade based on time filter + if (UseTimeFilter && !IsTradeTimeAllowed()) + return; + + // Get current bar time + datetime currentBarTime = iTime(_Symbol, _Period, 0); + + // Only process on new bar + if (currentBarTime == lastBarTime) + return; + + lastBarTime = currentBarTime; + + // Update indicator buffers + if (!UpdateIndicatorBuffers()) + return; + + // Check for open positions and manage them + ManageOpenPositions(); + + // Check for new trade signals + CheckForTradeSignals(); +} + +//+------------------------------------------------------------------+ +//| Update all indicator buffers | +//+------------------------------------------------------------------+ +bool UpdateIndicatorBuffers() +{ + // Copy XCA indicator buffers + if (CopyBuffer(xcaHandle, 18, 0, 3, xcaTrendBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 19, 0, 3, xcaTrendColorBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 20, 0, 3, xcaKIBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 21, 0, 3, xcaKIColorBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 26, 0, 3, xcaVidyaBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 27, 0, 3, xcaVidyaColorBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 22, 0, 3, xcaSwingLowBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 23, 0, 3, xcaSwingHighBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 8, 0, 3, xcaSupportBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 9, 0, 3, xcaResistanceBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 0, 0, 3, xcaSHBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 1, 0, 3, xcaSLBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 2, 0, 3, xcaMHBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 3, 0, 3, xcaMLBuffer) < 3) + return false; + if (CopyBuffer(xcaHandle, 15, 0, 3, xcaFiboLevel2Buffer) < 3) + return false; + + // Copy X3MA indicator buffers + if (CopyBuffer(x3maHandle, 0, 0, 3, x3maFastBuffer) < 3) + return false; + if (CopyBuffer(x3maHandle, 1, 0, 3, x3maFastColorBuffer) < 3) + return false; + if (CopyBuffer(x3maHandle, 2, 0, 3, x3maMediumBuffer) < 3) + return false; + if (CopyBuffer(x3maHandle, 3, 0, 3, x3maMediumColorBuffer) < 3) + return false; + if (CopyBuffer(x3maHandle, 4, 0, 3, x3maSlowBuffer) < 3) + return false; + if (CopyBuffer(x3maHandle, 5, 0, 3, x3maSlowColorBuffer) < 3) + return false; + + return true; +} + +//+------------------------------------------------------------------+ +//| Check if current time is within allowed trading hours | +//+------------------------------------------------------------------+ +bool IsTradeTimeAllowed() +{ + // If time filter is not enabled, always allow trading + if (!UseTimeFilter) + return true; + + // Get current server time + datetime serverTime = TimeCurrent(); + MqlDateTime currentTime; + TimeToStruct(serverTime, currentTime); + + // Convert start and end time strings to hours and minutes + int startHour = 0, startMinute = 0; + int endHour = 0, endMinute = 0; + + // Parse start time + string startTimeParts[]; + if (StringSplit(StartTradeTime, ':', startTimeParts) == 2) + { + startHour = (int)StringToInteger(startTimeParts[0]); + startMinute = (int)StringToInteger(startTimeParts[1]); + } + + // Parse end time + string endTimeParts[]; + if (StringSplit(EndTradeTime, ':', endTimeParts) == 2) + { + endHour = (int)StringToInteger(endTimeParts[0]); + endMinute = (int)StringToInteger(endTimeParts[1]); + } + + // Convert current time, start time and end time to minutes for easy comparison + int currentTimeInMinutes = currentTime.hour * 60 + currentTime.min; + int startTimeInMinutes = startHour * 60 + startMinute; + int endTimeInMinutes = endHour * 60 + endMinute; + + // Check if current time is within trading hours + bool isAllowed = false; + + // If end time is greater than start time (same day trading session) + if (endTimeInMinutes > startTimeInMinutes) + { + isAllowed = (currentTimeInMinutes >= startTimeInMinutes && + currentTimeInMinutes <= endTimeInMinutes); + } + // If end time is less than start time (overnight trading session) + else if (endTimeInMinutes < startTimeInMinutes) + { + isAllowed = (currentTimeInMinutes >= startTimeInMinutes || + currentTimeInMinutes <= endTimeInMinutes); + } + // If start time equals end time (24-hour trading) + else + { + isAllowed = true; + } + + return isAllowed; +} + +//+------------------------------------------------------------------+ +//| Check for new trade signals | +//+------------------------------------------------------------------+ +void CheckForTradeSignals() +{ + // Don't open new trades if we already have open positions + if (CountOpenPositions() > 0) + return; + + // Check for buy signal + if (IsBuySignal()) + { + OpenBuyPosition(); + } + // Check for sell signal + else if (IsSellSignal()) + { + OpenSellPosition(); + } +} + +//+------------------------------------------------------------------+ +//| Check if we have a valid buy signal | +//+------------------------------------------------------------------+ +bool IsBuySignal() +{ + // 1. Trend Confirmation + bool trendConfirmation = true; + if (UseTrendConfirmation) + { + // X3MA: Fast MA crosses above Medium MA + bool fastCrossingAboveMedium = x3maFastBuffer[1] <= x3maMediumBuffer[1] && + x3maFastBuffer[0] > x3maMediumBuffer[0]; + + // X3MA: All three MAs are in bullish alignment + bool maAlignment = true; + if (UseMAAlignment) + { + maAlignment = x3maFastBuffer[0] > x3maMediumBuffer[0] && + x3maMediumBuffer[0] > x3maSlowBuffer[0]; + } + + // XCA: VIDYA line shows bullish color + bool vidyaBullish = true; + if (UseVidyaConfirmation) + { + vidyaBullish = xcaVidyaColorBuffer[0] == 1; // 1 is bullish color index + } + + // XCA: Trend line shows bullish color + bool trendBullish = xcaTrendColorBuffer[0] == 1; // 1 is bullish color index + + trendConfirmation = (fastCrossingAboveMedium || maAlignment) && + (vidyaBullish || !UseVidyaConfirmation) && + trendBullish; + } + + // 2. Cycle Analysis + bool cycleAnalysis = true; + { + // XCA: Price is above the Short Cycle low + double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); + bool priceAboveSL = currentPrice > xcaSLBuffer[0]; + + // XCA: Medium Cycle is in uptrend phase + bool mediumCycleUptrend = xcaMLBuffer[0] > xcaMLBuffer[1]; + + // XCA: Support level is established and holding + bool supportHolding = currentPrice > xcaSupportBuffer[0]; + + cycleAnalysis = priceAboveSL && mediumCycleUptrend && supportHolding; + } + + // 3. Entry Timing + bool entryTiming = true; + { + // XCA: A swing low is identified + bool swingLowFound = UseSwingPoints ? xcaSwingLowBuffer[0] != 0 : true; + + // XCA: Price is near but above the Fibonacci level 2 (50% retracement) + bool nearFiboLevel = true; + if (UseFiboLevels) + { + double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); + double fiboLevel = xcaFiboLevel2Buffer[0]; + double priceDiff = MathAbs(currentPrice - fiboLevel); + double priceRange = xcaResistanceBuffer[0] - xcaSupportBuffer[0]; + nearFiboLevel = priceDiff < priceRange * 0.1 && currentPrice > fiboLevel; + } + + // XCA: KI line shows bullish color + bool kiBullish = true; + if (UseKIConfirmation) + { + kiBullish = xcaKIColorBuffer[0] == 1; // 1 is bullish color index + } + + entryTiming = swingLowFound && (nearFiboLevel || !UseFiboLevels) && + (kiBullish || !UseKIConfirmation); + } + + return trendConfirmation && cycleAnalysis && entryTiming; +} + +//+------------------------------------------------------------------+ +//| Check if we have a valid sell signal | +//+------------------------------------------------------------------+ +bool IsSellSignal() +{ + // 1. Trend Confirmation + bool trendConfirmation = true; + if (UseTrendConfirmation) + { + // X3MA: Fast MA crosses below Medium MA + bool fastCrossingBelowMedium = x3maFastBuffer[1] >= x3maMediumBuffer[1] && + x3maFastBuffer[0] < x3maMediumBuffer[0]; + + // X3MA: All three MAs are in bearish alignment + bool maAlignment = true; + if (UseMAAlignment) + { + maAlignment = x3maFastBuffer[0] < x3maMediumBuffer[0] && + x3maMediumBuffer[0] < x3maSlowBuffer[0]; + } + + // XCA: VIDYA line shows bearish color + bool vidyaBearish = true; + if (UseVidyaConfirmation) + { + vidyaBearish = xcaVidyaColorBuffer[0] == 2; // 2 is bearish color index + } + + // XCA: Trend line shows bearish color + bool trendBearish = xcaTrendColorBuffer[0] == 2; // 2 is bearish color index + + trendConfirmation = (fastCrossingBelowMedium || maAlignment) && + (vidyaBearish || !UseVidyaConfirmation) && + trendBearish; + } + + // 2. Cycle Analysis + bool cycleAnalysis = true; + { + // XCA: Price is below the Short Cycle high + double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); + bool priceBelowSH = currentPrice < xcaSHBuffer[0]; + + // XCA: Medium Cycle is in downtrend phase + bool mediumCycleDowntrend = xcaMHBuffer[0] < xcaMHBuffer[1]; + + // XCA: Resistance level is established and holding + bool resistanceHolding = currentPrice < xcaResistanceBuffer[0]; + + cycleAnalysis = priceBelowSH && mediumCycleDowntrend && resistanceHolding; + } + + // 3. Entry Timing + bool entryTiming = true; + { + // XCA: A swing high is identified + bool swingHighFound = UseSwingPoints ? xcaSwingHighBuffer[0] != 0 : true; + + // XCA: Price is near but below the Fibonacci level 2 (50% retracement) + bool nearFiboLevel = true; + if (UseFiboLevels) + { + double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); + double fiboLevel = xcaFiboLevel2Buffer[0]; + double priceDiff = MathAbs(currentPrice - fiboLevel); + double priceRange = xcaResistanceBuffer[0] - xcaSupportBuffer[0]; + nearFiboLevel = priceDiff < priceRange * 0.1 && currentPrice < fiboLevel; + } + + // XCA: KI line shows bearish color + bool kiBearish = true; + if (UseKIConfirmation) + { + kiBearish = xcaKIColorBuffer[0] == 2; // 2 is bearish color index + } + + entryTiming = swingHighFound && (nearFiboLevel || !UseFiboLevels) && + (kiBearish || !UseKIConfirmation); + } + + return trendConfirmation && cycleAnalysis && entryTiming; +} + +//+------------------------------------------------------------------+ +//| Count open positions with our magic number | +//+------------------------------------------------------------------+ +int CountOpenPositions() +{ + int count = 0; + int total = PositionsTotal(); + + for (int i = 0; i < total; i++) + { + ulong ticket = PositionGetTicket(i); + if (ticket <= 0) + continue; + + // Check if position belongs to current symbol and our EA + if (PositionGetString(POSITION_SYMBOL) == _Symbol && + PositionGetInteger(POSITION_MAGIC) == magicNumber) + { + count++; + } + } + + return count; +} + +//+------------------------------------------------------------------+ +//| Manage open positions (trailing stops, partial closes) | +//+------------------------------------------------------------------+ +void ManageOpenPositions() +{ + int total = PositionsTotal(); + + // Loop through all open positions + for (int i = total - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if (ticket <= 0) + continue; + + // Check if position belongs to current symbol and our EA + if (PositionGetString(POSITION_SYMBOL) != _Symbol || + PositionGetInteger(POSITION_MAGIC) != magicNumber) + continue; + + // Get position details + double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); + double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT); + double stopLoss = PositionGetDouble(POSITION_SL); + double takeProfit = PositionGetDouble(POSITION_TP); + double positionVolume = PositionGetDouble(POSITION_VOLUME); + ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); + + // Check for exit signals + bool exitSignal = false; + + // For BUY positions + if (positionType == POSITION_TYPE_BUY) + { + // Exit if Fast MA crosses below Medium MA + if (x3maFastBuffer[1] > x3maMediumBuffer[1] && x3maFastBuffer[0] <= x3maMediumBuffer[0]) + exitSignal = true; + + // Apply trailing stop if enabled + if (UseTrailingStop && !exitSignal) + { + // Calculate new stop loss level + double newStopLoss = 0; + + // Use the most recent swing low as trailing stop if available + if (xcaSwingLowBuffer[0] > 0 && xcaSwingLowBuffer[0] < currentPrice) + { + newStopLoss = xcaSwingLowBuffer[0] - 5 * _Point; + } + else + { + // Traditional trailing stop + double trailingLevel = currentPrice - TrailingStop * _Point; + if (trailingLevel > stopLoss + TrailingStep * _Point) + newStopLoss = trailingLevel; + } + + // Update stop loss if we have a better level + if (newStopLoss > stopLoss && newStopLoss > 0) + { + trade.PositionModify(ticket, newStopLoss, takeProfit); + Print("Updated BUY trailing stop to: ", newStopLoss); + } + } + + // Handle multiple targets + if (UseMultipleTargets && positionVolume > LotSize * 0.5) + { + // First target: When price reaches resistance or initial TP + if (currentPrice >= xcaResistanceBuffer[0] && xcaResistanceBuffer[0] > openPrice) + { + double closeVolume = NormalizeDouble(positionVolume * Target1Percent / 100, 2); + if (closeVolume > 0) + { + trade.PositionClosePartial(ticket, closeVolume); + Print("First target reached for BUY position. Partially closed: ", closeVolume); + } + } + + // Second target: When price reaches Fibonacci level 3 (76.4%) + double fiboLevel3Buffer[]; + ArraySetAsSeries(fiboLevel3Buffer, true); + if (CopyBuffer(xcaHandle, 16, 0, 1, fiboLevel3Buffer) > 0) + { + double fiboLevel3 = fiboLevel3Buffer[0]; + if (fiboLevel3 > 0 && currentPrice >= fiboLevel3 && fiboLevel3 > openPrice) + { + double closeVolume = NormalizeDouble(positionVolume * Target2Percent / 100, 2); + if (closeVolume > 0) + { + trade.PositionClosePartial(ticket, closeVolume); + Print("Second target reached for BUY position. Partially closed: ", closeVolume); + } + } + } + } + } + // For SELL positions + else if (positionType == POSITION_TYPE_SELL) + { + // Exit if Fast MA crosses above Medium MA + if (x3maFastBuffer[1] < x3maMediumBuffer[1] && x3maFastBuffer[0] >= x3maMediumBuffer[0]) + exitSignal = true; + + // Apply trailing stop if enabled + if (UseTrailingStop && !exitSignal) + { + // Calculate new stop loss level + double newStopLoss = 0; + + // Use the most recent swing high as trailing stop if available + if (xcaSwingHighBuffer[0] > 0 && xcaSwingHighBuffer[0] > currentPrice) + { + newStopLoss = xcaSwingHighBuffer[0] + 5 * _Point; + } + else + { + // Traditional trailing stop + double trailingLevel = currentPrice + TrailingStop * _Point; + if (trailingLevel < stopLoss - TrailingStep * _Point || stopLoss == 0) + newStopLoss = trailingLevel; + } + + // Update stop loss if we have a better level + if ((newStopLoss < stopLoss || stopLoss == 0) && newStopLoss > 0) + { + trade.PositionModify(ticket, newStopLoss, takeProfit); + Print("Updated SELL trailing stop to: ", newStopLoss); + } + } + + // Handle multiple targets + if (UseMultipleTargets && positionVolume > LotSize * 0.5) + { + // First target: When price reaches support or initial TP + if (currentPrice <= xcaSupportBuffer[0] && xcaSupportBuffer[0] < openPrice) + { + double closeVolume = NormalizeDouble(positionVolume * Target1Percent / 100, 2); + if (closeVolume > 0) + { + trade.PositionClosePartial(ticket, closeVolume); + Print("First target reached for SELL position. Partially closed: ", closeVolume); + } + } + + // Second target: When price reaches Fibonacci level 3 (76.4%) + double fiboLevel3Buffer[]; + ArraySetAsSeries(fiboLevel3Buffer, true); + if (CopyBuffer(xcaHandle, 16, 0, 1, fiboLevel3Buffer) > 0) + { + double fiboLevel3 = fiboLevel3Buffer[0]; + if (fiboLevel3 > 0 && currentPrice <= fiboLevel3 && fiboLevel3 < openPrice) + { + double closeVolume = NormalizeDouble(positionVolume * Target2Percent / 100, 2); + if (closeVolume > 0) + { + trade.PositionClosePartial(ticket, closeVolume); + Print("Second target reached for SELL position. Partially closed: ", closeVolume); + } + } + } + } + } + + // Close position if exit signal is triggered + if (exitSignal) + { + trade.PositionClose(ticket); + Print("Exit signal triggered. Position closed."); + } + } +} + +//+------------------------------------------------------------------+ +//| Open a buy position with proper risk management | +//+------------------------------------------------------------------+ +void OpenBuyPosition() +{ + double entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); + double stopLossPrice = 0; + double takeProfitPrice = 0; + + // Calculate stop loss price + if (StopLoss > 0) + { + // Use either the specified stop loss or the swing low, whichever is closer + double swingLowPrice = xcaSwingLowBuffer[0]; + double slByPoints = entryPrice - StopLoss * _Point; + + // If we have a valid swing low and it's not too far + if (swingLowPrice > 0 && entryPrice - swingLowPrice < StopLoss * 2 * _Point) + stopLossPrice = swingLowPrice - 5 * _Point; // Place SL slightly below swing low + else + stopLossPrice = slByPoints; + } + + // Calculate take profit price + if (TakeProfit > 0) + { + // If using multiple targets, we'll set the first one here and manage the rest in position management + if (UseMultipleTargets) + { + // Use resistance level as first target if available + if (xcaResistanceBuffer[0] > entryPrice) + takeProfitPrice = xcaResistanceBuffer[0]; + else + takeProfitPrice = entryPrice + TakeProfit * _Point; + } + else + { + takeProfitPrice = entryPrice + TakeProfit * _Point; + } + } + + // Calculate position size + double volume = LotSize; + if (UseAutoLotSize && stopLossPrice > 0) + { + double riskAmount = AccountInfoDouble(ACCOUNT_BALANCE) * RiskPercent / 100; + double pipValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE) * + (SymbolInfoDouble(_Symbol, SYMBOL_POINT) / SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE)); + double pipDistance = MathAbs(entryPrice - stopLossPrice) / SymbolInfoDouble(_Symbol, SYMBOL_POINT); + + if (pipDistance > 0 && pipValue > 0) + volume = NormalizeDouble(riskAmount / (pipDistance * pipValue), 2); + + // Ensure volume is within allowed limits + double minVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); + double maxVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); + double volumeStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); + + volume = MathMax(minVolume, MathMin(maxVolume, volume)); + volume = NormalizeDouble(volume / volumeStep, 0) * volumeStep; + } + + // Execute the trade + trade.Buy(volume, _Symbol, 0, stopLossPrice, takeProfitPrice, "X121 Strategy Buy"); + + // Log the trade + Print("BUY Signal: Opening position at ", entryPrice, + ", SL: ", stopLossPrice, + ", TP: ", takeProfitPrice, + ", Volume: ", volume); +} + +//+------------------------------------------------------------------+ +//| Open a sell position with proper risk management | +//+------------------------------------------------------------------+ +void OpenSellPosition() +{ + double entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); + double stopLossPrice = 0; + double takeProfitPrice = 0; + + // Calculate stop loss price + if (StopLoss > 0) + { + // Use either the specified stop loss or the swing high, whichever is closer + double swingHighPrice = xcaSwingHighBuffer[0]; + double slByPoints = entryPrice + StopLoss * _Point; + + // If we have a valid swing high and it's not too far + if (swingHighPrice > 0 && swingHighPrice - entryPrice < StopLoss * 2 * _Point) + stopLossPrice = swingHighPrice + 5 * _Point; // Place SL slightly above swing high + else + stopLossPrice = slByPoints; + } + + // Calculate take profit price + if (TakeProfit > 0) + { + // If using multiple targets, we'll set the first one here and manage the rest in position management + if (UseMultipleTargets) + { + // Use support level as first target if available + if (xcaSupportBuffer[0] > 0 && xcaSupportBuffer[0] < entryPrice) + takeProfitPrice = xcaSupportBuffer[0]; + else + takeProfitPrice = entryPrice - TakeProfit * _Point; + } + else + { + takeProfitPrice = entryPrice - TakeProfit * _Point; + } + } + + // Calculate position size + double volume = LotSize; + if (UseAutoLotSize && stopLossPrice > 0) + { + double riskAmount = AccountInfoDouble(ACCOUNT_BALANCE) * RiskPercent / 100; + double pipValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE) * + (SymbolInfoDouble(_Symbol, SYMBOL_POINT) / SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE)); + double pipDistance = MathAbs(entryPrice - stopLossPrice) / SymbolInfoDouble(_Symbol, SYMBOL_POINT); + + if (pipDistance > 0 && pipValue > 0) + volume = NormalizeDouble(riskAmount / (pipDistance * pipValue), 2); + + // Ensure volume is within allowed limits + double minVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); + double maxVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); + double volumeStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); + + volume = MathMax(minVolume, MathMin(maxVolume, volume)); + volume = NormalizeDouble(volume / volumeStep, 0) * volumeStep; + } + + // Execute the trade + trade.Sell(volume, _Symbol, 0, stopLossPrice, takeProfitPrice, "X121 Strategy Sell"); + + // Log the trade + Print("SELL Signal: Opening position at ", entryPrice, + ", SL: ", stopLossPrice, + ", TP: ", takeProfitPrice, + ", Volume: ", volume); +}