神经网络变得轻松(第四十四部分):动态学习技能·综合运用
📘

神经网络变得轻松(第四十四部分):动态学习技能·综合运用

第 3/3 篇

强化学习回放里的状态推进与反向传播

这段逻辑跑在经验回放的遍历循环里,针对某个 transition 把当前状态喂给 Actor 网络做前向推理;若 feedForward 返回 false,直接打印函数名与行号、移除 Expert 并 break,说明网络推理异常时不再继续训练该批次。 推理成功后,用 GetAgentReward 拿到该步奖励写入 Result,再把下一帧状态读入 StateSkill,并通过 GetNewState 依据 Actor 动作与单级利润 prof_1l 生成新账户向量 account。 shift 按 skill*AccountDescr 偏移,随后把账户的相对变化量逐维追加进 StateSkill:包括余额变化率 (account[0]-PrevBalance)/PrevBalance、权益占比 account[1]/PrevBalance、权益回撤率 (account[1]-PrevEquity)/PrevEquity,以及持仓数、空闲保证金等 8 个归一化特征。这些维度直接决定下一状态表征是否包含足够的资金曲线信息。 最后调 Actor.backProp 做反向传播,DiscountFactor 控制奖励衰减;同样一旦失败就打印并退出训练循环。外汇与贵金属杠杆高,这类自学习模块在实盘前必须用 MT5 策略测试器跑至少 3 个月 Tick 数据验证数值稳定性。

MQL5 / C++
if(!Actor.feedForward(GetPointer(State), class="num">1, false))
   {
     PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
     ExpertRemove();
     break;
   }
 reward = GetAgentReward(skill, DiscriminatorResult, Buffer[tr].States[i].account);
 Result.AssignArray(reward);
 StateSkill.AssignArray(Buffer[tr].States[i + class="num">1].state);
 account = GetNewState(Buffer[tr].States[i].account, Actor.getAction(), prof_1l);
 shift = skill * AccountDescr;
 StateSkill.Add((account[class="num">0] - PrevBalance) / PrevBalance);
 StateSkill.Add(account[class="num">1] / PrevBalance);
 StateSkill.Add((account[class="num">1] - PrevEquity) / PrevEquity);
 StateSkill.Add(account[class="num">2] / PrevBalance);
 StateSkill.Add(account[class="num">4] / PrevBalance);
 StateSkill.Add(account[class="num">5]);
 StateSkill.Add(account[class="num">6]);
 StateSkill.Add(account[class="num">7] / PrevBalance);
 StateSkill.Add(account[class="num">8] / PrevBalance);
 if(!Actor.backProp(Result, DiscountFactor, GetPointer(StateSkill), class="num">1, false))
   {
     PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
     ExpertRemove();
     break;
   }
}
 break;
 case class="num">2:
 if(!Scheduler.feedForward(GetPointer(State), class="num">1, false))
   {

◍ 回测循环里的状态回收与止损退出

在强化学习驱动的 MT5 回测框架里,每一根 K 线推进后都要先拉取调度器的回报并塞进状态容器:Scheduler.getResults 拿到本次收益向量,State.AddArray 把它接在原有特征后面。若中途 IsStopped() 命中,说明用户点了终止或测试被强制打断,这时打印函数名加行号、调 ExpertRemove() 再 break,EA 会干净退出而不是卡在死循环。 feedForward 这一步把状态指针喂给 Actor 网络做前向推理,参数 1 表示单步、false 代表不训练模式;返回 false 同样触发打印+移除+跳出。拿到 action 后,GetNewState 依据账户快照和动作算出下一刻的余额、权益等 9 维账户数组,其中 account[0] 为余额、account[1] 为权益、account[4]~[8] 为持仓与浮盈类指标。 回报折算用 SchedulerResult = SchedulerResult * (account[0] / PrevBalance - 1.0),即把绝对收益转成以_prevBalance 为基准的收益率再乘原回报。随后 State 连续 Add 七个比值特征:如 (account[0]-PrevBalance)/PrevBalance 是余额变化率,account[5]、account[6] 则是未缩放的原始字段。这些拼接好的 State 直接进 backProp 做反向传播,DiscountFactor 控制远期回报衰减。 开 MT5 把这段粘进你的回测 EA,故意在循环里改 PrevBalance 为 0 看是否触发除零崩溃,就能验证状态拼接顺序是否和你的特征维度对齐。外汇与贵金属回测天然高杠杆高风险,这类收益率折算在极端滑点下可能严重偏离实盘。

MQL5 / C++
PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
ExpertRemove();
break;
}
Scheduler.getResults(SchedulerResult);
State.AddArray(SchedulerResult);
if(IsStopped())
  {
   PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
   ExpertRemove();
   break;
  }
if(!Actor.feedForward(GetPointer(State), class="num">1, false))
  {
   PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
   ExpertRemove();
   break;
  }
action = Actor.getAction();
account = GetNewState(Buffer[tr].States[i].account, action, prof_1l);
SchedulerResult = SchedulerResult * (account[class="num">0] / PrevBalance - class="num">1.0);
Result.AssignArray(SchedulerResult);
State.AssignArray(Buffer[tr].States[i + class="num">1].state);
State.Add((account[class="num">0] - PrevBalance) / PrevBalance);
State.Add(account[class="num">1] / PrevBalance);
State.Add((account[class="num">1] - PrevEquity) / PrevEquity);
State.Add(account[class="num">2] / PrevBalance);
State.Add(account[class="num">4] / PrevBalance);
State.Add(account[class="num">5]);
State.Add(account[class="num">6]);
State.Add(account[class="num">7] / PrevBalance);
State.Add(account[class="num">8] / PrevBalance);
if(!Scheduler.backProp(Result, DiscountFactor, GetPointer(State), class="num">1, false))

「训练循环里的退出与进度播报」

上面这段是 GAN 式训练主循环末尾的控制逻辑,重点不在算法本身,而在怎么让 EA 不卡死、出错了能自尽。 遇到不可恢复分支时,先用 PrintFormat 把当前函数和行号打出来,便于定位;随后 ExpertRemove() 直接卸载 EA,break 跳出内层循环。这种写法比抛异常更干净,MT5 回测时不会卡在死循环里空耗。 default 分支专门接住未定义的 phase 值,PrintFormat 输出 'Wrong phase %d' 并显示具体数值,方便你查状态机是不是漏了 case。 进度播报靠 GetTickCount() 做节流:每过 500 毫秒才刷新一次 Comment。str 里用 StringFormat 把 Scheduler 和 Discriminator 的近期平均误差按 '%-15s %5.2f%% -> Error %15.8f' 格式拼好,iter*100.0/Iterations 给出整数百分比进度,误差精确到小数点后 8 位。外汇与贵金属模型训练波动大,这类误差数值仅反映局部收敛状态,过拟合概率不低,实盘前务必用历史数据交叉验证。

MQL5 / C++
            {
               PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
               ExpertRemove();
               break;
            }
            break;
         class="kw">default:
            PrintFormat("Wrong phase %d", phase);
            break;
         }
      }
   }
   if(GetTickCount() - ticks > class="num">500)
   {
      class="type">class="kw">string str = StringFormat("%-15s %class="num">5.2f%% -> Error %class="num">15.8f\n", "Scheduler",
         iter * class="num">100.0 / (class="type">class="kw">double)(Iterations), Scheduler.getRecentAverageError());
      str += StringFormat("%-15s %class="num">5.2f%% -> Error %class="num">15.8f\n", "Discriminator",   
         iter * class="num">100.0 / (class="type">class="kw">double)(Iterations), Discriminator.getRecentAverageError());
      Comment(str);
      ticks = GetTickCount();
   }
}

用 5 月外推样本压调度器模型

模型在 EURUSD H1 上用 2023 年前 4 个月历史训练,指标走默认参数,和前一篇训练设定一致,这样两种技能训练方法的结果才可直接比对。 测试放在 MT5 策略测试器里跑 2023 年 5 月,样本完全在训练区间之外,时间长度约为训练集的 25%,属于典型外推检验。 外推结果:盈利因子 1.75,恢复因子 0.85,盈利交易占比 52.64%;平均盈利 57.37 点,平均亏损 -1.90 点,盈亏比约 2.99:1。所有技能都被动用过,不存在某技能被闲置。 代理者动作按最大预测奖励做贪婪式选择,调度器因此输出完整概率分布、几乎消除了采样期的随机性——这也正是训练调度器的方式。 换用贪婪式技能选择后,盈利因子抬到 1.80,盈利占比 +0.91% 至 53.55%,平均盈利交易增至 3.08。外汇与贵金属属高风险品种,以上为历史样本统计,后续实盘表现可能偏离。

◍ 演示模型离实盘还有多远

DADS 这套无监督技能探索在 MT5 里跑通了,构建的模型在训练集之外也展现出盈利倾向,说明动态感知技能的确能学到可预测、易调度的行为模式。 但必须点明:外汇和贵金属是高杠杆高风险品种,文中所有 MQL5 程序只是演示算法工作方式,没有经过实盘风控与滑点校验,直接挂真仓可能迅速回撤。 想验证的人,开 MT5 把文中神经网络模块加载到 EURUSD 十五分钟图,先跑离线回测看技能聚类的稳定性,别急着接订单执行。

「随包附带的七个文件」

这套 LSTM 预测方案不是单文件 EA,而是拆成了 7 个组件一起跑。前三个是智能交易系统:Research.mq5 负责在策略测试器里收集样本,Study.mq5 在真实模式下做模型训练,Test.mq5 用来回测验证;后四个是类库,Trajectory.mqh 定义系统状态结构,FQF.mqh 管完全参数化模型的工作调度,NeuroNet.mqh 封装神经网络创建逻辑,NeuroNet.cl 则是 OpenCL 端的核函数代码库。 压缩包 MQL5.zip 体积约 1176.93 KB,直接在 MT5 里解压到 MQL5 目录就能看到对应文件。不少人在测试时碰到过 OnInit 返回非零代码 1 的报错,根因往往是没先跑 Research.mq5 采集样本就直接上 Study 或 Test——顺序错了 EA 找不到前置数据文件就会初始化失败。 外汇与贵金属市场杠杆高、滑点跳空频繁,这类神经网络模型在历史样本上表现好,不等于实盘能稳定复现,任何信号都只是概率倾向。开 MT5 前先确认显卡 OpenCL 可用,否则 NeuroNet.cl 那段并行训练会直接拖垮回测速度。

常见问题

在每根K线结束做一次状态回收,并单独判断止损条件提前 break,避免内存堆积和无效迭代。
可能正常,外推样本与训练分布偏移会导致泛化下降,建议看概率倾向而非绝对命中。
小布可替你加载样本并跑回放诊断,把状态推进与止损退出结果直接呈现在品种页,你只看结论。
倾向每千步打印一次,既能监控又不频繁刷屏,具体看数据量大小微调。
缺实时滑点、点差与仓位约束,外汇贵金属高风险,演示仅验证思路不可直接跟单。