這里主要講的是,中低頻交易策略的程序化實(shí)盤代碼實(shí)現(xiàn)。
策略
一個(gè)量化交易策略的誕生,一般是經(jīng)由對(duì)市場(chǎng)的觀察了解,產(chǎn)生策略 idea,然后設(shè)計(jì)策略的具體細(xì)節(jié),再回測(cè)多品種的數(shù)據(jù),從而獲取、驗(yàn)證具體參數(shù)數(shù)值的效果。如果效果可以,具有普適性,那么下一步就是上實(shí)盤小資金測(cè)試。樣本外實(shí)盤可行,最終就可以加大資金并長(zhǎng)期運(yùn)行。
為了防杠精,說(shuō)明一下,當(dāng)然也有其他方法產(chǎn)生策略,比如數(shù)據(jù)挖掘,挖呀挖呀挖因子。還有機(jī)器學(xué)習(xí)、強(qiáng)化學(xué)習(xí)這類自動(dòng)生成因子。不過(guò),這類黑盒方法,在策略回撤期很難堅(jiān)持運(yùn)行,因?yàn)槟悴恢肋@些挖出來(lái)的因子,什么時(shí)候失效,為什么失效。而做量化交易,最重要的一點(diǎn)就是要堅(jiān)持實(shí)盤,行情不好,你可以縮倉(cāng),但是不能停機(jī)。沒有人能知道大行情什么時(shí)候爆發(fā)。錯(cuò)過(guò)了,你之前的回撤就是白瞎。
一個(gè)類比,策略的設(shè)計(jì)、回測(cè),就相當(dāng)于《孫子兵法》上說(shuō)的“廟算”,“夫未戰(zhàn)而廟算勝者,得算多也;未戰(zhàn)而廟算不勝者,得算少也。多算勝,少算不勝,而況于無(wú)算乎!”
所以,交易策略在最初的設(shè)計(jì)上就得謀劃好,計(jì)算周密,不然還沒實(shí)盤其實(shí)就已經(jīng)輸了。這一步是最難的,后面的實(shí)盤代碼雖然也不容易,但不過(guò)大部分只是繁瑣而已,多花花時(shí)間精力,總能做好的。實(shí)盤的關(guān)鍵還是在風(fēng)控。
交易策略,或者叫交易規(guī)則,就是讓我們 “做正確的事”,具體的實(shí)盤落地,就是“正確地做事”。很多時(shí)候,知道正確的方向,遠(yuǎn)遠(yuǎn)要比走完路程要難。就如那個(gè)維修設(shè)備的段子,畫一條線收費(fèi)一千塊。畫線本身只值 1 塊,但是知道在哪里畫收費(fèi) 999。
策略+實(shí)盤共同組成交易系統(tǒng)。關(guān)于交易系統(tǒng)的設(shè)計(jì),可以參看之前這篇:[完備交易系統(tǒng)的七要素]
這里的實(shí)盤其實(shí)就是交易系統(tǒng)的后面 4 步“進(jìn)出盈損”的具體操作。前面 3 步,在策略設(shè)計(jì)完成時(shí),就基本已經(jīng)確定了。
但是,就算策略不錯(cuò),如果實(shí)盤執(zhí)行不到位,那么效果也會(huì)大打折扣,甚至導(dǎo)致停掉本來(lái)還不錯(cuò)的潛力策略。研究出一個(gè)好策略是很不容易的事情,不要倒在執(zhí)行這一關(guān)。好的想法,要最終穩(wěn)妥落地才行。
問(wèn)題
1. 突然的行情
這種是有 bar 內(nèi)信號(hào)的策略才會(huì)遇到。bar 內(nèi)就是沒有采用某根 k 線的關(guān)倉(cāng)價(jià)或者開倉(cāng)價(jià),而是盤中的價(jià)格來(lái)觸發(fā)信號(hào)。
這里以幾天前實(shí)盤遇到的一個(gè)問(wèn)題為例。一個(gè)交易市場(chǎng)的微觀例子。在幣圈挺普遍的。
下圖是前幾天(2023.7.10 17:21) BNB 永續(xù)合約的 10 秒 k 線(就是一根 bar 是 10 秒內(nèi)的交易數(shù)據(jù)聚合出 OHLCV 的意思)。
可以看到,那根大陽(yáng)線振幅 3.53%,成交了大概 3 千萬(wàn)刀左右。10 秒內(nèi)完成。在這次行情啟動(dòng)前,走勢(shì)幾乎毫無(wú)波瀾。
再看下圖是當(dāng)時(shí)的 1 秒 k 線,可以得到更多的細(xì)節(jié)。
[圖片上傳失敗...(image-f0607e-1692097492252)]
基本上 4 秒左右就完全漲到位了。第 1 秒漲了大概2 個(gè)多點(diǎn)。這些,基本都是那些高頻策略(事件驅(qū)動(dòng),高頻趨勢(shì),做市等)還有可能事先布置的算法單之類的博弈結(jié)果。
再來(lái)看 BNB 的 4 小時(shí) k 線。最大那根陽(yáng)線就是當(dāng)時(shí)消息出來(lái)的時(shí)間段。振幅 4.05%。
[圖片上傳失敗...(image-ec63a9-1692097492252)]
對(duì)比一下這幾個(gè)周期的 k 線就可以看出,這種消息驅(qū)動(dòng)的波動(dòng)(這次消息為幣安新IEO,ARKM),一般都是瞬間完成的。4 小時(shí)的漲幅居然和 10 秒相差并不太多,而真正的啟動(dòng),也就 4 秒左右。這種戰(zhàn)場(chǎng),是屬于那些武裝到牙齒的高頻策略的。但是,它也可能會(huì)影響到中低頻策略,造成更大的滑點(diǎn)。
這里不研究高頻策略,對(duì)于中低頻,說(shuō)明什么問(wèn)題呢?我想到的主要有兩點(diǎn):
1. 有的趨勢(shì)爆發(fā)非常突然,而且是在很短時(shí)間內(nèi)完成,所以不能隨便暫停策略。在幣圈的實(shí)盤要保證 24 小時(shí)在線,特別是有倉(cāng)位的情況下,不能掉線。
2. 實(shí)盤的滑點(diǎn)有可能達(dá)到 2 個(gè)百分點(diǎn)級(jí)別??瓷厦娴?1 秒 k 線,如果你的買入信號(hào)是以某個(gè)價(jià)格為基準(zhǔn),這個(gè)價(jià)格又正好在大 k 線的下部出來(lái),那么不管怎樣,你進(jìn)場(chǎng)后的價(jià)格大概都可能是滑出 2~3 個(gè)百分點(diǎn)。最差能到 4 個(gè)百分點(diǎn),那些在針尖成交的買單就是。不過(guò),如果你是賣單,那么就會(huì)獲取超出預(yù)期的利潤(rùn)。但是,根據(jù)墨菲法則,落下的面包總是涂著黃油的一面著地。長(zhǎng)期來(lái)看,大概率是滑點(diǎn)更多。
不過(guò),好的是,這種情況發(fā)生的次數(shù)有限。只要策略沒問(wèn)題,滑點(diǎn)大一點(diǎn),只是盈利回吐而已,不影響長(zhǎng)期正收益。就當(dāng)多承受了一次震蕩磨損。
緩解的一個(gè)辦法,就是采用 websocket 獲取最新價(jià)格,文檔說(shuō) 250 毫秒更新一次,但是有的時(shí)候其實(shí)更新更快,這樣就有可能在第一時(shí)間反應(yīng)了。websocket 的另外一個(gè)優(yōu)點(diǎn),是不占用交易所 Restful API 的頻率限制。這個(gè)后面有詳細(xì)提到。
2. 整點(diǎn)行情
這種是很多趨勢(shì)策略會(huì)遇到,因?yàn)榇蠖际遣捎瞄_關(guān)倉(cāng)價(jià)格來(lái)計(jì)算進(jìn)出場(chǎng)的信號(hào)。那么整點(diǎn)一過(guò),各自紛紛啟動(dòng)。
整點(diǎn)時(shí)刻,特別是 8 的倍數(shù),因?yàn)闊o(wú)論你的策略是什么周期,這三個(gè)點(diǎn)應(yīng)該都遇到公約數(shù)時(shí)間了,可能大家的自動(dòng)化策略都會(huì)有動(dòng)作,高中低頻擠在一起產(chǎn)生各自的信號(hào),互為對(duì)手盤。
另外,這個(gè)時(shí)候,交易所還需要結(jié)算交割。雖然永續(xù)合約不交割,但是要計(jì)算交割資金費(fèi),可能還有其他操作。在 0/8/16 這三個(gè)整點(diǎn)之后的開始 6 秒鐘左右,幣安的 websocket 是不推送 k 線信息的,它停了!就問(wèn)你怎么辦?現(xiàn)實(shí)情況,很多時(shí)候就是沒有辦法。
更嚴(yán)重的是,行情會(huì)共振,特別是下跌行情,恐慌的傳染性更強(qiáng)烈。交易所的服務(wù)器更忙了,很多行情激烈的小幣直接就宕機(jī),下單是下不進(jìn)去的,全是 1001 錯(cuò)誤。等你忙不迭地下單成功了,可能針尖上成交的那些單子就是你的。
所以,滑點(diǎn)真的是避免不了的,都是命,接受它吧,過(guò)分優(yōu)化代碼也沒太大用處。就如前面說(shuō)的,就當(dāng)多止損了一次。
不過(guò),也有一些小技巧分享。
可以在整點(diǎn)前 n 秒提前計(jì)算信號(hào)跑路,那個(gè)時(shí)候還沒開始擁擠,因?yàn)榭赡艽蠖鄶?shù)自動(dòng)化策略都是等到整點(diǎn) k 線關(guān)倉(cāng)價(jià)出來(lái)之后,才開始計(jì)算信號(hào)的。不過(guò)這樣操作帶來(lái)的問(wèn)題就是,可能會(huì)產(chǎn)生錯(cuò)誤信號(hào),例如價(jià)格馬上又回去了,本來(lái)不該有信號(hào)的,false positive 發(fā)生了。這時(shí)就看你的取舍了,要不要這么干。不過(guò),可以再加一定閾值,超過(guò)了才觸發(fā),這樣防止短時(shí)間價(jià)格回歸造成的誤發(fā)信號(hào)?;c(diǎn)能減少一點(diǎn)是一點(diǎn)吧。
還有就是采用時(shí)間 offset 或者 shift k 線這類偏移信號(hào)的方法,不在整點(diǎn)產(chǎn)生信號(hào),不跟別人卷到一起去,更好地避開擁擠。還有就是 TWAP 等等方式。
不過(guò),這些都需要一些技巧,會(huì)加大實(shí)盤代碼的難度。
實(shí)盤
初級(jí)的中低頻 CTA 策略,一般來(lái)說(shuō)實(shí)盤代碼非常簡(jiǎn)單。一個(gè)品種只有一個(gè)策略,而且沒有加減倉(cāng),沒有交叉信息混淆在一起。那么本地代碼可以不維護(hù)任何狀態(tài),因?yàn)榻灰姿鶐湍阌涗浟艘磺?。是否有倉(cāng)位,保證金多少,單子是否成交等等。因?yàn)槭侵械皖l,需要的時(shí)候可以隨時(shí)查詢交易所的賬戶信息,而且這個(gè)是最即時(shí)準(zhǔn)確的。免去了本地使用數(shù)據(jù)庫(kù)之類記錄交易狀態(tài)的麻煩。
這種簡(jiǎn)單策略其實(shí)也是可行的,但是問(wèn)題就是可能回撤會(huì)大一點(diǎn),不夠分散,風(fēng)險(xiǎn)高。單品種可能一直沒行情,震蕩回撤不見回頭,或者遇到極端行情,資金曲線上又會(huì)來(lái)一個(gè)大坑。嚴(yán)重點(diǎn),扛不住就會(huì)被嚇得停策略,輕一點(diǎn),交易體驗(yàn)會(huì)差很多。注意,這些說(shuō)的都是在合理倉(cāng)位的條件下。不然倉(cāng)位太輕,回撤是小了,但是收益也降低了,盈虧同源,機(jī)會(huì)錯(cuò)過(guò)也可惜;太重,就是錯(cuò)誤,遲早要完蛋的。
不過(guò),建議新手可以先搞一個(gè)這種策略實(shí)盤跑起來(lái)。實(shí)踐才能快速提高自己的交易水平。但是注意一定要輕倉(cāng),然后杠桿要低一點(diǎn),最好是不用杠杠,這樣可以堅(jiān)持更長(zhǎng)時(shí)間。
如果要做得更專業(yè)一點(diǎn),降低單點(diǎn)風(fēng)險(xiǎn),資金曲線更平滑,實(shí)盤的時(shí)候更少擔(dān)心(如前所述,合理倉(cāng)位下,這點(diǎn)其實(shí)挺重要,因?yàn)楦菀讏?jiān)持運(yùn)行策略,等到風(fēng)來(lái))一般都是多品種多策略多參數(shù)的模式,然后還有加減倉(cāng)。不一定是在一個(gè)策略上的加減倉(cāng),也可以是多個(gè)子策略實(shí)現(xiàn)加減倉(cāng)的效果。不過(guò),這樣的話,策略就會(huì)比較擁擠,如果還想盡量降低開平倉(cāng)滑點(diǎn),那么代碼復(fù)雜度就會(huì)直線上升。
下面說(shuō)說(shuō)這種多品種多策略多參數(shù)模式帶來(lái)的難題。
難點(diǎn)
1. API 頻率限制
多策略的第一個(gè)難題就是 API 頻率限制。品種多了,策略多了,為了獲得即時(shí)的價(jià)格、倉(cāng)位、下單等信息,就得不停去訪問(wèn)交易所。像前面講的,頻率低了,慢了,那么獲得價(jià)格就可能不是最新的,滑點(diǎn)就可能大不少。
交易所的服務(wù)器資源有限,那么就產(chǎn)生了 API 的訪問(wèn)頻率限制。幣安的限制是每分鐘 2400,然后還有 10 秒的限制,然后還有其他所謂機(jī)器學(xué)習(xí)探測(cè)惡意行為的模式,反正就是太頻繁肯定不行,至于怎么不行,交易所說(shuō)了算,這種你懂的,沒有定式。
雖然說(shuō)是 2400,但是我用并發(fā)隨便測(cè)試了一下,如果是請(qǐng)求 k 線,一次就算是最短的 99 根(API 權(quán)重為 1)那么,一起請(qǐng)求 75 個(gè)幣左右就會(huì)被 ban 幾分鐘。所以,不能輕易去挑戰(zhàn)極限的。
違反了,就是收到 429, 418 錯(cuò)誤,IP 被封禁幾分鐘到幾天,這個(gè)時(shí)候,如果你有倉(cāng)位就慘了(下單,特別是出清已有倉(cāng)位不受限制,但是你沒有價(jià)格信息了,除非還有 websocket)
這個(gè)問(wèn)題的解決,就是使用 websocket 獲取數(shù)據(jù),這樣交易所主動(dòng)推過(guò)來(lái),時(shí)間及時(shí)又不占用 API??梢詼p少一些滑點(diǎn)。帶來(lái)的問(wèn)題,就是你得再維護(hù)一套基于 websocket 的行情中心。實(shí)盤代碼難度上升了,變復(fù)雜了。要 24 小時(shí)不掉線,斷線了要及時(shí)自動(dòng)重連等等。
2. 策略狀態(tài)
前面說(shuō)了,策略多了,復(fù)雜了,就不得不記錄每個(gè)策略的狀態(tài),不然交易所的倉(cāng)位也不知道是哪個(gè)策略開的,每個(gè)開的多少之類。而且后面要復(fù)盤的話,也得記錄更多數(shù)據(jù)。
這個(gè)時(shí)候就得引入數(shù)據(jù)庫(kù)了(當(dāng)然,用文件記錄也行,一個(gè)意思)。
引入了數(shù)據(jù)庫(kù),就帶來(lái)數(shù)據(jù)庫(kù)的那些問(wèn)題。不過(guò)這和其他行業(yè)的軟件開發(fā)類似,沒有新意。特別是電商行業(yè),因?yàn)槎际琴Y金余額,訂單管理這些類似的東西,沒有經(jīng)驗(yàn)的可以看看這類書籍文章。
要注意的就是,很多步驟要達(dá)到類似數(shù)據(jù)庫(kù)事務(wù)的原子化操作,就是一組操作要么就是全部成功,一旦某一步失敗,就得什么都不干,回滾到最初狀態(tài)。
然后,因?yàn)楫吘故钦娼鸢足y的交易,對(duì)于下單信息的數(shù)據(jù)庫(kù)操作,最好是采用 4 個(gè)隔離級(jí)別的最高級(jí),串行化 Serializable,杜絕所有臟讀、不可重復(fù)讀、幻讀的可能性。也就是不采用任何并發(fā),多線程,甚至異步去讀寫數(shù)據(jù)庫(kù)的關(guān)鍵信息,所有操作在一個(gè)線程內(nèi)完成。實(shí)現(xiàn)操作的冥等。
不要輕易去挑戰(zhàn)并發(fā)編程。這種系統(tǒng)出了問(wèn)題大概都無(wú)從下手,屬于軟件開發(fā)里最難發(fā)現(xiàn)的 bug。能把這類活完成得干凈利落的,都是 5 萬(wàn)+月薪級(jí)別的程序員。
我能想到的不多的采用多線程或者異步(多線程和異步,建議采用多線程,因?yàn)楫惒皆?Python 里就跟傳染病似的,一旦用了,一條調(diào)用鏈上都得用它才行)確實(shí)有益處的地方,就是發(fā)送釘釘告警信號(hào)之類。因?yàn)獒斸敺?wù)在中國(guó),而幣圈交易所服務(wù)器又都在海外,你的代碼服務(wù)器應(yīng)該布置在海外,更靠近交易所,所以發(fā)送釘釘,有時(shí)候要等好幾秒才返回,有時(shí)候甚至阻塞十幾秒都有。
最后
其實(shí)很多事情做不到也不要緊。只要有接受大一點(diǎn)滑點(diǎn)的覺悟,也就是可能吐出差不多 1/5 甚至更多的利潤(rùn)出來(lái)。重要的還是策略,收益正期望,能夠不停機(jī)一直運(yùn)行才是王道。
總之,實(shí)盤的第一要?jiǎng)?wù)是持續(xù)穩(wěn)定執(zhí)行策略的既定邏輯?;c(diǎn)是交易的一部分,盡力減少就行了,只要不傷筋動(dòng)骨。
一流策略,2、3 流實(shí)盤實(shí)現(xiàn),問(wèn)題不大。但是如果策略不過(guò)關(guān),頂級(jí)實(shí)盤代碼也無(wú)濟(jì)于事,該虧也還是得虧。所以光是代碼寫得好,是做不好量化交易的。策略好,實(shí)盤代碼也健壯,當(dāng)然是最好的。不過(guò),在時(shí)間有限的情況下,要把主要精力放在策略上。
To be continued