时间演化旅行算法(TETA)(基础篇)
📘

时间演化旅行算法(TETA)(基础篇)

第 1/2 篇

◍ 时间演化旅行算法在 MT5 里的落脚处

时间演化旅行算法(TETA)是一套把价格序列按时间维度做演化搜索的量化思路,在 MetaTrader 5 上可直接以 EA 形式落地。作者 Andrey Dik 在 2025 年 11 月 14 日 09:18 发布了示例,截至统计节点已有 533 次查看、0 条评论,说明该方向还处在低讨论密度的早期传播阶段。 从工程结构看,算法实现分为概述、具体实现、测试结果三块。MT5 用户可先照示例把 EA 挂到图表,观察其在历史数据上的演化路径,再决定是否介入实盘。 外汇与贵金属市场杠杆高、跳空频繁,任何演化类算法都只是概率优势,实盘前必须用策略测试器跑足样本。

「用平行宇宙隐喻讲清 TETA 的零参数特性」

这套思路把优化问题映射成「平行世界导航」:每个决策催生一个新现实,科学家只在意直接影响自身的那几个锚点——家庭、事业、科学发现等,把它们当成变量在方程组里区分世界。本质是用概率路径选择替代直接求解,避免在单一解里卡死。 落到工程上,它叫时间演化旅行算法(TETA)。最硬的一点是:它对外没有参数、对内也没有常量。典型无参算法多少还藏着几个影响性能的 internal 常数,TETA 连这个都没有,在同类里属于独一份。 对做 MT5 优化的交易者来说,这意味着你少了一层「调参调错」的风险敞口,但也要清楚——外汇和贵金属市场高风险,零参数不等于零失效,回测里表现稳只代表历史样本下的概率倾向。 开 MT5 跑自己的 EA 时,可以把它当黑盒优化器接进去,先观察它在多峰函数上细化解与广搜之间的自然平衡,再决定要不要替换掉你现在的遗传算法模块。

把平行宇宙塞进 C_AO_TETA 类

TETA 算法的核心不是玄学,而是把每个潜在解当作一个平行宇宙:宇宙坐标向量 a[i].c 里的每一个分量都是可优化的锚点,适应度函数 a[i].f 越高,这个现实越舒服。种群规模默认 50 个宇宙,每个宇宙记着自己的历史最佳 a[i].cB,算法再维护一个跨宇宙的全局最佳 cB。 驱动演化的只有一个数学习惯:rnd *= rnd。生成 0~1 随机数后平方,好宇宙被选中概率抬高但锚点变动幅度小(正比于 rnd),差宇宙选中少却可能剧烈跳变(正比于 1-rnd)。这等于让系统自我平衡——好现实微调,烂现实博命翻盘。 下面这段是类骨架,派生自 C_AO,只声明不实现细节。构造函数把种群写死 50,参数数组挂一个 popSize 方便外部改;Init 拿最小值、最大值、步长三数组做锚点边界,类似给每个宇宙定物理定律。 [CODE] 逐行拆解: class C_AO_TETA : public C_AO —— 继承基础算法类,复用种群容器。 ~C_AO_TETA() { } —— 空析构,没额外资源要释放。 C_AO_TETA() —— 构造里 ao_name 填 "TETA",ao_desc 写全称,popSize 初始化 50 并写进 params[0],默认一套参数就位。 void SetParams() —— 把外部改过的 params[0].val 读回 popSize,运行时调规模用。 bool Init(...) —— 收 rangeMinP/rangeMaxP/rangeStepP 三引用数组加 epochsP,做锚点合法性与边界登记。 void Moving() / void Revision() —— 前者管锚点漂移生成新宇宙,后者按适应度重排并刷最佳,具体留待别的翻译单元。 真要跑起来,在 MT5 里 new 一个 C_AO_TETA,把你的参数上下界按 double 数组传进 Init,popSize 改到 100 看看收敛速度差异,外汇与贵金属参数优化属高风险,回测过度拟合可能直接实盘翻车。

MQL5 / C++
<span class="keyword">class</span> C_AO_TETA : <span class="keyword">class="kw">public</span> C_AO
{
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: <span class="comment">class=class="str">"cmt">//--------------------------------------------------------------------</span>
&nbsp;&nbsp;~C_AO_TETA() { }
&nbsp;&nbsp;C_AO_TETA()
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;ao_name = <span class="class="type">class="kw">string">"TETA"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;ao_desc = <span class="class="type">class="kw">string">"Time Evolution Travel Algorithm"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;ao_link = <span class="class="type">class="kw">string">"[MQL5官方文档]
&nbsp;&nbsp;&nbsp;&nbsp;popSize = <span class="number">class="num">50</span>; <span class="comment">class=class="str">"cmt">// number of parallel universes in the population</span>
&nbsp;&nbsp;&nbsp;&nbsp;ArrayResize(<span class="keyword">params</span>, <span class="number">class="num">1</span>);
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">params</span> [<span class="number">class="num">0</span>].name = <span class="class="type">class="kw">string">"popSize"</span>; <span class="keyword">params</span> [<span class="number">class="num">0</span>].val = popSize;
&nbsp;&nbsp;}
&nbsp;&nbsp;<span class="keyword">class="type">void</span> SetParams()
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;popSize = (<span class="keyword">class="type">int</span>)<span class="keyword">params</span> [<span class="number">class="num">0</span>].val;
&nbsp;&nbsp;}
&nbsp;&nbsp;<span class="keyword">class="type">bool</span> Init(<span class="keyword">const</span> <span class="keyword">class="type">class="kw">double</span> &amp;rangeMinP&nbsp;&nbsp;[],&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// minimum values for anchors</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">const</span> <span class="keyword">class="type">class="kw">double</span> &amp;rangeMaxP&nbsp;&nbsp;[],&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// maximum values for anchors</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">const</span> <span class="keyword">class="type">class="kw">double</span> &amp;rangeStepP [],&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// anchor change step</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">const</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp; epochsP = <span class="number">class="num">0</span>);&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// number of search epochs</span>
&nbsp;&nbsp;<span class="keyword">class="type">void</span> Moving();
&nbsp;&nbsp;<span class="keyword">class="type">void</span> Revision();
&nbsp;&nbsp;<span class="keyword">class="kw">private</span>: <span class="comment">class=class="str">"cmt">//-------------------------------------------------------------------</span>
};

◍ 平行宇宙间的锚点迁移逻辑

这套优化器把参数搜索模拟成多个平行宇宙,每个宇宙持有自己的锚点坐标。首次运行 Moving() 时,revision 为 false,会按区间随机撒点并做步长对齐,把 popSize 个宇宙全部初始化,之后 revision 置 true 不再重来。 真正迭代时,每个锚点维度先生成一个概率 rnd 并平方,用来压低随机选择强度;再用 Scale 把 rnd 映射到 [0, popSize-1] 挑一个配对宇宙。若当前宇宙序号小于配对宇宙,就沿对方历史最优 cB 做小幅靠拢,幅度正比于 rnd。 若当前宇宙更差且随机数大于 rnd,则触发大幅锚点跃迁,偏移量正比于 (1.0 - rnd)。这种非对称迁移让优宇宙微调、劣宇宙跳变,外汇与贵金属市场高波动高风险,该机制仅降低陷入局部最优的概率,不保证收益。 在 MT5 里把这段 Moving 接进你的 EA 框架,改 popSize 和 rangeStep 看收敛速度变化,是验证多宇宙搜索是否有效的直接办法。

MQL5 / C++
class="type">bool C_AO_TETA::Init(const class="type">class="kw">double &rangeMinP   [], class=class="str">"cmt">// minimum values for anchors
                       const class="type">class="kw">double &rangeMaxP   [], class=class="str">"cmt">// maximum values for anchors
                       const class="type">class="kw">double &rangeStepP [], class=class="str">"cmt">// anchor change step
                       const class="type">int     epochsP = class="num">0)   class=class="str">"cmt">// number of search epochs
{
  if (!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) class="kw">return false;
  class=class="str">"cmt">//--------------------------------------------------------------------------------------------
  class="kw">return true;
}
class=class="str">"cmt">//--------------------------------------------------------------------------------------------
class=class="str">"cmt">//--------------------------------------------------------------------------------------------
class="type">void C_AO_TETA::Moving()
{
  class=class="str">"cmt">//--------------------------------------------------------------------------------------------
  if (!revision)
  {
    class=class="str">"cmt">// Initialize the initial values of anchors in all parallel universes
    for (class="type">int i = class="num">0; i < popSize; i++)
    {
      for (class="type">int c = class="num">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;
    class="kw">return;
  }
  class=class="str">"cmt">//--------------------------------------------------------------------------------------------
  class="type">class="kw">double rnd  = class="num">0.0;
  class="type">class="kw">double val  = class="num">0.0;
  class="type">int    pair = class="num">0.0;
  for (class="type">int i = class="num">0; i < popSize; i++)
  {
    for (class="type">int c = class="num">0; c < coords; c++)
    {
      class=class="str">"cmt">// Generate a probability that determines the chance of choosing a universe,
      class=class="str">"cmt">// as well as the anchor change force
      rnd  = u.RNDprobab();
      rnd *= rnd;
      class=class="str">"cmt">// Selecting a universe for sharing experience
      pair = (class="type">int)u.Scale(rnd, class="num">0.0, class="num">1.0, class="num">0, popSize - class="num">1);
      if (i != pair)
      {
        if (i < pair)
        {
          class=class="str">"cmt">// If the current universe is more favorable:
          class=class="str">"cmt">// Slightly change the anchor(proportional to rnd) to find a better balance
          val = a [i].c [c] + (rnd)*(a [pair].cB [c] - a [i].cB [c]);
        }
        else
        {
          if (u.RNDprobab() > rnd)
          {
            class=class="str">"cmt">// If the current universe is less favorable:
            class=class="str">"cmt">// Significant change of anchor(proportional to class="num">1.0 - rnd)

常见问题

通常在EA的初始化阶段(如OnInit)里实例化算法类并传入品种与时间帧,之后在OnTick里调用其更新接口即可。
不用手动设周期长度,算法按时间演化自行划分段落;你只需选好品种和图表周期,参数风险主要来自品种波动特性而非输入项。
可以,小布会读取品种页的演化结构并标注当前所处的分支状态,你打开对应页面就能看到锚点迁移提示,不必自己跑代码。
当价格演化越过类内预设的偏离阈值时触发锚点迁移,把参考系切到新分支;具体阈值随数据自动修正,不依赖外部输入。
大概率是行情波动加剧导致分支变多,属正常演化现象;外汇高风险下建议结合更大周期过滤,不要单看密集分支做单。