> ## Documentation Index
> Fetch the complete documentation index at: https://docs.intention.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# 撮合

> 訂單簿、價格優先與時間優先如何表示，以及訂單有效期與自成交防護如何在其上生效。

撮合是[內核執行](/zh-Hant/protocol/architecture/kernel)內部的一個階段，不是鏈去呼叫的一項服務。它按已提交的次序取出本區塊的訂單，逐筆對著訂單簿走一遍，產出成交。撮合不移動任何人的餘額：那是[清算所](/zh-Hant/protocol/architecture/clearinghouse)的事，發生在撮合完成之後。

把這兩件事分開，正是引擎可測試的原因。撮合回答的是**什麼與什麼成交了**；清算所回答的是**這要花多少錢、現在誰欠誰**。

<h2 id="the-book">
  訂單簿
</h2>

每個商品有自己的訂單簿，在記憶體中由三個相互配合的結構承載：

<div className="dg" data-dg="matching-book">
  <div className="dg-c" style={{aspectRatio:"720 / 302"}}>
    <svg className="dg-w" viewBox="0 0 720 302" aria-hidden="true">
      <path className="dg-wire dg-soft" d="M 348.60 82.00 L 352.60 82.00" />

      <path className="dg-wire dg-soft" d="M 438.20 82.00 L 442.20 82.00" />

      <path className="dg-wire dg-soft" d="M 527.80 82.00 L 531.80 82.00" />

      <path className="dg-wire dg-soft" d="M 617.40 82.00 L 621.40 82.00" />

      <path className="dg-wire dg--blue" d="M 205.00 68.00 L 238.60 68.00" />

      <path className="dg-head dg--blue" d="M 245.00 68.00 L 238.60 72.40 L 238.60 63.60 Z" />

      <path className="dg-wire dg--sky" d="M 205.00 162.00 L 238.60 162.00" />

      <path className="dg-head dg--sky" d="M 245.00 162.00 L 238.60 166.40 L 238.60 157.60 Z" />
    </svg>

    <div className="dg-band" style={{left:"34.7222%",top:"9.9338%",width:"65.2778%",height:"56.2914%"}}><span className="dg-cap">Slab arena — 一塊預先配置好的訂單槽位區</span></div>
    <div className="dg-b dg--blue" style={{left:"0.0000%",top:"9.9338%",width:"27.7778%",height:"25.1656%"}}><span className="dg-t">價位表</span><span className="dg-s">有序映射，價格 → 價位</span><span className="dg-n">盤口只需走到端點就能找到，不必掃描</span></div>
    <div className="dg-b dg--sky" style={{left:"0.0000%",top:"41.0596%",width:"27.7778%",height:"25.1656%"}}><span className="dg-t">訂單索引</span><span className="dg-s">訂單 ID → 槽位</span><span className="dg-n">撤單與改單是常數時間</span></div>
    <div className="dg-b dg--yellow" style={{left:"36.9444%",top:"19.5364%",width:"11.0556%",height:"15.2318%"}}><span className="dg-t">訂單</span></div>
    <div className="dg-b dg--yellow" style={{left:"49.3889%",top:"19.5364%",width:"11.0556%",height:"15.2318%"}}><span className="dg-t">訂單</span></div>
    <div className="dg-b dg--yellow" style={{left:"61.8333%",top:"19.5364%",width:"11.0556%",height:"15.2318%"}}><span className="dg-t">訂單</span></div>
    <div className="dg-b dg--yellow" style={{left:"74.2778%",top:"19.5364%",width:"11.0556%",height:"15.2318%"}}><span className="dg-t">訂單</span></div>
    <div className="dg-b dg--yellow" style={{left:"86.7222%",top:"19.5364%",width:"11.0556%",height:"15.2318%"}}><span className="dg-t">訂單</span></div>
    <div className="dg-b dg-plain dg-left" style={{left:"36.9444%",top:"41.3907%",width:"60.8333%",height:"15.2318%"}}><span className="dg-s">一條按到達次序串起訂單的雙向鏈結串列，因此同一價位內的優先級由位置決定，不是算出來的。</span></div>
    <div className="dg-b dg-dashed dg-left" style={{left:"0.0000%",top:"74.8344%",width:"100.0000%",height:"20.5298%"}}><span className="dg-t">重複使用的次序為什麼固定</span><span className="dg-s">釋放掉的槽位按固定次序重複使用，索引也用固定種子。這不是效能選擇：兩個驗證者以不同次序重複使用槽位，就是一次分叉。</span></div>
    <div className="dg-lbl" style={{left:"31.2500%",top:"17.8808%",width:"15.2778%",whiteSpace:"normal"}}>各價位的第一筆</div>
    <div className="dg-lbl" style={{left:"31.2500%",top:"62.9139%",width:"15.2778%",whiteSpace:"normal"}}>直接查詢</div>
  </div>
</div>

| 結構       | 作用                                         |
| -------- | ------------------------------------------ |
| **價位表**  | 從價格到價位的有序映射，因此最佳買價和最佳賣價只需走到端點就能找到，不必掃描     |
| **訂單鏈**  | 一條按到達次序串起訂單的雙向鏈結串列，因此同一價位內的優先級由位置決定，不是算出來的 |
| **訂單索引** | 從訂單 ID 直接映射到槽位，因此撤單和改單是常數時間，不是一次搜尋         |

訂單存放在一個 **slab arena** 裡，那是一塊預先配置好的區域，配置與釋放都是常數時間。因此訂單簿操作在熱路徑上不做記憶體配置，釋放掉的槽位也按固定次序重複使用，不是配置器碰巧放到哪裡就是哪裡。最後這一點不是效能選擇：如果兩個驗證者以不同次序重複使用槽位，任何能觀察到槽位佈局的東西都會發散。

同樣的紀律也適用於訂單索引：它用的是固定種子，不是隨機種子。按行程隨機播種的雜湊表，是抵禦碰撞攻擊的標準做法；但放在共識執行路徑上，它就是一次分叉。

價格全程都是整數：單位是 subtick，不是小數。這和你提交的數值怎麼對應，參見[精度](/zh-Hant/trading/precision)。

<h2 id="priority">
  優先級
</h2>

優先級先看價格，再看訂單在該價位鏈結串列裡的位置。「時間」指的是這筆訂單在已提交區塊序列中的規範位置，不是它到達某個節點的時刻。

這消除了區塊內的延遲競速。同一區塊中的兩筆訂單有一個確定的先後，每個驗證者算出來的結果都一樣，離某個節點再近也改變不了。在區塊之間，到達時間仍然重要，但競爭的單位是區塊，不是微秒。

內核的階段次序進一步強化了這一點：在一個區塊內，撤單先於主動吃單執行，因此撤單和某筆訂單落在同一個區塊時，掛著的報價不會被那筆訂單吃掉。

<h2 id="matching-an-order">
  撮合一筆訂單
</h2>

<div className="dg" data-dg="matching-walk">
  <div className="dg-c" style={{aspectRatio:"720 / 334"}}>
    <svg className="dg-w" viewBox="0 0 720 334" aria-hidden="true">
      <path className="dg-wire dg--blue" d="M 134.00 144.00 L 155.60 144.00" />

      <path className="dg-head dg--blue" d="M 162.00 144.00 L 155.60 148.40 L 155.60 139.60 Z" />

      <path className="dg-wire dg--sky" d="M 320.00 144.00 L 345.60 144.00" />

      <path className="dg-head dg--sky" d="M 352.00 144.00 L 345.60 148.40 L 345.60 139.60 Z" />

      <path className="dg-wire dg--green" d="M 510.00 144.00 L 535.60 144.00" />

      <path className="dg-head dg--green" d="M 542.00 144.00 L 535.60 148.40 L 535.60 139.60 Z" />

      <path className="dg-wire dg--sky" d="M 633.00 114.00 L 633.00 80.00 L 241.00 80.00 L 241.00 103.60" />

      <path className="dg-head dg--sky" d="M 241.00 110.00 L 236.60 103.60 L 245.40 103.60 Z" />

      <path className="dg-wire dg--green" d="M 633.00 174.00 L 633.00 213.60" />

      <path className="dg-head dg--green" d="M 633.00 220.00 L 628.60 213.60 L 637.40 213.60 Z" />

      <path className="dg-wire dg--sky" d="M 241.00 178.00 L 241.00 213.60" />

      <path className="dg-head dg--sky" d="M 241.00 220.00 L 236.60 213.60 L 245.40 213.60 Z" />
    </svg>

    <div className="dg-b dg--blue" style={{left:"0.0000%",top:"35.3293%",width:"18.0556%",height:"15.5689%"}}><span className="dg-t">新到訂單</span></div>
    <div className="dg-b dg--yellow dg-round" style={{left:"23.0556%",top:"34.1317%",width:"20.8333%",height:"17.9641%"}}><span className="dg-t">與訂單簿交叉？</span></div>
    <div className="dg-b dg--sky" style={{left:"49.4444%",top:"35.3293%",width:"20.8333%",height:"15.5689%"}}><span className="dg-t">吃掉對手方最佳價位</span></div>
    <div className="dg-b dg--green" style={{left:"75.8333%",top:"35.3293%",width:"24.1667%",height:"15.5689%"}}><span className="dg-t">產出成交</span></div>
    <div className="dg-b dg--green" style={{left:"75.8333%",top:"67.0659%",width:"24.1667%",height:"25.7485%"}}><span className="dg-t">結束</span></div>
    <div className="dg-b dg--sky dg-left" style={{left:"10.5556%",top:"67.0659%",width:"45.8333%",height:"25.7485%"}}><span className="dg-t">剩餘部分按訂單有效期掛單或拒絕</span><span className="dg-s">GTC 掛單 · IOC 撤銷 · FOK 無法全部成交就一點也不執行 · 只掛單會交叉時直接拒絕</span></div>
    <div className="dg-lbl" style={{left:"60.6944%",top:"23.9521%",width:"27.7778%",whiteSpace:"normal"}}>還有剩餘 — 吃下一檔</div>
    <div className="dg-lbl" style={{left:"87.9167%",top:"58.9820%"}}>沒有剩餘</div>
  </div>
</div>

撮合器反覆吃掉對手方最前面的掛單，每吃掉一個掛單方就產出一筆成交，直到新到訂單用盡，或訂單簿不再交叉。剩餘部分怎麼處理，由訂單有效期決定：

* **GTC** — 剩餘部分掛到訂單簿上。
* **IOC** — 撤銷剩餘部分。
* **FOK** — 如果訂單無法全部成交，就一點也不執行。
* **ALO** — 只掛單：這筆訂單如果會吃掉流動性，就直接拒絕，不讓它交叉成交。

成交在產生的同時就帶上歸屬資訊。每一筆成交都記錄了自己在本商品成交序列中的位置，組裝輸出時，各商品的這些位置會歸併成整個區塊內的單一順序。正因如此，日後才能把一個事件追溯回引起它的那筆確切交易，以及區塊中的那個確切位置。

<h2 id="self-trade-prevention">
  自成交防護
</h2>

新到的訂單如果會和同一個所有者掛著的流動性成交，這次撮合就會被抑制，不會執行。由哪一方讓路是可配置的：

| 模式                      | 行為              |
| ----------------------- | --------------- |
| **Expire taker**（撤銷吃單方） | 新到的訂單被撤銷        |
| **Expire maker**（撤銷掛單方） | 掛著的訂單被撤銷，新到訂單繼續 |
| **Expire both**（雙方撤銷）   | 兩筆都被撤銷          |

這樣撤銷掉的掛單，會在撮合過程中收集起來，在同一個區塊內移除，因此訂單簿不會留著一筆已經抑制掉的訂單。

這項檢查中的「所有者」，按訂單簿所追蹤的帳戶層級判定。交易側的說明參見[自成交防護](/zh-Hant/trading/self-trade-prevention)。

<h2 id="what-matching-does-not-do">
  撮合不做什麼
</h2>

撮合不計算手續費、不實現損益、不調整部位、也不檢查保證金。這些都發生在撮合之後的[清算所](/zh-Hant/protocol/architecture/clearinghouse)裡，由撮合產出的成交驅動。

撮合也不判斷一筆訂單准不准存在。保證金夠不夠、掛單數量上限、只減倉約束，以及市價轉限價，都在訂單到達訂單簿之前就處理完了。等撮合器看到一筆訂單，唯一的問題只剩下它該放在訂單簿的什麼位置。

<h2 id="where-to-go-next">
  後續閱讀
</h2>

<CardGroup cols={2}>
  <Card title="清算所" href="/zh-Hant/protocol/architecture/clearinghouse">
    成交出現之後，餘額和部位會怎樣。
  </Card>

  <Card title="訂單類型" href="/zh-Hant/trading/order-types">
    交易側的視角：你可以提交什麼，每一種如何表現。
  </Card>

  <Card title="訂單簿" href="/zh-Hant/trading/order-book">
    深度、價位，以及交易者該怎麼讀盤。
  </Card>

  <Card title="IntentionKernel" href="/zh-Hant/protocol/architecture/kernel">
    撮合在區塊執行中處於什麼位置。
  </Card>
</CardGroup>
