人工喷淋算法(ASHA)(基础篇)
📘

人工喷淋算法(ASHA)(基础篇)

第 1/2 篇

◍ ASHA 喷淋算法在 MT5 的落地与测试

人工喷淋算法(ASHA)是一套跑在 MetaTrader 5 上的自动化逻辑,由 Andrey Dik 在 2025 年 7 月 21 日 07:59 发布至社区,截至统计时点获得 777 次查看、0 条评论,说明该策略当前曝光有限、讨论度极低。 算法核心分两层:概述层定义喷淋式挂单与动态间距,实现层负责在 MT5 中按波动率调整下单密度。外汇与贵金属品种波动大,ASHA 这类密集挂单逻辑在极端行情中可能放大滑点与穿仓风险,实盘前务必用策略测试器跑历史数据。 已公开的测试结果显示,该算法在标准账户样本中存在成交集中、回撤陡增的现象,但原文未给具体收益率或最大回撤数值,因此任何盈利预期都只是概率层面的推测。

「元启发式为什么适合盯盘优化」

数据量膨胀让传统数值方法在 MT5 策略调参里越来越吃力。元启发式算法靠高收敛速度和并行处理,把从金融行情拟合到科研寻优的耗时压下来,这是它打开局面的根本原因。 在严格时间窗里,每一根 tick 都可能改变信号,元启发式能在有限步内吐出比穷举更好的解。对比传统方法,它处理大样本快,且解质量往往更高,这对日内高频过滤噪音有直接价值。 算力和内存受限时(比如云 VPS 跑多品种),这类算法更省资源。对动态报价系统的适应性,让模型在新数据进来时快速重算,保持信号不过期。

  • 年 Ali J. 等人提出的 ASHA(人工淋浴算法)就是典型一例:它把搜索空间当田野、资源单位当水,用流动与积聚模拟寻优,属于受自然启发的无约束优化思路,后面可直接在 MT5 里做基准对照。

人工淋浴算法在 MT5 里的代理结构与移动逻辑

ASHA(人工淋浴算法)把优化问题当成一块有坡度的田:每个水单元是一个搜索代理,目标函数越低代表地势越低。它靠几个参数控场——F 是水流量(步长),δ 是渗透阈值,β 是概率变化率,ρ₀ 是初始下坡概率。核心矛盾是 ρ 随迭代从 max((M-K)/(β×M), ρ₀) 衰减:前期乱流探底,后期被全局最低点吸住。 标准 S_AO_Agent 结构被扩了字段:c[] 当前坐标、cP[] 上次坐标、cB[] 历史最佳坐标,f/fP/fB 分别对应这三者的适应度,cnt 记代理「卡住」次数。Init() 按坐标数分配这三组数组,并把 f 系值先置到最小可能,保证首次评估必更新。这个结构改动不破坏旧算法,只是给新算法铺路。 C_AO_ASHA 类从 C_AO 继承,私有里塞了 aT[] 临时种群、epochs 总代数、epochNow 当前代。Init() 先调 StandardInit 做参数体检,再把 epochs 设为传入值、epochNow 归零、给 aT 留内存,全过才返 true。 Moving() 是重头戏:首轮用 u.RNDfromCI 撒初始点并离散化;之后每代 epochNow 自增,inf 由 u.Scale 算 cnt 的四次方影响力。对每个代理 i 和坐标 c,先随机选低位代理 ind;若 rnd<inf 就按高斯分布绕 cB 抖;否则按 ρ 决定是贴 xLower 还是贴全局 xLowest 更新,最后 u.SeInDiSp 把坐标夹回范围与步长。 渗透机制原文用固定 δ 判断 f(x_i)<δ 就随机重生,但作者指出 δ 难预设:太小不渗、太大狂重启。他的改法是给每滴加尝试计数器,泄漏概率按二次律加速,避免一滴死赖一处。Revision() 则扫全群,f 超 fB 就刷 cB、cnt 归零,否则 cnt++;找到全局最优 ind 后抄进 cB 全局并调 u.Sorting_fB 排序。外汇/贵金属参数优化属高风险,回测过拟合可能让实盘失效,建议开 MT5 用自有品种跑一遍 Moving 的 ρ 衰减节奏。

◍ ASHA 智能体的结构体与类初始化

人工淋浴算法(ASHA)把每个搜索个体封装成 S_AO_Agent 结构体,除了当前坐标 c 与适应度 f,还显式保留了上一代坐标 cP、历史最优坐标 cB 以及对应的 fP、fB,并带一个 cnt 计数器。Init 里把三类坐标数组按维度 resize,适应度统一置为 -DBL_MAX,意味着初始个体尚未评估过有效解。 继承类 C_AO_ASHA 在构造时写死了五组参数:种群规模 popSize=100、水流速度 F=0.3、渗透阈值 δ=2、概率变化率 β=0.8、初始概率 ρ0=0.1。这些量直接进 params 数组,方便在 MT5 输入面板里改值后由 SetParams 回写。 外汇与贵金属行情噪声大,用 100 个体跑进化搜索只能说提高命中概率,不代表稳定出信号;建议先把 popSize 调到 30 看回测耗时,再逐步加压。

MQL5 / C++
class="kw">struct S_AO_Agent
{
   class="type">class="kw">double c  []; class=class="str">"cmt">//coordinates
   class="type">class="kw">double cP []; class=class="str">"cmt">//previous coordinates
   class="type">class="kw">double cB []; class=class="str">"cmt">//best coordinates
   class="type">class="kw">double f;     class=class="str">"cmt">//fitness
   class="type">class="kw">double fP;    class=class="str">"cmt">//previous fitness
   class="type">class="kw">double fB;    class=class="str">"cmt">//best fitness
   class="type">int    cnt;   class=class="str">"cmt">//counter
   class="type">void Init(class="type">int coords)
   {
      ArrayResize(c,  coords);
      ArrayResize(cP, coords);
      ArrayResize(cB, coords);
      f  = -DBL_MAX;
      fP = -DBL_MAX;
      fB = -DBL_MAX;
      cnt = class="num">0;
   }
};
class C_AO_ASHA : class="kw">public C_AO
{
  class="kw">public: class=class="str">"cmt">//--------------------------------------------------------------------
  ~C_AO_ASHA() { }
  C_AO_ASHA()
  {
     ao_name = "ASHA";
     ao_desc = "Artificial Showering Algorithm";
     ao_link = "[MQL5官方文档]
     popSize      = class="num">100;  class=class="str">"cmt">//population size
     F            = class="num">0.3;  class=class="str">"cmt">//water flow velocity
     δ            = class="num">2;    class=class="str">"cmt">//resistance level(infiltration threshold)
     β            = class="num">0.8;  class=class="str">"cmt">//parameter that controls the rate of change in probability
     ρ0           = class="num">0.1;  class=class="str">"cmt">//initial probability
     ArrayResize(params, class="num">5);
     params [class="num">0].name = "popSize"; params [class="num">0].val = popSize;
     params [class="num">1].name = "F";       params [class="num">1].val = F;
     params [class="num">2].name = "δ";       params [class="num">2].val = δ;
     params [class="num">3].name = "β";       params [class="num">3].val = β;
     params [class="num">4].name = "ρ0";      params [class="num">4].val = ρ0;
  }
  class="type">void SetParams()
  {
     popSize = (class="type">int)params [class="num">0].val;
     F       = params      [class="num">1].val;
     δ       = (class="type">int)params [class="num">2].val;
     β       = params      [class="num">3].val;

常见问题

元启发式用随机扰动和概率接受机制跳出局部坑,适合多峰目标;网格在维度稍高时就爆炸,优先换随机类算法。
用结构体存位置、步长和能量,每轮按邻域随机游走并更新最优;先跑单品种验证移动是否收敛再扩。
可以,小布能按你给的代理参数自动迭代并标注陷入局部的概率,把重复跑数交给它,你只看拐点。
步长过大导致邻域覆盖失控、能量耗散快;建议先用品种波动率乘 0.1 做初值再微调。
用近 3 个月分时分段回放,观察夏普和最大回撤漂移;外汇贵金属高风险,实盘前必做样本外验证。