436 lines
17 KiB
MQL5
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());
|
|
}
|
|
|