//+------------------------------------------------------------------+
//| MultiTimeframe_StochasticRSI.mq4 |
//| |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025"
#property link ""
#property version "1.23"
#property strict
#property indicator_separate_window
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_buffers 7 // Increased to accommodate new signal buffers
#property indicator_color1 DodgerBlue // Stochastic Main Line (K)
#property indicator_color2 Red // Stochastic Signal Line (D)
#property indicator_color3 Aqua // RSI Line
#property indicator_color4 Lime // Buy Signal (Non-Repainting)
#property indicator_color5 Red // Sell Signal (Non-Repainting)
#property indicator_color6 Green // Buy Signal (Repainting)
#property indicator_color7 Crimson // Sell Signal (Repainting)
#property indicator_width1 2
#property indicator_width2 1
#property indicator_width3 2
#property indicator_width4 1
#property indicator_width5 1
#property indicator_width6 1
#property indicator_width7 1
#property indicator_style1 STYLE_SOLID
#property indicator_style2 STYLE_SOLID
#property indicator_style3 STYLE_SOLID
// Input parameters
input ENUM_TIMEFRAMES Signal_Timeframe = PERIOD_CURRENT; // Timeframe for signals
input int K_Period = 5; // Stochastic %K period
input int D_Period = 54; // Stochastic %D period
input int Slowing = 1; // Stochastic slowing
input int RSI_Period = 8; // RSI period
input int Signal_Shift = 0; // Signal shift (0 = no shift)
input bool Non_Repainting = true; // Enable non-repainting mode
input int Max_History_Bars = 1000; // Maximum history bars for signals
// Arrow settings
input double Arrow_Vertical_Shift = 5; // Vertical shift for arrows (positive =
up, negative = down)
input int Buy_Arrow_Code = 233; // Buy signal arrow code (233 = Up arrow)
input int Sell_Arrow_Code = 234; // Sell signal arrow code (234 = Down
arrow)
input int RepaintBuy_Arrow_Code = 159; // Repainting buy signal arrow code (159
= Hollow circle)
input int RepaintSell_Arrow_Code = 159; // Repainting sell signal arrow code
(159 = Hollow circle)
input double Repainting_Vertical_Gap = 3.0; // Gap between repainting and non-
repainting signals
input ENUM_APPLIED_PRICE RSI_Price = PRICE_CLOSE; // RSI price
input ENUM_MA_METHOD MA_Method = MODE_SMA; // Moving average method
input bool Show_MTF_Info = true; // Show timeframe information in indicator
name
input bool Show_Repainting_Signals = true; // Show repainting signals
// Alert settings
input bool Enable_Alerts = false; // Enable alerts
input bool Alert_NonRepainting = true; // Alert on non-repainting signals only
input bool Alert_Repainting = false; // Alert on repainting signals
input bool Enable_Push_Notifications = false; // Enable push notifications
input bool Enable_Email_Alerts = false; // Enable email alerts
input int Alert_Cooldown = 300; // Cooldown between alerts (seconds)
input string Email_Subject = "StochRSI Alert"; // Email subject
// Indicator buffers
double StochMainBuffer[]; // Stochastic Main (K)
double StochSignalBuffer[]; // Stochastic Signal (D)
double RSIBuffer[]; // RSI line
double BuySignalBuffer[]; // Buy signals (Non-Repainting)
double SellSignalBuffer[]; // Sell signals (Non-Repainting)
double BuySignalRepaintBuffer[]; // Buy signals (Repainting)
double SellSignalRepaintBuffer[]; // Sell signals (Repainting)
// Variables for non-repainting logic
datetime LastSignalTime = 0;
datetime LastAlertTime = 0; // Track last alert time for cooldown
// Variables for storing crossover detection
bool PreviousCrossBuy[1000];
string TimeframeStr = "";
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Set indicator buffers
SetIndexBuffer(0, StochMainBuffer);
SetIndexBuffer(1, StochSignalBuffer);
SetIndexBuffer(2, RSIBuffer);
SetIndexBuffer(3, BuySignalBuffer);
SetIndexBuffer(4, SellSignalBuffer);
SetIndexBuffer(5, BuySignalRepaintBuffer);
SetIndexBuffer(6, SellSignalRepaintBuffer);
// Set indicator labels
SetIndexLabel(0, "Stoch Main");
SetIndexLabel(1, "Stoch Signal");
SetIndexLabel(2, "RSI");
SetIndexLabel(3, "Buy Signal (Confirmed)");
SetIndexLabel(4, "Sell Signal (Confirmed)");
SetIndexLabel(5, "Buy Signal (Real-time)");
SetIndexLabel(6, "Sell Signal (Real-time)");
// Set drawing settings
SetIndexStyle(0, DRAW_LINE);
SetIndexStyle(1, DRAW_LINE);
SetIndexStyle(2, DRAW_LINE);
SetIndexStyle(3, DRAW_ARROW); // Use arrows for buy signals (non-repainting)
SetIndexStyle(4, DRAW_ARROW); // Use arrows for sell signals (non-repainting)
SetIndexStyle(5, DRAW_ARROW); // Use arrows for buy signals (repainting)
SetIndexStyle(6, DRAW_ARROW); // Use arrows for sell signals (repainting)
SetIndexArrow(3, Buy_Arrow_Code); // Customizable arrow for non-
repainting buy signals
SetIndexArrow(4, Sell_Arrow_Code); // Customizable arrow for non-
repainting sell signals
SetIndexArrow(5, RepaintBuy_Arrow_Code); // Customizable arrow for repainting
buy signals
SetIndexArrow(6, RepaintSell_Arrow_Code); // Customizable arrow for repainting
sell signals
// Set draw begin for each buffer (to ensure history is displayed)
SetIndexDrawBegin(0, K_Period + D_Period + Slowing);
SetIndexDrawBegin(1, K_Period + D_Period + Slowing);
SetIndexDrawBegin(2, RSI_Period);
// Set empty values for signal buffers to prevent connecting lines
SetIndexEmptyValue(3, 0.0);
SetIndexEmptyValue(4, 0.0);
SetIndexEmptyValue(5, 0.0);
SetIndexEmptyValue(6, 0.0);
// Initialize signal tracking array
ArrayInitialize(PreviousCrossBuy, false);
// Get timeframe string for display
TimeframeStr = GetTimeframeStr(Signal_Timeframe);
// Set indicator name
string indicatorName = "StochasticRSI Cross ("
+ IntegerToString(K_Period) + ","
+ IntegerToString(D_Period) + ","
+ IntegerToString(Slowing) + ","
+ IntegerToString(RSI_Period) + ")";
// Add timeframe info if enabled
if(Show_MTF_Info && Signal_Timeframe != PERIOD_CURRENT) {
indicatorName += " [" + TimeframeStr + "]";
}
IndicatorShortName(indicatorName);
// Check email alerts configuration
if(Enable_Email_Alerts) {
// Display a message about checking email settings
Print("Email alerts enabled. Ensure your MetaTrader email settings are
properly configured.");
}
// Initialize LastAlertTime
LastAlertTime = 0;
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[])
{
int limit, counted_bars = prev_calculated;
// Calculate starting bar
if(counted_bars < 0) return(-1);
if(counted_bars > 0) counted_bars--;
int limit_bars = MathMin(rates_total - counted_bars, Max_History_Bars);
// Clear buffers on first calculation
if(counted_bars == 0) {
ArrayInitialize(BuySignalBuffer, 0);
ArrayInitialize(SellSignalBuffer, 0);
ArrayInitialize(BuySignalRepaintBuffer, 0);
ArrayInitialize(SellSignalRepaintBuffer, 0);
// Initialize signal tracking array
ArrayInitialize(PreviousCrossBuy, false);
}
// Variables for alerts
bool buySignalDetected = false;
bool sellSignalDetected = false;
bool buyRepaintSignalDetected = false;
bool sellRepaintSignalDetected = false;
// First pass: Calculate indicator values
for(int i = MathMin(rates_total - 1, limit_bars); i >= 0; i--)
{
// Get the corresponding time for the selected timeframe
datetime curr_time = time[i];
// Calculate Stochastic and RSI using the selected timeframe
if(Signal_Timeframe == PERIOD_CURRENT) {
// Use current timeframe
StochMainBuffer[i] = iStochastic(NULL, 0, K_Period, D_Period, Slowing,
MA_Method, 0, MODE_MAIN, i);
StochSignalBuffer[i] = iStochastic(NULL, 0, K_Period, D_Period, Slowing,
MA_Method, 0, MODE_SIGNAL, i);
RSIBuffer[i] = iRSI(NULL, 0, RSI_Period, RSI_Price, i);
} else {
// Use multi-timeframe approach
StochMainBuffer[i] = iStochastic(NULL, Signal_Timeframe, K_Period,
D_Period, Slowing, MA_Method, 0, MODE_MAIN, iBarShift(NULL, Signal_Timeframe,
curr_time));
StochSignalBuffer[i] = iStochastic(NULL, Signal_Timeframe, K_Period,
D_Period, Slowing, MA_Method, 0, MODE_SIGNAL, iBarShift(NULL, Signal_Timeframe,
curr_time));
RSIBuffer[i] = iRSI(NULL, Signal_Timeframe, RSI_Period, RSI_Price,
iBarShift(NULL, Signal_Timeframe, curr_time));
}
// Initialize signal buffers for this bar
BuySignalBuffer[i] = 0;
SellSignalBuffer[i] = 0;
BuySignalRepaintBuffer[i] = 0;
SellSignalRepaintBuffer[i] = 0;
}
// Second pass: Process signals separately for repainting and non-repainting
modes
for(int i = MathMin(rates_total - 1, limit_bars); i >= 0; i--)
{
// Skip if not enough bars for calculation
if(i >= rates_total-1 || i < 1) continue;
// Detecting signal conditions
bool buyCondition = RSIBuffer[i+1] >= StochMainBuffer[i+1] && RSIBuffer[i] <
StochMainBuffer[i];
bool sellCondition = RSIBuffer[i+1] <= StochMainBuffer[i+1] && RSIBuffer[i] >
StochMainBuffer[i];
// FIRST: Process repainting signals (real-time)
if(Show_Repainting_Signals) {
// BUY signal: RSI crosses BELOW Stochastic Main
if(buyCondition) {
BuySignalRepaintBuffer[i + Signal_Shift] = 15 + Arrow_Vertical_Shift -
Repainting_Vertical_Gap;
// Set flag for repainting buy signal
if(i == 0) buyRepaintSignalDetected = true;
}
// SELL signal: RSI crosses ABOVE Stochastic Main
if(sellCondition) {
SellSignalRepaintBuffer[i + Signal_Shift] = 85 - Arrow_Vertical_Shift +
Repainting_Vertical_Gap;
// Set flag for repainting sell signal
if(i == 0) sellRepaintSignalDetected = true;
}
}
// SECOND: Process non-repainting signals (confirmed only)
if(i > 0) {
// Historical bars: process normally
if(buyCondition) {
BuySignalBuffer[i + Signal_Shift] = 15 + Arrow_Vertical_Shift;
}
if(sellCondition) {
SellSignalBuffer[i + Signal_Shift] = 85 - Arrow_Vertical_Shift;
}
}
else if(i == 0 && (!Non_Repainting || time[0] != LastSignalTime)) {
// Current bar: check if we should process it
if(buyCondition) {
BuySignalBuffer[i + Signal_Shift] = 15 + Arrow_Vertical_Shift;
buySignalDetected = true;
}
if(sellCondition) {
SellSignalBuffer[i + Signal_Shift] = 85 - Arrow_Vertical_Shift;
sellSignalDetected = true;
}
// Update last signal time if in non-repainting mode
if(Non_Repainting) {
LastSignalTime = time[0];
}
}
}
// Process alerts if enabled and cooldown period has passed
if(Enable_Alerts && (TimeCurrent() - LastAlertTime > Alert_Cooldown)) {
string alertMessage = "";
bool sendAlert = false;
// Check for non-repainting signals
if(Alert_NonRepainting) {
if(buySignalDetected) {
alertMessage = Symbol() + " " + TimeframeToString(Period()) + ":
StochRSI BUY Signal (Confirmed)";
sendAlert = true;
}
else if(sellSignalDetected) {
alertMessage = Symbol() + " " + TimeframeToString(Period()) + ":
StochRSI SELL Signal (Confirmed)";
sendAlert = true;
}
}
// Check for repainting signals if no non-repainting signal was triggered
if(!sendAlert && Alert_Repainting) {
if(buyRepaintSignalDetected) {
alertMessage = Symbol() + " " + TimeframeToString(Period()) + ":
StochRSI BUY Signal (Real-time)";
sendAlert = true;
}
else if(sellRepaintSignalDetected) {
alertMessage = Symbol() + " " + TimeframeToString(Period()) + ":
StochRSI SELL Signal (Real-time)";
sendAlert = true;
}
}
// Send alert if triggered
if(sendAlert) {
// Regular MetaTrader alert
Alert(alertMessage);
// Push notification
if(Enable_Push_Notifications) {
SendNotification(alertMessage);
}
// Email alert
if(Enable_Email_Alerts) {
SendMail(Email_Subject, alertMessage);
}
// Update last alert time
LastAlertTime = TimeCurrent();
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//| Convert timeframe to readable string |
//+------------------------------------------------------------------+
string GetTimeframeStr(ENUM_TIMEFRAMES timeframe)
{
switch(timeframe)
{
case PERIOD_M1: return "M1";
case PERIOD_M5: return "M5";
case PERIOD_M15: return "M15";
case PERIOD_M30: return "M30";
case PERIOD_H1: return "H1";
case PERIOD_H4: return "H4";
case PERIOD_D1: return "D1";
case PERIOD_W1: return "W1";
case PERIOD_MN1: return "MN";
default: return "Current";
}
}
//+------------------------------------------------------------------+
//| Convert Period() to readable string |
//+------------------------------------------------------------------+
string TimeframeToString(int timeframe)
{
switch(timeframe)
{
case PERIOD_M1: return "M1";
case PERIOD_M5: return "M5";
case PERIOD_M15: return "M15";
case PERIOD_M30: return "M30";
case PERIOD_H1: return "H1";
case PERIOD_H4: return "H4";
case PERIOD_D1: return "D1";
case PERIOD_W1: return "W1";
case PERIOD_MN1: return "MN";
default: return "Unknown";
}
}
//+------------------------------------------------------------------+