Files
EA/EMA Indi/Base_EMA_Reversal_V2.mq5

406 lines
14 KiB
Plaintext

//+------------------------------------------------------------------+
//| 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");
}