|
|
#property copyright "更多免费EA;https://www.115ku.com/"
#property link "https://www.115ku.com/"
#property version "1.00"
#property description "https://www.115ku.com/"
// "=== 授权与时间限制 ==="
string InpAllowedAccounts = ""; // 允许的账号 (多个请用英文逗号隔开,留空为不限制账号)
datetime InpExpiryDate = D'2066.12.31 23:59'; // 使用截止日期 (年.月.日 时:分)
#property strict
// --- MT5必需的库引用 ---
input bool PrintDebug = true; // 调试开关
#include <Trade/Trade.mqh>
#include <Trade/SymbolInfo.mqh>
#include <Trade/PositionInfo.mqh>
#include <Trade/AccountInfo.mqh>
// --- MT5必需的对象声明 ---
CTrade Trade;
CSymbolInfo SymbolInfo;
CPositionInfo PositionInfo;
CAccountInfo AccountInfo;
// --- 枚举定义 ---
enum ENUM_TRADE_DIRECTION
{
TRADE_DIRECTION_BOTH, // 双向交易
TRADE_DIRECTION_BUY_ONLY, // 只做多
TRADE_DIRECTION_SELL_ONLY // 只做空
};
enum ENUM_LOT_MODE
{
LOT_MODE_ADD, // 递增模式 (加法)
LOT_MODE_MULTIPLY // 倍数模式 (乘法)
};
enum ENUM_TRAILING_MODE
{
TRAILING_NONE, // 不使用跟踪止损
TRAILING_FIXED, // 固定点数跟踪
TRAILING_CANDLE, // K线高低点跟踪
TRAILING_FRACTAL // 分形指标跟踪
};
enum ENUM_DD_ACTION
{
DD_CLOSE_ALL, // 平仓所有订单
DD_STOP_NEW, // 仅停止新开仓
DD_CLOSE_AND_STOP // 平仓并停止新开仓
};
// --- 输入参数 ---
input group "=== 核心交易控制 ==="
input bool InpEnableBuy = true; // 允许做多
input bool InpEnableSell = true; // 允许做空
input int InpMagicNumber = 888999; // 魔术号码
input int InpStartHour = 0; // 允许交易开始小时(0-23)
input int InpEndHour = 23; // 允许交易结束小时(0-23)
input group "=== 首单挂单设置 ==="
input bool InpEnableFirstOrder = true; // 允许首单挂单
input double InpFirstOrderDistance = 50.0; // 首单挂单距离(点)
input double InpOrderMoveStep = 10.0; // 挂单移动步长(点)
input group "=== 加仓逻辑设置 ==="
input bool InpEnableReverseAdd = true; // 允许逆势加仓(亏损加仓)
input bool InpEnableTrendAdd = false; // 允许顺势加仓(盈利加仓)
input int InpStepOrderCount = 5; // 阶梯加仓分水岭单数
input double InpStep1Distance = 100.0; // 第一阶段加仓间距(点)
input double InpStep2Distance = 200.0; // 第二阶段加仓间距(点)
input group "=== 手数与风控设置 ==="
input double InpInitialLot = 0.01; // 初始手数
input ENUM_LOT_MODE InpLotMode = LOT_MODE_MULTIPLY; // 加仓手数模式
input double InpLotStepOrMultiplier = 1.5; // 递增量或倍数
input double InpMaxTotalLots = 10.0; // 单边最大总持仓手数限制
input double InpPauseAddLoss = 500.0; // 单边亏损暂停加仓阈值(金额)
input group "=== 金额止盈止损设置 ==="
input double InpBuyProfitTarget = 100.0; // 多头整体止盈金额 (0为关闭)
input double InpBuyLossLimit = 1000.0; // 多头整体止损金额 (0为关闭)
input double InpSellProfitTarget = 100.0; // 空头整体止盈金额 (0为关闭)
input double InpSellLossLimit = 1000.0; // 空头整体止损金额 (0为关闭)
input double InpTotalProfitTarget = 200.0; // 账户总盈亏止盈金额 (0为关闭)
input double InpTotalLossLimit = 2000.0; // 账户总盈亏止损金额 (0为关闭)
input group "=== 阶梯保利熔断 ==="
input bool InpStepProtectEnable = true; // 启用阶梯保利熔断
input double InpStep1Profit = 20.0; // 阶梯1 盈利门槛(净值)
input double InpStep1Protect = 10.0; // 阶梯1 保利金额
input double InpStep2Profit = 50.0; // 阶梯2 盈利门槛(净值)
input double InpStep2Protect = 30.0; // 阶梯2 保利金额
input double InpStep3Profit = 70.0; // 阶梯3 盈利门槛(净值)
input double InpStep3Protect = 50.0; // 阶梯3 保利金额
input double InpStep4Profit = 0.0; // 阶梯4 盈利门槛(净值,0关闭)
input double InpStep4Protect = 0.0; // 阶梯4 保利金额
input double InpStep5Profit = 0.0; // 阶梯5 盈利门槛(净值,0关闭)
input double InpStep5Protect = 0.0; // 阶梯5 保利金额
input ENUM_DD_ACTION InpDDAction = DD_CLOSE_ALL; // 熔断触发动作
input group "=== 跟踪止损设置 ==="
input ENUM_TRAILING_MODE InpTrailingMode = TRAILING_FIXED; // 跟踪止损模式
input double InpTrailingStart = 50.0; // 启动门槛/保利点数(点)
input double InpTrailingStep = 10.0; // 移动步长(点)
input int InpCandleCount = 3; // K线模式:参考K线根数
input group "=== 界面与调试 ==="
input bool InpPrintDebug = true; // 打印调试信息
input bool InpShowInfoOnChart = true; // 显示信息面板
input bool InpDrawLines = true; // 在图表绘制均价线和止损线
// --- 面板样式参数 ---
input string UI_Group = "=== 面板样式设置 ===";
input ENUM_BASE_CORNER UI_Corner = CORNER_RIGHT_UPPER;
input int UI_X_Offset = 20;
input int UI_Y_Offset = 30;
input int UI_FontSize = 10;
input color UI_BgColor = clrBlack;
input color UI_BorderColor = clrGray;
input color UI_TextColor = clrWhite;
input color UI_TitleColor = clrGold;
input color UI_ProfitColor = clrLime;
input color UI_LossColor = clrRed;
// --- 全局变量 ---
int fractalHandle = INVALID_HANDLE;
double upperFractals[];
double lowerFractals[];
// 统计变量
int buyCount = 0;
int sellCount = 0;
double buyLots = 0.0;
double sellLots = 0.0;
double buyProfit = 0.0;
double sellProfit = 0.0;
double buyAvgPrice = 0.0;
double sellAvgPrice = 0.0;
double lastBuyPrice = 0.0;
double lastSellPrice = 0.0;
// 挂单变量
ulong buyStopTicket = 0;
ulong sellStopTicket = 0;
double currentBuyStopPrice = 0.0;
double currentSellStopPrice = 0.0;
// 阶梯保利熔断变量
double startEquity = 0.0; // 基准净值 (初始或重新武装时)
double currentProfit = 0.0; // 当前净值盈利 (净值 = 余额 + 持仓浮动盈亏)
double peakProfit = 0.0; // 历史峰值净值盈利
bool circuitBreakerTriggered = false;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
if(!SymbolInfo.Name(_Symbol) || !SymbolInfo.RefreshRates())
{
Print("初始化交易品种信息失败!");
return(INIT_FAILED);
}
Trade.SetExpertMagicNumber(InpMagicNumber);
if(InpTrailingMode == TRAILING_FRACTAL)
{
fractalHandle = iFractals(_Symbol, PERIOD_CURRENT);
if(fractalHandle == INVALID_HANDLE)
{
Print("创建分形指标失败!");
return(INIT_FAILED);
}
ArraySetAsSeries(upperFractals, true);
ArraySetAsSeries(lowerFractals, true);
}
if(InpPrintDebug) Print("极品双向多功能加仓 EA 初始化成功");
// 初始化阶梯保利熔断变量 (盈利以当前净值为准)
startEquity = AccountInfo.Equity();
currentProfit = 0.0;
peakProfit = 0.0;
circuitBreakerTriggered = false;
if(InpPrintDebug) Print(StringFormat("阶梯保利熔断初始化: 基准净值=%.2f", startEquity));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(fractalHandle != INVALID_HANDLE) IndicatorRelease(fractalHandle);
ObjectsDeleteAll(ChartID(), "InfoPanel");
ObjectsDeleteAll(ChartID(), "EA_Line_");
ChartRedraw();
if(InpPrintDebug) Print("EA已卸载");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// --- 账号与时间授权检查 ---
long currentAccount = AccountInfoInteger(ACCOUNT_LOGIN);
datetime currentStartTime = TimeCurrent();
bool isAccountAuthorized = false;
// 检查是否启用账号限制(账号字符串非空)
StringTrimLeft(InpAllowedAccounts);
StringTrimRight(InpAllowedAccounts);
if(StringLen(InpAllowedAccounts) > 0)
{
// 解析允许的账号字符串
string accounts[];
ushort sep = StringGetCharacter(",", 0);
int count = StringSplit(InpAllowedAccounts, sep, accounts);
for(int i = 0; i < count; i++)
{
StringTrimLeft(accounts);
StringTrimRight(accounts);
if(StringToInteger(accounts) == currentAccount)
{
isAccountAuthorized = true;
break;
}
}
// 验证账号授权
if(!isAccountAuthorized)
{
Alert("错误:账号未获得授权!");
Comment("错误:账号未获得授权!");
Sleep(10000);
return;
}
}
// 验证时间限制
if(currentStartTime > InpExpiryDate)
{
Alert("错误:系统已到期!");
Comment("错误:系统已到期!");
Sleep(10000);
return;
}
SymbolInfo.RefreshRates();
// 1. 检查交易时间
if(!CheckTradeTime()) return;
// 2. 统计当前持仓和挂单状态
CalculateStatistics();
// 2.1 阶梯保利熔断检查
CheckDrawdownProtection();
// 如果熔断已触发,跳过所有新交易逻辑
if(circuitBreakerTriggered && InpDDAction != DD_CLOSE_ALL)
{
// 仅停止新开仓,但仍需跟踪止损和界面更新
if(InpTrailingMode != TRAILING_NONE) ManageTrailingStop();
if(InpShowInfoOnChart) DrawInfoPanel();
if(InpDrawLines) DrawLines();
return;
}
// 3. 检查金额止盈止损 (全平仓逻辑)
if(CheckMoneyClose())
{
// 如果触发了全平仓,重新统计状态
CalculateStatistics();
}
// 4. 跟踪止损逻辑
if(InpTrailingMode != TRAILING_NONE)
{
ManageTrailingStop();
}
// 5. 首单挂单逻辑
if(InpEnableFirstOrder)
{
ManageFirstOrders();
}
// 6. 加仓逻辑
ManageGridOrders();
// 7. 更新界面和画线
if(InpShowInfoOnChart) DrawInfoPanel();
if(InpDrawLines) DrawLines();
}
//+------------------------------------------------------------------+
//| 检查交易时间 |
//+------------------------------------------------------------------+
bool CheckTradeTime()
{
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
if(InpStartHour <= InpEndHour)
{
if(dt.hour >= InpStartHour && dt.hour <= InpEndHour) return true;
}
else // 跨天情况,如 22 到 2
{
if(dt.hour >= InpStartHour || dt.hour <= InpEndHour) return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 统计持仓和挂单信息 |
//+------------------------------------------------------------------+
void CalculateStatistics()
{
buyCount = 0; sellCount = 0;
buyLots = 0.0; sellLots = 0.0;
buyProfit = 0.0; sellProfit = 0.0;
double buyCost = 0.0; double sellCost = 0.0;
// 寻找最后开仓的价格 (按时间排序,这里简化为遍历记录最后一个)
datetime lastBuyTime = 0;
datetime lastSellTime = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionInfo.SelectByIndex(i))
{
if(PositionInfo.Symbol() == _Symbol && PositionInfo.Magic() == InpMagicNumber)
{
double vol = PositionInfo.Volume();
double price = PositionInfo.PriceOpen();
double profit = PositionInfo.Profit() + PositionInfo.Swap();
datetime time = PositionInfo.Time();
if(PositionInfo.PositionType() == POSITION_TYPE_BUY)
{
buyCount++;
buyLots += vol;
buyProfit += profit;
buyCost += price * vol;
if(time > lastBuyTime)
{
lastBuyTime = time;
lastBuyPrice = price;
}
}
else if(PositionInfo.PositionType() == POSITION_TYPE_SELL)
{
sellCount++;
sellLots += vol;
sellProfit += profit;
sellCost += price * vol;
if(time > lastSellTime)
{
lastSellTime = time;
lastSellPrice = price;
}
}
}
}
}
buyAvgPrice = (buyLots > 0) ? (buyCost / buyLots) : 0.0;
sellAvgPrice = (sellLots > 0) ? (sellCost / sellLots) : 0.0;
// 统计挂单
buyStopTicket = 0; sellStopTicket = 0;
currentBuyStopPrice = 0.0; currentSellStopPrice = 0.0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(ticket > 0)
{
if(OrderGetString(ORDER_SYMBOL) == _Symbol && OrderGetInteger(ORDER_MAGIC) == InpMagicNumber)
{
ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
if(type == ORDER_TYPE_BUY_STOP)
{
buyStopTicket = ticket;
currentBuyStopPrice = OrderGetDouble(ORDER_PRICE_OPEN);
}
else if(type == ORDER_TYPE_SELL_STOP)
{
sellStopTicket = ticket;
currentSellStopPrice = OrderGetDouble(ORDER_PRICE_OPEN);
}
}
}
}
}
//+------------------------------------------------------------------+
//| 阶梯保利熔断检查 (盈利以当前净值为准) |
//+------------------------------------------------------------------+
bool CheckDrawdownProtection()
{
if(!InpStepProtectEnable) return false;
// DD_CLOSE_ALL模式: 平仓完成后重新武装,允许后续再次触发保护
if(circuitBreakerTriggered && InpDDAction == DD_CLOSE_ALL && buyCount == 0 && sellCount == 0)
{
circuitBreakerTriggered = false;
// 以平仓后的净值为新基准,重新累计盈利
startEquity = AccountInfo.Equity();
peakProfit = 0.0;
currentProfit = 0.0;
if(InpPrintDebug) Print(StringFormat("阶梯保利熔断: 持仓已清空, 保护重新武装, 新基准净值=%.2f", startEquity));
}
// 盈利以当前净值为准 (净值 = 余额 + 持仓浮动盈亏)
currentProfit = AccountInfo.Equity() - startEquity;
if(currentProfit > peakProfit) peakProfit = currentProfit;
// 按峰值盈利确定保护档位 (取最高匹配档)
double protectAmount = CalculateProtectAmount();
if(protectAmount <= 0) return false;
// 触发: 当前净值盈利回落到保护金额以下 (锁定至少保护金额的利润)
if(currentProfit <= protectAmount && !circuitBreakerTriggered)
{
circuitBreakerTriggered = true;
if(InpPrintDebug) Print(StringFormat("触发阶梯保利熔断! 峰值净值盈利:%.2f, 应保利:%.2f, 当前净值盈利:%.2f",
peakProfit, protectAmount, currentProfit));
if(InpDDAction == DD_CLOSE_ALL || InpDDAction == DD_CLOSE_AND_STOP)
{
if(InpPrintDebug) Print("阶梯保利熔断: 平仓所有持仓并删除挂单");
CloseAllPositions(POSITION_TYPE_BUY);
CloseAllPositions(POSITION_TYPE_SELL);
DeleteAllOrders();
}
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 计算当前阶梯应保利金额 (按峰值净值盈利匹配最高档) |
//+------------------------------------------------------------------+
double CalculateProtectAmount()
{
if(!InpStepProtectEnable) return 0.0;
double protect = 0.0;
if(InpStep1Profit > 0 && peakProfit >= InpStep1Profit) protect = InpStep1Protect;
if(InpStep2Profit > 0 && peakProfit >= InpStep2Profit) protect = InpStep2Protect;
if(InpStep3Profit > 0 && peakProfit >= InpStep3Profit) protect = InpStep3Protect;
if(InpStep4Profit > 0 && peakProfit >= InpStep4Profit) protect = InpStep4Protect;
if(InpStep5Profit > 0 && peakProfit >= InpStep5Profit) protect = InpStep5Protect;
return protect;
}
//+------------------------------------------------------------------+
//| 检查金额止盈止损 |
//+------------------------------------------------------------------+
bool CheckMoneyClose()
{
bool closed = false;
double totalProfit = buyProfit + sellProfit;
// 1. 账户总盈亏检查
if((InpTotalProfitTarget > 0 && totalProfit >= InpTotalProfitTarget) ||
(InpTotalLossLimit > 0 && totalProfit <= -InpTotalLossLimit))
{
if(InpPrintDebug) Print(StringFormat("触发总盈亏平仓! 当前总盈亏: %.2f", totalProfit));
CloseAllPositions(POSITION_TYPE_BUY);
CloseAllPositions(POSITION_TYPE_SELL);
DeleteAllOrders();
return true;
}
// 2. 多头单边检查
if(buyCount > 0)
{
if((InpBuyProfitTarget > 0 && buyProfit >= InpBuyProfitTarget) ||
(InpBuyLossLimit > 0 && buyProfit <= -InpBuyLossLimit))
{
if(InpPrintDebug) Print(StringFormat("触发多头金额平仓! 多头盈亏: %.2f", buyProfit));
CloseAllPositions(POSITION_TYPE_BUY);
DeleteOrdersByType(ORDER_TYPE_BUY_STOP);
closed = true;
}
}
// 3. 空头单边检查
if(sellCount > 0)
{
if((InpSellProfitTarget > 0 && sellProfit >= InpSellProfitTarget) ||
(InpSellLossLimit > 0 && sellProfit <= -InpSellLossLimit))
{
if(InpPrintDebug) Print(StringFormat("触发空头金额平仓! 空头盈亏: %.2f", sellProfit));
CloseAllPositions(POSITION_TYPE_SELL);
DeleteOrdersByType(ORDER_TYPE_SELL_STOP);
closed = true;
}
}
return closed;
}
//+------------------------------------------------------------------+
//| 平仓指定方向的所有持仓 |
//+------------------------------------------------------------------+
void CloseAllPositions(ENUM_POSITION_TYPE type)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionInfo.SelectByIndex(i))
{
if(PositionInfo.Symbol() == _Symbol && PositionInfo.Magic() == InpMagicNumber)
{
if(PositionInfo.PositionType() == type)
{
Trade.PositionClose(PositionInfo.Ticket());
}
}
}
}
}
//+------------------------------------------------------------------+
//| 删除所有挂单 |
//+------------------------------------------------------------------+
void DeleteAllOrders()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(ticket > 0 && OrderGetString(ORDER_SYMBOL) == _Symbol && OrderGetInteger(ORDER_MAGIC) == InpMagicNumber)
{
Trade.OrderDelete(ticket);
}
}
}
//+------------------------------------------------------------------+
//| 删除指定类型的挂单 |
//+------------------------------------------------------------------+
void DeleteOrdersByType(ENUM_ORDER_TYPE type)
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(ticket > 0 && OrderGetString(ORDER_SYMBOL) == _Symbol && OrderGetInteger(ORDER_MAGIC) == InpMagicNumber)
{
if((ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE) == type)
{
Trade.OrderDelete(ticket);
}
}
}
}
//+------------------------------------------------------------------+
//| 管理首单挂单 |
//+------------------------------------------------------------------+
void ManageFirstOrders()
{
double pt = Point();
double minLevel = SymbolInfo.StopsLevel() * pt;
double dist = MathMax(InpFirstOrderDistance * pt, minLevel);
double step = InpOrderMoveStep * pt;
// 多头首单
if(InpEnableBuy && buyCount == 0)
{
double ask = SymbolInfo.Ask();
double targetBuyPrice = SymbolInfo.NormalizePrice(ask + dist);
if(buyStopTicket == 0) // 没有挂单,下新单
{
if(Trade.BuyStop(InpInitialLot, targetBuyPrice, _Symbol, 0, 0, 0, 0, "First_BuyStop"))
{
if(InpPrintDebug) Print("下达多头首单挂单成功, 价格: ", targetBuyPrice);
}
}
else // 有挂单,检查是否需要移动 (价格下降时下移)
{
if(targetBuyPrice < currentBuyStopPrice - step)
{
if(Trade.OrderModify(buyStopTicket, targetBuyPrice, 0, 0, 0, 0))
{
if(InpPrintDebug) Print("移动多头挂单成功, 新价格: ", targetBuyPrice);
}
}
}
}
else if(buyCount > 0 && buyStopTicket > 0) // 已有持仓,删除多余挂单
{
Trade.OrderDelete(buyStopTicket);
}
// 空头首单
if(InpEnableSell && sellCount == 0)
{
double bid = SymbolInfo.Bid();
double targetSellPrice = SymbolInfo.NormalizePrice(bid - dist);
if(sellStopTicket == 0)
{
if(Trade.SellStop(InpInitialLot, targetSellPrice, _Symbol, 0, 0, 0, 0, "First_SellStop"))
{
if(InpPrintDebug) Print("下达空头首单挂单成功, 价格: ", targetSellPrice);
}
}
else
{
if(targetSellPrice > currentSellStopPrice + step)
{
if(Trade.OrderModify(sellStopTicket, targetSellPrice, 0, 0, 0, 0))
{
if(InpPrintDebug) Print("移动空头挂单成功, 新价格: ", targetSellPrice);
}
}
}
}
else if(sellCount > 0 && sellStopTicket > 0)
{
Trade.OrderDelete(sellStopTicket);
}
}
//+------------------------------------------------------------------+
//| 计算加仓手数 |
//+------------------------------------------------------------------+
double CalculateNextLot(int currentCount)
{
double nextLot = InpInitialLot;
if(InpLotMode == LOT_MODE_ADD)
{
nextLot = InpInitialLot + currentCount * InpLotStepOrMultiplier;
}
else if(InpLotMode == LOT_MODE_MULTIPLY)
{
nextLot = InpInitialLot * MathPow(InpLotStepOrMultiplier, currentCount);
}
// 向下取整到最小步长
double lotStep = SymbolInfo.LotsStep();
nextLot = MathFloor(nextLot / lotStep) * lotStep;
// 限制最大最小手数
if(nextLot < SymbolInfo.LotsMin()) nextLot = SymbolInfo.LotsMin();
if(nextLot > SymbolInfo.LotsMax()) nextLot = SymbolInfo.LotsMax();
return nextLot;
}
//+------------------------------------------------------------------+
//| 管理加仓逻辑 |
//+------------------------------------------------------------------+
void ManageGridOrders()
{
double pt = Point();
double ask = SymbolInfo.Ask();
double bid = SymbolInfo.Bid();
// 多头加仓
if(InpEnableBuy && buyCount > 0)
{
bool pauseBuy = (InpPauseAddLoss > 0 && buyProfit <= -InpPauseAddLoss);
if(!pauseBuy && buyLots < InpMaxTotalLots)
{
double currentDist = (buyCount < InpStepOrderCount) ? InpStep1Distance * pt : InpStep2Distance * pt;
double nextLot = CalculateNextLot(buyCount);
// 逆势加仓 (价格下跌)
if(InpEnableReverseAdd && ask <= lastBuyPrice - currentDist)
{
if(Trade.Buy(nextLot, _Symbol, ask, 0, 0, "Grid_Reverse_Buy"))
{
if(InpPrintDebug) Print(StringFormat("多头逆势加仓成功! 第%d单, 手数:%.2f", buyCount+1, nextLot));
}
}
// 顺势加仓 (价格上涨)
else if(InpEnableTrendAdd && ask >= lastBuyPrice + currentDist)
{
if(Trade.Buy(nextLot, _Symbol, ask, 0, 0, "Grid_Trend_Buy"))
{
if(InpPrintDebug) Print(StringFormat("多头顺势加仓成功! 第%d单, 手数:%.2f", buyCount+1, nextLot));
}
}
}
}
// 空头加仓
if(InpEnableSell && sellCount > 0)
{
bool pauseSell = (InpPauseAddLoss > 0 && sellProfit <= -InpPauseAddLoss);
if(!pauseSell && sellLots < InpMaxTotalLots)
{
double currentDist = (sellCount < InpStepOrderCount) ? InpStep1Distance * pt : InpStep2Distance * pt;
double nextLot = CalculateNextLot(sellCount);
// 逆势加仓 (价格上涨)
if(InpEnableReverseAdd && bid >= lastSellPrice + currentDist)
{
if(Trade.Sell(nextLot, _Symbol, bid, 0, 0, "Grid_Reverse_Sell"))
{
if(InpPrintDebug) Print(StringFormat("空头逆势加仓成功! 第%d单, 手数:%.2f", sellCount+1, nextLot));
}
}
// 顺势加仓 (价格下跌)
else if(InpEnableTrendAdd && bid <= lastSellPrice - currentDist)
{
if(Trade.Sell(nextLot, _Symbol, bid, 0, 0, "Grid_Trend_Sell"))
{
if(InpPrintDebug) Print(StringFormat("空头顺势加仓成功! 第%d单, 手数:%.2f", sellCount+1, nextLot));
}
}
}
}
}
//+------------------------------------------------------------------+
//| 管理跟踪止损 |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
double pt = Point();
double minLevel = SymbolInfo.StopsLevel() * pt;
double startDist = InpTrailingStart * pt;
double stepDist = InpTrailingStep * pt;
double ask = SymbolInfo.Ask();
double bid = SymbolInfo.Bid();
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionInfo.SelectByIndex(i))
{
if(PositionInfo.Symbol() == _Symbol && PositionInfo.Magic() == InpMagicNumber)
{
ulong ticket = PositionInfo.Ticket();
double openPrice = PositionInfo.PriceOpen();
double currentSL = PositionInfo.StopLoss();
double currentTP = PositionInfo.TakeProfit();
ENUM_POSITION_TYPE type = PositionInfo.PositionType();
double newSL = 0.0;
if(type == POSITION_TYPE_BUY)
{
if(bid - openPrice >= startDist) // 达到启动门槛
{
if(InpTrailingMode == TRAILING_FIXED)
{
newSL = bid - startDist;
}
else if(InpTrailingMode == TRAILING_CANDLE)
{
double lowest = iLow(_Symbol, PERIOD_CURRENT, iLowest(_Symbol, PERIOD_CURRENT, MODE_LOW, InpCandleCount, 1));
newSL = lowest - minLevel;
}
else if(InpTrailingMode == TRAILING_FRACTAL)
{
if(CopyBuffer(fractalHandle, 1, 1, 10, lowerFractals) > 0)
{
for(int j=0; j<10; j++)
{
if(lowerFractals[j] != EMPTY_VALUE && lowerFractals[j] < bid)
{
newSL = lowerFractals[j] - minLevel;
break;
}
}
}
}
newSL = SymbolInfo.NormalizePrice(newSL);
// 检查是否需要更新 (新SL比旧SL高,且差距大于步长)
if(newSL > openPrice && (currentSL == 0.0 || newSL > currentSL + stepDist))
{
if(newSL < bid - minLevel) // 满足平台限制
{
Trade.PositionModify(ticket, newSL, currentTP);
if(InpPrintDebug) Print("多单跟踪止损修改成功, Ticket:", ticket, " 新SL:", newSL);
}
}
}
}
else if(type == POSITION_TYPE_SELL)
{
if(openPrice - ask >= startDist)
{
if(InpTrailingMode == TRAILING_FIXED)
{
newSL = ask + startDist;
}
else if(InpTrailingMode == TRAILING_CANDLE)
{
double highest = iHigh(_Symbol, PERIOD_CURRENT, iHighest(_Symbol, PERIOD_CURRENT, MODE_HIGH, InpCandleCount, 1));
newSL = highest + minLevel;
}
else if(InpTrailingMode == TRAILING_FRACTAL)
{
if(CopyBuffer(fractalHandle, 0, 1, 10, upperFractals) > 0)
{
for(int j=0; j<10; j++)
{
if(upperFractals[j] != EMPTY_VALUE && upperFractals[j] > ask)
{
newSL = upperFractals[j] + minLevel;
break;
}
}
}
}
newSL = SymbolInfo.NormalizePrice(newSL);
if(newSL < openPrice && (currentSL == 0.0 || newSL < currentSL - stepDist))
{
if(newSL > ask + minLevel)
{
Trade.PositionModify(ticket, newSL, currentTP);
if(InpPrintDebug) Print("空单跟踪止损修改成功, Ticket:", ticket, " 新SL:", newSL);
}
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 绘制图表辅助线 |
//+------------------------------------------------------------------+
void DrawLines()
{
// 多头均价线
string buyLineName = "EA_Line_BuyAvg";
if(buyCount > 0)
{
if(ObjectFind(0, buyLineName) < 0) ObjectCreate(0, buyLineName, OBJ_HLINE, 0, 0, buyAvgPrice);
else ObjectSetDouble(0, buyLineName, OBJPROP_PRICE, buyAvgPrice);
ObjectSetInteger(0, buyLineName, OBJPROP_COLOR, clrDodgerBlue);
ObjectSetInteger(0, buyLineName, OBJPROP_STYLE, STYLE_DASH);
}
else
{
ObjectDelete(0, buyLineName);
}
// 空头均价线
string sellLineName = "EA_Line_SellAvg";
if(sellCount > 0)
{
if(ObjectFind(0, sellLineName) < 0) ObjectCreate(0, sellLineName, OBJ_HLINE, 0, 0, sellAvgPrice);
else ObjectSetDouble(0, sellLineName, OBJPROP_PRICE, sellAvgPrice);
ObjectSetInteger(0, sellLineName, OBJPROP_COLOR, clrOrangeRed);
ObjectSetInteger(0, sellLineName, OBJPROP_STYLE, STYLE_DASH);
}
else
{
ObjectDelete(0, sellLineName);
}
}
//+------------------------------------------------------------------+
//| 信息面板显示函数 |
//+------------------------------------------------------------------+
void DrawInfoPanel()
{
string lines[25];
lines[0] = "=== 极品双向加仓 EA ===";
lines[1] = "账户余额: " + DoubleToString(AccountInfo.Balance(), 2);
lines[2] = "账户净值: " + DoubleToString(AccountInfo.Equity(), 2);
lines[3] = "可用保证: " + DoubleToString(AccountInfo.FreeMargin(), 2);
lines[4] = "------------------------";
lines[5] = "多单数量: " + IntegerToString(buyCount) + " 单";
lines[6] = "多单手数: " + DoubleToString(buyLots, 2) + " 手";
lines[7] = "多单盈亏: " + DoubleToString(buyProfit, 2);
lines[8] = "多单均价: " + DoubleToString(buyAvgPrice, 5);
lines[9] = "------------------------";
lines[10] = "空单数量: " + IntegerToString(sellCount) + " 单";
lines[11] = "空单手数: " + DoubleToString(sellLots, 2) + " 手";
lines[12] = "空单盈亏: " + DoubleToString(sellProfit, 2);
lines[13] = "空单均价: " + DoubleToString(sellAvgPrice, 5);
lines[14] = "------------------------";
lines[15] = "全局总盈亏: " + DoubleToString(buyProfit + sellProfit, 2);
lines[16] = "净值盈利: " + DoubleToString(currentProfit, 2);
lines[17] = "峰值盈利: " + DoubleToString(peakProfit, 2);
lines[18] = "当前应保利: " + DoubleToString(CalculateProtectAmount(), 2);
lines[19] = "熔断保护: " + (circuitBreakerTriggered ? "已触发" : "正常");
string status = "运行中";
if(!CheckTradeTime()) status = "非交易时段";
lines[16] = "EA 状态: " + status;
DrawInfoPanelFuction(lines, "InfoPanel", UI_Corner, UI_X_Offset, UI_Y_Offset, 10, UI_BgColor, UI_BorderColor, "Arial", UI_FontSize, 1.8, 0.8, UI_TextColor, UI_TitleColor, UI_ProfitColor, UI_LossColor);
}
void DrawInfoPanelFuction(string &lines[],
string panelPrefix = "InfoPanel",
ENUM_BASE_CORNER panelCorner = CORNER_LEFT_UPPER,
int panelX_Offset = 10,
int panelY_Offset = 30,
int panelPadding = 10,
color bgColor = clrNavy,
color borderColor = clrWhite,
string fontName = "",
int fontSize = 10,
double lineHeightRatio = 1.8,
double fontWidthRatio = 0.8,
color textColor = clrWhite,
color titleColor = clrYellow,
color profitColor = clrLime,
color lossColor = clrRed)
{
int linesCount = ArraySize(lines);
int maxTextPixelWidth = 0;
int validLines = 0;
for(int i = 0; i < linesCount; i++)
{
if(lines == "") continue;
validLines++;
int currentLineWidth = (int)(StringLen(lines) * fontSize * fontWidthRatio * 1.5);
if(currentLineWidth > maxTextPixelWidth)
{
maxTextPixelWidth = currentLineWidth;
}
}
int calculatedPanelWidth = maxTextPixelWidth + panelPadding * 2;
int calculatedLineHeight = (int)(fontSize * lineHeightRatio);
int backgroundHeight = validLines * calculatedLineHeight + panelPadding * 2;
string backgroundName = panelPrefix + "_Background";
if(ObjectFind(0, backgroundName) < 0)
{
ObjectCreate(0, backgroundName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
}
ObjectSetInteger(0, backgroundName, OBJPROP_CORNER, panelCorner);
ObjectSetInteger(0, backgroundName, OBJPROP_XDISTANCE, panelX_Offset);
ObjectSetInteger(0, backgroundName, OBJPROP_YDISTANCE, panelY_Offset);
ObjectSetInteger(0, backgroundName, OBJPROP_XSIZE, calculatedPanelWidth);
ObjectSetInteger(0, backgroundName, OBJPROP_YSIZE, backgroundHeight);
ObjectSetInteger(0, backgroundName, OBJPROP_BGCOLOR, bgColor);
ObjectSetInteger(0, backgroundName, OBJPROP_COLOR, borderColor);
ObjectSetInteger(0, backgroundName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, backgroundName, OBJPROP_BACK, false);
int drawIndex = 0;
for(int i = 0; i < linesCount; i++)
{
if(lines == "") continue;
string objName = panelPrefix + "_Text_" + IntegerToString(drawIndex);
if(ObjectFind(0, objName) < 0)
{
ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0);
}
color currentTextColor = textColor;
if(StringFind(lines, "===") >= 0) currentTextColor = titleColor;
else if(StringFind(lines, "多单盈亏") >= 0 || StringFind(lines, "全局总盈亏") >= 0)
{
if(StringFind(lines, "-") >= 0) currentTextColor = lossColor;
else currentTextColor = profitColor;
}
else if(StringFind(lines, "空单盈亏") >= 0)
{
if(StringFind(lines, "-") >= 0) currentTextColor = lossColor;
else currentTextColor = profitColor;
}
ObjectSetString(0, objName, OBJPROP_TEXT, " " + lines);
ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize);
ObjectSetString(0, objName, OBJPROP_FONT, fontName);
ObjectSetInteger(0, objName, OBJPROP_COLOR, currentTextColor);
ObjectSetInteger(0, objName, OBJPROP_CORNER, panelCorner);
ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, panelX_Offset + panelPadding);
int yPos = panelY_Offset + panelPadding + drawIndex * calculatedLineHeight;
ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yPos);
drawIndex++;
}
int i = drawIndex;
while(true)
{
string oldObjName = panelPrefix + "_Text_" + IntegerToString(i);
if(ObjectFind(0, oldObjName) >= 0)
{
ObjectDelete(0, oldObjName);
i++;
}
else
{
break;
}
}
ChartRedraw();
}
|
|