从头开始开发智能交易系统(第 12 部分):时序与交易(I)(基础篇)
📘

从头开始开发智能交易系统(第 12 部分):时序与交易(I)(基础篇)

第 1/3 篇

用 TimeCurrent 锁住下单节奏

在 MT5 里写 EA,最容易被忽略的不是信号,而是时间。很多新手把下单逻辑直接塞进 OnTick,结果同一根蜡烛内被 tick 狂轰滥炸,重复开仓、滑点放大。 核心函数是 TimeCurrent(),它返回终端当前时间(秒级 Unix 时间戳)。把它和上一笔成交的 Time 字段做差,就能强制限定最小下单间隔。下面这段代码演示了如何过滤掉 5 秒内的重复触发: [CODE]datetime lastTradeTime = 0; int minInterval = 5; // 秒 void OnTick() { if (TimeCurrent() - lastTradeTime < minInterval) return; // 间隔太短,直接退出 // 此处放你的交易逻辑 lastTradeTime = TimeCurrent(); }[/CODE] datetime lastTradeTime 先置 0,保证 EA 启动后第一 tick 就能跑;minInterval 设为 5 只是示例,实盘做黄金或外汇时,建议先到策略测试器用 2022 年 7 月那段每天 1.8k+ tick 的行情回测,看 3 秒和 10 秒间隔分别对成交数的影响。 外汇与贵金属杠杆高、跳空频繁,靠时间节流不等于控了风险,只是少交无效手续费。把间隔参数外挂到输入变量,用小布盯盘跑不同品种对比,比拍脑袋定数靠谱。

MQL5 / C++
class="type">class="kw">datetime lastTradeTime = class="num">0;
class="type">int minInterval = class="num">5; class=class="str">"cmt">// 秒

class="type">void OnTick()
  {
   if (TimeCurrent() - lastTradeTime < minInterval)
      class="kw">return; class=class="str">"cmt">// 间隔太短,直接退出
   class=class="str">"cmt">// 此处放你的交易逻辑
   lastTradeTime = TimeCurrent();
  }

◍ 为什么老式看盘正在拖垮你的注意力

看盘曾是不少交易员在不同阶段依赖的方法。运用得当时,它比纯烛条观测更可能提供一致的利润增长路径,但过程复杂、乏味,且要求持续专注。时间一长,人不可避免地走神,观测出错的概率随之上升。 问题的核心不是看盘本身,而是信息量。迷你合约里的数据流并不实际,我们往往直接盯完整合约——因为它们才是推动行情的根。即便如此,系统依旧繁琐,需要高度集中。 更严峻的是止损触发时:屏幕信息滚动极快,你可能漏掉一段走势。对外汇、贵金属这类高波动品种,这种漏看会直接放大高风险敞口。

「把成交噪音搬回主图」

MT5 对迷你合约有独立的成交监控面板,读起来比完整合约干净,但实战里完整合约的成交流更值得盯。问题在于:完整合约的逐笔成交混着买卖噪音,红圈是卖、蓝圈是买、绿圈是直接挂单成交,这些信息散在独立窗口,逼着你分屏看两块屏幕。 分屏在多数时候算优势,可一旦你要同步价格行为和成交异动,两个窗口的割裂会直接拖慢反应。外汇与贵金属杠杆高、滑点跳空频繁,视线切换本身就是风险敞口。 可行的做法是把成交分布指标直接画在交易图表上,和 K 线同频。这样不用切窗口,圈注的买卖成交和直接订单就能对照支撑阻力一眼看完,验证路径很直接:开 MT5 把成交指标拖到主图,调出红蓝绿配色即可。

用重命名技巧绕过指标文件创建子窗口

在 MT5 里给 EA 做专属子窗口,常规做法是每个窗口配一个指标文件,数量一多就很啰嗦。这里改走一条更省事的路:让 C_FnSubWin 类按你给的短名去搜指标,而不是写死 SubWinSupport,这样换名字就能复用同一套逻辑,不必为每一个子窗口单独建文件。 具体落地时,先动 C_Terminal 类,补上访问完整合约资产的代码;再新建一个支持文件,把指标名改成体系外独立窗口用的名字。EA 侧则用新类顶替老的 C_SubWindow,新类里的指标定义支持模板,高亮部分就是关键改动。 下面这段 CurrentSymbol 函数演示了如何根据 _Symbol 前缀(WDO/DOL/WIN/IND)拼出当前和关联合约名,并循环推算未过期合约。注意 WDO 对应 DOL、WIN 对应 IND,月份代码串分别是 FGHJKMNQUVXZ 和 GJMQVZ,年份从 2000 起算。 [CODE] 中的 GetFullSymbol 内联函数直接返回 m_Infos.szFullSymbol,配合前面高亮赋值,就能在交易计划里拿到带后缀的完整合约名。外汇与贵金属波动剧烈、杠杆风险高,这类自动拼合约的逻辑在实盘前务必用策略测试器跑一遍到期日判断。

MQL5 / C++
<span class="keyword">class="type">void</span> CurrentSymbol(<span class="keyword">class="type">void</span>)
{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="predefines">class="type">MqlDateTime</span> mdt1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">string</span> sz0, sz1<span style="background-class="type">class="kw">color:rgb(class="num">119, class="num">183, class="num">83);">, sz2</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">datetime</span> dt = <span class="functions">TimeLocal</span>();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sz0 = <span class="functions">StringSubstr</span>(m_Infos.szSymbol = <span class="predefines">_Symbol</span>, <span class="number">class="num">0</span>, <span class="number">class="num">3</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-class="type">class="kw">color:rgb(class="num">119, class="num">183, class="num">83);">m_Infos.szFullSymbol = <span class="predefines">_Symbol</span>;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_Infos.TypeSymbol = ((sz0 == <span class="class="type">class="kw">string">"WDO"</span>) || (sz0 == <span class="class="type">class="kw">string">"DOL"</span>) ? WDO : ((sz0 == <span class="class="type">class="kw">string">"WIN"</span>) || (sz0 == <span class="class="type">class="kw">string">"IND"</span>) ? WIN : OTHER));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span> ((sz0 != <span class="class="type">class="kw">string">"WDO"</span>) &amp;&amp; (sz0 != <span class="class="type">class="kw">string">"DOL"</span>) &amp;&amp; (sz0 != <span class="class="type">class="kw">string">"WIN"</span>) &amp;&amp; (sz0 != <span class="class="type">class="kw">string">"IND"</span>)) <span class="keyword">class="kw">return</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-class="type">class="kw">color:rgb(class="num">119, class="num">183, class="num">83);">sz2 = (sz0 == <span class="class="type">class="kw">string">"WDO"</span> ? <span class="class="type">class="kw">string">"DOL"</span> : (sz0 == <span class="class="type">class="kw">string">"WIN"</span> ? <span class="class="type">class="kw">string">"IND"</span> : sz0));</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sz1 = (sz2 == <span class="class="type">class="kw">string">"DOL"</span> ? <span class="class="type">class="kw">string">"FGHJKMNQUVXZ"</span> : <span class="class="type">class="kw">string">"GJMQVZ"</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">TimeToStruct</span>(<span class="functions">TimeLocal</span>(), mdt1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span> (<span class="keyword">class="type">int</span> i0 = <span class="number">class="num">0</span>, i1 = mdt1.year - <span class="number">class="num">2000</span>;;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_Infos.szSymbol = <span class="functions">StringFormat</span>(<span class="class="type">class="kw">string">"%s%s%d"</span>, sz0, <span class="functions">StringSubstr</span>(sz1, i0, <span class="number">class="num">1</span>), i1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-class="type">class="kw">color:rgb(class="num">119, class="num">183, class="num">83);">m_Infos.szFullSymbol = <span class="functions">StringFormat</span>(<span class="class="type">class="kw">string">"%s%s%d"</span>, sz2, <span class="functions">StringSubstr</span>(sz1, i0, <span class="number">class="num">1</span>), i1);</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span> (i0 &lt; <span class="functions">StringLen</span>(sz1)) i0++; <span class="keyword">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i0 = <span class="number">class="num">0</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i1++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span> (macroGetDate(dt) &lt; macroGetDate(<span class="functions">SymbolInfoInteger</span>(m_Infos.szSymbol, <span class="macro">SYMBOL_EXPIRATION_TIME</span>))) <span class="keyword">class="kw">break</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
}
<span class="comment">class=class="str">"cmt">// ... 类代码 ...
</span>
<span style="background-class="type">class="kw">color:rgb(class="num">119, class="num">183, class="num">83);"><span class="keyword">class="kw">inline</span> <span class="keyword">class="type">class="kw">string</span> GetFullSymbol(<span class="keyword">class="type">void</span>) <span class="keyword">class="kw">const</span> { <span class="keyword">class="kw">return</span> m_Infos.szFullSymbol; }</span>
<span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">copyright</span> <span class="class="type">class="kw">string">"Daniel Jose class="num">07-class="num">02-class="num">2022 (A)"</span>
<span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">version</span>&nbsp;&nbsp; <span class="class="type">class="kw">string">"class="num">1.00"</span>
<span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">description</span> <span class="class="type">class="kw">string">"This file only serves as supporting indicator for SubWin"</span>
<span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">indicator_chart_window</span>
<span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">indicator_plots</span> <span class="number">class="num">0</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>

常见问题

在开仓前记录当前K线起始时间,只有当 TimeCurrent 对应的K线时间与上次记录不同才允许下单,并在成交后更新记录。
分散的子窗口延长了视线跳转和认知整合时间,突发波动易被忽略;把关键成交噪音搬回主图可减少切换成本,但外汇贵金属本身高风险,仍需警惕滑点。
可以,小布已内置时序锁单与主图成交噪音的AIGC诊断,打开对应品种页即可直接看到节奏异常和噪音提示。
复制原指标文件并改一个不同文件名再加载,MT终端会将其识别为新指标从而单独开子窗口,避免原文件占用冲突。
结合时序过滤掉同根K线内的重复噪音,仅保留首次成交峰值;概率上真突破常伴随后续两根K线同向延续,但外汇贵金属高风险,须用止损约束。