259 行
9.1 KiB
MQL5
259 行
9.1 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Reverse_RSI_Bands.mq5 |
|
|
//| Copyright 2026, Ondeb 0
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2026, Ondeb"
|
|
#property version "1.0"
|
|
#property description "Exact mathematical Reverse RSI bands"
|
|
#property indicator_chart_window
|
|
#property indicator_buffers 7
|
|
#property indicator_plots 2
|
|
|
|
//--- plot Overbought Price Level
|
|
#property indicator_label1 "RSI OB Band"
|
|
#property indicator_type1 DRAW_LINE
|
|
#property indicator_color1 clrCrimson
|
|
#property indicator_style1 STYLE_SOLID
|
|
#property indicator_width1 2
|
|
|
|
//--- plot Oversold Price Level
|
|
#property indicator_label2 "RSI OS Band"
|
|
#property indicator_type2 DRAW_LINE
|
|
#property indicator_color2 clrMediumSeaGreen
|
|
#property indicator_style2 STYLE_SOLID
|
|
#property indicator_width2 2
|
|
|
|
//--- input parameters
|
|
input uint InpPeriodRSI = 14; // RSI Period
|
|
input double InpObLevel = 70.0; // Overbought target RSI
|
|
input double InpOsLevel = 30.0; // Oversold target RSI
|
|
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied Price
|
|
|
|
//--- indicator buffers
|
|
double BufferOB[]; // Plot 1: Price level for OB
|
|
double BufferOS[]; // Plot 2: Price level for OS
|
|
|
|
double BufferPrice[]; // Internal: Source prices
|
|
double BufferUP[]; // Internal: Up changes
|
|
double BufferDN[]; // Internal: Down changes
|
|
double BufferAvgU[]; // Internal: Wilder Avg Gain
|
|
double BufferAvgD[]; // Internal: Wilder Avg Loss
|
|
|
|
//--- constants
|
|
const double MAX_RSI_VALUE = 100.0;
|
|
const double MAX_RSI_LIMIT = 99.9;
|
|
const double MIN_RSI_LIMIT = 0.1;
|
|
const double NEUTRAL_RSI = 50.0;
|
|
|
|
//--- global variables
|
|
int period_rsi;
|
|
double ob_level;
|
|
double os_level;
|
|
int handle_price;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
//--- sanitize inputs
|
|
period_rsi = (int)(InpPeriodRSI < 2 ? 2 : InpPeriodRSI);
|
|
ob_level = InpObLevel;
|
|
if(ob_level >= MAX_RSI_VALUE) ob_level = MAX_RSI_LIMIT;
|
|
if(ob_level <= 0.0) ob_level = MIN_RSI_LIMIT;
|
|
|
|
os_level = InpOsLevel;
|
|
if(os_level >= MAX_RSI_VALUE) os_level = MAX_RSI_LIMIT;
|
|
if(os_level <= 0.0) os_level = MIN_RSI_LIMIT;
|
|
|
|
//--- indicator buffers mapping
|
|
SetIndexBuffer(0, BufferOB, INDICATOR_DATA);
|
|
SetIndexBuffer(1, BufferOS, INDICATOR_DATA);
|
|
SetIndexBuffer(2, BufferPrice, INDICATOR_CALCULATIONS);
|
|
SetIndexBuffer(3, BufferUP, INDICATOR_CALCULATIONS);
|
|
SetIndexBuffer(4, BufferDN, INDICATOR_CALCULATIONS);
|
|
SetIndexBuffer(5, BufferAvgU, INDICATOR_CALCULATIONS);
|
|
SetIndexBuffer(6, BufferAvgD, INDICATOR_CALCULATIONS);
|
|
|
|
//--- set indicator parameters
|
|
IndicatorSetString(INDICATOR_SHORTNAME, "Reverse RSI Bands (" + (string)period_rsi + ", " + DoubleToString(ob_level, 1) + "/" + DoubleToString(os_level, 1) + ")");
|
|
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
|
|
|
|
//--- set drawing offset
|
|
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, period_rsi);
|
|
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, period_rsi);
|
|
|
|
//--- arrays are treated as standard arrays (index 0 is oldest, rates_total-1 is latest)
|
|
ArraySetAsSeries(BufferOB, false);
|
|
ArraySetAsSeries(BufferOS, false);
|
|
ArraySetAsSeries(BufferPrice, false);
|
|
ArraySetAsSeries(BufferUP, false);
|
|
ArraySetAsSeries(BufferDN, false);
|
|
ArraySetAsSeries(BufferAvgU, false);
|
|
ArraySetAsSeries(BufferAvgD, false);
|
|
|
|
//--- create helper MA handle to easily copy applied price
|
|
ResetLastError();
|
|
handle_price = iMA(NULL, PERIOD_CURRENT, 1, 0, MODE_SMA, InpAppliedPrice);
|
|
if(handle_price == INVALID_HANDLE)
|
|
{
|
|
Print("Failed to create price iMA handle: Error ", GetLastError());
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom 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[])
|
|
{
|
|
//--- check minimum bars
|
|
if(rates_total < period_rsi + 2)
|
|
return(0);
|
|
|
|
//--- copy applied price values
|
|
int copied = CopyBuffer(handle_price, 0, 0, rates_total, BufferPrice);
|
|
if(copied != rates_total)
|
|
{
|
|
Print("Error copying price data: ", GetLastError());
|
|
return(0);
|
|
}
|
|
|
|
//--- determine starting index
|
|
int start = prev_calculated;
|
|
if(start <= 0)
|
|
{
|
|
start = 1; // start from index 1 because we need BufferPrice[i] - BufferPrice[i-1]
|
|
// Initialize arrays
|
|
ArrayInitialize(BufferOB, EMPTY_VALUE);
|
|
ArrayInitialize(BufferOS, EMPTY_VALUE);
|
|
ArrayInitialize(BufferUP, 0.0);
|
|
ArrayInitialize(BufferDN, 0.0);
|
|
ArrayInitialize(BufferAvgU, 0.0);
|
|
ArrayInitialize(BufferAvgD, 0.0);
|
|
}
|
|
else
|
|
{
|
|
// Recalculate last bar to update live tick calculations
|
|
start = prev_calculated - 1;
|
|
if(start < 1) start = 1;
|
|
}
|
|
|
|
//--- Calculate raw gains and losses (UP and DN)
|
|
CalculateRawGains(start, rates_total);
|
|
|
|
//--- Calculate Wilder Smoothing for AvgU and AvgD
|
|
CalculateWilderSmoothing(start, rates_total);
|
|
|
|
//--- Calculate exact Reverse RSI Prices for OB and OS levels
|
|
CalculateReversePrices(start, rates_total);
|
|
|
|
return(rates_total);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate raw positive and negative price changes |
|
|
//+------------------------------------------------------------------+
|
|
void CalculateRawGains(const int start, const int rates_total)
|
|
{
|
|
for(int i = start; i < rates_total && !IsStopped(); i++)
|
|
{
|
|
double diff = BufferPrice[i] - BufferPrice[i-1];
|
|
BufferUP[i] = (diff > 0.0) ? diff : 0.0;
|
|
BufferDN[i] = (diff < 0.0) ? -diff : 0.0;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate Wilder Smoothing averages |
|
|
//+------------------------------------------------------------------+
|
|
void CalculateWilderSmoothing(const int start, const int rates_total)
|
|
{
|
|
double alpha = 1.0 / (double)period_rsi;
|
|
int first_valid_index = period_rsi;
|
|
|
|
for(int i = start; i < rates_total && !IsStopped(); i++)
|
|
{
|
|
if(i < first_valid_index)
|
|
{
|
|
BufferAvgU[i] = 0.0;
|
|
BufferAvgD[i] = 0.0;
|
|
BufferOB[i] = EMPTY_VALUE;
|
|
BufferOS[i] = EMPTY_VALUE;
|
|
continue;
|
|
}
|
|
|
|
//--- Seed initialization: Simple Moving Average on the first window
|
|
if(i == first_valid_index)
|
|
{
|
|
double sumU = 0.0;
|
|
double sumD = 0.0;
|
|
for(int j = 1; j <= period_rsi; j++)
|
|
{
|
|
sumU += BufferUP[j];
|
|
sumD += BufferDN[j];
|
|
}
|
|
BufferAvgU[i] = sumU / (double)period_rsi;
|
|
BufferAvgD[i] = sumD / (double)period_rsi;
|
|
}
|
|
else
|
|
{
|
|
//--- Wilder Smoothing recursive formula
|
|
BufferAvgU[i] = BufferAvgU[i-1] * (1.0 - alpha) + BufferUP[i] * alpha;
|
|
BufferAvgD[i] = BufferAvgD[i-1] * (1.0 - alpha) + BufferDN[i] * alpha;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate exact Reverse RSI target prices |
|
|
//+------------------------------------------------------------------+
|
|
void CalculateReversePrices(const int start, const int rates_total)
|
|
{
|
|
int first_valid_index = period_rsi;
|
|
for(int i = start; i < rates_total && !IsStopped(); i++)
|
|
{
|
|
if(i <= first_valid_index)
|
|
{
|
|
BufferOB[i] = EMPTY_VALUE;
|
|
BufferOS[i] = EMPTY_VALUE;
|
|
continue;
|
|
}
|
|
|
|
//--- Prior state
|
|
double prev_avg_u = BufferAvgU[i-1];
|
|
double prev_avg_d = BufferAvgD[i-1];
|
|
double prev_price = BufferPrice[i-1];
|
|
|
|
double AU0 = prev_avg_u * (double)(period_rsi - 1);
|
|
double AD0 = prev_avg_d * (double)(period_rsi - 1);
|
|
|
|
//--- Overbought Price Band
|
|
double RS_ob = ob_level / (MAX_RSI_VALUE - ob_level);
|
|
double x_ob = RS_ob * AD0 - AU0;
|
|
if(x_ob >= 0.0)
|
|
BufferOB[i] = prev_price + x_ob;
|
|
else
|
|
BufferOB[i] = prev_price + x_ob * (MAX_RSI_VALUE - ob_level) / ob_level;
|
|
|
|
//--- Oversold Price Band
|
|
double RS_os = os_level / (MAX_RSI_VALUE - os_level);
|
|
double x_os = RS_os * AD0 - AU0;
|
|
if(x_os >= 0.0)
|
|
BufferOS[i] = prev_price + x_os;
|
|
else
|
|
BufferOS[i] = prev_price + x_os * (MAX_RSI_VALUE - os_level) / os_level;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|