forked from animatedread/Warrior_EA
The //| box blocks were excluded from0b06f8eand5efdb48and were what remained: 160 of them ran to 10+ lines, the longest to 88. Compressed to their leading topic sentences - 5 lines for a function header, 8 for a file header - keeping the box format and the standard MQL5 name/author lines verbatim. Verified at the BYTE level this time, across every in-scope file: the list of non-comment lines is byte-identical to HEAD and braces balance. The first check compared a locale-decoded 'git show' against a UTF-8 read and flagged 25 files that had not changed at all - every BOM and every non-ASCII line mismatched. 47,696 -> 40,665 lines in scope; comment share 38% -> 26%. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
95 lines
5 KiB
MQL5
95 lines
5 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| BinomialStats.mqh |
|
|
//| AnimateDread |
|
|
//| project. Free functions, no state, so the deploy gate, the two |
|
|
//| edge floors, the barrier ladder and the detectability reports |
|
|
//| all read the same formula instead of nine transcriptions of it. |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef WARRIOR_SYSTEM_BINOMIALSTATS_MQH
|
|
#define WARRIOR_SYSTEM_BINOMIALSTATS_MQH
|
|
#include <Math\Stat\Normal.mqh>
|
|
//+------------------------------------------------------------------+
|
|
//| Upper-tail standard normal, Q(z) = P(Z >= z). |
|
|
//| |
|
|
//| Wraps the library so the NaN policy lives in one place: an |
|
|
//| unusable z reads as "not significant" rather than propagating a |
|
|
//| NaN into a gate decision. tail=false asks for the UPPER tail, and |
|
|
//| the clamp keeps a -1e-17 round-off out of the Sidak power. |
|
|
//+------------------------------------------------------------------+
|
|
double NormalUpperTailQ(const double z)
|
|
{
|
|
if(!MathIsValidNumber(z))
|
|
return(1.0);
|
|
int err=0;
|
|
double q=MathCumulativeDistributionNormal(z,0.0,1.0,false,false,err);
|
|
if(err!=ERR_OK || !MathIsValidNumber(q))
|
|
return(1.0);
|
|
return(MathMax(0.0,MathMin(1.0,q)));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sampling variance of a binomial rate p over n observations: |
|
|
//| p(1-p)/n, in fraction^2. |
|
|
//| |
|
|
//| Returns 0 for a degenerate rate or an empty sample, which every |
|
|
//| caller already treats as "no bar to clear". Callers that combine |
|
|
//| symbols by inverse variance want this rather than the SE. |
|
|
//+------------------------------------------------------------------+
|
|
double BinomialVar(const double p,const double n)
|
|
{
|
|
if(!MathIsValidNumber(p) || !MathIsValidNumber(n))
|
|
return(0.0);
|
|
if(n<=0.0 || p<=0.0 || p>=1.0)
|
|
return(0.0);
|
|
return(p*(1.0-p)/n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Standard error of a binomial rate p over n observations, in |
|
|
//| percentage points: 100 * sqrt(p(1-p)/n). |
|
|
//+------------------------------------------------------------------+
|
|
double BinomialSEPct(const double p,const double n)
|
|
{
|
|
return(100.0*MathSqrt(BinomialVar(p,n)));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Independent observations needed to certify an edge of `edge` over |
|
|
//| a base rate p, at `sigmas` standard errors: |
|
|
//| n = sigmas^2 * p(1-p) / edge^2 |
|
|
//| |
|
|
//| BinomialSEPct solved for n. Answers "could this configuration |
|
|
//| EVER prove an edge this size" - a property of the geometry, the |
|
|
//| horizon and the window, which no amount of training moves. |
|
|
//+------------------------------------------------------------------+
|
|
double BinomialCallsForEdge(const double p,const double edge,const double sigmas)
|
|
{
|
|
if(edge<=0.0 || p<=0.0 || p>=1.0)
|
|
return(0.0);
|
|
return(sigmas*sigmas*p*(1.0-p)/(edge*edge));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| A rate shrunk toward a prior - the estimator, where everything |
|
|
//| above is the test. |
|
|
//+------------------------------------------------------------------+
|
|
double ShrunkRatePct(const double hits,const double n,const double priorPct,const double priorN)
|
|
{
|
|
bool havePrior=(priorN>0.0 && MathIsValidNumber(priorPct) && priorPct>=0.0);
|
|
if(!MathIsValidNumber(hits) || !MathIsValidNumber(n) || n<=0.0)
|
|
return(havePrior ? priorPct : 0.0); // no evidence => the prior IS the estimate
|
|
if(!havePrior)
|
|
return(100.0*hits/n);
|
|
return((hits+priorN*(priorPct/100.0))*100.0/(n+priorN));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sidak family-wise p for the best of nTried candidates: |
|
|
//| 1 - (1 - p_single)^N. |
|
|
//| |
|
|
//| The null of the MAXIMUM, not of a single draw. At the magnitudes |
|
|
//| in play (p ~ 1e-4..1e-2, N ~ 10..1000) plain double precision is |
|
|
//| ample - no need for the log1p/expm1 form MQL5 would not give us |
|
|
//| anyway. |
|
|
//+------------------------------------------------------------------+
|
|
double SidakFamilyP(const double zObs,const int nTried)
|
|
{
|
|
return(1.0-MathPow(1.0-NormalUpperTailQ(zObs),(double)MathMax(nTried,1)));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
#endif
|