将EA从MQL4迁移到MQL5并非简单的查找替换工作。两大平台的架构存在根本性差异:MQL4采用面向过程的编程范式与简单的订单模型,而MQL5全面拥抱面向对象编程,并引入独立的订单-持仓系统及基于句柄的指标机制。
1. 入口函数映射
最基础但至关重要的修改是重命名主函数:
```cpp
// MQL4写法
init() // 返回int
deinit() // 返回void
start() // 返回void
// MQL5等效写法
int OnInit() // 必须返回INIT_SUCCEEDED或INIT_FAILED
void OnDeinit(const int reason) // 现在接收原因代码参数
void OnTick() // EA用OnTick,指标用OnCalculate
```
关键差异:`OnInit()`成功时必须显式返回`INIT_SUCCEEDED`,失败时需返回`INIT_FAILED`,否则EA将无法加载。
2. OrderSend转CTrade转换
这是架构变化最大的部分。MQL5将挂单(Pending Orders)与持仓(Open Positions)彻底分离:
```cpp
// === MQL4:简单粗暴的OrderSend ===
int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, sl, tp, "EA", magic, 0, clrNONE);
if(ticket < 0) Print("下单错误: ", GetLastError());
// === MQL5:使用CTrade类 ===
#include
#include
#include
CTrade trade;
CPositionInfo position;
CSymbolInfo symbol_info;
// 在OnInit()中初始化
trade.SetExpertMagicNumber(magic);
trade.SetDeviationInPoints(10);
symbol_info.Name(_Symbol);
// 开多单
if(!trade.Buy(0.1, _Symbol, 0, sl, tp, "EA"))
{
Print("下单错误: ", trade.ResultRetcodeDescription());
}
```
注意:将价格参数设为`0`时,`CTrade`类会自动使用当前市价。
3. 订单与持仓的分离处理
MQL4的`OrdersTotal()`同时包含挂单和持仓。MQL5将两者分开处理:
```cpp
// === MQL4:混在一起遍历 ===
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magic) {
// 处理订单/持仓
}
}
}
// === MQL5:分别遍历 ===
// 仅遍历持仓
for(int i = PositionsTotal() - 1; i >= 0; i--) {
if(position.SelectByIndex(i)) {
if(position.Symbol() == _Symbol && position.Magic() == magic) {
double profit = position.Profit();
// 处理持仓
}
}
}
// 单独遍历挂单
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
// 仅处理挂单
}
}
```
4. 指标系统:从直接调用到句柄机制
MQL4每次调用指标都会重新计算。MQL5使用句柄(Handle)系统以提升性能:
```cpp
// === MQL4:直接调用 ===
double ma = iMA(NULL, 0, 20, 0, MODE_SMA, PRICE_CLOSE, 0);
double ma_prev = iMA(NULL, 0, 20, 0, MODE_SMA, PRICE_CLOSE, 1);
// === MQL5:句柄+缓冲区 ===
// 全局变量
int ma_handle;
double ma_buffer[];
// 在OnInit()中创建句柄
ma_handle = iMA(_Symbol, _Period, 20, 0, MODE_SMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE) return(INIT_FAILED);
ArraySetAsSeries(ma_buffer, true); // 关键:翻转索引顺序
// 在OnTick()中获取数据
if(CopyBuffer(ma_handle, 0, 0, 2, ma_buffer) < 2) return;
double ma = ma_buffer[0]; // 当前K线(MQL4中为Close[0])
double ma_prev = ma_buffer[1]; // 前一根K线(MQL4中为Close[1])
// 在OnDeinit()中释放句柄
IndicatorRelease(ma_handle);
```
`ArraySetAsSeries(true)`调用至关重要——MQL5数组默认按从旧到新的顺序索引,而MQL4按从新到旧顺序索引。
5. 价格与市场数据访问
MQL5用`SymbolInfoDouble()`等函数替代了MQL4的全局变量:
```cpp
// === MQL4 ===
double bid = Bid;
double ask = Ask;
double point = Point;
int digits = Digits;
double close = Close[0];
double high = High[1];
// === MQL5 ===
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
double close = iClose(_Symbol, _Period, 0);
double high = iHigh(_Symbol, _Period, 1);
```
6. 账户信息迁移
MQL5使用统一访问函数配合枚举参数:
```cpp
// === MQL4 ===
double balance = AccountBalance();
double equity = AccountEquity();
double margin = AccountFreeMargin();
string name = AccountName();
// === MQL5 ===
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double margin = AccountInfoDouble(ACCOUNT_FREE_MARGIN);
string name = AccountInfoString(ACCOUNT_NAME);
```
7. 完整持仓平仓函数
一个生产级平仓函数,同时处理多单和空单:
```cpp
bool ClosePositionByTicket(ulong ticket, int slippage = 10) {
if(!PositionSelectByTicket(ticket)) return false;
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.position = ticket;
request.symbol = PositionGetString(POSITION_SYMBOL);
request.volume = PositionGetDouble(POSITION_VOLUME);
request.deviation = slippage;
request.type_filling = ORDER_FILLING_FOK;
long positionType = PositionGetInteger(POSITION_TYPE);
if(positionType == POSITION_TYPE_BUY) {
request.price = SymbolInfoDouble(request.symbol, SYMBOL_BID);
request.type = ORDER_TYPE_SELL;
} else {
request.price = SymbolInfoDouble(request.symbol, SYMBOL_ASK);
request.type = ORDER_TYPE_BUY;
}
return OrderSend(request, result);
}
```
参考来源:Jaume Sancho,《MQL4到MQL5迁移:实战指南》(MQL5博客,2026年2月);《MT4到MT5代码迁移全攻略》(MQL5社区文章,2025年)。