用 MQL5 表示统计概率分布(基础篇)
「概率论在 MQL5 里的根基视角」
把概率分布写进 MQL5,先得认清一件事:整套概率论其实建立在「不可欲性」哲学体系之上,这不是数学装饰,而是建模时对待随机性的底层态度。 在 MT5 里用代码表达统计概率分布,意味着你要把「未知但可测」的行情波动,转成可重复计算的函数与数组,而不是凭感觉猜方向。 外汇与贵金属杠杆高、跳空频繁,任何分布假设都只是近似,实盘里要随时准备分布失效。
随机与规律:交易盈利的底层变量
把每一笔交易拆开看,胜败往往只是概率事件,交易者日常处理的其实就是随机性。但随机性并非不可穿透,它的反面是规律性——当某种结构反复出现,随机就被普遍规律收编成可识别的秩序。 把视角切到外汇或贵金属市场,这种随机—规律的拉扯直接决定了账户净值的走向。高杠杆环境下,误把噪声当规律会快速吞噬保证金,这是这类品种的高风险本质。 本文后续要铺开的基础理论工具,目的只有一个:帮你在MT5的历史数据里,把那些“可能重复”的局部规律先捞出来,而不是凭手感猜。
◍ 概率分布到底在描述什么
做价格行为分析时,常要刻画某个随机变量的取值规律,这时就需要一维统计概率分布。它本质是按某种规律去描述一组随机变量样本,也就是说任何分布规律的应用都依赖一组随机变量,而非单个孤立数值。 分析理论分布的直接用处,是能依据变量属性值快速识别频率变化图形,并顺手拿到该分布的一些统计参数(如均值、方差)。这对后续判断行情波动聚集或稀散有底层参考价值。 专业文献习惯按随机变量集合类型,把分布系列分成连续分布和离散分布。但这只是惯例,还能按分布曲线 f(x) 相对 x=x0 的对称性、位置参数、模式数量、随机变量区间等标准再切分。 定义分布规律最流行的方式有五种:概率密度函数、分布函数、逆分布函数、可靠性函数,以及其他衍生形式。外汇与贵金属属高杠杆高风险品种,分布仅描述历史样本概率结构,不代表未来必然重复。
「在 MT5 里搭一套概率分布类库」
做价格行为分析时,把历史波动套进理论分布去比对异常,比肉眼看 K 线更靠谱。MQL5 不支持多继承,所以这里没有搞复杂的类树,而是借了科学计算 C++ 实践里的函数,按 MT5 语法重写,避免重复造轮子。 连续分布先铺了 10 种:正态 Nor(μ,σ²)、对数正态 Logn(μ,σ²)、柯西 Cau(μ,σ)、双曲线正割 HS(μ,σ)、学生 t Stt(ν,μ,σ)、逻辑 Logi(α,β)、指数 Exp(λ)、伽玛 Gam(α,β)、贝塔 Beta(α,β)、拉普拉斯 Lap(α,β)。图 1 到图 10 分别对应它们的密度曲线,比如 Nor(0,1) 就是均值 0 方差 1 的标准钟形。 离散分布补了二项 Bin(n,p) 和泊松 Pois(λ)。二项分布里 X 只能取 0 或 1,正好拿来数交易系统里亏(0)和赚(1)的笔数;泊松描述固定密度下的稀有事件,对估风险程度有用。 所有类写在 Distribution_class.mqh,CNormaldist 派生于 CErf(误差函数基类),CCauchydist 用 atan2() 取弧度制反正切主值,CStudenttdist 和 CBetadist 都挂 CBeta,而 CBeta 与 CGamma 共用 CGauleg18 做高斯-勒让德积分系数。开 MT5 把头文件拖进脚本,就能直接 new CNormaldist() 算密度。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Normal Distribution class definition | class=class="str">"cmt">//+------------------------------------------------------------------+ class CNormaldist : CErf class=class="str">"cmt">// Erf class inheritance { class="kw">public: class="type">class="kw">double mu, class=class="str">"cmt">//mean parameter(μ) sig; class=class="str">"cmt">//variance parameter(σ) class=class="str">"cmt">//+------------------------------------------------------------------+
正态分布类的四个核心函数怎么写
在 MT5 里自己撸一个正态分布工具类,最实用的落点是把 pdf、cdf、invcdf、sf 四个函数一次写齐,后面做价格标准差通道或波动率带都能直接调。 构造函数默认 μ=0.0、σ=1.0,若 σ≤0 会弹 Alert 报警,避免后续算出负数尺度。pdf 里那个常数 0.398942280401432678 就是 1/√(2π),cdf 借 erfc 实现,系数 -0.707106781186547524 实为 -1/√2。 invcdf 做分位反查,要求 p 严格在 (0,1),否则报警;返回用 -1.41421356237309505(即 -√2)乘 sig 再接 inverfc(2p) 加 μ。sf 只是 1-cdf(x),即右尾生存概率。 下面这段是类内方法原文,逐行拆完你就能在 EA 里直接复用:构造函数置初值并校验 sig;pdf 返回某点的概率密度;cdf 返回累计概率;invcdf 由概率反推分位价;sf 返回超过 x 的概率。外汇与贵金属波动大,用这类分布假设前先想清楚尾部风险。
class="type">void CNormaldist() { mu=class="num">0.0;sig=class="num">1.0; class=class="str">"cmt">//class="kw">default parameters μ and σ if(sig<=class="num">0.) Alert("bad sig in Normal Distribution!"); } class="type">class="kw">double pdf(class="type">class="kw">double x) { class="kw">return(class="num">0.398942280401432678/sig)*exp(-class="num">0.5*pow((x-mu)/sig,class="num">2)); } class="type">class="kw">double cdf(class="type">class="kw">double x) { class="kw">return class="num">0.5*erfc(-class="num">0.707106781186547524*(x-mu)/sig); } 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 Normal Distribution!"); class="kw">return -class="num">1.41421356237309505*sig*inverfc(class="num">2.*p)+mu; } class="type">class="kw">double sf(class="type">class="kw">double x) { class="kw">return class="num">1-cdf(x); }
◍ 误差函数落在切比雪夫系数上
把高斯相关的统计计算搬进 MT5,绕不开误差函数 erf。上面这段把 28 阶切比雪夫逼近系数直接写死在数组里,首项是 -1.3026537197817094,末项量级已到 -2.8e-17,说明这套系数在双精度下把尾部误差压到了极低水平。 CErf() 构造函数只做一件事:把 28 个系数和阶数 Ncof=28 通过 setCErf 拷进类内数组 cof。setCErf 用 ArrayCopy 完成浅引用式的复制,ncof 记录阶数,后面 erf 调用时直接读这两样。 erf(double x) 本身不硬算积分,而是借 erfccheb 做互补误差函数的多项式逼近:x≥0 时返回 1.0-erfccheb(x),负数则对称处理成 erfccheb(-x)-1.0。你在 EA 里若要做正态分布概率换算,直接挂这个类、调 erf 即可,外汇与贵金属波动下用这类统计工具需意识到杠杆放大的高风险。 开 MT5 新建脚本把这段塞进自定义类,改 Ncof 或动前几项系数,能直观看到尾段概率估算偏移——参数敏感性比想象中集中在前 6 阶。
class="type">void CErf() { class="type">int Ncof=class="num">28; class="type">class="kw">double Cof[class="num">28]=class=class="str">"cmt">//Chebyshev coefficients { -class="num">1.3026537197817094,class="num">6.4196979235649026e-1, class="num">1.9476473204185836e-2,-class="num">9.561514786808631e-3,-class="num">9.46595344482036e-4, class="num">3.66839497852761e-4,class="num">4.2523324806907e-5,-class="num">2.0278578112534e-5, -class="num">1.624290004647e-6,class="num">1.303655835580e-6,class="num">1.5626441722e-8,-class="num">8.5238095915e-8, class="num">6.529054439e-9,class="num">5.059343495e-9,-class="num">9.91364156e-10,-class="num">2.27365122e-10, class="num">9.6467911e-11, class="num">2.394038e-12,-class="num">6.886027e-12,class="num">8.94487e-13, class="num">3.13092e-13, -class="num">1.12708e-13,class="num">3.81e-16,class="num">7.106e-15,-class="num">1.523e-15,-class="num">9.4e-17,class="num">1.21e-16,-class="num">2.8e-17 }; setCErf(Ncof,Cof); }; class="type">void setCErf(class="type">int Ncof,class="type">class="kw">double &Cof[]) { ncof=Ncof; ArrayCopy(cof,Cof); }; class="type">void ~CErf(){}; class="type">class="kw">double erf(class="type">class="kw">double x) { if(x>=class="num">0.0) class="kw">return class="num">1.0-erfccheb(x); else class="kw">return erfccheb(-x)-class="num">1.0; }