FX 專用 VPS / OptiMax 活用案例
回測的解析度,取決於資料的解析度。在日線、小時線上會被抹平的價格波動結構(點差內部的動態、成交的偏向、同時急變),只保留在 tick(逐筆成交)資料 中。但 tick 的數量級完全不同:FX 6 個貨幣對、完整 2 年就有 超過 4 億筆記錄。要做到「毫無遺漏地蒐集」「載入記憶體」「窮舉式最佳化」,一般的小型 VPS 力有未逮。本文將以本公司的 FX 專用 VPS OptiMax 為例,說明從零建構完整 2 年的 tick 資料基礎,再到 以 64 核心對 2,430 種策略參數組合進行窮舉最佳化 的全過程,並在每個難關附上實測數值。
測試環境:OptiMax VPS(64 vCPU / 251GB RAM / Ubuntu 24.04)
測量期間:2023–2024(完整 2 年)/ 6 個貨幣對。本文數值皆為實測值。
範圍:僅涉及資料基礎建構、規避速率限制、平行最佳化等 通用工程技術,不包含本公司的獨家訊號研究內容。
為什麼要「tick 資料 × 高規格 VPS」 #
tick 是「每次成交」的最小單位記錄。點差內部究竟發生了什麼、急變時成交偏向哪一側——這類微觀結構,一旦聚合成 K 線就消失了。正因如此,驗證短線策略需要 tick。然而 tick 的資料量級別不同,蒐集、保存、計算的每一環都在考驗機器的真實底力。本文會坦誠分享 tick 資料蒐集中人人都會踩的坑,直到 「以為蒐集到了、實則缺漏 8 成」的失敗及其修正。
做了什麼(成果) #
| 指標 | 數值 |
|---|---|
| 期間 × 貨幣對 | 完整 2 年(2023–2024)× 6 對 |
| 總 tick 數 | 405,616,079(約 4.06 億) |
| 涵蓋率 | ≈100%(交易時段零遺漏。每對 15,048 小時中 data≈12,473 + 休market(404)≈2,574,未取得 ≤6) |
| 換算為 1 秒線 | 1.41 億根 |
| 最佳化試驗數 | 2,430 種(訊號 × 持倉時間 × 閾值 × 貨幣對,各自附 bootstrap CI) |
全流程耗時(實測) #
| 流程 | 耗時 | 備註 |
|---|---|---|
| ① 完整 tick 取得(2 個乾淨 IP 分擔 · 缺口填補) | 約 3 小時 | 每台取得機 3 對耗時 10,856 秒,2 台並跑 |
| ② 傳輸至計算機(OptiMax)(約 1GB) | 51 秒 | 約 13.6 MB/s |
| ③ 分析用格式轉換 | 116 秒 | npz → parquet |
| ④ 64 核窮舉最佳化 2,430 種 | 581 秒 | 建構網格 111s + 掃描 470s,約佔用 60 核 |
從零到「可分析的完整 2 年 tick 基礎 + 最佳化結果」,實際只需 3 小時多一點。
STEP 1:資料取得 ― 免費 feed 與「兩個陷阱」 #
歷史 tick 可從 Dukascopy 的免費資料源取得(.bi5 = LZMA 壓縮 + 20 位元組定長記錄)。1 小時 = 1 檔案,可用純 Python 解碼。
# .bi5(LZMA + 20B 定長: ms_offset, ask, bid, ask_vol, bid_vol)以小時為單位平行取得
import lzma, urllib.request, numpy as np, concurrent.futures as cf
REC = np.dtype([("t",">u4"),("ask",">u4"),("bid",">u4"),("av",">f4"),("bv",">f4")])
def fetch_hour(sym, y, m, d, h):
url = f"https://datafeed.dukascopy.com/datafeed/{sym}/{y:04d}/{m-1:02d}/{d:02d}/{h:02d}h_ticks.bi5"
raw = urllib.request.urlopen(url, timeout=30).read()
return np.frombuffer(lzma.decompress(raw), dtype=REC) # 價格 = points/(JPY 為 1000, 其他為 100000)
陷阱①:HTTP 503(速率限制)由「爆發」觸發,而非「總量」 #
為了提速,我們把工作分散到多台機器,反覆停止→立即重啟,結果 IP 被 Dukascopy 暫時以 503 → 無回應 封鎖。驗證的結論:
- 503 的觸發因素不是「請求總量」,而是「短時間內的連線爆發」(=連續停止、重啟,過多的並發連線)。
- 即使被封,只要完全停止請求,5~10 分鐘即可自然恢復。
- 鐵律是 「只跑一次 · 用克制的並發 · 不停頓地一氣呵成」。這次 每對 24 執行緒 × 2 對平行(=48 連線) 是穩定區間。
陷阱②(最重要):「靜默丟棄」 ― 以為取得了,實則缺漏 8 成 #
第一次取得看似「跑完」了,但 之後量測涵蓋率卻只有約 22%。原因是下載器 把 HTTP 503 當作「無資料(404)」一樣靜默丟棄。即便沒被封,壅塞時也會零星出現軟性 503,那些小時就這樣悄悄缺漏。「資料在流 = 完整」並不成立。
🛠 如何發現、如何修正(可重現的檢查清單)
- 完整性要用「涵蓋率/缺漏率」來驗證,而非「是否在流」。若「nonempty 率」大幅低於交易時段的預期(≈80%),就要懷疑缺漏。這次是下游分析中對齊極度變瘦(5 分鐘面板本應 15 萬根,卻只有 200 根上下)才暴露出來。
- 區分 404 與 503。404=正當休市(不重取),503/逾時=需重取。一旦把兩者等同,缺漏就被隱形化。
- 多趟 · 缺口填補。記錄每個 (日, 時) 的取得結果,僅對失敗的小時以指數退避重取 → 反覆直到失敗為 0。
- 以涵蓋報告收尾。務必輸出「total / data / 休市(404) / 未取得」,確認未取得 ≈0 且 ticks/年 符合預期後再去信任。
結果涵蓋率從 22% → ≈100%(每對未取得 ≤6 小時 / 15,048)。完整 2 年可靠取得 4.06 億 tick。
STEP 2:陷阱③記憶體 ― tick 處理是「RAM 受限」 #
tick 的解碼會先把目標範圍的記錄在記憶體中展開,再寫出。也就是說 所需 RAM 與 期間 × 貨幣對數 成正比。
驗證中,用 1.9GB RAM 的小型 VPS 作輔助,多年 × 多對時立刻 OOM(記憶體不足被強制結束)。而 OptiMax(251GB RAM)即便把完整 2 年 × 6 對整體載入記憶體也游刃有餘(分析尖峰時仍大量空閒)。
tick 資料處理中,比 CPU 更先到來的是 RAM 這道牆。OptiMax 的大容量記憶體正是為此用途而生。
STEP 3:設計要點 ―把「下載機」與「計算機」分開 #
- 下載由線路品質(到 Dukascopy 的路由)主導。
- 分析 · 最佳化由 RAM 和核心數主導。
它們是不同機器的強項,所以分工。這次用 2 台乾淨 IP 的取得機蒐集資料(把每 IP 速率限制規避翻倍=分擔的真正功效),並讓 OptiMax 擔任「計算母艦」專注於傳輸、彙整、最佳化。約 1GB 的傳輸僅 51 秒。不被任一瓶頸拖累。
STEP 4:以 64 核「對策略窮舉最佳化」 #
這正是 OptiMax 的本領。與 MT5 的策略測試器最佳化同樣的思路,把 1 次試驗=1 核 分配下去,把所有核心用盡。
關鍵在 平行的單位。僅按「貨幣對」平行只能跑 6 核。把 (貨幣對 × 訊號 × 持倉時間 × 進場閾值) 的全部組合 作為 1 次試驗,試驗數一下子暴增,64 核就能跑滿。沉重的前處理(1 秒網格)只建構一次並共享。
# 沉重的前處理(1秒網格)只做1次 → 用 fork pool 共享(copy-on-write) → 窮舉所有組合
import multiprocessing as mp
GRIDS = {p: build_grid(p) for p in PAIRS} # 父行程只建構一次(用 COW 共享給子行程)
tasks = [(p,sig,H,thr) for p in PAIRS for sig in SIGNALS for H in HORIZONS for thr in THRESHOLDS]
with mp.Pool(64) as pool:
results = pool.map(eval_combo, tasks) # 每個 combo = walk-forward + 計入成本 + bootstrap CI
實測:在 1.41 億根 1 秒線 上,2,430 種 以 約 60 核佔用 · 581 秒(建構網格 111s + 掃描 470s)跑完。每次試驗都是包含 walk-forward(時序的訓練→驗證切分)+ 扣除來回點差的計入成本損益 + 區塊 bootstrap 信賴區間的完整驗證。
「因為是回測所以用不滿所有核」是誤解。把平行的單位取為「參數組合」,最佳化就會隨核心數線性提速。OptiMax 的 64 核在此真正滿載運轉。
結果(通用的收穫) #
完整 2 年 · 4.06 億 tick · 2,430 次最佳化試驗的量化結果:
- 訊號本身「存在」:忽略成本(GROSS)的夏普比率最高達 +429。短線價格波動確實有結構。
- 但以 taker(市價)拿不到:扣除來回點差後,2,430 種裡沒有一種為正(NET>0 僅 0/2,430)。而且每次試驗的 95% 信賴區間上限也是 0/2,430 為正=從統計上看「可能為正」的組合也為零(把資料補全後,源自雜訊的「僥倖陽性」也隨之消失)。
- 這是在 大規模 · 完整資料上嚴謹地再次確認 了市場微觀結構的教科書結論(短線優勢收斂於 bid-ask 點差之內,支付點差的一方=taker 無法回收)。
實務含意:短線優勢的研究,只有「扣除成本之後」才有意義。而要在現實時間內跑完那種大規模驗證,就需要 完整的資料 · RAM · 核心數。
總結 #
| 做了什麼 | 起作用的要素 |
|---|---|
| 取得完整 2 年 · 4.06 億 tick | 克制的平行 + 404/503 區分 + 多趟 · 缺口填補,涵蓋率 22%→100% |
| 把 4 億 tick 在記憶體中展開 · 處理 | 251GB RAM(小型 VPS 會 OOM) |
| 2,430 種的窮舉最佳化 | 64 核 + 「組合層級平行」 |
| 全流程 | 實際 3 小時多一點 |
tick 級別的大規模回測 · 最佳化,已不再需要獨佔一台特殊計算機。有了 OptiMax VPS,就能在需要時取用需要的 RAM 和核心,在現實時間內跑完這種規模的驗證。資料蒐集的那些陷阱(503 爆發 · 靜默丟棄 · RAM 上限),也都能用本文的檢查清單規避。
本次驗證全部在 OptiMax VPS(64 vCPU / 251GB RAM)上進行。
大容量記憶體 · 多核 · 低延遲,需要多少、何時需要都能滿足。從 FX 自動交易到量化研究。