作為 FinTech 創業公司的 CTO,今天分享一次我們在交易網關架構上的重要重構。這次重構解決了一個核心痛點:回測環境與生產環境的數據一致性問題。
背景:數據異構帶來的災難
在早期的架構設計中,為了快速上線(MVP),我們的行情服務直接訂閲了上游供應商的 RESTful/WebSocket K 線接口。這在低頻交易中表現尚可,但當我們涉足日內高頻策略(Intraday High-Frequency)時,系統出現了一個致命 Bug:
實盤交易的 PnL(盈虧)曲線與回測曲線出現了嚴重背離。
經過排查,我們排除了策略邏輯層的 Bug,最終將問題鎖定在 Data Layer。我們發現這是一個經典的數據異構(Data Heterogeneity)問題。
回測環境: 依賴靜態的歷史 OHLC 文件,數據是經過清洗和插值的。
生產環境: 依賴實時的流式數據,受限於網絡包的到達順序和上游的切片邏輯。
簡單來説,上游 API 告訴我們的“收盤價”,和我們在那一瞬間看到的“最新價”,往往對不上。
技術歸因:API 的黑盒效應
當我們依賴外部 API 生成 K 線時,我們實際上是將“定價權”交給了別人。不同的數據提供商對於 K 線的生成算法存在細微差異:
Windowing(窗口化): 時間窗口是按 Event Time 還是 Processing Time?
Aggregation(聚合): 在高併發下,如果有亂序到達的 Tick,API 是丟棄還是重算?
這些差異在量化策略中被指數級放大。僅僅幾個 Pip 的偏差,就會導致 MACD 金叉提前或滯後,進而導致錯誤的開倉。
架構重構:基於 Tick 的流式計算
為了徹底解決這個問題,我們決定實施“數據源左移”策略。
Old Architecture: Provider (K-line) -> Strategy Engine
New Architecture: Provider (Raw Tick) -> Internal Aggregator (Tick to OHLC) -> Strategy Engine
我們需要的是最原始的原子數據。我們切換到了提供低延遲 Tick 數據服務的供應商(例如 AllTick API
),確保能拿到每一筆報價的 timestamp 和 price。
在內部,我們基於 Python (Pandas/NumPy) 實現了一套輕量級的流式聚合引擎。
Input: 實時 Tick 流。
Process: 維護一個時間桶(Time Bucket),按照嚴格的 Event Time 將 Tick 放入對應的桶中。
Output: 當時間窗口關閉(例如當前時間 > 10:01:00),立即觸發 K 線生成並推送到策略端。
同時,我們的回測系統也複用了這套聚合代碼。回測時,我們不再讀取 CSV 裏的 OHLC,而是讀取歷史 Tick 數據,通過完全相同的管道跑一遍。
Refactoring 後的收益
邏輯閉環: 回測代碼和實盤代碼在數據處理層面實現了 100% 複用。
透明可控: 我們可以自定義異常數據的清洗規則(例如過濾掉價格偏離均值 10% 的噪點)。
置信度提升: 策略回測結果的可信度大幅提升,實盤上線後的心理壓力驟減。
Show Me The Code
在技術選型上,其實並不複雜。關鍵在於對 Pandas Resample 機制的理解。下面是我們簡化版的數據處理邏輯,通過 requests 獲取 Tick 並自行 Resample:
import requests
import pandas as pd
url = "https://apis.alltick.co/v1/forex/tick"
params = {"symbol": "EURUSD", "limit": 1000}
resp = requests.get(url, params=params).json()
df = pd.DataFrame(resp['data'])
df['time'] = pd.to_datetime(df['time'], unit='ms')
kline = df.resample('1T', on='time').agg({'price':['first','max','min','last']})
print(kline.tail())
總結
在金融系統架構中,越靠近底層數據,越需要掌控力。不要為了省事而使用過度封裝的數據接口。Raw Data is King.