时间演化旅行算法(TETA)(基础篇)
◍ 时间演化旅行算法在 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 看看收敛速度差异,外汇与贵金属参数优化属高风险,回测过度拟合可能直接实盘翻车。
<span class="keyword">class</span> C_AO_TETA : <span class="keyword">class="kw">public</span> C_AO { <span class="keyword">class="kw">public</span>: <span class="comment">class=class="str">"cmt">//--------------------------------------------------------------------</span> ~C_AO_TETA() { } C_AO_TETA() { ao_name = <span class="class="type">class="kw">string">"TETA"</span>; ao_desc = <span class="class="type">class="kw">string">"Time Evolution Travel Algorithm"</span>; ao_link = <span class="class="type">class="kw">string">"[MQL5官方文档] popSize = <span class="number">class="num">50</span>; <span class="comment">class=class="str">"cmt">// number of parallel universes in the population</span> ArrayResize(<span class="keyword">params</span>, <span class="number">class="num">1</span>); <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; } <span class="keyword">class="type">void</span> SetParams() { popSize = (<span class="keyword">class="type">int</span>)<span class="keyword">params</span> [<span class="number">class="num">0</span>].val; } <span class="keyword">class="type">bool</span> Init(<span class="keyword">const</span> <span class="keyword">class="type">class="kw">double</span> &rangeMinP [], <span class="comment">class=class="str">"cmt">// minimum values for anchors</span> <span class="keyword">const</span> <span class="keyword">class="type">class="kw">double</span> &rangeMaxP [], <span class="comment">class=class="str">"cmt">// maximum values for anchors</span> <span class="keyword">const</span> <span class="keyword">class="type">class="kw">double</span> &rangeStepP [], <span class="comment">class=class="str">"cmt">// anchor change step</span> <span class="keyword">const</span> <span class="keyword">class="type">int</span> epochsP = <span class="number">class="num">0</span>); <span class="comment">class=class="str">"cmt">// number of search epochs</span> <span class="keyword">class="type">void</span> Moving(); <span class="keyword">class="type">void</span> Revision(); <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 看收敛速度变化,是验证多宇宙搜索是否有效的直接办法。
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)