群体优化算法:智能水滴(IWD)算法(基础篇)
◍ 用智能水滴算法给 MT5 找最优参数
智能水滴(IWD)属于群体优化算法的一类,思路是模拟水滴在河道中流动时「冲刷—沉积」的路径选择,用大量虚拟水滴的集体行为去逼近问题的较优解。在交易系统里,它常被拿来做参数寻优,替代网格遍历那种指数级爆炸的暴力搜索。 原文给出的示例发布于 2024 年 6 月 26 日,运行环境为 MetaTrader 5,截至记录时该示例获得 1 381 次查看与 6 条讨论。这个量级说明它更偏工程验证性质,不是被大规模实盘验证过的成熟方案。 该篇结构分为四块:概述、算法、修改版 SDSm、测试结果。想验证的人可以直接在 MT5 里新建 EA 骨架,把 IWD 的种群规模先设到 50~100 之间跑一轮,外汇与贵金属杠杆高、滑点随机,这类优化结果仅代表历史样本倾向,实盘存在显著回撤风险。
智能水滴怎么借河床找路
智能水滴优化(IWD)属于群体智能里的构造型算法,2007 年由伊朗科学家 Hamed Shah-Hosseini 提出,核心是把自然河流的侵蚀与沉积过程抽象成数学规则。河床在流水长期作用下不断改变起伏,弯道处冲刷出新路、留下牛轭湖,这种‘阻力最小处优先通行’的现象,正是算法要模拟的对象。 在 IWD 里,每一滴人造水滴对应一个候选解,水滴在搜索空间移动时会改变所在路径的‘土壤量’(即阻力参数),多滴之间靠共享环境状态逐步收敛到更优路径。它和蚁群、粒子群一样是群体算法,但偏重构造式逐步建解,而非在连续空间里漂移。 对外汇与贵金属交易者来说,这类元启发式可用于参数寻优,但实盘环境高波动、高杠杆,任何回测优解都只是概率倾向,不能直接当信号用。
「把河谷搬进优化器:IWD 水滴怎么挑路」
智能水滴(IWD)原本是找最短路径的算法:一滴水带走的泥土量跟自身速度挂钩,速度越快、清走的土越多,后方水滴就越通畅。最优路径就是土最少、速度能拉满的那条——自然河流也是这么被水滴群冲出来的,硬土段阻力大、软土段好走,土会从急流搬去缓流处沉淀。 直接套用原版 IWD 解不了我们关心的优化问题。原开发者拿它跑图搜索、旅行商,群体优化文章里的变种才能啃任意问题。关键改造是:不让土壤等价于适应度(像蚁群拿信息素当适应度那样),而是把坐标切成等宽区段,像把河谷划格子。某滴位置改善,就按它前后两次适应度的差值(最大减最小)去减相应区段的土量。 移动时,水滴按概率挑土最少的区段落下,概率与该区段土壤量成正比;选中后新坐标服从二次分布——离旧坐标近的命中率高,远的概率低,区段宽度由外部参数控。全局“河床”库存每个区段的最深坐标,迭代伪代码就是:随机生成水滴→算适应度→更新全局最佳→算高度变化→按区段刷土→概率选区段滴落→再算适应度,循环。 速度增量公式是 vel = vel(t-1) + Av / [Bv + Cv * soil^2(i,j)],土少的边速度涨得猛;过河时间 time(i,j,vel) = R / vel,带走的土 dSoil = As / [Bs + Cs * time],新土量 soil(i+1,j+1) = Po*soil(i,j) + Pn*dSoil(i,j)。这些非线性比例决定了水滴偏爱低土路径。 代码里 S_Drop 是单滴的载体:Init 按坐标数 coords 重分配 c 与 rSection 数组,适应度 f、fPrev 都预置为 -DBL_MAX,altChange 清零。rSection 记该坐标落在哪个区段,c 存实际坐标,后面 Moving 和 Revision 就靠这套结构刷河床。
class="kw">struct S_Drop { class="type">void Init(class="type">int coords) { ArrayResize(c, coords); ArrayResize(rSection, coords); f = -DBL_MAX; fPrev = -DBL_MAX; altChange = class="num">0.0; } class="type">class="kw">double c []; class=class="str">"cmt">//coordinates class="type">int rSection []; class=class="str">"cmt">//river section(number of cells: number of coordinates, cell value: sector index on the coordinate)
◍ 水滴寻优的类结构怎么搭
上面这段把智能水滴(IWD)算法的核心载体直接写成了一个 C_AO_IWDm 类,对外暴露的接口只有 Init、Moving、Revision 三个方法,其余全是私有成员。也就是说,你要在 MT5 里跑这套仿生寻优,真正需要外部调用的就这三步:初始化参数、让水滴移动、做迭代修正。 类里 public 区的 cB 和 fB 分别存「历史最优坐标」和「对应适应度」,p[] 是水滴群体,rangeMax/rangeMin/rangeStep 框定了每个维度的搜索上下界与步长。注意原文把 rangeMin 的注释拼错成 manimum,复制代码时别照抄这个笔误。 私有成员里 waterViscosity 要求 >= 1,这是算法里控制水滴「记忆衰减」的黏度系数;sectorsNumber 与 rb[](河床结构)配合,把搜索空间切成若干扇区并记录河床深度。Init 的入参固定为 coordsP(维度)、popSizeP(种群数)、sectorsNumberP(扇区数)、waterViscosityP(黏度),改这几个数就能直接调搜索粒度。 开 MT5 新建 EA 把这段类声明贴进头文件,先拿 coords=2、popSize=10、sectorsNumber=5、waterViscosity=1.0 做最小验证,能编译通过再扩维度。外汇与贵金属品种波动随机性强,这类优化仅用于参数探索,实盘前务必小资金验证风险。
class="type">class="kw">double f; class=class="str">"cmt">//fitness class="type">class="kw">double fPrev; class=class="str">"cmt">//previous fitness class="type">class="kw">double altChange; class=class="str">"cmt">//change in altitude }; class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="kw">struct S_Riverbed class=class="str">"cmt">//riverbed { class="type">class="kw">double riverbedDepth []; class=class="str">"cmt">//riverbed depth class="type">class="kw">double coordOnSector []; class=class="str">"cmt">//coordinate on the sector }; class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class C_AO_IWDm { class=class="str">"cmt">//---------------------------------------------------------------------------- class="kw">public: class="type">class="kw">double cB []; class=class="str">"cmt">//best coordinates class="kw">public: class="type">class="kw">double fB; class=class="str">"cmt">//FF of the best coordinates class="kw">public: S_Drop p []; class=class="str">"cmt">//particles(drops) class="kw">public: class="type">class="kw">double rangeMax []; class=class="str">"cmt">//maximum search range class="kw">public: class="type">class="kw">double rangeMin []; class=class="str">"cmt">//manimum search range class="kw">public: class="type">class="kw">double rangeStep []; class=class="str">"cmt">//step search class="kw">public: class="type">void Init(const class="type">int coordsP, class=class="str">"cmt">//coordinates number const class="type">int popSizeP, class=class="str">"cmt">//population size const class="type">int sectorsNumberP, class=class="str">"cmt">//sectors number const class="type">class="kw">double waterViscosityP); class=class="str">"cmt">//water viscosity(>= class="num">1) class="kw">public: class="type">void Moving(); class="kw">public: class="type">void Revision(); class=class="str">"cmt">//---------------------------------------------------------------------------- class="kw">private: class="type">int coords; class=class="str">"cmt">//coordinates number class="kw">private: class="type">int popSize; class=class="str">"cmt">//population size class="kw">private: class="type">int sectorsNumber; class=class="str">"cmt">//sectors number class="kw">private: class="type">class="kw">double waterViscosity; class=class="str">"cmt">//water viscosity class="kw">private: class="type">class="kw">double sectorSpace []; class=class="str">"cmt">//sector space class="kw">private: S_Riverbed rb []; class=class="str">"cmt">//riverbed class="kw">private: class="type">int iterationCounter; class="kw">private: class="type">class="kw">double SeInDiSp(class="type">class="kw">double In, class="type">class="kw">double InMin, class="type">class="kw">double InMax, class="type">class="kw">double Step); class="kw">private: class="type">class="kw">double RNDfromCI(class="type">class="kw">double min, class="type">class="kw">double max); class="kw">private: class="type">class="kw">double Scale(class="type">class="kw">double In, class="type">class="kw">double InMIN, class="type">class="kw">double InMAX, class="type">class="kw">double OutMIN, class="type">class="kw">double OutMAX, class="type">bool revers); };