//+------------------------------------------------------------------+ //| Lock_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 CTrade trade; //+------------------------------------------------------------------+ //| 输入参数 | //+------------------------------------------------------------------+ input group "=====基本参数======" input int MagicNumber = 123458; input string OrderComment = "Lock_Strategy"; input group "=====交易参数=====" input double StartLots = 0.1; // 开仓手数 input bool EnableDynamicLots = true; // 是否启用动态手数(按余额比例) input double BaseBalance = 10000.0; // 基准余额(用于动态手数计算) input group "=====盈亏设置=====" input double ProfitTarget = 5.0; // 止盈目标(美元) input double LossThreshold = 5.0; // 亏损阈值(美元),达到此值开始锁仓 input group "=====锁仓管理=====" input bool EnableLockPosition = true; // 是否启用锁仓功能 input bool EnableBreakEvenClose = true; // 是否启用价格区间平仓 input double PriceRangeTolerance = 0.1; // 价格区间容差(0.1表示10%的价格区间) //+------------------------------------------------------------------+ //| 全局变量 | //+------------------------------------------------------------------+ // 锁仓组信息结构体 struct LockGroup { ulong buyTicket; // 多单订单号 ulong sellTicket; // 空单订单号 double buyPrice; // 多单开仓价 double sellPrice; // 空单开仓价 double lots; // 手数 datetime lockTime; // 锁仓时间 bool isActive; // 是否活跃 double totalProfit; // 锁仓组总盈亏 // 复制构造函数 LockGroup() { buyTicket = 0; sellTicket = 0; buyPrice = 0.0; sellPrice = 0.0; lots = 0.0; lockTime = 0; isActive = false; totalProfit = 0.0; } // 复制构造函数 LockGroup(const LockGroup& other) { buyTicket = other.buyTicket; sellTicket = other.sellTicket; buyPrice = other.buyPrice; sellPrice = other.sellPrice; lots = other.lots; lockTime = other.lockTime; isActive = other.isActive; totalProfit = other.totalProfit; } }; // 锁仓组数组 LockGroup lockGroups[]; int lockGroupCount = 0; // 当前活跃的多空单 ulong currentBuyTicket = 0; ulong currentSellTicket = 0; double currentLots = 0.0; // 状态控制 bool isInitialized = false; datetime lastCheckTime = 0; //+------------------------------------------------------------------+ //| 计算开仓手数:可选动态手数(按余额比例)或固定手数 | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| 开仓函数 | //+------------------------------------------------------------------+ bool OpenPosition(int posType, double lots, double &price, string comment) { trade.SetExpertMagicNumber(MagicNumber); bool result = false; if(posType == POSITION_TYPE_BUY) { price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); result = trade.Buy(lots, _Symbol, price, 0, 0, comment); } else if(posType == POSITION_TYPE_SELL) { price = SymbolInfoDouble(_Symbol, SYMBOL_BID); result = trade.Sell(lots, _Symbol, price, 0, 0, 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; } } //+------------------------------------------------------------------+ //| 计算持仓盈亏(美元) | //+------------------------------------------------------------------+ double GetPositionProfit(ulong ticket) { if(!PositionSelectByTicket(ticket)) return 0.0; double profit = PositionGetDouble(POSITION_PROFIT); double swap = PositionGetDouble(POSITION_SWAP); return profit + swap; } //+------------------------------------------------------------------+ //| 检查是否需要锁仓 | //+------------------------------------------------------------------+ void CheckLockPosition() { if(!EnableLockPosition) return; if(currentBuyTicket > 0 && PositionSelectByTicket(currentBuyTicket)) { double buyProfit = GetPositionProfit(currentBuyTicket); if(buyProfit <= -LossThreshold) { Print("多单亏损达到锁仓阈值,开始锁仓操作"); CreateLockGroup(currentBuyTicket, POSITION_TYPE_BUY); } } if(currentSellTicket > 0 && PositionSelectByTicket(currentSellTicket)) { double sellProfit = GetPositionProfit(currentSellTicket); if(sellProfit <= -LossThreshold) { Print("空单亏损达到锁仓阈值,开始锁仓操作"); CreateLockGroup(currentSellTicket, POSITION_TYPE_SELL); } } } //+------------------------------------------------------------------+ //| 创建锁仓组 | //+------------------------------------------------------------------+ void CreateLockGroup(ulong originalTicket, int originalType) { if(!PositionSelectByTicket(originalTicket)) return; double lots = PositionGetDouble(POSITION_VOLUME); double price = PositionGetDouble(POSITION_PRICE_OPEN); double lockPrice; int lockType = (originalType == POSITION_TYPE_BUY) ? POSITION_TYPE_SELL : POSITION_TYPE_BUY; string comment = OrderComment + "_Lock_" + IntegerToString(originalTicket); if(OpenPosition(lockType, lots, lockPrice, comment)) { int newIndex = ArraySize(lockGroups); ArrayResize(lockGroups, newIndex + 1); LockGroup group; if(originalType == POSITION_TYPE_BUY) { group.buyTicket = originalTicket; group.sellTicket = trade.ResultOrder(); group.buyPrice = price; group.sellPrice = lockPrice; } else { group.buyTicket = trade.ResultOrder(); group.sellTicket = originalTicket; group.buyPrice = lockPrice; group.sellPrice = price; } group.lots = lots; group.lockTime = TimeCurrent(); group.isActive = true; group.totalProfit = 0.0; // 将锁仓组信息保存到数组中 lockGroups[newIndex] = group; lockGroupCount++; Print("锁仓组创建成功 - 多单:", group.buyTicket, " 空单:", group.sellTicket, " 手数:", lots); if(originalType == POSITION_TYPE_BUY) { currentBuyTicket = 0; } else { currentSellTicket = 0; } } } //+------------------------------------------------------------------+ //| 检查锁仓组状态 | //+------------------------------------------------------------------+ void CheckLockGroups() { for(int i = 0; i < ArraySize(lockGroups); i++) { LockGroup group = lockGroups[i]; if(!group.isActive) continue; if(!PositionSelectByTicket(group.buyTicket) || !PositionSelectByTicket(group.sellTicket)) { Print("锁仓组订单丢失,标记为非活跃 - 多单:", group.buyTicket, " 空单:", group.sellTicket); group.isActive = false; lockGroups[i] = group; continue; } double buyProfit = GetPositionProfit(group.buyTicket); double sellProfit = GetPositionProfit(group.sellTicket); group.totalProfit = buyProfit + sellProfit; // 计算锁仓组的价格区间中间水平 double priceRange = MathAbs(group.buyPrice - group.sellPrice); double middlePrice = (group.buyPrice + group.sellPrice) / 2.0; // 获取当前市场价格 double currentPrice = (group.buyPrice > group.sellPrice) ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK); // 检查价格是否回到锁仓组价格区间的中间水平 if(EnableBreakEvenClose && MathAbs(currentPrice - middlePrice) <= (priceRange * PriceRangeTolerance)) { Print("锁仓组价格回到中间水平,开始平仓 - 当前价格:", currentPrice, " 中间价格:", middlePrice, " 价格区间:", priceRange); CloseLockGroup(i); } // 更新数组中的锁仓组信息 lockGroups[i] = group; } } //+------------------------------------------------------------------+ //| 平仓锁仓组 | //+------------------------------------------------------------------+ void CloseLockGroup(int index) { if(index < 0 || index >= ArraySize(lockGroups)) return; LockGroup group = lockGroups[index]; if(PositionSelectByTicket(group.buyTicket)) { if(trade.PositionClose(group.buyTicket)) { Print("锁仓组多单平仓成功:", group.buyTicket); } else { Print("锁仓组多单平仓失败:", group.buyTicket, " 错误:", trade.ResultRetcode()); } } if(PositionSelectByTicket(group.sellTicket)) { if(trade.PositionClose(group.sellTicket)) { Print("锁仓组空单平仓成功:", group.sellTicket); } else { Print("锁仓组空单平仓失败:", group.sellTicket, " 错误:", trade.ResultRetcode()); } } group.isActive = false; lockGroups[index] = group; Print("锁仓组平仓完成,总盈亏:", group.totalProfit); } //+------------------------------------------------------------------+ //| 检查止盈条件 | //+------------------------------------------------------------------+ void CheckTakeProfit() { if(currentBuyTicket > 0 && PositionSelectByTicket(currentBuyTicket)) { double profit = GetPositionProfit(currentBuyTicket); if(profit >= ProfitTarget) { Print("多单达到止盈目标,平仓 - 盈利:", profit); if(trade.PositionClose(currentBuyTicket)) { currentBuyTicket = 0; Print("多单止盈平仓成功"); } } } if(currentSellTicket > 0 && PositionSelectByTicket(currentSellTicket)) { double profit = GetPositionProfit(currentSellTicket); if(profit >= ProfitTarget) { Print("空单达到止盈目标,平仓 - 盈利:", profit); if(trade.PositionClose(currentSellTicket)) { currentSellTicket = 0; Print("空单止盈平仓成功"); } } } } //+------------------------------------------------------------------+ //| 开新仓 | //+------------------------------------------------------------------+ void OpenNewPositions() { if(currentBuyTicket == 0) { double buyPrice; string comment = OrderComment + "_Buy"; if(OpenPosition(POSITION_TYPE_BUY, currentLots, buyPrice, comment)) { currentBuyTicket = trade.ResultOrder(); Print("新开多单成功,订单号:", currentBuyTicket); } } if(currentSellTicket == 0) { double sellPrice; string comment = OrderComment + "_Sell"; if(OpenPosition(POSITION_TYPE_SELL, currentLots, sellPrice, comment)) { currentSellTicket = trade.ResultOrder(); Print("新开空单成功,订单号:", currentSellTicket); } } } //+------------------------------------------------------------------+ //| 初始化持仓 | //+------------------------------------------------------------------+ void InitializePositions() { if(isInitialized) return; currentLots = GetDynamicLots(); OpenNewPositions(); isInitialized = true; Print("策略初始化完成,多单:", currentBuyTicket, " 空单:", currentSellTicket, " 手数:", currentLots); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(TimeCurrent() - lastCheckTime < 1) return; lastCheckTime = TimeCurrent(); InitializePositions(); CheckTakeProfit(); CheckLockPosition(); CheckLockGroups(); OpenNewPositions(); static datetime lastPrintTime = 0; if(TimeCurrent() - lastPrintTime >= 10) { PrintStatus(); lastPrintTime = TimeCurrent(); } } //+------------------------------------------------------------------+ //| 打印状态信息 | //+------------------------------------------------------------------+ void PrintStatus() { Print("=== 锁仓策略状态 ==="); Print("当前多单:", currentBuyTicket, " 空单:", currentSellTicket, " 手数:", currentLots); if(currentBuyTicket > 0 && PositionSelectByTicket(currentBuyTicket)) { double buyProfit = GetPositionProfit(currentBuyTicket); Print("多单盈亏:", buyProfit, " 美元"); } if(currentSellTicket > 0 && PositionSelectByTicket(currentSellTicket)) { double sellProfit = GetPositionProfit(currentSellTicket); Print("空单盈亏:", sellProfit, " 美元"); } Print("活跃锁仓组数量:", GetActiveLockGroupCount()); Print("总锁仓组数量:", lockGroupCount); } //+------------------------------------------------------------------+ //| 获取活跃锁仓组数量 | //+------------------------------------------------------------------+ int GetActiveLockGroupCount() { int count = 0; for(int i = 0; i < ArraySize(lockGroups); i++) { if(lockGroups[i].isActive) count++; } return count; } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { Print("=== 锁仓策略EA初始化 ==="); Print("止盈目标:", ProfitTarget, " 美元"); Print("亏损阈值:", LossThreshold, " 美元"); Print("锁仓功能:", (EnableLockPosition ? "启用" : "禁用")); Print("价格区间平仓:", (EnableBreakEvenClose ? "启用" : "禁用")); if(EnableBreakEvenClose) { Print("价格区间容差:", PriceRangeTolerance * 100, "%"); } Print("手数模式:", (EnableDynamicLots ? "动态手数" : "固定手数")); if(EnableDynamicLots) { Print("基准余额:", BaseBalance, " 基准手数:", StartLots); } else { Print("固定手数:", StartLots); } Print("当前服务器时间: ", TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES)); ArrayResize(lockGroups, 0); lockGroupCount = 0; return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Print("锁仓策略EA已停止,原因代码:", reason); Print("总锁仓组数量:", lockGroupCount); Print("活跃锁仓组数量:", GetActiveLockGroupCount()); }