//+------------------------------------------------------------------+ //| BOSSSelfTest.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 "Behavioral Self-Test" #property version "1.00" #property strict #property script_show_inputs #include //--- shape parameters shared by every synthetic series in the test. input int WordLen = 2; // SFA coefficients kept (word = 2*WordLen letters). input int Alphabet = 4; // letters per coefficient slot input int Window = 24; // sliding window length input int SeriesLen = 240; // length of each labelled/query series input double NoiseAmp = 0.35; // noise amplitude for the robustness test //--- three canonical shapes the classifier must tell apart. Each is //--- built on a common grid so only the shape differs, not the scale. //+------------------------------------------------------------------+ //| Fill 'out' with a trending series: a steady drift plus a small | //| oscillation and optional noise. | //+------------------------------------------------------------------+ void MakeTrend(double &out[],int len,double noise) { ArrayResize(out,len); for(int i=0;i %d/%d correct (%.0f%%)", levels[L],correct,trials,100.0*correct/trials); } } //+------------------------------------------------------------------+ //| Test 3: vocabulary readout - the interpretability money-shot. | //| Print the most frequent words each clean shape produces, so the | //| reader can literally see the "dialect" of each regime. | //+------------------------------------------------------------------+ void TestVocabulary(const CBossClassifier &clf) { Print("=== Test 3: vocabulary of each shape ==="); const CBossTransform *tr=clf.Transform(); if(tr==NULL) { Print(" no transform"); return; } double s[]; string names[3]= {"trend","range","volatile"}; for(int c=0;c<3;c++) { if(c==0) MakeTrend(s,SeriesLen,NoiseAmp*0.3); if(c==1) MakeRange(s,SeriesLen,NoiseAmp*0.3); if(c==2) MakeVolatile(s,SeriesLen,NoiseAmp*0.3); CBossHistogram h; tr.Transform(s,SeriesLen,h); //--- find the top-3 words by count (small vocab: linear scans) string top=""; for(int pick=0;pick<3 && pick=0); if(!taken && h.CountAt(i)>bestC) { bestC=h.CountAt(i); bestI=i; } } if(bestI<0) break; top+=StringFormat("[%s]x%d ",h.WordAt(bestI),h.CountAt(bestI)); } PrintFormat(" %-9s (%d distinct words): %s",names[c],h.Size(),top); } } //+------------------------------------------------------------------+ //| Script entry: build the classifier, train one clean example per | //| class, then run the three behavioural tests. | //+------------------------------------------------------------------+ void OnStart() { MathSrand(20260712); //--- learn MCB breakpoints from a broad window pool double windows[]; int nwin; BuildTrainingWindows(windows,nwin,Window); CBossClassifier clf; if(!clf.Build(WordLen,Alphabet,windows,nwin,Window)) { Print("Build() failed - check WordLen/Alphabet/Window vs limits"); return; } PrintFormat("classifier built: word=%d letters, alphabet=%d, window=%d", WordLen*2,Alphabet,Window); //--- one clean labelled prototype per class double s[]; MakeTrend(s,SeriesLen,NoiseAmp*0.2); clf.AddExample(s,SeriesLen,"trend"); MakeRange(s,SeriesLen,NoiseAmp*0.2); clf.AddExample(s,SeriesLen,"range"); MakeVolatile(s,SeriesLen,NoiseAmp*0.2); clf.AddExample(s,SeriesLen,"volatile"); PrintFormat("trained on %d prototypes",clf.TrainCount()); TestDiscrimination(clf); TestNoiseRobustness(clf); TestVocabulary(clf); Print("=== self-test complete ==="); } //+------------------------------------------------------------------+