|
《程序化交易實戰(zhàn)》連載7:策略編寫陷阱—未來函數(shù) 1. 未來函數(shù) ⑴.概念介紹 未來函數(shù)是指引用未來數(shù)據(jù)的函數(shù),在策略中的表現(xiàn)形式也就是引用未來時刻的信息作為判斷條件,對現(xiàn)在時刻下達(dá)開平倉指令。換句話說,函數(shù)利用了現(xiàn)在還不知道的信息,產(chǎn)生交易信號。這在歷史后驗中是完全可以實現(xiàn)的,因為在程序化交易的歷史后驗中,我們從一開始就擁有所有的歷史數(shù)據(jù)。而且,Bar 驅(qū)動策略是基于 Bar 信息開發(fā)的策略,如果不加以注意,很容易在編寫策略時出錯,引用未來的信息。但是在實盤交易中,行情是按時間順序到達(dá),在“現(xiàn)在”這個時間點上,我們只能擁有這個時間點以前的信息,也就是過去的數(shù)據(jù),不可能獲取這個時間點之后的信息,即未來的行情。因此,我們必須確保在歷史后驗中也遵循這一規(guī)則,發(fā)出的每一個交易信號都是基于該時點以前的信息,而并不包含該時點之后的信息,以避免這種“偷窺未來”的錯誤。 與偷價格類似,未來函數(shù)也一樣會為我們帶來一條完美的后驗曲線和漂亮的后驗數(shù)據(jù),但是在實盤使用這個策略之后,它會立即現(xiàn)出原形,喪失盈利能力。下面我們分別舉幾個未來函數(shù)的例子,并分析其發(fā)生的原因。 ⑵.原因解析 與偷價格類似,未來函數(shù)的表現(xiàn)形式也有很多,但歸根結(jié)底,原因只有一個:我們使用了未來才能知道的信息作為現(xiàn)在的判斷條件或者開倉條件。這并非是我們根據(jù)現(xiàn)在的信息有效地預(yù)測了未來,而是通過bar后驗的機制,偷看到了未來的信息,并且加以利用。我們也通過幾個例子來仔細(xì)分析一下未來函數(shù)出現(xiàn)的原因。 ①.案例一 上面這段代碼是Q語言編寫的一段開倉條件:如果上一根bar的收盤價高于長期均線,且當(dāng)前K線收陽,也就是說這根bar的收盤價大于這根bar的開盤價, 則以這根bar的開盤價做多。這就是一個典型的未來函數(shù)。問題在于,下單價格是當(dāng)前K線的開盤價Open[0]。所以“現(xiàn)在”這一時間點就是當(dāng)前K線開盤的一瞬間,而判斷條件中使用了當(dāng)前K線的收盤價Close[0],也就是“未來”的信息。這相當(dāng)于我們知道了這根K線收陽之后,通過時光機再回到開盤的那個時間點進(jìn)行下單,這樣當(dāng)然可以盈利。 上圖就是這個策略的后驗曲線,股指年化收益率達(dá)到500%,而且收益曲線極其平滑,幾乎沒有回撤。但非常遺憾,這在實盤交易中顯然是不可能實現(xiàn)的。因為我們在開盤時是無法知道收盤價是多少的;而等到收盤以后,雖然我們已經(jīng)知道了收盤價是多少,卻再也無法以開盤價成交。 這是用Q語言編寫的一個多頭止盈的模塊,當(dāng)頭寸價格上漲超過0.5%時,一旦從最高點回撤0.8個點即止盈平倉。這段代碼中也存在未來函數(shù)。在歷史后驗中,由于使用的是bar驅(qū)動的后驗機制,在這根bar執(zhí)行主程序的時候,我們就已經(jīng)知道Open[0],High[0],Low[0]和Close[0]的最終值。但是在實盤中,只有Open[0]是在這根bar開盤的時候就固定下來的,而High[0],Low[0]和Close[0]在這根bar結(jié)束之前,都是不停變動的。只有在bar結(jié)束之后,才成為固定的值,并且當(dāng)新的bar到來之后立即變成High[1],Low[1]和Close[1]。上面這段代碼相當(dāng)于在這根bar還沒有結(jié)束的時候,就已經(jīng)使用了這根bar結(jié)束之后才能知道的信息作為判斷條件,那么下面K線圖中所有的平倉點位都接近每根bar的高點也就不足為奇了。 然而,一旦我們實盤使用這個策略,程序會這樣運行:在前面幾個條件都滿足的情況下,在bar內(nèi)第一次出現(xiàn)比0.8個點更大的回落時,就會觸發(fā)Close[0] < High[0] – 0.8的條件,策略會立即平掉多倉。所以在實盤中是不可能出現(xiàn)每次平倉都在當(dāng)根bar的最高點附近這種情況的。 ③.案例三 上面一段代碼是在之前討論偷價格時給出的正確寫法,這段代碼是否存在未來函數(shù)的問題呢?在代碼中,雖然在這根bar結(jié)束之前使用了仍然會變動的High[0]作為判斷條件,但這并不是未來函數(shù)。因為在實時行情中,最新的bar會不斷運動。當(dāng)一個新的Tick被推送過來后,最高價High[0]也隨之更新,但是High[0]的變化是單方向的,它只可能越來越大。當(dāng)價格突破上邊界UpperBand的一瞬間,條件High[0] >= UpperBand剛好得到滿足。而一旦滿足,該條件便會固定下來,無論接下來價格如何變化,都不會改變。在此過程中,我們沒有引入任何未來的信息。因此,這段代碼中不存在任何未來函數(shù)。 相反,如果我們以High[0] <= UpperBand作為判斷條件,就會落入未來函數(shù)的陷阱。因為,在后驗中是以整根K線的最高價與UpperBand進(jìn)行比較,而實盤中會在剛開盤時就將當(dāng)時的最高價與UpperBand進(jìn)行比較。有可能出現(xiàn)這樣的情況:K線開盤時High[0] <= UpperBand成立,導(dǎo)致實盤中策略開倉;但收盤時High[0] <= UpperBand不再成立,導(dǎo)致后驗中策略不開倉。 ④.案例四 在上面的代碼中,在判斷條件中同時使用了High[0]和Low[0],這也會導(dǎo)致 未來函數(shù)的錯誤。因為在實時行情中,Low[0] <= LowerBand條件發(fā)生的時間有可能在High[0] >= UpperBand條件發(fā)生之后,等到這兩個條件同時滿足的時候,我們設(shè)定的開倉價格已經(jīng)不復(fù)存在了。而在Bar后驗時,我們并沒有考慮到時間的先后。相當(dāng)于我們在High[0] >= UpperBand的一瞬間已經(jīng)知道了未來會觸發(fā)Low[0] <= LowerBand的條件,所以我們立刻以UpperBand的價格開了多倉,這就造成了后驗與實盤的不一致。 我們再看下面一段代碼,它也引入了未來函數(shù),也是錯誤的。 在High[0] >= UpperBand和Low[0] <= LowerBand這兩個條件不都被滿足時,這段代碼不會出現(xiàn)問題。但是,一旦同一根bar內(nèi)這兩個條件都被滿足,就會引入未來函數(shù)。在實盤中,程序執(zhí)行的是開倉條件先被觸發(fā)的代碼。如果先觸發(fā)了Low[0] <= LowerBand,那么程序會開空倉??v使之后價格上漲,又突破了UpperBand,程序也不會再開多倉了。這是因為之前程序已經(jīng)開了空倉,Pos.MarketPosition == 0不再成立,開多倉的條件自然就不會被觸發(fā)。但是后驗的時候,bar驅(qū)動的后驗機制下無法識別先觸發(fā)哪個條件,程序會執(zhí)行寫在前面的代碼,即High[0] >= UpperBand,然后開多倉。這意味著,在后驗中,我們在開空倉條件成立的時候,知道了未來開多倉的條件也會成立,所以沒有開倉,并且等到開多倉條件成立的時候開了多倉。這相當(dāng)于將未來的信息引入了判斷條件,造成后驗與實盤不一致。 ⑶.總結(jié) 綜上所述,未來函數(shù)也是一個非常容易導(dǎo)致策略的歷史后驗與實盤表現(xiàn)不一致的陷阱。與偷價格相比,未來函數(shù)更加隱蔽,更不容易被人察覺,對后驗真實度的影響也更大。因為通過在K線上畫出輔助線的方法,結(jié)合K線上的開平倉信號,大部分偷價格的問題都可以直觀地看出來。而未來函數(shù)則不同,它的出現(xiàn)是由于策略在設(shè)計邏輯上出了問題,因此無法通過觀察K線直接判斷,而只能通過檢查代碼的邏輯來識別。這個問題在比較復(fù)雜的策略中更加容易出現(xiàn)。所以,當(dāng)我們在看到很平滑的后驗曲線之后,一定要仔細(xì)檢查未來函數(shù)的問題。 我們進(jìn)行歷史后驗的目的就是通過策略的后驗表現(xiàn),預(yù)測這個策略在實盤使用之后是否具有盈利能力。如果策略中存在未來函數(shù),后驗與實盤會出現(xiàn)極大的偏離,我們也就無法把策略后驗的表現(xiàn)作為對實盤表現(xiàn)的預(yù)測依據(jù),使得后驗徹底失去了意義。
|