Trend Regime Indicator
Похожие материалы по тегам
Сообщите нам, если считаете, что материал размещён без разрешения.
Сообщить о материале
Обращение по товару «trendregimeindicator.mq5» отправится на рассмотрение. Укажите контакт для ответа и опишите причину.
О программе
Данный индикатор классифицирует каждый бар графика по одному из трёх режимов: восходящий тренд, нисходящий тренд или боковое движение. Классификация основывается на анализе среднего направленного индекса (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)