//+------------------------------------------------------------------+ //| BOSSRegime.mq5| //| MMQ — Muhammad Minhas Qamar | //| www.mql5.com/en/articles/23491 | //+------------------------------------------------------------------+ #property copyright "MMQ — Muhammad Minhas Qamar" #property link "https://www.mql5.com/en/articles/23491" #property description "A regime-context indicator driven by a BOSS ensemble." #property version "1.00" #property strict #property indicator_separate_window #property indicator_buffers 2 #property indicator_plots 1 #property indicator_minimum -0.5 #property indicator_maximum 2.5 //--- one colour-histogram plot; colour index selects the regime hue. #property indicator_label1 "Regime" #property indicator_type1 DRAW_COLOR_HISTOGRAM #property indicator_color1 clrDodgerBlue,clrLimeGreen,clrTomato #property indicator_width1 3 #include #include //--- training / classification geometry input int Segment = 120; // bars per classified segment input int WordLen = 2; // SFA coefficients kept input int Alphabet = 4; // SFA letters per coefficient slot input int TrainBars = 20000; // history bars used to train the ensemble //--- labeler thresholds (balance the training classes) input double TrendACF = 0.05; // lag-1 autocorrelation above this -> TREND input double VolHighMult = 1.3; // volatility above mult*reference -> VOLATILE input bool ShowLabel = true; // draw a text label of the current regime //--- plot buffers: value 0/1/2 = regime code, colour index picks hue. double RegimeBuffer[]; double ColorBuffer[]; //--- the trained model and the labeler, built once in OnInit. CBossEnsemble g_ensemble; CRegimeLabeler g_labeler; bool g_ready=false; //--- forward declarations (functions reference each other out of order). void SliceSeries(const double &src[],int start,int len,double &out[]); void DrawRegimeLabel(ENUM_REGIME r); //--- colour index per regime, matching indicator_color1 order: //--- 0 = range (blue), 1 = trend (green), 2 = volatile (red). int RegimeColorIndex(ENUM_REGIME r) { switch(r) { case REGIME_RANGE: return 0; case REGIME_TREND: return 1; case REGIME_VOLATILE: return 2; } return 0; } //--- plot height per regime so all three sit on distinct rows: //--- range=0, trend=1, volatile=2. double RegimeLevel(ENUM_REGIME r) { switch(r) { case REGIME_RANGE: return 0.0; case REGIME_TREND: return 1.0; case REGIME_VOLATILE: return 2.0; } return 0.0; } //+------------------------------------------------------------------+ //| Map a class-name string back to its regime enum. | //+------------------------------------------------------------------+ ENUM_REGIME RegimeFromName(const string name) { if(name=="trend") return REGIME_TREND; if(name=="volatile") return REGIME_VOLATILE; return REGIME_RANGE; } //+------------------------------------------------------------------+ //| Train the BOSS ensemble on recent labelled history. | //| Cuts TrainBars of closes into non-overlapping segments, labels | //| each by the statistical rule, and trains the ensemble to | //| reproduce those labels. Returns false if history/data is thin. | //+------------------------------------------------------------------+ bool TrainEnsemble(void) { int need=TrainBars; double close[]; int got=CopyClose(_Symbol,_Period,1,need,close); // closed bars only if(got<4*Segment) { PrintFormat("BOSSRegime: only %d bars, need >= %d",got,4*Segment); return false; } ArraySetAsSeries(close,false); // oldest first //--- reference volatility for the labeler = mean segment volatility. g_labeler.SetTrendACF(TrendACF); g_labeler.SetVolHighMult(VolHighMult); int nseg=got/Segment; double segbuf[]; double volsum=0.0; for(int s=0;s0)?volsum/nseg:0.0); //--- cut and label segments. double segs[]; ArrayResize(segs,nseg*Segment); int labels[]; ArrayResize(labels,nseg); int kept=0, cnt[4]= {0,0,0,0}; for(int s=0;s0)?prev_calculated-1:Segment; if(start