//+------------------------------------------------------------------+ //| Basket Trailing Stop Manager | //+------------------------------------------------------------------+ void ManageBasketTrailingStop() { if(!UseBasketTrailing) return; double basket_profit = CalculateBasketProfit(); double basket_profit_percent = CalculateBasketProfitPercent(); double account_equity = AccountInfoDouble(ACCOUNT_EQUITY); // Check if we should activate trailing if(!basket_trailing_active && basket_profit_percent >= BasketProfitPercent) { basket_trailing_active = true; basket_breakeven_level = 0; // We'll set this when we have profit highest_basket_profit = basket_profit; Print("BASKET TRAILING ACTIVATED! Total Profit: $", basket_profit, " (", DoubleToString(basket_profit_percent, 2), "%)"); } // If trailing is active, manage it if(basket_trailing_active) { // Update highest profit if(basket_profit > highest_basket_profit) { highest_basket_profit = basket_profit; } // Calculate required profit for breakeven (including spreads and commissions) double breakeven_buffer = account_equity * 0.002; // 0.2% buffer // Set breakeven level once we have sufficient profit if(basket_breakeven_level == 0 && basket_profit > breakeven_buffer) { basket_breakeven_level = breakeven_buffer; Print("BASKET BREAKEVEN SET at: $", breakeven_buffer); } // Apply trailing to individual positions ApplyPositionTrailingStops(); // Emergency close if basket loss exceeds maximum risk if(CalculateBasketProfitPercent() <= -MaxRiskPerBasket) { EmergencyCloseAllTrades(); Print("!!! EMERGENCY CLOSE !!! Basket risk exceeded ", MaxRiskPerBasket, "%"); } } } //+------------------------------------------------------------------+ //| Apply trailing stops to individual positions | //+------------------------------------------------------------------+ void ApplyPositionTrailingStops() { int total_positions = PositionsTotal(); for(int i = total_positions - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket <= 0) continue; string symbol = PositionGetString(POSITION_SYMBOL); if(symbol != _Symbol) continue; if(PositionGetInteger(POSITION_MAGIC) != 123456) continue; // Get position details double current_sl = PositionGetDouble(POSITION_SL); double current_tp = PositionGetDouble(POSITION_TP); double open_price = PositionGetDouble(POSITION_PRICE_OPEN); double volume = PositionGetDouble(POSITION_VOLUME); ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // Calculate new stop loss double new_sl = CalculateNewStopLoss(ticket, type, current_sl, open_price); // Only modify if new stop loss is better if(IsBetterStopLoss(type, current_sl, new_sl)) { if(trade.PositionModify(ticket, new_sl, current_tp)) { Print("Trailing Stop Updated: Ticket ", ticket, ", New SL: ", new_sl); } else { Print("Error modifying trailing stop: ", GetLastError()); } } } } //+------------------------------------------------------------------+ //| Calculate new stop loss for position | //+------------------------------------------------------------------+ double CalculateNewStopLoss(ulong ticket, ENUM_POSITION_TYPE type, double current_sl, double open_price) { double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * point; double new_sl = current_sl; double current_price = (type == POSITION_TYPE_BUY) ? bid : ask; double profit_points = 0; // Calculate profit in points if(type == POSITION_TYPE_BUY) { profit_points = (bid - open_price) / point; } else { profit_points = (open_price - ask) / point; } // Only trail if we have minimum profit if(profit_points >= TrailingStartPoints) { if(type == POSITION_TYPE_BUY) { // For long positions, trail below current price new_sl = bid - (TrailingStepPoints * point); // Ensure minimum distance from current price double min_distance = MinDistanceFromPrice * point; if(bid - new_sl < min_distance) { new_sl = bid - min_distance; } // Never move stop loss below breakeven if we have it if(basket_breakeven_level > 0 && new_sl < open_price) { new_sl = open_price; } } else // POSITION_TYPE_SELL { // For short positions, trail above current price new_sl = ask + (TrailingStepPoints * point); // Ensure minimum distance from current price double min_distance = MinDistanceFromPrice * point; if(new_sl - ask < min_distance) { new_sl = ask + min_distance; } // Never move stop loss above breakeven if we have it if(basket_breakeven_level > 0 && new_sl > open_price) { new_sl = open_price; } } } return NormalizeDouble(new_sl, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS)); } //+------------------------------------------------------------------+ //| Check if new stop loss is better than current one | //+------------------------------------------------------------------+ bool IsBetterStopLoss(ENUM_POSITION_TYPE type, double current_sl, double new_sl) { if(current_sl == 0) return true; // No current stop loss if(type == POSITION_TYPE_BUY) { // For buys, higher stop loss is better return (new_sl > current_sl); } else // POSITION_TYPE_SELL { // For sells, lower stop loss is better return (new_sl < current_sl); } } //+------------------------------------------------------------------+ //| Emergency close all trades if risk limit exceeded | //+------------------------------------------------------------------+ void EmergencyCloseAllTrades() { Print("!!! INITIATING EMERGENCY CLOSE PROCEDURE !!!"); int total_positions = PositionsTotal(); int closed_count = 0; for(int i = total_positions - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket <= 0) continue; string symbol = PositionGetString(POSITION_SYMBOL); if(symbol != _Symbol) continue; if(PositionGetInteger(POSITION_MAGIC) != 123456) continue; double volume = PositionGetDouble(POSITION_VOLUME); ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if(type == POSITION_TYPE_BUY) { if(trade.Sell(volume, symbol, 0, 0, 0, "Emergency Close")) closed_count++; } else if(type == POSITION_TYPE_SELL) { if(trade.Buy(volume, symbol, 0, 0, 0, "Emergency Close")) closed_count++; } } Print("Emergency close completed. Closed ", closed_count, " positions."); basket_trailing_active = false; highest_basket_profit = 0; basket_breakeven_level = 0; }