找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 10|回复: 0

[MT5] 50U战神一单一节

[复制链接]

274

主题

6

回帖

1020

积分

管理员

积分
1020
发表于 昨天 10:14 | 显示全部楼层 |阅读模式
#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();
}

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|量化魔方 ( 陕ICP备2025062059号-3 )|网站地图

GMT+8, 2026-9-9 01:05 , Processed in 0.196873 second(s), 5 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表