//+------------------------------------------------------------------+
//| GoldenCrossEA.mq4 |
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- 输入参数
input bool EnableGoldenCross = true; // 启用金叉死叉模式
input int MagicNumber = 12345; // 金叉模式魔术码
input int TradeDirection = 0; // 金叉交易方向: 0-多空都做, 1-只做多, 2-只做空
input double FirstLotSize = 0.01; // 金叉模式首单手数
//--- 移动平均线参数
input int FastMAPeriod = 5; // 金叉快线周期
input int SlowMAPeriod = 20; // 金叉慢线周期
//--- 时间控制参数
input bool EnableTimeControl = false; // 启用时间控制
input string StartTime = "00:00"; // 开始时间 (HH:MM)
input string EndTime = "23:59"; // 结束时间 (HH:MM)
//--- 加仓参数
input bool EnablePyramid = true; // 启用加仓
input int PyramidMode = 0; // 加仓模式: 0-顺势加仓, 1-逆势补仓
input double PyramidMultiplier = 1.5; // 加仓倍数
input int PyramidInterval = 50; // 加仓间隔(点数)
input int MaxPyramidLevel = 5; // 最大加仓层数
//--- 平仓参数
input bool EnableFirstOrderProfit = true; // 启用首单盈利平仓
input int FirstOrderProfitPoints = 100; // 首单盈利点数
input bool EnableUnidirectionalProfit = true; // 启用单边整体盈利平仓
input int UnidirectionalProfitPoints = 200; // 单边整体盈利点数
input bool EnableTotalProfitClose = true; // 启用整体盈利平仓
input int TotalProfitPoints = 500; // 整体盈利点数(按总持仓手数计算)
//--- 全局变量
double fastMA0, fastMA1, slowMA0, slowMA1;
datetime lastTradeTime = 0;
bool timeControlActive = false;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
Print("Golden Cross EA with Time Control initialized successfully");
Print("Pyramid Mode: ", PyramidMode == 0 ? "顺势加仓" : "逆势补仓");
Print("Time Control: ", EnableTimeControl ? "Enabled" : "Disabled");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EA deinitialized with reason: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(!EnableGoldenCross) return;
// 更新指标数据
if(!UpdateMAData()) return;
// 检查时间控制状态
CheckTimeControl();
// 检查平仓条件(始终执行,不受时间控制影响)
CheckCloseConditions();
// 只有在允许交易的时间内才执行开仓和加仓
if(IsTradingAllowed())
{
CheckOpenConditions();
CheckPyramidConditions();
}
}
//+------------------------------------------------------------------+
//| 检查时间控制状态 |
//+------------------------------------------------------------------+
void CheckTimeControl()
{
if(!EnableTimeControl)
{
timeControlActive = false;
return;
}
int totalOrders = CountOrders();
// 如果有订单,时间控制不生效
if(totalOrders > 0)
{
timeControlActive = false;
return;
}
// 没有订单时,检查是否在限定时间内
timeControlActive = IsTradeTime();
}
//+------------------------------------------------------------------+
//| 检查是否允许交易(重命名避免冲突) |
//+------------------------------------------------------------------+
bool IsTradingAllowed()
{
// 如果没有启用时间控制,始终允许交易
if(!EnableTimeControl) return true;
// 如果有订单,始终允许交易(等订单结束后再受时间限制)
if(CountOrders() > 0) return true;
// 没有订单时,只在交易时间内允许交易
return timeControlActive;
}
//+------------------------------------------------------------------+
//| 检查交易时间 |
//+------------------------------------------------------------------+
bool IsTradeTime()
{
datetime current = TimeCurrent();
int currentHour = TimeHour(current);
int currentMinute = TimeMinute(current);
string startHours = StringSubstr(StartTime, 0, 2);
string startMinutes = StringSubstr(StartTime, 3, 2);
string endHours = StringSubstr(EndTime, 0, 2);
string endMinutes = StringSubstr(EndTime, 3, 2);
int startHour = StrToInteger(startHours);
int startMinute = StrToInteger(startMinutes);
int endHour = StrToInteger(endHours);
int endMinute = StrToInteger(endMinutes);
int currentTime = currentHour * 100 + currentMinute;
int startTime = startHour * 100 + startMinute;
int endTime = endHour * 100 + endMinute;
if(startTime <= endTime)
return (currentTime >= startTime && currentTime <= endTime);
else
return (currentTime >= startTime || currentTime <= endTime);
}
//+------------------------------------------------------------------+
//| 检查平仓条件 |
//+------------------------------------------------------------------+
void CheckCloseConditions()
{
// 首单盈利平仓
if(EnableFirstOrderProfit && CheckFirstOrderProfit())
{
CloseAllOrders();
Print("首单盈利平仓: 达到目标盈利点数 ", FirstOrderProfitPoints, "点");
return;
}
// 单边整体盈利平仓
if(EnableUnidirectionalProfit)
{
if(CheckUnidirectionalProfit(OP_BUY))
{
CloseAllOrdersByType(OP_BUY);
Print("多单整体盈利平仓: 达到目标盈利点数 ", UnidirectionalProfitPoints, "点");
}
else if(CheckUnidirectionalProfit(OP_SELL))
{
CloseAllOrdersByType(OP_SELL);
Print("空单整体盈利平仓: 达到目标盈利点数 ", UnidirectionalProfitPoints, "点");
}
}
// 整体盈利平仓(按总持仓手数计算)
if(EnableTotalProfitClose && CheckTotalProfitPoints())
{
CloseAllOrders();
Print("整体盈利平仓: 达到目标盈利点数 ", TotalProfitPoints, "点");
return;
}
}
//+------------------------------------------------------------------+
//| 检查首单盈利条件 |
//+------------------------------------------------------------------+
bool CheckFirstOrderProfit()
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
// 检查是否是首单(手数等于首单手数)
if(OrderLots() == FirstLotSize)
{
double profitPoints = 0;
if(OrderType() == OP_BUY)
profitPoints = (Bid - OrderOpenPrice()) / Point;
else if(OrderType() == OP_SELL)
profitPoints = (OrderOpenPrice() - Ask) / Point;
if(profitPoints >= FirstOrderProfitPoints)
{
Print("首单盈利达标: 订单=", OrderTicket(),
" 手数=", OrderLots(),
" 盈利点数=", profitPoints);
return true;
}
}
}
}
}
return false;
}
//+------------------------------------------------------------------+
//| 检查单边整体盈利条件 |
//+------------------------------------------------------------------+
bool CheckUnidirectionalProfit(int orderType)
{
double totalProfit = 0;
double totalLots = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == orderType)
{
totalProfit += OrderProfit() + OrderSwap() + OrderCommission();
totalLots += OrderLots();
}
}
}
if(totalLots <= 0) return false;
// 计算每手平均盈利点数
double profitPerLotPoints = CalculateProfitPerLotPoints(totalProfit, totalLots);
if(TimeCurrent() % 60 == 0) // 每分钟打印一次
{
string typeStr = (orderType == OP_BUY) ? "多单" : "空单";
Print(typeStr, "盈利统计: 总盈利=", totalProfit,
" 总手数=", totalLots,
" 每手盈利点数=", profitPerLotPoints);
}
return (profitPerLotPoints >= UnidirectionalProfitPoints);
}
//+------------------------------------------------------------------+
//| 检查整体盈利点数(按总持仓手数计算) |
//+------------------------------------------------------------------+
bool CheckTotalProfitPoints()
{
double totalProfit = 0;
double totalLots = 0;
double totalBuyLots = 0;
double totalSellLots = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
totalProfit += OrderProfit() + OrderSwap() + OrderCommission();
totalLots += OrderLots();
if(OrderType() == OP_BUY)
totalBuyLots += OrderLots();
else if(OrderType() == OP_SELL)
totalSellLots += OrderLots();
}
}
}
if(totalLots <= 0) return false;
// 计算每手平均盈利点数
double profitPerLotPoints = CalculateProfitPerLotPoints(totalProfit, totalLots);
// 打印详细盈利信息
if(TimeCurrent() % 60 == 0) // 每分钟打印一次
{
Print("整体盈利统计: 总盈利=", totalProfit,
" 总手数=", totalLots,
" 多单手数=", totalBuyLots,
" 空单手数=", totalSellLots,
" 每手盈利点数=", profitPerLotPoints);
}
return (profitPerLotPoints >= TotalProfitPoints);
}
//+------------------------------------------------------------------+
//| 计算每手平均盈利点数 |
//+------------------------------------------------------------------+
double CalculateProfitPerLotPoints(double totalProfit, double totalLots)
{
if(totalLots <= 0) return 0;
// 方法1:使用tickvalue计算(更精确)
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
if(tickValue > 0 && tickSize > 0)
{
// 计算每手平均盈利点数
double profitPerLot = totalProfit / totalLots;
double pointsPerLot = (profitPerLot / tickValue) * (Point / tickSize);
return pointsPerLot;
}
// 方法2:简化计算(备用)
double profitPerLotPoints = totalProfit / totalLots / 10.0; // 近似计算
return profitPerLotPoints;
}
//+------------------------------------------------------------------+
//| 检查开仓条件 |
//+------------------------------------------------------------------+
void CheckOpenConditions()
{
// 检查金叉死叉
bool goldenCross = (fastMA1 <= slowMA1 && fastMA0 > slowMA0);
bool deadCross = (fastMA1 >= slowMA1 && fastMA0 < slowMA0);
if(goldenCross && (TradeDirection == 0 || TradeDirection == 1))
{
// 检查是否已经有首单
if(CountOrders(OP_BUY) == 0)
{
OpenBuyOrder(FirstLotSize);
Print("金叉开多单,手数: ", FirstLotSize);
}
}
else if(deadCross && (TradeDirection == 0 || TradeDirection == 2))
{
// 检查是否已经有首单
if(CountOrders(OP_SELL) == 0)
{
OpenSellOrder(FirstLotSize);
Print("死叉开空单,手数: ", FirstLotSize);
}
}
}
//+------------------------------------------------------------------+
//| 检查加仓条件 |
//+------------------------------------------------------------------+
void CheckPyramidConditions()
{
if(!EnablePyramid) return;
// 多单加仓逻辑
CheckBuyPyramid();
// 空单加仓逻辑
CheckSellPyramid();
}
//+------------------------------------------------------------------+
//| 多单加仓检查 |
//+------------------------------------------------------------------+
void CheckBuyPyramid()
{
int buyCount = CountOrders(OP_BUY);
if(buyCount == 0 || buyCount >= MaxPyramidLevel) return;
double currentPrice = Ask;
double avgBuyPrice = GetAveragePrice(OP_BUY);
double lastBuyPrice = GetLastOrderPrice(OP_BUY);
// 计算价格差异(点数)
double priceDiffFromLast = (lastBuyPrice - currentPrice) / Point;
double priceDiffFromAvg = (avgBuyPrice - currentPrice) / Point;
bool shouldPyramid = false;
if(PyramidMode == 0) // 顺势加仓模式
{
// 顺势加仓:价格继续下跌时加仓(多单成本更低)
shouldPyramid = (priceDiffFromLast >= PyramidInterval);
}
else if(PyramidMode == 1) // 逆势补仓模式
{
// 逆势补仓:当多单亏损达到间隔时补仓
double currentProfitPoints = (currentPrice - avgBuyPrice) / Point;
shouldPyramid = (currentProfitPoints <= -PyramidInterval);
}
if(shouldPyramid)
{
double pyramidLot = CalculatePyramidLot(OP_BUY);
if(pyramidLot > 0)
{
OpenBuyOrder(pyramidLot);
string modeStr = (PyramidMode == 0) ? "顺势加仓" : "逆势补仓";
Print("多单", modeStr, " #", buyCount + 1, " 手数:", pyramidLot);
}
}
}
//+------------------------------------------------------------------+
//| 空单加仓检查 |
//+------------------------------------------------------------------+
void CheckSellPyramid()
{
int sellCount = CountOrders(OP_SELL);
if(sellCount == 0 || sellCount >= MaxPyramidLevel) return;
double currentPrice = Bid;
double avgSellPrice = GetAveragePrice(OP_SELL);
double lastSellPrice = GetLastOrderPrice(OP_SELL);
// 计算价格差异(点数)
double priceDiffFromLast = (currentPrice - lastSellPrice) / Point;
double priceDiffFromAvg = (currentPrice - avgSellPrice) / Point;
bool shouldPyramid = false;
if(PyramidMode == 0) // 顺势加仓模式
{
// 顺势加仓:价格继续上涨时加仓(空单成本更高)
shouldPyramid = (priceDiffFromLast >= PyramidInterval);
}
else if(PyramidMode == 1) // 逆势补仓模式
{
// 逆势补仓:当空单亏损达到间隔时补仓
double currentProfitPoints = (avgSellPrice - currentPrice) / Point;
shouldPyramid = (currentProfitPoints <= -PyramidInterval);
}
if(shouldPyramid)
{
double pyramidLot = CalculatePyramidLot(OP_SELL);
if(pyramidLot > 0)
{
OpenSellOrder(pyramidLot);
string modeStr = (PyramidMode == 0) ? "顺势加仓" : "逆势补仓";
Print("空单", modeStr, " #", sellCount + 1, " 手数:", pyramidLot);
}
}
}
//+------------------------------------------------------------------+
//| 计算加仓手数 |
//+------------------------------------------------------------------+
double CalculatePyramidLot(int orderType)
{
int orderCount = CountOrders(orderType);
if(orderCount >= MaxPyramidLevel)
return 0;
double pyramidLot = FirstLotSize * MathPow(PyramidMultiplier, orderCount);
pyramidLot = NormalizeLot(pyramidLot);
return pyramidLot;
}
//+------------------------------------------------------------------+
//| 获取平均开仓价格 |
//+------------------------------------------------------------------+
double GetAveragePrice(int orderType)
{
double totalLots = 0;
double totalValue = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == orderType)
{
totalValue += OrderOpenPrice() * OrderLots();
totalLots += OrderLots();
}
}
}
return (totalLots > 0) ? totalValue / totalLots : 0;
}
//+------------------------------------------------------------------+
//| 标准化手数 |
//+------------------------------------------------------------------+
double NormalizeLot(double lot)
{
double minlot = MarketInfo(Symbol(), MODE_MINLOT);
double maxlot = MarketInfo(Symbol(), MODE_MAXLOT);
double steplot = MarketInfo(Symbol(), MODE_LOTSTEP);
if(lot < minlot) lot = minlot;
if(lot > maxlot) lot = maxlot;
lot = MathRound(lot / steplot) * steplot;
return NormalizeDouble(lot, 2);
}
//+------------------------------------------------------------------+
//| 统计订单数量 |
//+------------------------------------------------------------------+
int CountOrders(int orderType = -1)
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(orderType == -1)
count++;
else if(OrderType() == orderType)
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| 获取最后订单价格 |
//+------------------------------------------------------------------+
double GetLastOrderPrice(int orderType)
{
datetime lastTime = 0;
double lastPrice = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == orderType)
{
if(OrderOpenTime() > lastTime)
{
lastTime = OrderOpenTime();
lastPrice = OrderOpenPrice();
}
}
}
}
return lastPrice;
}
//+------------------------------------------------------------------+
//| 开多单 |
//+------------------------------------------------------------------+
void OpenBuyOrder(double lotSize)
{
if(lotSize <= 0) return;
double price = Ask;
int ticket = OrderSend(Symbol(), OP_BUY, lotSize, price, 3, 0, 0,
"GoldenCross Buy", MagicNumber, 0, Green);
if(ticket > 0)
{
Print("多单开仓成功: 订单=", ticket, " 手数=", lotSize, " 价格=", price);
lastTradeTime = TimeCurrent();
}
else
{
Print("多单开仓失败: 错误=", GetLastError());
}
}
//+------------------------------------------------------------------+
//| 开空单 |
//+------------------------------------------------------------------+
void OpenSellOrder(double lotSize)
{
if(lotSize <= 0) return;
double price = Bid;
int ticket = OrderSend(Symbol(), OP_SELL, lotSize, price, 3, 0, 0,
"GoldenCross Sell", MagicNumber, 0, Red);
if(ticket > 0)
{
Print("空单开仓成功: 订单=", ticket, " 手数=", lotSize, " 价格=", price);
lastTradeTime = TimeCurrent();
}
else
{
Print("空单开仓失败: 错误=", GetLastError());
}
}
//+------------------------------------------------------------------+
//| 平仓所有订单 |
//+------------------------------------------------------------------+
void CloseAllOrders()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
bool result = OrderClose(OrderTicket(), OrderLots(), closePrice, 3, Yellow);
if(result)
Print("订单平仓成功: ", OrderTicket());
else
Print("订单平仓失败: ", GetLastError());
}
}
}
}
//+------------------------------------------------------------------+
//| 平仓指定类型的所有订单 |
//+------------------------------------------------------------------+
void CloseAllOrdersByType(int orderType)
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == orderType)
{
double closePrice = (orderType == OP_BUY) ? Bid : Ask;
bool result = OrderClose(OrderTicket(), OrderLots(), closePrice, 3, Yellow);
if(result)
Print("订单平仓成功: ", OrderTicket());
else
Print("订单平仓失败: ", GetLastError());
}
}
}
}
//+------------------------------------------------------------------+
//| 更新移动平均线数据 |
//+------------------------------------------------------------------+
bool UpdateMAData()
{
fastMA0 = iMA(NULL, 0, FastMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 0);
fastMA1 = iMA(NULL, 0, FastMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
slowMA0 = iMA(NULL, 0, SlowMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 0);
slowMA1 = iMA(NULL, 0, SlowMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
return (fastMA0 != 0 && slowMA0 != 0);
}
//+------------------------------------------------------------------+
logo
|