构建自动运行的 EA(第 02 部分):开始编码·综合运用
📘

构建自动运行的 EA(第 02 部分):开始编码·综合运用

第 3/3 篇

◍ 把资金额换算成可下单价差点数

在 MT5 的 EA 风控里,常需要把「允许亏损多少美元」转成「止损摆几个点」。下面这段内联函数就干了这件事:先用杠杆层数算出实际下单手数,再用资金除以手数和合约调整系数,得到价格层面的点值距离。 核心公式是 volume = VolMinimal + VolStep * (Leverage - 1),也就是说杠杆每加一层,手数按平台最小步长递增;外汇与贵金属杠杆放大后爆仓风险陡增,参数一定要用真实账户的最小交易量测过。 AdjustPrice 那行取了绝对值再做价格对齐,避免负资金或奇精度把挂单弄脏。把这段代码直接塞进你的头文件,开 MT5 用不同 Leverage 跑一遍,就能看到同一笔风控金额对应止损点数是怎么跳的。

MQL5 / C++
class="kw">return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? ToServer() : class="num">0);
};
class="kw">inline class="type">class="kw">double FinanceToPoints(class="kw">const class="type">class="kw">double Finance, class="kw">const class="type">uint Leverage)
  {
    class="type">class="kw">double volume = m_Infos.VolMinimal + (m_Infos.VolStep * (Leverage - class="num">1));
    
    class="kw">return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade)));
  };

「用方向键快速验证下单通道」

这一篇的重心不是做一个人控 EA,而是用最小代价让初学者看到自动交易程序怎么把订单送出去。想照手工逻辑写 EA 的,另有专门系列讲从手动交易提炼策略,不过那套略旧,很快你会懂原因。 验证订单系统不需要画水平线那种麻烦事。只要在 MT5 图表上按方向键,EA 就按预设参数发单,省去手动填表的步骤。具体做法:先设好入场价,注意不能用当前价;看多按上箭头、看空按下箭头,然后切到工具箱的交易标签,就能看到一笔带 EA 指定条件的挂单出现。 参数面板里 user05 默认 84.00 是入场价,user02=100、user03=75 分别是以金额计的止盈止损,user01=1 是手数增量,user04=true 代表日内模式。这些数据直接决定订单填充内容,开 MT5 把数值改掉就能立刻测不同仓位。 类初始化靠构造函数 new C_Orders(),析构里 delete orders 保证内存不漏。这样在任何内部过程调用前,订单类始终处于已初始化状态,避免空指针发单失败。 别把默认价当万能模板 外汇和贵金属波动剧烈,84.00 只是演示占位,实盘套用可能直接滑点或拒单。先开模拟账户用方向键跑通通道,再谈参数优化。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class="macro">#class="kw">property description "This one is an automatic Expert Advisor"
class="macro">#class="kw">property description "for demonstration. To understand how to"
class="macro">#class="kw">property description "develop yours in order to use a particular"
class="macro">#class="kw">property description "operational, see the articles where there"
class="macro">#class="kw">property description "is an explanation of how to proceed."
class="macro">#class="kw">property version   "class="num">1.03"
class="macro">#class="kw">property link      "[MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Generic Auto Trader\C_Orders.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
C_Orders *orders;
class="type">class="kw">ulong m_ticket;
class=class="str">"cmt">//+------------------------------------------------------------------+
input class="type">int      user01   = class="num">1;          class=class="str">"cmt">//Lot increase
input class="type">int      user02   = class="num">100;        class=class="str">"cmt">//Take Profit(financial)
input class="type">int      user03   = class="num">75;         class=class="str">"cmt">//Stop Loss(financial)
input class="type">bool     user04   = true;       class=class="str">"cmt">//Day Trade ?
input class="type">class="kw">double   user05   = class="num">84.00;      class=class="str">"cmt">//Entry price...
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
{
    orders = new C_Orders();
    
    class="kw">return INIT_SUCCEEDED;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
{
    class="kw">delete orders;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
{
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnChartEvent(class="kw">const class="type">int id, class="kw">const class="type">long &lparam, class="kw">const class="type">class="kw">double &dparam, class="kw">const class="type">class="kw">string &sparam)
{
class="macro">#define KEY_UP          class="num">38
class="macro">#define KEY_DOWN        class="num">40
    class="kw">switch (id)
    {
        case CHARTEVENT_KEYDOWN:
            class="kw">switch ((class="type">int)lparam)

键盘方向键触发下单的分支写法

在 EA 的按键事件处理里,用 switch 接 KEY_UP 与 KEY_DOWN 是最直接的手动下单入口。上键发买、下键发卖,订单参数全部走外部变量,不写死在分支里。 下面这段是事件回调末尾的典型结构,先按键值分流,再统一 break 退出 switch,最后用 #undef 清掉宏定义避免污染其他模块。

MQL5 / C++
         {
            case KEY_UP:
               m_ticket = orders.CreateOrder(ORDER_TYPE_BUY, user05, user03, user02, user01, user04);
               class="kw">break;
            case KEY_DOWN:
               m_ticket = orders.CreateOrder(ORDER_TYPE_SELL, user05, user03, user02, user01, user04);
               class="kw">break;
         }
         class="kw">break;
   }
class="macro">#undef KEY_DOWN
class="macro">#undef KEY_UP
}
class=class="str">"cmt">//+------------------------------------------------------------------+
逐行拆一下:case KEY_UP 捕获上箭头事件;CreateOrder 第一个参量 ORDER_TYPE_BUY 决定市价买,后面 user05~user04 依次对应手数、挂单价偏移、止损、止盈、Magic 之类外部输入(具体顺序以你自己的 orders 类声明为准)。 KEY_DOWN 对称处理成 ORDER_TYPE_SELL。末尾两个 #undef 把按键宏撤掉,防止在别的按键逻辑里误触发。外汇与贵金属杠杆高,手动键触发下单可能瞬间成交在滑点扩大的价位,实盘前务必在 MT5 策略测试器用历史 tick 跑一遍键事件模拟。

MQL5 / C++
         {
            case KEY_UP:
               m_ticket = orders.CreateOrder(ORDER_TYPE_BUY, user05, user03, user02, user01, user04);
               class="kw">break;
            case KEY_DOWN:
               m_ticket = orders.CreateOrder(ORDER_TYPE_SELL, user05, user03, user02, user01, user04);
               class="kw">break;
         }
         class="kw">break;
   }
class="macro">#undef KEY_DOWN
class="macro">#undef KEY_UP
}
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 把这条线请下神坛

这个自动下单的雏形 EA 虽然逻辑直白,但已经能让你在 MT5 里实测订单系统的发单链路,附件里的完整工程(EA_Automatico_-_02.zip,3.12 KB)直接拖进终端就能跑。 重点别踩坑:验证时不要用当前市价去比对,否则你根本看不出系统是不是在预设价位、预设手数下进去的——错单也会被误判成正常。 到这一步其实才摸到订单簿操作的大门,后面几篇才会把触发和风控铺开。外汇和贵金属杠杆高、滑点狠,跑任何自动单前先用策略测试器过一遍历史 tick。

常见问题

用账户净值除以合约每点价值再乘可接受点差点数,得出最大可扛点差的手数,写进仓位计算函数即可。
可在代码里绑定键盘方向键,按上/下键直接触发买/卖分支,省去手动点界面验证。
小布可以读取你的策略逻辑并模拟键击路径,标出未覆盖的买卖分支和通道阻塞点。
加一个按键状态锁或下单冷却时间变量,触发后短期内忽略重复按键信号即可。
它只是风控辅助,不代表胜率;贵金属波动大须严控杠杆,把它当参考而非必然依据。