上期文章我們講到了程序化交易腳本。其實交易策略就是一個交易腳本程序,文章主要講到了交易腳本程序需要有硬件載體(程序在哪里運行),這個腳本交易程序可以用那種計算機編程語言編寫(列舉了在發明者量化交易平台上使用的三種編程語言,當然本身做程序化交易可以使用任何編程語言實現策略)。本期文章我們繼續討論幣圈量化、了解幣圈量化的知識。
程序化交易腳本
交易策略的種類
初識程序化交易、量化交易的萌新們可能上來就被各種趨勢策略、套利策略、高頻策略、網格策略等名詞整暈了,其實程序化交易、量化交易常見策略類型簡單說就幾個方向。
套利對沖策略
簡單說,基本上一邊持有看多頭寸,一邊持有看空頭寸這樣的策略可以歸類為套利策略。具體的種類有很多,現貨跨市場、期貨跨期、期現套利、跨品種套利等。
趨勢策略
簡單說就是跟蹤趨勢下單持倉的策略,雙均線、MACD等策略。
回歸策略
例如,網格策略,賺取震盪行情中價格波動收益。
高頻策略
簡單說就是通過一些算法發掘市場微觀結構、規律、機會等進行高頻率交易的策略。
以上是從交易策略角度去划分的,在發明者量化交易平台上從策略設計角度去看,策略還可以分為:
單品種策略
就是說這個策略只操作一個品種,比如做BTC交易或者做ETH交易。
多品種策略
簡單說就是按照一個策略邏輯去操作多個品種。
多賬號策略
簡單說就是一個實盤上配置多個交易所對象(上一篇文章中已經介紹交易所的概念,配置好API KEY的交易所對象就代表一個交易所賬戶)。比如一些跟單策略,多個賬號一起跟隨操作(可能是同一個交易所,也可能是不同交易所),總之一個實盤上管理多個交易所對象(賬號)。
多邏輯策略
一個實盤上比如同時設計了MACD策略、均線策略、網格策略等(當然,是操作不同交易所對象,操作相同的交易所對象要看具體的策略是不是邏輯上有沖突)
交易所API接口
程序化交易腳本是如何操作交易所賬戶呢?答案就是通過交易所開放的API接口。那么交易所開放的接口都有哪些種類呢?上篇文章中我們在「交易所」部分講了交易所一般有REST、Websocket接口。這里我們從策略程序層面補充一點概念。交易所接口按是否驗證划分(REST、Websocket都是)有驗證和非驗證。
不需要驗證的接口
一般稱為“公共接口”,這類接口不需要驗證API KEY
(忘記API KEY是什么的可以翻下上篇文章)。這類接口一般是行情接口,例如查詢深度行情、查詢K線數據、查詢資金費率、查詢交易品種相關信息、查詢交易所服務器時間戳等。簡單說就是和你的賬戶基本毫無相關的接口可以大致確定就是一個公共接口(不需要驗證)在發明者量化交易平台上,調用不驗證的API函數時(封裝交易所非驗證接口,公共接口)即使API KEY配置錯誤,也可以正常獲取到接口返回的數據。(因為不驗證)
需要驗證的接口
簡單說就是需要驗證的接口(通過API KEY驗證),這類接口叫做私有接口。這類接口通常都和你的賬戶一些操作或者信息有關系,例如查詢賬戶資產、查詢賬戶持倉、查詢掛單、查詢轉賬、轉幣、調整杠桿、設置持倉模式等。這些操作都必須驗證。在發明者量化交易平台上,調用需要驗證的API函數時(封裝的交易所需要驗證的接口,私有接口),如果API KEY配置錯誤,調用接口時會報錯,返回空值。
那么在發明者量化交易平台上這些接口是如何使用的呢?
發明者量化交易平台封裝了交易所行為、定義一致的接口(例如K線接口、深度行情接口、查詢當前資產接口、下單接口、撤單接口等),這些接口在發明者量化交易平台上叫做發明者量化交易平台API函數,可以通過查詢API文檔查閱( https://www.fmz.com/api )。
那么一些行為、定義並不統一的交易所接口在發明者量化交易平台上如何使用呢?
這些交易所接口例如:資產划轉、條件委托、批量下單、批量撤單、修改訂單等。這些接口有些交易所具備,有些交易所沒有,並且功能、使用細節可能差別較大,所以這些接口在發明者量化交易平台上通過exchange.IO
這個函數去訪問(詳情可以參看發明者量化交易平台API文檔:https://www.fmz.com/api#exchange.io...)。在發明者量化交易平台策略廣場上也有些實用IO的范例策略。
是否在發明者量化交易平台API文檔上的所有API函數都會產生網絡請求呢?
我們先說下交易所API接口是有訪問頻率限制的(例如一秒5次之類的),訪問不能過於頻繁否則就會報http 429錯誤,就拒絕訪問了(大部分交易所都是報429)。那么在發明者量化交易平台上調用封裝好的交易所接口也是同樣有這個限制,對於發明者量化交易平台上不產生網絡請求的API函數則沒有這個限制。
並不是所有發明者量化交易平台的API函數都會產生網絡請求,有些發明者的API函數只是修改一些本地設置,例如設置當前交易對、設置合約代碼、指標計算函數、獲取交易所對象名稱等。基本上從函數的用途可以判斷出來是否發生網絡請求,只要是獲取交易所數據、對交易所賬戶操作之類的都是產生網絡請求的,這些接口都需要注意調用頻率。
再來說幾個在發明者量化交易平台使用API函數時常見的問題、經驗
容錯
這個是最常見的錯誤,困擾無數萌新,經常策略回測好好的一切正常,為何實盤跑了一段時間(隨時可能觸發)實盤就掛掉了~
編寫策略時對於接口返回的數據我們是都需要判斷驗證的,例如在發明者量化交易平台上獲取行情這行代碼(自己寫程序直接訪問交易所接口也是一樣):var ticker = exchange.GetTicker()
,假如我們需要用這個ticker
變量(參看GetTicker函數返回的結構)里面的Last
(最近價格)這個數據,我們需要使用var newPrice = ticker.Last
這樣取得數據(newPrice是啥?new:最新,Price:價格,對!合起來!)
此時,如果GetTicker()
函數返回了正常數據還好,但是如果發生了請求超時、網絡錯誤、交易所拔網線、挖斷電纜的、熊孩子拉電閘的等等..就會導致GetTicker()
函數返回null
。此時ticker
的值就是null
,我再去訪問它的Last
就會發生程序異常導致策略程序停止。
由此看來發生接口調用失敗(GetTicker調用失敗返回null)不是導致策略實盤停止的直接原因(直接原因是訪問了一個null
變量的屬性),接口調用失敗報錯是不會導致實盤停止的(划重點)。
所以我們要怎么做才能避免實盤異常停止呢?答案就是對接口返回的數據做容錯處理,很簡單只用判斷返回的數據是不是null
(JavaScript語言舉例,其它語言基本差不多)。寫個小代碼段說明(這只是個說明,直接運行是跑不起來的哦!)
var ticker = exchange.GetTicker()
if (ticker) {
var newPrice = ticker.Last
Log("打印最新價格:", newPrice)
} else {
// 數據為null,不做操作就不會出問題
}
不光是GetTicker
接口需要做容錯,有網絡請求的接口都需要對返回值做容錯(如果你對函數的返回值使用的話)容錯方式有很多種,可以使用_C()
函數(參看FMZ API文檔),自己寫容錯函數,自己設計容錯機制、邏輯。關於_C()
函數的使用,很多萌新同學也是大概率用錯,注意_C()
函數的參數是函數引用,不是函數調用。
通俗說:_C(funcName, param1, param2)
,調用正確,funcName 不帶小括號,param1、param2是要給funcName這個函數傳的參數。_C(funcName(param1, param2))
,調用錯誤,通常萌新沒認真看FMZ API文檔都會這么寫。
現貨市價單買單的下單量
現貨市價單買單的下單量也是很多萌新容易搞錯的,上一篇中已經講過現貨市價單買單的下單量通常是金額(極個別的交易所可能是其它設定,一般FMZ上這些特殊的交易所設定都會在FMZ API文檔上說明),例如我用OKEX V5 模擬盤測試:
交易對設置為:LTC_USDT
function main() {
exchange.IO("simulate", true) // 切換為OKEX交易所的模擬盤
exchange.Buy(-1, 1) // 價格是-1,表示下的訂單為市價單,數量為1表示下單量是1USDT
}
由於交易所一般都有下單金額限制,小於限制的訂單是不予報單的(例如幣安現貨要求每單大於5USDT才可以報單成功)。所以這樣下單會報錯:
錯誤 Buy(-1, 1): map[code:1 data:[map[clOrdId: ordId: sCode:51020 sMsg:Order amount should be greater than the min available amount. tag:]] msg:]
期貨下單時的方向
在做期貨策略時下單方向也是萌新們經常搞錯導致出問題的,以在發明者量化交易平台上編寫策略為例。我們先看下API文檔上的描述:https://www.fmz.com/api#exchange.setdirection...
由於下單函數就只有Buy
,Sell
。然而期貨(現貨當然沒問題了,現貨只有買賣)有開多、平多、開空、平空這些方向,那么顯然Buy/Sell表示不了這么多個方向的操作,這時就需要引入設置期貨交易方向這個函數exchange.SetDirection()
。
在FMZ上
exchange.SetDirection("buy")
(先設置方向)和exchange.Buy
配合使用,就表示下的單子是開多倉的訂單。
以此類推:
exchange.SetDirection("sell")
和exchange.Sell
配合使用,就表示下的單子是開空倉的訂單。
exchange.SetDirection("closebuy")
和exchange.Sell
配合使用,就表示下的單子是平多倉的訂單。
exchange.SetDirection("closesell")
和exchange.Buy
配合使用,就表示下的單子是平空倉的訂單。
通常萌新會exchange.SetDirection("sell")
和exchange.Buy
配合使用,或者其它的錯誤組合。然后就報錯了(回測可能不報錯,但是這個明顯是邏輯錯誤,強迫症不能忍....的)。
另一個萌新經常犯的錯誤
function main() {
exchange.SetContractType("quarter") // 設置當前合約為季度合約
exchange.SetDirection("sell")
var id = exchange.Sell(-1, 1)
Log("看我市價單下單了,成交了,就有持倉了", exchange.GetPosition())
exchange.SetDirection("closebuy") // closebuy 和Sell 搭配使用,嗯沒錯~
exchange.Sell(-1, 1)
}
看到這里會問:“為什么我有持倉並且closebuy和Sell也是搭配使用的,怎么就報錯,不能平倉了?”。我會回答:“平錯方向了!平的是多頭倉位”以上這個報錯還可能出現的一種情況是:平倉方向設置正確、下單函數使用也正確、也持有這個方向的持倉,但是還是報這個錯誤。原因是可能你的程序下了多次單,開始的訂單並沒成交,平倉單在盤口掛着等待成交,這個時候程序繼續去平倉,就會提示超出平倉頭寸的錯誤。
日志輸出、交易信息展示
設計編寫程序化、量化交易策略就離不開“數據顯示”,“操作日志輸出”等人機交互的設計。通常使用原生編程語言編寫實盤腳本、策略程序。直接使用當前語言的輸出函數。
例如:
python用print
。
javascript用console.log
。
Golang用fmt.Println()
。
C++用cout
再來說下FMZ平台上的信息顯示,在發明者量化交易平台上,顯示信息的地方有兩個主要位置。
狀態欄
在實盤運行起來之后,實盤頁面如圖
顯示部分為狀態欄信息,狀態欄主要是為了顯示一些實時變動的數據(因為實時變動需要實時觀察,又不能每次都打印成日志,所以這類數據可以在狀態欄顯示,如果每條都打印日志會很多重復無意義的數據,影響查詢)。狀態欄上顯示數據使用LogStatus
函數,具體可以參看FMZ的API文檔。
日志欄
同樣在實盤頁面,如圖所示:
顯示部分為日志欄,日志欄主要是為了永久記錄某個時刻某些數據,或者記錄某個時候策略的某項操作。日志分為多種類型:
1、普通日志,FMZ的策略中使用Log函數輸出、打印在策略日志。
2、下單日志,FMZ的策略中使用exchange.Sell
/exchange.Buy
會自動在日志輸出記錄。
3、撤單日志,FMZ的策略中使用exchange.CancelOrder
,會自動在日志輸出撤單日志。
4、錯誤日志,FMZ的策略運行時,進行網絡請求的接口發生調用錯誤時,拋出異常時(例如throw之類的語句),會自動在日志中輸出錯誤日志。
FMZ的API函數,可以產生日志輸出的函數比如Log(...),exchange.Buy(Price, Amount),exchange.CancelOrder(Id)等都可以在必要參數后跟一些附帶輸出參數,比如:exchange.CancelOrder(orders[j].Id, orders[j])這樣就是在取消orders[j]這個訂單時,附帶輸出這個訂單信息。
function main() {
Log("數據1", "數據2", "數據3", "...")
var data2 = 200
var id = exchange.Sell(100000, 0.1, "附帶數據1", data2, "...")
exchange.CancelOrder(id, "附帶數據1", data2, "...")
LogProfit(100, "附帶數據1", data2, "...")
}
指標函數的使用
在說指標函數之前,我們先弄懂什么是指標,簡單說就是均線、MACD、ATR之類的線。
問:這些指標怎么來的?
答:當然是計算出來的。
問:是依據什么計算的呢?
答:根據K線數據計算。
問:舉個例子?
答:以最簡單的指標均線指標舉例,如果我們使用日K線(就是一根陽線或者陰線代表一天)數據作為指標計算的數據源。均線指標參數為10,那么計算出的均線指標就是10日均線。
問:如果K線BAR數量不夠10根,可以算出均線指標么?
答:不僅是均線指標無法算出,任何指標在K線數據BAR數量不滿足指標周期參數時都無法算出有效指標值,算出的數組對應位置上就會用空值填充,例如JavaScript
語言策略打印算出的指標數據時就會顯示null
。
正好策略廣場上有個教學例子:https://www.fmz.com/strategy/125770
回測這個教學例子策略,可以看到回測系統生成的圖表以及10周期的均線:
策略自定義畫圖,畫出的K線以及均線圖表:
問:如果我要10小時均線呢?
答:K線數據用小時周期的K線數據就可以了。
通俗說,我們看到的K線,我們把它數據化之后就是一個數組(數組概念不了解,可以百度下),其中每個元素就是一個K線柱,是按順序排列的,數組第一個元素是距離當前時間最遠的,數組最后一個元素是距離當前時間最近的。
通常K線數據最后一個線柱是當前周期的線柱,是實時變動的,是未完成的(登錄一個交易所的頁面觀察他的K線就能觀察出來變動)。計算出的指標也是和K線柱一一對應的,上面的例子可以看到一個指標數值對應一個K線柱。注意最后一個K線柱是實時變動的,算出的指標也是會跟隨K線柱變化而變化的。
在發明者量化交易平台上,可以使用TA庫(FMZ平台實現的庫,集成在托管者,各種語言都可以直接使用)或者使用talib庫(talib老牌指標庫,JS、C++集成,Python需要自行安裝)。
例如以上例子中計算計算均線:使用TA庫:
function main() {
var records = exchange.GetRecords()
var ma = TA.MA(records, 10)
Log(ma) // 打印均線
}
使用talib庫:
function main() {
var records = exchange.GetRecords()
var ma = talib.MA(records, 10)
Log(ma) // 打印均線
}
算出的指標數據ma是一個數組,每個元素一一對應K線數組(records),即ma[ma.length -1]
對應records[records.length - 1]
,以此類推。其它再復雜的指標也是同理,需要注意MACD這類指標。
var macd = TA.MACD(records) // 這樣只傳入K線數據,不傳入指標參數,指標參數采用的就是默認值,其它指標函數也是同理
此時macd這個變量是一個二維數組(不了解概念可以百度),二維數組簡單說就是一個數組它的每個元素也是一個數組。
問:為什么macd指標數據是個二維數組呢?
答:因為macd指標是由兩條線(dif線,dea線)和一組量柱組成(macd量柱,其實這個量柱數據也可以看成是一條線)。所以macd變量可以拆分為:
var dif = macd[0]
var dea = macd[1]
var macdColumn = macd[2]
這里也有一個現成的教學例子,有興趣的研究:https://www.fmz.com/strategy/151972