从头开始开发智能交易系统(第 12 部分):时序与交易(I)·进阶篇
⏱️

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

(2/3)· 传统看盘依赖双屏且易被成交噪音干扰,本篇用子窗口把时序直接叠进交易图表

实战向 第 2/3 篇
很多交易者看盘时只盯完整合约,却忽略了迷你合约里更干净的价格节奏,等发现时已错过起涨点。双屏分看还容易在止损触发那瞬漏掉关键走势。把两套数据流硬塞进人脑,疲劳出错只是时间问题。

◍ 给子窗口指标挂上短名与自动创建逻辑

在 MT5 写自定义指标时,先用 input string 把短名暴露给用户在属性框改,比写死在代码里灵活。下面这段把 "SubSupport" 作为默认短名,OnInit 里通过 IndicatorSetString(INDICATOR_SHORTNAME, user01) 写进指标缓存,图表上显示的名称就跟着用户输入走。 OnCalculate 这里直接 return rates_total,说明本小节只搭骨架,还没往价格数组里填任何计算值;rates_total 是当前图表可用 K 线总数,返回它就告诉终端“已处理完所有柱”。 类 C_FnSubWin 的私有成员 m_szIndicator 存指标名,m_SubWin 存子窗口索引。Create 方法里先用 ChartWindowFind 按名找窗口,返回 -1 表示还没挂;此时用 ChartGetInteger(CHART_WINDOWS_TOTAL) 拿到已有子窗口数,把 iCustom 生成的指标句柄通过 ChartIndicatorAdd 塞进新窗口,并把索引回写 m_SubWin。 注意外汇与贵金属杠杆高、滑点跳空频繁,这类自动挂窗口的代码在实盘前务必在策略测试器用至少 3 个月 tick 数据跑一遍,确认不会重复建窗导致资源泄漏。

MQL5 / C++
class="kw">input class="type">class="kw">string user01 = "SubSupport";                    class=class="str">"cmt">//短名
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
{
        IndicatorSetString(INDICATOR_SHORTNAME, user01);
        
        class="kw">return INIT_SUCCEEDED;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnCalculate(class="kw">const class="type">int rates_total, class="kw">const class="type">int prev_calculated, class="kw">const class="type">int begin, class="kw">const class="type">class="kw">double &price[])
{
        class="kw">return rates_total;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Daniel Jose"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "C_Terminal.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class C_FnSubWin
{
        class="kw">private :
                class="type">class="kw">string  m_szIndicator;
                class="type">int     m_SubWin;
class=class="str">"cmt">//+------------------------------------------------------------------+
                class="type">void Create(class="kw">const class="type">class="kw">string szIndicator)
                {
                        class="type">int i0;
                        m_szIndicator = szIndicator;
                        if ((i0 = ChartWindowFind(Terminal.Get_ID(), szIndicator)) == -class="num">1)
                            ChartIndicatorAdd(Terminal.Get_ID(), i0 = (class="type">int)ChartGetInteger(Terminal.Get_ID(), CHART_WINDOWS_TOTAL), iCustom(NULL, class="num">0, "::" + def_Resource, szIndicator));
                        m_SubWin = i0;
                }
class=class="str">"cmt">//+------------------------------------------------------------------+
        class="kw">public  :
class=class="str">"cmt">//+------------------------------------------------------------------+
                C_FnSubWin()
                {
                        m_szIndicator = NULL;
                        m_SubWin = -class="num">1;
                }
class=class="str">"cmt">//+------------------------------------------------------------------+

「子窗口句柄的清理与复用逻辑」

在 MT5 的 EA 架构里,自建副图子窗口如果只建不拆,切换周期或重载 EA 时会留下僵尸指标引用,导致 ChartIndicatorDelete 找不到目标而静默失败。下面这段把子窗口的生命周期收口放在析构和显式 Close 两道闸上。 ~C_FnSubWin() 析构直接调 Close(),Close() 里先判断 m_SubWin >= 0 才执行 ChartIndicatorDelete(Terminal.Get_ID(), m_SubWin, m_szIndicator),随后把 m_SubWin 置回 -1。这个 -1 就是「无窗口」哨兵值,GetIdSubWinEA 碰到 m_SubWin < 0 且传了指标名才会 Create,避免重复建窗。 ExistSubWin 用 return m_SubWin >= 0 一行给出布尔态,调用方不必碰内部变量。C_SubWindow 公有 Close 先转交 m_fnSubWin.Close() 再 CloseAlls(),说明子类在拆指标前还顺手清掉自己挂的浮动物件。 开 MT5 把这段塞进你的面板基类,重载 EA 十次观察副图数是否恒为 1;若偶尔变 2,八成是 m_SubWin 没在 OnInit 失败分支复位。外汇与贵金属品种上跑这类自绘界面前请先认清滑点与重连风险,副图泄漏本身不致命但会拖慢报价刷新。

MQL5 / C++
~C_FnSubWin()
  {
  Close();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
  class="type">void Close(class="type">void)
  {
  if (m_SubWin >= class="num">0) ChartIndicatorDelete(Terminal.Get_ID(), m_SubWin, m_szIndicator);
  m_SubWin = -class="num">1;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">inline class="type">int GetIdSubWinEA(class="kw">const class="type">class="kw">string szIndicator = NULL)
  {
  if ((szIndicator != NULL) && (m_SubWin < class="num">0)) Create(szIndicator);
  class="kw">return m_SubWin;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">inline class="type">bool ExistSubWin(class="type">void) class="kw">const { class="kw">return m_SubWin >= class="num">0; }
class=class="str">"cmt">//+------------------------------------------------------------------+
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "C_ChartFloating.mqh"
class="macro">#include <NanoEA-SIMD\Auxiliar\C_FnSubWin.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class C_SubWindow : class="kw">public C_ChartFloating
{
class=class="str">"cmt">//+------------------------------------------------------------------+
  class="kw">private :
    C_FnSubWin      m_fnSubWin;
class=class="str">"cmt">//+------------------------------------------------------------------+
  class="kw">public  :
class=class="str">"cmt">//+------------------------------------------------------------------+
    ~C_SubWindow()
    {
    Close();
    }       
class=class="str">"cmt">//+------------------------------------------------------------------+
    class="type">void Close(class="type">void)
    {
    m_fnSubWin.Close();
    CloseAlls();

EA 子窗口的取号与存在判定

在 MT5 的 EA 封装里,支撑位子窗口的识别靠两个 inline 接口直连底层函数。GetIdSubWinEA 写死传入 "SubWinSupport" 字符串,回头拿到的是该子窗口在图表上的唯一整数 ID,方便后续往里画线或读坐标。 ExistSubWin 则是一行 const 包装,直接返 m_fnSubWin.ExistSubWin() 的布尔值。实盘里若子窗口没创建成功(比如手动删了指标窗),这句能让你在 OnTick 前就短路退出,避免空指针式报错。 外汇与贵金属波动剧烈、杠杆高风险大,这类窗口存在性检查建议放在初始化和周期切换时各跑一次,别只信启动那一下。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">inline class="type">int GetIdSubWinEA(class="type">void)
  {
   class="kw">return m_fnSubWin.GetIdSubWinEA("SubWinSupport");
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">inline class="type">bool ExistSubWin(class="type">void) class="kw">const { class="kw">return m_fnSubWin.ExistSubWin(); }
class=class="str">"cmt">//+------------------------------------------------------------------+
};

◍ 把成交流变成一分钟虚拟烛条

C_TimesAndTrade 类在 EA 里第一件事是确认自定义子窗口是否存在,没有就创建。初始化时调用 Init,传入缩放参数,默认 2,超过 5 按 5 封顶,低于 0 按 0 处理。 创建自定义品种的路径固定在 Custom\Robot\_&lt;品名&gt;,源数据来自真实品种,建完先塞进 Market Watch 才能画图。下面这段就是类里干这事的核心。 捕获函数只认带成交量的 tick:出价/要价跳动但没量就不存,纯挂单导致的价格强拉也不记。每根烛条对应一分钟虚拟时间,不是真实毫秒,只是按比例每分钟重绘一次,图形最小粒度就是 1 分钟。 烛条高低影线 = 成交瞬间的点差,开盘价 = 实际成交价。代码里对成交量做了小幅缩放避免比例溢出,你可以按具体资产改这个系数。系统跑起来能看到四种配置,它们分别对应什么下篇再拆,但当前版本已能密集使用。 外汇与贵金属杠杆高、滑点突变频繁,这类自定义时序图只作盘口微观参考,不构成方向判断。

MQL5 / C++
class="type">void Init(class="kw">const class="type">int iScale = class="num">2)
{
	if (!ExistSubWin())
	{
		CreateCustomSymbol();
		CreateChart();
	}
	ObjectSetInteger(Terminal.Get_ID(), m_szObjName, OBJPROP_CHART_SCALE, (iScale > class="num">5 ? class="num">5 : (iScale < class="num">0 ? class="num">0 : iScale)));
}
class="kw">inline class="type">void CreateCustomSymbol(class="type">void)
{
	m_szCustomSymbol = "_" + Terminal.GetFullSymbol();
	SymbolSelect(Terminal.GetFullSymbol(), true);
	SymbolSelect(m_szCustomSymbol, class="kw">false);
	CustomSymbolDelete(m_szCustomSymbol);
	CustomSymbolCreate(m_szCustomSymbol, StringFormat("Custom\\Robot\\%s", m_szCustomSymbol), Terminal.GetFullSymbol());
	CustomRatesDelete(m_szCustomSymbol, class="num">0, LONG_MAX);
	CustomTicksDelete(m_szCustomSymbol, class="num">0, LONG_MAX);
	SymbolSelect(m_szCustomSymbol, true);
};
让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到买卖成交噪音和迷你合约节奏的同步标记,你只需在信号出现时做决策。

常见问题

迷你合约过滤了部分大单噪音,呈现的买卖流更直观,但推动市场的根源仍在完整合约,所以两者需结合观测。
需修改 C_Terminal 类开放完整合约资产访问,再新建支持文件生成独立窗口类,避免与原有指标体系冲突。
可以,小布在品种页内置了成交方向高亮和迷你合约同步视图,省去你自己写类的重复劳动。
把时序直接叠到交易图表子窗口,减少双屏切换,并用程序高亮停止位触发瞬间的成交分布,降低人工追踪负担。