帳戶監控 EA:每日虧損限制與自動停止交易

📌 本文重點
建立 MQL5 帳戶監控 EA:每日最大虧損限制、最大回撤保護、自動停止交易,以及帳戶狀態通知功能。

為什麼需要帳戶監控?

即使策略有止損,在特殊市場條件(如跳空、流動性不足)下,單日虧損仍可能超出預期。帳戶監控 EA 作為最後一道防線,當虧損達到設定上限時自動停止所有交易。

input double InpMaxDailyLoss    = 3.0;   // 最大每日虧損(帳戶餘額%)
input double InpMaxTotalDD      = 10.0;  // 最大總回撤(%)
input double InpMaxDailyProfit  = 5.0;   // 每日獲利上限(%,達到後停止)
input bool   InpCloseOnLimit    = true;  // 達到限制時自動平倉
input bool   InpSendAlert       = true;  // 發送警報

double g_dayStartBalance = 0;
datetime g_lastDay = 0;
bool   g_tradingStopped = false;

int OnInit()
{
    g_dayStartBalance = AccountInfoDouble(ACCOUNT_BALANCE);
    return INIT_SUCCEEDED;
}

void OnTick()
{
    // 每天重置
    MqlDateTime dt;
    TimeToStruct(TimeCurrent(), dt);
    datetime today = TimeCurrent() - dt.hour*3600 - dt.min*60 - dt.sec;
    if (today != g_lastDay)
    {
        g_lastDay         = today;
        g_dayStartBalance = AccountInfoDouble(ACCOUNT_BALANCE);
        g_tradingStopped  = false;
        Print("新交易日開始,起始餘額: $", g_dayStartBalance);
    }

    if (g_tradingStopped) return;

    double balance   = AccountInfoDouble(ACCOUNT_BALANCE);
    double equity    = AccountInfoDouble(ACCOUNT_EQUITY);
    double highWater = MathMax(balance, g_dayStartBalance); // 歷史最高點

    // 每日虧損計算(以每日起始餘額為基準)
    double dailyPnL    = balance - g_dayStartBalance;
    double dailyPnLPct = (g_dayStartBalance > 0) ? dailyPnL / g_dayStartBalance * 100 : 0;

    // 總回撤計算(以歷史最高點為基準)
    double totalDD     = highWater - equity;
    double totalDDPct  = (highWater > 0) ? totalDD / highWater * 100 : 0;

    bool shouldStop = false;
    string stopReason = "";

    if (dailyPnLPct <= -InpMaxDailyLoss)
    {
        shouldStop = true;
        stopReason = StringFormat("每日虧損達到 %.1f%%(限制: %.1f%%)", MathAbs(dailyPnLPct), InpMaxDailyLoss);
    }
    else if (totalDDPct >= InpMaxTotalDD)
    {
        shouldStop = true;
        stopReason = StringFormat("總回撤達到 %.1f%%(限制: %.1f%%)", totalDDPct, InpMaxTotalDD);
    }
    else if (dailyPnLPct >= InpMaxDailyProfit)
    {
        shouldStop = true;
        stopReason = StringFormat("每日獲利達到 %.1f%%,鎖定獲利", dailyPnLPct);
    }

    if (shouldStop)
    {
        g_tradingStopped = true;
        Print("⚠️ 交易停止!原因: ", stopReason);

        if (InpSendAlert)  Alert("MQL5 監控警報!
", stopReason);
        if (InpCloseOnLimit) CloseAllPositions();
    }

    // 每 5 分鐘在圖表顯示狀態
    static datetime lastUpdate = 0;
    if (TimeCurrent() - lastUpdate >= 300)
    {
        lastUpdate = TimeCurrent();
        Comment(StringFormat(
            "帳戶監控狀態
今日盈虧: %+.2f%% ($%+.2f)
總回撤: %.2f%%
狀態: %s",
            dailyPnLPct, dailyPnL, totalDDPct,
            g_tradingStopped ? "⛔ 已停止" : "✅ 運行中"
        ));
    }
}

void CloseAllPositions()
{
    MqlTradeRequest req={}; MqlTradeResult res={};
    for (int i = PositionsTotal()-1; i >= 0; i--)
    {
        string sym    = PositionGetSymbol(i);
        ulong  ticket = PositionGetInteger(POSITION_TICKET);
        int    type   = (int)PositionGetInteger(POSITION_TYPE);
        req.action    = TRADE_ACTION_DEAL;
        req.symbol    = sym;
        req.volume    = PositionGetDouble(POSITION_VOLUME);
        req.position  = ticket;
        req.type      = (type == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
        req.price     = (req.type == ORDER_TYPE_SELL) ? SymbolInfoDouble(sym, SYMBOL_BID) : SymbolInfoDouble(sym, SYMBOL_ASK);
        req.deviation = 20;
        OrderSend(req, res);
    }
    Print("所有持倉已平倉");
}

本文由 James Lee 撰寫。內容僅供教育目的,不構成投資建議。

Similar Posts

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *