从头开始开发智能交易系统(第 13 部分):时序与交易(II)(基础篇)
用 OrderSend 把信号变成挂单
在 MT5 里把价格行为信号落地成实际仓位,核心动作是调用 OrderSend。它不像老版本 EA 那样直接市价吃单,而是先构造一个 MqlTradeRequest 结构,再交给 CTrade 或底层接口去执行,这样你能精确控制挂单类型、价位与失效时间。 下面这段演示了如何用当前 Ask 上方 10 点挂一个限价买单(BUY_LIMIT),并附带了止损与注释。注意:外汇与贵金属杠杆高,挂单未成交前也可能因跳空直接触发,实盘前务必在策略测试器跑一遍。 [CODE] MqlTradeRequest request; // 交易请求结构 ZeroMemory(request); // 清空结构防止脏数据 request.action = TRADE_ACTION_PENDING; // 动作:下挂单 request.type = ORDER_TYPE_BUY_LIMIT; // 挂单类型:限价买 request.symbol = Symbol(); // 当前图表品种 request.volume = 0.1; // 手数 0.1 request.price = SymbolInfoDouble(Symbol(), SYMBOL_ASK) + 10 * _Point; // 挂单价=卖价+10点 request.sl = 0; // 暂不设止损 request.tp = 0; // 暂不设止盈 request.comment= "PB_signal"; // 注释:pin bar 信号 OrderSend(request, result); // 发送请求,result 接收回执 [/CODE] 逐行看:ZeroMemory 是防上次循环残留;TRADE_ACTION_PENDING 与 ORDER_TYPE_BUY_LIMIT 的组合决定这是限价单而非市价单;挂单价用 SYMBOL_ASK 加 10*_Point,点值随品种变动,XAUUSD 的 _Point 是 0.01,EURUSD 是 0.00001,写死 10 点在不同品种上距离不同。 想验证就打开 MT5 的 MetaEditor,新建 EA 把上面代码塞进 OnTick,把图表切到 EURUSD 的 M1,观察市场报价窗口是否出现该挂单。若没出来,优先查 result.retcode——常见 10015 是参数非法,多半是 price 超出了最小挂单距离限制。
class="type">MqlTradeRequest request; class=class="str">"cmt">// 交易请求结构 ZeroMemory(request); class=class="str">"cmt">// 清空结构防止脏数据 request.action = TRADE_ACTION_PENDING; class=class="str">"cmt">// 动作:下挂单 request.type = ORDER_TYPE_BUY_LIMIT; class=class="str">"cmt">// 挂单类型:限价买 request.symbol = Symbol(); class=class="str">"cmt">// 当前图表品种 request.volume = class="num">0.1; class=class="str">"cmt">// 手数 class="num">0.1 request.price = SymbolInfoDouble(Symbol(), SYMBOL_ASK) + class="num">10 * _Point; class=class="str">"cmt">// 挂单价=卖价+10点 request.sl = class="num">0; class=class="str">"cmt">// 暂不设止损 request.tp = class="num">0; class=class="str">"cmt">// 暂不设止盈 request.comment= "PB_signal"; class=class="str">"cmt">// 注释:pin bar 信号 OrderSend(request, result); class=class="str">"cmt">// 发送请求,result 接收回执
◍ 为什么要把盘口信息拆成文本
前一篇讲了一种替代性图表组织方式,用来快速解释市场成交,但那套系统还缺一块:它没法把某些关键信息直接以可读形式呈现。这类信息硬塞进图表会显得杂乱,解释成本反而更高。 更实际的做法是走经典路线——用文本格式的数值把数据吐出来。当前智能交易系统没有这个能力,所以这一步必须补上。 为了不把前一篇搞得太重,我当时故意略掉了一些普通用户用不上的细节。但那些细节在排查真实盘口行为时偶尔是关键,这一节就把系统扩完整,让需要的人能直接读到。 外汇与贵金属市场波动剧烈、杠杆风险高,这类文本诊断只用于辅助理解成交结构,不构成方向判断。
「读盘时别漏掉的点差与止损跳空」
把 MT5 的报价窗放大看,会撞见一个反直觉现象:某一刻买价卖价都动了,但成交流水里只有一笔成交,甚至零成交。大多数人切到普通蜡烛图后就彻底看不见这种缝隙——图表看起来连续运转,实则盘口只是在某些价位挂单等待,触价瞬间抢先手,价动而量不动。 在「时序与交易」面板里能数出四类烛条形态,本该有第五类:直接市价单造成的烛条被系统剔除,因为它不驱动市场移动。剩下四类里,常出现影线够不着实体的情况——影线由点差(买卖差)勾勒,若真按点差成交应是十字星(DOJI),那种才是第五类被隐藏的。 实体与影线留空隙,往往不是系统 bug。对照盘口能发现:一连串既不碰买价也不碰卖价的触发点,全是止损单被击发。价格因此跳段,普通形态分析会误读成趋势加力,实际它可能迅速回补再把你扫损。 大批量「实体不触影线」的烛条,就是止损簇发的指纹;具体哪一秒发生没法精准捕捉,但用买卖价位移能反推成因。若你真把读盘当信号源,这一层不能瞎。 光靠烛条还不够:盘有时在等数据而显疲态,「时序与交易」裸看很难解读。把盘口数据建模后,面板会补出完整图景且提速,对读盘派是关键一步。
给时序与交易类补上画布与缓存
要在 MT5 里跑这套微观结构监控,得先给 C_TimesAndTrade 类加几个成员。原工程只引了子窗口辅助类,缺一块画布能力,所以高亮处补了 C_Canvas 的引用,并定义了 macro_Limits 取低 8 位、def_MaxInfos 为 257 条上限。 新增的 m_CountStrings 计数器与两个结构体 st0、st1 是核心。st0 存每笔交易的时间字符串和标记,st1 内嵌 C_Canvas 实例并记下了区域宽度、X 偏移和最大 Y 坐标——这些决定了文字落在副图的什么位置。 C_Canvas 本身不够用,作者往里塞了四个子例程专门负责建文本,逻辑很直白:在指定区域写字符串。初始化流程也微调了,把画布名和区域参数在构造阶段就绑定好。 替补程序(时序与交易主逻辑)同样改了:核心子例程里新增的代码会统计两次成交间隔的毫秒数,以及未引起价变的成交笔数。若这两个值同时放大,往往意味着换手在衰减,你能比看普通报价的人更早察觉。 m_CountInfos 这类计数器在实时系统里不强制做边界测试,新数据来了直接自增。这是处理效率上的妥协——实盘 EA 对延迟敏感,少一次判断就少一点开销,积累起来影响可观。 编译后副图上会浮现微观结构纹路。作者自认交易经验有限,未能直接靠这些纹路获利,但盘口信息被大幅过滤后,读数据速度确实提升,贵金属与外汇的高风险环境下,早半秒看懂盘口可能就少挨一次止损。
class="macro">#include <NanoEA-SIMD\Auxiliar\C_FnSubWin.mqh> class="macro">#include <NanoEA-SIMD\Auxiliar\C_Canvas.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class C_TimesAndTrade : class="kw">private C_FnSubWin { class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define def_SizeBuff class="num">2048 class="macro">#define macro_Limits(A) (A & 0xFF) class="macro">#define def_MaxInfos class="num">257 class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">private : class="type">class="kw">string m_szCustomSymbol, m_szObjName; class="type">char m_ConnectionStatus; class="type">class="kw">datetime m_LastTime; class="type">class="kw">ulong m_MemTickTime; class="type">int m_CountStrings; class="kw">struct st0 { class="type">class="kw">string szTime; class="type">int flag; }m_InfoTrades[def_MaxInfos]; class="kw">struct st1 { C_Canvas Canvas; class="type">int WidthRegion, PosXRegion, MaxY; class="type">class="kw">string szNameCanvas;