754 lines
26 KiB
Plaintext
754 lines
26 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| Base_EMA_Reversal.mq5 |
|
|
//| Multi-Stage EMA Reversal Signal Indicator |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025"
|
|
#property link ""
|
|
#property version "1.00"
|
|
#property indicator_chart_window
|
|
|
|
// Properties
|
|
#property indicator_buffers 15
|
|
#property indicator_plots 6
|
|
|
|
// Plot configurations - Signal plots (6)
|
|
#property indicator_label1 "BUY_SIGNAL"
|
|
#property indicator_type1 DRAW_ARROW
|
|
#property indicator_color1 clrLime
|
|
#property indicator_style1 STYLE_SOLID
|
|
#property indicator_width1 2
|
|
#property indicator_label2 "SELL_SIGNAL"
|
|
#property indicator_type2 DRAW_ARROW
|
|
#property indicator_color2 clrRed
|
|
#property indicator_style2 STYLE_SOLID
|
|
#property indicator_width2 2
|
|
#property indicator_label3 "BUY_SL"
|
|
#property indicator_type3 DRAW_ARROW
|
|
#property indicator_color3 clrGray
|
|
#property indicator_style3 STYLE_SOLID
|
|
#property indicator_width3 1
|
|
#property indicator_label4 "SELL_SL"
|
|
#property indicator_type4 DRAW_ARROW
|
|
#property indicator_color4 clrGray
|
|
#property indicator_style4 STYLE_SOLID
|
|
#property indicator_width4 1
|
|
#property indicator_label5 "BUY_TP"
|
|
#property indicator_type5 DRAW_ARROW
|
|
#property indicator_color5 clrBlue
|
|
#property indicator_style5 STYLE_SOLID
|
|
#property indicator_width5 1
|
|
#property indicator_label6 "SELL_TP"
|
|
#property indicator_type6 DRAW_ARROW
|
|
#property indicator_color6 clrBlue
|
|
#property indicator_style6 STYLE_SOLID
|
|
#property indicator_width6 1
|
|
|
|
// EMA Line plots (7)
|
|
#property indicator_label7 "EMA_50"
|
|
#property indicator_type7 DRAW_LINE
|
|
#property indicator_color7 clrRed
|
|
#property indicator_style7 STYLE_SOLID
|
|
#property indicator_width7 1
|
|
#property indicator_label8 "EMA_100"
|
|
#property indicator_type8 DRAW_LINE
|
|
#property indicator_color8 clrOrange
|
|
#property indicator_style8 STYLE_SOLID
|
|
#property indicator_width8 1
|
|
#property indicator_label9 "EMA_200"
|
|
#property indicator_type9 DRAW_LINE
|
|
#property indicator_color9 clrYellow
|
|
#property indicator_style9 STYLE_SOLID
|
|
#property indicator_width9 1
|
|
#property indicator_label10 "EMA_300"
|
|
#property indicator_type10 DRAW_LINE
|
|
#property indicator_color10 clrGreen
|
|
#property indicator_style10 STYLE_SOLID
|
|
#property indicator_width10 1
|
|
#property indicator_label11 "EMA_400"
|
|
#property indicator_type11 DRAW_LINE
|
|
#property indicator_color11 clrBlue
|
|
#property indicator_style11 STYLE_SOLID
|
|
#property indicator_width11 1
|
|
#property indicator_label12 "EMA_500"
|
|
#property indicator_type12 DRAW_LINE
|
|
#property indicator_color12 clrPurple
|
|
#property indicator_style12 STYLE_SOLID
|
|
#property indicator_width12 1
|
|
#property indicator_label13 "EMA_600"
|
|
#property indicator_type13 DRAW_LINE
|
|
#property indicator_color13 clrMagenta
|
|
#property indicator_style13 STYLE_SOLID
|
|
#property indicator_width13 1
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Input Parameters |
|
|
//+------------------------------------------------------------------+
|
|
input group "=== EMA Settings ==="
|
|
input int InpEMA50_Period = 50;
|
|
input int InpEMA100_Period = 100;
|
|
input int InpEMA200_Period = 200;
|
|
input int InpEMA300_Period = 300;
|
|
input int InpEMA400_Period = 400;
|
|
input int InpEMA500_Period = 500;
|
|
input int InpEMA600_Period = 600;
|
|
|
|
input group "=== Setup Settings ==="
|
|
input int InpLookbackPeriod = 100; // Lookback bars for base detection
|
|
input double InpBaseThreshold = 50; // Pullback threshold (points)
|
|
input int InpPullbackBars = 2; // Max bars to wait for pullback
|
|
input int InpSkipBars = 50; // Bars to skip at startup
|
|
|
|
input group "=== ATR Settings ==="
|
|
input int InpATRPeriod = 14; // ATR period for TP fallback
|
|
|
|
input group "=== Display Settings ==="
|
|
input bool InpShowStageLabels = true; // Show stage labels on chart
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Indicator Buffers |
|
|
//+------------------------------------------------------------------+
|
|
double BuySignalBuffer[];
|
|
double SellSignalBuffer[];
|
|
double BuySLBuffer[];
|
|
double SellSLBuffer[];
|
|
double BuyTPBuffer[];
|
|
double SellTPBuffer[];
|
|
double BuyStateBuffer[];
|
|
double SellStateBuffer[];
|
|
|
|
// EMA Line buffers for display
|
|
double EMA50_Buffer[];
|
|
double EMA100_Buffer[];
|
|
double EMA200_Buffer[];
|
|
double EMA300_Buffer[];
|
|
double EMA400_Buffer[];
|
|
double EMA500_Buffer[];
|
|
double EMA600_Buffer[];
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| EMA Handles |
|
|
//+------------------------------------------------------------------+
|
|
int h_ema50, h_ema100, h_ema200;
|
|
int h_ema300, h_ema400, h_ema500, h_ema600;
|
|
int h_atr;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| State Tracking Variables |
|
|
//+------------------------------------------------------------------+
|
|
int buy_state = 0;
|
|
int sell_state = 0;
|
|
double BaseLow = 0;
|
|
double BaseHigh = 0;
|
|
datetime last_signal_time = 0;
|
|
|
|
// EMA touch tracking
|
|
bool buy_ema_touched = false;
|
|
bool sell_ema_touched = false;
|
|
int ema_touch_bar = -1;
|
|
int buy_touch_ema_count = 0;
|
|
int sell_touch_ema_count = 0;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| OnInit |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
// Create EMA handles
|
|
h_ema50 = iMA(_Symbol, _Period, InpEMA50_Period, 0, MODE_EMA, PRICE_CLOSE);
|
|
h_ema100 = iMA(_Symbol, _Period, InpEMA100_Period, 0, MODE_EMA, PRICE_CLOSE);
|
|
h_ema200 = iMA(_Symbol, _Period, InpEMA200_Period, 0, MODE_EMA, PRICE_CLOSE);
|
|
h_ema300 = iMA(_Symbol, _Period, InpEMA300_Period, 0, MODE_EMA, PRICE_CLOSE);
|
|
h_ema400 = iMA(_Symbol, _Period, InpEMA400_Period, 0, MODE_EMA, PRICE_CLOSE);
|
|
h_ema500 = iMA(_Symbol, _Period, InpEMA500_Period, 0, MODE_EMA, PRICE_CLOSE);
|
|
h_ema600 = iMA(_Symbol, _Period, InpEMA600_Period, 0, MODE_EMA, PRICE_CLOSE);
|
|
h_atr = iATR(_Symbol, _Period, InpATRPeriod);
|
|
|
|
// Validate handles
|
|
if(h_ema50 == INVALID_HANDLE || h_ema100 == INVALID_HANDLE || h_ema200 == INVALID_HANDLE ||
|
|
h_ema300 == INVALID_HANDLE || h_ema400 == INVALID_HANDLE || h_ema500 == INVALID_HANDLE ||
|
|
h_ema600 == INVALID_HANDLE || h_atr == INVALID_HANDLE)
|
|
{
|
|
Print("Error creating indicator handles");
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
// Set buffers
|
|
SetIndexBuffer(0, BuySignalBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(1, SellSignalBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(2, BuySLBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(3, SellSLBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(4, BuyTPBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(5, SellTPBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(6, BuyStateBuffer, INDICATOR_CALCULATIONS);
|
|
SetIndexBuffer(7, SellStateBuffer, INDICATOR_CALCULATIONS);
|
|
|
|
// EMA Line buffers
|
|
SetIndexBuffer(8, EMA50_Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(9, EMA100_Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(10, EMA200_Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(11, EMA300_Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(12, EMA400_Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(13, EMA500_Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(14, EMA600_Buffer, INDICATOR_DATA);
|
|
|
|
// Configure plots
|
|
PlotIndexSetInteger(0, PLOT_ARROW, 233); // Up arrow for buy
|
|
PlotIndexSetInteger(1, PLOT_ARROW, 234); // Down arrow for sell
|
|
PlotIndexSetInteger(2, PLOT_ARROW, 159); // Dot for SL
|
|
PlotIndexSetInteger(3, PLOT_ARROW, 159); // Dot for SL
|
|
PlotIndexSetInteger(4, PLOT_ARROW, 160); // Plus for TP
|
|
PlotIndexSetInteger(5, PLOT_ARROW, 160); // Plus for TP
|
|
|
|
// Set empty values for all plots
|
|
for(int i = 0; i < 14; i++)
|
|
{
|
|
PlotIndexSetDouble(i, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
|
}
|
|
|
|
Print("Base_EMA_Reversal initialized successfully");
|
|
Print("EMA Periods: ", InpEMA50_Period, ", ", InpEMA100_Period, ", ", InpEMA200_Period, ", ",
|
|
InpEMA300_Period, ", ", InpEMA400_Period, ", ", InpEMA500_Period, ", ", InpEMA600_Period);
|
|
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| OnDeinit |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
// Release handles
|
|
if(h_ema50 != INVALID_HANDLE) IndicatorRelease(h_ema50);
|
|
if(h_ema100 != INVALID_HANDLE) IndicatorRelease(h_ema100);
|
|
if(h_ema200 != INVALID_HANDLE) IndicatorRelease(h_ema200);
|
|
if(h_ema300 != INVALID_HANDLE) IndicatorRelease(h_ema300);
|
|
if(h_ema400 != INVALID_HANDLE) IndicatorRelease(h_ema400);
|
|
if(h_ema500 != INVALID_HANDLE) IndicatorRelease(h_ema500);
|
|
if(h_ema600 != INVALID_HANDLE) IndicatorRelease(h_ema600);
|
|
if(h_atr != INVALID_HANDLE) IndicatorRelease(h_atr);
|
|
|
|
// Delete all stage label objects
|
|
ObjectsDeleteAll(0, "EMA_Stage_");
|
|
|
|
Print("Base_EMA_Reversal deinitialized");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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[])
|
|
{
|
|
// Set arrays as series (index 0 = current bar)
|
|
ArraySetAsSeries(time, true);
|
|
ArraySetAsSeries(open, true);
|
|
ArraySetAsSeries(high, true);
|
|
ArraySetAsSeries(low, true);
|
|
ArraySetAsSeries(close, true);
|
|
ArraySetAsSeries(BuySignalBuffer, true);
|
|
ArraySetAsSeries(SellSignalBuffer, true);
|
|
ArraySetAsSeries(BuySLBuffer, true);
|
|
ArraySetAsSeries(SellSLBuffer, true);
|
|
ArraySetAsSeries(BuyTPBuffer, true);
|
|
ArraySetAsSeries(SellTPBuffer, true);
|
|
ArraySetAsSeries(BuyStateBuffer, true);
|
|
ArraySetAsSeries(SellStateBuffer, true);
|
|
|
|
// Check minimum data requirement
|
|
if(rates_total < InpSkipBars + InpLookbackPeriod + InpEMA600_Period)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
// Copy EMA data
|
|
double ema50[], ema100[], ema200[];
|
|
double ema300[], ema400[], ema500[], ema600[];
|
|
double atr[];
|
|
|
|
ArraySetAsSeries(ema50, true);
|
|
ArraySetAsSeries(ema100, true);
|
|
ArraySetAsSeries(ema200, true);
|
|
ArraySetAsSeries(ema300, true);
|
|
ArraySetAsSeries(ema400, true);
|
|
ArraySetAsSeries(ema500, true);
|
|
ArraySetAsSeries(ema600, true);
|
|
ArraySetAsSeries(atr, true);
|
|
|
|
ArraySetAsSeries(EMA50_Buffer, true);
|
|
ArraySetAsSeries(EMA100_Buffer, true);
|
|
ArraySetAsSeries(EMA200_Buffer, true);
|
|
ArraySetAsSeries(EMA300_Buffer, true);
|
|
ArraySetAsSeries(EMA400_Buffer, true);
|
|
ArraySetAsSeries(EMA500_Buffer, true);
|
|
ArraySetAsSeries(EMA600_Buffer, true);
|
|
|
|
if(CopyBuffer(h_ema50, 0, 0, rates_total, ema50) <= 0) return(0);
|
|
if(CopyBuffer(h_ema100, 0, 0, rates_total, ema100) <= 0) return(0);
|
|
if(CopyBuffer(h_ema200, 0, 0, rates_total, ema200) <= 0) return(0);
|
|
if(CopyBuffer(h_ema300, 0, 0, rates_total, ema300) <= 0) return(0);
|
|
if(CopyBuffer(h_ema400, 0, 0, rates_total, ema400) <= 0) return(0);
|
|
if(CopyBuffer(h_ema500, 0, 0, rates_total, ema500) <= 0) return(0);
|
|
if(CopyBuffer(h_ema600, 0, 0, rates_total, ema600) <= 0) return(0);
|
|
if(CopyBuffer(h_atr, 0, 0, rates_total, atr) <= 0) return(0);
|
|
|
|
// Copy EMA values to display buffers
|
|
if(CopyBuffer(h_ema50, 0, 0, rates_total, EMA50_Buffer) <= 0) return(0);
|
|
if(CopyBuffer(h_ema100, 0, 0, rates_total, EMA100_Buffer) <= 0) return(0);
|
|
if(CopyBuffer(h_ema200, 0, 0, rates_total, EMA200_Buffer) <= 0) return(0);
|
|
if(CopyBuffer(h_ema300, 0, 0, rates_total, EMA300_Buffer) <= 0) return(0);
|
|
if(CopyBuffer(h_ema400, 0, 0, rates_total, EMA400_Buffer) <= 0) return(0);
|
|
if(CopyBuffer(h_ema500, 0, 0, rates_total, EMA500_Buffer) <= 0) return(0);
|
|
if(CopyBuffer(h_ema600, 0, 0, rates_total, EMA600_Buffer) <= 0) return(0);
|
|
|
|
// Calculate start position
|
|
int bars_to_process;
|
|
int start_bar;
|
|
|
|
if(prev_calculated == 0)
|
|
{
|
|
// First calculation - initialize base levels
|
|
bars_to_process = rates_total - InpSkipBars - 1;
|
|
start_bar = InpSkipBars;
|
|
|
|
// Initialize BaseLow/BaseHigh from lookback period
|
|
int lookback_start = InpSkipBars + InpLookbackPeriod;
|
|
int lookback_end = InpSkipBars + 1;
|
|
|
|
BaseLow = low[lookback_start];
|
|
BaseHigh = high[lookback_start];
|
|
|
|
for(int i = lookback_start; i >= lookback_end; i--)
|
|
{
|
|
if(low[i] < BaseLow) BaseLow = low[i];
|
|
if(high[i] > BaseHigh) BaseHigh = high[i];
|
|
}
|
|
|
|
// Initialize states
|
|
buy_state = 1;
|
|
sell_state = 1;
|
|
buy_ema_touched = false;
|
|
sell_ema_touched = false;
|
|
ema_touch_bar = -1;
|
|
|
|
Print("Initialization: BaseLow = ", BaseLow, ", BaseHigh = ", BaseHigh);
|
|
|
|
// Clear buffers
|
|
ArrayInitialize(BuySignalBuffer, EMPTY_VALUE);
|
|
ArrayInitialize(SellSignalBuffer, EMPTY_VALUE);
|
|
ArrayInitialize(BuySLBuffer, EMPTY_VALUE);
|
|
ArrayInitialize(SellSLBuffer, EMPTY_VALUE);
|
|
ArrayInitialize(BuyTPBuffer, EMPTY_VALUE);
|
|
ArrayInitialize(SellTPBuffer, EMPTY_VALUE);
|
|
ArrayInitialize(BuyStateBuffer, 0);
|
|
ArrayInitialize(SellStateBuffer, 0);
|
|
ArrayInitialize(EMA50_Buffer, EMPTY_VALUE);
|
|
ArrayInitialize(EMA100_Buffer, EMPTY_VALUE);
|
|
ArrayInitialize(EMA200_Buffer, EMPTY_VALUE);
|
|
ArrayInitialize(EMA300_Buffer, EMPTY_VALUE);
|
|
ArrayInitialize(EMA400_Buffer, EMPTY_VALUE);
|
|
ArrayInitialize(EMA500_Buffer, EMPTY_VALUE);
|
|
ArrayInitialize(EMA600_Buffer, EMPTY_VALUE);
|
|
}
|
|
else if(rates_total > prev_calculated)
|
|
{
|
|
// New bars added - process only new bars
|
|
bars_to_process = rates_total - prev_calculated;
|
|
start_bar = 0;
|
|
}
|
|
else
|
|
{
|
|
// Recalculate last bar only
|
|
bars_to_process = 1;
|
|
start_bar = 0;
|
|
}
|
|
|
|
// Process bars
|
|
for(int i = start_bar; i < start_bar + bars_to_process && i < rates_total - InpSkipBars; i++)
|
|
{
|
|
// Skip initialization area
|
|
if(i >= rates_total - InpSkipBars) continue;
|
|
|
|
// Default buffer values (EMPTY_VALUE for all signal/SL/TP buffers)
|
|
BuySignalBuffer[i] = EMPTY_VALUE;
|
|
SellSignalBuffer[i] = EMPTY_VALUE;
|
|
BuySLBuffer[i] = EMPTY_VALUE;
|
|
SellSLBuffer[i] = EMPTY_VALUE;
|
|
BuyTPBuffer[i] = EMPTY_VALUE;
|
|
SellTPBuffer[i] = EMPTY_VALUE;
|
|
|
|
// Copy EMA values to display buffers
|
|
EMA50_Buffer[i] = ema50[i];
|
|
EMA100_Buffer[i] = ema100[i];
|
|
EMA200_Buffer[i] = ema200[i];
|
|
EMA300_Buffer[i] = ema300[i];
|
|
EMA400_Buffer[i] = ema400[i];
|
|
EMA500_Buffer[i] = ema500[i];
|
|
EMA600_Buffer[i] = ema600[i];
|
|
|
|
// Update state buffers
|
|
BuyStateBuffer[i] = buy_state;
|
|
SellStateBuffer[i] = sell_state;
|
|
|
|
// State 1: Finding Base (continuous tracking)
|
|
if(buy_state == 1 || buy_state == 0)
|
|
{
|
|
if(low[i] < BaseLow)
|
|
{
|
|
BaseLow = low[i];
|
|
}
|
|
buy_state = 1;
|
|
}
|
|
|
|
if(sell_state == 1 || sell_state == 0)
|
|
{
|
|
if(high[i] > BaseHigh)
|
|
{
|
|
BaseHigh = high[i];
|
|
}
|
|
sell_state = 1;
|
|
}
|
|
|
|
// Check for EMA touch (State 1 -> State 2)
|
|
// BUY SIDE: Price touches EMA from below (close below -> close above)
|
|
if(buy_state == 1 && !buy_ema_touched)
|
|
{
|
|
// Find lowest EMA (most resistance from below)
|
|
double lowest_ema = ema50[i];
|
|
if(ema100[i] < lowest_ema) lowest_ema = ema100[i];
|
|
if(ema200[i] < lowest_ema) lowest_ema = ema200[i];
|
|
|
|
// Previous bar close
|
|
double prev_close = (i < rates_total - 1) ? close[i + 1] : close[i];
|
|
|
|
// EMA touch from below (previous close at/below, current close above)
|
|
if(prev_close <= lowest_ema && close[i] > lowest_ema)
|
|
{
|
|
buy_ema_touched = true;
|
|
ema_touch_bar = i;
|
|
|
|
// Count EMAs crossed
|
|
buy_touch_ema_count = 0;
|
|
if(close[i] > ema50[i]) buy_touch_ema_count++;
|
|
if(close[i] > ema100[i]) buy_touch_ema_count++;
|
|
if(close[i] > ema200[i]) buy_touch_ema_count++;
|
|
|
|
buy_state = 2;
|
|
BuyStateBuffer[i] = 2;
|
|
}
|
|
}
|
|
|
|
// SELL SIDE: Price touches EMA from above (close above -> close below)
|
|
if(sell_state == 1 && !sell_ema_touched)
|
|
{
|
|
// Find highest EMA (most support from above)
|
|
double highest_ema = ema50[i];
|
|
if(ema100[i] > highest_ema) highest_ema = ema100[i];
|
|
if(ema200[i] > highest_ema) highest_ema = ema200[i];
|
|
|
|
// Previous bar close
|
|
double prev_close = (i < rates_total - 1) ? close[i + 1] : close[i];
|
|
|
|
// EMA touch from above (previous close at/above, current close below)
|
|
if(prev_close >= highest_ema && close[i] < highest_ema)
|
|
{
|
|
sell_ema_touched = true;
|
|
ema_touch_bar = i;
|
|
|
|
// Count EMAs crossed
|
|
sell_touch_ema_count = 0;
|
|
if(close[i] < ema50[i]) sell_touch_ema_count++;
|
|
if(close[i] < ema100[i]) sell_touch_ema_count++;
|
|
if(close[i] < ema200[i]) sell_touch_ema_count++;
|
|
|
|
sell_state = 2;
|
|
SellStateBuffer[i] = 2;
|
|
}
|
|
}
|
|
|
|
// State 3: Decision (within pullback window)
|
|
// BUY SIDE DECISION
|
|
if(buy_ema_touched)
|
|
{
|
|
int bars_since_touch = ema_touch_bar - i;
|
|
|
|
// Timeout check - if more than pullback bars passed, cancel
|
|
if(bars_since_touch > InpPullbackBars)
|
|
{
|
|
buy_ema_touched = false;
|
|
buy_state = 1;
|
|
BuyStateBuffer[i] = 1;
|
|
continue;
|
|
}
|
|
|
|
// Cancel condition: crossed EMA below again
|
|
double lowest_ema_current = ema50[i];
|
|
if(ema100[i] < lowest_ema_current) lowest_ema_current = ema100[i];
|
|
if(ema200[i] < lowest_ema_current) lowest_ema_current = ema200[i];
|
|
|
|
if(close[i] < lowest_ema_current)
|
|
{
|
|
buy_ema_touched = false;
|
|
buy_state = 1;
|
|
BuyStateBuffer[i] = 1;
|
|
continue;
|
|
}
|
|
|
|
// Pullback check
|
|
double pullback_distance = MathAbs(low[i] - BaseLow) / _Point;
|
|
|
|
if(pullback_distance <= InpBaseThreshold)
|
|
{
|
|
// GENERATE BUY SIGNAL
|
|
BuySignalBuffer[i] = close[i];
|
|
BuySLBuffer[i] = BaseLow;
|
|
BuyTPBuffer[i] = CalculateBuyTP(close[i], ema300[i], ema400[i], ema500[i], ema600[i], atr[i]);
|
|
BuyStateBuffer[i] = 3;
|
|
|
|
// Create signal label
|
|
if(InpShowStageLabels)
|
|
{
|
|
UpdateStageLabels(time[i], 3, sell_state, low[i] - 50 * _Point, i);
|
|
}
|
|
|
|
// Reset
|
|
buy_ema_touched = false;
|
|
buy_state = 1;
|
|
last_signal_time = time[i];
|
|
|
|
// Recalculate BaseLow from bar before signal
|
|
RecalculateBaseBuy(i, rates_total, low);
|
|
|
|
Print("BUY SIGNAL at ", TimeToString(time[i]), " Price: ", close[i], " SL: ", BaseLow,
|
|
" TP: ", BuyTPBuffer[i]);
|
|
}
|
|
}
|
|
|
|
// SELL SIDE DECISION
|
|
if(sell_ema_touched)
|
|
{
|
|
int bars_since_touch = ema_touch_bar - i;
|
|
|
|
// Timeout check - if more than pullback bars passed, cancel
|
|
if(bars_since_touch > InpPullbackBars)
|
|
{
|
|
sell_ema_touched = false;
|
|
sell_state = 1;
|
|
SellStateBuffer[i] = 1;
|
|
continue;
|
|
}
|
|
|
|
// Cancel condition: crossed EMA above again
|
|
double highest_ema_current = ema50[i];
|
|
if(ema100[i] > highest_ema_current) highest_ema_current = ema100[i];
|
|
if(ema200[i] > highest_ema_current) highest_ema_current = ema200[i];
|
|
|
|
if(close[i] > highest_ema_current)
|
|
{
|
|
sell_ema_touched = false;
|
|
sell_state = 1;
|
|
SellStateBuffer[i] = 1;
|
|
continue;
|
|
}
|
|
|
|
// Pullback check
|
|
double pullback_distance = MathAbs(high[i] - BaseHigh) / _Point;
|
|
|
|
if(pullback_distance <= InpBaseThreshold)
|
|
{
|
|
// GENERATE SELL SIGNAL
|
|
SellSignalBuffer[i] = close[i];
|
|
SellSLBuffer[i] = BaseHigh;
|
|
SellTPBuffer[i] = CalculateSellTP(close[i], ema300[i], ema400[i], ema500[i], ema600[i], atr[i]);
|
|
SellStateBuffer[i] = 3;
|
|
|
|
// Create signal label
|
|
if(InpShowStageLabels)
|
|
{
|
|
UpdateStageLabels(time[i], 3, sell_state, high[i] + 50 * _Point, i);
|
|
}
|
|
|
|
// Reset
|
|
sell_ema_touched = false;
|
|
sell_state = 1;
|
|
last_signal_time = time[i];
|
|
|
|
// Recalculate BaseHigh from bar before signal
|
|
RecalculateBaseSell(i, rates_total, high);
|
|
|
|
Print("SELL SIGNAL at ", TimeToString(time[i]), " Price: ", close[i], " SL: ", BaseHigh,
|
|
" TP: ", SellTPBuffer[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(rates_total);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate Buy Take Profit |
|
|
//+------------------------------------------------------------------+
|
|
double CalculateBuyTP(double entry_price, double ema300, double ema400,
|
|
double ema500, double ema600, double atr)
|
|
{
|
|
double tp = 0;
|
|
|
|
// Find lowest valid EMA ABOVE price (closest = best risk/reward)
|
|
if(ema300 > entry_price)
|
|
{
|
|
if(tp == 0 || ema300 < tp) tp = ema300;
|
|
}
|
|
if(ema400 > entry_price)
|
|
{
|
|
if(tp == 0 || ema400 < tp) tp = ema400;
|
|
}
|
|
if(ema500 > entry_price)
|
|
{
|
|
if(tp == 0 || ema500 < tp) tp = ema500;
|
|
}
|
|
if(ema600 > entry_price)
|
|
{
|
|
if(tp == 0 || ema600 < tp) tp = ema600;
|
|
}
|
|
|
|
// ATR fallback: if no valid EMA above price
|
|
if(tp == 0 && atr > 0)
|
|
{
|
|
tp = entry_price + atr * 2;
|
|
}
|
|
|
|
return NormalizeDouble(tp, _Digits);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate Sell Take Profit |
|
|
//+------------------------------------------------------------------+
|
|
double CalculateSellTP(double entry_price, double ema300, double ema400,
|
|
double ema500, double ema600, double atr)
|
|
{
|
|
double tp = 0;
|
|
|
|
// Find highest valid EMA BELOW price (closest = best risk/reward)
|
|
if(ema300 < entry_price)
|
|
{
|
|
if(tp == 0 || ema300 > tp) tp = ema300;
|
|
}
|
|
if(ema400 < entry_price)
|
|
{
|
|
if(tp == 0 || ema400 > tp) tp = ema400;
|
|
}
|
|
if(ema500 < entry_price)
|
|
{
|
|
if(tp == 0 || ema500 > tp) tp = ema500;
|
|
}
|
|
if(ema600 < entry_price)
|
|
{
|
|
if(tp == 0 || ema600 > tp) tp = ema600;
|
|
}
|
|
|
|
// ATR fallback: if no valid EMA below price
|
|
if(tp == 0 && atr > 0)
|
|
{
|
|
tp = entry_price - atr * 2;
|
|
}
|
|
|
|
return NormalizeDouble(tp, _Digits);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Recalculate Buy Base Level After Signal |
|
|
//+------------------------------------------------------------------+
|
|
void RecalculateBaseBuy(int signal_bar, int total_bars, const double &low_arr[])
|
|
{
|
|
// Start from bar before signal
|
|
BaseLow = low_arr[signal_bar + 1];
|
|
|
|
// Find new base from that point forward
|
|
for(int j = signal_bar + 1; j < total_bars - InpSkipBars; j++)
|
|
{
|
|
if(low_arr[j] < BaseLow)
|
|
{
|
|
BaseLow = low_arr[j];
|
|
}
|
|
}
|
|
|
|
// Print("BaseLow recalculated: ", BaseLow, " after buy signal");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Recalculate Sell Base Level After Signal |
|
|
//+------------------------------------------------------------------+
|
|
void RecalculateBaseSell(int signal_bar, int total_bars, const double &high_arr[])
|
|
{
|
|
// Start from bar before signal
|
|
BaseHigh = high_arr[signal_bar + 1];
|
|
|
|
// Find new base from that point forward
|
|
for(int j = signal_bar + 1; j < total_bars - InpSkipBars; j++)
|
|
{
|
|
if(high_arr[j] > BaseHigh)
|
|
{
|
|
BaseHigh = high_arr[j];
|
|
}
|
|
}
|
|
|
|
// Print("BaseHigh recalculated: ", BaseHigh, " after sell signal");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update Stage Labels |
|
|
//+------------------------------------------------------------------+
|
|
void UpdateStageLabels(datetime label_time, int buy_st, int sell_st, double price, int bar_index)
|
|
{
|
|
string label_name = "EMA_Stage_" + IntegerToString(bar_index);
|
|
|
|
string text = "";
|
|
color label_color;
|
|
|
|
// Format: "BUY/SELL" for signal bars
|
|
if(buy_st == 3)
|
|
{
|
|
text = "BUY SIGNAL";
|
|
label_color = clrLime;
|
|
}
|
|
else if(sell_st == 3)
|
|
{
|
|
text = "SELL SIGNAL";
|
|
label_color = clrRed;
|
|
}
|
|
else
|
|
{
|
|
// Show state only when not in signal
|
|
text = StringFormat("B:%d S:%d", buy_st, sell_st);
|
|
label_color = clrGray;
|
|
switch(buy_st)
|
|
{
|
|
case 0: label_color = clrGray; break;
|
|
case 1: label_color = clrBlue; break;
|
|
case 2: label_color = clrOrange; break;
|
|
case 3: label_color = clrGreen; break;
|
|
default: label_color = clrGray;
|
|
}
|
|
}
|
|
|
|
// Create or update label
|
|
if(ObjectFind(0, label_name) < 0)
|
|
{
|
|
ObjectCreate(0, label_name, OBJ_TEXT, 0, label_time, price);
|
|
}
|
|
|
|
ObjectSetString(0, label_name, OBJPROP_TEXT, text);
|
|
ObjectSetInteger(0, label_name, OBJPROP_COLOR, label_color);
|
|
ObjectSetInteger(0, label_name, OBJPROP_FONTSIZE, 14);
|
|
ObjectSetInteger(0, label_name, OBJPROP_ANCHOR, ANCHOR_UPPER);
|
|
ObjectSetInteger(0, label_name, OBJPROP_SELECTABLE, false);
|
|
}
|
|
//+------------------------------------------------------------------+
|