The indicator is not perfect yet, is there anything that can improve it so that it can be used?

 
// SimpleLinregRSI_v4_fixed_XAU_TF15.mq5
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1  Gold
#property indicator_color2  Crimson
#property indicator_width1  2
#property indicator_width2  2

input int    LinregPeriod        = 18;                // tuned for XAUUSD TF15
input int    RSIPeriod           = 14;
input double RSIOverbought       = 70.0;
input double RSIOversold         = 30.0;
input int    EMA_Smoothing       = 3;                 // light smoothing
input ENUM_TIMEFRAMES HTF        = PERIOD_H1;         // HTF filter H1 for TF15
input bool   UseMTFTrend         = true;              // enable MTF trend filter

// Color and display options
input color  ColorUp             = clrGold;
input color  ColorDown           = clrCrimson;
input int    LineWidth           = 2;
input bool   ShowRawAndSmoothed  = false;
input bool   DrawSmoothedOnly    = true;              // show smoothed line by default

// Alerts and notifications
input bool   EnableAlerts        = true;
input bool   EnablePush          = false;
input bool   EnableEmail         = false;
input string AlertSound          = "alert.wav";
input string EmailTo             = "";

// CSV export
input bool   EnableCSVExport     = false;
input string CSVFileName         = "SimpleLinreg_signals_XAU_TF15.csv";

// Performance tuning
input int    MaxBarsToProcess    = 2000;              // tuned for TF15
input bool   UseIncremental      = true;

double UpBuf[];
double DnBuf[];
double ArrClose[];
double ArrRSI[];
double HTF_Close[];

int hRSI;

// ---------- Utility functions ----------
double EMA_Step(double prev, double value, int period)
{
   if(period <= 1) return value;
   double k = 2.0 / (period + 1.0);
   return prev + k * (value - prev);
}

double CalcLinRegAt(const double &price[], int i, int period)
{
   if(i < period - 1) return price[i];
   double sumX=0.0, sumY=0.0, sumXY=0.0, sumX2=0.0;
   for(int k=0;k<period;k++)
   {
      double x = (double)k;
      double y = price[i - k];
      sumX += x; sumY += y; sumXY += x * y; sumX2 += x * x;
   }
   double denom = (period * sumX2 - sumX * sumX);
   if(denom == 0.0) return price[i];
   double slope = (period * sumXY - sumX * sumY) / denom;
   double intercept = (sumY - slope * sumX) / period;
   return slope * (period - 1) + intercept;
}

int GetHTFTrend()
{
   if(!UseMTFTrend) return 0;
   int needed = LinregPeriod + 1;
   ArrayResize(HTF_Close, needed);
   if(CopyClose(_Symbol, HTF, 0, needed, HTF_Close) <= 0) return 0;
   ArraySetAsSeries(HTF_Close, true);
   double cur = CalcLinRegAt(HTF_Close, 0, LinregPeriod);
   double prev = CalcLinRegAt(HTF_Close, 1, LinregPeriod);
   if(cur > prev) return 1;
   if(cur < prev) return -1;
   return 0;
}

// Safe file existence check in MQL5/Files
bool FileExists(string filename)
{
   int handle = FileOpen(filename, FILE_READ|FILE_ANSI|FILE_COMMON);
   if(handle == INVALID_HANDLE) return false;
   FileClose(handle);
   return true;
}

void WriteCSV(string filename, string line)
{
   int handle = FileOpen(filename, FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON);
   if(handle == INVALID_HANDLE)
   {
      handle = FileOpen(filename, FILE_WRITE|FILE_CSV|FILE_COMMON);
      if(handle == INVALID_HANDLE) return;
   }
   FileSeek(handle, 0, SEEK_END);
   FileWriteString(handle, line);
   FileClose(handle);
}

// ---------- Init ----------
int OnInit()
{
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);

   SetIndexBuffer(0, UpBuf, INDICATOR_DATA);
   SetIndexBuffer(1, DnBuf, INDICATOR_DATA);

   PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
   PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_LINE);

   PlotIndexSetInteger(0, PLOT_LINE_COLOR, ColorUp);
   PlotIndexSetInteger(1, PLOT_LINE_COLOR, ColorDown);

   PlotIndexSetInteger(0, PLOT_LINE_WIDTH, LineWidth);
   PlotIndexSetInteger(1, PLOT_LINE_WIDTH, LineWidth);

   hRSI = iRSI(_Symbol, PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE);

   if(EnableCSVExport)
   {
      if(!FileExists(CSVFileName))
      {
         string hdr = "Time;Symbol;TF;Signal;Price;RSI;LinReg;Notes\n";
         WriteCSV(CSVFileName, hdr);
      }
   }

   return(INIT_SUCCEEDED);
}

// ---------- Main ----------
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &prOpen[],
                const double &prHigh[],
                const double &prLow[],
                const double &prClose[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   if(rates_total < LinregPeriod + 2) return(0);

   int toCopy = rates_total;
   if(MaxBarsToProcess > 0 && MaxBarsToProcess < rates_total) toCopy = MaxBarsToProcess;

   ArrayResize(ArrClose, toCopy);
   ArrayResize(ArrRSI, toCopy);

   if(CopyClose(_Symbol, PERIOD_CURRENT, 0, toCopy, ArrClose) <= 0) return(prev_calculated);
   if(CopyBuffer(hRSI, 0, 0, toCopy, ArrRSI) <= 0) return(prev_calculated);

   ArraySetAsSeries(ArrClose, true);
   ArraySetAsSeries(ArrRSI, true);

   ArrayResize(UpBuf, toCopy);
   ArrayResize(DnBuf, toCopy);
   ArraySetAsSeries(UpBuf, true);
   ArraySetAsSeries(DnBuf, true);

   double sumX=0.0, sumX2=0.0;
   for(int k=0;k<LinregPeriod;k++){ sumX += (double)k; sumX2 += (double)k*(double)k; }
   double denom = (LinregPeriod * sumX2 - sumX * sumX);
   if(denom == 0.0) denom = 1.0;

   int htfTrend = GetHTFTrend();

   int start = LinregPeriod;
   if(UseIncremental && prev_calculated > 1) start = MathMax(LinregPeriod, prev_calculated - 1);

   double prev_smoothed_up = 0.0;
   bool havePrevSmooth = false;

   static int lastSignal = 0;

   for(int i = 0; i < toCopy; i++) { UpBuf[i] = EMPTY_VALUE; DnBuf[i] = EMPTY_VALUE; }

   for(int idx = start; idx < toCopy; idx++)
   {
      double sumY=0.0, sumXY=0.0;
      for(int k=0;k<LinregPeriod;k++){ double x=(double)k; double y=ArrClose[idx - k]; sumY += y; sumXY += x * y; }
      double slope = (LinregPeriod * sumXY - sumX * sumY) / denom;
      double intercept = (sumY - slope * sumX) / (double)LinregPeriod;
      double linreg = slope * (LinregPeriod - 1) + intercept;

      double sumYp=0.0, sumXYp=0.0;
      for(int k=0;k<LinregPeriod;k++){ double x=(double)k; double y=ArrClose[(idx - 1) - k]; sumYp += y; sumXYp += x * y; }
      double slope_p = (LinregPeriod * sumXYp - sumX * sumYp) / denom;
      double intercept_p = (sumYp - slope_p * sumX) / (double)LinregPeriod;
      double linreg_p = slope_p * (LinregPeriod - 1) + intercept_p;

      double display = linreg;
      if(EMA_Smoothing > 1)
      {
         if(!havePrevSmooth)
         {
            if(idx-1 >= 0) prev_smoothed_up = (UpBuf[idx-1] != EMPTY_VALUE ? UpBuf[idx-1] : linreg);
            else prev_smoothed_up = linreg;
            havePrevSmooth = true;
         }
         display = EMA_Step(prev_smoothed_up, linreg, EMA_Smoothing);
         prev_smoothed_up = display;
      }

      bool isUp = (linreg > linreg_p);
      bool isDown = (linreg < linreg_p);

      if(UseMTFTrend && htfTrend != 0)
      {
         if(htfTrend == 1 && isDown) isUp = false;
         if(htfTrend == -1 && isUp) isDown = false;
      }

      double rsiVal = ArrRSI[idx];
      bool rsiOkUp = (rsiVal < RSIOverbought);
      bool rsiOkDown = (rsiVal > RSIOversold);

      if(isUp && rsiOkUp)
      {
         UpBuf[idx] = (DrawSmoothedOnly ? display : linreg);
         DnBuf[idx] = EMPTY_VALUE;
      }
      else if(isDown && rsiOkDown)
      {
         DnBuf[idx] = (DrawSmoothedOnly ? display : linreg);
         UpBuf[idx] = EMPTY_VALUE;
      }
      else
      {
         UpBuf[idx] = EMPTY_VALUE; DnBuf[idx] = EMPTY_VALUE;
      }

      if(idx == 0)
      {
         int thisSignal = 0;
         if(isUp && rsiOkUp) thisSignal = 1;
         else if(isDown && rsiOkDown) thisSignal = -1;

         if(thisSignal != lastSignal && thisSignal != 0)
         {
            if(EnableAlerts)
            {
               if(thisSignal == 1) Alert("SimpleLinregRSI: BUY signal on ", _Symbol, " TF=", EnumToString(Period()));
               else Alert("SimpleLinregRSI: SELL signal on ", _Symbol, " TF=", EnumToString(Period()));
               if(StringLen(AlertSound) > 0) PlaySound(AlertSound);
            }
            if(EnablePush) SendNotification("SimpleLinregRSI: signal on " + _Symbol);
            if(EnableEmail && StringLen(EmailTo)>0)
            {
               string subj = "SimpleLinregRSI signal " + _Symbol;
               string body = "Signal=" + IntegerToString(thisSignal) + " Price=" + DoubleToString(ArrClose[0], _Digits) + " RSI=" + DoubleToString(rsiVal,2);
               SendMail(subj, body);
            }
            if(EnableCSVExport)
            {
               string line = TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES) + ";" + _Symbol + ";" + EnumToString(Period()) + ";" +
                             (thisSignal==1 ? "BUY" : "SELL") + ";" + DoubleToString(ArrClose[0], _Digits) + ";" + DoubleToString(rsiVal,2) + ";" + DoubleToString(linreg, _Digits) + ";\n";
               WriteCSV(CSVFileName, line);
            }
            lastSignal = thisSignal;
         }
      }
   }

   return(rates_total);
}
 
There are several issues that would take considerable time to fix properly. 

NOTE: Traders and coders are working for free:

  • if it is interesting for them personally, or
  • if it is interesting for many members on this forum.

Freelance section of the forum should be used in most of the cases.

Trading applications for MetaTrader 5 to order
Trading applications for MetaTrader 5 to order
  • 2025.10.13
  • www.mql5.com
The largest freelance service with MQL5 application developers
 

Hi, my suggest:


//+------------------------------------------------------------------+
//|                                       SimpleLinregRSI_v4_improved.mq5 |
//|                                                  Improved by PEDRO GOMES |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1  Gold
#property indicator_color2  Crimson
#property indicator_width1  2
#property indicator_width2  2

//--- Input Parameters ---
// Indicator Settings
input int    LinregPeriod        = 18;                // Período da Regressão Linear
input int    RSIPeriod           = 14;                // Período do RSI
input double RSIOverbought       = 70.0;              // Nível de sobrecompra do RSI
input double RSIOversold         = 30.0;              // Nível de sobrevenda do RSI
input int    EMA_Smoothing       = 3;                 // Suavização EMA para a linha de regressão

// Multi-Timeframe (MTF) Filter
input ENUM_TIMEFRAMES HTF        = PERIOD_H1;         // Timeframe superior para filtro de tendência
input bool   UseMTFTrend         = true;              // Ativar filtro de tendência MTF

// Display Options
input color  ColorUp             = clrGold;           // Cor para tendência de alta
input color  ColorDown           = clrCrimson;        // Cor para tendência de baixa
input int    LineWidth           = 2;                 // Largura da linha do indicador
input bool   ShowRawAndSmoothed  = false;             // Mostrar linhas bruta e suavizada (não implementado diretamente, usar DrawSmoothedOnly)
input bool   DrawSmoothedOnly    = true;              // Mostrar apenas a linha suavizada

// Alert and Notification Settings
input bool   EnableAlerts        = true;              // Ativar alertas pop-up
input bool   EnablePush          = false;             // Ativar notificações push
input bool   EnableEmail         = false;             // Ativar notificações por e-mail
input string AlertSound          = "alert.wav";       // Ficheiro de som para alertas
input string EmailTo             = "";                // Endereço de e-mail para notificações

// CSV Export Settings
input bool   EnableCSVExport     = false;             // Ativar exportação para CSV
input string CSVFileName         = "SimpleLinreg_signals_XAU_TF15.csv"; // Nome do ficheiro CSV

// Performance and Calculation Settings
input int    MaxBarsToProcess    = 2000;              // Número máximo de barras a processar
input bool   UseIncremental      = true;              // Usar cálculo incremental

//--- Indicator Buffers ---
double UpBuf[];
double DnBuf[];

//--- Handles and Arrays for Calculations ---
double ArrClose[];
double ArrRSI[];
double HTF_Close[];
int hRSI;

//--- Global Variables for Optimization ---
double linreg_sumX;
double linreg_sumX2;
double linreg_denom;

//+------------------------------------------------------------------+
//|                         Utility Functions                        |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Calcula um passo da Média Móvel Exponencial (EMA).               |
//+------------------------------------------------------------------+
double EMA_Step(double prev, double value, int period)
{
   if(period <= 1) return value;
   double k = 2.0 / (period + 1.0);
   return prev + k * (value - prev);
}

//+------------------------------------------------------------------+
//| Calcula o valor da Regressão Linear num índice específico.       |
//+------------------------------------------------------------------+
double CalcLinRegAt(const double &price[], int i, int period)
{
   if(i < period - 1) return price[i]; // Não há dados suficientes para o cálculo

   double sumY = 0.0;
   double sumXY = 0.0;

   for(int k = 0; k < period; k++)
   {
      double x = (double)k;
      double y = price[i - k];
      sumY += y;
      sumXY += x * y;
   }

   // Usar os valores pré-calculados de sumX, sumX2 e denom
   if(linreg_denom == 0.0) return price[i]; // Evitar divisão por zero

   double slope = (period * sumXY - linreg_sumX * sumY) / linreg_denom;
   double intercept = (sumY - slope * linreg_sumX) / (double)period;
   return slope * (period - 1) + intercept;
}

//+------------------------------------------------------------------+
//| Obtém a tendência do timeframe superior usando Regressão Linear. |
//+------------------------------------------------------------------+
int GetHTFTrend()
{
   if(!UseMTFTrend) return 0;

   int needed = LinregPeriod + 1; // Precisamos de LinregPeriod + 1 barras para calcular a LR em 'i' e 'i-1'
   if(ArraySize(HTF_Close) < needed) ArrayResize(HTF_Close, needed);

   if(CopyClose(_Symbol, HTF, 0, needed, HTF_Close) <= 0) return 0;
   ArraySetAsSeries(HTF_Close, true);

   double cur_linreg = CalcLinRegAt(HTF_Close, 0, LinregPeriod);
   double prev_linreg = CalcLinRegAt(HTF_Close, 1, LinregPeriod);

   if(cur_linreg > prev_linreg) return 1;  // Tendência de alta
   if(cur_linreg < prev_linreg) return -1; // Tendência de baixa
   return 0; // Tendência neutra ou indefinida
}

//+------------------------------------------------------------------+
//| Verifica a existência de um ficheiro no diretório MQL5/Files.    |
//+------------------------------------------------------------------+
bool FileExistsInCommon(string filename)
{
   int handle = FileOpen(filename, FILE_READ|FILE_ANSI|FILE_COMMON);
   if(handle == INVALID_HANDLE) return false;
   FileClose(handle);
   return true;
}

//+------------------------------------------------------------------+
//| Escreve uma linha para um ficheiro CSV. Cria o ficheiro se não   |
//| existir e adiciona um cabeçalho.                                |
//+------------------------------------------------------------------+
void WriteCSV(string filename, string line)
{
   int handle = FileOpen(filename, FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON);
   if(handle == INVALID_HANDLE)
   {
      // Se a abertura falhar, tenta criar o ficheiro com permissão de escrita
      handle = FileOpen(filename, FILE_WRITE|FILE_CSV|FILE_COMMON);
      if(handle == INVALID_HANDLE)
      {
         Print("Erro ao abrir/criar ficheiro CSV: ", filename);
         return;
      }
   }
   FileSeek(handle, 0, SEEK_END);
   FileWriteString(handle, line);
   FileClose(handle);
}

//+------------------------------------------------------------------+
//|                       Initialization Function                    |
//+------------------------------------------------------------------+
int OnInit()
{
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);

   SetIndexBuffer(0, UpBuf, INDICATOR_DATA);
   SetIndexBuffer(1, DnBuf, INDICATOR_DATA);

   PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
   PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_LINE);

   PlotIndexSetInteger(0, PLOT_LINE_COLOR, ColorUp);
   PlotIndexSetInteger(1, PLOT_LINE_COLOR, ColorDown);

   PlotIndexSetInteger(0, PLOT_LINE_WIDTH, LineWidth);
   PlotIndexSetInteger(1, PLOT_LINE_WIDTH, LineWidth);

   // Inicializar o handle do RSI
   hRSI = iRSI(_Symbol, PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE);
   if(hRSI == INVALID_HANDLE)
   {
      Print("Erro ao obter handle do RSI. Erro: ", GetLastError());
      return(INIT_FAILED);
   }

   // Pré-calcular valores para a Regressão Linear (constantes para um dado período)
   linreg_sumX = 0.0;
   linreg_sumX2 = 0.0;
   for(int k = 0; k < LinregPeriod; k++)
   {
      linreg_sumX += (double)k;
      linreg_sumX2 += (double)k * (double)k;
   }
   linreg_denom = (LinregPeriod * linreg_sumX2 - linreg_sumX * linreg_sumX);
   if(linreg_denom == 0.0) linreg_denom = 1.0; // Evitar divisão por zero, embora improvável com LinregPeriod > 1

   // Configurar cabeçalho CSV se a exportação estiver ativada e o ficheiro não existir
   if(EnableCSVExport)
   {
      if(!FileExistsInCommon(CSVFileName))
      {
         string hdr = "Time;Symbol;TF;Signal;Price;RSI;LinReg;Notes\n";
         WriteCSV(CSVFileName, hdr);
      }
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//|                       Calculation Function                       |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &prOpen[],
                const double &prHigh[],
                const double &prLow[],
                const double &prClose[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   // Validar dados mínimos
   if(rates_total < LinregPeriod + 2) return(0);

   int limit;
   if(UseIncremental && prev_calculated > 1)
   {
      limit = rates_total - prev_calculated; // Calcular apenas as novas barras
   }
   else
   {
      limit = rates_total; // Recalcular tudo
   }

   // Ajustar o início do cálculo para o período da regressão linear
   int start_calc_idx = MathMax(LinregPeriod, rates_total - limit);

   // Redimensionar arrays e copiar dados
   if(ArraySize(ArrClose) < rates_total) ArrayResize(ArrClose, rates_total);
   if(ArraySize(ArrRSI) < rates_total) ArrayResize(ArrRSI, rates_total);

   if(CopyClose(_Symbol, PERIOD_CURRENT, 0, rates_total, ArrClose) <= 0) return(prev_calculated);
   if(CopyBuffer(hRSI, 0, 0, rates_total, ArrRSI) <= 0) return(prev_calculated);

   ArraySetAsSeries(ArrClose, true);
   ArraySetAsSeries(ArrRSI, true);

   // Redimensionar buffers do indicador
   if(ArraySize(UpBuf) < rates_total) ArrayResize(UpBuf, rates_total);
   if(ArraySize(DnBuf) < rates_total) ArrayResize(DnBuf, rates_total);
   ArraySetAsSeries(UpBuf, true);
   ArraySetAsSeries(DnBuf, true);

   // Limpar buffers para as barras não calculadas ou anteriores
   for(int i = 0; i < start_calc_idx; i++)
   {
      UpBuf[i] = EMPTY_VALUE;
      DnBuf[i] = EMPTY_VALUE;
   }

   static int lastSignal = 0; // Manter o último sinal para evitar repetições de alerta
   double prev_smoothed_linreg = 0.0; // Variável para a suavização EMA
   bool first_smooth_calc = true; // Flag para a primeira inicialização da EMA

   // Obter tendência do timeframe superior uma vez por cálculo
   int htfTrend = GetHTFTrend();

   // Loop principal de cálculo
   for(int idx = start_calc_idx; idx < rates_total; idx++)
   {
      // Calcular Regressão Linear atual
      double current_linreg = CalcLinRegAt(ArrClose, idx, LinregPeriod);

      // Calcular Regressão Linear anterior (para determinar a direção)
      double previous_linreg = CalcLinRegAt(ArrClose, idx - 1, LinregPeriod);

      double display_linreg = current_linreg;
      if(EMA_Smoothing > 1)
      {
         if(first_smooth_calc)
         {
            // Encontrar o último valor suavizado válido ou inicializar com o valor atual
            if(idx > 0 && UpBuf[idx-1] != EMPTY_VALUE)
            {
                prev_smoothed_linreg = UpBuf[idx-1];
            }
            else if (idx > 0 && DnBuf[idx-1] != EMPTY_VALUE)
            {
                prev_smoothed_linreg = DnBuf[idx-1];
            }
            else
            {
                prev_smoothed_linreg = current_linreg;
            }
            first_smooth_calc = false;
         }
         display_linreg = EMA_Step(prev_smoothed_linreg, current_linreg, EMA_Smoothing);
         prev_smoothed_linreg = display_linreg;
      }

      bool isUp = (current_linreg > previous_linreg);
      bool isDown = (current_linreg < previous_linreg);

      // Aplicar filtro MTF
      if(UseMTFTrend && htfTrend != 0)
      {
         if(htfTrend == 1 && isDown) isUp = false; // Se MTF é alta, ignorar sinais de baixa
         if(htfTrend == -1 && isUp) isDown = false; // Se MTF é baixa, ignorar sinais de alta
      }

      // Verificar condições do RSI
      double rsiVal = ArrRSI[idx];
      bool rsiOkUp = (rsiVal < RSIOverbought);
      bool rsiOkDown = (rsiVal > RSIOversold);

      // Atribuir valores aos buffers do indicador
      if(isUp && rsiOkUp)
      {
         UpBuf[idx] = (DrawSmoothedOnly ? display_linreg : current_linreg);
         DnBuf[idx] = EMPTY_VALUE;
      }
      else if(isDown && rsiOkDown)
      {
         DnBuf[idx] = (DrawSmoothedOnly ? display_linreg : current_linreg);
         UpBuf[idx] = EMPTY_VALUE;
      }
      else
      {
         UpBuf[idx] = EMPTY_VALUE;
         DnBuf[idx] = EMPTY_VALUE;
      }

      // Lógica de Alerta e Notificação (apenas para a barra mais recente)
      if(idx == rates_total - 1) // Apenas para a barra atual (índice 0 na série temporal)
      {
         int thisSignal = 0;
         if(isUp && rsiOkUp) thisSignal = 1; // Sinal de Compra
         else if(isDown && rsiOkDown) thisSignal = -1; // Sinal de Venda

         // Evitar alertas repetidos para o mesmo sinal
         if(thisSignal != lastSignal && thisSignal != 0)
         {
            string signal_type = (thisSignal == 1 ? "BUY" : "SELL");
            string alert_message = "SimpleLinregRSI: " + signal_type + " signal on " + _Symbol + " TF=" + EnumToString(Period());

            if(EnableAlerts)
            {
               Alert(alert_message);
               if(StringLen(AlertSound) > 0) PlaySound(AlertSound);
            }
            if(EnablePush) SendNotification(alert_message);
            if(EnableEmail && StringLen(EmailTo) > 0)
            {
               string subj = "SimpleLinregRSI signal " + _Symbol;
               string body = "Signal=" + signal_type + " Price=" + DoubleToString(ArrClose[idx], _Digits) + " RSI=" + DoubleToString(rsiVal, 2);
               SendMail(subj, body);
            }
            if(EnableCSVExport)
            {
               string line = TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES) + ";" + _Symbol + ";" + EnumToString(Period()) + ";" +
                             signal_type + ";" + DoubleToString(ArrClose[idx], _Digits) + ";" + DoubleToString(rsiVal, 2) + ";" + DoubleToString(current_linreg, _Digits) + ";\n";
               WriteCSV(CSVFileName, line);
            }
            lastSignal = thisSignal;
         }
      }
   }

   return(rates_total);
}