针对交易的组合数学和概率论(第三部分):第一个数学模型·进阶篇
(2/3)· 对称只是特例,嵌套才是分形的真骨架,本篇把数学原型一路推到可测代码
不少交易者把对称分形的平均步阶公式当万能钥匙,遇到非对称走廊就手动拍脑袋调参。事实上对称只是 K=n=m 的特例,真正能覆盖实盘价格走廊的是嵌套乘积属性 S(a*k,b*k)=S(a,b)*S(k,k)。
「用随机撒点替原型找最佳系数」
给一个函数原型找系数,最省事也最通用的做法不是梯度下降,而是在允许范围内直接随机生成候选值,再比对哪一组和真实行情贴合得最好。这套思路不挑原型:只要把目标函数换成另一个数学表达式,同样的搜索框架还能接着用。 之所以选随机搜索而不是解析法,是因为它几乎零假设——不需要目标函数可导,也不依赖初值。代价是计算量随维度上升很快,但在 MT5 里跑离线优化,几十万次试算也就是几秒的事。 后续章节会复用同一套规划去处理另一个原型,所以这一节的接口设计要留好扩展性:输入是数据缓冲区和系数边界,输出是最佳匹配参数。
◍ 拆解边界跨越的步阶期望
把平均步阶数摊开看,公式本质是在算价格碰到任一边界前的期望步数。向上和向下步阶被默认等价,但这只在纯随机游走里成立;真实行情是非对称分形,必须额外判定上下边界各自的真实构成。 上边界相关值可写成 SP[U,ud] = (S[U,u] + S[U,d]) * P[U] = S[U] * P[U],其中 S[U] 是链首触上边界时的平均总步阶,P[U] 是首触上边界的概率。下边界镜像对称:SP[D,ud] = S[D] * P[D]。两者相加即总平均步阶 S,说明整个系统只由五个基础值驱动。 这五个基础值是 S[U,u]、S[U,d]、S[D,u]、S[D,d] 与 P[U],其余如 P[D]=1-P[U]、S=SP[U,ud]+SP[D,ud] 都是派生。上一篇文章给出的分形输出 SP[U,u]、SP[U,d]、SP[D,u]、SP[D,d]、P[U]、P[D]、S 正是一组可由基础值反推的单独元素。 做 MT5 验证时,建议先写脚本打印这七个输出值,再除回 P[U] 或 P[D] 还原基础值;外汇与贵金属波动有跳空,非对称程度可能偏离随机假设,高杠杆下误用对称假设会放大风险。
分形走廊步阶的数学闭合方程
在对称边界的分形模型里,上半走廊步阶数 n 与下半走廊步阶数 m 共同决定了跨越边界时的平均步阶结构。对 10 组测试数据做统计后,一个稳定现象是:上边界平均上升步阶数减去平均下降步阶数刚好等于 n,下边界平均下降步阶数减去平均上升步阶数刚好等于 m,即 S[U,u] – S[U,d] = n、S[D,d] – S[D,u] = m。 以测试索引 3 为例(n=3, m=3, p=0.5),跨越上边界概率 P(U)=0.5 时,S[U,u]=6、S[U,d]=3,差值确为 3;S[D,d]=6、S[D,u]=3,差值也为 3。这种成对小数部分相同的结构,说明边界距离直接映射进条件期望。 仅靠这两个关系不够。引入无限实验下的频率极限,上升与下降步阶出现比倾向与概率 p 成正比,可推出 S[UD,u]=S*p、S[UD,d]=S*(1-p),再展开成含边界概率 P[U] 的两个混合方程。加上由极限商得到的第五式 P[U]*n – (1-P[U])*m = 2*p*S – S,便凑足五个方程解五个未知量。 顺次消元后,方程 3 与方程 4 相减会退化为那条直观极限式,单靠原四未知量方程组仍解不出全部值。后来补了一个由分形日志数据反推的额外公式,才把剩余四个量全部锁定。外汇与贵金属市场下这类边界模型仅描述概率结构,实盘应用属高风险,须用 MT5 自带分形指标做样本核对。
「逆推全部状态变量的闭合解」
已知 S[U,u] 后,整张状态表可以逆推出来。第一步用 S[U,d] = S[U,u] – n 直接拿到上边界下行步阶均值,再把这两个值代入方程 3、4 解出 S[D,u] 与 S[D,d]。其中 S[D,u] 的显式形式是 ( S*p – S[U,u] * P[U] ) / ( 1 – P[U] ),最后一个未知量 S[D,d] 代入后化为 ( S*(1-p) - ( S[U,u] – n ) * P[U] ) / ( 1 – P[U] )。 唯一还缺的是上边界穿越概率 P[U]。从第五个方程解起:P[U] * n – (1 - P[U]) * m = 2*p*S – S,整理后得到 P[U] = ( 2 * p * S – S + m ) / (n + m)。这里的 n 是到上边界步阶数,m 是到下边界步阶数,p 为初始向上概率,S[U,u] 是越上界时平均上升步阶数。 S[U,u] 本身可用 Ss[m,n,p] = (n ^ Pn[p]) * ( m ^ Pm[p] ) 计算,其中 p>=0.5 时 Pn[p]=1、否则 Pn[p]=((1-p)/0.5)^K;p<=0.5 时 Pm[p]=1、否则 Pm[p]=(p/0.5)^K。K 控制调节函数平坦度,稍后单独程序拟合。 对称边界且 p=0.5 时我推导出 S[U,u] = Summ[i=0,n](i),S[U,d] = Summ[i=0,n](i-1),但这套只覆盖 p=0.5 且 n==m。表格测试结果显示 m>n 时公式对 S[U,u]、S[U,d] 有效;若 m<n 则要对 S[D,d]、S[D,u] 做镜像,用 S[D,u] = S[D,d] – m 起手,再代回方程 3、4 求另一组。 推广到非对称边界后,m>n 且 p=0.5 时 S[U,u] = Summ[i=0…n](i) + (m-1)/3;反过来 m<n 时 S[D,d] = Summ[i=0…m](i) + (n-1)/3。对称标准情形仍是最简:m=n=p=0.5 时 S[U,u] 与 S[D,d] 都等于 Summ[i=0…n](i)。外汇与贵金属价格序列具高风险,这些闭合解仅描述数学模型均值,实盘路径可能显著偏离。
◍ 用幂函数把概率轴钉死在三个基准点
把上行结构 S[U,u] 与下行结构 S[D,d] 统一进一个可调原型,核心就是给原函数套一层幂次 K(p)、K(q):Sp[U,u]=S[U,u]^K(p),Sp[D,d]=S[D,d]^K(q),且 q=1-p。 镜像关系让下行不必独立验证——S[D,d](n,m,p)=S[U,u](m,n,p-1),所以测试程序只需比对 S(n,m,p) 与 S[U,u](n,m,p) 两类原型。 在相同随机权重与幂系数组合下,更简单原型在同等搜索周期内曲线更干净,复杂原型并非必要。 K(p) 的内部必须在 p=0、0.5、1 三处强制重合已知值:p=0.5 时 K=1(幂次不动,退化为原结构);p=1 时 K=Log[S[U,u]→n](把值抬到 n);p=0 时 K=-∞(结果压到 0,意味着 p、q 必须进分母,除以零才出负无穷)。 下行 q 轴三点是对称副本。 这三个硬约束比任何拟合口号都重要,读者开 MT5 写个散点打印就能核对。
| 我落地的原型长这样:K(p)=1+D·Σ(Kn[i]· | ((p-0.5)/(0.5p)) | ^A[i]),权重和 Kn[0]+…+Kn[N]=Log[S[U,u]→n]-1,D 是符号因子 (((p-0.5)/(0.5p))/其绝对值)。 K(q) 同构,换 Km、B 与 C。 当 p=0.5,分式为零,累加消失只剩 1;当 p=1,分式绝对值变 1,累加和正好补出对数偏移,模数写法只为避复数值、符号交给 D 单独扛。 |
|---|
这套插值思路类似拉格朗日多项式但为任务定制,好处是边界点零自由量。 要从曲线族里捞出目标函数,实际只需求出 Kn[] 与 Km[] 两组数——外汇与贵金属波动里套用此模型仍属高风险,参数失配会快速放大偏差。
把分形公式塞进 MT5 结构体和辅助函数
分形数学模型的落地,核心是一个承载主函数全部返回值的容器结构,其余都是辅助计算。下面这段结构体把平均步数、上下边界触达概率、以及条件期望步数全部字段化,开 MT5 新建一个 include 头文件就能直接粘贴。 验证环节我用外部数学软件做了矩阵比对:当 n、m 控制在中等规模时,模拟返回与模型理论值基本一致;但随着 n、m 增大,因算力限制只能截断模拟深度,微小偏差会放大,这点在调参时要留余地。外汇与贵金属价格路径受突发流动性影响,此类随机游走模型仅描述概率倾向,实盘高风险。 辅助函数里 Ss() 用幂次逼近任意边界的平均步数原型,Log() 补了任意底对数,SUu() 和 SDd() 则处理 p=0.5 的对称特例——注意 m>n 时多了 (m-1)/3.0 的修正项,这是边界非对称带来的偏移。复制下面代码到 MT5 编译,改 n、m 跑几组就能看到结构体字段怎么填。
class="kw">struct MathModel1class=class="str">"cmt">//structure for the first mathematical model { class="type">class="kw">double S;class=class="str">"cmt">//the average number of any steps class="type">class="kw">double pU;class=class="str">"cmt">//the probability that the price will first reach the upper border class="type">class="kw">double pD;class=class="str">"cmt">//the probability that the price will first reach the lower border class="type">class="kw">double SUu;class=class="str">"cmt">//the average number of steps up if the price first reaches the upper border class="type">class="kw">double SUd;class=class="str">"cmt">//the average number of steps down if the price first reaches the upper border class="type">class="kw">double SDu;class=class="str">"cmt">//the average number of steps up if the price first reaches the lower border class="type">class="kw">double SDd;class=class="str">"cmt">//the average number of steps down if the price first reaches the lower border class="type">class="kw">double SPUu;class=class="str">"cmt">//the average probable number of steps up if the price first reaches the upper border class="type">class="kw">double SPUd;class=class="str">"cmt">//the average probable number of steps down if the price first reaches the upper border class="type">class="kw">double SPDu;class=class="str">"cmt">//the average probable number of steps up if the price first reaches the lower border class="type">class="kw">double SPDd;class=class="str">"cmt">//the average probable number of steps down if the price first reaches the lower border class="type">class="kw">double SPUud;class=class="str">"cmt">//the average probable number of steps in any direction if the price first reaches the upper border class="type">class="kw">double SPDud;class=class="str">"cmt">//the average probable number of steps in any direction if the price first reaches the lower border class="type">class="kw">double SUDu;class=class="str">"cmt">//the average number of steps up when reaching any of the borders class="type">class="kw">double SUDd;class=class="str">"cmt">//the average number of steps down when reaching any of the borders }; class="type">class="kw">double Ss(class="type">int n, class="type">int m,class="type">class="kw">double p, class="type">class="kw">double K)class=class="str">"cmt">//prototype of the function of the average number of steps in any direction when reaching any border { if (p>=class="num">0.5) class="kw">return n*MathPow(m,MathPow((class="num">1-p)/class="num">0.5,K)); else class="kw">return MathPow(n,MathPow(p/class="num">0.5,K))*m; } class="type">class="kw">double Log(class="type">class="kw">double a, class="type">class="kw">double b)class=class="str">"cmt">//logarithm function for any base { if (MathLog(a) != class="num">0) class="kw">return MathLog(b)/MathLog(a); else class="kw">return class="num">0.0; } class="type">class="kw">double SUu(class="type">int n,class="type">int m)class=class="str">"cmt">//average number of steps up to the upper border if p=class="num">0.5 { class="type">class="kw">double rez=class="num">0.0; if (m>n) { for (class="type">int i=class="num">0;i<=n;i++) rez+=class="type">class="kw">double(i); rez+=(m-class="num">1)/class="num">3.0; } if (m==n) for (class="type">int i=class="num">0;i<=n;i++) rez+=class="type">class="kw">double(i); class="kw">return rez; } class="type">class="kw">double SDd(class="type">int n,class="type">int m)class=class="str">"cmt">//average number of steps down to the lower border if p=class="num">0.5 { class="type">class="kw">double rez=class="num">0.0; if (n>m) { for (class="type">int i=class="num">0;i<=m;i++) rez+=class="type">class="kw">double(i); rez+=(n-class="num">1)/class="num">3.0; }