訂單簿
每個商品有自己的訂單簿,在記憶體中由三個相互配合的結構承載:Slab arena — 一塊預先配置好的訂單槽位區
價位表有序映射,價格 → 價位盤口只需走到端點就能找到,不必掃描
訂單索引訂單 ID → 槽位撤單與改單是常數時間
訂單
訂單
訂單
訂單
訂單
一條按到達次序串起訂單的雙向鏈結串列,因此同一價位內的優先級由位置決定,不是算出來的。
重複使用的次序為什麼固定釋放掉的槽位按固定次序重複使用,索引也用固定種子。這不是效能選擇:兩個驗證者以不同次序重複使用槽位,就是一次分叉。
各價位的第一筆
直接查詢
訂單存放在一個 slab arena 裡,那是一塊預先配置好的區域,配置與釋放都是常數時間。因此訂單簿操作在熱路徑上不做記憶體配置,釋放掉的槽位也按固定次序重複使用,不是配置器碰巧放到哪裡就是哪裡。最後這一點不是效能選擇:如果兩個驗證者以不同次序重複使用槽位,任何能觀察到槽位佈局的東西都會發散。
同樣的紀律也適用於訂單索引:它用的是固定種子,不是隨機種子。按行程隨機播種的雜湊表,是抵禦碰撞攻擊的標準做法;但放在共識執行路徑上,它就是一次分叉。
價格全程都是整數:單位是 subtick,不是小數。這和你提交的數值怎麼對應,參見精度。
優先級
優先級先看價格,再看訂單在該價位鏈結串列裡的位置。「時間」指的是這筆訂單在已提交區塊序列中的規範位置,不是它到達某個節點的時刻。 這消除了區塊內的延遲競速。同一區塊中的兩筆訂單有一個確定的先後,每個驗證者算出來的結果都一樣,離某個節點再近也改變不了。在區塊之間,到達時間仍然重要,但競爭的單位是區塊,不是微秒。 內核的階段次序進一步強化了這一點:在一個區塊內,撤單先於主動吃單執行,因此撤單和某筆訂單落在同一個區塊時,掛著的報價不會被那筆訂單吃掉。撮合一筆訂單
新到訂單
與訂單簿交叉?
吃掉對手方最佳價位
產出成交
結束
剩餘部分按訂單有效期掛單或拒絕GTC 掛單 · IOC 撤銷 · FOK 無法全部成交就一點也不執行 · 只掛單會交叉時直接拒絕
還有剩餘 — 吃下一檔
沒有剩餘
- GTC — 剩餘部分掛到訂單簿上。
- IOC — 撤銷剩餘部分。
- FOK — 如果訂單無法全部成交,就一點也不執行。
- ALO — 只掛單:這筆訂單如果會吃掉流動性,就直接拒絕,不讓它交叉成交。
自成交防護
新到的訂單如果會和同一個所有者掛著的流動性成交,這次撮合就會被抑制,不會執行。由哪一方讓路是可配置的:
這樣撤銷掉的掛單,會在撮合過程中收集起來,在同一個區塊內移除,因此訂單簿不會留著一筆已經抑制掉的訂單。
這項檢查中的「所有者」,按訂單簿所追蹤的帳戶層級判定。交易側的說明參見自成交防護。
撮合不做什麼
撮合不計算手續費、不實現損益、不調整部位、也不檢查保證金。這些都發生在撮合之後的清算所裡,由撮合產出的成交驅動。 撮合也不判斷一筆訂單准不准存在。保證金夠不夠、掛單數量上限、只減倉約束,以及市價轉限價,都在訂單到達訂單簿之前就處理完了。等撮合器看到一筆訂單,唯一的問題只剩下它該放在訂單簿的什麼位置。後續閱讀
清算所
成交出現之後,餘額和部位會怎樣。
訂單類型
交易側的視角:你可以提交什麼,每一種如何表現。
訂單簿
深度、價位,以及交易者該怎麼讀盤。
IntentionKernel
撮合在區塊執行中處於什麼位置。