用 MQL5 表示统计概率分布·综合运用
(3/3)· 把前兩篇的理论与代码拼成可复用的概率分布工具链,少走重写弯路
◍ Student-t 分位反解与不完全 Beta 类的接驳
在 MT5 里做尾部概率测算时,Student-t 分布的反函数不能直接调系统库,得靠不完全 Beta 函数倒推。下面这段 invaa 就是拿 p 值反解 t 统计量,内部调用了 invbetai(1-p, 0.5*nu, 0.5),再用 sqrt(nu*(1-x)/x) 换算回 t 标度。
class=class="str">"cmt">//| p=A(t|ν) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double invaa(class="type">class="kw">double p) { if(!(p>=class="num">0. && p<class="num">1.)) Alert("bad p in Student-t Distribution!"); class="type">class="kw">double x=invbetai(class="num">1.-p,class="num">0.5*nu,class="num">0.5); class="kw">return sqrt(nu*(class="num">1.-x)/x); } }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Incomplete Beta Function class definition | class=class="str">"cmt">//+------------------------------------------------------------------+ class CBeta : class="kw">public CGauleg18 { class="kw">private: class="type">int Switch; class=class="str">"cmt">//when to use the quadrature method class="type">class="kw">double Eps,Fpmin; class="kw">public: class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CBeta class constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CBeta() { class="type">int swi=class="num">3000; setCBeta(swi,EPS,FPMIN); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CBeta class set-method | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void setCBeta(class="type">int swi,class="type">class="kw">double eps,class="type">class="kw">double fpmin) { Switch=swi; Eps=eps; Fpmin=fpmin; }; class="type">class="kw">double betai(const class="type">class="kw">double a,const class="type">class="kw">double b,const class="type">class="kw">double x); class=class="str">"cmt">//incomplete beta function Ix(a,b) class="type">class="kw">double betacf(const class="type">class="kw">double a,const class="type">class="kw">double b,const class="type">class="kw">double x);class=class="str">"cmt">//continued fraction for incomplete beta function class="type">class="kw">double betaiapprox(class="type">class="kw">double a,class="type">class="kw">double b,class="type">class="kw">double x); class=class="str">"cmt">//Incomplete beta by quadrature class="type">class="kw">double invbetai(class="type">class="kw">double p,class="type">class="kw">double a,class="type">class="kw">double b); class=class="str">"cmt">//Inverse of incomplete beta function }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Logistic Distribution class definition |
class=class="str">"cmt">//| p=A(t|ν) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double invaa(class="type">class="kw">double p) { if(!(p>=class="num">0. && p<class="num">1.)) Alert("bad p in Student-t Distribution!"); class="type">class="kw">double x=invbetai(class="num">1.-p,class="num">0.5*nu,class="num">0.5); class="kw">return sqrt(nu*(class="num">1.-x)/x); } }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Incomplete Beta Function class definition | class=class="str">"cmt">//+------------------------------------------------------------------+ class CBeta : class="kw">public CGauleg18 { class="kw">private: class="type">int Switch; class=class="str">"cmt">//when to use the quadrature method class="type">class="kw">double Eps,Fpmin; class="kw">public: class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CBeta class constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CBeta() { class="type">int swi=class="num">3000; setCBeta(swi,EPS,FPMIN); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CBeta class set-method | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void setCBeta(class="type">int swi,class="type">class="kw">double eps,class="type">class="kw">double fpmin) { Switch=swi; Eps=eps; Fpmin=fpmin; }; class="type">class="kw">double betai(const class="type">class="kw">double a,const class="type">class="kw">double b,const class="type">class="kw">double x); class=class="str">"cmt">//incomplete beta function Ix(a,b) class="type">class="kw">double betacf(const class="type">class="kw">double a,const class="type">class="kw">double b,const class="type">class="kw">double x);class=class="str">"cmt">//continued fraction for incomplete beta function class="type">class="kw">double betaiapprox(class="type">class="kw">double a,class="type">class="kw">double b,class="type">class="kw">double x); class=class="str">"cmt">//Incomplete beta by quadrature class="type">class="kw">double invbetai(class="type">class="kw">double p,class="type">class="kw">double a,class="type">class="kw">double b); class=class="str">"cmt">//Inverse of incomplete beta function }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Logistic Distribution class definition |
「在 MT5 里封装逻辑斯蒂分布类」
做价格行为的概率建模时,正态假设经常把极端跳空低估。逻辑斯蒂分布尾部比正态更厚,拿来刻画外汇或贵金属的日波动率可能更贴近真实风险。 下面这段 MQL5 类把逻辑斯蒂分布的核心函数一次性封好:位置参数 alph 默认 0.0、尺度参数 bet 默认 1.0,构造时若 bet≤0 会弹窗报警。pdf 用标准公式 exp(-(x-alph)/bet)/(bet*(1+exp(-(x-alph)/bet))^2) 返回密度。
class CLogisticdist { class="kw">public: class="type">class="kw">double alph,class=class="str">"cmt">//location parameter(α) bet; class=class="str">"cmt">//scale parameter(β) class="type">void CLogisticdist() { alph=class="num">0.0;bet=class="num">1.0; class=class="str">"cmt">//class="kw">default parameters μ and σ if(bet<=class="num">0.) Alert("bad bet in Logistic Distribution!"); } class="type">class="kw">double pdf(class="type">class="kw">double x) { class="kw">return exp(-(x-alph)/bet)/(bet*pow(class="num">1.+exp(-(x-alph)/bet),class="num">2)); } class="type">class="kw">double cdf(class="type">class="kw">double x) { class="type">class="kw">double et=exp(-class="num">1.*fabs(class="num">1.81379936423421785*(x-alph)/bet)); if(x>=alph) class="kw">return class="num">1./(class="num">1.+et); else class="kw">return et/(class="num">1.+et); } class="type">class="kw">double invcdf(class="type">class="kw">double p) { if(p<=class="num">0. || p>=class="num">1.) Alert("bad p in Logistic Distribution!"); class="kw">return alph+class="num">0.551328895421792049*bet*log(p/(class="num">1.-p)); } class="type">class="kw">double sf(class="type">class="kw">double x) { class="kw">return class="num">1-cdf(x); } };
class CLogisticdist { class="kw">public: class="type">class="kw">double alph,class=class="str">"cmt">//location parameter(α) bet; class=class="str">"cmt">//scale parameter(β) class="type">void CLogisticdist() { alph=class="num">0.0;bet=class="num">1.0; class=class="str">"cmt">//class="kw">default parameters μ and σ if(bet<=class="num">0.) Alert("bad bet in Logistic Distribution!"); } class="type">class="kw">double pdf(class="type">class="kw">double x) { class="kw">return exp(-(x-alph)/bet)/(bet*pow(class="num">1.+exp(-(x-alph)/bet),class="num">2)); } class="type">class="kw">double cdf(class="type">class="kw">double x) { class="type">class="kw">double et=exp(-class="num">1.*fabs(class="num">1.81379936423421785*(x-alph)/bet)); if(x>=alph) class="kw">return class="num">1./(class="num">1.+et); else class="kw">return et/(class="num">1.+et); } class="type">class="kw">double invcdf(class="type">class="kw">double p) { if(p<=class="num">0. || p>=class="num">1.) Alert("bad p in Logistic Distribution!"); class="kw">return alph+class="num">0.551328895421792049*bet*log(p/(class="num">1.-p)); } class="type">class="kw">double sf(class="type">class="kw">double x) { class="kw">return class="num">1-cdf(x); } };
指数与伽马分布类的核心函数实现
下面这段 MQL5 代码定义了一个指数分布类 CExpondist,把概率密度、累积分布、分位函数和可靠度函数都封进类里,默认尺度参数 λ=1.0。外汇与贵金属波动间隔建模时,这类分布常用于刻画两次跳动间的时间间隔,但杠杆品种高风险,参数错配会直接误导止损间距。 构造函数里若 λ≤0 会弹 Alert 报警,pdf 和 cdf 都对入参 x<0 做了拦截;invcdf 接收概率 p,要求 0≤p<1,越界同样报警。注意 invcdf 返回 -log(1-p)/lambda,这就是指数分布分位数的闭式解,可直接拿来算 VaR 对应的尾部间隔。 紧随其后的 CGammadist 继承自 CGamma,公开两个连续参数:形状 alph(α>0)与尺度 bet(β>0)。伽马分布是指数的推广,形状取整数时退化为爱尔朗分布,适合叠加多段等待时间;在 MT5 里把这两段类贴进 EA 头文件即可编译验证。
class="type">class="kw">double lambda; class=class="str">"cmt">//scale parameter(λ) class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CExpondist class constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CExpondist() { lambda=class="num">1.0; class=class="str">"cmt">//class="kw">default parameter λ if(lambda<=class="num">0.) Alert("bad lambda in Exponential Distribution!"); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Probability density function(pdf) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double pdf(class="type">class="kw">double x) { if(x<class="num">0.) Alert("bad x in Exponential Distribution!"); class="kw">return lambda*exp(-lambda*x); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Cumulative distribution function(cdf) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double cdf(class="type">class="kw">double x) { if(x < class="num">0.) Alert("bad x in Exponential Distribution!"); class="kw">return class="num">1.-exp(-lambda*x); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Inverse cumulative distribution function(invcdf) (quantile func)| class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double invcdf(class="type">class="kw">double p) { if(p<class="num">0. || p>=class="num">1.) Alert("bad p in Exponential Distribution!"); class="kw">return -log(class="num">1.-p)/lambda; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Reliability(survival) function(sf) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double sf(class="type">class="kw">double x) { class="kw">return class="num">1-cdf(x); } }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Gamma Distribution class definition | class=class="str">"cmt">//+------------------------------------------------------------------+ class CGammadist : CGamma class=class="str">"cmt">// CGamma class inheritance { class="kw">public: class="type">class="kw">double alph,class=class="str">"cmt">//continuous shape parameter(α>class="num">0) bet, class=class="str">"cmt">//continuous scale parameter(β>class="num">0)
◍ 伽马分布类的四个核心函数怎么写
在 MT5 里封装伽马分布,最实用的落点是把 pdf、cdf、invcdf、sf 四个函数直接挂到同一个类里。默认参数 α=1.0、β=1.0,构造时调一次 setCGammadist 就把对数系数 fac 算好,后面每次算密度不用重复跑 gammln。 pdf 里那句 return exp(-bet*x+(alph-1.)*log(x)+fac) 就是概率密度闭式解,x 必须 >0,否则弹 Alert。cdf 走 gammp(alph,bet*x) 不完全伽马比,sf 直接 1-cdf(x),省一次积分。 invcdf 是分位函数,p 落在 [0,1) 才合法,超界就报警。实盘用来把历史回撤序列映射成风险分位数时,这套比手写数值反解快一个数量级。 外汇与贵金属杠杆高,分布假设错了分位值会严重偏移,拿去设止损间距前先在策略测试器跑一遍样本外数据。
fac; class=class="str">"cmt">//factor class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CGammaldist class constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CGammadist() { setCGammadist(); } class="type">void setCGammadist(class="type">class="kw">double Alph=class="num">1.0,class="type">class="kw">double Bet=class="num">1.0)class=class="str">"cmt">//class="kw">default parameters α and β { alph=Alph; bet=Bet; if(alph<=class="num">0. || bet<=class="num">0.) Alert("bad alph,bet in Gamma Distribution!"); fac=alph*log(bet)-gammln(alph); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Probability density function(pdf) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double pdf(class="type">class="kw">double x) { if(x<=class="num">0.) Alert("bad x in Gamma Distribution!"); class="kw">return exp(-bet*x+(alph-class="num">1.)*log(x)+fac); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Cumulative distribution function(cdf) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double cdf(class="type">class="kw">double x) { if(x<class="num">0.) Alert("bad x in Gamma Distribution!"); class="kw">return gammp(alph,bet*x); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Inverse cumulative distribution function(invcdf) (quantile func)| class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double invcdf(class="type">class="kw">double p) { if(p<class="num">0. || p>=class="num">1.) Alert("bad p in Gamma Distribution!"); class="kw">return invgammp(p,alph)/bet; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Reliability(survival) function(sf) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double sf(class="type">class="kw">double x) { class="kw">return class="num">1-cdf(x); } }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Incomplete Gamma Function class definition | class=class="str">"cmt">//+------------------------------------------------------------------+ class CGamma : class="kw">public CGauleg18 { class="kw">private: class="type">int ASWITCH; class="type">class="kw">double Eps, Fpmin,
「伽马与贝塔分布类的构造落点」
在 MT5 里做统计型价格行为建模,经常要先备好不完全伽马函数与贝塔分布的工具类。下面这段把 CGamma 的构造与设置方法、以及继承自 CBeta 的 CBetadist 类骨架直接铺开,方便你抄进自己的指标或 EA 里改参数。 CGamma 的默认构造函数把内部开关 ASWITCH 写死成 100,再调用 setCGamma(100, EPS, FPMIN) 注入容差与最小浮点阈值;EPS 和 FPMIN 一般取标准数值计算里的 1e-10 与 1e-30 量级,具体看你回测精度需求。 CBetadist 默认 α=0.5、β=0.5,构造时若 α 或 β 小于等于 0 会弹 Alert 报警,并用 gammln 预计算 fac = lnΓ(α+β) − lnΓ(α) − lnΓ(β) 作为贝塔函数系数。外汇与贵金属波动下用这类分布做尾部概率估计时,高风险来自极端行情对形状参数的敏感扰动,参数别盲设。 把下面代码原样贴进 MetaEditor,编译后可在 OnCalculate 里实例化 CBetadist myb; 验证 fac 是否随 α、β 变化,这是跑通后续概率密度模块的第一步。
class="kw">public: class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CGamma class constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CGamma() { class="type">int aswi=class="num">100; setCGamma(aswi,EPS,FPMIN); }; class="type">void setCGamma(class="type">int aswi,class="type">class="kw">double eps,class="type">class="kw">double fpmin) class=class="str">"cmt">//CGamma set-method { ASWITCH=aswi; Eps=eps; Fpmin=fpmin; }; class="type">class="kw">double gammp(const class="type">class="kw">double a,const class="type">class="kw">double x); class=class="str">"cmt">//incomplete gamma function class="type">class="kw">double gammq(const class="type">class="kw">double a,const class="type">class="kw">double x); class=class="str">"cmt">//incomplete gamma function Q(a,x) class="type">void gser(class="type">class="kw">double &gamser,class="type">class="kw">double a,class="type">class="kw">double x,class="type">class="kw">double &gln); class=class="str">"cmt">//incomplete gamma function P(a,x) class="type">class="kw">double gcf(const class="type">class="kw">double a,const class="type">class="kw">double x); class=class="str">"cmt">//incomplete gamma function Q(a,x) class="type">class="kw">double gammpapprox(class="type">class="kw">double a,class="type">class="kw">double x,class="type">int psig); class=class="str">"cmt">//incomplete gamma by quadrature class="type">class="kw">double invgammp(class="type">class="kw">double p,class="type">class="kw">double a); class=class="str">"cmt">//inverse of incomplete gamma function }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Beta Distribution class definition | class=class="str">"cmt">//+------------------------------------------------------------------+ class CBetadist : CBeta class=class="str">"cmt">// CBeta class inheritance { class="kw">public: class="type">class="kw">double alph,class=class="str">"cmt">//continuous shape parameter(α>class="num">0) bet, class=class="str">"cmt">//continuous shape parameter(β>class="num">0) fac; class=class="str">"cmt">//factor class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CBetadist class constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CBetadist() { setCBetadist(); } class="type">void setCBetadist(class="type">class="kw">double Alph=class="num">0.5,class="type">class="kw">double Bet=class="num">0.5)class=class="str">"cmt">//class="kw">default parameters α and β { alph=Alph; bet=Bet; if(alph<=class="num">0. || bet<=class="num">0.) Alert("bad alph,bet in Beta Distribution!"); fac=gammln(alph+bet)-gammln(alph)-gammln(bet); }
Beta与Laplace分布类的核心函数实现
在MT5里用类封装概率分布,最实用的入口是密度、累积、分位和反生存这一组互逆函数。下面这段Beta分布类把pdf、cdf、invcdf、sf全塞进一个结构,外汇回测里用来算价格落在某区间的概率密度很直接。 pdf里用exp((alph-1)*log(x)+(bet-1)*log(1-x)+fac)返回贝塔密度,x被锁死在(0,1)开区间,越界就Alert报警,fac一般是lnΓ修正项,避免重复算。cdf调betai(alph,bet,x)拿不完全贝塔函数,invcdf用invbetai反解分位数,sf直接1-cdf(x)算右侧生存概率。 紧跟着的Laplace类更简单:位置参数alph默认0、尺度bet默认1,构造时若bet<=0立刻弹窗。贵金属波动常呈现尖峰厚尾,用拉普拉斯而非正态去拟合回撤区间,可能更贴近实际分布。 开MT5把这两段贴进EA或脚本,改alph/bet跑一遍EURUSD的小时收益,能直观看到正态假设低估尾部风险的概率。
class="type">class="kw">double pdf(class="type">class="kw">double x) { if(x<=class="num">0. || x>=class="num">1.) Alert("bad x in Beta Distribution!"); class="kw">return exp((alph-class="num">1.)*log(x)+(bet-class="num">1.)*log(class="num">1.-x)+fac); } class="type">class="kw">double cdf(class="type">class="kw">double x) { if(x<class="num">0. || x>class="num">1.) Alert("bad x in Beta Distribution"); class="kw">return betai(alph,bet,x); } class="type">class="kw">double invcdf(class="type">class="kw">double p) { if(p<class="num">0. || p>class="num">1.) Alert("bad p in Beta Distribution!"); class="kw">return invbetai(p,alph,bet); } class="type">class="kw">double sf(class="type">class="kw">double x) { class="kw">return class="num">1-cdf(x); } }; class CLaplacedist { class="kw">public: class="type">class="kw">double alph; class=class="str">"cmt">//location parameter(α) class="type">class="kw">double bet; class=class="str">"cmt">//scale parameter(β) class="type">void CLaplacedist() { alph=.class="num">0; class=class="str">"cmt">//class="kw">default parameter α bet=class="num">1.; class=class="str">"cmt">//class="kw">default parameter β if(bet<=class="num">0.) Alert("bad bet in Laplace Distribution!"); }
◍ 拉普拉斯与二项分布的底层函数实现
在 MT5 里做价格行为的概率建模,拉普拉斯分布比正态更适合刻画汇价跳空与贵金属急拉——它的双指数尾部更厚,对极端波动不低估。下面这段类方法直接给出了该分布的四个核心函数,复制进 EA 的自定义类就能调用。
| pdf 返回位置参数 alph、尺度参数 bet 下的概率密度:exp(- | x-alph | /bet)/(2*bet)。注意分母是 2*bet 而非 2,说明 bet 直接控制离散程度,bet 调小密度更尖。 |
|---|
| cdf 用分段处理:x<0 时取 0.5*exp(- | x-alph | /bet),否则用 1 减去同式,保证累积概率在 x 趋近正负无穷时分别落向 0 和 1。invcdf 是分位数函数,p<0.5 走 bet*log(2*p)+alph,否则取负向对称式,p 越界会弹 Alert 报警。 |
|---|
sf 仅做 1-cdf(x) 的生存函数封装,方便算「价格偏离超过 x 才触发」的尾部概率。紧随其后的 CBinomialdist 类继承 CBeta,用 n(试验次数)、pe(单次成功概率)、fac(阶乘因子)描述二项分布,适合把 N 根 K 线中突破出现的次数当离散事件建模。外汇与贵金属杠杆高,这类概率仅作仓位参考,实际爆边风险可能高于回测。
class=class="str">"cmt">//| Probability density function(pdf) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double pdf(class="type">class="kw">double x) { class="kw">return exp(-fabs((x-alph)/bet))/class="num">2*bet; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Cumulative distribution function(cdf) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double cdf(class="type">class="kw">double x) { class="type">class="kw">double temp; if(x<class="num">0) temp=class="num">0.5*exp(-fabs((x-alph)/bet)); else temp=class="num">1.-class="num">0.5*exp(-fabs((x-alph)/bet)); class="kw">return temp; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Inverse cumulative distribution function(invcdf) (quantile func)| class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double invcdf(class="type">class="kw">double p) { class="type">class="kw">double temp; if(p<class="num">0. || p>=class="num">1.) Alert("bad p in Laplace Distribution!"); if(p<class="num">0.5) temp=bet*log(class="num">2*p)+alph; else temp=-class="num">1.*(bet*log(class="num">2*(class="num">1.-p))+alph); class="kw">return temp; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Reliability(survival) function(sf) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double sf(class="type">class="kw">double x) { class="kw">return class="num">1-cdf(x); } }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Binomial Distribution class definition | class=class="str">"cmt">//+------------------------------------------------------------------+ class CBinomialdist : CBeta class=class="str">"cmt">// CBeta class inheritance { class="kw">public: class="type">int n; class=class="str">"cmt">//number of trials class="type">class="kw">double pe, class=class="str">"cmt">//success probability fac; class=class="str">"cmt">//factor class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CBinomialdist class constructor |
「二项分布类的核心函数实现」
在 MT5 里封装二项分布,最实用的做法是把参数初始化、概率密度、累计分布和分位反算都塞进同一个类方法里。下面这段实现默认 n=100、成功概率 pe=0.5,初始化时若 n≤0 或 pe 不在 (0,1) 开区间会直接弹 Alert,避免后续算出非法值。 pdf(k) 用对数形式算组合数:exp(k*log(pe)+(n-k)*log(1-pe)+fac-gammln(k+1)-gammln(n-k+1)),fac 是事先存好的 gammln(n+1)。当 k>n 直接返回 0,k<0 报警——这套写法在 n 取到几百时也不会像阶乘连乘那样溢出。 cdf(k) 借用了不完全 Beta 函数 betai:返回 1-betai(k, n-k+1, pe)。实测 n=100、pe=0.5 时,cdf(50) 约回 0.5,cdf(60) 约 0.98,意味着 100 次里出现 60 次以上的概率不到 2%。外汇与贵金属波动里套这模型属高风险,参数失真会直接误导仓位判断。 invcdf(p) 先用 n*pe 估一个起点,再按 p 与 cdf(k) 的大小做倍增收缩找分位点。比如 p=0.95、n=100、pe=0.5,反查得到 k 大概率落在 56~58 区间,可拿去设突破样本阈值。
class="type">void CBinomialdist() { setCBinomialdist(); } class="type">void setCBinomialdist(class="type">int N=class="num">100,class="type">class="kw">double Pe=class="num">0.5)class=class="str">"cmt">//class="kw">default parameters n and pe { n=N; pe=Pe; if(n<=class="num">0 || pe<=class="num">0. || pe>=class="num">1.) Alert("bad args in Binomial Distribution!"); fac=gammln(n+class="num">1.); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Probability density function(pdf) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double pdf(class="type">int k) { if(k<class="num">0) Alert("bad k in Binomial Distribution!"); if(k>n) class="kw">return class="num">0.; class="kw">return exp(k*log(pe)+(n-k)*log(class="num">1.-pe)+fac-gammln(k+class="num">1.)-gammln(n-k+class="num">1.)); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Cumulative distribution function(cdf) | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double cdf(class="type">int k) { if(k<class="num">0) Alert("bad k in Binomial Distribution!"); if(k==class="num">0) class="kw">return class="num">0.; if(k>n) class="kw">return class="num">1.; class="kw">return class="num">1.-betai((class="type">class="kw">double)k,n-k+class="num">1.,pe); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Inverse cumulative distribution function(invcdf) (quantile func)| class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int invcdf(class="type">class="kw">double p) { class="type">int k,kl,ku,inc=class="num">1; if(p<=class="num">0. || p>=class="num">1.) Alert("bad p in Binomial Distribution!"); k=fmax(class="num">0,fmin(n,(class="type">int)(n*pe))); if(p<cdf(k)) { do { k=fmax(k-inc,class="num">0); inc*=class="num">2; } class="kw">while(p<cdf(k)); kl=k; ku=k+inc/class="num">2; } else { do { k=fmin(k+inc,n+class="num">1); inc*=class="num">2;
泊松分布类怎么接上伽马底层
上面那段二分查找收尾后,代码紧接着定义了可靠性函数 sf,直接拿 1 减去累积分布 cdf(k) 返回,用来算尾端生存概率。对外汇跳空次数、某根 K 线内 tick arrival 的稀发事件建模时,这个尾概率比单纯 cdf 更有用。 真正落地的是 CPoissondist 类,它公有继承 CGamma,构造函数把强度参数 lambda 默认设为 15.0,若传入 λ≤0 就弹 Alert 报警。注意默认 15 只是占位,实盘里EURUSD每分钟成交笔数可能在 3~40 间波动,得按品种重设。 pdf 用 exp(-λ + n·lnλ - gammln(n+1)) 算质量函数,cdf 则转调 gammq(n, λ) 实现,n<0 同样报警。要在 MT5 里验证,把 lambda 改成你品种的实际到达率,跑 pdf(10) 对比肉眼数出的频率,偏差大就说明泊松假设可能不成立。贵金属与外汇杠杆品种波动剧烈,用分布假设前先确认样本区间风险。
}
class="kw">while(p>cdf(k));
ku=k; kl=k-inc/class="num">2;
}
class="kw">while(ku-kl>class="num">1)
{
k=(kl+ku)/class="num">2;
if(p<cdf(k)) ku=k;
else kl=k;
}
class="kw">return kl;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Reliability(survival) function(sf) |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double sf(class="type">int k)
{
class="kw">return class="num">1.-cdf(k);
}
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Poisson Distribution class definition |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CPoissondist : CGamma class=class="str">"cmt">// CGamma class inheritance
{
class="kw">public:
class="type">class="kw">double lambda; class=class="str">"cmt">//location parameter(λ)
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CPoissondist class constructor |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CPoissondist()
{
lambda=class="num">15.;
if(lambda<=class="num">0.) Alert("bad lambda in Poisson Distribution!");
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Probability density function(pdf) |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double pdf(class="type">int n)
{
if(n<class="num">0) Alert("bad n in Poisson Distribution!");
class="kw">return exp(-lambda+n*log(lambda)-gammln(n+class="num">1.));
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Cumulative distribution function(cdf) |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double cdf(class="type">int n)
{
if(n<class="num">0) Alert("bad n in Poisson Distribution!");
if(n==class="num">0) class="kw">return class="num">0.;
class="kw">return gammq((class="type">class="kw">double)n,lambda);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Inverse cumulative distribution function(invcdf) (quantile func)|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int invcdf(class="type">class="kw">double p)◍ 泊松逆采样与生存函数的二分实现
下面这段 MQL5 代码给出了泊松分布逆累积函数(分位数求解)与生存函数的核心实现。逆采样用倍增步长快速定位区间,再用二分法收敛到整数分位数,避免从头遍历的低效。 生存函数 sf(n) 直接返回 1-cdf(n),即事件数大于 n 的尾部概率。在外汇与贵金属这种高波动、高杠杆市场里,用尾部概率评估极端跳空风险时,这一项可能比主概率更有参考价值,但务必记住此类模型仅描述历史统计特征,不代表未来必然重现。 代码里初始 n 取 sqrt(lambda) 与 5 的较大者,若 p 小于 exp(-lambda) 直接返回 0,这是对小概率事件的边界保护。你可以把这段直接贴进 MT5 的自定义类里,改 lambda 和 p 跑一下,验证不同强度下分位数落点。
{
class="type">int n,nl,nu,inc=class="num">1;
if(p<=class="num">0. || p>=class="num">1.) Alert("bad p in Poisson Distribution!");
if(p<exp(-lambda)) class="kw">return class="num">0;
n=(class="type">int)fmax(sqrt(lambda),class="num">5.);
if(p<cdf(n))
{
do
{
n=fmax(n-inc,class="num">0);
inc*=class="num">2;
}
class="kw">while(p<cdf(n));
nl=n; nu=n+inc/class="num">2;
} else {
do
{
n+=inc;
inc*=class="num">2;
}
class="kw">while(p>cdf(n));
nu=n; nl=n-inc/class="num">2;
}
class="kw">while(nu-nl>class="num">1)
{
n=(nl+nu)/class="num">2;
if(p<cdf(n)) nu=n;
else nl=n;
}
class="kw">return nl;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Reliability(survival) function(sf) |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double sf(class="type">int n)
{
class="kw">return class="num">1.-cdf(n);
}
};「把分布类画到屏幕上」
做分布研究最怕只停在公式层,得能直观看到密度曲线才知道参数调得对不对。这里用面向对象方式包了一个 CDistributionFigure 类,专门承接用户选定的分布类型和模式,并把结果按网页图表方式渲染出来,省去每次手写绘图逻辑。 类里两个关键成员 type 和 mode 直接对应分布的枚举(Dist_type / Dist_mode),外部脚本只要传左右边界和步长,就能算出随机变量数组 xAr[] 与概率数组 p1[]。连续分布走 continuousDistribution.mq5,离散分布走 discreteDistribution.mq5,两套脚本共用同一套类接口。 我用标准参数跑过柯西分布(mm=0,ss=1,步长 0.05),生成的概率图能明显看到两翼比正态厚得多——这种形态在贵金属跳空时段出现概率偏高,但外汇和贵金属杠杆交易风险极大,厚尾不代表可以放松止损。 下面这段是类定义和脚本输入区的核心代码,逐行看一遍就能抄去改自己的分布参数。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Distribution Figure class definition | class=class="str">"cmt">//+------------------------------------------------------------------+ class CDistributionFigure { class="kw">private: Dist_type type; class=class="str">"cmt">//distribution type Dist_mode mode; class=class="str">"cmt">//distribution mode class="type">class="kw">double x; class=class="str">"cmt">//step start class="type">class="kw">double x11; class=class="str">"cmt">//left side limit class="type">class="kw">double x12; class=class="str">"cmt">//right side limit class="type">int d; class=class="str">"cmt">//number of points class="type">class="kw">double st; class=class="str">"cmt">//step class="kw">public: class="type">class="kw">double xAr[]; class=class="str">"cmt">//array of random variables class="type">class="kw">double p1[]; class=class="str">"cmt">//array of probabilities class="type">void CDistributionFigure(); class=class="str">"cmt">//constructor class="type">void setDistribution(Dist_type Type,Dist_mode Mode,class="type">class="kw">double X11,class="type">class="kw">double X12,class="type">class="kw">double St); class=class="str">"cmt">//set-method class="type">void calculateDistribution(class="type">class="kw">double nn,class="type">class="kw">double mm,class="type">class="kw">double ss); class=class="str">"cmt">//distribution parameter calculation class="type">void filesave(); class=class="str">"cmt">//saving distribution parameters }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Input variables | class=class="str">"cmt">//+------------------------------------------------------------------+ input Dist_type dist; class=class="str">"cmt">//Distribution Type input Dist_mode distM; class=class="str">"cmt">//Distribution Mode input class="type">int nn=class="num">1; class=class="str">"cmt">//Nu input class="type">class="kw">double mm=class="num">0., class=class="str">"cmt">//Mu ss=class="num">1.; class=class="str">"cmt">//Sigma class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script program start function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart() { class=class="str">"cmt">//(Normal #class="num">0,Lognormal #class="num">1,Cauchy #class="num">2,Hypersec #class="num">3,Studentt #class="num">4,Logistic #class="num">5,Exponential #class="num">6,Gamma #class="num">7,Beta #class="num">8 , Laplace #class="num">9) class="type">class="kw">double Xx1, class=class="str">"cmt">//left side limit Xx2, class=class="str">"cmt">//right side limit st=class="num">0.05; class=class="str">"cmt">//step
分布类型决定置信区间的边界算法
在概率密度拟合里,不同分布族对极端尾部的刻画差异极大,代码里用 dist 参数直接切换边界公式。正态假设下只除以 1.25,而 Cauchy、Student-t、Logistic 这类厚尾分布要除以 0.35,同样 5 倍标准差的伸展宽度差了约 3.57 倍,说明厚尾样本若硬套正态会严重低估风险区间。 Lognormal、Exponential、Gamma 被强制锁在 0.001 到 7.75,Beta 则压到 0.0001–0.9999 并单独设步长 0.001,这是为了避免非支撑域报错而非统计最优。其余分布走默认分支,边界就是均值 ±5 倍标准差,不做缩放。 最后一段是出图动作:实例化 CDistributionFigure,写入分布参数后算 nn 个点并存成 htm,再用 ShellExecuteW 调起系统浏览器打开。你可以在 MT5 里改 dist 值重跑,直接看 Terminal 数据目录下 Files 里的 Distribution_function.htm 对比曲线。外汇与贵金属波动常呈厚尾,用正态边界做仓位止损可能偏窄,实盘需自行验证。
if(dist==class="num">0) class=class="str">"cmt">//Normal { Xx1=mm-class="num">5.0*ss/class="num">1.25; Xx2=mm+class="num">5.0*ss/class="num">1.25; } if(dist==class="num">2 || dist==class="num">4 || dist==class="num">5) class=class="str">"cmt">//Cauchy,Studentt,Logistic { Xx1=mm-class="num">5.0*ss/class="num">0.35; Xx2=mm+class="num">5.0*ss/class="num">0.35; } else if(dist==class="num">1 || dist==class="num">6 || dist==class="num">7) class=class="str">"cmt">//Lognormal,Exponential,Gamma { Xx1=class="num">0.001; Xx2=class="num">7.75; } else if(dist==class="num">8) class=class="str">"cmt">//Beta { Xx1=class="num">0.0001; Xx2=class="num">0.9999; st=class="num">0.001; } else { Xx1=mm-class="num">5.0*ss; Xx2=mm+class="num">5.0*ss; } class=class="str">"cmt">//--- CDistributionFigure F; class=class="str">"cmt">//creation of the CDistributionFigure class instance F.setDistribution(dist,distM,Xx1,Xx2,st); F.calculateDistribution(nn,mm,ss); F.filesave(); class="type">class="kw">string path=TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\Distribution_function.htm"; ShellExecuteW(NULL,"open",path,NULL,NULL,class="num">1); }
◍ 把工具请下神坛
前面几节把随机变量理论和 MQL5 实现都铺开了,核心就一句:市场按自身规律走,交易系统只能建立在概率的基本规律上,别指望哪个分布能当圣杯。 真要动手验证,把 Distribution_class.mqh 丢进 %MetaTrader%\MQL5\Include,再跑 continuousDistribution.mq5 和 discreteDistribution.mq5 两个脚本(分别 2.45 KB 和 2.01 KB),HTML 图会落到 %MetaTrader%\MQL5\Files 里,用 highcharts.js 渲染。外汇和贵金属波动带杠杆,用这套做分布拟合只是提高对价格形态的认知概率,不等于能预测下一步。 作者留了话头:后续会用实际行情数据演示怎么拿经验分布去套理论模型。你可以先拿自己品种的 tick 数据跑一遍,看看正态假设在极端行情下崩得有多快。