﻿//+------------------------------------------------------------------+
//|                                                          TSI.mq5 |
//|                      Copyright © 2005, MetaQuotes Software Corp. |
//|                                        http://www.metaquotes.net |
//+------------------------------------------------------------------+
//  Converted to MQL5 from MQL4 original
//+------------------------------------------------------------------+
#property copyright "Copyright © 2005, MetaQuotes Software Corp."
#property link      "http://www.metaquotes.net"
#property version   "1.00"

#property indicator_separate_window
#property indicator_buffers 7
#property indicator_plots   1

#property indicator_label1  "TSI"
#property indicator_type1   DRAW_LINE
#property indicator_color1  Yellow
#property indicator_width1  1

//---- input parameters
input int  First_R  = 5;
input int  Second_S = 8;

//---- plot buffer (index 0)
double TSI_Buffer[];

//---- calculation buffers (indices 1-6, hidden)
double MTM_Buffer[];
double EMA_MTM_Buffer[];
double EMA2_MTM_Buffer[];
double ABSMTM_Buffer[];
double EMA_ABSMTM_Buffer[];
double EMA2_ABSMTM_Buffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   // Plot buffer
   SetIndexBuffer(0, TSI_Buffer,        INDICATOR_DATA);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   // Calculation-only buffers (not drawn)
   SetIndexBuffer(1, MTM_Buffer,          INDICATOR_CALCULATIONS);
   SetIndexBuffer(2, EMA_MTM_Buffer,      INDICATOR_CALCULATIONS);
   SetIndexBuffer(3, EMA2_MTM_Buffer,     INDICATOR_CALCULATIONS);
   SetIndexBuffer(4, ABSMTM_Buffer,       INDICATOR_CALCULATIONS);
   SetIndexBuffer(5, EMA_ABSMTM_Buffer,   INDICATOR_CALCULATIONS);
   SetIndexBuffer(6, EMA2_ABSMTM_Buffer,  INDICATOR_CALCULATIONS);

   IndicatorSetString(INDICATOR_SHORTNAME, "TSI(" + IntegerToString(First_R) +
                                           "," + IntegerToString(Second_S) + ")");
   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[])
{
   if(rates_total < 2)
      return(0);

   // Use series indexing: index 0 = most recent bar
   ArraySetAsSeries(TSI_Buffer,          true);
   ArraySetAsSeries(MTM_Buffer,          true);
   ArraySetAsSeries(EMA_MTM_Buffer,      true);
   ArraySetAsSeries(EMA2_MTM_Buffer,     true);
   ArraySetAsSeries(ABSMTM_Buffer,       true);
   ArraySetAsSeries(EMA_ABSMTM_Buffer,   true);
   ArraySetAsSeries(EMA2_ABSMTM_Buffer,  true);
   ArraySetAsSeries(close,               true);

   // Pass 1: Momentum (price change) and its absolute value
   for(int i = rates_total - 2; i >= 0; i--)
   {
      MTM_Buffer[i]    = close[i] - close[i + 1];
      ABSMTM_Buffer[i] = MathAbs(MTM_Buffer[i]);
   }
   // Oldest bar has no previous close — set to 0
   MTM_Buffer[rates_total - 1]   = 0.0;
   ABSMTM_Buffer[rates_total - 1] = 0.0;

   // Pass 2: First EMA (period = First_R) on MTM and |MTM|
   CalcEMA(MTM_Buffer,   EMA_MTM_Buffer,   rates_total, First_R);
   CalcEMA(ABSMTM_Buffer, EMA_ABSMTM_Buffer, rates_total, First_R);

   // Pass 3: Second EMA (period = Second_S) on the first EMAs
   CalcEMA(EMA_MTM_Buffer,   EMA2_MTM_Buffer,   rates_total, Second_S);
   CalcEMA(EMA_ABSMTM_Buffer, EMA2_ABSMTM_Buffer, rates_total, Second_S);

   // Pass 4: TSI = 100 * EMA2(MTM) / EMA2(|MTM|)
   for(int i = rates_total - 1; i >= 0; i--)
   {
      if(EMA2_ABSMTM_Buffer[i] != 0.0)
         TSI_Buffer[i] = 100.0 * EMA2_MTM_Buffer[i] / EMA2_ABSMTM_Buffer[i];
      else
         TSI_Buffer[i] = EMPTY_VALUE;
   }

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Compute EMA on a series-indexed source array, result in dst[]    |
//| Oldest bar first (high index), newest bar last (index 0).        |
//+------------------------------------------------------------------+
void CalcEMA(const double &src[], double &dst[], int total, int period)
{
   if(total < 1 || period < 1) return;

   double k = 2.0 / (period + 1.0);

   // Seed with the oldest available value
   int seed = total - 1;
   dst[seed] = src[seed];

   // Walk from old to new (decreasing index in series mode)
   for(int i = seed - 1; i >= 0; i--)
      dst[i] = src[i] * k + dst[i + 1] * (1.0 - k);
}
//+------------------------------------------------------------------+