神经类群优化算法 (NOA)(基础篇)
「NOA 神经类群优化在 MT5 测试器里的落地」
神经类群优化算法(NOA)是一套把类群智能与神经网络权重更新揉在一起的寻优思路,在 MetaTrader 5 的策略测试器里可以直接挂作自定义优化模式。它不像遗传算法只靠交叉变异,而是用类群个体携带的「神经态」去逼近参数高原,对多峰、带噪的目标函数更不容易卡在局部极值。 从社区实测记录看,Andrey Dik 在 2026 年 2 月 3 日发布的 NOA 测试帖,截至发稿被查看 381 次、零回复,说明这套玩法在零售交易者里还远没铺开。你可以在 MT5 的「测试器 → 优化 → 自定义算法」里找同名接入点,先拿一个 3 参数 EA 跑一轮对照遗传算法。 外汇与贵金属品种参数面受点差和滑点扰动极大,NOA 给出的「较优解」只是历史样本下的概率倾向,实盘前务必用不同年份数据重跑验证。
◍ 极简神经代理怎么凑出解空间搜索力
做优化算法最怕堆复杂度,但作者反其道:用一群极简单的神经代理(neuroboids)去碰复杂问题的解空间。每个代理只是一个双层神经网络,拿 Adam 训,单看一个几乎干不了什么。 关键点在于集体行为——个体规则极基础,靠相互作用自发探索。这思路借的是自然系统自组织:简单单元跑基本规则,能涌现出适应性结构。 原文作者在标准优化测试函数上做了实验来验证效率,理论上这种「弱个体+强群体」结构,对 MT5 里参数寻优类 EA 的搜索路径设计有可直接参照的价值。外汇与贵金属市场高风险,任何算法只降低盲目性,不消除亏损概率。
用数字小虫拼出自组织优化器
神经类群(Neuroboid)把神经网络和 boid 集体行为揉在一起:每个代理只是解空间里的一个坐标点,带着一个输入/输出维数等于问题维度的微型神经网络。它没有中央指挥官,代理们靠试错学怎么靠近当前最优解(金色圆圈),又保留自己的移动个性,表面乱序实则自组织收敛。 和传统遗传算法不同,NOA 里代理行为不是固定规则,而是各自网络在优化中训练出来的。训练信号就是已找到的最佳解 cB,用 ADAM 反向传播调权重,于是种群里总权重数可以远大于问题维度,且不用手动去协调整个种群——这事自动发生。 技术骨架可以提炼成四条:前向+反向误差传播贯穿优化、多个网络分布式各练各的策略、按当前搜索状态自适应调行为、用激活函数造非线性搜索动力学。它不是拿神经网络近似目标函数,而是拿网络间接拽着搜索方向走,算一种元学习式混合范式。 代码层面,C_AO_NOA 类从 C_AO 派生。构造函数先把 popSize 设 50、actFunc 设 0、dispScale 设 0.01,析构时逐个 delete 掉 nn[i].actFunc 防内存漏。下面这段是类头加构造/析构的骨架,能直接塞进 MT5 的 EA 或指标工程里改。
class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class C_AO_NOA : class="kw">public C_AO { class="kw">public: class=class="str">"cmt">//-------------------------------------------------------------------- ~C_AO_NOA() { for (class="type">int i = class="num">0; i < ArraySize(nn); i++) if (CheckPointer(nn [i].actFunc)) class="kw">delete nn [i].actFunc; } C_AO_NOA() { ao_name = "NOA"; ao_desc = "Neuroboids Optimization Algorithm(joo)"; ao_link = "[MQL5官方文档] popSize = class="num">50; class=class="str">"cmt">// population size actFunc = class="num">0; class=class="str">"cmt">// neuron activation function dispScale = class="num">0.01; class=class="str">"cmt">// scale of movements
「把参数塞回神经网与初始化边界」
eliteProb 默认给到 0.1,意思是每代有 10% 的概率直接复制精英坐标,而不是靠扰动探索。这个数值写在参数数组第 3 号位,和 popSize、actFunc、dispScale 一起通过 ArrayResize(params,4) 固定下来,方便外部面板调参。 SetParams() 干的是反向活:把 params[] 里的值按索引读回 popSize、actFunc、dispScale、eliteProb。你在 MT5 里改了输入框的数值,靠这个函数落地到类成员变量,否则优化器跑的还是旧配置。 Init() 先调 StandardInit 做范围校验,失败直接返 false。随后建了一个长度为 2 的 nnConf 数组,两个元素都填 coords(即维度数量),等于给底层每个 MLP 网络配了「输入=输出=坐标维度」的对称结构。 激活函数枚举从 eActTanh 起步编号为 0,说明默认隐藏层走 tanh。外汇与贵金属杠杆高、滑点跳空频繁,这类基于群体的坐标搜索在外盘实盘前务必用历史数据回测验证稳定性。
eliteProb = class="num">0.1; class=class="str">"cmt">// probability of copying elite coordinates ArrayResize(params, class="num">4); params [class="num">0].name = "popSize"; params [class="num">0].val = popSize; params [class="num">1].name = "actFunc"; params [class="num">1].val = actFunc; params [class="num">2].name = "dispScale"; params [class="num">2].val = dispScale; params [class="num">3].name = "eliteProb"; params [class="num">3].val = eliteProb; } class="type">void SetParams() { popSize = (class="type">int)params [class="num">0].val; actFunc = (class="type">int)params [class="num">1].val; dispScale = params [class="num">2].val; eliteProb = params [class="num">3].val; } class="type">bool Init(const class="type">class="kw">double &rangeMinP [], class=class="str">"cmt">// minimum values const class="type">class="kw">double &rangeMaxP [], class=class="str">"cmt">// maximum values const class="type">class="kw">double &rangeStepP [], class=class="str">"cmt">// step change const class="type">int epochsP = class="num">0); class=class="str">"cmt">// number of epochs class="type">void Moving(); class="type">void Revision(); class=class="str">"cmt">//---------------------------------------------------------------------------- class="type">int actFunc; class=class="str">"cmt">// neuron activation function class="type">class="kw">double dispScale; class=class="str">"cmt">// scale of movements class="type">class="kw">double eliteProb; class=class="str">"cmt">// probability of copying elite coordinates class="kw">private: class=class="str">"cmt">//------------------------------------------------------------------- C_MLPa nn []; class="type">void ScaleInp(class="type">class="kw">double &inp [], class="type">class="kw">double &out []); class="type">void ScaleOut(class="type">class="kw">double &inp [], class="type">class="kw">double &out []); }; class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">bool C_AO_NOA::Init(const class="type">class="kw">double &rangeMinP [], class=class="str">"cmt">// minimum values const class="type">class="kw">double &rangeMaxP [], class=class="str">"cmt">// maximum values const class="type">class="kw">double &rangeStepP [], class=class="str">"cmt">// step change const class="type">int epochsP = class="num">0) class=class="str">"cmt">// number of epochs { if (!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) class="kw">return class="kw">false; class=class="str">"cmt">//---------------------------------------------------------------------------- class="type">int nnConf []; ArrayResize(nnConf, class="num">2); nnConf [class="num">0] = coords; nnConf [class="num">1] = coords; enum E_Act { eActTanh, class=class="str">"cmt">//class="num">0