198 lines
6.9 KiB
Plaintext
198 lines
6.9 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| RSIDivergence.mq5|
|
|
//| Custom RSI Divergence Detection Indicator |
|
|
//+------------------------------------------------------------------+
|
|
#property strict
|
|
#property indicator_chart_window
|
|
#property indicator_buffers 2
|
|
#property indicator_plots 0
|
|
|
|
//--- Input parameters
|
|
input int RSI_Period = 14; // RSI Period
|
|
input double Deviation = 0.0001; // Minimum price move to consider a swing
|
|
input int MaxBarsBack = 300; // Number of bars to analyze
|
|
|
|
//--- Global handles
|
|
double rsiBuffer[];
|
|
int rsiHandle;
|
|
|
|
//--- For divergence detection
|
|
struct SwingPoint
|
|
{
|
|
int index;
|
|
double price;
|
|
double rsi;
|
|
};
|
|
|
|
SwingPoint priceSwings[];
|
|
SwingPoint rsiSwings[];
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
SetIndexBuffer(0, rsiBuffer, INDICATOR_DATA);
|
|
rsiHandle = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE);
|
|
if (rsiHandle == INVALID_HANDLE)
|
|
{
|
|
Print("Could not create RSI handle");
|
|
return (INIT_FAILED);
|
|
}
|
|
return (INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Main indicator iteration function |
|
|
//+------------------------------------------------------------------+
|
|
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[])
|
|
{
|
|
//--- Get RSI values
|
|
ArraySetAsSeries(rsiBuffer, true);
|
|
CopyBuffer(rsiHandle, 0, 0, MaxBarsBack, rsiBuffer);
|
|
|
|
//--- Detect swings
|
|
DetectSwingPoints(close, priceSwings);
|
|
DetectSwingPoints(rsiBuffer, rsiSwings);
|
|
|
|
//--- Find and draw divergences
|
|
FindAndDrawDivergences(time, close);
|
|
|
|
return (rates_total);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Detect swing highs/lows |
|
|
//+------------------------------------------------------------------+
|
|
void DetectSwingPoints(const double &values[], SwingPoint &swings[])
|
|
{
|
|
ArrayResize(swings, 0); // Clear previous swings
|
|
int count = 0;
|
|
|
|
int size = ArraySize(values);
|
|
if (size < 3)
|
|
return; // Not enough data
|
|
|
|
// Pre-allocate a safe amount of space
|
|
ArrayResize(swings, size / 2); // Max possible swings ~50% of total bars
|
|
|
|
for (int i = 2; i < size - 2; i++)
|
|
{
|
|
// Bounds check
|
|
if (i - 1 < 0 || i + 1 >= size)
|
|
continue;
|
|
|
|
// Check for swing high
|
|
if (values[i] > values[i - 1] && values[i] > values[i + 1])
|
|
{
|
|
// Ensure we have space in the swings array
|
|
if (count >= ArraySize(swings))
|
|
ArrayResize(swings, ArraySize(swings) + 10);
|
|
|
|
swings[count].index = i;
|
|
swings[count].price = values[i];
|
|
count++;
|
|
}
|
|
// Check for swing low
|
|
else if (values[i] < values[i - 1] && values[i] < values[i + 1])
|
|
{
|
|
// Ensure we have space in the swings array
|
|
if (count >= ArraySize(swings))
|
|
ArrayResize(swings, ArraySize(swings) + 10);
|
|
|
|
swings[count].index = i;
|
|
swings[count].price = values[i];
|
|
count++;
|
|
}
|
|
}
|
|
|
|
// Final resize to actual number of swings found
|
|
ArrayResize(swings, count);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Find and draw divergence |
|
|
//+------------------------------------------------------------------+
|
|
void FindAndDrawDivergences(const datetime &time[], const double &close[])
|
|
{
|
|
static int lastAlertBar = 0;
|
|
int limit = MathMin(MaxBarsBack - 2, Bars(_Symbol, _Period));
|
|
|
|
for (int i = 0; i < ArraySize(priceSwings) - 1; i++)
|
|
{
|
|
for (int j = 0; j < ArraySize(rsiSwings) - 1; j++)
|
|
{
|
|
int p1 = priceSwings[i].index;
|
|
int p2 = priceSwings[i + 1].index;
|
|
int r1 = rsiSwings[j].index;
|
|
int r2 = rsiSwings[j + 1].index;
|
|
|
|
if (MathAbs(p1 - r1) < 3 && MathAbs(p2 - r2) < 3)
|
|
{
|
|
double price1 = close[p1];
|
|
double price2 = close[p2];
|
|
double rsi1 = rsiBuffer[r1];
|
|
double rsi2 = rsiBuffer[r2];
|
|
|
|
// Bullish Divergence
|
|
if (price2 < price1 && rsi2 > rsi1)
|
|
{
|
|
string name;
|
|
StringConcatenate(name, "BullishDiv", IntegerToString(p2));
|
|
DrawArrow(name, time[p2], close[p2], clrLime);
|
|
// DrawLine("BullishLineP" + IntegerToString(p2), time[p2], close[p2], time[p1], close[p1], clrLime);
|
|
// DrawLine("BullishLineR" + IntegerToString(p2), time[p2], rsi2, time[p1], rsi1, clrLime);
|
|
}
|
|
|
|
// Bearish Divergence
|
|
if (price2 > price1 && rsi2 < rsi1)
|
|
{
|
|
string name;
|
|
StringConcatenate(name, "BearishDiv", IntegerToString(p2));
|
|
DrawArrow(name, time[p2], close[p2], clrRed);
|
|
// DrawLine("BearishLineP" + IntegerToString(p2), time[p2], close[p2], time[p1], close[p1], clrRed);
|
|
// DrawLine("BearishLineR" + IntegerToString(p2), time[p2], rsi2, time[p1], rsi1, clrRed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Draw arrow on chart |
|
|
//+------------------------------------------------------------------+
|
|
void DrawArrow(string name, datetime time, double price, color col)
|
|
{
|
|
ObjectCreate(0, name, OBJ_ARROW, 0, time, price);
|
|
ObjectSetInteger(0, name, OBJPROP_ARROWCODE, 233); // Up arrow
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, col);
|
|
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Draw line |
|
|
//+------------------------------------------------------------------+
|
|
void DrawLine(string name, datetime t1, double p1, datetime t2, double p2, color col)
|
|
{
|
|
// Delete existing object if any
|
|
ObjectDelete(0, name);
|
|
|
|
// Create trendline (use 1 for OBJ_TRENDLINE if undefined)
|
|
ObjectCreate(0, name, 1, 0, t1, p1, t2, p2);
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, col);
|
|
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DASHDOT);
|
|
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
|
|
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
|
}
|
|
|
|
// |