← К каталогу
Исходный код · ID 4758

Trend Regime Indicator

trendregimeindicator.mq5

Похожие материалы по тегам

goldlevelbreakout 1.00 optimised.mq4MQL4 / MetaTrader 4 · 1 общих теговgold level breakout.mq5MQL5 / MetaTrader 5 · 1 общих тегов+++ wickpercentage_adjusted_alerts .mq5MQL5 / MetaTrader 5 · 1 общих тегов(v) volatility stop.mq4MQL4 / MetaTrader 4 · 1 общих тегов(v) volatility stop-b.mq4MQL4 / MetaTrader 4 · 1 общих тегов(t_s_r)-slope direction line.mq5MQL5 / MetaTrader 5 · 1 общих тегов
Вы автор или правообладатель?

Сообщите нам, если считаете, что материал размещён без разрешения.

Краткий обзор

О программе

Данный индикатор классифицирует каждый бар графика по одному из трёх режимов: восходящий тренд, нисходящий тренд или боковое движение. Классификация основывается на анализе среднего направленного индекса (ADX) и индексов направленного движения (DI) в сочетании с фильтром волатильности на основе средней истинной амплитуды (ATR).

Визуализация реализована в виде цветных столбцов ADX, где цвет соответствует текущему сглаженному режиму. На графике цены отображаются прямоугольники, охватывающие участки последовательных баров одного режима. В верхней части окна индикатора размещена панель с информацией о текущем режиме, его длительности и значении ADX.

Индикатор поддерживает дополнительные настройки: сглаживание режима, отображение долей каждого режима в заданном окне баров, а также оповещение при смене режима. Данные рассчитываются на закрытых барах, а текущий незакрытый бар пересчитывается при каждом обновлении.

Настройки программы

НастройкаОписание
InpDirPeriod Период расчёта индекса направленного движения (DI) и среднего направленного индекса (ADX).
InpAdxLevel Пороговое значение ADX, при котором бар классифицируется как боковое движение. Если уровень ADX находится на или ниже этого значения, режим считается боковым. При значении 0 фильтр отключён.
InpAtrPeriod Период расчёта индекса средней истинной амплитуды (ATR), используемый для оценки волатильности.
InpAtrAvgPeriod Период усреднения ATR для фильтра бокового движения. Если значение больше нуля, бар с ATR на или ниже среднего считается боковым. При значении 0 фильтр отключён.
InpAtrAvgType Тип усреднения ATR для фильтра: простое или экспоненциальное.
InpBlend Количество баров для сглаживания классификации режима. Допустимые значения от 1 до 5.
InpBoxes Определяет, какие участки отображаются прямоугольниками на графике цены: отключено, только тренды или тренды и боковое движение.
InpBoxBars Количество баров назад от текущего, в пределах которых отображаются прямоугольники на графике цены.
InpBoxMin Минимальное количество баров в участке одного режима для отображения прямоугольника на графике цены.
InpWindow Количество баров для расчёта долей каждого режима во второй строке панели. При значении 0 строка долей скрыта.
InpUpColor Цвет отображения столбцов и прямоугольников при восходящем тренде.
InpSideColor Цвет отображения столбцов и прямоугольников при боковом движении.
InpDownColor Цвет отображения столбцов и прямоугольников при нисходящем тренде.
InpAlert Включает отправку оповещения при переходе закрытого бара в новый режим.

Поиск по коду файла

#property version   "1.07"
#property description "Trend up, trend down or sideways for every bar, drawn as ADX columns in"
#property description "the color of the regime, with a box on the price chart around each stretch"
#property description "of one regime and a one-line panel for the regime, its age and the ADX."
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   4
#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_label1  "Regime column"
#property indicator_type2   DRAW_NONE
#property indicator_label2  "Regime (1 up, 0 sideways, -1 down)"
#property indicator_type3   DRAW_NONE
#property indicator_label3  "Bars in regime"
#property indicator_type4   DRAW_NONE
#property indicator_label4  "ADX"

enum ENUM_ATR_AVERAGE
  {
   ATR_AVERAGE_SIMPLE=0,            // simple
   ATR_AVERAGE_EXPONENTIAL=1        // exponential
  };

enum ENUM_PRICE_BOXES
  {
   PRICE_BOXES_OFF=0,               // none
   PRICE_BOXES_TRENDS=1,            // trends only
   PRICE_BOXES_ALL=2                // trends and sideways
  };

input group "Trend"
input int              InpDirPeriod    = 14;                   // ADX and DI period
input double           InpAdxLevel     = 25;                   // ADX level for a trend, 0 = off
input group "Volatility"
input int              InpAtrPeriod    = 14;                   // ATR period
input int              InpAtrAvgPeriod = 0;                    // ATR average period, 0 = off
input ENUM_ATR_AVERAGE InpAtrAvgType   = ATR_AVERAGE_SIMPLE;   // ATR average type
input group "Display"
input int              InpBlend        = 1;                    // regime smoothing, 1 to 5
input ENUM_PRICE_BOXES InpBoxes        = PRICE_BOXES_ALL;      // boxes on the price chart
input int              InpBoxBars      = 300;                  // bars back with boxes
input int              InpBoxMin       = 3;                    // shortest stretch with a box, bars
input int              InpWindow       = 0;                    // regime shares over N bars, 0 = hidden
input color            InpUpColor      = C'30,110,230';        // trend up
input color            InpSideColor    = C'150,150,150';       // sideways
input color            InpDownColor    = C'220,40,50';         // trend down
input group "Alerts"
input bool             InpAlert        = false;                // alert when the regime changes

#define NO_VALUE     EMPTY_VALUE    // value not available yet
#define REGIME_UP    2.0            // trend up
#define REGIME_SIDE  0.0            // sideways
#define REGIME_DOWN  -2.0           // trend down

struct RegimeRules
  {
   bool              quietGate;     // quiet ATR marks sideways bars
   int               atrSpan;       // ATR period
   bool              atrAvgExp;     // exponential ATR average
   int               atrAvgSpan;    // ATR average period
   bool              weakGate;      // weak ADX marks sideways bars
   int               adxSpan;       // ADX smoothing period
   int               dirSpan;       // directional index period
   double            adxFloor;      // ADX at or below it is sideways
   int               blend;         // bars in the regime smoothing
   int               window;        // bars in the regime shares, 0 = none
  };

struct RegimeBar
  {
   double            avgRange;      // average true range
   double            atrAvg;        // average of the ATR
   double            plusDi;        // plus directional index
   double            minusDi;       // minus directional index
   double            strength;      // average directional index
   bool              quiet;         // ATR at or below its average
   bool              weak;          // ADX at or below the level
   double            raw;           // regime of the bar
   double            settled;       // smoothed regime
   double            column;        // ADX, negative in a trend down; NO_VALUE if no regime
   int               reason;        // sideways cause: 1 quiet ATR, 2 weak ADX, 3 both
   int               age;           // bars in the current smoothed regime
   double            prior;         // smoothed regime of the bar before
   int               priorAge;      // bars the regime of the bar before had lasted
   int               seen;          // bars with a regime in the window
   int               upBars;        // of those, trend up
   int               sideBars;      // of those, sideways
   int               downBars;      // of those, trend down
  };

struct SeededAverage
  {
   int               span;          // values in the seed
   double            alpha;         // weight of each new value
   int               run;           // values received since the last gap
   double            seedSum;       // sum of those values before the seed
   double            value;         // current average, NO_VALUE if not ready
  };

void SeedReset(SeededAverage &avg,const int span,const double alpha)
  {
   avg.span=span;
   avg.alpha=alpha;
   avg.run=0;
   avg.seedSum=0.0;
   avg.value=NO_VALUE;
  }

void SeedFeed(SeededAverage &avg,const double v)
  {
   if(v==NO_VALUE)
     {
      avg.run=0;
      avg.seedSum=0.0;
      avg.value=NO_VALUE;
      return;
     }
   if(avg.value!=NO_VALUE)
     {
      avg.value=avg.alpha*v+(1.0-avg.alpha)*avg.value;
      return;
     }
   avg.run++;
   avg.seedSum+=v;
   if(avg.run==avg.span)
      avg.value=avg.seedSum/avg.span;
  }

class CRollingMean
  {
public:
   int               span;          // values in the mean
   int               run;           // values received since the last gap
   int               head;          // slot of the next value
   double            ring[];        // the last span values

   void              Reset(const int n);

   void              CopyFrom(const CRollingMean &src);

   double            Feed(const double v);
  };

void CRollingMean::Reset(const int n)
  {
   span=n;
   run=0;
   head=0;
   ArrayResize(ring,n);
   ArrayInitialize(ring,0.0);
  }

void CRollingMean::CopyFrom(const CRollingMean &src)
  {
   span=src.span;
   run=src.run;
   head=src.head;
   ArrayCopy(ring,src.ring);
  }

double CRollingMean::Feed(const double v)
  {
   if(v==NO_VALUE)
     {
      run=0;
      return(NO_VALUE);
     }
   ring[head]=v;
   head=(head+1)%span;
   run++;
   if(run<span)
      return(NO_VALUE);
   double s=0.0;
   for(int k=0; k<span; k++)
      s+=ring[(head+k)%span];
   return(s/span);
  }

class CRegimeLog
  {
public:
   int               window;        // bars in the window, 0 = none
   int               age;           // bars in the current regime
   double            last;          // regime of the last bar added
   int               head;          // slot of the next bar
   int               filled;        // slots in use
   int               seen;          // bars with a regime in the window
   int               upBars;        // trend up bars in the window
   int               sideBars;      // sideways bars in the window
   int               downBars;      // trend down bars in the window
   double            ring[];        // regimes of the bars in the window

   void              Reset(const int n);

   void              CopyFrom(const CRegimeLog &src);

   void              Add(const double state,RegimeBar &out);

private:

   void              Count(const double state,const int delta);
  };

void CRegimeLog::Reset(const int n)
  {
   window=MathMax(n,0);
   age=0;
   last=NO_VALUE;
   head=0;
   filled=0;
   seen=0;
   upBars=0;
   sideBars=0;
   downBars=0;
   ArrayResize(ring,MathMax(window,1));
   ArrayInitialize(ring,NO_VALUE);
  }

void CRegimeLog::CopyFrom(const CRegimeLog &src)
  {
   window=src.window;
   age=src.age;
   last=src.last;
   head=src.head;
   filled=src.filled;
   seen=src.seen;
   upBars=src.upBars;
   sideBars=src.sideBars;
   downBars=src.downBars;
   ArrayCopy(ring,src.ring);
  }

void CRegimeLog::Count(const double state,const int delta)
  {
   if(state==NO_VALUE)
      return;
   seen+=delta;
   if(state==REGIME_UP)
      upBars+=delta;
   if(state==REGIME_SIDE)
      sideBars+=delta;
   if(state==REGIME_DOWN)
      downBars+=delta;
  }

void CRegimeLog::Add(const double state,RegimeBar &out)
  {
   out.prior=last;
   out.priorAge=age;
   if(state==NO_VALUE)
      age=0;
   else
      age=(last!=NO_VALUE && last==state) ? age+1 : 1;
   last=state;
   out.age=age;
   if(window>0)
     {
      if(filled==window)
         Count(ring[head],-1);
      else
         filled++;
      ring[head]=state;
      Count(state,1);
      head=(head+1)%window;
     }
   out.seen=seen;
   out.upBars=upBars;
   out.sideBars=sideBars;
   out.downBars=downBars;
  }

class CRegimeTape
  {
public:
   SeededAverage     atrAvg;        // Wilder average of the true range
   SeededAverage     atrExp;        // exponential average of the ATR
   CRollingMean      atrMean;       // simple average of the ATR
   SeededAverage     rangeAvg;      // Wilder average of the true range for DI
   SeededAverage     plusAvg;       // Wilder average of the upward move
   SeededAverage     minusAvg;      // Wilder average of the downward move
   SeededAverage     spreadAvg;     // Wilder average of the DI spread
   CRollingMean      blendMean;     // mean of the last regimes
   CRegimeLog        log;           // age and shares of the regimes
   double            plusDi;        // last valid plus directional index
   double            minusDi;       // last valid minus directional index

   void              Clear(const RegimeRules &r);

   void              CopyFrom(const CRegimeTape &src);

   void              Step(const RegimeRules &r,const double &high[],const double &low[],
                          const double &close[],const int i,RegimeBar &out);
  };

void CRegimeTape::Clear(const RegimeRules &r)
  {
   SeedReset(atrAvg,r.atrSpan,1.0/r.atrSpan);
   SeedReset(atrExp,r.atrAvgSpan,2.0/(r.atrAvgSpan+1));
   atrMean.Reset(r.atrAvgSpan);
   SeedReset(rangeAvg,r.dirSpan,1.0/r.dirSpan);
   SeedReset(plusAvg,r.dirSpan,1.0/r.dirSpan);
   SeedReset(minusAvg,r.dirSpan,1.0/r.dirSpan);
   SeedReset(spreadAvg,r.adxSpan,1.0/r.adxSpan);
   blendMean.Reset(r.blend);
   log.Reset(r.window);
   plusDi=NO_VALUE;
   minusDi=NO_VALUE;
  }

void CRegimeTape::CopyFrom(const CRegimeTape &src)
  {
   atrAvg=src.atrAvg;
   atrExp=src.atrExp;
   atrMean.CopyFrom(src.atrMean);
   rangeAvg=src.rangeAvg;
   plusAvg=src.plusAvg;
   minusAvg=src.minusAvg;
   spreadAvg=src.spreadAvg;
   blendMean.CopyFrom(src.blendMean);
   log.CopyFrom(src.log);
   plusDi=src.plusDi;
   minusDi=src.minusDi;
  }

void CRegimeTape::Step(const RegimeRules &r,const double &high[],const double &low[],
                       const double &close[],const int i,RegimeBar &out)
  {
   double span=high[i]-low[i];
   double trueRange=span;
   double dirRange=NO_VALUE;
   double upMove=NO_VALUE;
   double downMove=NO_VALUE;
   if(i>0)
     {
      trueRange=MathMax(span,MathMax(MathAbs(high[i]-close[i-1]),MathAbs(low[i]-close[i-1])));
      dirRange=trueRange;
      double rise=high[i]-high[i-1];
      double fall=low[i-1]-low[i];
      upMove=(rise>fall && rise>0.0) ? rise : 0.0;
      downMove=(fall>rise && fall>0.0) ? fall : 0.0;
     }

   SeedFeed(atrAvg,trueRange);
   out.avgRange=atrAvg.value;
   if(r.atrAvgExp)
     {
      SeedFeed(atrExp,out.avgRange);
      out.atrAvg=atrExp.value;
     }
   else
      out.atrAvg=atrMean.Feed(out.avgRange);

   SeedFeed(rangeAvg,dirRange);
   SeedFeed(plusAvg,upMove);
   SeedFeed(minusAvg,downMove);
   if(rangeAvg.value!=NO_VALUE && rangeAvg.value!=0.0)
     {
      if(plusAvg.value!=NO_VALUE)
         plusDi=100.0*plusAvg.value/rangeAvg.value;
      if(minusAvg.value!=NO_VALUE)
         minusDi=100.0*minusAvg.value/rangeAvg.value;
     }
   out.plusDi=plusDi;
   out.minusDi=minusDi;
   double spread=NO_VALUE;
   if(plusDi!=NO_VALUE && minusDi!=NO_VALUE)
     {
      double total=plusDi+minusDi;
      spread=MathAbs(plusDi-minusDi)/(total==0.0 ? 1.0 : total);
     }
   SeedFeed(spreadAvg,spread);
   out.strength=(spreadAvg.value==NO_VALUE) ? NO_VALUE : 100.0*spreadAvg.value;

   bool missing=(out.avgRange==NO_VALUE || out.strength==NO_VALUE || plusDi==NO_VALUE || minusDi==NO_VALUE ||
                 (r.quietGate && out.atrAvg==NO_VALUE));
   out.quiet=(r.quietGate && out.avgRange!=NO_VALUE && out.atrAvg!=NO_VALUE && out.avgRange<=out.atrAvg);
   out.weak=(r.weakGate && out.strength!=NO_VALUE && out.strength<=r.adxFloor);
   if(missing)
      out.raw=NO_VALUE;
   else
      if(out.quiet || out.weak)
         out.raw=REGIME_SIDE;
      else
         out.raw=(plusDi>minusDi) ? REGIME_UP : REGIME_DOWN;
   double mean=blendMean.Feed(out.raw);
   out.settled=(out.raw==NO_VALUE || mean==NO_VALUE) ? NO_VALUE : MathFloor(mean/2.0+0.5)*2.0;

   out.column=NO_VALUE;
   out.reason=0;
   if(out.settled!=NO_VALUE)
     {
      out.column=(out.settled==REGIME_DOWN) ? -out.strength : out.strength;
      if(out.settled==REGIME_SIDE)
         out.reason=(out.quiet ? 1 : 0)+(out.weak ? 2 : 0);
     }
   log.Add(out.settled,out);
  }

#define COLUMN_WIDTH 3              // width of the regime columns
#define PANEL_X      6              // panel distance from the left edge
#define PANEL_Y      18             // panel distance from the window top
#define PANEL_PAD    5              // inner margin of the panel
#define LINE_STEP    15             // pixels between panel lines
#define TEXT_FONT    "Arial"        // font of the shares line
#define TITLE_FONT   "Arial Bold"   // font of the regime line
#define TEXT_SIZE    8              // font size of the panel
#define PANEL_GAP    4              // pixels between the panel and the columns
#define ROOM_MAX     0.6            // largest share of the window kept for the panel
#define EDGE_SHARE   0.5            // regime color share in the panel frame
#define TREND_FILL   0.14           // regime color share in a trend box
#define SIDE_FILL    0.10           // regime col

Показано первые 50% кода (символов: 20833)