用 MQL5 表示统计概率分布·综合运用
📊

用 MQL5 表示统计概率分布·综合运用

(3/3)· 把前兩篇的理论与代码拼成可复用的概率分布工具链,少走重写弯路

案例拆解新手友好 第 3/3 篇
很多人在 MQL5 里写分布函数时,一上来就堆继承,结果被多继承缺失卡死。其实借 C++ 成熟代码做轻量改写,比自己造层次结构更省事,也更稳。

◍ Student-t 分位反解与不完全 Beta 类的接驳

在 MT5 里做尾部概率测算时,Student-t 分布的反函数不能直接调系统库,得靠不完全 Beta 函数倒推。下面这段 invaa 就是拿 p 值反解 t 统计量,内部调用了 invbetai(1-p, 0.5*nu, 0.5),再用 sqrt(nu*(1-x)/x) 换算回 t 标度。

MQL5 / C++
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            |
逐行拆解:invaa 先卡 p 的定义域,越界就弹 Alert 报警;x 拿到的是 Beta 逆累积的中段值,最后一行才是真正的 t 反变换。CBeta 类继承 CGauleg18,构造函数里写死 Switch=3000 作为 quadrature 方法的分流阈值,setCBeta 允许外部覆盖精度 Eps 和最小浮点保护 Fpmin。 外汇与贵金属杠杆高,用这类尾部分布算 VaR 只代表概率倾向,实盘仍可能穿出历史分位。把 Switch 从 3000 调到更小值, quadrature 触发更频繁,速度会掉但极端参数下数值可能更稳,建议开 MT5 跑两组 nu=3 和 nu=10 对比一下。

MQL5 / C++
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) 返回密度。

MQL5 / C++
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);
     }
  };
逐行拆一下:alph 和 bet 是公开成员,分别对应分布的中心与展开宽度;构造函数把默认值设好并检查 bet 合法性。pdf 直接套密度闭式解;cdf 里用了常数 1.81379936423421785 做近似加速,x≥alph 与 x<alph 分两支返回,避免浮点抵消。 invcdf 是分位函数,常数 0.551328895421792049 实为 1/1.813799… 的近似,p 超出 (0,1) 会报警;sf 就是生存函数,等于 1-cdf(x)。把这类代码丢进 MT5 的 include 里,后续做 VaR 回测时直接调 cdf 算尾部概率即可,外汇贵金属杠杆高,厚尾漏算可能吃掉本金。

MQL5 / C++
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 头文件即可编译验证。

MQL5 / C++
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) 才合法,超界就报警。实盘用来把历史回撤序列映射成风险分位数时,这套比手写数值反解快一个数量级。 外汇与贵金属杠杆高,分布假设错了分位值会严重偏移,拿去设止损间距前先在策略测试器跑一遍样本外数据。

MQL5 / C++
    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 是否随 α、β 变化,这是跑通后续概率密度模块的第一步。

MQL5 / C++
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的小时收益,能直观看到正态假设低估尾部风险的概率。

MQL5 / C++
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 线中突破出现的次数当离散事件建模。外汇与贵金属杠杆高,这类概率仅作仓位参考,实际爆边风险可能高于回测。

MQL5 / C++
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 区间,可拿去设突破样本阈值。

MQL5 / C++
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) 对比肉眼数出的频率,偏差大就说明泊松假设可能不成立。贵金属与外汇杠杆品种波动剧烈,用分布假设前先确认样本区间风险。

MQL5 / C++
      }
      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 跑一下,验证不同强度下分位数落点。

MQL5 / C++
  {
      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),生成的概率图能明显看到两翼比正态厚得多——这种形态在贵金属跳空时段出现概率偏高,但外汇和贵金属杠杆交易风险极大,厚尾不代表可以放松止损。 下面这段是类定义和脚本输入区的核心代码,逐行看一遍就能抄去改自己的分布参数。

MQL5 / C++
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 对比曲线。外汇与贵金属波动常呈厚尾,用正态边界做仓位止损可能偏窄,实盘需自行验证。

MQL5 / C++
  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 数据跑一遍,看看正态假设在极端行情下崩得有多快。

把分布诊断交给小布盯盘
这些分布类与图形诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到拟合结果,你只需判断市场处于哪类随机状态。

常见问题

倾向用扁平结构和组合替代深层继承,借 Numerical Recipes 的 C++ 函数做轻量重定义,避免复杂基类链。
接口层可统一,但密度与概率质量函数的采样逻辑不同,建议分两个基础模板减少后期 bug。
可以,小布盯盘支持自定义指标与脚本接入,把分布图输出到品种页就能自动叠加诊断。
可能用分位数截断做仓位上限更稳妥,避免在样本外周期硬套历史逆函数参数。