从头开始开发智能交易系统(第 30 部分):CHART TRADE 当作指标?·综合运用
📘

从头开始开发智能交易系统(第 30 部分):CHART TRADE 当作指标?·综合运用

第 3/3 篇

「用全局变量和定时器串起交易面板」

这段初始化逻辑把止损参数写进全局变量,再拉起交易视图对象,并通过 500 毫秒的毫秒级定时器维持面板响应。外汇与贵金属杠杆高,全局变量被多个 EA 共享时,误读其他程序的 def_GlobalVariableStop 可能导致仓位风控错位。 OnDeinit 里第一件事是 EventKillTimer(),否则 EA 卸载后定时器回调仍可能触发空指针。OnTimer 按编译开关分流:未集成图表交易时只跑 ChartTrade_ClickButton(),集成盘口阅读时刷新 VolumeAtPrice 并重连 TimesAndTrade。 鼠标事件里用位运算拆按键:0x01 是左键点击,0x04 是 Shift,0x08 是 Ctrl。当 Shift 与 Ctrl 状态不同(bKeyBuy != bKeySell)且未处于挂单挂载态时,非集成模式下直接读全局变量拼仓位——手数 = 杠杆全局变量 × 终端最小成交量,止盈止损取自 def_GlobalVariableTake / Stop。 让小布替你跑这套 把下面代码贴进 MT5 的 EA 模板,改 def_GlobalVariableLeverage 的值,能在 500ms 内看到手数随杠杆实时变;黄金 XAUUSD 点差跳变时建议先把定时器改 1000ms 降低开销。

MQL5 / C++
GlobalVariableSet(def_GlobalVariableStop, user22);
TradeView.Initilize();
GlobalVariableSet(def_GlobalVariableResult, TradeView.GetFinanceRoof());
EventSetMillisecondTimer(class="num">500);
class="macro">#endif
class="type">void OnDeinit(const class="type">int reason)
{
   EventKillTimer();
class="type">void OnTimer()
{
class="macro">#ifndef def_INTEGRATION_CHART_TRADER
   ChartTrade_ClickButton();
class="macro">#endif
class="macro">#ifdef def_INTEGRATION_TAPE_READING
   VolumeAtPrice.Update();
   TimesAndTrade.Connect();
class="macro">#endif
}
   case CHARTEVENT_MOUSE_MOVE:
      Mouse.GetPositionDP(dt, price);
      mKeys   = Mouse.GetButtonStatus();
      bEClick = (mKeys & 0x01) == 0x01;   class=class="str">"cmt">//Left mouse button click
      bKeyBuy = (mKeys & 0x04) == 0x04;   class=class="str">"cmt">//SHIFT pressed
      bKeySell= (mKeys & 0x08) == 0x08;   class=class="str">"cmt">//CTRL pressed
      if (bKeyBuy != bKeySell)
      {
         if (!bMounting)
         {
class="macro">#ifdef def_INTEGRATION_CHART_TRADER
            m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl);
class="macro">#else
            m_Selection.vol = GlobalVariableGet(def_GlobalVariableLeverage) * Terminal.GetVolumeMinimal();
            valueTp = GlobalVariableGet(def_GlobalVariableTake);
            valueSl = GlobalVariableGet(def_GlobalVariableStop);
            m_Selection.bIsDayTrade = EA_user23;
class="macro">#endif

◍ 切周期别把图表交易指标弄丢了

MT5 里只要动一下图表周期、改个 EA 参数,终端就会抛 OnDeinit 事件。对普通 EA 这通常无伤大雅,但如果你搭的是「EA 做服务器 + 指标做客户端」的架构,靠全局变量互相探活,原生 OnDeinit 会直接把那些变量删掉。 指标端靠 GlobalVariableCheck 轮询变量是否存在来判断 EA 是否还挂在图上。一旦 EA 触发 OnDeinit 先把变量删了,指标可能赶在 EA 重建变量前就被误判离线、自己从图表消失——这种诡异退出时有时无,复现概率随操作节奏浮动。 查官方逆初始化原因枚举后,解法很直接:在 OnDeinit 里对 reason 做 switch,仅当 reason 不是 REASON_CHARTCHANGE 时才清变量。这样切周期、重绘不会误杀,EA 和 Chart Trade 指标能稳稳共存。 不过参数层面的重新初始化仍可能拆掉指标,所以 EA 和指标都加载后,再去动 EA 输入参数意义不大,容易引发不可控卸载。外汇与贵金属品种波动剧烈,这类客户端架构的边界条件建议先在模拟盘验证。

MQL5 / C++
class="type">void OnDeinit(const class="type">int reason)
{
    EventKillTimer();
    Mouse.Destroy();
    TradeView.Finish();
class="macro">#ifndef def_INTEGRATION_CHART_TRADER
    GlobalVariableDel(def_GlobalVariableLeverage);
    GlobalVariableDel(def_GlobalVariableTake);
    GlobalVariableDel(def_GlobalVariableStop);
    GlobalVariableDel(def_GlobalVariableResult);
    GlobalVariableDel(def_GlobalVariableButton);
class="macro">#endif
class="macro">#ifdef def_INTEGRATION_TAPE_READING
    EventKillTimer();
class="macro">#endif 
}
class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam)
{
    if (!GlobalVariableCheck(def_GlobalVariableLeverage)) OnDeinit(REASON_INITFAILED);
    Chart.DispatchMessage(id, lparam, dparam, sparam);
}
class="type">void OnDeinit(const class="type">int reason)
{
    EventKillTimer();
    Mouse.Destroy();
    TradeView.Finish();
class="macro">#ifndef def_INTEGRATION_CHART_TRADER
    class="kw">switch (reason)
    {
        case REASON_CHARTCHANGE:
            break;
        class="kw">default:            
            GlobalVariableDel(def_GlobalVariableLeverage);
            GlobalVariableDel(def_GlobalVariableTake);
            GlobalVariableDel(def_GlobalVariableStop);
            GlobalVariableDel(def_GlobalVariableResult);
            GlobalVariableDel(def_GlobalVariableButton);
    };
class="macro">#endif
class="macro">#ifdef def_INTEGRATION_TAPE_READING
    EventKillTimer();
class="macro">#endif 
}

文档才是破局的底牌

做 MT5 自定义工具时,很多卡点看起来像死路——比如想让 EA 在退出前干净地卸掉声音模块,或者让平台用语音播报状态。实际上 MQL5 官方文档里大多埋着接口说明,只是没人愿意逐行读。 前面几篇拆过从 EA 里逐步抽离部件的过程,最后要移除的正是声音系统;若你没跟过系列,直接看源码会一头雾水,但对照文档里的 PlaySound / 资源释放规则就能理顺调用链。 外汇与贵金属杠杆高、滑点随机,任何自研系统都只是概率工具。下次碰到看似无解的需求,先翻文档再动手,比盲写代码省一半时间。

常见问题

多半是面板状态只存在当前图表对象里,切周期后对象被清掉;用全局变量存挂单与按钮状态,切回周期时再读回来就能恢复。
定时器只负责读全局变量并重绘界面,下单动作单独走事件函数;把刷新和发单解耦,就不会因定时触发导致重复开仓。
可以,小布能按你贴的面板逻辑指出哪些状态没进全局变量,并提示用定时器回读的重绘位置,省去你手动翻文档试错。
用「品种_周期_功能」前缀统一命名,并在面板初始化时列一遍已用名;正经文档模板比拍脑袋命名稳得多。
给按钮加双击确认和价格波动阈值锁,超出阈值当根K线禁止发单;外汇贵金属高风险,误触成本可能直接吃掉半月利润。