385 lines
16 KiB
MQL5
385 lines
16 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| EMA_Trend_Strategy.mq5 |
|
|
//| Copyright 2025, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.00"
|
|
|
|
#include <Trade\Trade.mqh>
|
|
CTrade trade;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 输入参数 |
|
|
//+------------------------------------------------------------------+
|
|
input group "=====基本参数======"
|
|
input int MagicNumber = 123459;
|
|
input string OrderComment = "EMA_Trend_Strategy";
|
|
|
|
input group "=====交易参数====="
|
|
input double StartLots = 0.1; // 开仓手数
|
|
input bool EnableDynamicLots = true; // 是否启用动态手数(按余额比例)
|
|
input double BaseBalance = 10000.0; // 基准余额(用于动态手数计算)
|
|
|
|
input group "=====EMA参数====="
|
|
input int FastEMA = 12; // 快线EMA周期
|
|
input int SlowEMA = 144; // 慢线EMA周期
|
|
|
|
input group "=====交易控制====="
|
|
input bool EnableBuy = true; // 是否允许做多
|
|
input bool EnableSell = true; // 是否允许做空
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 全局变量 |
|
|
//+------------------------------------------------------------------+
|
|
// EMA指标句柄
|
|
int fastEMAHandle = INVALID_HANDLE;
|
|
int slowEMAHandle = INVALID_HANDLE;
|
|
|
|
// 当前持仓信息
|
|
ulong currentBuyTicket = 0;
|
|
ulong currentSellTicket = 0;
|
|
double currentLots = 0.0;
|
|
|
|
// 状态控制
|
|
bool isInitialized = false;
|
|
datetime lastCrossTime = 0;
|
|
int lastCrossDirection = 0; // 0=无交叉, 1=金叉(快线上穿慢线), -1=死叉(快线下穿慢线)
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 计算开仓手数:可选动态手数(按余额比例)或固定手数 |
|
|
//+------------------------------------------------------------------+
|
|
double GetDynamicLots() {
|
|
double minLots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
|
double maxLots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
|
|
double stepLots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
|
|
|
if(!EnableDynamicLots) {
|
|
double fixedLots = StartLots;
|
|
fixedLots = MathMax(minLots, MathMin(maxLots, fixedLots));
|
|
fixedLots = NormalizeDouble(fixedLots / stepLots, 0) * stepLots;
|
|
return fixedLots;
|
|
}
|
|
|
|
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
double baseLots = StartLots;
|
|
double dynamicLots = (accountBalance / BaseBalance) * baseLots;
|
|
|
|
dynamicLots = MathMax(minLots, MathMin(maxLots, dynamicLots));
|
|
dynamicLots = NormalizeDouble(dynamicLots / stepLots, 0) * stepLots;
|
|
return dynamicLots;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 获取EMA值 |
|
|
//+------------------------------------------------------------------+
|
|
double GetEMAValue(int handle, int shift) {
|
|
if(handle == INVALID_HANDLE) return 0.0;
|
|
|
|
double emaArray[1];
|
|
if(CopyBuffer(handle, 0, shift, 1, emaArray) <= 0) return 0.0;
|
|
return emaArray[0];
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 检测EMA交叉 |
|
|
//+------------------------------------------------------------------+
|
|
int DetectEMACross() {
|
|
// 获取当前K线和上一根K线的EMA值
|
|
double fastEMA_Current = GetEMAValue(fastEMAHandle, 0);
|
|
double slowEMA_Current = GetEMAValue(slowEMAHandle, 0);
|
|
double fastEMA_Previous = GetEMAValue(fastEMAHandle, 1);
|
|
double slowEMA_Previous = GetEMAValue(slowEMAHandle, 1);
|
|
|
|
// 添加调试信息
|
|
static datetime lastEMADebugTime = 0;
|
|
if(TimeCurrent() - lastEMADebugTime >= 10) { // 每10秒打印一次EMA值
|
|
Print("=== EMA交叉检测调试 ===");
|
|
Print("当前K线 - EMA", FastEMA, ":", fastEMA_Current, " EMA", SlowEMA, ":", slowEMA_Current);
|
|
Print("上一K线 - EMA", FastEMA, ":", fastEMA_Previous, " EMA", SlowEMA, ":", slowEMA_Previous);
|
|
Print("快线变化:", fastEMA_Current - fastEMA_Previous);
|
|
Print("慢线变化:", slowEMA_Current - slowEMA_Previous);
|
|
lastEMADebugTime = TimeCurrent();
|
|
}
|
|
|
|
if(fastEMA_Current == 0.0 || slowEMA_Current == 0.0 ||
|
|
fastEMA_Previous == 0.0 || slowEMA_Previous == 0.0) {
|
|
Print("EMA数据不足,无法检测交叉");
|
|
return 0; // 数据不足
|
|
}
|
|
|
|
// 检测金叉:快线上穿慢线
|
|
if(fastEMA_Previous <= slowEMA_Previous && fastEMA_Current > slowEMA_Current) {
|
|
Print("检测到金叉信号!快线上穿慢线");
|
|
return 1; // 金叉
|
|
}
|
|
|
|
// 检测死叉:快线下穿慢线
|
|
if(fastEMA_Previous >= slowEMA_Previous && fastEMA_Current < slowEMA_Current) {
|
|
Print("检测到死叉信号!快线下穿慢线");
|
|
return -1; // 死叉
|
|
}
|
|
|
|
return 0; // 无交叉
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 开仓函数 |
|
|
//+------------------------------------------------------------------+
|
|
bool OpenPosition(int posType, double lots, double &price, double sl, double tp, string comment) {
|
|
trade.SetExpertMagicNumber(MagicNumber);
|
|
bool result = false;
|
|
|
|
if(posType == POSITION_TYPE_BUY) {
|
|
price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
result = trade.Buy(lots, _Symbol, price, sl, tp, comment);
|
|
} else if(posType == POSITION_TYPE_SELL) {
|
|
price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
result = trade.Sell(lots, _Symbol, price, sl, tp, comment);
|
|
}
|
|
|
|
if(result) {
|
|
string direction = (posType == POSITION_TYPE_BUY) ? "多单" : "空单";
|
|
Print("开仓成功:", direction, " 价格:", price, " 手数:", lots, " 订单号:", trade.ResultOrder());
|
|
return true;
|
|
} else {
|
|
Print("开仓失败: 错误代码:", trade.ResultRetcode(), " 错误描述:", trade.ResultRetcodeDescription());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 平仓函数 |
|
|
//+------------------------------------------------------------------+
|
|
bool ClosePosition(ulong ticket, string reason) {
|
|
if(!PositionSelectByTicket(ticket)) return false;
|
|
|
|
if(trade.PositionClose(ticket)) {
|
|
Print("平仓成功:订单号:", ticket, " 原因:", reason);
|
|
return true;
|
|
} else {
|
|
Print("平仓失败:订单号:", ticket, " 错误:", trade.ResultRetcode());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 计算止损止盈价格 |
|
|
//+------------------------------------------------------------------+
|
|
// 移除止损计算函数,改为通过信号止损
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 处理EMA交叉信号 |
|
|
//+------------------------------------------------------------------+
|
|
void ProcessEMACross(int crossDirection) {
|
|
if(crossDirection == 0) return;
|
|
|
|
// 避免重复处理同一交叉信号
|
|
if(crossDirection == lastCrossDirection) return;
|
|
|
|
lastCrossDirection = crossDirection;
|
|
lastCrossTime = TimeCurrent();
|
|
|
|
if(crossDirection == 1) {
|
|
// 金叉:快线上穿慢线,开多平空
|
|
Print("=== EMA金叉信号 ===");
|
|
|
|
// 平空单
|
|
if(currentSellTicket > 0) {
|
|
if(ClosePosition(currentSellTicket, "EMA金叉平空")) {
|
|
currentSellTicket = 0;
|
|
}
|
|
}
|
|
|
|
// 开多单
|
|
if(EnableBuy && currentBuyTicket == 0) {
|
|
double entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
|
|
Print("尝试开多单 - 手数:", currentLots, " 开仓价:", entryPrice, " 止损方式:信号止损");
|
|
|
|
string comment = OrderComment + "_EMA_GoldenCross";
|
|
if(OpenPosition(POSITION_TYPE_BUY, currentLots, entryPrice, 0, 0, comment)) {
|
|
currentBuyTicket = trade.ResultOrder();
|
|
Print("EMA金叉开多成功,订单号:", currentBuyTicket);
|
|
} else {
|
|
Print("EMA金叉开多失败,请检查交易条件");
|
|
}
|
|
} else {
|
|
if(!EnableBuy) {
|
|
Print("做多功能已禁用");
|
|
} else if(currentBuyTicket > 0) {
|
|
Print("已有多单,订单号:", currentBuyTicket);
|
|
}
|
|
}
|
|
|
|
} else if(crossDirection == -1) {
|
|
// 死叉:快线下穿慢线,开空平多
|
|
Print("=== EMA死叉信号 ===");
|
|
|
|
// 平多单
|
|
if(currentBuyTicket > 0) {
|
|
if(ClosePosition(currentBuyTicket, "EMA死叉平多")) {
|
|
currentBuyTicket = 0;
|
|
}
|
|
}
|
|
|
|
// 开空单
|
|
if(EnableSell && currentSellTicket == 0) {
|
|
double entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
Print("尝试开空单 - 手数:", currentLots, " 开仓价:", entryPrice, " 止损方式:信号止损");
|
|
|
|
string comment = OrderComment + "_EMA_DeathCross";
|
|
if(OpenPosition(POSITION_TYPE_SELL, currentLots, entryPrice, 0, 0, comment)) {
|
|
currentSellTicket = trade.ResultOrder();
|
|
Print("EMA死叉开空成功,订单号:", currentSellTicket);
|
|
} else {
|
|
Print("EMA死叉开空失败,请检查交易条件");
|
|
}
|
|
} else {
|
|
if(!EnableSell) {
|
|
Print("做空功能已禁用");
|
|
} else if(currentSellTicket > 0) {
|
|
Print("已有空单,订单号:", currentSellTicket);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 检查持仓状态 |
|
|
//+------------------------------------------------------------------+
|
|
void CheckPositions() {
|
|
// 检查多单是否还存在
|
|
if(currentBuyTicket > 0 && !PositionSelectByTicket(currentBuyTicket)) {
|
|
Print("多单已不存在,重置状态");
|
|
currentBuyTicket = 0;
|
|
}
|
|
|
|
// 检查空单是否还存在
|
|
if(currentSellTicket > 0 && !PositionSelectByTicket(currentSellTicket)) {
|
|
Print("空单已不存在,重置状态");
|
|
currentSellTicket = 0;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 打印状态信息 |
|
|
//+------------------------------------------------------------------+
|
|
void PrintStatus() {
|
|
Print("=== EMA趋势策略状态 ===");
|
|
Print("当前多单:", currentBuyTicket, " 空单:", currentSellTicket, " 手数:", currentLots);
|
|
|
|
// 获取当前EMA值
|
|
double fastEMA = GetEMAValue(fastEMAHandle, 0);
|
|
double slowEMA = GetEMAValue(slowEMAHandle, 0);
|
|
Print("EMA", FastEMA, ":", fastEMA, " EMA", SlowEMA, ":", slowEMA);
|
|
|
|
if(fastEMA > slowEMA) {
|
|
Print("当前趋势:多头趋势(快线在慢线上方)");
|
|
} else if(fastEMA < slowEMA) {
|
|
Print("当前趋势:空头趋势(快线在慢线下方)");
|
|
} else {
|
|
Print("当前趋势:无明确趋势(快线等于慢线)");
|
|
}
|
|
|
|
if(lastCrossDirection == 1) {
|
|
Print("最后信号:金叉(快线上穿慢线)");
|
|
} else if(lastCrossDirection == -1) {
|
|
Print("最后信号:死叉(快线下穿慢线)");
|
|
} else {
|
|
Print("最后信号:无交叉");
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick() {
|
|
// 检查持仓状态
|
|
CheckPositions();
|
|
|
|
// 检测EMA交叉
|
|
int crossDirection = DetectEMACross();
|
|
|
|
// 添加调试信息
|
|
static datetime lastDebugTime = 0;
|
|
if(TimeCurrent() - lastDebugTime >= 5) { // 每5秒打印一次调试信息
|
|
Print("=== 调试信息 ===");
|
|
Print("交叉检测结果:", crossDirection, " (0=无交叉, 1=金叉, -1=死叉)");
|
|
Print("最后交叉方向:", lastCrossDirection);
|
|
Print("当前多单:", currentBuyTicket, " 空单:", currentSellTicket);
|
|
Print("做多启用:", EnableBuy, " 做空启用:", EnableSell);
|
|
Print("当前手数:", currentLots);
|
|
|
|
// 获取当前EMA值
|
|
double fastEMA = GetEMAValue(fastEMAHandle, 0);
|
|
double slowEMA = GetEMAValue(slowEMAHandle, 0);
|
|
Print("EMA", FastEMA, ":", fastEMA, " EMA", SlowEMA, ":", slowEMA);
|
|
|
|
lastDebugTime = TimeCurrent();
|
|
}
|
|
|
|
// 处理交叉信号
|
|
if(crossDirection != 0) {
|
|
Print("检测到交叉信号:", crossDirection, " 开始处理...");
|
|
ProcessEMACross(crossDirection);
|
|
}
|
|
|
|
// 每10秒打印一次状态
|
|
static datetime lastPrintTime = 0;
|
|
if(TimeCurrent() - lastPrintTime >= 10) {
|
|
PrintStatus();
|
|
lastPrintTime = TimeCurrent();
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit() {
|
|
Print("=== EMA趋势策略EA初始化 ===");
|
|
Print("EMA参数 - 快线:", FastEMA, " 慢线:", SlowEMA);
|
|
Print("交易方向 - 多单:", (EnableBuy ? "启用" : "禁用"), " 空单:", (EnableSell ? "启用" : "禁用"));
|
|
Print("止损方式:通过EMA交叉信号止损(金叉止损空单,死叉止损多单)");
|
|
Print("止盈方式:通过反向信号止盈(金叉平空,死叉平多)");
|
|
Print("手数模式:", (EnableDynamicLots ? "动态手数" : "固定手数"));
|
|
if(EnableDynamicLots) {
|
|
Print("基准余额:", BaseBalance, " 基准手数:", StartLots);
|
|
} else {
|
|
Print("固定手数:", StartLots);
|
|
}
|
|
Print("当前服务器时间: ", TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES));
|
|
|
|
// 初始化EMA指标句柄
|
|
fastEMAHandle = iMA(_Symbol, _Period, FastEMA, 0, MODE_EMA, PRICE_CLOSE);
|
|
slowEMAHandle = iMA(_Symbol, _Period, SlowEMA, 0, MODE_EMA, PRICE_CLOSE);
|
|
|
|
if(fastEMAHandle == INVALID_HANDLE || slowEMAHandle == INVALID_HANDLE) {
|
|
Print("EMA指标初始化失败");
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
// 计算初始手数
|
|
currentLots = GetDynamicLots();
|
|
|
|
isInitialized = true;
|
|
Print("EMA趋势策略EA初始化完成");
|
|
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason) {
|
|
// 释放指标句柄
|
|
if(fastEMAHandle != INVALID_HANDLE)
|
|
IndicatorRelease(fastEMAHandle);
|
|
if(slowEMAHandle != INVALID_HANDLE)
|
|
IndicatorRelease(slowEMAHandle);
|
|
|
|
Print("EMA趋势策略EA已停止,原因代码:", reason);
|
|
Print("当前多单:", currentBuyTicket, " 空单:", currentSellTicket);
|
|
}
|