MQL5 訂單管理完整指南:掛單、修改止損與部分平倉

📌 本文重點
完整掌握 MQL5 訂單管理:各類掛單下法、修改掛單與持倉、部分平倉技巧,以及訂單與持倉的查詢方法。

MQL5 的訂單狀態流程

MQL5 的交易流程:掛單(Order) → 觸發後成為 持倉(Position) → 平倉後進入 歷史(History)

各類掛單下法

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

void PlaceAllOrderTypes()
{
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double pt  = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

    // 1. 限價買入(低於現價買)
    g_trade.BuyLimit(0.1, bid - 20*pt);

    // 2. 限價賣出(高於現價賣)
    g_trade.SellLimit(0.1, ask + 20*pt);

    // 3. 停損買入(高於現價突破買)
    g_trade.BuyStop(0.1, ask + 20*pt);

    // 4. 停損賣出(低於現價突破賣)
    g_trade.SellStop(0.1, bid - 20*pt);

    // 5. 停損限價買入(BuyStopLimit)
    // 當價格達到 stop 後,在 limit 掛限價買單
    double stopPrice  = ask + 30*pt;
    double limitPrice = ask + 20*pt;
    MqlTradeRequest req={}; MqlTradeResult res={};
    req.action        = TRADE_ACTION_PENDING;
    req.symbol        = _Symbol;
    req.volume        = 0.1;
    req.type          = ORDER_TYPE_BUY_STOP_LIMIT;
    req.price         = stopPrice;
    req.stoplimit     = limitPrice;
    req.magic         = 12345;
    OrderSend(req, res);
}

修改掛單

void ModifyPendingOrder(ulong ticket, double newPrice, double newSL = 0, double newTP = 0)
{
    if (!OrderSelect(ticket)) { Print("找不到掛單 #", ticket); return; }

    MqlTradeRequest req={}; MqlTradeResult res={};
    req.action  = TRADE_ACTION_MODIFY;
    req.order   = ticket;
    req.price   = NormalizeDouble(newPrice, _Digits);
    req.sl      = NormalizeDouble(newSL, _Digits);
    req.tp      = NormalizeDouble(newTP, _Digits);

    if (!OrderSend(req, res))
        Print("修改掛單失敗 #", ticket, " retcode=", res.retcode);
    else
        Print("掛單修改成功 #", ticket, " 新價格=", newPrice);
}

// 取消掛單
void CancelOrder(ulong ticket)
{
    if (g_trade.OrderDelete(ticket))
        Print("掛單取消成功 #", ticket);
}

部分平倉

// 平倉持倉的指定手數(部分平倉)
bool PartialClose(ulong ticket, double closeLots)
{
    if (!PositionSelectByTicket(ticket)) return false;

    int    type   = (int)PositionGetInteger(POSITION_TYPE);
    double volume = PositionGetDouble(POSITION_VOLUME);
    string sym    = PositionGetString(POSITION_SYMBOL);

    // 確保部分平倉手數不超過總手數
    closeLots = MathMin(closeLots, volume);
    double minLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
    if (closeLots < minLot) { Print("平倉手數過小"); return false; }

    MqlTradeRequest req={}; MqlTradeResult res={};
    req.action   = TRADE_ACTION_DEAL;
    req.symbol   = sym;
    req.volume   = closeLots;
    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= 10;

    if (OrderSend(req, res))
    {
        Print("部分平倉成功:", closeLots, " 手(剩餘 ", volume-closeLots, " 手)");
        return true;
    }
    return false;
}

// 實用場景:到達第一目標時平掉一半
void ManagePartialTP()
{
    for (int i = PositionsTotal()-1; i >= 0; i--)
    {
        if (PositionGetSymbol(i) != _Symbol) continue;
        ulong  ticket    = PositionGetInteger(POSITION_TICKET);
        double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
        double volume    = PositionGetDouble(POSITION_VOLUME);
        double bid       = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double pt        = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

        // 當盈利達到 30 點,平倉一半
        if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
        {
            if (bid >= openPrice + 30*pt && volume >= 0.02)
                PartialClose(ticket, NormalizeDouble(volume/2, 2));
        }
    }
}

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

Similar Posts

發佈留言

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