追蹤止損與保本止損 MQL5 完整實作

📌 本文重點
學習三種追蹤止損(Trailing Stop)方式:固定點數追蹤、ATR 動態追蹤、步進式追蹤,以及保本止損(Break-Even)的完整 MQL5 實作。

為什麼需要追蹤止損?

固定止損無法鎖住浮動盈利。追蹤止損讓止損跟隨價格移動,在保住已有獲利的同時,讓盈利繼續奔跑。

方法一:固定點數追蹤止損

#include <Trade\Trade.mqh>
CTrade g_trade;

input int InpTrailPoints  = 30;  // 追蹤點數
input int InpTrailStep    = 10;  // 步進點數(避免頻繁修改)

void TrailingStop()
{
    double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

    for (int i = PositionsTotal()-1; i >= 0; i--)
    {
        string sym = PositionGetSymbol(i);
        if (sym != _Symbol) continue;
        if (PositionGetInteger(POSITION_MAGIC) != 12345) continue;

        ulong  ticket  = PositionGetInteger(POSITION_TICKET);
        int    type    = (int)PositionGetInteger(POSITION_TYPE);
        double curSL   = PositionGetDouble(POSITION_SL);
        double tp      = PositionGetDouble(POSITION_TP);
        double bid     = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double ask     = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

        if (type == POSITION_TYPE_BUY)
        {
            double newSL = NormalizeDouble(bid - InpTrailPoints * pt, _Digits);
            // 只在新止損比現有止損高且差距超過步進時才修改
            if (newSL > curSL + InpTrailStep * pt)
                g_trade.PositionModify(ticket, newSL, tp);
        }
        else if (type == POSITION_TYPE_SELL)
        {
            double newSL = NormalizeDouble(ask + InpTrailPoints * pt, _Digits);
            if (curSL == 0 || newSL < curSL - InpTrailStep * pt)
                g_trade.PositionModify(ticket, newSL, tp);
        }
    }
}

方法二:保本止損(Break-Even)

input int InpBreakEvenTrigger = 20;  // 觸發保本的盈利點數
input int InpBreakEvenOffset  = 2;   // 保本後止損超過進場價的點數

void MoveToBreakEven()
{
    double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

    for (int i = PositionsTotal()-1; i >= 0; i--)
    {
        if (PositionGetSymbol(i) != _Symbol) continue;
        if (PositionGetInteger(POSITION_MAGIC) != 12345) continue;

        ulong  ticket    = PositionGetInteger(POSITION_TICKET);
        int    type      = (int)PositionGetInteger(POSITION_TYPE);
        double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
        double curSL     = PositionGetDouble(POSITION_SL);
        double tp        = PositionGetDouble(POSITION_TP);
        double bid       = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double ask       = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

        if (type == POSITION_TYPE_BUY)
        {
            // 當盈利達到觸發點數,且止損還在進場價以下
            double triggerPrice = openPrice + InpBreakEvenTrigger * pt;
            double beStop       = openPrice + InpBreakEvenOffset * pt;

            if (bid >= triggerPrice && curSL < beStop)
            {
                if (g_trade.PositionModify(ticket, NormalizeDouble(beStop, _Digits), tp))
                    Print("保本止損設定 #", ticket, " @ ", beStop);
            }
        }
        else if (type == POSITION_TYPE_SELL)
        {
            double triggerPrice = openPrice - InpBreakEvenTrigger * pt;
            double beStop       = openPrice - InpBreakEvenOffset * pt;

            if (ask <= triggerPrice && (curSL == 0 || curSL > beStop))
            {
                if (g_trade.PositionModify(ticket, NormalizeDouble(beStop, _Digits), tp))
                    Print("保本止損設定 #", ticket, " @ ", beStop);
            }
        }
    }
}

// 在 OnTick 中組合使用
void OnTick()
{
    MoveToBreakEven(); // 先執行保本
    TrailingStop();    // 再執行追蹤
}

方法三:階梯式(Stepped)追蹤止損

// 盈利每增加 N 點,止損跟進 N 點(階梯式)
void SteppedTrailing()
{
    double pt   = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    int    step = 25; // 每 25 點一個台階

    for (int i = PositionsTotal()-1; i >= 0; i--)
    {
        if (PositionGetSymbol(i) != _Symbol) continue;

        ulong  ticket    = PositionGetInteger(POSITION_TICKET);
        int    type      = (int)PositionGetInteger(POSITION_TYPE);
        double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
        double curSL     = PositionGetDouble(POSITION_SL);
        double tp        = PositionGetDouble(POSITION_TP);
        double bid       = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double ask       = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

        if (type == POSITION_TYPE_BUY)
        {
            // 計算已走了幾個台階
            int steps = (int)((bid - openPrice) / (step * pt));
            if (steps <= 0) continue;
            double newSL = NormalizeDouble(openPrice + (steps-1) * step * pt, _Digits);
            if (newSL > curSL)
                g_trade.PositionModify(ticket, newSL, tp);
        }
    }
}

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

Similar Posts

發佈留言

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