feat: Implement Base EMA Reversal V2 MQL5 indicator with multi-stage signals and trade management.
This commit is contained in:
405
EMA Indi/Base_EMA_Reversal_V2.mq5
Normal file
405
EMA Indi/Base_EMA_Reversal_V2.mq5
Normal file
@@ -0,0 +1,405 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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");
|
||||
}
|
||||
Reference in New Issue
Block a user