交易所市场算法(EMA)
内容
引言
在数值优化领域,新算法层出不穷,旨在有效解决不确定性和高维度条件下的复杂问题。其中,基于种群的元启发式算法占有特殊地位。通过模拟自然或社会过程,这类算法在寻找全局最优解方面展现出令人瞩目的能力。
然而,并非每一种新算法都能达到具有竞争力的性能水平。本文对交易所市场算法(EMA)进行了详细分析,它是上述方法类别中的代表,其灵感来源于股票市场交易者的行为。该算法模拟股票交易过程,市场中成功程度各异的参与者采用多种策略以最大化利润。
算法的实现
算法首先创建一个由市场参与者(交易者)组成的种群,随后将其平均分为三组。每位交易者被随机分配一个搜索空间中的初始位置,代表其当前的股票投资组合。这些位置均匀分布在整个允许范围内。
在对每位交易者的初始成功程度进行评估(计算目标函数值)之后,整个种群按成功程度降序排列,并划分为三组:
- 第一组为精英组。这是最成功的市场参与者,他们找到了收益最高的位置。一个重要特征:在整个交易周期内,精英交易者保持其位置不变,为其他交易者树立榜样。
- 第二组为中间组。中等成功程度的参与者,他们通过向精英学习来寻求提升自身地位,但也愿意承担适度风险。
- 第三组由新手组成。成功程度最低的参与者,愿意承担高风险,试图彻底改变自身处境。
算法的每次迭代代表一个交易轮次,包含两个阶段,对应不同的市场状态。
第一阶段:均衡市场(稳定交易)
在稳定的市场环境中,交易者采用保守策略,以模仿更成功的参与者为基础。
中产阶级的行为。第二组中的每位交易者从精英组中随机选择一位导师,并逐步向该导师的位置偏移。偏移幅度由吸收系数(r1 = 1.5)决定,该系数随算法推进自适应递减。这种移动并非确定性的 —— 通过引入随机乘数来模拟市场决策的不确定性。
新手的行为。第三组交易者同时受到精英组和中产阶级的双重影响。每位新手分别从第一组和第二组中各选一位导师,其新位置由向两位导师方向移动的组合形成。这反映了他们同时向最成功的交易者和近期有所提升的交易者学习的尝试。
第二阶段:波动市场(震荡交易)
在震荡的市场环境中,交易者采用更激进的策略以探索新的机会。
中产阶级的探索行为。第二组交易者采用双重策略。有 50% 的概率,他们会完全复制全局最优解(历史上最成功交易者)的位置。其余情况下,他们围绕精英组的质心进行区域探索,在该位置上叠加可控的随机噪声。噪声水平由风险因子(riskAlpha = 0.3)决定,并随时间推移逐渐降低。
新手的激进行为。第三组采用风险最高的策略:
- 以等于风险因子(30%)的概率,交易者完全忘记当前位置,从随机点重新开始;
- 有 35% 的概率,以较大半径围绕当前位置进行广泛搜索;
- 其余情况下采用对立学习:交易者向最差决策中心的反方向移动,试图远离不成功的区域。
EMA 算法的核心特征是其自适应性。随着迭代的推进,基于 S 型函数计算衰减系数。该系数确保从(算法初期的)探索阶段到(后期的)对已有前景解的利用阶段实现平滑过渡。
两个阶段全部完成后,执行一个关键步骤:除精英组外,所有交易者的位置均根据计算出的新值进行更新。精英交易者保留原有位置,从而确保已找到的最优解不丢失。
随后,为所有交易者重新计算成功率(目标函数值),种群再次排序,并确定新的全局最优解。接下来我们通过下图来了解算法的公式表达。

该图包含了两种市场模式下各组所有关键的位置更新公式。基于上述描述和公式,我们现在可以给出 EMA 算法的伪代码。
1. 初始化
- 创建 N = 60 个交易者的种群(N 为 3 的倍数)
- 设置参数:r₁ = 1.5,r₂ = 0.8,α = 0.3(风险因子)
- 为每位交易者随机初始化位置
- 确定每组规模:m = N/3 = 20
2. 主循环
对每次迭代 t = 1, 2, ..., T,执行:
2.1排序
- 评估所有交易者的适应度
- 按适应度水平降序排列
- 划分为三组:
- G₁ = {1, ..., m} — 精英组
- G₂ = {m+1, ..., 2m} — 中产阶级
- G₃ = {2m+1, ..., N} — 新手组
2.2参数自适应
- progress = t / T
- decay = 1 / (1 + exp(-10(progress - 0.5)))
- r₁ᵃᵈᵃᵖᵗ = r₁ × (1 - decay × 0.5)
- r₂ᵃᵈᵃᵖᵗ = r₂ × (1 - decay × 0.3)
2.3均衡市场阶段
- G₁:保留位置不变
- G₂:对每个 i ∈ G₂
- 随机选择一位领导者 k ∈ G₁
- xᵢ = xᵢ + rand() × r₁ᵃᵈᵃᵖᵗ × (xₖ - xᵢ)
- G₃:对每个 i ∈ G₃
- 选择一位领导者 k₁ ∈ G₁ 和一位领导者 k₂ ∈ G₂
- xᵢ = xᵢ + rand() × r₁ᵃᵈᵃᵖᵗ × (xₖ₁ - xᵢ) + rand() × r₂ᵃᵈᵃᵖᵗ × (xₖ₂ - xᵢ)
2.4波动市场阶段
- G₂:对每个 i ∈ G₂
- 若 rand () < 0.5:xᵢ = xᵇᵉˢᵗ
- 否则:xᵢ = center (G₁) + noise × α × (1 - decay × 0.5)
- G₃:对每个 i ∈ G₃
- 若 rand () < α:xᵢ = random_position ()
- 否则若 rand () < 0.5 + α/2:xᵢ = xᵢ + big_disturbance
- 否则:xᵢ = 2xᵢ - center (worst) + small_noise
2.5更新
- 应用 G₂和 G₃的新位置
- 更新全局最优解
3. 返回结果
返回找到的最优解 xᵇᵉˢᵗ
现在我们来编写 C_AO_EMA 类,它继承自 C_AO 基类,实现 EMA 优化算法。主要成员包括:popSize 为种群大小(代理数量),r1、r2 为不同代理组的吸收系数,riskAlpha 为风险因子。接下来初始化算法设置参数并赋予默认值。
函数方法:
- SetParams() — 从参数数组更新算法参数值,并对这些参数进行验证检查,确保其在可接受范围内。
- Init() — 使用前初始化算法,接收搜索边界(rangeMinP, rangeMaxP)、参数步长(rangeStepP)和世代数(epochsP)等参数。
- Moving() — 在搜索空间中移动代理,包含 EMA 算法更新代理位置的核心逻辑。
- Revision() — 更新最优解,包含改进和调整代理当前位置的机制。
- GetDecayRate() — 返回衰减系数。
变量成员:
- r1 — 某一组代理的吸收系数。数值越高,表示该组代理被其他代理或 "更优" 解 "吸引" 的程度越强。
- r2 — 另一组代理的吸收系数。
- riskAlpha — 风险因子,决定代理做决策时承担的风险程度。
- groupSize — 种群划分的三个组各自的规模(popSize / 3)。
- currentEpoch — 当前算法迭代的索引。
- totalEpochs — 算法执行的总迭代次数。
- tempPop — 存储代理移动过程中新位置的临时数组。S_AO_Agent 类型定义了单个代理(决策方案)的结构。
C_AO_EMA 类提供了用于数值优化的 EMA 算法实现。该算法将种群划分为采用不同策略的组别(由系数 r1 和 r2 定义),并通过风险因子(riskAlpha)在搜索空间探索与利用已找到的 "好" 解之间取得平衡。Moving () 和 Revision () 函数包含核心优化逻辑,Init () 用于初始化算法,SetParams () 则允许我们修改参数值。
//———————————————————————————————————————————————————————————————————— class C_AO_EMA : public C_AO { public: //---------------------------------------------------------- ~C_AO_EMA () { } C_AO_EMA () { ao_name = "EMA"; ao_desc = "Exchange Market Algorithm"; ao_link = "https://www.mql5.com/en/articles/18605"; popSize = 60; // Population size r1 = 1.5; // Absorption coefficient for group 2 r2 = 0.8; // Absorption coefficient for group 3 riskAlpha = 0.3; // Risk factor ArrayResize (params, 4); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "r1"; params [1].val = r1; params [2].name = "r2"; params [2].val = r2; params [3].name = "riskAlpha"; params [3].val = riskAlpha; } void SetParams () { popSize = (int)params [0].val; r1 = params [1].val; r2 = params [2].val; riskAlpha = params [3].val; // Check the parameters validity if (popSize < 6) popSize = 6; if (popSize % 3 != 0) popSize = ((popSize / 3) + 1) * 3; // Multiple of 3 if (r1 < 0.0) r1 = 0.0; if (r2 < 0.0) r2 = 0.0; if (riskAlpha < 0.0) riskAlpha = 0.0; if (riskAlpha > 1.0) riskAlpha = 1.0; } bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0); void Moving (); void Revision (); //------------------------------------------------------------------ double r1; // Absorption coefficient 1 double r2; // Absorption coefficient 2 double riskAlpha; // Risk factor private: //--------------------------------------------------------- int groupSize; // size of each group (popSize/3) int currentEpoch; // current epoch int totalEpochs; // total number of epochs S_AO_Agent tempPop []; // temporary population for storing new positions double GetDecayRate (); }; //————————————————————————————————————————————————————————————————————
Init 初始化方法用于为算法执行做准备。它接收搜索参数的最小值数组、最大值数组和步长数组,以及世代数(迭代次数)。首先调用标准初始化函数,处理搜索范围的设置和有效性检查。如果返回 false,则该方法的初始化过程终止。
接下来,计算每个代理组的规模,即总种群大小除以 3。初始化当前世代计数器,并存储算法设定的总世代数。之后为临时种群分配内存 —— 这是一个与主种群大小相同的代理数组。在循环中,该临时数组的每个代理都用 coords 数组中指定的坐标进行初始化。
当所有步骤成功完成后,返回值为 true,表示算法已成功准备就绪,可以执行。
//———————————————————————————————————————————————————————————————————— //--- Initialization bool C_AO_EMA::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ groupSize = popSize / 3; currentEpoch = 0; totalEpochs = epochsP; // Initialize the temporary population ArrayResize (tempPop, popSize); for (int i = 0; i < popSize; i++) { tempPop [i].Init (coords); } return true; } //————————————————————————————————————————————————————————————————————
Moving 方法是实现 EMA 算法中代理进化的核心函数。它负责改变代理在搜索空间中的位置。初始设置(仅执行一次):
- 如果这是算法首次运行,则在指定范围(rangeMin, rangeMax)内对所有代理的坐标进行随机初始化。
- 每个代理的坐标都根据离散化步长(rangeStep)进行调整。
- 将 revision 变量设为 true,这样后续调用该方法时就不会重复执行初始化。
世代与衰减率更新:
- 当前世代计数器(currentEpoch)递增。
- 计算 decayRate(衰减率),该值逐代递减,影响算法的自适应行为。
复制当前种群。将当前 'a' 种群中所有代理的坐标和目标函数 f 值复制到 tempPop 临时种群中,这样做是为了在当前迭代的所有计算完成前,tempPop 的修改不会影响 'a'。
第一阶段:均衡市场(吸收算子)。该阶段模拟代理从最优代理那里 "吸收" 信息的行为。种群被分为三组:
- 第一组(精英组):这些代理(索引从 0 到 groupSize - 1)在此阶段不发生变化,它们是最优代理,位置保持不变。
- 第二组(吸收算子 1):该组代理(索引从 groupSize 到 2*groupSize - 1)更新其位置。
- 对该组中的每个代理,从第一组中随机选择一位 "领导者"。
- 第二组代理的坐标向选中的第一组领导者的坐标偏移。偏移量取决于 r1 自适应系数,该系数结合 decayRate 递减。
- 坐标按范围和步长进行调整。
- 第三组(吸收算子 2):该组代理(索引从 "2 * groupSize" 到 "popSize - 1")同样更新位置,但移动更加活跃。
- 对该组中的每个代理,随机选择两位领导者:一位来自第一组,一位来自第二组。
- 第三组代理的坐标在给出的代码实现中,都使用了基于 r1 的自适应系数,同时向两位选中的领导者方向偏移。
- 坐标按范围和步长进行调整。
第二阶段:波动市场(搜索算子)。 该阶段模拟代理的探索行为,允许它们搜索空间中的新区域。
- 第二组(搜索算子 1 — 中等风险):
- 该组每个代理的坐标都会被更新。
- 有 50% 的概率,坐标被完全替换为 cB(全局最优解)的值。
- 否则,坐标相对于 "精英中心"(第一组代理坐标的平均值)偏移,并叠加噪声。噪声大小取决于 riskAlpha、坐标范围和 decayRate。
- 坐标按范围和步长进行调整。
- 第三组(搜索算子 2 — 高风险):
- 该组每个代理的坐标都会被更新。
- 以 riskAlpha 概率,坐标在范围内完全随机重新初始化。这模拟了一种极高风险的大规模探索。
- 否则,如果第二个随机数小于 "0.5 + riskAlpha / 2.0",则执行 "广泛搜索":坐标在一定半径内随机偏移,半径大小取决于 riskAlpha 和 decayRate。
- 否则,应用 "对立学习":坐标向相反方向偏移。并添加少量随机噪声。
- 坐标按范围和步长进行调整。
复制变更。最后,在所有计算完成后,将 tempPop 中(第二组和第三组)代理的更新坐标复制回主种群 'a'。第一组的坐标保持不变(最初就没有复制回去)。
总体而言,Moving 方法实现了一个迭代过程,种群中的代理通过向最优代理 "学习",并以不同程度的风险探索搜索空间,来改变自身位置。
//———————————————————————————————————————————————————————————————————— //--- Main loop of the algorithm void C_AO_EMA::Moving () { // Initial setup if (!revision) { for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } revision = true; return; } currentEpoch++; double decayRate = GetDecayRate (); // Copy the current population to the temporary one for (int i = 0; i < popSize; i++) { ArrayCopy (tempPop [i].c, a [i].c, 0, 0, WHOLE_ARRAY); tempPop [i].f = a [i].f; } // PHASE 1: Balanced market (absorbing operators) // Group 1 (elite) - does not change // Indices: 0 ... groupSize-1 // Group 2 - absorbing operator 1 // Indices: groupSize ... 2*groupSize-1 double adaptiveR1 = r1 * (1.0 - decayRate * 0.5); for (int i = groupSize; i < 2 * groupSize; i++) { // Each agent in group 2 selects a random leader from group 1 int leaderIdx = u.RNDminusOne (groupSize); for (int c = 0; c < coords; c++) { tempPop [i].c [c] = a [i].c [c] + u.RNDprobab () * adaptiveR1 * (a [leaderIdx].c [c] - a [i].c [c]); tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } // Group 3 - absorbing operator 2 // Indices: 2*groupSize ... popSize-1 adaptiveR1 = r1 * (1.0 - decayRate * 0.3); for (int i = 2 * groupSize; i < popSize; i++) { // Selection of leaders from groups 1 and 2 int leader1Idx = u.RNDminusOne (groupSize); int leader2Idx = groupSize + u.RNDminusOne (groupSize); for (int c = 0; c < coords; c++) { tempPop [i].c [c] = a [i].c [c] + u.RNDprobab () * adaptiveR1 * (a [leader1Idx].c [c] - a [i].c [c]) + u.RNDprobab () * adaptiveR1 * (a [leader2Idx].c [c] - a [i].c [c]); tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } // PHASE 2: Fluctuating market (search operators) // Group 2 - search operator 1 (moderate risk) for (int i = groupSize; i < 2 * groupSize; i++) { for (int c = 0; c < coords; c++) { double range = rangeMax [c] - rangeMin [c]; if (u.RNDprobab () < 0.5) { tempPop [i].c [c] = cB [c];// tempPop [i].c [c] + delta; } else { // Search around the center of the elite double eliteCenter = 0.0; for (int j = 0; j < groupSize; j++) { eliteCenter += a [j].c [c]; } eliteCenter /= (double)groupSize; double noise = riskAlpha * range * u.RNDfromCI (-0.5, 0.5) * (1.0 - decayRate * 0.5); tempPop [i].c [c] = eliteCenter + noise; } // Check boundaries tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } // Group 3 - search operator 2 (high risk) for (int i = 2 * groupSize; i < popSize; i++) { for (int c = 0; c < coords; c++) { double range = rangeMax [c] - rangeMin [c]; if (u.RNDprobab () < riskAlpha) { // Full reinitialization tempPop [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); } else if (u.RNDprobab () < 0.5 + riskAlpha / 2.0) { // Broad search double searchRadius = 2.0 * riskAlpha * range * (1.0 - decayRate * 0.3); double delta = u.RNDfromCI (-searchRadius, searchRadius); tempPop [i].c [c] = tempPop [i].c [c] + delta; } else { // Opposition-based training double worstCenter = 0.0; int worstCount = groupSize / 2; for (int j = popSize - worstCount; j < popSize; j++) { worstCenter += a [j].c [c]; } worstCenter /= (double)worstCount; // Moving in the opposite direction from the worst ones tempPop [i].c [c] = 2.0 * tempPop [i].c [c] - worstCenter; // Add a little noise double noise = riskAlpha * range * u.RNDfromCI (-0.1, 0.1); tempPop [i].c [c] += noise; } // Check boundaries tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } // Copying from the temporary population to the main one (except group 1) for (int i = groupSize; i < popSize; i++) { ArrayCopy (a [i].c, tempPop [i].c, 0, 0, WHOLE_ARRAY); } } //————————————————————————————————————————————————————————————————————
GetDecayRate 方法用于计算衰减率,该参数控制算法在执行过程中行为变化的程度。它首先检查总世代数 totalEpochs 是否为 "0" 或负数,若是则返回零,以避免除零错误,确保运行安全。接下来,计算搜索进度,即已完成世代数占总世代数的比例,记为 ‘progress’。随着算法推进,"progress" 值从 "0" 逐渐增加到 "1"。
基于 "progress",该方法返回指数形式的 S 型函数计算值。该函数实现衰减因子从 "0" 到 "1" 的平滑过渡,使得搜索初期(progress 接近 0 时值接近 0)和搜索末期(progress 接近 1 时值接近 1)呈现渐变效果。这种非线性过渡有助于在空间探索与利用已找到的解之间调整平衡。
具体使用的 S 型函数形式为:1 / (1 + exp (-10 * (progress - 0.5)))。这种形式在优化过程中点(progress ≈ 0.5)附近过渡更陡峭,使算法运行期间系数的变化更具动态性。
//———————————————————————————————————————————————————————————————————— //--- Get the decay rate double C_AO_EMA::GetDecayRate () { if (totalEpochs <= 0) return 0.0; // Non-linear attenuation for better exploitation/exploration balance double progress = (double)currentEpoch / (double)totalEpochs; // Use the sigmoid function for a smooth transition return 1.0 / (1.0 + MathExp (-10.0 * (progress - 0.5))); } //————————————————————————————————————————————————————————————————————
在 EMA 算法中,Revision 方法负责在每次迭代中更新已找到的最优解(全局最优)。创建一个 S_AO_Agent 类型的临时静态数组 aT。"static" 表示该数组在函数首次调用时创建一次,并在多次调用之间保持其状态。ArrayResize (aT, popSize); 调整该临时数组的大小,使其能容纳 popSize 个代理(种群大小)。该数组将用于排序操作。从辅助对象 'u' 中调用 Sorting 函数。该函数按适应度(目标函数 'f' 的值)对代理主种群 'a' 进行排序。
注意:该操作完成后,'a' 数组将被排序,目标函数 'f' 值最优的代理位于第一位(索引 "0")。在最小化算法中,这是 'f' 最小的代理;在最大化算法中,则是 'f' 最大的代理。当前种群最优代理(排序后位于 a [0])的坐标被复制到 cB 数组中,该数组存储算法迄今为止找到的最优解的坐标。最优代理的目标函数值被赋值给 fB 变量。该变量存储与最优坐标 cB 对应的目标函数值。
//———————————————————————————————————————————————————————————————————— //--- Update the best solutions void C_AO_EMA::Revision () { static S_AO_Agent aT []; ArrayResize (aT, popSize); u.Sorting (a, aT, popSize); ArrayCopy (cB, a [0].c, 0, 0, WHOLE_ARRAY); fB = a [0].f; } //————————————————————————————————————————————————————————————————————
测试结果
测试结果远非完美,但该算法确实可以正常运行,并具备一定的搜索能力。
EMA | 交易所市场算法 | 60.0 | 1.5 | 0.8 | 0.3 |
=============================
5 Hilly's;函数运行次数:10000;结果:0.6706604188712635
25 Hilly's;函数运行次数:10000;结果:0.42759923501764946
500 Hilly's;函数运行次数:10000;结果0.252217676777693
=============================
5 Forest's;函数运行次数:10000;结果:0.7419215403847332
25 Forest's;函数运行次数:10000;结果:0.38137087707323236
500 Forest's;函数运行次数:10000;结果:0.19454127467011006
=============================
5 Megacity's;函数运行次数:10000;结果:0.38769230769230767
25 Megacity's;函数运行次数:10000;结果:0.21323076923076928
500 Megacity's;函数运行次数:10000;结果:0.09672307692307769
=============================
总得分:3.36596 (37.40%)
可视化结果显示,低维函数的测试结果较为分散。

EMA 算法在 Hilly 测试函数上的表现

EMA 算法在 Forest 测试函数上的表现

EMA 算法在Megacity 测试函数上的表现
测试完成后,EMA 算法将被纳入我们的种群优化算法排名表中,仅供参考。
| # | 算法 | 说明 | Hilly | Hilly 最终结果 | Forest | Forest 最终结果 | Megacity (离散) | Megacity 最终结果 | 最终 结果 | % of MAX | ||||||
| 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | ||||||||
| 1 | ANS | 交叉邻近搜索 | 0.94948 | 0.84776 | 0.43857 | 2.23581 | 1.00000 | 0.92334 | 0.39988 | 2.32323 | 0.70923 | 0.63477 | 0.23091 | 1.57491 | 6.134 | 68.15 |
| 2 | CLA | 代码锁定算法(JOO) | 0.95345 | 0.87107 | 0.37590 | 2.20042 | 0.98942 | 0.91709 | 0.31642 | 2.22294 | 0.79692 | 0.69385 | 0.19303 | 1.68380 | 6.107 | 67.86 |
| 3 | AMOm | 动物迁徙优化M | 0.90358 | 0.84317 | 0.46284 | 2.20959 | 0.99001 | 0.92436 | 0.46598 | 2.38034 | 0.56769 | 0.59132 | 0.23773 | 1.39675 | 5.987 | 66.52 |
| 4 | (P+O)ES | (P+O) 进化策略 | 0.92256 | 0.88101 | 0.40021 | 2.20379 | 0.97750 | 0.87490 | 0.31945 | 2.17185 | 0.67385 | 0.62985 | 0.18634 | 1.49003 | 5.866 | 65.17 |
| 5 | CTA | 彗尾算法(JOO) | 0.95346 | 0.86319 | 0.27770 | 2.09435 | 0.99794 | 0.85740 | 0.33949 | 2.19484 | 0.88769 | 0.56431 | 0.10512 | 1.55712 | 5.846 | 64.96 |
| 6 | TETA | 时间演化旅行算法(JOO) | 0.91362 | 0.82349 | 0.31990 | 2.05701 | 0.97096 | 0.89532 | 0.29324 | 2.15952 | 0.73462 | 0.68569 | 0.16021 | 1.58052 | 5.797 | 64.41 |
| 7 | SDSm | 随机扩散搜索 M | 0.93066 | 0.85445 | 0.39476 | 2.17988 | 0.99983 | 0.89244 | 0.19619 | 2.08846 | 0.72333 | 0.61100 | 0.10670 | 1.44103 | 5.709 | 63.44 |
| 8 | BOAm | 台球优化算法 M | 0.95757 | 0.82599 | 0.25235 | 2.03590 | 1.00000 | 0.90036 | 0.30502 | 2.20538 | 0.73538 | 0.52523 | 0.09563 | 1.35625 | 5.598 | 62.19 |
| 9 | AAm | 射箭算法 M | 0.91744 | 0.70876 | 0.42160 | 2.04780 | 0.92527 | 0.75802 | 0.35328 | 2.03657 | 0.67385 | 0.55200 | 0.23738 | 1.46323 | 5.548 | 61.64 |
| 10 | ESG | 社会群体的进化(JOO) | 0.99906 | 0.79654 | 0.35056 | 2.14616 | 1.00000 | 0.82863 | 0.13102 | 1.95965 | 0.82333 | 0.55300 | 0.04725 | 1.42358 | 5.529 | 61.44 |
| 11 | SIA | 模拟各向同性退火(JOO) | 0.95784 | 0.84264 | 0.41465 | 2.21513 | 0.98239 | 0.79586 | 0.20507 | 1.98332 | 0.68667 | 0.49300 | 0.09053 | 1.27020 | 5.469 | 60.76 |
| 12 | BBO | 基于生物地理学的优化算法 | 0.94912 | 0.69456 | 0.35031 | 1.99399 | 0.93820 | 0.67365 | 0.25682 | 1.86867 | 0.74615 | 0.48277 | 0.17369 | 1.40261 | 5.265 | 58.50 |
| 13 | ACS | 人工协同搜索 | 0.75547 | 0.74744 | 0.30407 | 1.80698 | 1.00000 | 0.88861 | 0.22413 | 2.11274 | 0.69077 | 0.48185 | 0.13322 | 1.30583 | 5.226 | 58.06 |
| 14 | DA | 辩证算法 | 0.86183 | 0.70033 | 0.33724 | 1.89940 | 0.98163 | 0.72772 | 0.28718 | 1.99653 | 0.70308 | 0.45292 | 0.16367 | 1.31967 | 5.216 | 57.95 |
| 15 | BHAm | 黑洞算法 M | 0.75236 | 0.76675 | 0.34583 | 1.86493 | 0.93593 | 0.80152 | 0.27177 | 2.00923 | 0.65077 | 0.51646 | 0.15472 | 1.32195 | 5.196 | 57.73 |
| 16 | ASO | 无政府社会优化 | 0.84872 | 0.74646 | 0.31465 | 1.90983 | 0.96148 | 0.79150 | 0.23803 | 1.99101 | 0.57077 | 0.54062 | 0.16614 | 1.27752 | 5.178 | 57.54 |
| 17 | RFO | 皇家同花顺优化算法 (JOO) | 0.83361 | 0.73742 | 0.34629 | 1.91733 | 0.89424 | 0.73824 | 0.24098 | 1.87346 | 0.63154 | 0.50292 | 0.16421 | 1.29867 | 5.089 | 56.55 |
| 18 | AOSm | 原子轨道搜索 M | 0.80232 | 0.70449 | 0.31021 | 1.81702 | 0.85660 | 0.69451 | 0.21996 | 1.77107 | 0.74615 | 0.52862 | 0.14358 | 1.41835 | 5.006 | 55.63 |
| 19 | TSEA | 龟壳进化算法(JOO) | 0.96798 | 0.64480 | 0.29672 | 1.90949 | 0.99449 | 0.61981 | 0.22708 | 1.84139 | 0.69077 | 0.42646 | 0.13598 | 1.25322 | 5.004 | 55.60 |
| 20 | BSA | 回溯搜索算法 | 0.97309 | 0.54534 | 0.29098 | 1.80941 | 0.99999 | 0.58543 | 0.21747 | 1.80289 | 0.84769 | 0.36953 | 0.12978 | 1.34700 | 4.959 | 55.10 |
| 21 | DE | 差分进化 | 0.95044 | 0.61674 | 0.30308 | 1.87026 | 0.95317 | 0.78896 | 0.16652 | 1.90865 | 0.78667 | 0.36033 | 0.02953 | 1.17653 | 4.955 | 55.06 |
| 22 | SRA | 成功餐饮经营者算法(joo) | 0.96883 | 0.63455 | 0.29217 | 1.89555 | 0.94637 | 0.55506 | 0.19124 | 1.69267 | 0.74923 | 0.44031 | 0.12526 | 1.31480 | 4.903 | 54.48 |
| 23 | CRO | 化学反应优化 | 0.94629 | 0.66112 | 0.29853 | 1.90593 | 0.87906 | 0.58422 | 0.21146 | 1.67473 | 0.75846 | 0.42646 | 0.12686 | 1.31178 | 4.892 | 54.36 |
| 24 | BIO | 血缘遗传优化算法 (JOO) | 0.81568 | 0.65336 | 0.30877 | 1.77781 | 0.89937 | 0.65319 | 0.21760 | 1.77016 | 0.67846 | 0.47631 | 0.13902 | 1.29378 | 4.842 | 53.80 |
| 25 | BSA | 鸟群算法 | 0.89306 | 0.64900 | 0.26250 | 1.80455 | 0.92420 | 0.71121 | 0.24939 | 1.88479 | 0.69385 | 0.32615 | 0.10012 | 1.12012 | 4.809 | 53.44 |
| 26 | DEA | 海豚回声定位算法 | 0.75995 | 0.67572 | 0.34171 | 1.77738 | 0.89582 | 0.64223 | 0.23941 | 1.77746 | 0.61538 | 0.44031 | 0.15115 | 1.20684 | 4.762 | 52.91 |
| 27 | HS | 和声搜索算法 | 0.86509 | 0.68782 | 0.32527 | 1.87818 | 0.99999 | 0.68002 | 0.09590 | 1.77592 | 0.62000 | 0.42267 | 0.05458 | 1.09725 | 4.751 | 52.79 |
| 28 | SSG | 树苗播种和生长算法 | 0.77839 | 0.64925 | 0.39543 | 1.82308 | 0.85973 | 0.62467 | 0.17429 | 1.65869 | 0.64667 | 0.44133 | 0.10598 | 1.19398 | 4.676 | 51.95 |
| 29 | BCOm | 细菌趋化优化算法M | 0.75953 | 0.62268 | 0.31483 | 1.69704 | 0.89378 | 0.61339 | 0.22542 | 1.73259 | 0.65385 | 0.42092 | 0.14435 | 1.21912 | 4.649 | 51.65 |
| 30 | ABO | 非洲水牛优化 | 0.83337 | 0.62247 | 0.29964 | 1.75548 | 0.92170 | 0.58618 | 0.19723 | 1.70511 | 0.61000 | 0.43154 | 0.13225 | 1.17378 | 4.634 | 51.49 |
| 31 | (PO)ES | (PO) 进化策略 | 0.79025 | 0.62647 | 0.42935 | 1.84606 | 0.87616 | 0.60943 | 0.19591 | 1.68151 | 0.59000 | 0.37933 | 0.11322 | 1.08255 | 4.610 | 51.22 |
| 32 | FBA | 基于分形的算法 | 0.79000 | 0.65134 | 0.28965 | 1.73099 | 0.87158 | 0.56823 | 0.18877 | 1.62858 | 0.61077 | 0.46062 | 0.12398 | 1.19537 | 4.555 | 50.61 |
| 33 | TSm | 禁忌搜索优化算法 | 0.87795 | 0.61431 | 0.29104 | 1.78330 | 0.92885 | 0.51844 | 0.19054 | 1.63783 | 0.61077 | 0.38215 | 0.12157 | 1.11449 | 4.536 | 50.40 |
| 34 | BSO | 头脑风暴优化 | 0.93736 | 0.57616 | 0.29688 | 1.81041 | 0.93131 | 0.55866 | 0.23537 | 1.72534 | 0.55231 | 0.29077 | 0.11914 | 0.96222 | 4.498 | 49.98 |
| 35 | WOAm | 鲸鱼优化算法M | 0.84521 | 0.56298 | 0.26263 | 1.67081 | 0.93100 | 0.52278 | 0.16365 | 1.61743 | 0.66308 | 0.41138 | 0.11357 | 1.18803 | 4.476 | 49.74 |
| 36 | AEFA | 人工电场算法 | 0.87700 | 0.61753 | 0.25235 | 1.74688 | 0.92729 | 0.72698 | 0.18064 | 1.83490 | 0.66615 | 0.11631 | 0.09508 | 0.87754 | 4.459 | 49.55 |
| 37 | AEO | 基于人工生态系统的优化算法 | 0.91380 | 0.46713 | 0.26470 | 1.64563 | 0.90223 | 0.43705 | 0.21400 | 1.55327 | 0.66154 | 0.30800 | 0.28563 | 1.25517 | 4.454 | 49.49 |
| 38 | CAm | 骆驼算法 M | 0.78684 | 0.56042 | 0.35133 | 1.69859 | 0.82772 | 0.56041 | 0.24336 | 1.63149 | 0.64846 | 0.33092 | 0.13418 | 1.11356 | 4.444 | 49.37 |
| 39 | ACOm | 蚁群优化 M | 0.88190 | 0.66127 | 0.30377 | 1.84693 | 0.85873 | 0.58680 | 0.15051 | 1.59604 | 0.59667 | 0.37333 | 0.02472 | 0.99472 | 4.438 | 49.31 |
| 40 | CMAES | 协方差矩阵自适应进化策略 | 0.76258 | 0.72089 | 0.00000 | 1.48347 | 0.82056 | 0.79616 | 0.00000 | 1.61672 | 0.75846 | 0.49077 | 0.00000 | 1.24923 | 4.349 | 48.33 |
| 41 | BFO-GA | 细菌觅食优化 - ga | 0.89150 | 0.55111 | 0.31529 | 1.75790 | 0.96982 | 0.39612 | 0.06305 | 1.42899 | 0.72667 | 0.27500 | 0.03525 | 1.03692 | 4.224 | 46.93 |
| 42 | SOA | 简单优化算法 | 0.91520 | 0.46976 | 0.27089 | 1.65585 | 0.89675 | 0.37401 | 0.16984 | 1.44060 | 0.69538 | 0.28031 | 0.10852 | 1.08422 | 4.181 | 46.45 |
| 43 | ABHA | 人工蜂巢算法 | 0.84131 | 0.54227 | 0.26304 | 1.64663 | 0.87858 | 0.47779 | 0.17181 | 1.52818 | 0.50923 | 0.33877 | 0.10397 | 0.95197 | 4.127 | 45.85 |
| 44 | ACMO | 大气云层模型优化 | 0.90321 | 0.48546 | 0.30403 | 1.69270 | 0.80268 | 0.37857 | 0.19178 | 1.37303 | 0.62308 | 0.24400 | 0.10795 | 0.97503 | 4.041 | 44.90 |
| 45 | ADAMm | 自适应动量评估 M | 0.88635 | 0.44766 | 0.26613 | 1.60014 | 0.84497 | 0.38493 | 0.16889 | 1.39880 | 0.66154 | 0.27046 | 0.10594 | 1.03794 | 4.037 | 44.85 |
| EMA | 交易所市场算法 | 0.67066 | 0.42759 | 0.25221 | 1.35046 | 0.74192 | 0.38137 | 0.19454 | 1.31783 | 0.38769 | 0.21323 | 0.09672 | 0.69764 | 3.366 | 37.40 | |
| RW | 随机游走 | 0.48754 | 0.32159 | 0.25781 | 1.06694 | 0.37554 | 0.21944 | 0.15877 | 0.75375 | 0.27969 | 0.14917 | 0.09847 | 0.52734 | 2.348 | 26.09 | |
总结
本文提出的 EMA 算法虽然具备基本的搜索能力,但其性能不足以跻身种群优化算法排名表前45名之列。其结构较为单一,以及缺乏克服陷入局部极值这一常见问题的明确机制,暴露出若干潜在缺陷。
若不对其功能和自适应行为控制进行显著改进,EMA 将始终停留在基础算法层面,解决复杂优化问题的潜力有限,这也是它未能跻身顶级元启发式算法排名的原因。不过,该算法中的一些思路颇有意思,也很有希望成为我们后续开发思路库的一部分。

图 2. 根据相应测试结果的算法颜色分级

图 3. 算法测试结果直方图(比例从 0 到 100,越高越好,其中 100 是最大可能的理论结果,存档中有一个用于计算评级表的脚本)
EMA 算法的优缺点:
优点:
- 实现简单
缺点:
- 收敛精度低
本文附带了一个压缩包,其中包含了算法代码的当前版本。作者不对文中经典算法描述的绝对准确性承担责任。其中许多算法均经过修改,以提升搜索能力。文章中表述的结论和论断是基于实验的结果。
本文使用的程序
| # | 名称 | 类型 | 说明 |
|---|---|---|---|
| 1 | #C_AO.mqh | 库 | 种群优化算法的父类 |
| 2 | #C_AO_enum.mqh | 库 | 群体优化算法枚举 |
| 3 | TestFunctions.mqh | 库 | 测试函数库 |
| 4 | TestStandFunctions.mqh | 库 | 测试台函数库 |
| 5 | Utilities.mqh | 库 | 辅助函数库 |
| 6 | CalculationTestResults.mqh | 库 | 用于计算对比表中结果的脚本 |
| 7 | Testing AOs.mq5 | 脚本 | 所有种群优化算法的统一测试程序 |
| 8 | Simple use of population optimization algorithms.mq5 | 脚本 | 一个不包含可视化功能的种群优化算法使用示例 |
| 9 | Test_AO_EMA.mq5 | 脚本 | EMA 算法测试程序 |
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/18605
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
MQL5中的价格时间缺口分析(第二部分):构建时间维度下的流动性分布热力图
新手在交易中的10个基本错误
开发多币种 EA(第 27 部分):多行文本显示组件