//+------------------------------------------------------------------+ //| BLNK5.mq5 | //| Copyright 2023, MetaTrader 5 Community | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property strict // Enforce strict syntax checking in MQL5 #include // Include the Errors header for ErrorDescription // Input parameters: These allow the user to configure the EA's behavior through the UI. input int InitialStopLoss = 1000; // Base Stop Loss in points (can be adjusted for different risk levels) - Used as minimum input int InitialTakeProfit = 1000; // Base Take Profit in points (can be modified for different reward goals) - Used as minimum input int MaFastPeriod = 5; // Fast Moving Average period (defines the sensitivity of the MA) input int MaSlowPeriod = 20; // Slow Moving Average period (defines the trend direction) input int AtrPeriod = 14; // ATR period (used to calculate volatility over a specified time frame) input int AdxPeriod = 14; // ADX period (used to measure trend strength) input int BarsBackMin = 25; // Minimum lookback period for Fibonacci (defines how far back to calculate levels) input int BarsBackMax = 50; // Maximum lookback period for Fibonacci (limits the lookback to prevent excessive calculation) input int MarketScanInterval = 600; // Market scan interval in seconds (how often the EA checks market conditions) input double LossThreshold = 0.2; // Loss threshold to adjust trading (percentage loss that triggers strategy adjustments) input int MaxConsecutiveLosses = 2; // Max number of consecutive losses before strategy adjustments are made input int DelayAfterStart = 60; // Delay in seconds after the bot starts before trading begins input int RSIPeriod = 14; // RSI period (used for momentum-based trading decisions) input int StochasticKPeriod = 14; // Stochastic K period (used for momentum confirmation) input int StochasticDPeriod = 2; // Stochastic D period (used in signal generation for trade decisions) input int StochasticSlowing = 3; // Stochastic slowing (smoothens the signal line for better clarity) input int TrailingStopPips = 1000; // Trailing Stop distance in points (how far below the price to move SL for buys) input int TrailingTakeProfitPips = 1000; // Trailing Take Profit distance in points (how far above the price to move TP for buys) input double AtrMultiplierSL = 2.0; // Multiplier for ATR when calculating Stop Loss input double AtrMultiplierTP = 1.5; // Multiplier for ATR when calculating Take Profit // Optional new parameters for added flexibility from the UI input double AdxThreshold = 15.0; // Threshold for ADX trend strength (if ADX is below this, don't trade) input double RiskPercentPerTrade = 1.0; // Risk 1% of equity per trade - Used for dynamic lot sizing input double LotSizeReductionFactor = 0.7; // Factor to reduce lot size after losses (defines how drastically lot size is reduced) input double LotSizeIncreaseFactor = 1.3; // Factor to increase lot size after wins (adjusts lot size upwards after a win) input int MaxConsecutiveWins = 3; // Number of consecutive wins to trigger lot size increase (incentivizes higher stakes after success) input int BreakEvenThresholdPips = 50; // Profit in points to trigger break-even SL input int StartTradingHour = 0; // Start hour for trading (0-23) input int EndTradingHour = 23; // End hour for trading (0-23) input int StartTradingMinute = 0; // Start minute for trading (0-59) input int EndTradingMinute = 59; // End minute for trading (0-59) // Constants that define trading parameters const int MaxOpenPositions = 2; // Maximum number of open positions allowed at a time const int MaxPositionsPerDirection = 2; // Maximum positions allowed in either buy or sell direction const int Slippage = 4; // Allowed slippage in points (deviation from requested price) const long MagicNumber = 12345; // Unique number to identify trades from this EA // Global variables to store state throughout trading double fibLevel1, fibLevel2, fibLevel3; // Fibonacci levels based on recent high and low for support/resistance levels datetime lastMarketScan = 0; // Timestamp of the last market scan to prevent excessive scanning datetime botStartTime = 0; // Time when the bot was started to manage delays double accountStartBalance; // Initial account balance for calculating performance int consecutiveLosses = 0; // Count of consecutive losses to trigger strategy adjustments int consecutiveWins = 0; // Count of consecutive wins to trigger strategy adjustments double totalLossesAmount = 0; // Total amount lost across trades, for performance tracking double totalWinsAmount = 0; // Total amount won across trades, for performance tracking int totalTrades = 0; // Total number of trades executed by the bot datetime lastTradeTime = 0; // Timestamp of the last trade executed for pacing ulong lastProcessedDealTicket = 0; // Stores the ticket of the last deal processed for history tracking // Declare an array for Fibonacci Levels, useful for determining key support/resistance areas const double FibonacciLevels[] = {0.236, 0.382, 0.500, 0.618, 0.786}; // Common Fibonacci retracement levels // Declare modifiable variables for stop loss, take profit, and lot size double lotSize = 0.01; // Default lot size for each trade (will be dynamically adjusted) // Global variables for dynamic SL/TP and adaptive MA periods int dynamicStopLossPoints = 0; int dynamicTakeProfitPoints = 0; int adaptiveMaFastPeriod = MaFastPeriod; // Initialize with input value int adaptiveMaSlowPeriod = MaSlowPeriod; // Initialize with input value // Structure to store performance data for each trade (Optional - useful for detailed analysis) struct PerformanceData { double entryPrice; // Price at which trade was entered double exitPrice; // Price at which trade was closed double profit; // Profit/Loss amount of the trade bool isWin; // Indicator of whether the trade was a win datetime openTime; // Time when the trade was opened datetime closeTime; // Time when the trade was closed }; // Array to store performance metrics for analysis (Optional) // PerformanceData tradesData[]; // Uncomment if you want to store all trade data //+------------------------------------------------------------------+ //| Expert initialization function | //| Initializes the expert advisor by setting up initial parameters | //+------------------------------------------------------------------+ int OnInit() { accountStartBalance = AccountInfoDouble(ACCOUNT_BALANCE); // Get the starting account balance Print("Initial Account Balance: ", accountStartBalance); // Log the initial balance botStartTime = TimeCurrent(); // Record the time the bot starts running CalculateFibonacciLevels(); // Calculate Fibonacci levels initially for use in trading strategy CreateUI(); // Call the function to create user interface for monitoring trades and performance lastMarketScan = TimeCurrent(); // Initialize last market scan time // Initialize lot size based on initial risk percent and a default SL double equity = AccountInfoDouble(ACCOUNT_EQUITY); double riskAmount = equity * (RiskPercentPerTrade / 100.0); double defaultStopLossAmount = InitialStopLoss * _Point; // Use initial SL for default calculation if (defaultStopLossAmount > 0) { lotSize = riskAmount / defaultStopLossAmount; // Ensure lot size is within symbol limits and step double minVolume = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); double volumeStep = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP); lotSize = MathMax(minVolume, MathRound(lotSize / volumeStep) * volumeStep); double maxVolume = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX); lotSize = MathMin(lotSize, maxVolume); } else { lotSize = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); // Use minimum lot if default SL is zero Print("Warning: InitialStopLoss is zero or negative. Using minimum lot size."); } Print("Initial Lot Size calculated based on Risk: ", lotSize); return INIT_SUCCEEDED; // Indicates successful initialization } //+------------------------------------------------------------------+ //| Create User Interface | //| Creates visual components on the chart to display various metrics | //+------------------------------------------------------------------+ void CreateUI() { ObjectsDeleteAll(0, 0); // Clear all previous UI elements to avoid clutter and overlap // Create UI labels with initial values CreateLabel("Label_StopLoss", 10, 20, "Stop Loss: " + IntegerToString(dynamicStopLossPoints)); CreateLabel("Label_TakeProfit", 10, 40, "Take Profit: " + IntegerToString(dynamicTakeProfitPoints)); CreateLabel("Label_Equity", 10, 60, "Current Equity: " + DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2)); CreateLabel("Label_LotSize", 10, 80, "Current Lot Size: " + DoubleToString(lotSize, 2)); CreateLabel("Label_ConsecutiveLosses", 10, 100, "Consecutive Losses: " + IntegerToString(consecutiveLosses)); CreateLabel("Label_ConsecutiveWins", 10, 120, "Consecutive Wins: " + IntegerToString(consecutiveWins)); CreateLabel("Label_TotalTrades", 10, 140, "Total Trades: " + IntegerToString(totalTrades)); CreateLabel("Label_MagicNumber", 10, 160, "Magic Number: " + (string)MagicNumber); CreateLabel("Label_CurrentATR", 10, 180, "Current ATR: " + DoubleToString(CalculateATR(), _Digits)); // Display current ATR double adxValue = 0; if(CheckADX(adxValue)) CreateLabel("Label_CurrentADX", 10, 200, "Current ADX: " + DoubleToString(adxValue, 2)); // Display current ADX CreateLabel("Label_AdaptiveMAFast", 10, 220, "Adaptive MA Fast: " + IntegerToString(adaptiveMaFastPeriod)); // Display adaptive MA periods CreateLabel("Label_AdaptiveMASlow", 10, 240, "Adaptive MA Slow: " + IntegerToString(adaptiveMaSlowPeriod)); // Display adaptive MA periods } //+------------------------------------------------------------------+ //| Update User Interface Labels | //| Updates the text of the UI labels with current values | //+------------------------------------------------------------------+ void UpdateUI() { // Update existing labels with current values ObjectSetString(0, "Label_StopLoss", OBJPROP_TEXT, "Stop Loss: " + IntegerToString(dynamicStopLossPoints)); ObjectSetString(0, "Label_TakeProfit", OBJPROP_TEXT, "Take Profit: " + IntegerToString(dynamicTakeProfitPoints)); ObjectSetString(0, "Label_Equity", OBJPROP_TEXT, "Current Equity: " + DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2)); ObjectSetString(0, "Label_LotSize", OBJPROP_TEXT, "Current Lot Size: " + DoubleToString(lotSize, 2)); ObjectSetString(0, "Label_ConsecutiveLosses", OBJPROP_TEXT, "Consecutive Losses: " + IntegerToString(consecutiveLosses)); ObjectSetString(0, "Label_ConsecutiveWins", OBJPROP_TEXT, "Consecutive Wins: " + IntegerToString(consecutiveWins)); ObjectSetString(0, "Label_TotalTrades", OBJPROP_TEXT, "Total Trades: " + IntegerToString(totalTrades)); ObjectSetString(0, "Label_CurrentATR", OBJPROP_TEXT, "Current ATR: " + DoubleToString(CalculateATR(), _Digits)); double adxValue = 0; if(CheckADX(adxValue)) ObjectSetString(0, "Label_CurrentADX", OBJPROP_TEXT, "Current ADX: " + DoubleToString(adxValue, 2)); ObjectSetString(0, "Label_AdaptiveMAFast", OBJPROP_TEXT, "Adaptive MA Fast: " + IntegerToString(adaptiveMaFastPeriod)); ObjectSetString(0, "Label_AdaptiveMASlow", OBJPROP_TEXT, "Adaptive MA Slow: " + IntegerToString(adaptiveMaSlowPeriod)); } //+------------------------------------------------------------------+ //| Create a Label | //| Helper function to create a label on the chart | //+------------------------------------------------------------------+ void CreateLabel(const string name, int x, int y, string text) { // Creates a label object on the chart with specified properties if (!ObjectCreate(0, name, OBJ_LABEL, 0, TimeCurrent(), 0)) { LogError("Failed to create label: " + name); return; } // Set label properties such as size, location, and text ObjectSetInteger(0, name, OBJPROP_XSIZE, 250); // Increased size slightly ObjectSetInteger(0, name, OBJPROP_YSIZE, 20); ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); ObjectSetString(0, name, OBJPROP_TEXT, text); ObjectSetInteger(0, name, OBJPROP_COLOR, clrWhite); // Set text color to white for visibility ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9); // Reduced font size slightly } //+------------------------------------------------------------------+ //| Log trade details | //| Appends details of a trade to a log file for record-keeping | //+------------------------------------------------------------------+ void LogTradeDetails(string action, double price, double sl, double tp, double profit, bool isWin, ulong dealTicket = 0, ulong positionTicket = 0) { // Format the log message with trade details, including profit and tickets string logMessage = StringFormat("%s | Action: %s | Price: %.5f | SL: %.5f | TP: %.5f | Profit: %.2f | Result: %s | Deal: %lu | Position: %lu\n", TimeToString(TimeCurrent()), action, price, sl, tp, profit, isWin ? "Win" : "Loss", dealTicket, positionTicket); // Open the log file in append mode (FILE_WRITE | FILE_TXT | FILE_COMMON) int handle = FileOpen("TradeLog.txt", FILE_WRITE | FILE_TXT | FILE_COMMON); if (handle != INVALID_HANDLE) { FileSeek(handle, 0, SEEK_END); // Seek to the end of the file to append FileWrite(handle, logMessage); // Write the log message to the file FileClose(handle); // Close the file after writing } else { LogError("Failed to open log file."); } } //+------------------------------------------------------------------+ //| Update total wins, losses, and trades count | //| Updates global counters tracking wins, losses, and overall trades | //+------------------------------------------------------------------+ // This function is now called when a trade is *closed* and profit is known. void UpdateTradeStatsOnClose(double profit) { totalTrades++; // Increment the total trades count bool isWin = profit > 0; // Optional: Record performance data for analysis if tradesData array is uncommented // PerformanceData data; // data.profit = profit; // data.isWin = isWin; // ArrayResize(tradesData, ArraySize(tradesData) + 1); // tradesData[ArraySize(tradesData) - 1] = data; if (isWin) { totalWinsAmount += profit; // Accumulate total wins consecutiveWins++; // Increment consecutive wins counter consecutiveLosses = 0; // Reset consecutive losses count after a win Print("Win recorded. Profit: ", profit, " Total Wins Amount: ", totalWinsAmount, " Consecutive Wins: ", consecutiveWins); // Log a win } else { totalLossesAmount += MathAbs(profit); // Accumulate total losses (as a positive value) consecutiveLosses++; // Increment consecutive losses counter consecutiveWins = 0; // Reset consecutive wins counter on loss Print("Loss recorded. Loss: ", profit, " Total Losses Amount: ", totalLossesAmount, " Consecutive Losses: ", consecutiveLosses); // Log a loss } // After tracking results, adjust lot size based on consecutive wins/losses and performance AdjustLotSizeBasedOnPerformance(); // Update UI labels UpdateUI(); } //+------------------------------------------------------------------+ //| Adjust lot size based on consecutive wins/losses and performance | //| Adjusts the lot size based on recent trading results | //+------------------------------------------------------------------+ void AdjustLotSizeBasedOnPerformance() { double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY); double dynamicSLPoints = CalculateDynamicStopLoss(); // Recalculate dynamic SL for lot size calculation double stopLossAmount = dynamicSLPoints * _Point; double minVolume = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); double maxVolume = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX); double volumeStep = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP); // Get the value using SYMBOL_VOLUME as it works in your environment long volumeInfoLong = 0; if (!SymbolInfoInteger(Symbol(), SYMBOL_VOLUME, volumeInfoLong)) { LogError("Failed to get SYMBOL_VOLUME."); // Fallback: If SYMBOL_VOLUME is not available, try to infer digits from volume step if (volumeStep > 0) { // Count decimal places in volumeStep string volumeStepStr = DoubleToString(volumeStep, 10); // Convert to string with some precision int decimalPointPos = StringFind(volumeStepStr, "."); if (decimalPointPos != -1) { volumeInfoLong = StringLen(volumeStepStr) - decimalPointPos - 1; } else { volumeInfoLong = 0; // No decimal point, assume 0 digits } Print("Inferred volume digits from volume step: ", volumeInfoLong); // Keep this print for info } else { Print("Warning: Could not get SYMBOL_VOLUME or infer from volume step. Using default 2 digits for volume normalization."); // Keep this print for info volumeInfoLong = 2; // Default to 2 digits } } int volumeDigits = (int)volumeInfoLong; // Cast to int for NormalizeDouble // Calculate max recommended lot based on RiskPercentPerTrade and dynamic SL double maxRecommendedLot = 0; if (stopLossAmount > 0) { maxRecommendedLot = currentEquity * (RiskPercentPerTrade / 100.0) / stopLossAmount; } else { maxRecommendedLot = minVolume; // Use minimum lot if SL is zero Print("Warning: Dynamic Stop Loss is zero or negative. Using minimum lot size for max recommended lot."); // Keep this print for info } // Increase lot size after consecutive wins if (consecutiveWins >= MaxConsecutiveWins) { lotSize *= LotSizeIncreaseFactor; // Increase the lot size // Limit lot size to the calculated maximum recommended lot based on risk lotSize = MathMin(lotSize, maxRecommendedLot); // Ensure lot size is a valid step for the symbol lotSize = MathMax(minVolume, MathRound(lotSize / volumeStep) * volumeStep); lotSize = MathMin(lotSize, maxVolume); Print("Lot size increased to: ", lotSize, " after ", MaxConsecutiveWins, " consecutive wins. Max Recommended Lot: ", maxRecommendedLot); consecutiveWins = 0; // Reset consecutive wins counter after increase } // Decrease lot size after consecutive losses or significant total loss else if (consecutiveLosses >= MaxConsecutiveLosses || (accountStartBalance > 0 && totalLossesAmount / accountStartBalance >= LossThreshold)) { lotSize *= LotSizeReductionFactor; // Reduce the size of the lot by the reduction factor // Ensure lot size is at least the minimum allowed lotSize = MathMax(minVolume, lotSize); // Ensure lot size is a valid step for the symbol lotSize = MathRound(lotSize / volumeStep) * volumeStep; lotSize = MathMin(lotSize, maxVolume); Print("Lot size reduced to: ", lotSize, " after losses. Consecutive Losses: ", consecutiveLosses, " Total Losses Amount (vs Start Balance): ", totalLossesAmount, " / ", accountStartBalance); consecutiveLosses = 0; // Reset the losses counter after adjustment // Optionally reset totalLossesAmount if loss threshold was hit and you want to start fresh tracking // totalLossesAmount = 0; } else // Ensure lot size is within limits even if no consecutive wins/losses triggers { // Ensure lot size is at least the minimum allowed lotSize = MathMax(minVolume, lotSize); // Ensure lot size is a valid step for the symbol lotSize = MathRound(lotSize / volumeStep) * volumeStep; lotSize = MathMin(lotSize, maxVolume); } // Normalize lot size to the symbol's volume digits lotSize = NormalizeDouble(lotSize, volumeDigits); // Using the obtained volumeDigits } //+------------------------------------------------------------------+ //| Check conditions on a higher timeframe | //+------------------------------------------------------------------+ bool CheckHigherTimeframeConditions(ENUM_TIMEFRAMES higherTimeframe, bool isBuy) { // Get indicator handles for the higher timeframe using adaptive MA periods int maFastHandle = iMA(Symbol(), higherTimeframe, adaptiveMaFastPeriod, 0, MODE_SMA, PRICE_CLOSE); int maSlowHandle = iMA(Symbol(), higherTimeframe, adaptiveMaSlowPeriod, 0, MODE_SMA, PRICE_CLOSE); int adxHandle = iADX(Symbol(), higherTimeframe, AdxPeriod); // Check for valid handles if (maFastHandle == INVALID_HANDLE) LogError("Invalid MA Fast handle on higher timeframe."); if (maSlowHandle == INVALID_HANDLE) LogError("Invalid MA Slow handle on higher timeframe."); if (adxHandle == INVALID_HANDLE) LogError("Invalid ADX handle on higher timeframe."); if (maFastHandle == INVALID_HANDLE || maSlowHandle == INVALID_HANDLE || adxHandle == INVALID_HANDLE) { if (maFastHandle != INVALID_HANDLE) IndicatorRelease(maFastHandle); if (maSlowHandle != INVALID_HANDLE) IndicatorRelease(maSlowHandle); if (adxHandle != INVALID_HANDLE) IndicatorRelease(adxHandle); return false; } double maFastBuffer[2], maSlowBuffer[2], adxBuffer[1]; int barsToCopyMA = 2; int barsToCopyADX = 1; if (CopyBuffer(maFastHandle, 0, 0, barsToCopyMA, maFastBuffer) < barsToCopyMA || CopyBuffer(maSlowHandle, 0, 0, barsToCopyMA, maSlowBuffer) < barsToCopyMA || CopyBuffer(adxHandle, 0, 0, barsToCopyADX, adxBuffer) < barsToCopyADX) { LogError("Failed to copy enough indicator buffer data on higher timeframe in CheckHigherTimeframeConditions."); IndicatorRelease(maFastHandle); IndicatorRelease(maSlowHandle); IndicatorRelease(adxHandle); return false; } IndicatorRelease(maFastHandle); IndicatorRelease(maSlowHandle); IndicatorRelease(adxHandle); double maFastCurrent = maFastBuffer[0]; double maSlowCurrent = maSlowBuffer[0]; double maFastPrevious = maFastBuffer[1]; double maSlowPrevious = maSlowBuffer[1]; double adxValue = adxBuffer[0]; // Check for MA crossover on the higher timeframe bool bullishMAcrossHTF = (maFastPrevious <= maSlowPrevious) && (maFastCurrent > maSlowCurrent); bool bearishMAcrossHTF = (maFastPrevious >= maSlowPrevious) && (maFastCurrent < maSlowCurrent); // Check ADX strength on the higher timeframe bool adxStrongHTF = adxValue >= AdxThreshold; // Higher timeframe conditions if (isBuy) { return bullishMAcrossHTF && adxStrongHTF; } else // Sell { return bearishMAcrossHTF && adxStrongHTF; } } //+------------------------------------------------------------------+ //| Check market sentiment and conditions | //| Evaluates if market conditions are favorable for trading | //+------------------------------------------------------------------+ bool EnhancedMarketConditions(bool isBuy) { // First, check if basic market conditions for trading are met if (!CurrentMarketConditions(isBuy)) { // Print("Basic market conditions for ", (isBuy ? "Buy" : "Sell"), " are not met."); // Avoid excessive logging return false; // Exit if market conditions do not allow trading } // Check ADX trend strength on the current timeframe double adxValue; if (!CheckADX(adxValue) || adxValue < AdxThreshold) { // Print("Current timeframe ADX is not strong enough (", adxValue, "<", AdxThreshold, "). No trades."); // Avoid excessive logging return false; // Exit without trading } // Check conditions on a higher timeframe (e.g., H4) if (!CheckHigherTimeframeConditions(PERIOD_H4, isBuy)) // Use PERIOD_H4 or another timeframe { // Print("Higher timeframe conditions for ", (isBuy ? "Buy" : "Sell"), " are not met."); // Avoid excessive logging return false; } // Print("All enhanced market conditions are valid for trading."); // Log successful validation of market conditions return true; // Return true if all conditions allow trading } //+------------------------------------------------------------------+ //| Place an order with enhanced checks | //| Tries to place a buy/sell order if market conditions allow it | //+------------------------------------------------------------------+ void PlaceOrder(bool isBuy, double price) { MqlTradeRequest request = {}; // Prepare the trade request structure MqlTradeResult result = {}; // Prepare the result structure to capture order outcome // Enhanced market condition check immediately before placing an order if ((isBuy && !EnhancedMarketConditions(true)) || (!isBuy && !EnhancedMarketConditions(false))) { // Print("Conditions for ", (isBuy ? "Buy" : "Sell"), " are no longer valid right before placing order. Not placing order."); // Avoid excessive logging return; // Exit if conditions are not suitable for placing an order } request.action = TRADE_ACTION_DEAL; // Set the action type as a real trade request.symbol = Symbol(); // Define which currency pair to trade request.volume = lotSize; // Set the volume of the trade using the current lot size request.type = isBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL; // Specify whether it is a buy or sell order request.price = price; // Set the price at which the order should be executed // Calculate Stop Loss and Take Profit based on order type dynamically // double dynamicSLPoints = CalculateDynamicStopLoss(); // No longer needed here // double dynamicTPPoints = CalculateDynamicTakeProfit(); // No longer needed here double dynamicSL = dynamicStopLossPoints * _Point; // Use global variable double dynamicTP = dynamicTakeProfitPoints * _Point; // Use global variable if (isBuy) { request.sl = price - dynamicSL; // Calculate Stop Loss for buy order request.tp = price + dynamicTP; // Calculate Take Profit for buy order } else { request.sl = price + dynamicSL; // Calculate Stop Loss for sell order request.tp = price - dynamicTP; // Calculate Take Profit for sell order } // Normalize SL/TP prices to the symbol's digits request.sl = NormalizeDouble(request.sl, _Digits); request.tp = NormalizeDouble(request.tp, _Digits); request.deviation = Slippage; // Set allowed slippage request.type_filling = ORDER_FILLING_IOC; // Set order filling type (Immediate Or Cancel) request.type_time = ORDER_TIME_GTC; // Set order lifetime (Good Till Cancelled) request.magic = MagicNumber; // Assign the unique magic number // Check for valid SL/TP before sending the order long stop_level = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL); long tp_level = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_FREEZE_LEVEL); // Add a small buffer to the stop level check to avoid issues with floating point comparisons double pricePrecision = _Point; // Use the symbol's point size for precision comparison if (MathAbs(request.tp - request.price) < tp_level * _Point - pricePrecision || MathAbs(request.sl - request.price) < stop_level * _Point - pricePrecision) { Print("Invalid SL/TP distance (too close to price). Order not placed. SL: ", request.sl, " TP: ", request.tp, " Price: ", request.price); // Keep this print for info Print("Broker Stop Level: ", stop_level * _Point, " Broker TP Level: ", tp_level * _Point); // Keep this print for info return; } // Send the order and capture the result if (OrderSend(request, result)) // Try to send the order and check if successful { // Log the *attempt* and initial result of sending the order string action = isBuy ? "BUY" : "SELL"; Print("✅ EA-Sent ", action, " order request @ ", request.price, " | SL: ", request.sl, " | TP: ", request.tp, " | Lot: ", request.volume, " | Result Code: ", result.retcode, " | Deal Ticket: ", result.deal, " | Order Ticket: ", result.order); // **DO NOT** immediately update trade stats or log a "win" here. // We wait for the platform to confirm the deal via logs or OnTradeTransaction lastTradeTime = TimeCurrent(); // Update last trade time after sending request } else { LogError("Failed to send " + (isBuy ? "BUY" : "SELL") + " order request."); // Optional: Log this failure to your file as well // LogTradeDetails(isBuy ? "FAILED BUY REQUEST" : "FAILED SELL REQUEST", price, 0, 0, 0, false); } } //+------------------------------------------------------------------+ //| Expert tick function | //| Called every tick; main trading logic is contained here | //+------------------------------------------------------------------+ void OnTick() { // Delay trading after start to allow for market conditions to stabilize if (TimeCurrent() - botStartTime < DelayAfterStart) { // Print("Waiting for initial delay..."); // Avoid excessive logging return; // Exit function if the delay isn't complete } // Check if current time is within allowed trading hours MqlDateTime currentTime; TimeToStruct(TimeCurrent(), currentTime); if (currentTime.hour < StartTradingHour || (currentTime.hour == StartTradingHour && currentTime.min < StartTradingMinute) || currentTime.hour > EndTradingHour || (currentTime.hour == EndTradingHour && currentTime.min > EndTradingMinute)) { // Print("Outside of allowed trading hours."); // Avoid excessive logging ManageTrailingStops(); // Still manage trailing stops outside trading hours CheckForClosedPositions(); // Still check for closed positions outside trading hours return; // Exit function if outside trading hours } // Prevent placing orders until a certain amount of time has passed after the last trade request if (TimeCurrent() - lastTradeTime < 5) // Example: 5 seconds delay after sending a request { // Print("Waiting for delay after last trade request..."); // Avoid excessive logging // Still need to check for closed positions and manage trailing stops during this delay CheckForClosedPositions(); ManageTrailingStops(); // No need to pass open positions count here, it's calculated inside return; // Exit function if not enough time has passed for a new order } // Perform market scan at defined intervals to evaluate conditions if (TimeCurrent() - lastMarketScan >= MarketScanInterval) // Use input parameter for interval { lastMarketScan = TimeCurrent(); // Update last market scan time ScanMarketAndAdjust(); // Execute market scan and adjust parameters accordingly } // Count open positions and categorize them that belong to this EA int openPositionsCount = 0; int buyCount = 0; int sellCount = 0; // Loop through all open positions and count only those with the correct magic number for (int i = 0; i < PositionsTotal(); i++) { ulong positionTicket = PositionGetTicket(i); if (PositionSelectByTicket(positionTicket)) { if (PositionGetInteger(POSITION_MAGIC) == MagicNumber) // Check if the position belongs to this EA { openPositionsCount++; // Count total positions for this EA if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) buyCount++; if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) sellCount++; } } } // Main trading logic - determine if new orders can be placed // Check total position count and position count per direction *for this EA* if (openPositionsCount < MaxOpenPositions) // Check total positions limit for this EA { // Attempt to place a Buy order if below max buy positions for this EA if (buyCount < MaxPositionsPerDirection) { double askPrice = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double bufferZone = 0 * _Point; // Keep buffer zone as is // Check if Ask price is at or below Fibonacci levels for potential buys if (askPrice <= fibLevel1 + bufferZone || askPrice <= fibLevel2 + bufferZone) // Consider Fibo1 or Fibo2 { // Print("⏱️ OnTick: Potential Buy condition met (Ask price <= Fibo levels + Buffer). Ask Price: ", askPrice, " Fibo1: ", fibLevel1, " Fibo2: ", fibLevel2); // Avoid excessive logging // Enhanced market condition check is done inside PlaceOrder now PlaceOrder(true, askPrice); } // else { Print("❌ OnTick: Ask price too high for Buy."); } // Avoid excessive logging } // Attempt to place a Sell order if below max sell positions for this EA if (sellCount < MaxPositionsPerDirection) { double bidPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID); // Get current bid price for sell order placement // Check if Bid price is at or above Fibonacci levels for potential sells // Consider adding other conditions here (e.g., MA cross, RSI/Stoch levels) if (bidPrice >= fibLevel1 || bidPrice >= fibLevel2) // Consider Fibo1 or Fibo2 { // Print("⏱️ OnTick: Potential Sell condition met (Bid price >= Fibo levels). Bid Price: ", bidPrice, " Fibo1: ", fibLevel1, " Fibo2: ", fibLevel2); // Avoid excessive logging // Enhanced market condition check is done inside PlaceOrder now PlaceOrder(false, bidPrice); } // else { Print("❌ OnTick: Bid price too low for Sell."); } // Avoid excessive logging } } // Call trailing stop logic to manage existing open positions effectively ManageTrailingStops(); // No need to pass open positions count here // Check for closed positions and update stats CheckForClosedPositions(); // New function to check for closed trades } //+------------------------------------------------------------------+ //| Check for Closed Positions | //| Iterates through deals history to find closed trades and update stats | //+------------------------------------------------------------------+ void CheckForClosedPositions() { HistorySelect(0, TimeCurrent()); // Select all history from the beginning int dealsCount = HistoryDealsTotal(); // Get the total number of deals // Find the index of the last processed deal in the history. // This prevents reprocessing old deals on every tick. int startIndex = 0; if (lastProcessedDealTicket > 0) { for (int i = dealsCount - 1; i >= 0; i--) { if (HistoryDealGetTicket(i) == lastProcessedDealTicket) { startIndex = i + 1; // Start processing from the next deal break; } } } // Iterate through deals history from the last processed deal onwards for (int i = startIndex; i < dealsCount; i++) { ulong dealTicket = HistoryDealGetTicket(i); if (dealTicket > 0) { long positionTicket = 0; long dealType = 0; double dealProfit = 0; datetime dealTime = 0; long dealEntry = 0; long dealMagic = 0; // Variable to store the magic number // Get deal properties using the correct overloads HistoryDealGetInteger(dealTicket, DEAL_POSITION_ID, positionTicket); HistoryDealGetInteger(dealTicket, DEAL_TYPE, dealType); HistoryDealGetDouble(dealTicket, DEAL_PROFIT, dealProfit); HistoryDealGetInteger(dealTicket, DEAL_TIME, dealTime); HistoryDealGetInteger(dealTicket, DEAL_ENTRY, dealEntry); HistoryDealGetInteger(dealTicket, DEAL_MAGIC, dealMagic); // Get the magic number // Check if the deal is a closing deal and belongs to THIS EA instance if (dealEntry == DEAL_ENTRY_OUT && dealMagic == MagicNumber) { // Log the details of the closed trade string closeAction = (dealType == DEAL_TYPE_BUY) ? "CLOSED BUY" : "CLOSED SELL"; LogTradeDetails(closeAction, HistoryDealGetDouble(dealTicket, DEAL_PRICE), 0, 0, dealProfit, dealProfit > 0, dealTicket, positionTicket); // Update trade statistics based on the closed trade's profit UpdateTradeStatsOnClose(dealProfit); // Update the last processed deal ticket to the current deal's ticket lastProcessedDealTicket = dealTicket; } } } } //+------------------------------------------------------------------+ //| Calculate Volatility using ATR | //| Computes the Average True Range to gauge market volatility | //+------------------------------------------------------------------+ double CalculateATR() { double atrBuffer[1]; int handle = iATR(Symbol(), PERIOD_H1, AtrPeriod); // Create handle to the ATR indicator for specified period if (handle != INVALID_HANDLE) // If the handle was created successfully { int copied = CopyBuffer(handle, 0, 0, 1, atrBuffer); // Copy buffer of ATR values into atrBuffer IndicatorRelease(handle); // Clean up the indicator handle if (copied > 0) { return atrBuffer[0]; // Return the latest ATR value } else { LogError("Failed to copy ATR buffer."); } } else { LogError("Invalid ATR handle."); } return 0.0; // Return zero if ATR could not be calculated } //+------------------------------------------------------------------+ //| Function to calculate dynamic Stop Loss | //| Dynamically calculates stop loss based on current market volatility | //+------------------------------------------------------------------+ int CalculateDynamicStopLoss() { double atrValue = CalculateATR(); // Get the latest ATR value if (atrValue <= 0.0) atrValue = InitialStopLoss * _Point / AtrMultiplierSL; // Use a default based on InitialStopLoss if ATR is zero or negative // Calculate SL based on ATR multiplier and convert to points int dynamicSLPoints = (int)MathRound(atrValue * AtrMultiplierSL / _Point); // Ensure a minimum Stop Loss in points return MathMax(InitialStopLoss, dynamicSLPoints); // Use InitialStopLoss as a minimum floor } //+------------------------------------------------------------------+ //| Function to calculate dynamic Take Profit | //| Dynamically calculates take profit based on current market volatility | //+------------------------------------------------------------------+ int CalculateDynamicTakeProfit() { double atrValue = CalculateATR(); // Get the latest ATR value if (atrValue <= 0.0) atrValue = InitialTakeProfit * _Point / AtrMultiplierTP; // Use a default based on InitialTakeProfit if ATR is zero or negative // Calculate TP based on ATR multiplier and convert to points int dynamicTPPoints = (int)MathRound(atrValue * AtrMultiplierTP / _Point); // Ensure a minimum Take Profit in points return MathMax(InitialTakeProfit, dynamicTPPoints); // Use InitialTakeProfit as a minimum floor } //+------------------------------------------------------------------+ //| Check for Bullish Engulfing Pattern | //| Determines if a bullish engulfing pattern has occurred | //+------------------------------------------------------------------+ // Note: This function checks the *current* and *previous* completed bars. // To check the very last two bars (including the current, possibly incomplete one), // you would need to adjust the indices (0 and 1 for the last two completed bars, // or potentially 1 and 2 if you want to avoid the current incomplete bar entirely). bool CheckBullishEngulfing() { // Retrieve historical prices for previous two completed candlesticks double prevClose = iClose(Symbol(), PERIOD_H1, 1); double prevOpen = iOpen(Symbol(), PERIOD_H1, 1); double currClose = iClose(Symbol(), PERIOD_H1, 0); // Current bar's close (might be incomplete) double currOpen = iOpen(Symbol(), PERIOD_H1, 0); // Current bar's open // Return boolean if the conditions for a bullish engulfing pattern are met return (prevClose < prevOpen && // Previous candle was bearish currClose > currOpen && // Current candle is bullish currClose > prevOpen && // Current candle closes above previous open currOpen < prevClose); // Current candle opens below previous close } //+------------------------------------------------------------------+ //| Check for Bearish Engulfing Pattern | //| Determines if a bearish engulfing pattern has occurred | //+------------------------------------------------------------------+ // Note: This function checks the *current* and *previous* completed bars. // To check the very last two bars (including the current, possibly incomplete one), // you would need to adjust the indices (0 and 1 for the last two completed bars, // or potentially 1 and 2 if you want to avoid the current incomplete bar entirely). bool CheckBearishEngulfing() { // Retrieve historical prices for previous two completed candlesticks double prevClose = iClose(Symbol(), PERIOD_H1, 1); double prevOpen = iOpen(Symbol(), PERIOD_H1, 1); double currClose = iClose(Symbol(), PERIOD_H1, 0); // Current bar's close (might be incomplete) double currOpen = iOpen(Symbol(), PERIOD_H1, 0); // Current bar's open // Return boolean if the conditions for a bearish engulfing pattern are met return (prevClose > prevOpen && // Previous candle was bullish currClose < currOpen && // Current candle is bearish currClose < prevOpen && // Current candle closes below previous open currOpen > prevClose); // Current candle opens above previous close } //+------------------------------------------------------------------+ //| Evaluate sentiment based on previous price movements | //| Assess whether market sentiment supports trading | //+------------------------------------------------------------------+ // This is a very basic sentiment check. Consider more robust methods. bool CheckMarketSentiment() { // Check the completed previous bar (index 1) double prevHigh = iHigh(Symbol(), PERIOD_H1, 1); double prevLow = iLow(Symbol(), PERIOD_H1, 1); double currClose = iClose(Symbol(), PERIOD_H1, 0); // Current bar's close (might be incomplete) // Market sentiment indicates a trade should occur if current price is outside of the previous high/low return (currClose > prevHigh || currClose < prevLow); } //+------------------------------------------------------------------+ //| Evaluate if market conditions support a trade | //| Confirms if moving averages and indicators signal a valid trade | //+------------------------------------------------------------------+ bool CurrentMarketConditions(bool isBuy) { // Get indicator handles using adaptive MA periods int maFastHandle = iMA(Symbol(), PERIOD_H1, adaptiveMaFastPeriod, 0, MODE_SMA, PRICE_CLOSE); int maSlowHandle = iMA(Symbol(), PERIOD_H1, adaptiveMaSlowPeriod, 0, MODE_SMA, PRICE_CLOSE); int rsiHandle = iRSI(Symbol(), PERIOD_H1, RSIPeriod, PRICE_CLOSE); int stochHandle = iStochastic(Symbol(), PERIOD_H1, StochasticKPeriod, StochasticDPeriod, StochasticSlowing, MODE_SMA, STO_CLOSECLOSE); // Check for valid handles if (maFastHandle == INVALID_HANDLE) LogError("Invalid MA Fast handle."); if (maSlowHandle == INVALID_HANDLE) LogError("Invalid MA Slow handle."); if (rsiHandle == INVALID_HANDLE) LogError("Invalid RSI handle."); if (stochHandle == INVALID_HANDLE) LogError("Invalid Stochastic handle."); if (maFastHandle == INVALID_HANDLE || maSlowHandle == INVALID_HANDLE || rsiHandle == INVALID_HANDLE || stochHandle == INVALID_HANDLE) { if (maFastHandle != INVALID_HANDLE) IndicatorRelease(maFastHandle); if (maSlowHandle != INVALID_HANDLE) IndicatorRelease(maSlowHandle); if (rsiHandle != INVALID_HANDLE) IndicatorRelease(rsiHandle); if (stochHandle != INVALID_HANDLE) IndicatorRelease(stochHandle); return false; } double maFastBuffer[2], maSlowBuffer[2], rsiBuffer[1], stochBuffer[2]; // Need 2 bars for MA cross check, 1 for RSI, 2 for Stoch // Copy indicator data int barsToCopyMA = 2; int barsToCopyRSI = 1; int barsToCopyStoch = 2; if (CopyBuffer(maFastHandle, 0, 0, barsToCopyMA, maFastBuffer) < barsToCopyMA || CopyBuffer(maSlowHandle, 0, 0, barsToCopyMA, maSlowBuffer) < barsToCopyMA || CopyBuffer(rsiHandle, 0, 0, barsToCopyRSI, rsiBuffer) < barsToCopyRSI || CopyBuffer(stochHandle, 0, 0, barsToCopyStoch, stochBuffer) < barsToCopyStoch) { LogError("Failed to copy enough indicator buffer data in CurrentMarketConditions."); IndicatorRelease(maFastHandle); IndicatorRelease(maSlowHandle); IndicatorRelease(stochHandle); IndicatorRelease(rsiHandle); return false; } // Release handles after successfully copying data IndicatorRelease(maFastHandle); IndicatorRelease(maSlowHandle); IndicatorRelease(rsiHandle); IndicatorRelease(stochHandle); double maFastCurrent = maFastBuffer[0]; // Current bar (might be incomplete) double maSlowCurrent = maSlowBuffer[0]; double maFastPrevious = maFastBuffer[1]; // Previous completed bar double maSlowPrevious = maSlowBuffer[1]; double rsiValue = rsiBuffer[0]; // Value on the last completed bar double stochMain = stochBuffer[0]; // Value on the last completed bar double stochSignal = stochBuffer[1]; // Value on the last completed bar // Check for confirmed MA crossover on the previous completed bar bool bullishMAcross = (maFastPrevious <= maSlowPrevious) && (maFastCurrent > maSlowCurrent); bool bearishMAcross = (maFastPrevious >= maSlowPrevious) && (maFastCurrent < maSlowCurrent); // Check conditions for buying based on trend and indicator assessments if (isBuy) { // Require a bullish MA cross and avoid overbought conditions if (!bullishMAcross || rsiValue > 70 || stochMain > 80) { // Print("Conditions no longer support a buy trade: Bullish MA Cross: ", bullishMAcross, " RSI: ", rsiValue, " Stochastic Main: ", stochMain); // Avoid excessive logging return false; // Exit if conditions are unsuitable for a buy } // Print("CurrentMarketConditions: Buy conditions met. Bullish MA Cross: ", bullishMAcross, " RSI: ", rsiValue, " Stochastic Main: ", stochMain); } else // Logic for selling { // Require a bearish MA cross and avoid oversold conditions if (!bearishMAcross || rsiValue < 30 || stochMain < 20) { // Print("Conditions no longer support a sell trade: Bearish MA Cross: ", bearishMAcross, " RSI: ", rsiValue, " Stochastic Main: ", stochMain); // Avoid excessive logging return false; // Exit if conditions are unsuitable for a sell } // Print("CurrentMarketConditions: Sell conditions met. Bearish MA Cross: ", bearishMAcross, " RSI: ", rsiValue, " Stochastic Main: ", stochSignal); } return true; // Return true if all conditions for trading are met } //+------------------------------------------------------------------+ //| Function to check ADX strength | //| Validates if the ADX indicates a strong trend | //+------------------------------------------------------------------+ bool CheckADX(double& adxValue) { int adxHandle = iADX(Symbol(), PERIOD_H1, AdxPeriod); // Create handle for ADX if (adxHandle != INVALID_HANDLE) // Ensure handle is valid { double adxBuffer[1]; if (CopyBuffer(adxHandle, 0, 0, 1, adxBuffer) > 0) // Attempt to copy buffer data from ADX for the last completed bar { adxValue = adxBuffer[0]; // Store the indicator value IndicatorRelease(adxHandle); // Free handle after use return true; // Return true if ADX value was fetched successfully } LogError("Failed to copy ADX buffer."); IndicatorRelease(adxHandle); // Clean up invalid handle } LogError("Failed to get valid ADX handle."); return false; // Return false if ADX retrieval failed } //+------------------------------------------------------------------+ //| Function to scan the market and adjust levels | //| Gathers market data and recalculates indicators and levels | //+------------------------------------------------------------------+ void ScanMarketAndAdjust() { double atrValue = CalculateATR(); // Calculate current volatility using ATR double adxValue = 0; // Initialize variable for ADX // Gather necessary indicator data for adjustments bool adxOk = CheckADX(adxValue); // --- Adaptive MA Period Calculation --- // Example: Shorter periods with higher ATR if (atrValue > 0) { adaptiveMaFastPeriod = MathMax(2, (int)MathRound(MaFastPeriod * (1.0 / atrValue))); adaptiveMaSlowPeriod = MathMax(10, (int)MathRound(MaSlowPeriod * (1.0 / atrValue))); } // Ensure minimum periods adaptiveMaFastPeriod = MathMax(adaptiveMaFastPeriod, 2); adaptiveMaSlowPeriod = MathMax(adaptiveMaSlowPeriod, 10); // Recalculate Fibonacci levels (using adaptive lookback from Step 9) CalculateFibonacciLevels(); DrawFibonacciLevels(); // Draw updated Fibonacci levels on the chart for visual reference // Calculate and store dynamic SL/TP dynamicStopLossPoints = CalculateDynamicStopLoss(); dynamicTakeProfitPoints = CalculateDynamicTakeProfit(); // Log relevant market conditions Print("--- Market Scan Summary ---"); Print("ATR: ", atrValue); if(adxOk) Print("ADX: ", adxValue); Print("Adaptive MA Fast Period: ", adaptiveMaFastPeriod); Print("Adaptive MA Slow Period: ", adaptiveMaSlowPeriod); Print("Dynamic SL (points): ", dynamicStopLossPoints); Print("Dynamic TP (points): ", dynamicTakeProfitPoints); Print("Fibonacci Levels: Fibo1: ", fibLevel1, " Fibo2: ", fibLevel2, " Fibo3: ", fibLevel3); Print("-------------------------"); // Update UI labels after market scan UpdateUI(); } void CalculateFibonacciLevels() { double atrValue = CalculateATR(); // Get current ATR // Adapt lookback based on ATR int adaptiveBarsBackMin = MathMax(10, (int)MathRound(BarsBackMin * (1.0 / atrValue))); int adaptiveBarsBackMax = MathMax(20, (int)MathRound(BarsBackMax * (1.0 / atrValue))); // Clamp lookback range to sane limits adaptiveBarsBackMin = MathMax(adaptiveBarsBackMin, 10); adaptiveBarsBackMax = MathMin(adaptiveBarsBackMax, 100); int lookbackRange = MathMin(adaptiveBarsBackMax, adaptiveBarsBackMin); int totalBars = Bars(Symbol(), PERIOD_H1); if (totalBars < lookbackRange + 2) { Print("Not enough bars available for Fibonacci calculation. Need at least ", lookbackRange + 2, " bars. Available: ", totalBars); return; } double highArray[]; double lowArray[]; ArrayResize(highArray, lookbackRange); ArrayResize(lowArray, lookbackRange); if (CopyHigh(Symbol(), PERIOD_H1, 0, lookbackRange, highArray) != lookbackRange || CopyLow(Symbol(), PERIOD_H1, 0, lookbackRange, lowArray) != lookbackRange) { Print("Failed to copy high or low values for Fibonacci calculation."); return; } // Determine high/low from arrays double highestHigh = highArray[0]; double lowestLow = lowArray[0]; for (int i = 1; i < lookbackRange; i++) { if (highArray[i] > highestHigh) highestHigh = highArray[i]; if (lowArray[i] < lowestLow) lowestLow = lowArray[i]; } double fibRange = highestHigh - lowestLow; if (fibRange <= 0) { Print("Invalid Fibonacci range. Attempting fallback."); highestHigh = iHigh(Symbol(), PERIOD_H1, 1); lowestLow = iLow(Symbol(), PERIOD_H1, 1); fibRange = highestHigh - lowestLow; if (fibRange <= 0) { Print("Fallback Fibonacci range also invalid. Skipping calculation."); return; } } // Calculate Fibonacci levels fibLevel1 = lowestLow + FibonacciLevels[0] * fibRange; fibLevel2 = lowestLow + FibonacciLevels[1] * fibRange; fibLevel3 = lowestLow + FibonacciLevels[2] * fibRange; fibLevel1 = NormalizeDouble(fibLevel1, _Digits); fibLevel2 = NormalizeDouble(fibLevel2, _Digits); fibLevel3 = NormalizeDouble(fibLevel3, _Digits); // Optional: Print or use levels in logic // PrintFormat("Fibonacci Levels: %.2f, %.2f, %.2f", fibLevel1, fibLevel2, fibLevel3); } //+------------------------------------------------------------------+ //| Draw Fibonacci levels on the chart | //| Visual representation of significant price levels in the chart | //+------------------------------------------------------------------+ void DrawFibonacciLevels() { // Remove previous Fibonacci levels if they exist to avoid duplicates // Only remove objects with the correct name prefix string objectNamePrefix = "FibonacciLevel"; int objectsCount = ObjectsTotal(0); for(int i = objectsCount - 1; i >= 0; i--) { string name = ObjectName(0, i); if(StringFind(name, objectNamePrefix) == 0) // Check if the name starts with the prefix { ObjectDelete(0, name); } } // Draw Fibonacci levels on the chart for visual aid for (int i = 0; i < ArraySize(FibonacciLevels); i++) { double level = 0; // Initialize level variable // Properly assign the level based on which Fibonacci level we're processing switch (i) { case 0: level = fibLevel1; break; // Assign first Fibonacci level case 1: level = fibLevel2; break; // Assign second Fibonacci level case 2: level = fibLevel3; break; // Assign third Fibonacci level default: continue; // Skip if out of bounds } string fibLabel = "FibonacciLevel" + IntegerToString(i); // Create a unique label for each Fibonacci line // Check if the level is valid (not 0.0, which might happen if calculation failed) if (level == 0.0) continue; // Create horizontal line for the Fibonacci level // Use a fixed time/bar index (e.g., 0 for the current bar) as horizontal lines don't need a time coordinate if (!ObjectCreate(0, fibLabel, OBJ_HLINE, 0, 0, level)) { LogError("Failed to create Fibonacci level line: " + fibLabel); continue; // Continue to try creating other levels even if one fails } // Set properties for the drawn Fibonacci line ObjectSetInteger(0, fibLabel, OBJPROP_COLOR, clrGold); // Set color of the Fibonacci line to gold ObjectSetInteger(0, fibLabel, OBJPROP_WIDTH, 1); // Set width of the line ObjectSetInteger(0, fibLabel, OBJPROP_STYLE, STYLE_DASH); // Set the style of the line to dashed ObjectSetString(0, fibLabel, OBJPROP_TEXT, "Fibo " + DoubleToString(FibonacciLevels[i] * 100, 1) + "%"); // Set the displayed text on the line to the Fibonacci level percentage ObjectSetInteger(0, fibLabel, OBJPROP_SELECTABLE, false); // Make the line not selectable by default ObjectSetInteger(0, fibLabel, OBJPROP_HIDDEN, false); // Ensure the line is not hidden } } //+------------------------------------------------------------------+ //| Function to manage trailing stops | //| Adjusts stop losses for existing open positions to lock in profits | //+------------------------------------------------------------------+ void ManageTrailingStops() { if (TrailingStopPips <= 0 && TrailingTakeProfitPips <= 0 && BreakEvenThresholdPips <= 0) return; // No need to run if trailing and break-even are disabled int openPositionsCount = PositionsTotal(); // Get the total count of open positions for (int i = 0; i < openPositionsCount; i++) // Loop through all open positions { ulong positionTicket = PositionGetTicket(i); if (PositionSelectByTicket(positionTicket)) { // Only manage positions opened by this EA if (PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue; long positionType = PositionGetInteger(POSITION_TYPE); double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT); double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); double sl = PositionGetDouble(POSITION_SL); double tp = PositionGetDouble(POSITION_TP); double profit = PositionGetDouble(POSITION_PROFIT); // Get current profit double newSL = sl; // Initialize new Stop Loss variable double newTP = tp; // Initialize new Take Profit variable // Convert trailing pips to price distance double trailingStopDistance = TrailingStopPips * _Point; double trailingTakeProfitDistance = TrailingTakeProfitPips * _Point; // --- Break-Even Stop Loss Logic --- if (BreakEvenThresholdPips > 0) { double breakEvenThresholdPrice = BreakEvenThresholdPips * _Point; if (positionType == POSITION_TYPE_BUY) { // Check if the position is in profit by the break-even threshold if (currentPrice > openPrice + breakEvenThresholdPrice) { // Calculate potential break-even stop loss (slightly above open price to cover spread/commission) double breakEvenSL = openPrice + SymbolInfoDouble(Symbol(), SYMBOL_ASK) - SymbolInfoDouble(Symbol(), SYMBOL_BID); // Open price + spread breakEvenSL = MathMax(breakEvenSL, openPrice); // Ensure SL is at least at the open price // Move SL to break-even if the current SL is below it if (sl < breakEvenSL) { newSL = breakEvenSL; // Print("Position ", positionTicket, " moved to break-even."); // Avoid excessive logging } } } else if (positionType == POSITION_TYPE_SELL) { // Check if the position is in profit by the break-even threshold if (currentPrice < openPrice - breakEvenThresholdPrice) { // Calculate potential break-even stop loss (slightly below open price) double breakEvenSL = openPrice - (SymbolInfoDouble(Symbol(), SYMBOL_ASK) - SymbolInfoDouble(Symbol(), SYMBOL_BID)); // Open price - spread breakEvenSL = MathMin(breakEvenSL, openPrice); // Ensure SL is at least at the open price // Move SL to break-even if the current SL is above it if (sl > breakEvenSL) { newSL = breakEvenSL; // Print("Position ", positionTicket, " moved to break-even."); // Avoid excessive logging } } } } // --- Trailing Stop Loss Management (after break-even) --- if (TrailingStopPips > 0) { if (positionType == POSITION_TYPE_BUY) // For buy positions { // Only trail if the current price is further in profit than the new break-even SL (or initial SL if break-even not hit) if (currentPrice > newSL + trailingStopDistance) { double calculatedNewSL = currentPrice - trailingStopDistance; if (calculatedNewSL > newSL) // Only move SL up { newSL = calculatedNewSL; // Print("Trailing Buy Stop Loss triggered. Position: ", positionTicket, " New SL: ", newSL); // Avoid excessive logging } } } else if (positionType == POSITION_TYPE_SELL) // For sell positions { // Only trail if the current price is further in profit than the new break-even SL (or initial SL if break-even not hit) if (currentPrice < newSL - trailingStopDistance) { double calculatedNewSL = currentPrice + trailingStopDistance; if (calculatedNewSL < newSL) // Only move SL down { newSL = calculatedNewSL; // Print("Trailing Sell Stop Loss triggered. Position: ", positionTicket, " New SL: ", newSL); // Avoid excessive logging } } } // Normalize the new SL price newSL = NormalizeDouble(newSL, _Digits); } // --- Trailing Take Profit management logic --- if (TrailingTakeProfitPips > 0) { if (positionType == POSITION_TYPE_BUY) // For buy positions { // Calculate the potential new Take Profit double calculatedNewTP = currentPrice + trailingTakeProfitDistance; // Only move TP if it's above the current TP if (calculatedNewTP > tp) { newTP = calculatedNewTP; // Print("Trailing Buy Take Profit triggered. Position: ", positionTicket, " New TP: ", newTP); // Avoid excessive logging } } else if (positionType == POSITION_TYPE_SELL) // For sell positions { // Calculate the potential new Take Profit double calculatedNewTP = currentPrice - trailingTakeProfitDistance; // Only move TP if it's below the current TP if (calculatedNewTP < tp) { newTP = calculatedNewTP; // Print("Trailing Sell Take Profit triggered. Position: ", positionTicket, " New TP: ", newTP); // Avoid excessive logging } } // Normalize the new TP price newTP = NormalizeDouble(newTP, _Digits); } // Apply adjustments to Stop Loss and Take Profit if changes were made // Also ensure the new SL/TP are valid distances from the current price long stop_level = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL); long tp_level = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_FREEZE_LEVEL); double pricePrecision = _Point; if ((newSL != sl || newTP != tp) && MathAbs(newTP - currentPrice) >= tp_level * _Point - pricePrecision && MathAbs(newSL - currentPrice) >= stop_level * _Point - pricePrecision) { MqlTradeRequest modifyRequest = {}; MqlTradeResult modifyResult = {}; modifyRequest.action = TRADE_ACTION_SLTP; modifyRequest.position = positionTicket; modifyRequest.sl = newSL; modifyRequest.tp = newTP; modifyRequest.deviation = Slippage; // Attempt to send modification of the trade if (OrderSend(modifyRequest, modifyResult)) { Print("Position ", positionTicket, " modified successfully. New SL: ", newSL, " New TP: ", newTP); // Log successful modification } else { LogError("Error modifying position " + (string)positionTicket + "."); } } } } } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(0, 0); // Cleanup all UI elements on the removal of the EA from the chart Print("EA Deinitialized. Reason: ", reason); }