//+------------------------------------------------------------------+ //| Base_EMA_Reversal_V2.mq5 | //| Base-EMA Multi-Stage Signal Framework | //+------------------------------------------------------------------+ #property copyright "Copyright 2025" #property link "" #property version "3.07" // Fix Invisible Dots #property indicator_chart_window #property indicator_buffers 12 #property indicator_plots 8 // --- Plot 1: Buy Signal --- #property indicator_label1 "BUY_SIGNAL" #property indicator_type1 DRAW_ARROW #property indicator_color1 clrLightBlue #property indicator_width1 2 // --- Plot 2: Sell Signal --- #property indicator_label2 "SELL_SIGNAL" #property indicator_type2 DRAW_ARROW #property indicator_color2 clrRed #property indicator_width2 2 // --- Plot 3: EMA 50 --- #property indicator_label3 "EMA 50" #property indicator_type3 DRAW_LINE #property indicator_color3 clrRed #property indicator_style3 STYLE_SOLID #property indicator_width3 1 // --- Plot 4: EMA 100 --- #property indicator_label4 "EMA 100" #property indicator_type4 DRAW_LINE #property indicator_color4 clrOrange #property indicator_style4 STYLE_SOLID #property indicator_width4 1 // --- Plot 5: EMA 200 --- #property indicator_label5 "EMA 200" #property indicator_type5 DRAW_LINE #property indicator_color5 clrGreen #property indicator_style5 STYLE_SOLID #property indicator_width5 1 // --- Plot 6: EMA 600 --- #property indicator_label6 "EMA 600" #property indicator_type6 DRAW_LINE #property indicator_color6 clrMagenta #property indicator_style6 STYLE_SOLID #property indicator_width6 1 // --- Plot 7: Buy SL --- #property indicator_label7 "BUY_SL" #property indicator_type7 DRAW_ARROW #property indicator_color7 clrGold // Gold is more visible than Yellow #property indicator_width7 3 // Max Thickness // --- Plot 8: Sell SL --- #property indicator_label8 "SELL_SL" #property indicator_type8 DRAW_ARROW #property indicator_color8 clrGold // Gold is more visible than Yellow #property indicator_width8 3 // Max Thickness // Input Parameters input group "=== EMA Settings ===" input int InpEMA50 = 50; input int InpEMA100 = 100; input int InpEMA200 = 200; input int InpEMA600 = 600; input group "=== Setup Settings ===" input int InpLookback = 30; // Lookback for Base Detection input double InpBaseThreshold = 50; // Pullback threshold (points) input int InpStateReset = 100; // Reset state if too long input group "=== Debug Settings ===" input bool InpDebugLogs = false; // Indicator Buffers double BufferBuySignal[]; double BufferSellSignal[]; // EMA Display Buffers double BufferEMA50[]; double BufferEMA100[]; double BufferEMA200[]; double BufferEMA600[]; // SL Buffers double BufferBuySL[]; double BufferSellSL[]; double BufferBuyTP[]; double BufferSellTP[]; double BufferBuyState[]; double BufferSellState[]; // State Variables int buy_state = 0; int sell_state = 0; double current_base_low = 0; double current_base_high = 0; // Trade Tracking Struct struct Trade { bool active; int type; // 1=Buy, -1=Sell double entry; double sl; double tp; datetime time; }; Trade active_trade; int win_count = 0; int loss_count = 0; // Handles int h_ema50, h_ema100, h_ema200, h_ema600; //+------------------------------------------------------------------+ //| OnInit | //+------------------------------------------------------------------+ int OnInit() { SetIndexBuffer(0, BufferBuySignal, INDICATOR_DATA); SetIndexBuffer(1, BufferSellSignal, INDICATOR_DATA); SetIndexBuffer(2, BufferEMA50, INDICATOR_DATA); SetIndexBuffer(3, BufferEMA100, INDICATOR_DATA); SetIndexBuffer(4, BufferEMA200, INDICATOR_DATA); SetIndexBuffer(5, BufferEMA600, INDICATOR_DATA); SetIndexBuffer(6, BufferBuySL, INDICATOR_DATA); SetIndexBuffer(7, BufferSellSL, INDICATOR_DATA); SetIndexBuffer(8, BufferBuyTP, INDICATOR_CALCULATIONS); SetIndexBuffer(9, BufferSellTP, INDICATOR_CALCULATIONS); SetIndexBuffer(10, BufferBuyState, INDICATOR_CALCULATIONS); SetIndexBuffer(11, BufferSellState, INDICATOR_CALCULATIONS); // Force Arrow Types PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(6, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(7, PLOT_DRAW_TYPE, DRAW_ARROW); // Arrow Codes PlotIndexSetInteger(0, PLOT_ARROW, 233); // Arrow Up PlotIndexSetInteger(1, PLOT_ARROW, 234); // Arrow Down // Use Code 108 (Big Circle) for maximum visibility PlotIndexSetInteger(6, PLOT_ARROW, 108); PlotIndexSetInteger(7, PLOT_ARROW, 108); // Empty Values for(int i=0; i<8; i++) PlotIndexSetDouble(i, PLOT_EMPTY_VALUE, EMPTY_VALUE); h_ema50 = iMA(_Symbol, _Period, InpEMA50, 0, MODE_EMA, PRICE_CLOSE); h_ema100 = iMA(_Symbol, _Period, InpEMA100, 0, MODE_EMA, PRICE_CLOSE); h_ema200 = iMA(_Symbol, _Period, InpEMA200, 0, MODE_EMA, PRICE_CLOSE); h_ema600 = iMA(_Symbol, _Period, InpEMA600, 0, MODE_EMA, PRICE_CLOSE); if(h_ema50 == INVALID_HANDLE || h_ema600 == INVALID_HANDLE) return(INIT_FAILED); active_trade.active = false; active_trade.type = 0; // Setup Dashboard ObjectCreate(0, "Stat_BG", OBJ_RECTANGLE_LABEL, 0, 0, 0); ObjectSetInteger(0, "Stat_BG", OBJPROP_XDISTANCE, 10); ObjectSetInteger(0, "Stat_BG", OBJPROP_YDISTANCE, 20); ObjectSetInteger(0, "Stat_BG", OBJPROP_XSIZE, 150); ObjectSetInteger(0, "Stat_BG", OBJPROP_YSIZE, 80); ObjectSetInteger(0, "Stat_BG", OBJPROP_BGCOLOR, clrBlack); ObjectSetInteger(0, "Stat_BG", OBJPROP_BORDER_TYPE, BORDER_FLAT); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| OnCalculate | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { ArraySetAsSeries(time, true); ArraySetAsSeries(open, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); ArraySetAsSeries(BufferBuySignal, true); ArraySetAsSeries(BufferSellSignal, true); ArraySetAsSeries(BufferEMA50, true); ArraySetAsSeries(BufferEMA100, true); ArraySetAsSeries(BufferEMA200, true); ArraySetAsSeries(BufferEMA600, true); ArraySetAsSeries(BufferBuySL, true); ArraySetAsSeries(BufferSellSL, true); ArraySetAsSeries(BufferBuyTP, true); ArraySetAsSeries(BufferSellTP, true); ArraySetAsSeries(BufferBuyState, true); ArraySetAsSeries(BufferSellState, true); if(rates_total < InpEMA600 + InpLookback) return(0); double ema50[], ema100[], ema200[], ema600[]; ArraySetAsSeries(ema50, true); ArraySetAsSeries(ema100, true); ArraySetAsSeries(ema200, true); ArraySetAsSeries(ema600, true); CopyBuffer(h_ema50, 0, 0, rates_total, ema50); CopyBuffer(h_ema100, 0, 0, rates_total, ema100); CopyBuffer(h_ema200, 0, 0, rates_total, ema200); CopyBuffer(h_ema600, 0, 0, rates_total, ema600); int limit; if(prev_calculated == 0) { limit = rates_total - InpEMA600 - 1; buy_state = 0; sell_state = 0; win_count = 0; loss_count = 0; active_trade.active = false; ArrayInitialize(BufferBuySignal, EMPTY_VALUE); ArrayInitialize(BufferSellSignal, EMPTY_VALUE); ArrayInitialize(BufferBuySL, EMPTY_VALUE); ArrayInitialize(BufferSellSL, EMPTY_VALUE); } else { limit = rates_total - prev_calculated; } for(int i = limit; i >= 0; i--) { BufferBuySignal[i] = EMPTY_VALUE; BufferSellSignal[i] = EMPTY_VALUE; BufferBuySL[i] = EMPTY_VALUE; BufferSellSL[i] = EMPTY_VALUE; BufferEMA50[i] = ema50[i]; BufferEMA100[i] = ema100[i]; BufferEMA200[i] = ema200[i]; BufferEMA600[i] = ema600[i]; BufferBuyTP[i] = 0.0; BufferSellTP[i] = 0.0; // --- ACTIVE TRADE MANAGEMENT --- if(active_trade.active) { bool closed = false; if(active_trade.type == 1) { // Buy if(low[i] <= active_trade.sl) { loss_count++; closed = true; } else if(high[i] >= active_trade.tp) { win_count++; closed = true; } } else if(active_trade.type == -1) { // Sell if(high[i] >= active_trade.sl) { loss_count++; closed = true; } else if(low[i] <= active_trade.tp) { win_count++; closed = true; } } if(closed) { active_trade.active = false; active_trade.type = 0; } } // --- LOGIC BUY SIDE --- bool allow_buy = (!active_trade.active || active_trade.type != 1); int lowest_idx = -1; double min_val = DBL_MAX; for(int k=0; k<=InpLookback; k++) { if(i+k < rates_total && low[i+k] < min_val) min_val = low[i+k]; } if(low[i] == min_val) { buy_state = 1; current_base_low = low[i]; } if(buy_state == 1) { if(high[i] >= ema50[i] || high[i] >= ema100[i] || high[i] >= ema200[i]) buy_state = 2; } if(buy_state == 2) { // Strict Logic: Low < Base + Threshold AND Low > Base // Wait, if Low == Base, it touches SL immediately. Do we allow entry at base? // Usually entry is slightly above base. // Let's assume Low > Base for valid entry. if(low[i] <= current_base_low + (InpBaseThreshold * _Point) && low[i] > current_base_low) { if(allow_buy) { if(active_trade.active && active_trade.type == -1) { if(low[i] < active_trade.entry) win_count++; else loss_count++; active_trade.active = false; } BufferBuySignal[i] = low[i]; BufferBuySL[i] = current_base_low; // Visible Dot BufferBuyTP[i] = ema600[i]; if(InpDebugLogs) Print("Buy Signal @ ", i, " Price:", low[i], " SL:", current_base_low); active_trade.active = true; active_trade.type = 1; active_trade.entry = low[i]; active_trade.sl = current_base_low; active_trade.tp = ema600[i]; active_trade.time = time[i]; } buy_state = 0; } } if(buy_state > 0 && low[i] < current_base_low) { buy_state = 1; current_base_low = low[i]; } // --- LOGIC SELL SIDE --- bool allow_sell = (!active_trade.active || active_trade.type != -1); double max_val = -DBL_MAX; for(int k=0; k<=InpLookback; k++) { if(i+k < rates_total && high[i+k] > max_val) max_val = high[i+k]; } if(high[i] == max_val) { sell_state = 1; current_base_high = high[i]; } if(sell_state == 1) { if(low[i] <= ema50[i] || low[i] <= ema100[i] || low[i] <= ema200[i]) sell_state = 2; } if(sell_state == 2) { if(high[i] >= current_base_high - (InpBaseThreshold * _Point) && high[i] < current_base_high) { if(allow_sell) { if(active_trade.active && active_trade.type == 1) { if(high[i] > active_trade.entry) win_count++; else loss_count++; active_trade.active = false; } BufferSellSignal[i] = high[i]; BufferSellSL[i] = current_base_high; // Visible Dot BufferSellTP[i] = ema600[i]; if(InpDebugLogs) Print("Sell Signal @ ", i, " Price:", high[i], " SL:", current_base_high); active_trade.active = true; active_trade.type = -1; active_trade.entry = high[i]; active_trade.sl = current_base_high; active_trade.tp = ema600[i]; active_trade.time = time[i]; } sell_state = 0; } } if(sell_state > 0 && high[i] > current_base_high) { sell_state = 1; current_base_high = high[i]; } BufferBuyState[i] = buy_state; BufferSellState[i] = sell_state; } UpdateDashboard(); ChartRedraw(); return(rates_total); } void UpdateDashboard() { CreateStatLabel("Stat_Win", 30, "Win: " + IntegerToString(win_count)); CreateStatLabel("Stat_Loss", 45, "Loss: " + IntegerToString(loss_count)); double rate = 0; if(win_count + loss_count > 0) rate = (double)win_count * 100.0 / (win_count + loss_count); CreateStatLabel("Stat_Rate", 60, "Rate: " + DoubleToString(rate, 1) + "%"); } void CreateStatLabel(string name, int y, string text) { if(ObjectFind(0, name) < 0) { ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, name, OBJPROP_XDISTANCE, 20); ObjectSetInteger(0, name, OBJPROP_COLOR, clrWhite); ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9); ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); } ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); ObjectSetString(0, name, OBJPROP_TEXT, text); } void OnDeinit(const int reason) { ObjectDelete(0, "Stat_BG"); ObjectDelete(0, "Stat_Win"); ObjectDelete(0, "Stat_Loss"); ObjectDelete(0, "Stat_Rate"); }