//+------------------------------------------------------------------+ //| BetSizer.mqh | //| AnimateDread | //| | //| FROM A PROBABILITY TO A SIZE (AFML ch. 10). | //| | //| With p = P(this bet wins), the statistic z = (p - 1/2) / | //| sqrt(p (1 - p)) is the test of "is this better than a coin", and | //| m = 2 Phi(z) - 1 is the bet size it earns: 0 at p = 0.5, rising | //| smoothly to 1. Size follows conviction continuously instead of on | //| a cliff, and a coin-flip gets no capital at all. | //| | //| HOW THIS BOOK USES IT. The base risk (RISK_0_25) is what the | //| validated book earns at its AVERAGE win rate p0. A setup the | //| journal rates below average gets m(p) / m(p0) of that risk - so | //| the sizer can only take risk OFF, never add it. That is deliberate | //| on a prop account: the 5% limit is a ceiling, and a model that is | //| wrong about a setup should be wrong on the safe side. Sizes are | //| stepped to 0.1 so a fluctuating estimate does not create a | //| different lot every time. | //+------------------------------------------------------------------+ #ifndef WARRIOR_BETSIZER_MQH #define WARRIOR_BETSIZER_MQH #include "EdgeStats.mqh" class CBetSizer { public: //--- m(p), AFML eq. 10.1. 0 for p <= 1/2 (no edge, no bet). static double Size(const double p) { if(p <= 0.5) return 0.0; if(p >= 0.999) return 1.0; const double z = (p - 0.5) / MathSqrt(p * (1.0 - p)); return 2.0 * CEdgeStats::Phi(z) - 1.0; } //--- The multiple of the base risk for a setup rated p, given the book's average p0. //--- Never above 1. 0 means "the journal rates this a coin flip or worse". static double RiskScale(const double p, const double p0) { const double base = Size(p0); if(base <= 0.0) return 1.0; // the book has no measurable edge to scale against if(p >= p0) return 1.0; const double m = Size(p) / base; return MathFloor(MathMin(m, 1.0) * 10.0 + 0.5) / 10.0; } }; #endif // WARRIOR_BETSIZER_MQH