tangoline_v1.1

Download script
Author: Copyright 2015, fxborg
tangoline_v1.1

This script, known as the Tango Moving Average, is a sophisticated technical analysis tool designed to identify market trends and volatility. Unlike standard moving averages that simply follow price, this indicator uses a unique "spike filtering" logic to distinguish between meaningful price movements and temporary market noise.

Here is a breakdown of how the logic works in plain English:

1. Intelligent Spike Detection

The core innovation of this script is its ability to identify "spikes"—extreme high or low price points that occur within a specific window of time (the Period).

  • Noise Filtering: It looks for significant reversals by checking if a new low or high is actually distinct from the previous ones. If a move is too small, the script ignores it as "noise."
  • Identifying Key Points: By isolating these true highs and lows, the script identifies the "peak" and "bottom" of a price swing rather than just reacting to every minor fluctuation.

2. Dynamic Range Calculation

Once the script identifies a significant spike (a top or a bottom), it performs a smart calculation of the price range:

  • Symmetry Logic: If a move is identified as a "top," the script adjusts the lower boundary of the range to ensure it reflects the actual price structure.
  • Balanced Midpoints: It calculates a "Tango Line" based on the midpoint of these filtered extremes. This creates a more stable baseline than traditional averages because it is anchored to significant structural points in the chart rather than every single candle's close.

3. Smoothing and Trend Analysis

After establishing the core Tango Line, the script applies a second layer of mathematical refinement:

  • Moving Average: The "Tango Line" is smoothed out over a secondary period. This prevents the indicator from looking jagged, making it easier for a human trader to see the overall direction of the trend.
  • Volatility Bands: Finally, the script calculates the Standard Deviation—a measure of how much the price typically "wiggles" around its average. It then projects an Upper and Lower Band based on this volatility.

Summary for the Trader

  • The Tango Line (Main Line): Represents the smoothed "true" trend of the market, filtered to ignore minor price spikes.
  • The Bands (Dotted Lines): Act as a boundary for normal price behavior. When the price pushes toward or outside these bands, it suggests the market is becoming overextended relative to its recent history.
  • Visual Advantage: By filtering out "noise" and focusing on structural highs and lows, this indicator aims to provide a cleaner, more reliable trend signal than standard moving averages, which can often be "fooled" by sudden, insignificant price jumps.
Miscellaneous
Implements a curve of type %1It issuies visual alerts to the screen
10 Views
0 Downloads
0 Favorites
tangoline_v1.1
ÿþ//+------------------------------------------------------------------+

//|                                           TangoMovingAverage.mq4 |

//|                                           Copyright 2015, fxborg |

//|                                  http://blog.livedoor.jp/fxborg/ |

//+------------------------------------------------------------------+

#property copyright "Copyright 2015, fxborg"

#property link      "http://blog.livedoor.jp/fxborg/"

#property version   "1.1"

#property strict

#property indicator_chart_window

#property indicator_buffers 4

//--- plot Label1

//--- input parameters

input  int InpPeriod=20;   // Period

input double InpReversalNoiseFilter=5;     // Noise Filter

input  color InpLineColor=DeepPink; // Tango Line Color

input  color InpMaColor=DarkViolet; // Middle Line Color

input  color InpBandColor=Gray; // Band Color

input double InpDeviations=2.0; // Bands Deviations

//--- indicator buffers

double LineBuffer[];

double MaBuffer[];

//---- for calc 

double HighesBuffer[];

double LowesBuffer[];

double HighBuffer[];

double LowBuffer[];

double UpperBuffer[];

double LowerBuffer[];

int CalcBarCount=2;     // Calc Bar Count

//+------------------------------------------------------------------+

//| Custom indicator initialization function                         |

//+------------------------------------------------------------------+

int OnInit()

  {

   string short_name;

//--- indicator buffers mapping

   IndicatorBuffers(8);

   SetIndexBuffer(0,LineBuffer);

   SetIndexStyle(0,DRAW_LINE,STYLE_SOLID,1,InpLineColor);

   SetIndexShift(0,1);

   SetIndexBuffer(1,MaBuffer);

   SetIndexStyle(1,DRAW_LINE,STYLE_SOLID,1,InpMaColor);

   SetIndexShift(1,1);

//---

   SetIndexBuffer(2,UpperBuffer);

   SetIndexStyle(2,DRAW_LINE,STYLE_DOT,0,InpBandColor);

   SetIndexShift(2,1);

//---

   SetIndexBuffer(3,LowerBuffer);

   SetIndexStyle(3,DRAW_LINE,STYLE_DOT,0,InpBandColor);

   SetIndexShift(3,1);

//---

   SetIndexBuffer(4,HighesBuffer);

   SetIndexBuffer(5,LowesBuffer);

   SetIndexBuffer(6,HighBuffer);

   SetIndexBuffer(7,LowBuffer);

   if(InpPeriod<2)

     {

      Alert("InpPeriod is too small.");

      return(INIT_FAILED);

     }

//---

   short_name="Tango Line v1.1("+IntegerToString(InpPeriod)+")";

   IndicatorShortName(short_name);

//---

   return(INIT_SUCCEEDED);

  }

//--------------------------------------------------------------------

//| De-initialization                                                |

//--------------------------------------------------------------------

int deinit()

  {

   string short_name="Tango Line v1.1("+IntegerToString(InpPeriod)+")";

   IndicatorShortName(short_name);

   return(0);

  }

//+------------------------------------------------------------------+

//| 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 i,k,pos;

//--- check for bars count

   if(rates_total<=InpPeriod)

      return(0);

//--- counting from 0 to rates_total

   ArraySetAsSeries(LineBuffer,false);

   ArraySetAsSeries(MaBuffer,false);

   ArraySetAsSeries(UpperBuffer,false);

   ArraySetAsSeries(LowerBuffer,false);

//---

   ArraySetAsSeries(HighesBuffer,false);

   ArraySetAsSeries(LowesBuffer,false);

   ArraySetAsSeries(HighBuffer,false);

   ArraySetAsSeries(LowBuffer,false);

//---

   ArraySetAsSeries(close,false);

   ArraySetAsSeries(low,false);

   ArraySetAsSeries(high,false);

//---

   pos=InpPeriod-1;

   if(pos+1<prev_calculated)

      pos=prev_calculated-2;

   else

     {

      for(i=0; i<pos; i++)

        {

         LowesBuffer[i]=0.0;

         HighesBuffer[i]=0.0;

         HighBuffer[i]=0.0;

         LowBuffer[i]=0.0;

         LineBuffer[i]=0.0;

        }

     }

//--- calculate HighesBuffer[] and ExtHighesBuffer[]

   int spike_bar=pos-InpPeriod+1;

   int btm_bar=pos-InpPeriod+1;

   int top_bar=pos-InpPeriod+1;

//--- i < rates_total 

   for(i=pos; i<rates_total && !IsStopped(); i++)

     {

      //--- calculate range spread

      double dmin=1000000.0;

      double dmax=-1000000.0;

      for(k=i-InpPeriod+1; k<=i; k++)

        {

         if(dmin>low[k]) dmin=low[k];

         if(dmax<high[k]) dmax=high[k];

        }

      //--- hook

      if((LowesBuffer[i-2]-InpReversalNoiseFilter*Point)>LowesBuffer[i-1]

         && LowesBuffer[i-1]==dmin)

        {

         btm_bar=i-1;

        }

      if((HighesBuffer[i-2]+InpReversalNoiseFilter*Point)<HighesBuffer[i-1]

         && HighesBuffer[i-1]==dmax)

        {

         top_bar=i-1;

        }

      LowesBuffer[i]=dmin;

      HighesBuffer[i]=dmax;

      spike_bar=MathMax(top_bar,btm_bar);

      //---

      if(spike_bar>i-InpPeriod+1)

        {

         //--- calculate range spread

         dmin=1000000.0;

         dmax=-1000000.0;

         //---

         for(k=i-InpPeriod+1; k<=i; k++)

           {

            //---

            if(k>=spike_bar)

              {

               if(dmin>low[k]) dmin=low[k];

               if(dmax<high[k]) dmax=high[k];

              }

           }

        }

      double middle_line=(LowesBuffer[i]+HighesBuffer[i])/2;

      if(spike_bar==top_bar)

        {

         HighBuffer[i]=dmax;

         LowBuffer[i]=MathMin(dmin,middle_line);

        }

      else if(spike_bar==btm_bar)

        {

         HighBuffer[i]=MathMax(dmax,middle_line);

         LowBuffer[i]=(dmin);

        }

      LineBuffer[i]=(dmax+dmin)/2;



     }

//--- avg

   int AvgPeriod=(int)MathFloor(InpPeriod/2);

//   int AvgPeriod = 5;

   pos=AvgPeriod-1;

   if(pos+1<prev_calculated)

      pos=prev_calculated-2;

   else

     {

      for(i=0; i<pos; i++)

        {

         MaBuffer[i]=0.0;

        }

     }

//---

   for(i=pos; i<rates_total && !IsStopped(); i++)

     {

      double sumM=0.0;

      for(k=0; k<AvgPeriod; k++)

        {

         sumM+=LineBuffer[i-k];

        }

      MaBuffer[i]=sumM/AvgPeriod;

      double std =  calc_StdDev(i,close, MaBuffer[i],AvgPeriod);

      UpperBuffer[i]=  MaBuffer[i]+InpDeviations*std;

      LowerBuffer[i]=  MaBuffer[i]-InpDeviations*std;

     }

//--- return value of prev_calculated for next call

   return(rates_total);

  }

//+------------------------------------------------------------------+

//| Calculate Standard Deviation                                     |

//+------------------------------------------------------------------+

double calc_StdDev(int pos,const double &price[],const double mean,int period)

  {

   double res=0.0;

   if(pos>=period)

     {

      for(int i=0; i<period; i++)

         res+=MathPow(price[pos-i]-mean,2);

      res=MathSqrt(res/period);

     }

   return(res);

  }

//+------------------------------------------------------------------+

Comments