MQL4系列教学结束了。
从这篇文章开始,我们温习下整个系列教学里EA开发的过程。从最简单的基础版EA源码到进阶版,来感受一下我是如何给一个EA添砖加瓦、完善其功能的。这款EA是否盈利不重要,它的作用是来给大家演示一个EA的成型过程。
#property copyright "tudaojiaoyi" // 版权声明
// ===== 外部输入参数 =====
extern double LotSize = 0.1; // 交易手数
extern double StopLoss = 50; // 止损点数 (50点)
extern double TakeProfit = 100; // 止盈点数 (100点)
extern int Slippage = 5; // 允许滑点 (5个常规点)
extern int MagicNumber = 123; // EA魔术数字 (订单标识)
extern int FastMAPeriod = 10; // 快速移动平均线周期
extern int SlowMAPeriod = 20; // 慢速移动平均线周期
// ===== 全局变量 =====
int BuyTicket; // 保存买单订单号
int SellTicket; // 保存卖单订单号
double UsePoint; // 当前品种1个标准点的价格小数 (例如 0.0001)
int UseSlippage; // 根据当前品种调整后的滑点值
// ===== 初始化函数 (EA加载时执行一次) =====
int OnInit()
{
// 获取当前交易品种的点值信息
UsePoint = PipPoint(Symbol());
// 根据当前交易品种的点位精度,调整滑点设置
UseSlippage = GetSlippage(Symbol(), Slippage);
return(INIT_SUCCEEDED); // 表示初始化成功
}
// ===== 主体逻辑函数 (每个报价tick执行一次) =====
void OnTick()
{
// --- 计算移动平均线 ---
// 计算快速MA当前值 (参数:品种,周期,MA周期,MA位移,MA方法,应用价格,柱索引)
double FastMA = iMA(NULL, 0, FastMAPeriod, 0, 0, 0, 0); // 0=SMA, 0=PRICE_CLOSE, 0=当前柱
// 计算慢速MA当前值
double SlowMA = iMA(NULL, 0, SlowMAPeriod, 0, 0, 0, 0);
// --- 买入条件判断与执行 ---
if(FastMA > SlowMA && BuyTicket == 0) // 如果快速MA上穿慢速MA,并且当前没有持有的买单
{
// 尝试选择并关闭可能存在的反向卖单
if(SellTicket > 0) // 检查是否有活动的卖单号
{
if(OrderSelect(SellTicket, SELECT_BY_TICKET)) // 选择该卖单
{
if(OrderCloseTime() == 0) // 确认订单是开仓状态 (平仓时间为0)
{
double CloseLots = OrderLots(); // 获取订单手数
double ClosePrice = Ask; // 以当前卖价平掉空单
bool Closed = OrderClose(SellTicket, CloseLots, ClosePrice, UseSlippage, Red); // 执行平仓
}
}
}
// 准备开立新买单
double OpenPrice = Ask; // 买单开仓价为当前卖方报价(Ask)
double BuyStopLoss = 0; // 初始化买单止损价
double BuyTakeProfit = 0; // 初始化买单止盈价
// 计算止损和止盈价格
if(StopLoss > 0) // 如果设置了止损点数
{
BuyStopLoss = OpenPrice - (StopLoss * UsePoint); // 买单止损价 = 开仓价 - 止损点数对应价格
}
if(TakeProfit > 0) // 如果设置了止盈点数
{
BuyTakeProfit = OpenPrice + (TakeProfit * UsePoint); // 买单止盈价 = 开仓价 + 止盈点数对应价格
}
// 发送买入市价单
BuyTicket = OrderSend(Symbol(), OP_BUY, LotSize, OpenPrice, UseSlippage,
BuyStopLoss, BuyTakeProfit, "Buy Order", MagicNumber, 0, Green);
SellTicket = 0; // 重置卖单追踪号码,因为已建立买单或尝试建立买单
}
// --- 卖出条件判断与执行 ---
if(FastMA < SlowMA && SellTicket == 0) // 如果快速MA下穿慢速MA,并且当前没有持有的卖单
{
// 尝试选择并关闭可能存在的反向买单
if(BuyTicket > 0) // 检查是否有活动的买单号
{
if(OrderSelect(BuyTicket, SELECT_BY_TICKET)) // 选择该买单
{
if(OrderCloseTime() == 0) // 确认订单是开仓状态
{
double SellCloseLots = OrderLots();
double SellClosePrice = Bid; // 以当前买价平掉多单
bool SellClosed = OrderClose(BuyTicket, SellCloseLots, SellClosePrice, UseSlippage, Red);
}
}
}
// 准备开立新卖单
double SellOpenPrice = Bid; // 卖单开仓价为当前买方报价(Bid)
double SellStopLoss = 0;
double SellTakeProfit = 0;
// 计算止损和止盈价格
if(StopLoss > 0)
{
SellStopLoss = SellOpenPrice + (StopLoss * UsePoint); // 卖单止损价 = 开仓价 + 止损点数对应价格
}
if(TakeProfit > 0)
{
SellTakeProfit = SellOpenPrice - (TakeProfit * UsePoint); // 卖单止盈价 = 开仓价 - 止盈点数对应价格
}
// 发送卖出市价单
SellTicket = OrderSend(Symbol(), OP_SELL, LotSize, SellOpenPrice, UseSlippage,
SellStopLoss, SellTakeProfit, "Sell Order", MagicNumber, 0, Red);
BuyTicket = 0; // 重置买单追踪号码
}
}
// ===== 辅助函数:计算点值 =====
// 根据货币对的小数位数确定1个标准点的价格单位
double PipPoint(string Currency)
{
int CalcDigits = MarketInfo(Currency, MODE_DIGITS); // 获取小数位数
double CalcPoint = 0.0; // 初始化点值
if(CalcDigits == 2 || CalcDigits == 3) // 例如 JPY 对 (2位小数) 或某些特殊品种 (3位小数)
{
CalcPoint = 0.01;
}
else if(CalcDigits == 4 || CalcDigits == 5) // 例如 EURUSD (4位或5位小数)
{
CalcPoint = 0.0001;
}
return(CalcPoint);
}
// ===== 辅助函数:获取滑点 =====
// 根据货币对小数位数和输入的常规点数,计算实际传递给服务器的滑点值
int GetSlippage(string Currency, int SlippagePips)
{
int CalcDigits = MarketInfo(Currency, MODE_DIGITS);
int CalcSlippage = 0; // 初始化调整后的滑点值
if(CalcDigits == 2 || CalcDigits == 4) // 2位或4位报价的品种 (例如 JPY对, EURUSD的4位报价)
{
CalcSlippage = SlippagePips; // 滑点值不变
}
else if(CalcDigits == 3 || CalcDigits == 5) // 3位或5位报价的品种 (例如 EURUSD的5位报价)
{
CalcSlippage = SlippagePips * 10; // 滑点值需要乘以10 (因为1个常规点包含10个微点)
}
return(CalcSlippage);
}