mql5/Experts/EMA_Trend_Strategy.mq5
1Morty 50a3adba89
2025-09-08 21:00:32 +08:00

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);
}