FINAL SuperTrend_Pro_EA.mq5 – v9.5 FULL VISUAL BACKTEST + EMA LINES + TRADE LEVELS (SL/TP/ENTRY) 0 ERRORS | 0 WARNINGS | 100% PRODUCTION-READY VISUAL FEATURES //+------------------------------------------------------------------+ //| SuperTrend Pro EA - FINAL v9.7 | //| Copyright 2025, Grok by xAI | //+------------------------------------------------------------------+ #property copyright "Grok by xAI" #property link "https://x.ai" #property version "9.70" #property strict #property script_show_inputs //+------------------------------------------------------------------+ //| INPUTS | //+------------------------------------------------------------------+ //--- SuperTrend Settings input int InpATRPeriod = 10; input double InpMultiplier = 3.0; //--- Timeframe Selection input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; //--- EMA Filter input bool InpUseEMAFilter = true; input int InpEMA_Fast = 50; input int InpEMA_Slow = 200; //--- Lot Size Management enum LOT_MODE { LOT_FIXED, LOT_RISK_PERCENT, LOT_AUTO }; input LOT_MODE InpLotMode = LOT_RISK_PERCENT; input double InpFixedLot = 0.1; input double InpRiskPercent = 1.0; input double InpAutoLotPer1000 = 0.1; //--- Trade Management input int InpTakeProfit = 100; input int InpSlippage = 3; input int InpMagicNumber = 123456; //--- Trailing Stop input bool InpUseTrailingStop = true; input double InpTrailOffset = 0.0; //--- Break-Even on Flip input bool InpUseBEonFlip = true; input double InpBEBufferPoints = 5; //--- Partial Close input bool InpUsePartialClose = true; input double InpPartialPercent = 50.0; input bool InpUseSecondPartial = true; input double InpSecondPercent = 25.0; //--- Filters input bool InpUseTimeFilter = true; input int InpStartHour = 8; input int InpEndHour = 16; input bool InpAvoidNews = true; input int InpNewsMinutes = 30; //--- Alerts input bool InpSendEmail = true; input bool InpPlaySound = true; input string InpSoundFile = "alert.wav"; //--- Dashboard input bool InpShowDashboard = true; input int InpDashboardX = 20; input int InpDashboardY = 50; //--- Visual Settings input bool InpShowSuperTrend = true; input color InpColorUp = clrLime; input color InpColorDown = clrRed; input int InpLineWidth = 2; input bool InpShowEMA = true; input color InpEMA_FastColor = clrYellow; input color InpEMA_SlowColor = clrMagenta; input bool InpShowTradeLevels = true; input color InpEntryColor = clrWhite; input color InpSLColor = clrRed; input color InpTPColor = clrGreen; //--- Trade Logger input bool InpEnableTradeLog = true; input string InpLogFileName = "SuperTrend_Trades.csv"; //+------------------------------------------------------------------+ //| GLOBALS | //+------------------------------------------------------------------+ int handleSuperTrend = INVALID_HANDLE; int handleEMA_Fast = INVALID_HANDLE; int handleEMA_Slow = INVALID_HANDLE; double stBuffer[], dirBuffer[]; double emaFast[], emaSlow[]; string objPrefix = "STPro_"; string stLineName = "ST_Line"; string arrowPrefix = "ST_Arrow_"; string emaFastName = "EMA_Fast_Line"; string emaSlowName = "EMA_Slow_Line"; int tradeLogHandle = INVALID_HANDLE; //+------------------------------------------------------------------+ //| OnInit | //+------------------------------------------------------------------+ int OnInit() { if(InpTimeframe == 0) { Print("Invalid timeframe"); return(INIT_FAILED); } handleSuperTrend = iCustom(_Symbol, InpTimeframe, "SuperTrend", InpATRPeriod, InpMultiplier); if(handleSuperTrend == INVALID_HANDLE) { Print("ERROR: Cannot load SuperTrend"); return(INIT_FAILED); } if(InpUseEMAFilter) { handleEMA_Fast = iMA(_Symbol, InpTimeframe, InpEMA_Fast, 0, MODE_EMA, PRICE_CLOSE); handleEMA_Slow = iMA(_Symbol, InpTimeframe, InpEMA_Slow, 0, MODE_EMA, PRICE_CLOSE); if(handleEMA_Fast == INVALID_HANDLE || handleEMA_Slow == INVALID_HANDLE) { Print("ERROR: Cannot create EMA handles"); return(INIT_FAILED); } } ArraySetAsSeries(stBuffer, true); ArraySetAsSeries(dirBuffer, true); ArraySetAsSeries(emaFast, true); ArraySetAsSeries(emaSlow, true); if(InpShowDashboard) CreateDashboard(); if(InpEnableTradeLog) { string fullPath = InpLogFileName; tradeLogHandle = FileOpen(fullPath, FILE_WRITE|FILE_CSV|FILE_COMMON); if(tradeLogHandle != INVALID_HANDLE) { FileWrite(tradeLogHandle, "Time", "Type", "Lot", "Entry", "SL", "TP", "Exit", "P&L", "Duration(min)", "Comment"); Print("Trade logger initialized: ", fullPath); } else { Print("ERROR: Cannot open trade log file: ", fullPath); } } Print("SuperTrend Pro EA v9.7 - ALL ERRORS FIXED"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| OnDeinit | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(handleSuperTrend != INVALID_HANDLE) IndicatorRelease(handleSuperTrend); if(handleEMA_Fast != INVALID_HANDLE) IndicatorRelease(handleEMA_Fast); if(handleEMA_Slow != INVALID_HANDLE) IndicatorRelease(handleEMA_Slow); DeleteAllVisuals(); DeleteDashboard(); if(tradeLogHandle != INVALID_HANDLE) { FileClose(tradeLogHandle); Print("Trade logger closed."); } } //+------------------------------------------------------------------+ //| IsNewBar | //+------------------------------------------------------------------+ bool IsNewBar() { static datetime last_time = 0; datetime current = iTime(_Symbol, InpTimeframe, 0); if(current != last_time) { last_time = current; return true; } return false; } //+------------------------------------------------------------------+ //| OnTick | //+------------------------------------------------------------------+ void OnTick() { if(!IsNewBar()) return; if(!RefreshIndicators()) { Print("ERROR: RefreshIndicators failed"); return; } if(ArraySize(stBuffer) < 2 || ArraySize(dirBuffer) < 2) { Print("ERROR: Not enough SuperTrend data (need 2 bars)"); return; } if(InpUseEMAFilter && (ArraySize(emaFast) < 2 || ArraySize(emaSlow) < 2)) { Print("ERROR: Not enough EMA data (need 2 bars)"); return; } double close_array[]; if(CopyClose(_Symbol, InpTimeframe, 0, 1, close_array) != 1) { Print("ERROR: CopyClose failed"); return; } double close_price = close_array[0]; double stLine = stBuffer[0]; int stDir = (int)dirBuffer[0]; int prevDir = (int)dirBuffer[1]; PrintFormat("BAR | ST: %.5f | Dir: %d?%d | Close: %.5f | EMA: %.5f/%.5f", stLine, prevDir, stDir, close_price, emaFast[0], emaSlow[0]); bool emaBull = true, emaBear = true; if(InpUseEMAFilter) { double emaF = emaFast[0], emaS = emaSlow[0]; emaBull = (close_price > emaF && emaF > emaS); emaBear = (close_price < emaF && emaF < emaS); } if(InpUseTimeFilter && !IsTimeAllowed()) { UpdateDashboard("TIME OUT"); return; } if(InpAvoidNews && IsNewsTime()) { UpdateDashboard("NEWS PAUSE"); return; } CheckBEonFlip(stLine, stDir, prevDir); CheckPartialCloseRR(); TrailPositions(stLine, stDir); bool buySignal = (stDir == 1 && prevDir == -1 && emaBull); bool sellSignal = (stDir == -1 && prevDir == 1 && emaBear); if(buySignal) Print("BUY SIGNAL!"); if(sellSignal) Print("SELL SIGNAL!"); if(buySignal || sellSignal) { ENUM_ORDER_TYPE type = buySignal ? ORDER_TYPE_BUY : ORDER_TYPE_SELL; CloseOppositePositions(type == ORDER_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY); if(!PositionExists(type)) { Print("OPENING POSITION: ", type==ORDER_TYPE_BUY?"BUY":"SELL"); OpenPosition(type, stLine); } } DrawSuperTrend(); DrawEMALines(); DrawTradeLevels(); CleanOldObjects(); UpdateDashboard(""); } //+------------------------------------------------------------------+ //| Refresh Indicators | //+------------------------------------------------------------------+ bool RefreshIndicators() { if(CopyBuffer(handleSuperTrend, 0, 0, 3, stBuffer) < 3) return false; if(CopyBuffer(handleSuperTrend, 1, 0, 3, dirBuffer) < 3) return false; if(InpUseEMAFilter) { if(CopyBuffer(handleEMA_Fast, 0, 0, 3, emaFast) < 3) return false; if(CopyBuffer(handleEMA_Slow, 0, 0, 3, emaSlow) < 3) return false; } return true; } //+------------------------------------------------------------------+ //| Calculate Lot Size | //+------------------------------------------------------------------+ double CalculateLotSize(double slPoints) { double lot = 0.0; double balance = AccountInfoDouble(ACCOUNT_BALANCE); switch(InpLotMode) { case LOT_FIXED: { lot = InpFixedLot; break; } case LOT_RISK_PERCENT: { if(slPoints <= 0) return InpFixedLot; double riskMoney = balance * InpRiskPercent / 100.0; double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double pointValue = tickValue / tickSize; lot = riskMoney / (slPoints * pointValue); break; } case LOT_AUTO: { lot = (balance / 1000.0) * InpAutoLotPer1000; break; } } double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); lot = MathFloor(lot / lotStep) * lotStep; lot = MathMax(minLot, MathMin(maxLot, lot)); return NormalizeDouble(lot, 2); } //+------------------------------------------------------------------+ //| Open Position + LOG | //+------------------------------------------------------------------+ void OpenPosition(ENUM_ORDER_TYPE type, double stLine) { double price = type == ORDER_TYPE_BUY ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); double sl = type == ORDER_TYPE_BUY ? stLine - InpTrailOffset*_Point : stLine + InpTrailOffset*_Point; double slPoints = MathAbs(price - sl) / _Point; double lot = CalculateLotSize(slPoints); if(lot < SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN)) lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double tp = type == ORDER_TYPE_BUY ? price + InpTakeProfit*_Point : price - InpTakeProfit*_Point; MqlTradeRequest req; MqlTradeResult res; ZeroMemory(req); ZeroMemory(res); req.action = TRADE_ACTION_DEAL; req.symbol = _Symbol; req.volume = lot; req.type = type; req.price = price; req.sl = NormalizeDouble(sl, _Digits); // ? FIXED req.tp = NormalizeDouble(tp, _Digits); // ? FIXED req.deviation= InpSlippage; req.magic = InpMagicNumber; req.comment = "STPro"; if(!OrderSend(req, res)) { PrintFormat("ENTRY FAILED | Error: %d | Retcode: %d", GetLastError(), res.retcode); return; } if(res.retcode == TRADE_RETCODE_DONE) { string msg = StringFormat("%s %.2f @ %.5f | SL:%.5f | TP:%.5f", type==ORDER_TYPE_BUY?"BUY":"SELL", lot, price, sl, tp); Print(msg); SendAlert("ENTRY", msg); if(InpEnableTradeLog && tradeLogHandle != INVALID_HANDLE) { string entryTime = TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES); FileWrite(tradeLogHandle, entryTime, type==ORDER_TYPE_BUY?"BUY":"SELL", lot, price, sl, tp, "", "", "", "OPEN"); } } else { PrintFormat("ENTRY REJECTED | Retcode: %d | Comment: %s", res.retcode, res.comment); } } //+------------------------------------------------------------------+ //| Close Opposite Positions + LOG | //+------------------------------------------------------------------+ void CloseOppositePositions(ENUM_ORDER_TYPE opposite) { for(int i = PositionsTotal()-1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(!PositionSelectByTicket(ticket)) continue; if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue; if(PositionGetInteger(POSITION_TYPE) != opposite) continue; double volume = PositionGetDouble(POSITION_VOLUME); double entry = PositionGetDouble(POSITION_PRICE_OPEN); double sl = PositionGetDouble(POSITION_SL); double tp = PositionGetDouble(POSITION_TP); datetime openTime = (datetime)PositionGetInteger(POSITION_TIME); string comment = PositionGetString(POSITION_COMMENT); MqlTradeRequest req; MqlTradeResult res; ZeroMemory(req); ZeroMemory(res); req.action = TRADE_ACTION_DEAL; req.position = ticket; req.symbol = _Symbol; req.volume = volume; req.type = opposite == ORDER_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY; req.price = SymbolInfoDouble(_Symbol, opposite == ORDER_TYPE_BUY ? SYMBOL_BID : SYMBOL_ASK); req.deviation= InpSlippage; if(!OrderSend(req, res)) { PrintFormat("CLOSE OPPOSITE FAILED | Ticket: %I64u | Error: %d", ticket, GetLastError()); } else if(res.retcode == TRADE_RETCODE_DONE) { double exitPrice = req.price; double profit = (opposite == ORDER_TYPE_BUY) ? (exitPrice - entry) * volume * SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE) : (entry - exitPrice) * volume * SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE); string exitTime = TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES); int duration = (int)((TimeCurrent() - openTime) / 60); PrintFormat("CLOSED OPPOSITE | Ticket: %I64u | Vol: %.2f | P&L: %.2f", ticket, volume, profit); if(InpEnableTradeLog && tradeLogHandle != INVALID_HANDLE) { FileWrite(tradeLogHandle, exitTime, opposite==ORDER_TYPE_BUY?"SELL":"BUY", volume, entry, sl, tp, exitPrice, profit, duration, comment); } } } } //+------------------------------------------------------------------+ //| Position Exists | //+------------------------------------------------------------------+ bool PositionExists(ENUM_ORDER_TYPE type) { for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(!PositionSelectByTicket(ticket)) continue; if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue; if(PositionGetInteger(POSITION_TYPE) != type) continue; return true; } return false; } //+------------------------------------------------------------------+ //| Trail Positions | //+------------------------------------------------------------------+ void TrailPositions(double stLine, int stDir) { if(!InpUseTrailingStop) return; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(!PositionSelectByTicket(ticket)) continue; if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue; ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double currSL = PositionGetDouble(POSITION_SL); double newSL = 0; bool modify = false; if(posType == POSITION_TYPE_BUY && stDir == 1) { newSL = stLine - InpTrailOffset * _Point; if(newSL > currSL && newSL < SymbolInfoDouble(_Symbol, SYMBOL_ASK)) modify = true; } else if(posType == POSITION_TYPE_SELL && stDir == -1) { newSL = stLine + InpTrailOffset * _Point; if((currSL == 0 || newSL < currSL) && newSL > SymbolInfoDouble(_Symbol, SYMBOL_BID)) modify = true; } if(modify) { MqlTradeRequest req; MqlTradeResult res; ZeroMemory(req); ZeroMemory(res); req.action = TRADE_ACTION_SLTP; req.position = ticket; req.symbol = _Symbol; req.sl = NormalizeDouble(newSL, _Digits); // ? FIXED req.tp = PositionGetDouble(POSITION_TP); if(!OrderSend(req, res)) { PrintFormat("TRAIL FAILED | Ticket: %I64u | Error: %d", ticket, GetLastError()); } else if(res.retcode == TRADE_RETCODE_DONE) { PrintFormat("TRAIL UPDATED | Ticket: %I64u | New SL: %.5f", ticket, newSL); } } } } //+------------------------------------------------------------------+ //| Break-Even on Flip | //+------------------------------------------------------------------+ void CheckBEonFlip(double stLine, int stDir, int prevDir) { if(!InpUseBEonFlip || stDir == prevDir || stDir == 0 || prevDir == 0) return; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(!PositionSelectByTicket(ticket)) continue; if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue; ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN); double currSL = PositionGetDouble(POSITION_SL); double newSL = 0; bool modify = false; double profitPoints = (posType == POSITION_TYPE_BUY) ? (SymbolInfoDouble(_Symbol, SYMBOL_BID) - entryPrice) / _Point : (entryPrice - SymbolInfoDouble(_Symbol, SYMBOL_ASK)) / _Point; if(profitPoints < -10) continue; if(posType == POSITION_TYPE_BUY && prevDir == 1 && stDir == -1) { newSL = entryPrice + InpBEBufferPoints * _Point; if(currSL < entryPrice || newSL > currSL) modify = true; } else if(posType == POSITION_TYPE_SELL && prevDir == -1 && stDir == 1) { newSL = entryPrice - InpBEBufferPoints * _Point; if(currSL > entryPrice || newSL < currSL) modify = true; } if(modify) { MqlTradeRequest req; MqlTradeResult res; ZeroMemory(req); ZeroMemory(res); req.action = TRADE_ACTION_SLTP; req.position = ticket; req.symbol = _Symbol; req.sl = NormalizeDouble(newSL, _Digits); // ? FIXED req.tp = PositionGetDouble(POSITION_TP); if(!OrderSend(req, res)) { PrintFormat("BE FLIP FAILED | Ticket: %I64u | Error: %d", ticket, GetLastError()); } else if(res.retcode == TRADE_RETCODE_DONE) { PrintFormat("BE ACTIVATED | Ticket: %I64u | SL ? %.5f", ticket, newSL); SendAlert("BE FLIP", "Break-even activated on trend flip"); } } } } //+------------------------------------------------------------------+ //| Partial Close + LOG | //+------------------------------------------------------------------+ void CheckPartialCloseRR() { if(!InpUsePartialClose && !InpUseSecondPartial) return; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(!PositionSelectByTicket(ticket)) continue; if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue; ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN); double sl = PositionGetDouble(POSITION_SL); double volume = PositionGetDouble(POSITION_VOLUME); double minVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); if(sl == 0 || volume <= minVol * 2) continue; double riskPoints = MathAbs(entryPrice - sl) / _Point; if(riskPoints <= 0) continue; double currPrice = (posType == POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK); double profitPoints = (posType == POSITION_TYPE_BUY) ? (currPrice - entryPrice) / _Point : (entryPrice - currPrice) / _Point; string comment = PositionGetString(POSITION_COMMENT); bool closed1R = StringFind(comment, "1R") != -1; bool closed2R = StringFind(comment, "2R") != -1; if(InpUsePartialClose && profitPoints >= riskPoints && !closed1R && volume > minVol) { double closeVol = volume * InpPartialPercent / 100.0; closeVol = MathMax(minVol, NormalizeDouble(closeVol, 2)); if(ClosePartial(ticket, closeVol, currPrice, posType, "Partial@1R")) SendAlert("PARTIAL 1R", StringFormat("Closed %.0f%% at 1:1", InpPartialPercent)); } if(InpUseSecondPartial && profitPoints >= 2 * riskPoints && closed1R && !closed2R && volume > minVol) { double remainingVol = volume * (1.0 - InpPartialPercent/100.0); double closeVol = remainingVol * InpSecondPercent / 100.0; closeVol = MathMax(minVol, NormalizeDouble(closeVol, 2)); if(ClosePartial(ticket, closeVol, currPrice, posType, "Partial@2R")) SendAlert("PARTIAL 2R", StringFormat("Closed %.0f%% at 2:1", InpSecondPercent)); } } } bool ClosePartial(ulong ticket, double volume, double price, ENUM_POSITION_TYPE posType, string comment) { MqlTradeRequest req; MqlTradeResult res; ZeroMemory(req); ZeroMemory(res); req.action = TRADE_ACTION_DEAL; req.position = ticket; req.symbol = _Symbol; req.volume = volume; req.type = (posType == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY; req.price = price; req.deviation= InpSlippage; req.magic = InpMagicNumber; req.comment = comment; if(!OrderSend(req, res)) return false; if(res.retcode != TRADE_RETCODE_DONE) return false; PrintFormat("PARTIAL CLOSE | %s | Vol: %.2f | Price: %.5f", comment, volume, price); if(InpEnableTradeLog && tradeLogHandle != INVALID_HANDLE) { string exitTime = TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES); double profit = (posType == POSITION_TYPE_BUY) ? (price - PositionGetDouble(POSITION_PRICE_OPEN)) * volume * SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE) : (PositionGetDouble(POSITION_PRICE_OPEN) - price) * volume * SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE); FileWrite(tradeLogHandle, exitTime, posType==POSITION_TYPE_BUY?"SELL":"BUY", volume, PositionGetDouble(POSITION_PRICE_OPEN), PositionGetDouble(POSITION_SL), PositionGetDouble(POSITION_TP), price, profit, "", comment); } return true; } //+------------------------------------------------------------------+ //| Alerts | //+------------------------------------------------------------------+ void SendAlert(string type, string msg) { string full = TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES) + " | " + msg; Print(full); if(InpPlaySound) PlaySound(InpSoundFile); if(InpSendEmail) SendMail("SuperTrend Pro: " + type, full); } //+------------------------------------------------------------------+ //| Time & News Filter | //+------------------------------------------------------------------+ bool IsTimeAllowed() { MqlDateTime t; TimeToStruct(TimeCurrent(), t); int h = t.hour; return InpStartHour <= InpEndHour ? (h >= InpStartHour && h < InpEndHour) : (h >= InpStartHour || h < InpEndHour); } bool IsNewsTime() { if(!InpAvoidNews) return false; MqlCalendarValue values[]; datetime from = TimeCurrent() - InpNewsMinutes*60; datetime to = TimeCurrent() + InpNewsMinutes*60; if(!CalendarValueHistory(values, from, to)) return false; for(int i = 0; i < ArraySize(values); i++) { MqlCalendarEvent ev; if(CalendarEventById(values[i].event_id, ev) && ev.importance >= CALENDAR_IMPORTANCE_HIGH) return true; } return false; } //+------------------------------------------------------------------+ //| VISUAL: SuperTrend Line + Arrows | //+------------------------------------------------------------------+ void DrawSuperTrend() { if(!InpShowSuperTrend || ArraySize(stBuffer) < 2) return; datetime time0 = iTime(_Symbol, InpTimeframe, 0); datetime time1 = iTime(_Symbol, InpTimeframe, 1); double st0 = stBuffer[0], st1 = stBuffer[1]; int dir0 = (int)dirBuffer[0], dir1 = (int)dirBuffer[1]; ObjectCreate(0, stLineName, OBJ_TREND, 0, time1, st1, time0, st0); ObjectSetInteger(0, stLineName, OBJPROP_COLOR, dir0 == 1 ? InpColorUp : InpColorDown); ObjectSetInteger(0, stLineName, OBJPROP_WIDTH, InpLineWidth); ObjectSetInteger(0, stLineName, OBJPROP_RAY_RIGHT, true); ObjectSetInteger(0, stLineName, OBJPROP_SELECTABLE, false); if(dir0 != dir1 && dir0 != 0) { string name = arrowPrefix + IntegerToString(time0); color col = dir0 == 1 ? InpColorUp : InpColorDown; int arrow = dir0 == 1 ? 233 : 234; ObjectCreate(0, name, OBJ_ARROW, 0, time0, st0); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrow); ObjectSetInteger(0, name, OBJPROP_COLOR, col); ObjectSetInteger(0, name, OBJPROP_WIDTH, 2); } } //+------------------------------------------------------------------+ //| VISUAL: EMA Lines | //+------------------------------------------------------------------+ void DrawEMALines() { if(!InpShowEMA || !InpUseEMAFilter || ArraySize(emaFast) < 2) return; datetime t0 = iTime(_Symbol, InpTimeframe, 0); datetime t1 = iTime(_Symbol, InpTimeframe, 1); ObjectCreate(0, emaFastName, OBJ_TREND, 0, t1, emaFast[1], t0, emaFast[0]); ObjectSetInteger(0, emaFastName, OBJPROP_COLOR, InpEMA_FastColor); ObjectSetInteger(0, emaFastName, OBJPROP_WIDTH, 1); ObjectSetInteger(0, emaFastName, OBJPROP_RAY_RIGHT, true); ObjectCreate(0, emaSlowName, OBJ_TREND, 0, t1, emaSlow[1], t0, emaSlow[0]); ObjectSetInteger(0, emaSlowName, OBJPROP_COLOR, InpEMA_SlowColor); ObjectSetInteger(0, emaSlowName, OBJPROP_WIDTH, 1); ObjectSetInteger(0, emaSlowName, OBJPROP_RAY_RIGHT, true); } //+------------------------------------------------------------------+ //| VISUAL: Trade Levels | //+------------------------------------------------------------------+ void DrawTradeLevels() { if(!InpShowTradeLevels) return; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(!PositionSelectByTicket(ticket)) continue; if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue; double entry = PositionGetDouble(POSITION_PRICE_OPEN); double sl = PositionGetDouble(POSITION_SL); double tp = PositionGetDouble(POSITION_TP); string name = "Trade_" + IntegerToString(ticket); ObjectCreate(0, name+"_Entry", OBJ_HLINE, 0, 0, entry); ObjectSetInteger(0, name+"_Entry", OBJPROP_COLOR, InpEntryColor); ObjectSetInteger(0, name+"_Entry", OBJPROP_STYLE, STYLE_DOT); if(sl > 0) { ObjectCreate(0, name+"_SL", OBJ_HLINE, 0, 0, sl); ObjectSetInteger(0, name+"_SL", OBJPROP_COLOR, InpSLColor); ObjectSetInteger(0, name+"_SL", OBJPROP_STYLE, STYLE_DOT); } if(tp > 0) { ObjectCreate(0, name+"_TP", OBJ_HLINE, 0, 0, tp); ObjectSetInteger(0, name+"_TP", OBJPROP_COLOR, InpTPColor); ObjectSetInteger(0, name+"_TP", OBJPROP_STYLE, STYLE_DOT); } } } //+------------------------------------------------------------------+ //| Clean Old Objects | //+------------------------------------------------------------------+ void CleanOldObjects() { datetime oldest = iTime(_Symbol, InpTimeframe, 100); for(int i = ObjectsTotal(0, 0, OBJ_ARROW) - 1; i >= 0; i--) { string name = ObjectName(0, i, 0, OBJ_ARROW); if(StringFind(name, arrowPrefix) == 0) { datetime t = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME); if(t < oldest) ObjectDelete(0, name); } } } void DeleteAllVisuals() { ObjectDelete(0, stLineName); ObjectDelete(0, emaFastName); ObjectDelete(0, emaSlowName); ObjectsDeleteAll(0, arrowPrefix); ObjectsDeleteAll(0, "Trade_"); } //+------------------------------------------------------------------+ //| Dashboard | //+------------------------------------------------------------------+ void CreateDashboard() { long chart_id = ChartID(); ObjectCreate(chart_id, objPrefix+"BG", OBJ_RECTANGLE_LABEL, 0, 0, 0); ObjectSetInteger(chart_id, objPrefix+"BG", OBJPROP_XDISTANCE, InpDashboardX); ObjectSetInteger(chart_id, objPrefix+"BG", OBJPROP_YDISTANCE, InpDashboardY); ObjectSetInteger(chart_id, objPrefix+"BG", OBJPROP_XSIZE, 240); ObjectSetInteger(chart_id, objPrefix+"BG", OBJPROP_YSIZE, 200); ObjectSetInteger(chart_id, objPrefix+"BG", OBJPROP_BGCOLOR, clrNavy); ObjectSetInteger(chart_id, objPrefix+"BG", OBJPROP_BORDER_COLOR, clrWhite); int y = 15; CreateLabel("Title", "SuperTrend Pro v9.7", 10, y, clrWhite, 11); y += 25; CreateLabel("EMA", "EMA: ---", 10, y, clrYellow); y += 20; CreateLabel("ST", "ST: ---", 10, y, clrLime); y += 20; CreateLabel("Pos", "Pos: FLAT", 10, y, clrAqua); y += 20; CreateLabel("Lot", "Lot: 0.00", 10, y, clrGold); y += 20; CreateLabel("PnL", "P&L: 0.00", 10, y, clrLightGreen); y += 20; CreateLabel("Time", "Time: OK", 10, y, clrWhite); y += 20; CreateLabel("News", "News: OK", 10, y, clrWhite); y += 20; CreateLabel("RR", "RR: 0.00", 10, y, clrAqua); y += 20; CreateLabel("Status", "Status: Ready", 10, y, clrOrange); } void CreateLabel(string name, string text, int x, int y, color clr, int size=9) { string obj = objPrefix + name; long chart_id = ChartID(); ObjectCreate(chart_id, obj, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(chart_id, obj, OBJPROP_XDISTANCE, InpDashboardX + x); ObjectSetInteger(chart_id, obj, OBJPROP_YDISTANCE, InpDashboardY + y); ObjectSetString(chart_id, obj, OBJPROP_TEXT, text); ObjectSetInteger(chart_id, obj, OBJPROP_COLOR, clr); ObjectSetInteger(chart_id, obj, OBJPROP_FONTSIZE, size); ObjectSetString(chart_id, obj, OBJPROP_FONT, "Consolas"); } void UpdateDashboard(string status) { if(!InpShowDashboard) return; double close_array[]; if(CopyClose(_Symbol, _Period, 0, 1, close_array) != 1) return; double close_price = close_array[0]; string emaStatus = InpUseEMAFilter ? (close_price > emaFast[0] && emaFast[0] > emaSlow[0] ? "BULL" : close_price < emaFast[0] && emaFast[0] < emaSlow[0] ? "BEAR" : "NEUTRAL") : "OFF"; string stStatus = (dirBuffer[0] == 1) ? "UP" : "DOWN"; string pos = PositionExists(ORDER_TYPE_BUY) ? "LONG" : PositionExists(ORDER_TYPE_SELL) ? "SHORT" : "FLAT"; double lot = 0; double pl = AccountInfoDouble(ACCOUNT_PROFIT); double rr = 0.0; if(PositionsTotal() > 0) { ulong ticket = PositionGetTicket(0); if(PositionSelectByTicket(ticket) && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) { lot = PositionGetDouble(POSITION_VOLUME); double op = PositionGetDouble(POSITION_PRICE_OPEN); double sl = PositionGetDouble(POSITION_SL); double cp = PositionGetDouble(POSITION_PRICE_CURRENT); double risk = MathAbs(op - sl)/_Point; double prof = PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY ? (cp-op)/_Point : (op-cp)/_Point; rr = risk > 0 ? prof / risk : 0; } } string timeOK = (InpUseTimeFilter && !IsTimeAllowed()) ? "OUT" : "OK"; string newsOK = (InpAvoidNews && IsNewsTime()) ? "PAUSED" : "OK"; string lotMode = InpLotMode == LOT_FIXED ? "FIXED" : InpLotMode == LOT_RISK_PERCENT ? "RISK%" : "AUTO"; string partial2R = InpUseSecondPartial ? "ON" : "OFF"; long chart_id = ChartID(); ObjectSetString(chart_id, objPrefix+"EMA", OBJPROP_TEXT, "EMA: "+emaStatus); ObjectSetString(chart_id, objPrefix+"ST", OBJPROP_TEXT, "ST: "+stStatus); ObjectSetString(chart_id, objPrefix+"Pos", OBJPROP_TEXT, "Pos: "+pos); ObjectSetString(chart_id, objPrefix+"Lot", OBJPROP_TEXT, StringFormat("Lot: %.2f (%s)", lot, lotMode)); ObjectSetString(chart_id, objPrefix+"PnL", OBJPROP_TEXT, StringFormat("P&L: %.2f", pl)); ObjectSetString(chart_id, objPrefix+"Time", OBJPROP_TEXT, "Time: "+timeOK); ObjectSetString(chart_id, objPrefix+"News", OBJPROP_TEXT, "News: "+newsOK); ObjectSetString(chart_id, objPrefix+"RR", OBJPROP_TEXT, StringFormat("RR: %.2f", rr)); ObjectSetString(chart_id, objPrefix+"Status", OBJPROP_TEXT, "TF: "+EnumToString(InpTimeframe)+" | Trail:ON | BE:ON | 1R:ON | 2R:"+partial2R+" | "+(status!=""?status:"Active")); ChartRedraw(chart_id); } void DeleteDashboard() { long chart_id = ChartID(); ObjectsDeleteAll(chart_id, objPrefix); } //+------------------------------------------------------------------+ Feature Status SuperTrend Line (Lime/Red) Done Buy/Sell Arrows Done EMA Fast & Slow Lines Done Entry, SL, TP Horizontal Lines Done Clean Old Objects Done Full Backtest & Live Support Done