Author: Copyright 2015, fxborg
bsi_trend

This script is a sophisticated technical analysis tool designed for MetaTrader to identify market trends, potential reversals, and price channels. Rather than simply looking at raw price movement, it combines several mathematical concepts—including linear regression, volume weighting, and range analysis—to create a "smoothed" view of market behavior.

Here is a breakdown of the core logic used by the script:

1. Dynamic Channel Construction

The script identifies high and low price boundaries over a specific period to define the "trading area." It doesn't just take the highest/lowest points; it uses a linear regression calculation to determine the mathematical path of the trend. This allows the tool to distinguish between actual structural moves and minor price fluctuations (noise).

2. The "Tango" Framework

The heart of the script relies on two primary calculated lines:

  • Tango Line: This acts as a dynamic equilibrium point, representing the median of the current price channel.
  • Tango MA (Moving Average): A smoothed version of the Tango Line that helps traders visualize the underlying momentum. By comparing the current price to these lines, the script can identify whether the market is overextended or trending strongly in one direction.

3. Smart Reversal Detection

The script includes a "Noise Filter" to prevent false signals. It searches for specific Reversal Bars—points where the price structure significantly changes (e.g., a bottom turning into a top). To ensure these aren't just random spikes, the script requires the price move to exceed a minimum spread before it validates the reversal as a significant trend shift.

4. Volume Weighting

One of the more advanced features is the ability to weight the calculations based on trading volume. If enabled, the script gives more "importance" to price moves that occur with high volume. This helps filter out low-liquidity movements, ensuring that the trend lines and reversal signals are backed by significant market activity.

5. The BSI (Balanced Sentiment Index)

The script calculates a proprietary BSI value. This index measures the balance between "positive" and "negative" price pressure. It looks at:

  • How much price is pushing toward the upper boundary of the channel.
  • How much price is pulling toward the lower boundary.
  • The velocity of these moves relative to the Tango MA.

By balancing these factors, the BSI provides a sentiment score that indicates whether the current "push" in the market has enough strength to maintain the trend or if it is losing steam.

Summary of Visual Output

For the end user, the script translates all this complex math into:

  • Trend Lines: Visual representations of the upper and lower boundaries of the market.
  • Signal Arrows: Dynamic markers that appear when the logic detects a confirmed reversal bar (potential entry or exit points).
  • Smoothing Buffers: Lines that show the "fair value" of the price at any given time, helping to filter out market "jitter."
4 Views
0 Downloads
0 Favorites
bsi_trend
ÿþ//+------------------------------------------------------------------+

//|                                                    BSI_Trend.mq4 |

//| BSI_Trend                                 Copyright 2015, fxborg |

//|                                   http://fxborg-labo.hateblo.jp/ |

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

#property copyright "Copyright 2015, fxborg"

#property link      "http://fxborg-labo.hateblo.jp/"

#property version   "1.00"

#property strict



#property indicator_chart_window

#property indicator_buffers 8



#property indicator_type1 DRAW_LINE

#property indicator_type2 DRAW_LINE

#property indicator_type3 DRAW_LINE

#property indicator_type4 DRAW_LINE

#property indicator_type5 DRAW_LINE

#property indicator_type6 DRAW_ARROW

#property indicator_type7 DRAW_ARROW

#property indicator_type8 DRAW_LINE

//---

#property indicator_color1 Orange

#property indicator_color2 Gray

#property indicator_color3 Aqua

#property indicator_color4 DeepPink

#property indicator_color5 DarkViolet

#property indicator_color6 Red

#property indicator_color7 Blue

#property indicator_color8 Red



#property indicator_label1 "Trend High"

#property indicator_label2 "Trend Regr"

#property indicator_label3 "Trend Low"

#property indicator_label4 "Tango Line"

#property indicator_label5 "Tango MA"

#property indicator_label6 "Reversal Bar"

#property indicator_label7 "Reversal Bar"

#property indicator_label8 "BSI"



//---

#property indicator_width1 1

#property indicator_width2 1

#property indicator_width3 1

#property indicator_width4 2

#property indicator_width5 2

#property indicator_width6 2

#property indicator_width7 2

#property indicator_width8 1



#property indicator_style1 STYLE_DASH

#property indicator_style2 STYLE_DOT

#property indicator_style3 STYLE_DASH

#property indicator_style4 STYLE_SOLID

#property indicator_style5 STYLE_SOLID

#property indicator_style8 STYLE_DOT



//--- input parameters

input  int InpChannelPeriod=30;  // Channel Period

input  int InpTrendPeriod=30;    // Trend Period

input  double InpReversalNoiseFilter=5;  // NoiseFilter (Minimam Reversal Spread) 

input  bool InpUsingVolumeWeight=true;  // UseingVolumeWeight



double RevNoiseFilter=InpReversalNoiseFilter*Point;

//---

int min_rates_total;



//--- indicator buffers

double UpTrendBuffer[];

double LoTrendBuffer[];

double MdTrendBuffer[];

double TangoBuffer[];

double TangoMaBuffer[];

double TopBuffer[];

double BtmBuffer[];



//--- calc buffers

double SlopeBuffer[];

double BSIBuffer[];

double HighesBuffer[];

double LowesBuffer[];

double VolBuffer[];

double UpRangeBuffer[];

double DnRangeBuffer[];

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

//| Custom indicator initialization function                         |

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

int OnInit()

  {

//---- Initialization of variables of data calculation starting point

   min_rates_total=1+MathMax(InpTrendPeriod,InpChannelPeriod)+1;



//--- indicator buffers mapping

   IndicatorBuffers(14);



//--- indicator buffers

   SetIndexBuffer(0,UpTrendBuffer);

   SetIndexBuffer(1,MdTrendBuffer);

   SetIndexBuffer(2,LoTrendBuffer);

   SetIndexBuffer(3,TangoBuffer);

   SetIndexBuffer(4,TangoMaBuffer);

   SetIndexBuffer(5,TopBuffer);

   SetIndexBuffer(6,BtmBuffer);

   SetIndexBuffer(7,BSIBuffer);



//--- calc buffers

   SetIndexBuffer(8,HighesBuffer);

   SetIndexBuffer(9,LowesBuffer);

   SetIndexBuffer(10,SlopeBuffer);

   SetIndexBuffer(11,UpRangeBuffer);

   SetIndexBuffer(12,DnRangeBuffer);

   SetIndexBuffer(13,VolBuffer);



   SetIndexArrow(5,159);

   SetIndexArrow(6,159);



   SetIndexShift(0,1);

   SetIndexShift(1,1);

   SetIndexShift(2,1);

   SetIndexShift(3,1);

   SetIndexShift(4,1);

   SetIndexShift(7,1);

//---

   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 i,j,k,first;

//--- check for bars count

   if(rates_total<=min_rates_total)

      return(0);



//--- indicator buffers

   ArraySetAsSeries(UpTrendBuffer,false);

   ArraySetAsSeries(MdTrendBuffer,false);

   ArraySetAsSeries(LoTrendBuffer,false);

   ArraySetAsSeries(TangoBuffer,false);

   ArraySetAsSeries(TangoMaBuffer,false);

   ArraySetAsSeries(TopBuffer,false);

   ArraySetAsSeries(BtmBuffer,false);

   ArraySetAsSeries(BSIBuffer,false);



//--- calc buffers

   ArraySetAsSeries(HighesBuffer,false);

   ArraySetAsSeries(LowesBuffer,false);

   ArraySetAsSeries(UpRangeBuffer,false);

   ArraySetAsSeries(DnRangeBuffer,false);

   ArraySetAsSeries(SlopeBuffer,false);

   ArraySetAsSeries(VolBuffer,false);



//--- rate data

   ArraySetAsSeries(time,false);

   ArraySetAsSeries(high,false);

   ArraySetAsSeries(low,false);

   ArraySetAsSeries(close,false);

   ArraySetAsSeries(tick_volume,false);

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

//|Set High Low Buffeer                                |

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

   first=InpChannelPeriod-1;

   if(first+1<prev_calculated)

      first=prev_calculated-2;

   else

     {

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

        {

         LowesBuffer[i]=0.0;

         HighesBuffer[i]=0.0;

         VolBuffer[i]=0.0;

        }

     }



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

     {

      //--- calculate range spread

      double dmin=1000000.0;

      double dmax=-1000000.0;

      long volmax=0;

      //---      

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

        {

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

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

         if(InpUsingVolumeWeight && volmax<tick_volume[k]) volmax=tick_volume[k];

        }

      //---

      LowesBuffer[i]=dmin;

      HighesBuffer[i]=dmax;

      //---

      if(InpUsingVolumeWeight) VolBuffer[i]=(double)volmax;

     }

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

//|Set Tango Line Buffeer                              |

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

   first=InpChannelPeriod-1;

   if(first+1<prev_calculated)first=prev_calculated-2;

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

     {

      //--- 

      int btm_pos=NULL;

      int top_pos=NULL;

      //--- serach reversal bar

      for(j=i-1;j>1;j--)

        {

         if(LowesBuffer[j-2]-RevNoiseFilter*Point>LowesBuffer[j-1]

            && LowesBuffer[j-1]==LowesBuffer[j])

           {

            //--- reversal of top 

            btm_pos=j-1;

            BtmBuffer[btm_pos]=LowesBuffer[btm_pos];

            break;

           }

         //--- reversal of bottom

         if(HighesBuffer[j-2]+RevNoiseFilter*Point<HighesBuffer[j-1]

            && HighesBuffer[j-1]==HighesBuffer[j])

           {

            top_pos=j-1;

            TopBuffer[top_pos]=HighesBuffer[top_pos];

            break;

           }

        }



      //--- turnning point

      int turn_pos=NULL;

      if(btm_pos!=NULL || top_pos!=NULL)

        {

         //---

         if(btm_pos!=NULL) turn_pos=btm_pos;

         else turn_pos=top_pos;

         //--- 

         double range_lo=1000000.0;

         double range_hi=-1000000.0;

         for(j=turn_pos; j<=i-1;j++)

           {

            if(range_hi<high[j])range_hi=high[j];

            if(range_lo>low[j])range_lo=low[j];

           }

         //--- set channel data

         double prev_high=MathMax(MathMax(MathMax(high[turn_pos-4],

                                  high[turn_pos-3]),high[turn_pos-2]),high[turn_pos-1]);

         double prev_low=MathMin(MathMin(MathMin(low[turn_pos-4],

                                 low[turn_pos-3]),low[turn_pos-2]),low[turn_pos-1]);

         UpRangeBuffer[i]=MathMax(range_hi,prev_high);

         TangoBuffer[i]=(range_hi+range_lo)/2;

         DnRangeBuffer[i]=MathMin(range_lo,prev_low);

         //---

        }

     }

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

//| Set Tango MA Buffeer & BSI Buffer                  |

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

   int MaPeriod=(int)MathRound(InpChannelPeriod/2);

   first=InpChannelPeriod-1+MaPeriod-1;

   if(first+1<prev_calculated)

      first=prev_calculated-2;

   else

     {

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

        {

         TangoMaBuffer[i]=EMPTY_VALUE;

        }

     }

//--- ma cycle

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

     {

      double sum=0.0;

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

         sum+=TangoBuffer[k];

      //--- Tango Ma Buffer

      TangoMaBuffer[i]=sum/MaPeriod;

      //--- BSI Index Buffer

      BSIBuffer[i]=calc_bsi(high,low,close,tick_volume,i,InpChannelPeriod);

     }

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

//| Main calculation loop of the indicator             |

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

   first=min_rates_total-1;

   if(first+1<prev_calculated) first=prev_calculated-2;

//---

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

     {

      //--- 

      bool is_update=false;

      //---  detect trend 

      double ma1 = TangoMaBuffer[i-1];

      double ma2 = TangoMaBuffer[i];

      double bsi1 = BSIBuffer[i-1];

      double bsi2 = BSIBuffer[i];



      if((MathMax(ma1,ma2)<bsi2 && bsi1<bsi2) || (MathMin(ma1,ma2)>bsi2 && bsi1>bsi2))

        {

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

         //| Trend Line                                         |

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

         double a,b;

         double price[],upper[],lower[];

         ArraySetAsSeries(price,true);

         ArraySetAsSeries(upper,true);

         ArraySetAsSeries(lower,true);

         //---  Get Rate info

         int chk_h = CopyHigh(Symbol(),PERIOD_CURRENT,time[i],InpTrendPeriod,upper);

         int chk_l = CopyLow(Symbol(),PERIOD_CURRENT,time[i],InpTrendPeriod,lower);

         int chk_c = CopyClose(Symbol(),PERIOD_CURRENT,time[i],InpTrendPeriod,price);



         if(chk_c<InpTrendPeriod)continue;

         if(chk_h!=chk_c || chk_l!=chk_c)continue;

         //--- Calc regression

         if(!calc_regression(a,b,InpTrendPeriod,price))continue;

         //---

         double mid=a;

         double h1,h2,l1,l2;

         h1=0; h2=0;l1=0; l2=0;



         //---

         for(j=0;j<InpTrendPeriod;j++)

           {

            //---  calc trend high & low

            if(h1<upper[j]-mid) h1=upper[j]-mid;

            else if(h2<upper[j]-mid) h2=upper[j]-mid;

            if(l1<mid-lower[j]) l1=mid-lower[j];

            else if(l2<mid-lower[j]) l2=mid-lower[j];

            mid+=b;

            //---

           }

         //--- change trend line

         if(HighesBuffer[i-1]<HighesBuffer[i] || LowesBuffer[i-1]>LowesBuffer[i])

           {

            //---

            UpTrendBuffer[i] = a+(h1+h2)/2;

            LoTrendBuffer[i] = a-(l1+l2)/2;

            MdTrendBuffer[i]=a;

            SlopeBuffer[i]=b;

            //---

            for(j=1;j<InpTrendPeriod;j++)

              {

               //--- 

               UpTrendBuffer[i-j] = UpTrendBuffer[i-j+1]+b;

               LoTrendBuffer[i-j] = LoTrendBuffer[i-j+1]+b;

               MdTrendBuffer[i-j] = MdTrendBuffer[i-j+1]+b;

               //---

              }

            is_update=true;

           }

        }

      else

        {

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

         //| Channel                                            |

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

         is_update=true;

         UpTrendBuffer[i] = UpRangeBuffer[i];

         LoTrendBuffer[i] = DnRangeBuffer[i];

         MdTrendBuffer[i] = (UpRangeBuffer[i]+DnRangeBuffer[i])/2;

         SlopeBuffer[i]=0.0;

        }



      if(!is_update)

        {

         if(SlopeBuffer[i-1]==0)

           {

            //---

            UpTrendBuffer[i-1] = UpRangeBuffer[i-1];

            LoTrendBuffer[i-1] = DnRangeBuffer[i-1];

            MdTrendBuffer[i]=(UpRangeBuffer[i-1]+DnRangeBuffer[i-1])/2;

            SlopeBuffer[i-1]=0;

            //---

           }

         //---

         SlopeBuffer[i]=SlopeBuffer[i-1];

         UpTrendBuffer[i] = UpTrendBuffer[i-1]-SlopeBuffer[i];

         LoTrendBuffer[i] = LoTrendBuffer[i-1]-SlopeBuffer[i];

         MdTrendBuffer[i]=MdTrendBuffer[i-1]-SlopeBuffer[i];

        }

     }

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

   return(rates_total);

  }

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

//| Regression                                         |

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

bool calc_regression(double  &a,double  &b,int span,double  &price[])

  {

//--- 

   double sumy=0.0; double sumx=0.0; double sumxy=0.0; double sumx2=0.0;

//---

   int x;

   int cnt=0;

   for(x=0; x<span; x++)

     {

      //---

      if(price[x]==0)continue;

      sumx+=x;

      sumx2+= x*x;

      sumy += price[x];

      sumxy+= price[x]*x;

      cnt++;

     }

//---

   double c=sumx2*cnt-sumx*sumx;

   if(c==0.0)return false;

   b=(sumxy*cnt-sumx*sumy)/c;

   a=(sumy-sumx*b)/cnt;

   return true;

  }

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

//| B.S.I Index                                                      |

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

double calc_bsi(const double  &hi[],

                const double  &lo[],

                const double  &cl[],

                const long  &vol[],

                int  pos,

                int span)

  {

//---

   int k;

   double sumpos=0.0;

   double sumneg=0.0;

   double sumhigh=0.0;

   double sumpvol = 0.0;

   double sumnvol = 0.0;

//---

   for(k=(pos-span+1); k<=pos; k++)

     {

      //---

      double v=1.0;

      if(InpUsingVolumeWeight && VolBuffer[k]>0)

        {

         double vol_fact=MathSqrt(VolBuffer[k]);

         v=MathSqrt(vol[k])/vol_fact;

        }

      //--- Range position ratio

      double ratio=0;

      //--- Bar Spread

      double sp=(hi[k]-lo[k]);

      //--- Not DownBar

      if(!(cl[k-1]-sp*0.2>cl[k]))

        {

         ratio=-1*(lo[k]/TangoMaBuffer[k])+2;

         sumpos+=(cl[k]-lo[k])*ratio*v;

        }

      //--- Not UpBar

      if(!(cl[k-1]+sp*0.2<cl[k]))

        {

         ratio=-1*(hi[k]/TangoMaBuffer[k])+2;

         sumneg+=(hi[k]-cl[k])*ratio*v;

        }

     }

//---

   double tmppos,tmpneg;

   tmppos=sumpos/span/ Point;

   tmpneg=sumneg/span/ Point;

//---

   double bsi;

   if((tmppos+tmpneg)!=0)

      bsi=TangoMaBuffer[pos]+8*Point*(tmppos-tmpneg)*MathAbs((tmppos-tmpneg)/(tmppos+tmpneg));

   else

      bsi=TangoMaBuffer[pos]+0.0;

   return (bsi);

  }

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

Comments