//+------------------------------------------------------------------+ //| RegimeMath.mqh | //| AnimateDread | //| | //| THE ONE COPY OF THE REGIME ARITHMETIC. Efficiency ratio, variance | //| ratio and the three-way label they agree on used to live inside | //| CWarriorSignal, reading the stdlib series - which meant nothing | //| that was not a signal module (the Mind's price modality, the bar | //| cache) could ask "what is the market doing" without a second copy | //| of the formulas, and two copies eventually disagree. | //| | //| Every function takes a NEWEST-FIRST array: x[0] is the bar being | //| judged, x[k] is k bars before it - the same orientation as a | //| series shift, so CWarriorSignal fills it with Close(shift + k) and | //| CBarCache::ClosesBack() fills it from its oldest-first storage. | //| A caller must supply period + 1 values. | //+------------------------------------------------------------------+ #ifndef WARRIOR_REGIMEMATH_MQH #define WARRIOR_REGIMEMATH_MQH class CRegimeMath { public: //--- Net distance over path walked. 1 = a straight line, 0 = thrash that ends where it began. //--- A flat window (zero path) is "no information" and reads as chop, not as a 0/0 perfect trend. static double Efficiency(const double &x[], const int period) { if(ArraySize(x) < period + 1) return 0.0; const double net = MathAbs(x[0] - x[period]); double path = 0.0; for(int i = 0; i < period; i++) path += MathAbs(x[i] - x[i + 1]); return (path <= 0.0) ? 0.0 : net / path; } //--- Var(q-bar) / (q * Var(1-bar)). A random walk gives 1.0; above it moves compound (trend), //--- below it they cancel (mean reversion). The q-blocks do not overlap, so they are independent. static double Variance(const double &x[], const int period, const int q) { if(period < q * 4 || q < 2 || ArraySize(x) < period + 1) return 1.0; // too few independent blocks to estimate anything double m1 = 0.0; for(int i = 0; i < period; i++) m1 += (x[i] - x[i + 1]); m1 /= period; double v1 = 0.0; for(int i = 0; i < period; i++) { const double d = (x[i] - x[i + 1]) - m1; v1 += d * d; } v1 /= (period - 1); if(v1 <= 0.0) return 1.0; const int blocks = period / q; double mq = 0.0; for(int b = 0; b < blocks; b++) mq += (x[b * q] - x[(b + 1) * q]); mq /= blocks; double vq = 0.0; for(int b = 0; b < blocks; b++) { const double d = (x[b * q] - x[(b + 1) * q]) - mq; vq += d * d; } vq /= (blocks - 1); return vq / (q * v1); } //--- 0 = consolidation, 1 = trending, 2 = mean reverting. THREE, NOT MORE: every extra regime //--- divides the journal again, and a cell under the sample floor is no estimate at all. static int Code(const double er, const double vr) { if(er >= 0.35 && vr >= 1.0) return 1; // efficient AND compounding: a trend if(vr <= 0.85 && er <= 0.15) return 2; // cancelling and inefficient: mean reverting return 0; } //--- The window every caller uses for the label: ER(20) and VR(60, q 5) need 61 values. static const int LabelBars(void) { return 61; } }; #endif // WARRIOR_REGIMEMATH_MQH