QuestDB 演算法交易實戰:從訂單簿到生產架構
免責聲明:本文內容僅供教育和參考目的,不構成任何財務、投資或交易建議。加密貨幣交易存在重大虧損風險。
歡迎來到 QuestDB 系列的最終篇。在第 1 篇中,我們介紹了儲存架構。在第 2 篇中,我們探討了 SQL 擴充。現在讓我們將一切融合在一起:用於即時分析的物化檢視、原生 2D 陣列的訂單簿儲存,以及生產級演算法交易平臺的參考架構。
物化檢視:以網路速度提供預計算分析
級聯物化檢視:原始 tick 資料流經逐級粗化的聚合層,每一層處理的資料量都大幅減少
如果 SAMPLE BY 是 QuestDB 最常用的查詢,那麼物化檢視就是其最有影響力的最佳化。概念很簡單:不是在每次儀表板重新整理或 API 呼叫時計算 OHLCV 聚合,而是預先計算一次,並保持結果持續更新。
基礎 OHLC 物化檢視
CREATE MATERIALIZED VIEW trades_OHLC_15m
WITH BASE 'trades'
REFRESH IMMEDIATE
AS
SELECT timestamp, symbol,
first(price) AS open,
max(price) AS high,
min(price) AS low,
last(price) AS close,
sum(quantity) AS volume
FROM trades
SAMPLE BY 15m;
這就是完整的定義。每當新行被插入 trades 表時,QuestDB 會自動對該檢視進行增量重新整理。不是完整重算——只有受影響的時間桶會被更新。針對 trades_OHLC_15m 的查詢變成了對更小的預聚合資料集的簡單查詢。
效能差異是戲劇性的。在一張有數十億行的表上,查詢基礎表獲取 OHLC 資料可能需要 200 毫秒。物化檢視在 5 毫秒以內返回相同結果。當多個儀表板使用者併發訪問時,這不僅僅是一種最佳化——而是響應系統與崩潰系統之間的差距。
級聯檢視:單一資料來源支援多個時間週期
物化檢視在架構上優雅之處在於此。你可以將它們連結起來——每個檢視以下一個為基礎,從單一原始資料來源建立多層次的聚合層級:
-- 从原始成交数据生成 1 秒 K 线
CREATE MATERIALIZED VIEW ohlc_1s AS
SELECT timestamp, symbol,
first(price) AS open, max(price) AS high,
min(price) AS low, last(price) AS close,
sum(quantity) AS volume
FROM trades
SAMPLE BY 1s;
-- 从 1 秒 K 线生成 5 秒 K 线
CREATE MATERIALIZED VIEW ohlc_5s AS
SELECT timestamp, symbol,
first(open) AS open, max(high) AS high,
min(low) AS low, last(close) AS close,
sum(volume) AS volume
FROM ohlc_1s
SAMPLE BY 5s;
-- 从 5 秒 K 线生成 1 分钟 K 线
CREATE MATERIALIZED VIEW ohlc_1m AS
SELECT timestamp, symbol,
first(open) AS open, max(high) AS high,
min(low) AS low, last(close) AS close,
sum(volume) AS volume
FROM ohlc_5s
SAMPLE BY 1m;
每一層處理的資料集都比前一層小得多。1 分鐘檢視不掃描原始成交資料——它只讀取預聚合的 5 秒 K 線。這種級聯模式可以擴充到任意數量的時間週期:1s → 5s → 1m → 5m → 15m → 1h → 4h → 1d。
對於從 100 多家交易所寫入資料的加密資料平臺,這是整個 OHLC 分發管道的骨幹。
重新整理策略
QuestDB 提供三種重新整理模式,各自適合不同的工作負載:
REFRESH IMMEDIATE 在每次基礎表事務後觸發非同步重新整理。最適合亞秒級延遲至關重要的即時儀表板。
REFRESH EVERY 1h(基於定時器)將更新批次合併到定期重新整理中。對於高吞吐量寫入場景更合適,因為在每個微批次後觸發重新整理會產生額外開銷。
REFRESH PERIOD (LENGTH 1d TIME ZONE 'Europe/London' DELAY 2h) 定義日曆對齊的週期。"延遲"考慮了遲到資料的情況——對於可能在交易時段結束後數小時才傳送修正資料的市場,這一點至關重要。
REFRESH MANUAL 提供完全控制權。檢視僅在你顯式執行 REFRESH 命令時才會更新——對於日終對賬工作流非常有用。
LATEST ON 加速模式
最強大的模式之一是將物化檢視與 LATEST ON 結合,用於即時投資組合快照。掃描 13 億行原始資料以獲取每個交易對的最新價格需要數秒時間。但有了每日預聚合檢視:
CREATE MATERIALIZED VIEW trades_latest_1d AS
SELECT timestamp, symbol, side,
last(price) AS price,
last(quantity) AS quantity,
last(timestamp) AS latest
FROM trades
SAMPLE BY 1d;
LATEST ON 查詢掃描大約 25,000 行預聚合資料,而非數十億行:
SELECT symbol, side, price, quantity, latest AS timestamp
FROM (
trades_latest_1d
LATEST ON timestamp PARTITION BY symbol, side
)
ORDER BY timestamp DESC;
從數秒降至毫秒級。這就是生產交易儀表板如何在海量資料集上實現即時響應的秘訣。
TTL:自動資料生命週期管理
物化檢視支援 TTL(存活時間)策略用於自動資料過期:
CREATE MATERIALIZED VIEW ohlc_1h AS (
SELECT timestamp, symbol,
avg(price) AS avg_price
FROM trades
SAMPLE BY 1h
) PARTITION BY WEEK TTL 8 WEEKS;
這保留 8 周的小時資料,自動刪除較舊的分割槽。結合三層儲存引擎,你獲得了自然的資料生命週期:原始 tick 資料流經 WAL → 列式儲存 → 物件儲存中的 Parquet,而物化檢視維護著應用實際查詢的預聚合摘要。
2D 陣列:原生訂單簿分析
3D 訂單簿深度:買賣盤以原生 2D 陣列儲存,支援 SIMD 最佳化的價差計算和流動性分析
QuestDB 9.0 引入了 N 維陣列——真正的具有形狀和步長的類 NumPy 陣列,以零複製方式處理常見操作(切片、轉置)。對於交易而言,殺手級應用是訂單簿儲存。
傳統方案的痛點
歷史上,在關係型資料庫中儲存訂單簿快照是件苦差事。你只有兩種選擇:每個價格檔位一行(行數爆炸,查詢深度代價高昂),或者固定數量的列,如 bid1_price、bid1_size、bid2_price、bid2_size 等(僵硬、浪費且難看)。
QuestDB 的 2D 陣列徹底消除了這兩個問題:
CREATE TABLE market_data (
timestamp TIMESTAMP,
symbol SYMBOL,
bids DOUBLE[][],
asks DOUBLE[][]
) TIMESTAMP(timestamp) PARTITION BY HOUR;
每個 bids 和 asks 列儲存一個 2D 陣列,其中第一行包含每個檔位的價格,第二行包含每個檔位的數量。一個 20 檔訂單簿是一個緊湊的單一陣列,而非 40 個獨立的列。
SQL 中的訂單簿分析
價差計算——最基礎也是最頻繁計算的指標:
SELECT timestamp,
spread(bids[1][1], asks[1][1]) AS spread
FROM market_data
WHERE symbol = 'EURUSD'
AND timestamp IN today();
spread() 函數是內建函數,計算最優買價與最優賣價之差。bids[1][1] 訪問買盤陣列第一行(價格)的第一個元素(最優價格)。
對於更復雜的分析——流動性深度、訂單簿不平衡、特定價格檔位的成交機率——陣列切片和向量化操作使原本複雜的查詢變得直接:
-- 找到目标价格会被成交的档位
-- 并对该档位以上的所有数量求和
DECLARE @target := bids[1][1] * 1.01;
SELECT timestamp,
array_sum(asks[2][1:level_idx]) AS volume_to_fill
FROM market_data
WHERE symbol = 'EURUSD';
SIMD 最佳化的陣列操作意味著這些計算以接近硬體的速度執行,即使面對數百萬個快照也是如此。
陣列資料的寫入
QuestDB 的客戶端庫支援原生陣列寫入。Python 客戶端直接與 NumPy 陣列整合:
import numpy as np
from questdb.ingress import Sender
bids = np.array([[9.3, 9.2, 9.1], [100, 200, 150]]) # 价格, 数量
asks = np.array([[9.5, 9.6, 9.7], [80, 160, 120]])
with Sender.from_conf("http::addr=localhost:9000;") as sender:
sender.row(
'market_data',
symbols={'symbol': 'EURUSD'},
columns={'bids': bids, 'asks': asks},
at=timestamp
)
協議第 2 版以二進位制形式對陣列進行編碼,與基於文本的協議相比顯著降低了頻寬和伺服器端解析開銷。對於高頻訂單簿寫入——你可能每秒每個交易對接收數千個快照——這種效率至關重要。
C/C++ 客戶端使用帶形狀描述符的平鋪行主陣列,支援從現有交易系統資料結構進行零複製寫入。
融會貫通:參考架構
參考架構:交易所聯結器、列式資料庫核心、分析層、策略引擎和監控儀表板——全部互聯互通
讓我們為加密市場設計一個完整的基於 QuestDB 的演算法交易平臺。該架構處理來自多家交易所的資料寫入、即時分析、回測和策略執行。
資料寫入層
資料寫入:多個交易所聯結器通過 WebSocket 管道將即時行情資料經 ILP 協議寫入 QuestDB
通過 HTTP 上的 ILP,連線到各交易所(Binance、Bybit、OKX 等)的多個 WebSocket 連線將原始行情資料寫入 QuestDB。每個交易所聯結器是一個獨立程序,提供隔離性和容錯性。
資料流包括:成交資料(timestamp, symbol, side, price, quantity)、訂單簿快照(timestamp, symbol, bids[][], asks[][]),以及作為輔助流的資金費率/強平資料。
寫入吞吐量目標:所有交易所合計每秒數百萬行。QuestDB 的 WAL 能夠輕鬆應對,去重功能捕獲來自冗餘交易所連線的不可避免的重複資料。
即時分析層
物化檢視構成分析層的核心:
原始成交 → ohlc_1s → ohlc_5s → ohlc_1m → ohlc_5m → ohlc_15m → ohlc_1h → ohlc_1d
每一層增量重新整理。通過 QuestDB 原生外掛連線的 Grafana 儀表板查詢這些檢視繪製 K 線圖,無論歷史資料量多大,響應時間都在 5 毫秒以內。
額外的物化檢視計算:每日每交易對的 VWAP(成交量加權平均價格)、滾動波動率估算,以及跨交易所價差監控。
針對預聚合檢視的 LATEST ON 查詢支援即時投資組合儀表板——顯示當前持倉、未實現盈虧和各交易所敞口。
策略引擎
策略引擎:即時指標計算驅動演算法決策,買賣執行路徑通過物化檢視最佳化
交易策略查詢 QuestDB 以獲取當前市場狀態和歷史模式。QuestDB 的 PG 協議意味著任何具有 PostgreSQL 驅動的語言都可以連線:Python 用於研究策略,Rust 或 C++ 用於延遲敏感的執行。
策略的關鍵查詢模式:ASOF JOIN 用於將執行成交與成交時的市場狀況匹配、WINDOW JOIN 用於計算每個事件周圍的短週期指標,以及用於即時指標計算的視窗函數(RSI、布林帶、ATR)。
對於延遲敏感的策略,預計算物化檢視最大程度地減少了查詢時間。監控 50 個交易對的網格機器人不需要在每個 tick 時計算 50 個獨立的移動平均線——它從物化檢視中讀取。
回測管道
歷史資料以 Parquet 格式儲存在物件儲存中。QuestDB 可以透明地查詢它,但對於繁重的回測工作負載,資料也可以由 Polars、Pandas 或 DuckDB 直接讀取——完全繞過資料庫。
這種雙訪問模式非常強大:即時策略使用 QuestDB 的 SQL 介面進行即時決策,而回測框架通過 Parquet/Arrow 讀取相同資料進行批處理。相同的資料,兩條最佳化的訪問路徑。
監控與交易後分析
HORIZON JOIN 為交易後分析管道提供支援:
- 滑點分析:將執行價格與成交時的中間價進行比較
- 標記曲線:追蹤每筆成交後 1 秒、5 秒、30 秒、60 秒的價格演變
- 實施差異:將執行成本分解為價差成本、臨時衝擊和永久衝擊
- 交易場所評分:比較各交易所的成交質量以最佳化訂單路由
這些分析作為計劃查詢執行,將結果寫入專用表,為監控儀表板提供資料。告警規則在異常情況下觸發——突然的滑點峰值、異常的標記模式,或特定交易場所成交質量下降。
效能注意事項
生產環境效能調優:延遲、吞吐量和記憶體監控,以及 hot-warm-cold 資料生命週期管理
來自生產部署的一些實踐說明:
分割槽大小:對於每天每個交易對有數百萬行的加密 tick 資料,PARTITION BY HOUR 通常是最優選擇。這使單個分割槽在儲存和查詢效能方面都保持可管理。
物化檢視級聯:不要建立太多中間層次。每一層都會增加重新整理延遲。對於大多數用例,3-4 層(1s → 1m → 15m → 1d)在查詢效能和資料新鮮度之間提供了良好平衡。
去重開銷:對具有冗餘資料來源的表啟用去重。對於時間戳唯一的資料成本很小,但當許多相同時間戳的行需要基於列級別去重時,開銷會增加。
記憶體分配:QuestDB 的零 GC 引擎效率很高,但要為熱分割槽和寫入快取分配足夠的記憶體。通過內建指標端點進行監控。
客戶端協議選擇:使用 HTTP 上的 ILP 進行寫入(具有自動重試和健康檢查)。使用 PG 協議進行查詢。ILP 協議第 2 版(二進位制編碼)對於陣列資料和高吞吐量雙精度浮點值顯著更高效。
QuestDB 與競爭對手對比
競爭格局:QuestDB 與 TimescaleDB、ClickHouse、InfluxDB 和 kdb+ 在關鍵能力維度的對比
簡要對比交易領域常用的資料庫:
對比 TimescaleDB:TimescaleDB 是帶時序擴充的 PostgreSQL。它繼承了 PG 的通用性,但也繼承了其開銷。QuestDB 的原生列式引擎和 SIMD 執行在時序工作負載上提供了顯著更好的查詢效能,而 ASOF JOIN 等功能在 TimescaleDB 中沒有直接對應項。
對比 ClickHouse:ClickHouse 擅長對海量資料集進行分析查詢。但它並非專為時序設計——沒有原生 ASOF JOIN,沒有帶 FILL 的 SAMPLE BY,沒有用於訂單簿的 2D 陣列。對於混合 OLAP + 時序工作負載,ClickHouse 可能勝出;對於純交易資料,QuestDB 使用體驗更佳。
對比 InfluxDB:InfluxDB 存在高基數限制,對於多交易所加密資料非常棘手。其查詢語言(Flux,現已廢棄;InfluxQL)缺乏 QuestDB SQL 擴充的表達能力。大型歷史查詢的效能通常更差。
對比 kdb+/q:高頻交易的黃金標準。kdb+ 在某些單執行緒向量運算上更快,其 q 語言極為簡潔。但它是專有的、昂貴的,且學習曲線陡峭。QuestDB 以極低的成本提供了 80-90% 的能力,具備標準 SQL 和開源許可。
結論:真正懂交易的資料庫
在這三篇文章中,我們介紹了 QuestDB 的架構(三層儲存:WAL、列式儲存和 Parquet)、其 SQL 擴充(SAMPLE BY、ASOF JOIN、HORIZON JOIN、WINDOW JOIN、LATEST ON、TWAP),以及實際應用(物化檢視、訂單簿陣列、參考架構)。
貫穿始終的主線是一致的:QuestDB 正是為演算法交易所產生的工作負載而設計的。它不會強迫你繞開資料庫——相反,其原語直接對映到交易概念。OHLC 聚合是一行程式碼。成交-報價對齊是一個 JOIN。交易後分析是一個 HORIZON JOIN,而非多頁 PL/SQL 過程。
對於構建交易基礎設施的團隊——無論是加密行情資料平臺、量化研究環境,還是完整的演算法交易引擎——QuestDB 值得認真評估。開源版本涵蓋大多數用例,企業版填補了受監管環境的空白。
金融資料基礎設施格局正在快速演變。能夠讀懂市場語言的資料庫將會勝出。QuestDB 已經流利掌握了這門語言。
祝交易順利,願你的延遲永遠低。
引用
@software{soloviov2025questdb_algotrading_p3,
author = {Soloviov, Eugen},
title = {QuestDB for Algorithmic Trading: From Order Books to Production Architecture},
year = {2025},
url = {https://marketmaker.cc/en/blog/post/questdb-algotrading-production},
version = {0.1.0},
description = {Materialized views, 2D array order book analytics, and reference architecture for a QuestDB-powered algorithmic trading platform.}
}
Authors
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.