forked from animatedread/Warrior_EA
- Introduced `FeatureScale.mqh` with `FeatSquash` function for stateless feature scaling. - Added `RegimeMath.mqh` class for regime arithmetic, including efficiency and variance calculations. - Documented the Mind trading logic in `MIND.md`, detailing the trading process and modes. - Created `VOLNORM_PLAN.md` and `VOLNORM_RESULTS.md` for tick-volume normalization testing. - Implemented `read_book.py` for analyzing trade book data and correlations. - Developed `volnorm.py` for testing tick-volume normalization with new and old methods.
82 lines
3.6 KiB
MQL5
82 lines
3.6 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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
|