← 返回文章列表
March 23, 2026
5 分鐘閱讀

演算法交易中的訂單型別:從追價限價單到虛擬訂單

演算法交易中的訂單型別:從追價限價單到虛擬訂單
#訂單
#演算法交易
#限價單
#追價
#虛擬訂單
#網格機器人
#做市
📖
Part 2 of 6 · Collection
Order Book & Market Microstructure

當新手開啟交易所終端時,他看到兩個按鈕:"買入"和"賣出"。當演算法交易者開啟自己的程式碼庫時,他看到二十七種訂單型別、三層抽象和一堆讓人想合上筆記本去菜市場賣黃瓜的邊界情況。但遺憾的是,賣黃瓜無法在UTC時間3:59做資金費率套利——所以讓我們深入研究吧。

在這篇文章中,我們將從基礎的交易所訂單一路走到只存在於你係統中、永遠不會出現在訂單簿中的合成虛擬構造。期待TypeScript、Python、一些痛苦和一點頓悟。


1. 標準交易所訂單:不可或缺的基礎

標準訂單 標準訂單型別分類:從市價單到冰山訂單

在構建複雜的東西之前,我們需要確保正確理解基本構建模組。令人驚訝的是,有多少人混淆了止損限價單和止損市價單,然後奇怪為什麼他們的止損"沒有觸發"(劇透:確實觸發了,但限價單因滑點而未能成交)。

市價單(Market order)

最簡單同時也是最危險的型別。你告訴交易所:"現在買/賣,以任何可用價格。"交易所從訂單簿中獲取流動性,從最優價格開始。如果最優價位的數量不夠——就會繼續滑向更深的價位。

何時使用: 緊急平倉、執行速度重於價格的訊號。

陷阱: 在流動性稀薄的市場上,100 BTC的市價單可能將價格推動幾個百分點。不考慮市場衝擊來模擬市價單的回測純屬幻想。

限價單(Limit order)

你指定一個具體的價格。訂單進入訂單簿等待,直到有人接受你的價格。如果限價買入單的價格高於當前市場價——它會立即成交(類似市價單,但有最高價格保證)。

關鍵點: 限價單不保證成交。價格可能觸及你的水平後反轉,而你仍然排在佇列中(詳見我們關於佇列位置的文章)。

止損市價單和止損限價單

這裡是混淆開始的地方。兩種型別都是"休眠"訂單,在達到觸發價格(止損價格)時啟用。但是:

  • 止損市價單(Stop-market):觸發後轉為市價單。保證成交,但不保證價格。
  • 止損限價單(Stop-limit):觸發後轉為限價單。保證價格(不差於指定價),但不保證成交。

在波動劇烈的加密市場上,止損限價單可能會"失效"——價格突破了止損位,限價單已掛出,但市場已經飛走了。你留下了一個未成交的限價單和不斷增長的虧損。這正是為什麼止損通常使用止損市價單的原因。

追蹤止損(Trailing stop)

一種以設定距離"跟隨"價格的止損。價格上漲——止損上移。價格下跌——止損保持不動。適用於趨勢策略中保護利潤。

交易所支援: 並非所有交易所都支援原生追蹤止損。演算法交易者通常以程式設計方式實現——這樣可以更好地控制參數(回撥率、啟用價格、步長)。

冰山訂單(Iceberg order)

訂單簿中只顯示總量的一部分。你想買1000 BTC,但只在簿上顯示10個。當前10個成交後——下一個10個出現。

為什麼: 為了不向市場暴露你的真實意圖。訂單簿中的大單是向所有人發出的訊號,表明"有大戶想買/賣"。高頻交易演算法會開始搶先交易,價格就會偏離你的方向。

注意: 在許多加密交易所,冰山訂單要麼不支援,要麼很容易通過相同數量的模式被檢測到。高階演算法會隨機化可見部分的大小。

有效時間參數:GTC、GTD、IOC、FOK

這些不是獨立的訂單型別,而是有效時間參數——訂單存活多長時間:

參數 全稱 行為
GTC Good Till Cancelled 直到取消。預設標準
GTD Good Till Date 直到指定的日期/時間
IOC Immediate or Cancel 立即執行(全部或部分),剩餘取消
FOK Fill or Kill 全部立即執行。如不可能——完全取消

IOC與FOK: 區別至關重要。IOC可以部分成交——你想買100 BTC,買了3個,其餘被取消。FOK要麼100個,要麼全不成交。

僅掛單(Post-only / Maker-only)

保證以掛單方進入訂單簿、永遠不以吃單方成交的訂單。如果下單時的價格會導致立即成交——交易所會拒絕它(或調整價格,取決於交易所)。

為什麼: 掛單手續費通常低於吃單手續費(Binance上VIP級別為0.02% vs 0.04%)。對於每天下數千單的做市商來說,手續費差異就是盈利和虧損之間的差異。


2. TWAP和VWAP:機構如何在訂單簿中隱藏大象

當對沖基金想建立5000萬美元的倉位時,它不會下一個市價單。它使用執行演算法——將大訂單拆分成許多小訂單,並在一段時間內執行,以最小化市場衝擊。

TWAP(時間加權平均價格)

思路非常簡單:將總量分成等份,在等間隔的時間段內執行。

import asyncio
from datetime import datetime, timedelta

class TWAPExecutor:
    """
    TWAP执行器:将大订单分成等份,
    并在等间隔的时间段内执行。
    """
    def __init__(self, exchange, symbol: str, side: str,
                 total_qty: float, duration_minutes: int, num_slices: int):
        self.exchange = exchange
        self.symbol = symbol
        self.side = side
        self.total_qty = total_qty
        self.slice_qty = total_qty / num_slices
        self.interval = (duration_minutes * 60) / num_slices
        self.num_slices = num_slices
        self.executed_qty = 0.0
        self.fills: list[dict] = []

    async def execute(self):
        for i in range(self.num_slices):
            remaining = self.total_qty - self.executed_qty
            qty = min(self.slice_qty, remaining)
            if qty <= 0:
                break

            try:
                order = await self.exchange.create_order(
                    symbol=self.symbol,
                    type="market",
                    side=self.side,
                    amount=qty,
                )
                self.executed_qty += float(order["filled"])
                self.fills.append(order)
                print(f"[TWAP] slice {i+1}/{self.num_slices}: "
                      f"filled {order['filled']} @ {order['average']}")
            except Exception as e:
                print(f"[TWAP] slice {i+1} failed: {e}")

            if i < self.num_slices - 1:
                await asyncio.sleep(self.interval)

        avg_price = (
            sum(f["cost"] for f in self.fills) /
            sum(f["filled"] for f in self.fills)
        ) if self.fills else 0
        print(f"[TWAP] done: {self.executed_qty}/{self.total_qty} "
              f"avg price: {avg_price:.2f}")

VWAP(成交量加權平均價格)

VWAP更智慧:它考慮了典型的成交量分佈。如果9:00到10:00通常交易了日成交量的30%,那麼VWAP會在這個時段執行30%的訂單。目標是讓平均成交價儘可能接近市場VWAP。

class VWAPExecutor:
    """
    VWAP执行器:按照历史成交量分布
    按比例分配订单量。
    """
    def __init__(self, exchange, symbol: str, side: str,
                 total_qty: float, volume_profile: list[float]):
        self.exchange = exchange
        self.symbol = symbol
        self.side = side
        self.total_qty = total_qty
        total_weight = sum(volume_profile)
        self.weights = [w / total_weight for w in volume_profile]

    async def execute(self, interval_seconds: float = 60.0):
        executed = 0.0
        for i, weight in enumerate(self.weights):
            qty = self.total_qty * weight
            remaining = self.total_qty - executed
            qty = min(qty, remaining)

            if qty <= 0:
                break

            order = await self.exchange.create_order(
                symbol=self.symbol,
                type="market",
                side=self.side,
                amount=qty,
            )
            executed += float(order["filled"])
            print(f"[VWAP] period {i+1}: weight={weight:.2%}, "
                  f"filled={order['filled']} @ {order['average']}")

            await asyncio.sleep(interval_seconds)

TWAP與VWAP的區別: TWAP更簡單、更可預測。VWAP能獲得更好的平均價格,但需要可靠的成交量分佈。在加密市場中,由於成交量可能存在刷量行為,VWAP分佈需要謹慎構建。


3. 追價限價單:當你的訂單會追著價格跑

追價限價單 追價限價單:訂單以可配置的激程序度追逐移動的價格

現在進入最有趣的部分。標準限價單是一個被動實體:它掛在訂單簿中等待。如果價格移走了——訂單保持未成交。對於演算法交易者來說,這通常是不可接受的:入場訊號已發出,但因為市場移動了0.1%而未能建倉。

追價限價單(Chasing limit order) 是對限價單的程式化封裝,它:

  1. 以當前最優價格(或略有偏移)掛出限價單
  2. 通過WebSocket監控價格
  3. 如果價格偏離訂單——取消並以更接近當前價格重新掛單
  4. 重複直到訂單成交或超過允許的偏差

關鍵參數

  • chase_interval_ms — 檢查和重新掛單的頻率。100ms——激進,1000ms——溫和。
  • max_chase_distance — 距初始價格的最大偏差,超過後取消訂單。防止追逐失控的市場。
  • aggression_level — 限價單距市場價多近。0——在最優買/賣價(被動),1——穿越價差(激進,實際上是吃單)。
  • chase_on_partial — 訂單部分成交後是否繼續追價。

TypeScript實現

interface ChasingOrderParams {
  symbol: string;
  side: "buy" | "sell";
  totalQty: number;
  /** 0 = passive (at best bid/ask), 1 = cross spread */
  aggression: number;
  /** max price deviation from initial price */
  maxChaseDistance: number;
  /** how often to re-evaluate, ms */
  chaseIntervalMs: number;
  /** stop chasing after this many ms */
  timeoutMs: number;
}

class ChasingLimitOrder {
  private currentOrderId: string | null = null;
  private filledQty = 0;
  private initialPrice: number | null = null;
  private startTime = Date.now();

  constructor(
    private exchange: any, // ccxt exchange instance
    private params: ChasingOrderParams
  ) {}

  async execute(): Promise<{ filledQty: number; avgPrice: number }> {
    const fills: Array<{ qty: number; price: number }> = [];

    while (this.filledQty < this.params.totalQty) {
      // 超时检查
      if (Date.now() - this.startTime > this.params.timeoutMs) {
        console.log("[CHASE] timeout reached, cancelling");
        await this.cancelCurrent();
        break;
      }

      // 获取当前订单簿
      const book = await this.exchange.fetchOrderBook(
        this.params.symbol, 5
      );
      const bestBid = book.bids[0][0];
      const bestAsk = book.asks[0][0];
      const spread = bestAsk - bestBid;

      // 计算目标价格
      let targetPrice: number;
      if (this.params.side === "buy") {
        targetPrice = bestBid + spread * this.params.aggression;
      } else {
        targetPrice = bestAsk - spread * this.params.aggression;
      }

      // 记录初始价格
      if (this.initialPrice === null) {
        this.initialPrice = targetPrice;
      }

      // 检查最大追价距离
      const deviation = Math.abs(targetPrice - this.initialPrice);
      if (deviation > this.params.maxChaseDistance) {
        console.log(
          `[CHASE] max deviation exceeded: ${deviation.toFixed(4)} > ` +
          `${this.params.maxChaseDistance}`
        );
        await this.cancelCurrent();
        break;
      }

      // 检查当前订单
      if (this.currentOrderId) {
        const order = await this.exchange.fetchOrder(
          this.currentOrderId, this.params.symbol
        );

        if (order.status === "closed") {
          fills.push({ qty: order.filled, price: order.average });
          this.filledQty += order.filled;
          this.currentOrderId = null;
          continue;
        }

        // 更新部分成交的filledQty
        if (order.filled > 0) {
          const newFilled = order.filled - (
            fills.reduce((s, f) => s + f.qty, 0) - this.filledQty
          );
          // 订单还在——需要重新挂单吗?
        }

        const currentPrice = parseFloat(order.price);
        const priceDiff = Math.abs(currentPrice - targetPrice);
        const tickSize = spread * 0.1 || 0.01;

        if (priceDiff > tickSize) {
          // 价格移动了——重新挂单
          console.log(
            `[CHASE] repricing: ${currentPrice} -> ` +
            `${targetPrice.toFixed(4)}`
          );
          await this.cancelCurrent();
        } else {
          // 订单在正确的价格上——等待
          await this.sleep(this.params.chaseIntervalMs);
          continue;
        }
      }

      // 挂出新订单
      const remainingQty = this.params.totalQty - this.filledQty;
      const order = await this.exchange.createLimitOrder(
        this.params.symbol,
        this.params.side,
        remainingQty,
        targetPrice
      );
      this.currentOrderId = order.id;
      console.log(
        `[CHASE] placed ${this.params.side} ${remainingQty} ` +
        `@ ${targetPrice.toFixed(4)}`
      );

      await this.sleep(this.params.chaseIntervalMs);
    }

    const totalCost = fills.reduce((s, f) => s + f.qty * f.price, 0);
    const avgPrice = this.filledQty > 0 ? totalCost / this.filledQty : 0;
    return { filledQty: this.filledQty, avgPrice };
  }

  private async cancelCurrent(): Promise<void> {
    if (this.currentOrderId) {
      try {
        await this.exchange.cancelOrder(
          this.currentOrderId, this.params.symbol
        );
      } catch { /* 订单已成交或已取消 */ }
      this.currentOrderId = null;
    }
  }

  private sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
}

追價的危害

追價是一個強大的工具,但很容易變成虧損製造機:

  1. 取消/重掛洪水。 每次取消和重新掛單都會給API帶來負載。交易所限制請求頻率,激進的追價可能導致API金鑰被封禁。
  2. 逆向選擇。 如果價格一直在遠離你——市場可能知道一些你不知道的事情。在這種情況下追價意味著在頂部買入。
  3. 從掛單方變為吃單方。 高激進度下你實際上在支付吃單手續費,只是有延遲(取消+新訂單)。有時直接下市價單更簡單。

4. 基於時間的訂單:毫秒級精度

有些情況下,你需要執行的不是"以價格X"的訂單,而是"在時間T"的訂單。聽起來奇怪?這實際上是一整類策略。

使用場景

資金費率套利。 在永續合約上,資金費率每8小時支付一次(Binance上為UTC時間00:00、08:00、16:00)。如果資金費率=+0.1%,你需要在結算時刻持有空頭。策略:在結算前幾秒開空,收取資金費率,然後平倉。時間把控至關重要——延遲一秒就意味著錯過資金費率。

交易時段開盤/收盤。 在傳統市場和一些加密衍生品上,有固定的交易時段。開盤競價(NYSECME)是流動性最大的時刻。在競價前100ms下單是一種優勢。

基於新聞的執行。 通脹資料在預定時間釋出。演算法從新聞源解析數字,在50ms內下單。這裡基於時間的執行與事件驅動邏輯相結合。

實現

class TimeBasedOrder {
  constructor(
    private exchange: any,
    private symbol: string,
    private side: "buy" | "sell",
    private qty: number,
    private orderType: "market" | "limit",
    private limitPrice?: number
  ) {}

  /**
   * 安排在精确时间执行。
   * 使用忙等待循环以获得最高精度。
   */
  async executeAt(targetTime: Date): Promise<any> {
    const targetMs = targetTime.getTime();

    // 阶段1:粗略等待(sleep)
    const coarseWait = targetMs - Date.now() - 500; // 提前500ms唤醒
    if (coarseWait > 0) {
      console.log(
        `[TIME-ORDER] sleeping for ${(coarseWait / 1000).toFixed(1)}s`
      );
      await new Promise((r) => setTimeout(r, coarseWait));
    }

    // 阶段2:精确等待(忙等待)
    while (Date.now() < targetMs) {
      // 自旋——消耗CPU,但获得约1ms的精度
    }

    // 阶段3:执行
    const sendTime = Date.now();
    const order = await this.exchange.createOrder(
      this.symbol,
      this.orderType,
      this.side,
      this.qty,
      this.limitPrice
    );

    console.log(
      `[TIME-ORDER] executed at ${new Date(sendTime).toISOString()}, ` +
      `target was ${targetTime.toISOString()}, ` +
      `delta: ${sendTime - targetMs}ms`
    );

    return order;
  }
}

// 示例:在UTC时间00:00:00精确下单(资金费率结算)
const executor = new TimeBasedOrder(exchange, "BTC/USDT", "sell", 0.1, "market");
const target = new Date("2026-03-24T00:00:00.000Z");
await executor.executeAt(target);

重要提示: 基於時間訂單的精度不受你程式碼的限制,而是受到交易所網路延遲的限制。如果到API的ping是50ms,即使完美的忙等待也會有50ms的偏差。對於嚴肅的高頻交易,會使用託管服務——伺服器物理上就在交易所撮合引擎旁邊。


5. 虛擬/合成訂單:系統中的隱形人

網格機器人的虛擬訂單 虛擬訂單:訂單僅存在於機器人記憶體中,直到觸發條件滿足

這可能是演算法交易者武器庫中最被低估的工具。虛擬訂單(也稱為合成訂單)是一種僅存在於你係統中的訂單。在觸發條件滿足之前(通常是價格達到某個水平),它不會被髮送到交易所。

工作原理

  1. 你的演算法決定:"我想以40,000美元買入BTC"
  2. 它不是向交易所傳送限價單,而是在記憶體中建立虛擬訂單
  3. 訂閱WebSocket價格流
  4. 當bid/ask達到40,000美元時——向交易所傳送真實的市價單或限價單

為什麼需要虛擬訂單

無資訊洩露。 你的訂單在訂單簿中不可見。沒有人——其他交易者、高頻交易演算法、甚至交易所本身——在執行時刻之前不知道你的意圖。這從根本上改變了力量平衡。

防止搶先交易。 在加密交易所,特別是透明度較低的交易所,有合理的懷疑認為大型限價單的資訊可能被用於搶先交易(甚至有相關研究)。虛擬訂單消除了這個風險。

網格機器人。 經典的網格機器人在不同價格水平掛出50-200個訂單。如果全部發送到交易所——那就是訂單簿中200個訂單,它們:(a) 對所有人可見,(b) 佔用交易所的訂單限額(通常每個賬戶200-300個未完成訂單),(c) 如果價格劇烈波動,全部成交,你會得到一個巨大的倉位。虛擬訂單解決了這三個問題。

接飛刀。 策略:在當前價格下方-5%、-10%、-15%的水平設定虛擬買入訂單。如果市場下跌——訂單逐步觸發。如果沒有下跌——你不承擔任何風險,也不佔用交易所的訂單限額。

TypeScript實現

interface VirtualOrder {
  id: string;
  symbol: string;
  side: "buy" | "sell";
  triggerPrice: number;
  qty: number;
  /** 触发时发送到交易所的订单类型 */
  executionType: "market" | "limit";
  /** 对于限价单:距触发价格的偏移量 */
  limitOffset?: number;
  status: "pending" | "triggered" | "filled" | "failed";
}

class VirtualOrderManager {
  private orders: Map<string, VirtualOrder> = new Map();
  private orderCounter = 0;

  constructor(private exchange: any) {}

  /**
   * 创建虚拟订单。不会向交易所发送任何内容。
   */
  addOrder(params: Omit<VirtualOrder, "id" | "status">): string {
    const id = `virt_${++this.orderCounter}`;
    this.orders.set(id, { ...params, id, status: "pending" });
    console.log(
      `[VIRTUAL] created ${params.side} ${params.qty} ` +
      `${params.symbol} @ trigger ${params.triggerPrice}`
    );
    return id;
  }

  /**
   * 在每个价格tick时调用(来自WebSocket)。
   */
  async onPriceUpdate(
    symbol: string, bestBid: number, bestAsk: number
  ): Promise<void> {
    for (const [id, order] of this.orders) {
      if (order.symbol !== symbol || order.status !== "pending") continue;

      const triggered =
        (order.side === "buy" && bestAsk <= order.triggerPrice) ||
        (order.side === "sell" && bestBid >= order.triggerPrice);

      if (!triggered) continue;

      order.status = "triggered";
      console.log(
        `[VIRTUAL] ${id} triggered! bid=${bestBid} ask=${bestAsk}`
      );

      try {
        let realOrder: any;

        if (order.executionType === "market") {
          realOrder = await this.exchange.createMarketOrder(
            order.symbol, order.side, order.qty
          );
        } else {
          const limitPrice = order.side === "buy"
            ? order.triggerPrice + (order.limitOffset ?? 0)
            : order.triggerPrice - (order.limitOffset ?? 0);
          realOrder = await this.exchange.createLimitOrder(
            order.symbol, order.side, order.qty, limitPrice
          );
        }

        order.status = "filled";
        console.log(
          `[VIRTUAL] ${id} filled: ${realOrder.filled} ` +
          `@ ${realOrder.average ?? realOrder.price}`
        );
      } catch (err) {
        order.status = "failed";
        console.error(`[VIRTUAL] ${id} execution failed:`, err);
      }
    }
  }

  /**
   * 获取所有活跃的虚拟订单。
   */
  getPendingOrders(): VirtualOrder[] {
    return [...this.orders.values()].filter(
      (o) => o.status === "pending"
    );
  }

  cancelOrder(id: string): boolean {
    const order = this.orders.get(id);
    if (order && order.status === "pending") {
      this.orders.delete(id);
      return true;
    }
    return false;
  }
}

// --- 示例:使用虚拟订单的网格机器人 ---

async function gridBot(exchange: any) {
  const manager = new VirtualOrderManager(exchange);
  const currentPrice = 42000;
  const gridStep = 200;      // 网格步长
  const gridLevels = 20;     // 每个方向的级数
  const qtyPerLevel = 0.01;  // 每级BTC数量

  // 创建虚拟网格
  for (let i = 1; i <= gridLevels; i++) {
    // 当前价格下方的买入订单
    manager.addOrder({
      symbol: "BTC/USDT",
      side: "buy",
      triggerPrice: currentPrice - gridStep * i,
      qty: qtyPerLevel,
      executionType: "limit",
      limitOffset: 1,  // limit price = trigger + 1 USDT
    });

    // 当前价格上方的卖出订单
    manager.addOrder({
      symbol: "BTC/USDT",
      side: "sell",
      triggerPrice: currentPrice + gridStep * i,
      qty: qtyPerLevel,
      executionType: "limit",
      limitOffset: 1,
    });
  }

  console.log(
    `[GRID] created ${gridLevels * 2} virtual orders, ` +
    `0 on exchange`
  );

  // WebSocket订阅(ccxt.pro伪代码)
  while (true) {
    const ticker = await exchange.watchTicker("BTC/USDT");
    await manager.onPriceUpdate(
      "BTC/USDT", ticker.bid, ticker.ask
    );
  }
}

虛擬訂單的陷阱

  1. 延遲間隙。 從你看到價格到真實訂單到達交易所之間存在時間差。在波動劇烈的市場上,價格可能在這20-100ms內飛走。解決方案:傳送略微激進的限價單(留有餘量)。

  2. 錯過成交。 如果價格在一個tick內"穿過"了你的水平(閃崩)並反彈——你可能來不及反應。掛在簿上的普通限價單會成交,虛擬訂單則不會。

  3. 狀態管理。 虛擬訂單存在於記憶體中。如果程序崩潰——訂單丟失。解決方案:持久化儲存(Redis、SQLite、檔案),重啟時恢復。


6. 條件/智慧訂單:訂單組合學

當單個訂單不夠時,交易者將它們組合成條件結構。有些在交易所原生支援,有些則通過程式設計實現。

OCO(二擇一訂單)

兩個訂單相互關聯:如果一個成交——另一個自動取消。經典示例:你持有多頭倉位,想同時設定止盈和止損。無論哪個先觸發——另一個必須被取消。

class OCOHandler:
    """
    OCO:一个订单成交时,另一个被取消。
    """
    def __init__(self, exchange, symbol: str):
        self.exchange = exchange
        self.symbol = symbol
        self.order_a_id: str | None = None
        self.order_b_id: str | None = None

    async def place(
        self,
        take_profit_price: float,
        stop_loss_price: float,
        qty: float,
    ):
        tp = await self.exchange.create_limit_sell_order(
            self.symbol, qty, take_profit_price
        )
        self.order_a_id = tp["id"]

        sl = await self.exchange.create_order(
            self.symbol, "stop", "sell", qty,
            None, {"stopPrice": stop_loss_price}
        )
        self.order_b_id = sl["id"]

        print(f"[OCO] TP @ {take_profit_price}, SL @ {stop_loss_price}")

    async def monitor(self):
        """检查状态并取消配对订单。"""
        while True:
            if self.order_a_id:
                a = await self.exchange.fetch_order(
                    self.order_a_id, self.symbol
                )
                if a["status"] == "closed":
                    print("[OCO] take-profit filled, cancelling stop-loss")
                    await self.exchange.cancel_order(
                        self.order_b_id, self.symbol
                    )
                    break

            if self.order_b_id:
                b = await self.exchange.fetch_order(
                    self.order_b_id, self.symbol
                )
                if b["status"] == "closed":
                    print("[OCO] stop-loss filled, cancelling take-profit")
                    await self.exchange.cancel_order(
                        self.order_a_id, self.symbol
                    )
                    break

            await asyncio.sleep(0.5)

括號訂單(Bracket order)

三部分結構:主入場訂單 + OCO出場(止盈+止損)。本質上是在一次呼叫中完成完整的交易週期:

  1. 入場: 限價買入訂單
  2. 止盈: 限價賣出訂單(更高價位)
  3. 止損: 止損市價賣出訂單(更低價位)

當入場訂單成交時,自動掛出止盈和止損。當其中任何一個成交時——另一個被取消。

If-Then邏輯

最靈活的方案——具有任意條件的訂單鏈:


rules = [
    {
        "condition": {"symbol": "BTC/USDT", "price_above": 50000},
        "action": {"type": "market_buy", "symbol": "ETH/USDT", "qty": 10},
        "then": [
            {
                "condition": {"symbol": "ETH/USDT", "price_above": 4000},
                "action": {"type": "market_sell", "symbol": "ETH/USDT", "qty": 10},
            },
            {
                "condition": {"symbol": "ETH/USDT", "price_below": 3500},
                "action": {"type": "market_sell", "symbol": "ETH/USDT", "qty": 10},
            },
        ]
    }
]

這種構造沒有任何交易所原生支援——只能通過程式設計實現。這也是為什麼演算法交易系統不可避免地會發展出自己的訂單管理層的原因之一。


7. 做市商如何使用特殊訂單型別

做市是一個獨立的世界,其訂單工具箱也相應匹配。做市商的任務是持續報價買賣價差,通過價差盈利,同時最小化逆向選擇(知情交易者對你進行反向交易的情況)。

Post-only是必需品

對於做市商來說,post-only不是可選項——而是必要條件。如果你的訂單意外地以吃單方成交——你不是獲得掛單方返佣,而是支付吃單手續費。在每天數千個訂單的規模下,這是災難性的。

async def quote(exchange, symbol, mid_price, half_spread, qty):
    bid_price = mid_price - half_spread
    ask_price = mid_price + half_spread

    bid = await exchange.create_order(
        symbol, "limit", "buy", qty, bid_price,
        {"postOnly": True}  # 对做市商至关重要
    )
    ask = await exchange.create_order(
        symbol, "limit", "sell", qty, ask_price,
        {"postOnly": True}
    )
    return bid, ask

隱藏訂單

在一些交易所(KrakenBitfinex),可以使用隱藏訂單——它們不顯示在訂單簿中,但在交易所上參與撮合。權衡:即使作為掛單方你也要支付吃單手續費,但你獲得了匿名性。

對於做市商來說,這是庫存管理的工具:如果積累了大量倉位,可以掛一個隱藏訂單來減倉,而不向市場暴露你的意圖。

掛鉤訂單(Pegged orders)

與最優買/賣價掛鉤的訂單。在Coinbase Advanced Trade上,例如,你可以掛一個自動追蹤最優買價、始終排在佇列最前面的訂單。這是交易所層面的原生追價訂單——但並非隨處可用。

批次訂單管理

專業做市商使用批次API在單個HTTP請求中同時取消和掛出數十個訂單。在Binance上這是batchOrders,在Bybit上是place-batch-order。這降低了延遲和頻率限制壓力。


8. 訂單型別對比表

訂單型別 成交保證 價格保證 在簿中可見 交易所原生支援 實現複雜度
市價單 否(即時)
限價單
止損市價單 是(觸發後)
止損限價單 否(觸發前)
追蹤止損 是(觸發後) 部分
冰山訂單 部分 部分
僅掛單
TWAP 否(取決於分片) 部分
VWAP 部分
追價限價單 高於限價單 部分 是(當前訂單)
基於時間 取決於型別 取決於型別 否(直到時間T)
虛擬/合成 低於限價單 取決於型別
OCO 是(二選一) 部分 是(兩個) 部分
括號訂單 部分 少見
隱藏訂單 少見
掛鉤訂單 動態 極少 高(如程式設計實現)

結論:訂單作為策略的構建模組

訂單型別不只是"介面上的按鈕"。它們是構建任何交易系統執行層的基本原語。"策略在回測中盈利"和"策略在生產中盈利"之間的差異往往就在這裡——在你如何向交易所傳送訂單。

幾條實踐建議:

  1. 從標準訂單開始,確保你理解細微差別(止損限價單 vs 止損市價單,IOC vs FOK)。大多數錯誤都出在這裡。
  2. 虛擬訂單是網格機器人的必需品。 如果你要掛超過50個訂單——不要全部發送到交易所。
  3. 當成交率比價格更重要時需要追價。 但一定要設定max_chase_distance——否則可能會偏離很遠。
  4. 基於時間的執行是小眾但強大的工具,適用於資金費率套利和事件驅動策略。
  5. 自定義訂單管理層對於任何嚴肅的演算法交易系統都是不可避免的。交易所原生訂單型別是不夠的。

如果你正在構建交易系統並想深入瞭解——請檢視我們關於訂單簿中的佇列位置CCXT WebSocket方法資金費率套利的文章。


參考文獻和來源

免責宣告:本文提供的資訊僅用於教育和參考目的,不構成財務、投資或交易建議。加密貨幣交易涉及重大損失風險。

Authors

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

緊跟市場步伐

訂閱我們的時事通訊,獲取獨家 AI 交易見解、市場分析和平台更新。

我們尊重您的隱私。您可以隨時退訂。