本系列文章將會(huì)介紹如何使用DolphinDB進(jìn)行交易回測。本文以移動(dòng)平均線指標(biāo)為例,介紹如何在DolphinDB中實(shí)現(xiàn)技術(shù)信號(hào)回測。移動(dòng)平均線指標(biāo)(Moving average,簡稱MA)屬于趨勢指標(biāo)。在金融分析領(lǐng)域,移動(dòng)平均線是不可缺少的指標(biāo)工具。除了指示趨勢,均線指標(biāo)還能避免由于股價(jià)下跌錯(cuò)失清倉的機(jī)會(huì),減少收益的損失,及時(shí)止損,也能避免股價(jià)上漲錯(cuò)失買入的時(shí)機(jī),從而獲得更高的收益。 回測過程中,我們考慮兩種情況:不止損回測和止損回測。 數(shù)據(jù)表需要包含以下字段: 股票代碼:sym 日期:date 收盤價(jià)格:close 1. 定義MA信號(hào)當(dāng)短期均線大于長期均線時(shí),我們認(rèn)為這是一個(gè)MA交易信號(hào)。 def maSignal(x, shortHorizon, longHorizon){ 2. 不止損回測我們定義的交易算法如下: 假設(shè)前一天的MA信號(hào)為prevSignal,當(dāng)天的MA信號(hào)為signal。 (1)如果prevSignal=false,signal=true,那么買入多頭頭寸(long position)。 (2)如果prevSignal=true,signal=false,那么賣出空頭頭寸(short position)。 (3)如果不符合以上兩種情況,則保持與前一天相同的頭寸。
DolphinDB函數(shù)說明: iif(condition, trueResult, falseResult):如果滿足條件condition,則返回trueResult,否則返回falseResult。它相當(dāng)于if...else語句,但是語法上更加簡潔。 int():返回int類型的NULL值。 prev(x):把向量中的所有元素向右移動(dòng)一個(gè)位置。 ffill(x):使用NULL值前的非NULL元素填充向量中的NULL值。 isValid():檢查每個(gè)元素是否為NULL。如果為NULL,返回0,否則返回1。 backtest 函數(shù)說明: 回測時(shí)首先整理數(shù)據(jù),使用prev()函數(shù)把前一天的收盤價(jià)格prevClose和前一天的MA信號(hào)prevSignal與當(dāng)天的數(shù)據(jù)對(duì)齊,便于計(jì)算。 接著,按照我們定義的交易算法,計(jì)算每個(gè)股票的頭寸position。position=1表示買入,position=-1表示賣出,position=NULL表示保持不變。 最后,使用position*(close - prevClose)計(jì)算盈虧pnl。 3. 止損回測3.1 判斷止損點(diǎn) 首先,定義函數(shù)stoploss判斷是否需要止損。該函數(shù)返回布爾類型的向量。 def stoploss(ret, threshold){ DolphinDB內(nèi)置函數(shù)說明: cumprod:計(jì)算累計(jì)乘積。 cummax:計(jì)算累計(jì)最大值。 at(x):x是布爾表達(dá)式,找出符合條件x的元素的位置。 first:返回第一個(gè)元素。 take(X, k):返回包含k個(gè)x的向量。 stoploss 函數(shù)說明: 首先計(jì)算累計(jì)回報(bào)率cumret,接著計(jì)算當(dāng)前回報(bào)率和累計(jì)最大回報(bào)率的回撤drawdown,當(dāng)回撤drawdown大于等于預(yù)設(shè)閾值threshold時(shí),則認(rèn)為應(yīng)當(dāng)止損,并記錄止損的起始位置firstCutIndex(由于到股市收盤時(shí)才知道是否需要止損或止盈,所以firstCutIndex要加1)。止損信號(hào)indicator的所有元素一開始設(shè)定為全是false。如果止損的起始位置firstCutIndex不為NULL,且不超過當(dāng)前的數(shù)據(jù)量,則把止損信號(hào)indicator中從firstCutIndex開始到最后的所有元素設(shè)為true,表示從firstCutIndex開始,都應(yīng)當(dāng)止損。 3.2 止損回測 回測時(shí),將止損前后的盈虧進(jìn)行對(duì)比 。
DolphinDB函數(shù)說明: segmentby(func, funcArgs, segment):把funcArgs分成多個(gè)組,并把函數(shù)func應(yīng)用到每個(gè)組中。segment是一個(gè)向量,可以把它看作是分組方案,連續(xù)相同的元素為一組。通過下面的例子我們可以更好地理解segmentby: x=1 2 3 0 3 2 1 4 5 上面的例子中,y定義了3個(gè)分組:1 1 1、-1 -1 -1 和1 1 1,第一個(gè)分組的index是0-2,第二個(gè)分組的index是3-5,第三個(gè)分組的index是6-9。按照這個(gè)規(guī)則把x分成3組:1 2 3、0 3 2、1 4 5,并在每個(gè)分組中計(jì)算累計(jì)和。 stoploss{, thresholdDrawDown}這種表達(dá)方式是定義一個(gè)部分應(yīng)用,用于固定stoploss的第二個(gè)參數(shù)thresholdDrawDown。 backtest_stoploss 函數(shù)說明: 前三行代碼和1.2大致相同,除了計(jì)算盈虧pnl之外,還計(jì)算了回報(bào)率ret,因?yàn)閟toploss函數(shù)需要ret作為輸入。接著把每個(gè)股票的回報(bào)率ret按階段分組(position中的元素連續(xù)多個(gè)1表示持續(xù)買入,連續(xù)多個(gè)-1表示持續(xù)賣出,連續(xù)多個(gè)NULL表示持續(xù)不變),在每個(gè)階段分組中判斷是否需要止損,為每只股票生成止損信號(hào)stoplossInd。最后計(jì)算止損前后的盈虧,止損前的盈虧為nostoplossPnl,止損后的盈虧為pnl。 4. 統(tǒng)計(jì)信息通常情況下,我們還需要分析盈虧的統(tǒng)計(jì)信息。通過下面的自定義函數(shù)calcPerformance可以計(jì)算盈虧的統(tǒng)計(jì)信息,比如累計(jì)盈虧cumpnl、平均盈虧avgpnl、盈虧天數(shù)days、盈虧的標(biāo)準(zhǔn)差std、最大回撤maxDrawdown等。返回的數(shù)據(jù)類型是字典。
5. 運(yùn)行實(shí)例我們使用美國股市從1998年到2016年股票的每日交易信息作為數(shù)據(jù)集來進(jìn)行測試。數(shù)據(jù)集共包含3474萬條記錄。 //數(shù)據(jù)導(dǎo)入和數(shù)據(jù)處理,產(chǎn)生stock數(shù)據(jù)表,包含sym, date, close三個(gè)字段 情況一:不止損回測
情況二:止損回測。我們把預(yù)設(shè)閾值設(shè)為2.5%。 //止損回測 DolphinDB database 雖然是一個(gè)通用的分布式時(shí)序數(shù)據(jù)庫,但因?yàn)閮?nèi)置極其高效的多范式編程語言,開發(fā)效率非常高。如果回測不用考慮止損,僅用了3行代碼計(jì)算MA信號(hào),3行代碼進(jìn)行回測。DolphinDB的運(yùn)行效率更是驚人,對(duì)美國股市18年的全部股票按日進(jìn)行回測,不止損回測執(zhí)行耗時(shí)僅4秒多,止損回測僅7秒多。 本文的目的是從技術(shù)上幫助金融工程師使用DolphinDB快速實(shí)現(xiàn)交易回測。文中采用的各種參數(shù),譬如長短線時(shí)間,止損閾值,數(shù)據(jù)過濾的方法等等,只是起到演示的作用,并非實(shí)踐中的最佳參數(shù)。 歡迎訪問官網(wǎng)下載DolphinDB試用版 |
|