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

436 lines
17 KiB
MQL5

//+------------------------------------------------------------------+
//| 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 <Trade\Trade.mqh>
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());
}