#property strict #property version "1.0" #property description "Commodity Trend Following EA" #property description "Uses 20 EMA and 7 RSI for entries" #property description "Exits at 2.5x risk or RSI conditions" #include //+------------------------------------------------------------------+ //| Input Parameters | //+------------------------------------------------------------------+ input double InpRiskPercent = 1.0; // Risk % per trade input int InpATRPeriod = 14; // ATR Period for volatility filter input double InpATRMultiplier = 1.5; // ATR Multiplier for SL input bool InpUseTrailingStop= true; // Enable trailing stop input bool InpUseRSIExit = true; // Use RSI for exit condition input int InpMaxTrades = 3; // Maximum simultaneous trades //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ CAppDialog OurInterface; CButton NewSLButton, NewTPButton; CEdit LotSize, StartSL, StartTP, StartTSL; CLabel LotLabel, StartSLLabel, StartTPLabel, StartTSLLabel, BalanceLabel; double gTradeVolume = 1; // Changed from TradeVolume to avoid conflict double gSlPoints = 100; // Changed from slPoints double gTpPoints = 250; // Changed from tpPoints double gTslPoints = 0; // Changed from tslPoints int gMagicNumber = 202400; // Base magic number datetime gLastTradeTime; //+------------------------------------------------------------------+ //| Custom Trade Functions | //+------------------------------------------------------------------+ bool CustomBuyOrder(string symbol, double volume, double sl, double tp, int magic, string comment="") { double price = MarketInfo(symbol, MODE_ASK); double stoploss = price - sl * MarketInfo(symbol, MODE_POINT); double takeprofit = price + tp * MarketInfo(symbol, MODE_POINT); int ticket = OrderSend(symbol, OP_BUY, volume, price, 3, stoploss, takeprofit, comment, magic, 0, clrBlue); if(ticket > 0) { gLastTradeTime = TimeCurrent(); return true; } Print("Buy Order failed with error: ", GetLastError()); return false; } bool CustomSellOrder(string symbol, double volume, double sl, double tp, int magic, string comment="") { double price = MarketInfo(symbol, MODE_BID); double stoploss = price + sl * MarketInfo(symbol, MODE_POINT); double takeprofit = price - tp * MarketInfo(symbol, MODE_POINT); int ticket = OrderSend(symbol, OP_SELL, volume, price, 3, stoploss, takeprofit, comment, magic, 0, clrRed); if(ticket > 0) { gLastTradeTime = TimeCurrent(); return true; } Print("Sell Order failed with error: ", GetLastError()); return false; } //+------------------------------------------------------------------+ //| Update balance information on the panel | //+------------------------------------------------------------------+ void UpdateBalanceInfo() { string text = StringFormat("Balance: %.2f\nEquity: %.2f\nProfit: %.2f\nTrades: %d/%d", AccountBalance(), AccountEquity(), AccountProfit(), CountOrdersByMagic(gMagicNumber), InpMaxTrades); BalanceLabel.Text(text); } //+------------------------------------------------------------------+ //| Check existing trades on startup | //+------------------------------------------------------------------+ void CheckExistingTrades() { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderMagicNumber() == gMagicNumber) { gLastTradeTime = OrderOpenTime(); break; } } } //+------------------------------------------------------------------+ //| Count orders with specific magic number | //+------------------------------------------------------------------+ int CountOrdersByMagic(int magic) { int count = 0; for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderMagicNumber() == magic) { count++; } } return count; } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Create user interface ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); OurInterface.Create(0, "Commodity Trend EA", 0, 50, 70, 320, 240); createLabel(LotLabel, OurInterface, "LotLabel", "Lot Size:", 10, 50, 10, 30); createInputBox(LotSize, OurInterface, "LotSize", string(gTradeVolume), 90, 130, 10, 30); createLabel(StartSLLabel, OurInterface, "StartSLLabel", "Set SL (points):", 10, 80, 40, 60); createInputBox(StartSL, OurInterface, "StartSL", string(gSlPoints), 90, 130, 40, 60); createLabel(StartTPLabel, OurInterface, "StartTPLabel", "Set TP (points):", 10, 80, 70, 90); createInputBox(StartTP, OurInterface, "StartTP", string(gTpPoints), 90, 130, 70, 90); NewSLButton.Create(0, "NewSLButton", 0, 140, 40, 250, 60); OurInterface.Add(NewSLButton); NewSLButton.Text("Update SL"); NewTPButton.Create(0, "NewTPButton", 0, 140, 70, 250, 90); OurInterface.Add(NewTPButton); NewTPButton.Text("Update TP"); BalanceLabel.Create(0, "BalanceLabel", 0, 10, 100, 300, 120); OurInterface.Add(BalanceLabel); UpdateBalanceInfo(); OurInterface.Run(); // Check for existing trades on startup CheckExistingTrades(); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { OurInterface.Destroy(reason); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Only check for new trades at the start of a new candle if(IsNewCandle()) { CheckForEntry(); ManageExits(); } // Manage trailing stops if enabled if(InpUseTrailingStop) { ManageTrailingStops(); } // Update balance information periodically static datetime lastUpdate = 0; if(TimeCurrent() - lastUpdate >= 60) { UpdateBalanceInfo(); lastUpdate = TimeCurrent(); } } //+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { OurInterface.OnEvent(id, lparam, dparam, sparam); if(id == CHARTEVENT_OBJECT_ENDEDIT) { if(sparam == "LotSize") { gTradeVolume = updatetextboxdouble(sparam, "LotSize", gTradeVolume); } else if(sparam == "StartSL") { gSlPoints = updatetextboxdouble(sparam, "StartSL", gSlPoints); } else if(sparam == "StartTP") { gTpPoints = updatetextboxdouble(sparam, "StartTP", gTpPoints); } } if(id == CHARTEVENT_OBJECT_CLICK) { if(sparam == "NewSLButton") { UpdateStopLosses(); } else if(sparam == "NewTPButton") { UpdateTakeProfits(); } } } //+------------------------------------------------------------------+ //| Check for new trading opportunities | //+------------------------------------------------------------------+ void CheckForEntry() { // Don't open new trades if we've reached maximum if(CountOrdersByMagic(gMagicNumber) >= InpMaxTrades) return; // Get indicator values double ema20 = iMA(NULL, PERIOD_H2, 20, 0, MODE_EMA, PRICE_CLOSE, 1); double ema20Prev = iMA(NULL, PERIOD_H2, 20, 0, MODE_EMA, PRICE_CLOSE, 2); double rsi7 = iRSI(NULL, PERIOD_H2, 7, PRICE_CLOSE, 1); double rsi7Prev = iRSI(NULL, PERIOD_H2, 7, PRICE_CLOSE, 2); double atr = iATR(NULL, PERIOD_H2, InpATRPeriod, 1); // Get price data double open = iOpen(NULL, PERIOD_H2, 1); double close = iClose(NULL, PERIOD_H2, 1); double high = iHigh(NULL, PERIOD_H2, 1); double low = iLow(NULL, PERIOD_H2, 1); // Calculate dynamic position size based on risk double riskAmount = AccountBalance() * InpRiskPercent / 100; double pointValue = MarketInfo(Symbol(), MODE_TICKVALUE); double stopLossDistance = atr * InpATRMultiplier; double lotSize = NormalizeDouble(riskAmount / (stopLossDistance * pointValue), 2); // Check for long entry conditions if(close > ema20 && rsi7 >= 70 && OrdersTotal() == 0) { double entryPrice = Ask; double stopLossPrice = low - (5 * Point); // Low of candle minus buffer double stopLossPoints = (entryPrice - stopLossPrice) / Point; double takeProfitPoints = 2.5 * stopLossPoints; // Send buy order CustomBuyOrder(Symbol(), lotSize, stopLossPoints, takeProfitPoints, gMagicNumber); } // Check for short entry conditions else if(close < ema20 && rsi7 <= 30 && OrdersTotal() == 0 ) { double entryPrice = Bid; double stopLossPrice = high + (5 * Point); // High of candle plus buffer double stopLossPoints = (stopLossPrice - entryPrice) / Point; double takeProfitPoints = 2.5 * stopLossPoints; // Send sell order CustomSellOrder(Symbol(), lotSize, stopLossPoints, takeProfitPoints, gMagicNumber); } } //+------------------------------------------------------------------+ //| Manage trade exits | //+------------------------------------------------------------------+ void ManageExits() { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderMagicNumber() == gMagicNumber) { // Get current RSI value for exit conditions double currentRSI = iRSI(NULL, PERIOD_H2, 7, PRICE_CLOSE, 0); if(OrderType() == OP_BUY) { // Exit condition 1: RSI drops below 70 if(InpUseRSIExit && currentRSI < 70) { CloseBuyOrders(gMagicNumber); continue; } // Exit condition 2: Price closes below 20 EMA double ema20 = iMA(NULL, PERIOD_H2, 20, 0, MODE_EMA, PRICE_CLOSE, 0); if(iClose(NULL, PERIOD_H2, 0) < ema20) { CloseBuyOrders(gMagicNumber); continue; } } else if(OrderType() == OP_SELL) { // Exit condition 1: RSI rises above 30 if(InpUseRSIExit && currentRSI > 30) { CloseSellOrders(gMagicNumber); continue; } // Exit condition 2: Price closes above 20 EMA double ema20 = iMA(NULL, PERIOD_H2, 20, 0, MODE_EMA, PRICE_CLOSE, 0); if(iClose(NULL, PERIOD_H2, 0) > ema20) { CloseSellOrders(gMagicNumber); continue; } } } } } //+------------------------------------------------------------------+ //| Manage trailing stops | //+------------------------------------------------------------------+ void ManageTrailingStops() { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderMagicNumber() == gMagicNumber) { double currentPrice = (OrderType() == OP_BUY) ? Bid : Ask; double profitInPoints = (OrderType() == OP_BUY) ? (currentPrice - OrderOpenPrice()) / Point : (OrderOpenPrice() - currentPrice) / Point; // Only trail if we're in profit by more than 1.2x initial risk if(profitInPoints > 1.2 * gSlPoints) { double newStop = (OrderType() == OP_BUY) ? OrderOpenPrice() + (0.5 * gSlPoints * Point) : // Move to breakeven + buffer OrderOpenPrice() - (0.5 * gSlPoints * Point); // For buys, new stop must be higher than current stop if((OrderType() == OP_BUY && newStop > OrderStopLoss()) || (OrderType() == OP_SELL && newStop < OrderStopLoss()) || OrderStopLoss() == 0) { if(!ModifyOrder(OrderTicket(), OrderOpenPrice(), newStop, OrderTakeProfit())) { Print("Failed to modify order #", OrderTicket()); } } } } } } //+------------------------------------------------------------------+ //| Check if it's a new candle | //+------------------------------------------------------------------+ bool IsNewCandle() { static datetime lastBarTime = 0; datetime currentBarTime = iTime(NULL, PERIOD_H2, 0); if(lastBarTime != currentBarTime) { lastBarTime = currentBarTime; return true; } return false; } //+------------------------------------------------------------------+ //| Update stop losses for all trades | //+------------------------------------------------------------------+ void UpdateStopLosses() { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderMagicNumber() == gMagicNumber) { double newStop = (OrderType() == OP_BUY) ? OrderOpenPrice() - gSlPoints * Point : OrderOpenPrice() + gSlPoints * Point; if(!ModifyOrder(OrderTicket(), OrderOpenPrice(), newStop, OrderTakeProfit())) { Print("Failed to update SL for order #", OrderTicket()); } } } } //+------------------------------------------------------------------+ //| Update take profits for all trades | //+------------------------------------------------------------------+ void UpdateTakeProfits() { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderMagicNumber() == gMagicNumber) { double newTP = (OrderType() == OP_BUY) ? OrderOpenPrice() + gTpPoints * Point : OrderOpenPrice() - gTpPoints * Point; if(!ModifyOrder(OrderTicket(), OrderOpenPrice(), OrderStopLoss(), newTP)) { Print("Failed to update TP for order #", OrderTicket()); } } } } //+------------------------------------------------------------------+ //| Safe Order Modification Wrapper | //+------------------------------------------------------------------+ bool ModifyOrder(int ticket, double price, double sl, double tp) { bool res = OrderModify(ticket, price, sl, tp, 0, clrNONE); if(!res) { Print("OrderModify failed for ticket #", ticket, " Error: ", GetLastError()); return false; } return true; }