接前面三篇關於程序化交易實盤代碼的文章:

硬核乾貨 - 量化交易系統的自動化實盤細節與思考(一 問題與難點)

量化交易系統 - 自動化實盤細節與思考(二 實盤宗旨)

量化交易系統 - 自動化實盤細節與思考(三 處理技巧)

這裏再繼續說說多策略交易系統,實盤代碼總體架構裏的行情中心。

前言

交易這個行業,特別是期貨合約,每隔一段時間就會有幾個高槓杆交易大神閃現,十萬本金輕鬆翻到上千萬。但是,這些人幾乎都如流星劃過夜空,短暫的絢爛奪目之後,就沉寂消失不見。

而另外一羣長期做交易的老幫菜,整日唯唯諾諾,嘴裏唸叨着什麼未來不可知,敬畏市場,黑天鵝隨時降臨之類的喪氣話,同時用着很低的槓桿來回開倉試錯,方向猶如牆頭草,左右搖擺,一點也沒有高手的決絕與勇氣。但不知爲何,這一幫人卻一直在市場上活躍着,槓桿雖低,但是倉位還不小。

在交易世界這個人性的修羅場,明星易得,壽星難尋。

作爲一名活下來的量化交易員,我認爲交易中比較重要的一點,就是意識到多策略的必要性。主觀交易要實現多策略還是比較難的,因爲人力有限(除非像機構那樣僱傭多名交易員)7*24 小時運行的市場也容易錯過入場點。但是程序化交易,還是比較容易實現的。程序化失去了人腦人眼對價格走勢形態的識別能力,但是執行力更強,複製策略分散倉位的能力也更強,有利有弊。

多標的多策略多參數,資金倉位自然而然就分散開來。單次盈虧不再有刺激的大開大合,而是追求資金曲線儘量平滑。

總覽

下圖就是我的一組實盤代碼的大致架構圖。

图片

整個架構分爲兩塊。一塊是獨立的行情中心,另外一塊就是負責策略邏輯實現的交易程序。

通常我是以一個賬號啓動一個程序,它們的策略參數和賬號 key 以配置文件區別,這樣就可以完全共享代碼,代碼版本也更容易管理。策略的一些參數,可以略有不同,主要是爲了錯開某些操作,不用突然同時運行。

之前策略少的時候,我以每一個子策略啓動一個 Python 程序,到了後期發現服務器內存不夠用了。因爲一個 Python 程序,光自身就佔用了大概 60M 內存,如果策略多一點,再加上覆制到幾個賬號,那麼很快就會佔用大量內存。雖然可以加錢上更強大的服務器,但是這樣管理維護也比較麻煩。最終就改成了現在這樣,以賬號分,同類策略在不同的幣上(多幣種多參數)爲一組,然後一組策略爲一個 Python app。運行下來感覺這個組合模式對於多賬號多策略的佈局很不錯,代碼管理,實盤運維都很方便。

一個子策略就一個 Python app 的模式好處是,代碼可以簡單很多。但是,試想,如果你的策略運行到排名前 40 名的大幣上,然後每個幣運行 3 個不同的策略,那麼就是 120 個子策略,如果再來三個賬號,就是 360 個子策略,這種模式就難以爲繼。

app 少的時候,可以先採用 tmux 這類終端讓 log 信息實時輸出,在程序運行初期監控就非常方便。後期代碼穩定了,再採用 pm2 這類運維軟件管理。以後定期分析 log 文件就行。

多標的多策略的情況下,獨立的行情中心就得是標配了。獲取的各種市場信息,可以在多個交易程序之間共享,也就是上圖左邊的那些策略模塊。

行情中心

下圖是一張更詳細一點的行情中心模塊圖。

图片

首先,可以通過配置文件,設定需要獲取的 k 線 symbols,這樣以後方便更換品種。

如果是多週期的,那麼可以獲取它們的公約數週期 k 線,然後各個策略根據自己的需求再 resample 就可以了。

如果要簡化一點,一勞永逸,那麼直接獲取 15 分鐘的 k 線最好,因爲中低頻策略,特別是趨勢跟隨這類,週期低於 15 分鐘的,長期來看基本很難賺錢。

例如,你有 15 分鐘,1 小時,4 小時的策略,那麼行情中心僅僅獲取 15 分鐘的 k 線就行了。如果 resample 之後大週期的 k 線長度不夠,那麼就得在啓動時刻,把 15 分鐘的 k 線在數據庫中多保存一些。

因爲 websocket 只是推送最近更新的 k 線信息,如果你的策略用到的 k 線週期比較大,而且回溯時間又長的話,例如 4 小時的 ma150,那麼就是 600 個小時(25天)的數據,15 分鐘一根 k 線的話,需要 2400 根。這個得靠在行情中心啓動的時候,通過 rest api 一次性獲取保存,作爲之後不斷更新的基礎。

整個行情中心,我分了兩個 app。其實也可以合在一個 app,但是分開更加 robust。先說次要的 PlanB。

PlanB

之前幾篇文章說過,這個主要是防止 PlanA 的 websocket 掉鏈子的情況,用來臨時備用的,特別是策略的出場時刻,避免價格都已經反向走出一段距離了,該退出倉位還沒退出,造成意外的更多虧損。

PlanB 非常簡單,主要就是利用 public_get_ticker_price 這個函數,一次性獲取所有永續合約的最新價格。這也是B安自己聚合好的最新價格信息。

目前B安大概有 200 多個永續合約,如果一個一個單獨獲取最新價格的話,要保證每個品種價格 3 秒更新一次,一分鐘內大概就要調用超過 4000 次 api,肯定超過限制了,不可能實現。但是前面提到的那個函數 api 權重只有 2,如果 3 秒一次,每分鐘也才耗費 40 次 api quota,完全不用擔心超標。

經過我的測驗,基本上幣安就是 1~2 秒左右聚合一次所有合約的價格,所以對於中低頻策略的備用價格,5 秒左右的延遲完全夠用了。

如果要求不高,你甚至可以用它來合成所有合約的粗糙 k 線,完全避開採用 websocket 的方案。這種 k 線就算不能用來交易,也可以用在諸如全市場信息掃描上,然後根據自己設計的策略算法,例如什麼均線發散、收斂,簡單的形態識別等等,給主觀交易員提供入場信號。不然,那麼多幣,怎麼看得過來。如果有水平高的日內交易員,這種工具可能就會有用,輔助交易。我之前就給別人寫過類似工具,行情好的時候確實不錯,不過最近沒啥行情了。

當然,只有在交易很多幣的時候這個方案纔有實際用處。不然就直接獲取各個品種的盤口 BBO 價格更好。

還有注意,備用價格信息也得在主代碼中常常使用纔行,比如用來計算倉位對應的法幣資金額,大致盈虧率這類不需要很精確的數據。因爲,不經常使用的代碼片段和信息,早就崩壞了你可能還不知道,直到用時才發現就晚了。代碼寫多了的都知道,這種闌尾代碼很容易意外改壞而不自知。

PlanA

行情中心的主代碼。這個 app 主要有 4 個功能。

  1. 獲取歷史基礎 k 線。就是文章前面提到的,每次啓動,要保證所有策略需要的每根 k 線都在。

  2. websocket 管理。這一個模塊稍微複雜一點,需要處理各種掉線情況,還有重連等等。每次交易所推送了新的 k 線數據,就更新數據庫中 k 線最新那一根 bar 的數據。

  3. 除了 k 線數據,我還把每個品種的精度信息更新也放在行情中心。也就是 public_get_exchangeinfo 這個函數獲取的各種下單價格,下單數量的最小精度,再轉換成方便使用的字典類型。這樣所有策略可以共享。

  4. 最後就是檢查,簡單的 sanity check 就夠了。獲取的 k 線是否連續,沒有遺漏,然後再定期與 rest api 獲取的 k 線進行一一對比,與 PlanB 獲取的價格是否差距過大等等。總之,就是多對比檢查,有錯誤早發現。

這些大概就是我的中低頻策略行情中心架構。如果需要,還可以把賬號的更新信息通過 websocket 進行獲取。但是這樣做稍微麻煩一點,大多數時候用不到。