血液遗传优化算法(BIO)·进阶篇
「均线组合里 bTypes 的预置规则」
这段初始化逻辑在给一个 4×4 的均线排列矩阵 ma[i].pa[j].bTypes 填默认类型值,i、j 代表两条不同均线的相对位置序号(0~3),bTypes 数组存放该组合允许的边界类型编码(1~4)。 先看角上的对称项:ma[2].pa[0] 直接写死两个值 1 和 3,注释标了「1-4; 4-1」,说明第3条与第1条均线这种反向排列只认类型1和3;ma[0].pa[3] 与 ma[3].pa[0] 经 ArrayResize 扩到 2 位后填 2、3,对应「2-3; 3-2」的排列约束。 中间交叉项更密:ma[1].pa[2] 和 ma[2].pa[1] 都扩到 4 位,依次塞入 1、2、3、4,意味着这两条相邻均线互换位置时四种边界类型全放开;而 ma[1].pa[3]、ma[3].pa[1]、ma[2].pa[3]、ma[3].pa[2]、ma[3].pa[3] 这五项统一扩到 3 位并填 2、3、4,覆盖「2-4; 4-2; 3-4; 4-3; 4-4」所列组合,类型1被排除。 在 MT5 里把这段贴进 OnInit 前面跑一遍,用 ArraySize(ma[i].pa[j].bTypes) 打印,能直接核对每格长度是不是跟上面说的 2 或 3 或 4 对得上;外汇和贵金属波动大,这类硬编码矩阵改一位都可能让后续信号过滤逻辑偏掉,调之前先备份。
ma [class="num">2].pa [class="num">0].bTypes [class="num">0] = class="num">1; ma [class="num">2].pa [class="num">0].bTypes [class="num">1] = class="num">3; class=class="str">"cmt">//class="num">1-class="num">4; class="num">4-class="num">1 ArrayResize(ma [class="num">0].pa [class="num">3].bTypes, class="num">2); ArrayResize(ma [class="num">3].pa [class="num">0].bTypes, class="num">2); ma [class="num">0].pa [class="num">3].bTypes [class="num">0] = class="num">2; ma [class="num">0].pa [class="num">3].bTypes [class="num">1] = class="num">3; ma [class="num">3].pa [class="num">0].bTypes [class="num">0] = class="num">2; ma [class="num">3].pa [class="num">0].bTypes [class="num">1] = class="num">3; class=class="str">"cmt">//class="num">2-class="num">3; class="num">3-class="num">2 ArrayResize(ma [class="num">1].pa [class="num">2].bTypes, class="num">4); ArrayResize(ma [class="num">2].pa [class="num">1].bTypes, class="num">4); ma [class="num">1].pa [class="num">2].bTypes [class="num">0] = class="num">1; ma [class="num">1].pa [class="num">2].bTypes [class="num">1] = class="num">2; ma [class="num">1].pa [class="num">2].bTypes [class="num">2] = class="num">3; ma [class="num">1].pa [class="num">2].bTypes [class="num">3] = class="num">4; ma [class="num">2].pa [class="num">1].bTypes [class="num">0] = class="num">1; ma [class="num">2].pa [class="num">1].bTypes [class="num">1] = class="num">2; ma [class="num">2].pa [class="num">1].bTypes [class="num">2] = class="num">3; ma [class="num">2].pa [class="num">1].bTypes [class="num">3] = class="num">4; class=class="str">"cmt">//class="num">2-class="num">4; class="num">4-class="num">2; class="num">3-class="num">4; class="num">4-class="num">3; class="num">4-class="num">4 ArrayResize(ma [class="num">1].pa [class="num">3].bTypes, class="num">3); ArrayResize(ma [class="num">3].pa [class="num">1].bTypes, class="num">3); ArrayResize(ma [class="num">2].pa [class="num">3].bTypes, class="num">3); ArrayResize(ma [class="num">3].pa [class="num">2].bTypes, class="num">3); ArrayResize(ma [class="num">3].pa [class="num">3].bTypes, class="num">3); ma [class="num">1].pa [class="num">3].bTypes [class="num">0] = class="num">2; ma [class="num">1].pa [class="num">3].bTypes [class="num">1] = class="num">3; ma [class="num">1].pa [class="num">3].bTypes [class="num">2] = class="num">4; ma [class="num">3].pa [class="num">1].bTypes [class="num">0] = class="num">2; ma [class="num">3].pa [class="num">1].bTypes [class="num">1] = class="num">3; ma [class="num">3].pa [class="num">1].bTypes [class="num">2] = class="num">4; ma [class="num">2].pa [class="num">3].bTypes [class="num">0] = class="num">2; ma [class="num">2].pa [class="num">3].bTypes [class="num">1] = class="num">3; ma [class="num">2].pa [class="num">3].bTypes [class="num">2] = class="num">4; ma [class="num">3].pa [class="num">2].bTypes [class="num">0] = class="num">2; ma [class="num">3].pa [class="num">2].bTypes [class="num">1] = class="num">3; ma [class="num">3].pa [class="num">2].bTypes [class="num">2] = class="num">4; ma [class="num">3].pa [class="num">3].bTypes [class="num">0] = class="num">2; ma [class="num">3].pa [class="num">3].bTypes [class="num">1] = class="num">3;
遗传算子的移动与血型映射
C_AO_BIO::Moving() 负责种群迭代中的个体坐标更新。首次调用时 revision 为 false,会按 rangeMin/rangeMax/rangeStep 给全种群随机播种并置 revision=true 直接返回;之后每次进入才执行交叉与变异逻辑。 父代索引 papIND 与 mamIND 并非均匀抽取:先用 RNDprobab() 取 [0,1) 随机数再做平方,经 Scale 映射到 [0, popSize-1]。平方操作让小概率值更集中,倾向于选中排序靠前的个体,等价于一种隐式精英偏好。 每个坐标 c 先由 MathRand()%ArraySize 从双亲血型 bTypes 里挑出血型 cBloodType。若等于 1 则直接拷贝当前最优 cB[c];否则按 0.5 概率从父或母取坐标值,再走 GetBloodMutation 做变异并吸附到离散网格 SeInDiSp。 GetBloodType 用 ind%4 把个体映射为 1~4 四种血型,取模余数 0/1/2/3 分别对应返回 1/2/3/4,越界兜底返 1。外汇与贵金属市场高杠杆高风险,这类生物算子仅用于离线参数搜索,实盘前务必在 MT5 策略测试器用历史数据验证稳定性。 把下面代码贴进 MT5 的 EA 或指标类工程,断点跟一次 Moving() 就能看清平方抽样与血型分支的实际走向。
ma [class="num">3].pa [class="num">3].bTypes [class="num">2] = class="num">4; class="kw">return true; } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_BIO::Moving() { class=class="str">"cmt">//---------------------------------------------------------------------------- if (!revision) { for (class="type">int i = class="num">0; i < popSize; i++) { for (class="type">int j = class="num">0; j < coords; j++) { a [i].c [j] = u.RNDfromCI(rangeMin [j], rangeMax [j]); a [i].c [j] = u.SeInDiSp(a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]); } } revision = true; class="kw">return; } class=class="str">"cmt">//---------------------------------------------------------------------------- class="type">class="kw">double rnd = class="num">0.0; class="type">int papIND = class="num">0; class="type">int mamIND = class="num">0; class="type">int pBloodType = class="num">0; class="type">int mBloodType = class="num">0; class="type">int cBloodType = class="num">0; class="type">int bloodIND = class="num">0; for (class="type">int i = class="num">0; i < popSize; i++) { rnd = u.RNDprobab(); rnd *= rnd; papIND = (class="type">int)u.Scale(rnd, class="num">0.0, class="num">1.0, class="num">0, popSize - class="num">1); rnd = u.RNDprobab(); rnd *= rnd; mamIND = (class="type">int)u.Scale(rnd, class="num">0.0, class="num">1.0, class="num">0, popSize - class="num">1); pBloodType = GetBloodType(papIND); mBloodType = GetBloodType(mamIND); for (class="type">int c = class="num">0; c < coords; c++) { bloodIND = MathRand() % ArraySize(ma [mBloodType - class="num">1].pa [pBloodType - class="num">1].bTypes); cBloodType = ma [mBloodType - class="num">1].pa [pBloodType - class="num">1].bTypes [bloodIND]; if (cBloodType == class="num">1) a [i].c [c] = cB [c]; else { if (u.RNDbool() < class="num">0.5) a [i].c [c] = p [papIND].c [c]; else a [i].c [c] = p [mamIND].c [c]; GetBloodMutation(a [i].c [c], c, cBloodType); a [i].c [c] = u.SeInDiSp(a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">int C_AO_BIO::GetBloodType(class="type">int ind) { if (ind % class="num">4 == class="num">0) class="kw">return class="num">1; if (ind % class="num">4 == class="num">1) class="kw">return class="num">2; if (ind % class="num">4 == class="num">2) class="kw">return class="num">3; if (ind % class="num">4 == class="num">3) class="kw">return class="num">4; class="kw">return class="num">1; }
◍ 变异与修订里的基因操作细节
这段 C_AO_BIO 类的两个方法,把遗传算法的「变异」和「代际修订」落到了具体代码层。GetBloodMutation 按血型分支处理基因:血型2走幂分布重采样,血型3向当前最优基因做概率偏移,默认分支则是区间内镜像翻转。 看血型2那行,PowerDistribution 的第四个参数写死 20,意味着幂分布的指数被固定。你想调变异激进程度,改这个 20 比改范围更直接,MT5 里编译后跑不同指数对比收敛速度即可。 Revision 先扫一遍种群,若某个体适应度 a[i].f 大于记录值 fB,就更新全局最优并拷贝其基因数组 cB。随后把原种群塞进 p 的后半段,调用 Sorting 做规模翻倍后的排序筛选,典型精英保留思路。外汇与贵金属市场高波动、高风险,这类优化仅用于参数搜索,不代表任何收益保证。
class="type">void C_AO_BIO::GetBloodMutation(class="type">class="kw">double &gene, class="type">int indGene, class="type">int bloodType) { class="kw">switch (bloodType) { case class="num">2: gene = u.PowerDistribution(gene, rangeMin [indGene], rangeMax [indGene], class="num">20); class="kw">return; case class="num">3: gene += (cB [indGene] - gene) * u.RNDprobab(); class="kw">return; class="kw">default: { gene = rangeMax [indGene] - (gene - rangeMin [indGene]); } } } class="type">void C_AO_BIO::Revision() { for (class="type">int i = class="num">0; i < popSize; i++) { if (a [i].f > fB) { fB = a [i].f; ArrayCopy(cB, a [i].c, class="num">0, class="num">0, WHOLE_ARRAY); } } for (class="type">int i = class="num">0; i < popSize; i++) { p [popSize + i] = a [i]; } S_AO_Agent pT []; ArrayResize(pT, popSize * class="num">2); u.Sorting(p, pT, popSize * class="num">2); }
「BIO 跑分实测与低维陷阱」
把 BIO(血液遗传优化)扔进 Hilly、Forest、Megacity 三套测试函数里,维度分别取 5×2、25×2、500×2,每个配置都硬跑 10000 次目标函数评估。汇总得分 4.84175,换算百分比 53.80%,在 38 种种群优化算法里排第 20,正好卡在平均线——对新方法而言不算丢人。 拆开看单项:Hilly 的 5/25/500 维得分 0.81568 / 0.65336 / 0.30877,Forest 同维度 0.89937 / 0.65319 / 0.21760,Megacity 则是 0.67846 / 0.47631 / 0.13902。规律很直白——维度越高,BIO 的收敛质量掉得越狠,500 维几乎腰斩到 0.1~0.3 区间。 可视化过程暴露出一个老毛病:低维(5 维)时 BIO 容易陷局部最优。这不是它独有的,种群类算法通病,但实盘调参时得留神——别看小维度回测漂亮就直接上。 横向比对榜单头部,ANS 跨邻域搜索总分 6.134(68.15%)领跑,CLA、AMOm 紧追;BIO 后面还有 BSA、HS 等被拉开 1~2 分。想验证这套排名,自己把测试函数维度拉到 500 跑一遍 10000 次评估,看 Megacity 是不是也掉到 0.14 附近。
优化器横向跑分里垫底与拔尖的差距
把 39~45 号智能优化器连同随机游走(RW)放进同一组基准测试,综合得分一栏拉出来看:ASHA 拿到 3.664(折算 40.71%),RW 只有 2.348(26.09%),两者差了约 1.316 分,优化器选错可能直接吃掉三成以上的寻优效率。 细分维度上,ASHA 在第三列目标值 0.19160、第五列 0.47692 都明显优于 RW 的 0.15877 和 0.27969;而 MEC、CSA 这类算法在第七列能压到 0.07198、0.20525,说明不同优化器擅长的误差面不一样,不是分数高就全场景通吃。 外汇与贵金属策略调参属于高风险活动,回测里的优化器优势不等于实盘稳健,MT5 里用框架自带优化跑一遍上述算法对比,重点看你自己品种在第七列(最小误差)的表现再定。
◍ 记住这一条就够了
BIO 基础版在当前种群算法横评里拿到约 54% 的得分(满分 100 为理论最优),说明用血型隐喻搭参数继承结构这条路本身走得通,而不是靠某个特定变异算子撑着。 低维函数上它容易卡在局部最优,这点已经由测试台脚本实证;真要落地到 MT5 优化器,优先换掉对应血型的变异算子或加新的解空间探索逻辑,比调外部参数更管用。 最有油水的地方在自适应化——让每种血型的算子随目标函数特性动态切换,外汇与贵金属参数寻优高风险,回测顺手不代表实盘概率占优,开 MT5 跑一遍 Test_AO_BIO 才有发言权。