【譯】Python 金融:算法交易 (4)回測交易策略

本文翻譯自2018年最熱門的Python金融教程 Python For Finance: Algorithmic Trading寞酿。

本教程由以下五部分內(nèi)容構(gòu)成:

本文是該教程的第四部分有额。


既然你手頭已經(jīng)有了一項交易策略痹栖,最好對其進(jìn)行回溯測試并計算其性能嚷硫。但是蹦骑,在進(jìn)行深入研究之前晴玖,你可能想多了解一些相關(guān)知識冕杠,比如回溯測試中的陷阱矛洞,回測器的組成洼哎,以及可以用來回測算法的Python工具烫映。

然而,如果你已經(jīng)掌握了這些噩峦,可以跳過以下內(nèi)容锭沟,直接開始實現(xiàn)你的回測器。

回溯測試的陷阱

回溯測試识补,不僅僅是“測試交易策略”族淮,而是在相關(guān)的歷史數(shù)據(jù)中測試策略,以確保該策略在你開始操作之前是實際可行的凭涂。利用回溯測試祝辣,交易人員能夠模擬并分析在一段時間內(nèi),使用特定策略進(jìn)行交易的風(fēng)險和收益切油。然而蝙斜,在你進(jìn)行回測時,最好牢記一些剛開始可能并不起眼的陷阱澎胡。

例如孕荠,有一些外部事件肯定會影響回溯測試,如市場體制的轉(zhuǎn)變攻谁,這是監(jiān)管的變化或是宏觀經(jīng)濟(jì)事件稚伍。此外,流動性約束戚宦,如禁止賣空个曙,可能會嚴(yán)重影響回溯測試。

其次阁苞,你自己也可能引入陷阱困檩。比如當(dāng)你過擬合模型時(優(yōu)化偏差),當(dāng)你認(rèn)為這樣更好而忽略策略規(guī)則時(干擾)那槽,或當(dāng)你偶然將信息引入過去的數(shù)據(jù)時(前視偏差)悼沿。

在你學(xué)完本教程,開始制定自己的策略并進(jìn)行回測時骚灸,需要重點考慮這些陷阱糟趾。

回溯測試的構(gòu)成

除了這些陷阱, 最好要知道構(gòu)成回測器的四項不可或缺的組件:

  • 數(shù)據(jù)處理器甚牲,是數(shù)據(jù)集的接口义郑。
  • 策略,基于數(shù)據(jù)產(chǎn)生做多或做空的信號丈钙。
  • 投資組合非驮,生成訂單并管理利潤和損失(也稱為“PnL”)。
  • 執(zhí)行處理程序雏赦,向經(jīng)紀(jì)人發(fā)送訂單劫笙,并接收股票已被買入或賣出的信號芙扎。

除了這四個組件外,根據(jù)系統(tǒng)的復(fù)雜性填大,還可以向回測器中添加更多的東西戒洼。你絕對可以做的更多,而不僅僅局限于這四個組件允华。然而圈浇,在這篇初學(xué)者教程中,你只需專注于讓這些基本組件在代碼中順利運行靴寂。

Python 工具

為了實現(xiàn)回溯測試磷蜀,除了 Pandas 外還可以使用一些其它工具,在本教程的第一部分對數(shù)據(jù)進(jìn)行金融分析時榨汤,你其實已經(jīng)大量使用了這些工具蠕搜。除了 Pandas怎茫,還有如 NumPy 和 SciPy收壕,它們提供了向量化、優(yōu)化和線性代數(shù)的程序轨蛤,可以在開發(fā)交易策略時使用蜜宪。

此外,當(dāng)開發(fā)預(yù)測策略時祥山,Python 的機器學(xué)習(xí)庫 Scikit-Learn 也能派上用場圃验,因為它提供了創(chuàng)建回歸和分類模型所需的一切。DataCamp 的 Supervised Learning With Scikit-Learn 課程缝呕,提供了該庫的介紹澳窑。然而,如果你想使用統(tǒng)計庫進(jìn)行諸如時間序列的分析供常,statsmodels 庫是一個理想的選擇摊聋。在本教程中執(zhí)行普通最小二乘回歸(OLS)時,你其實已經(jīng)簡單使用過這個庫了栈暇。

最后麻裁,還有 IbPyZipLine 庫。前者為 Interactive Brokers 在線交易系統(tǒng)提供了Python接口:你將獲得連接到 Interactive Brokers 的所有功能源祈,如請求股票報價器數(shù)據(jù)煎源,提交股票訂單,…… ZipLine 是一個集成的 Python 回測框架香缺,在本教程中你將使用的 Quantopian 就是基于它的手销。

實現(xiàn)簡單的回測器

如前所述,一個簡單的回測器包括策略图张、數(shù)據(jù)處理器锋拖、投資組合以及執(zhí)行處理程序馏慨。在之前你已經(jīng)實現(xiàn)了一項策略,并且也能訪問數(shù)據(jù)處理器姑隅,即 pandas-datareader 庫或者是從Excel讀取數(shù)據(jù)的Pandas庫写隶。仍需實現(xiàn)的組件就剩執(zhí)行處理程序和投資組合了。

但是讲仰,作為初學(xué)者慕趴,你目前還無需要專注于實現(xiàn)執(zhí)行處理程序。相反鄙陡,接下來你將看到如何開始創(chuàng)建用于生成訂單并管理盈虧的投資組合冕房。

在開始之前,先獲取 apple 公司的股票數(shù)據(jù)趁矾,參考本教程的第一部分:基礎(chǔ)入門耙册。

# 獲取apple公司股票數(shù)據(jù)
import pandas_datareader as pdr
import datetime 
aapl = pdr.get_data_yahoo('AAPL', 
                          start=datetime.datetime(2006, 10, 1), 
                          end=datetime.datetime(2012, 1, 1))

然后是創(chuàng)建均線交叉策略,參考本教程的第三部分:用Python構(gòu)建交易策略毫捣。

# 導(dǎo)入pandas,numpy
import pandas as pd
import numpy as np

# 初始化短期和長期窗口
short_window = 40
long_window = 100

# 初始化 `signals` 數(shù)據(jù)框详拙,增加 `signal` 列
signals = pd.DataFrame(index=aapl.index)
signals['signal'] = 0.0

# 創(chuàng)建短期簡單移動均值
signals['short_mavg'] = aapl['Close']  \
        .rolling(window=short_window, min_periods=1, center=False)  \
        .mean()

# 創(chuàng)建長期簡單移動均值
signals['long_mavg'] = aapl['Close']  \
        .rolling(window=long_window, min_periods=1, center=False)  \
        .mean()

# 生成信號
signals['signal'][short_window:] = np.where(signals['short_mavg'][short_window:] 
                            > signals['long_mavg'][short_window:], 1.0, 0.0)   

# 生成交易命令
signals['positions'] = signals['signal'].diff()

現(xiàn)在讓我們開始創(chuàng)建回溯測試中的投資組合(portfolio)。

  • 首先蔓同,設(shè)置變量 initial_capital 來存儲初始資金饶辙。再創(chuàng)建一個新的數(shù)據(jù)框 positions,并拷貝數(shù)據(jù)框 signals 的索引給它斑粱,這是為了獲取信號生成的時間弃揽。
  • 接著,在 positions 數(shù)據(jù)框中創(chuàng)建新的列 AAPL则北。當(dāng)信號(signal)為1矿微,短期移動均線超過了長期移動均線(針對大于最短移動均值窗口的時期),這時買入100股尚揣。而對于信號為0的時段涌矢,運算 100*signals['signal'] 的結(jié)果是0。
  • 創(chuàng)建新的數(shù)據(jù)框 portfolio惑艇,存儲購買股票的市場價值蒿辙。
  • 然后,創(chuàng)建數(shù)據(jù)框 pos_diff 存儲股票數(shù)目的差值滨巴。
  • 接下來開始真正的回溯測試:在數(shù)據(jù)框 portfolio 中創(chuàng)建新的一列 holdings思灌,存儲買入股票的數(shù)量與調(diào)整的收盤價的乘積瑰艘。
  • 另外 portfolio 中還包含一列 cash啃炸,指剩余的資金:用 initial_capital 減去用于買股票的錢。
  • portfolio 數(shù)據(jù)框中再增加一列 total铸豁,包括了現(xiàn)金和所持股票總的價值蜈垮。
  • 最后耗跛,在 portfolio 中增加 returns 列裕照,存儲獲得的收益率。
# 設(shè)置初始資金
initial_capital= float(100000.0)

# 創(chuàng)建數(shù)據(jù)框 `positions`
positions = pd.DataFrame(index=signals.index).fillna(0.0)

# 當(dāng)signal為1時调塌,買入100股
positions['AAPL'] = 100*signals['signal']   
  
# 用擁有的價值初始化 portfolio  
portfolio = positions.multiply(aapl['Adj Close'], axis=0)

# 存儲股票數(shù)目的差值
pos_diff = positions.diff()

# 在 portfolio 中增加 `holdings` 列
portfolio['holdings'] = (positions.multiply(aapl['Adj Close'], axis=0))  \
                        .sum(axis=1)

# 在 portfolio 中增加`cash`列
portfolio['cash'] = initial_capital  \
                  - (pos_diff.multiply(aapl['Adj Close'], axis=0))  \
                    .sum(axis=1).cumsum()   

# 在 portfolio 中增加`total`列
portfolio['total'] = portfolio['cash'] + portfolio['holdings']

# 在 portfolio 中增加`returns` 列
portfolio['returns'] = portfolio['total'].pct_change()

# 輸出`portfolio`的前幾行
print(portfolio.head())
            AAPL  holdings      cash     total  returns
Date                                                   
2006-10-02   0.0       0.0  100000.0  100000.0      NaN
2006-10-03   0.0       0.0  100000.0  100000.0      0.0
2006-10-04   0.0       0.0  100000.0  100000.0      0.0
2006-10-05   0.0       0.0  100000.0  100000.0      0.0
2006-10-06   0.0       0.0  100000.0  100000.0      0.0

作為回測的最后一項練習(xí)晋南,利用 Matplotlib 和回測的結(jié)果,可視化投資組合的價值羔砾,即 portfolio['total'] 隨時間變化的情況负间。

# 導(dǎo)入`pyplot`模塊
import matplotlib.pyplot as plt
# 創(chuàng)建一幅圖
fig = plt.figure(figsize=(12,8))

ax1 = fig.add_subplot(111, ylabel='Portfolio value in $')

# 繪制資產(chǎn)曲線
portfolio['total'].plot(ax=ax1, lw=2.)

ax1.plot(portfolio.loc[signals.positions == 1.0].index, 
         portfolio.total[signals.positions == 1.0],
         '^', markersize=10, color='m')
ax1.plot(portfolio.loc[signals.positions == -1.0].index, 
         portfolio.total[signals.positions == -1.0],
         'v', markersize=10, color='k')

# 顯示繪圖結(jié)果
plt.show()

注意,在本教程中姜凄,回測器以及交易策略的 Pandas 代碼都是以能夠輕松交互的方式編寫的政溃。在實際應(yīng)用中,你可能會選擇使用類這種更面向?qū)ο蟮脑O(shè)計态秧,因為它包含了所有的邏輯董虱。在這里能夠找到相同的移動均線交叉策略在使用面向?qū)ο笤O(shè)計時的示例,查看這篇演示文稿申鱼,并且千萬不要忘了DataCamp的Python Functions Tutorial教程愤诱。

使用 ZipLine 和 Quantopian 進(jìn)行回測

現(xiàn)在你已經(jīng)了解了如何使用 Python 中流行的數(shù)據(jù)處理包 Pandas 實現(xiàn)回溯測試。然而润讥,你會發(fā)現(xiàn)這么做很容易出錯转锈,也可能不是每次使用時最安全的選擇:盡管已利用了 Pandas 來獲取結(jié)果,也需要你每次從頭開始構(gòu)建大部分組件楚殿。

這就是為什么人們普遍使用諸如 Quantopian 這樣的回測平臺來進(jìn)行回溯測試。Quantopian 是一個免費的竿痰、以社區(qū)為中心的托管平臺脆粥,用于構(gòu)建和執(zhí)行交易策略。它由 zipline 驅(qū)動 影涉,這是一個用于算法交易的Python庫变隔。你可以在本地使用該庫,但是在這篇初學(xué)者教程中蟹倾,你將使用 Quantopian 來編寫并回測你的算法匣缘。不過在使用它之前,確保你已經(jīng)注冊并登錄了該平臺鲜棠。

接下來肌厨,你就可以很輕松地開始了。點擊“新算法”按鈕來開始編寫你的交易算法豁陆,或者選擇已經(jīng)編寫好的實例代碼之一柑爸,以便更好地了解其使用方法。

讓我們從簡單的開始盒音,來實現(xiàn)一個新算法表鳍,但仍然延續(xù)移動均線交叉策略的例子馅而,這也是zipline 快速入門指南中的標(biāo)準(zhǔn)案例。碰巧這個例子非常類似于你在前一節(jié)中實現(xiàn)的簡單交易策略譬圣。不過瓮恭,如你所見,下方的代碼塊和上方的屏幕截圖的結(jié)構(gòu)與本教程之前所示有所不同厘熟,也就是說偎血,從 initialize()handle_data() 這兩個函數(shù)定義開始。

def initialize(context):
    context.sym = symbol('AAPL')
    context.i = 0


def handle_data(context, data):
    # Skip first 300 days to get full windows
    context.i += 1
    if context.i < 300:
        return

    # Compute averages
    # history() has to be called with the same params
    # from above and returns a pandas dataframe.
    short_mavg = data.history(context.sym, 'price', 100, '1d').mean()
    long_mavg = data.history(context.sym, 'price', 300, '1d').mean()

    # Trading logic
    if short_mavg > long_mavg:
        # order_target orders as many shares as needed to
        # achieve the desired number of shares.
        order_target(context.sym, 100)
    elif short_mavg < long_mavg:
        order_target(context.sym, 0)

    # Save values for later inspection
    record(AAPL=data.current(context.sym, "price"),
           short_mavg=short_mavg,
           long_mavg=long_mavg)

當(dāng)程序開始運行并執(zhí)行一次性的啟動邏輯時盯漂,調(diào)用第一個函數(shù) initialize()颇玷。其中的 context 參數(shù),用于存儲回溯測試或在線交易中的狀態(tài)就缆,并且可以在算法的不同地方被調(diào)用帖渠;如接下來的代碼所示,在第一個移動均值窗口的定義中竭宰,context 參數(shù)又出現(xiàn)了空郊。通過有價證券(如股票)的符號(如AAPL)將其查詢結(jié)果賦值給 context.security

在模擬或在線交易中切揭,每分鐘調(diào)用一次 handle_data() 函數(shù)狞甚,以確定進(jìn)行何種交易操作。該函數(shù)包含 contextdata 兩項參數(shù):context 和剛才所說的一樣廓旬,data 對象包含多種API函數(shù)哼审,比如 current() 函數(shù)用于獲取給定資產(chǎn)給定字段的最新數(shù)據(jù),history() 函數(shù)用于獲取歷史價格和成交量的移動窗口數(shù)據(jù)孕豹。這些API函數(shù)超出了本教程的范圍涩盾,因此沒有在代碼中顯示出來。

注意輸入Quantopian控制臺的代碼只能在該平臺中工作励背,不能用于像Jupyter Notebook這樣的本地程序中春霍。

data 對象使你能夠獲取向前填充的 price 價格,如果有的話返回最后一個已知的價格叶眉,否則返回 NaN 空值址儒。

order_target() 下訂單來調(diào)整目標(biāo)股份數(shù)量。如果資產(chǎn)中沒有頭寸衅疙,用完整的目標(biāo)數(shù)下訂單莲趣。如果資產(chǎn)中有頭寸,用目標(biāo)股份數(shù)和當(dāng)下持有量的差值下訂單炼蛤。負(fù)目標(biāo)訂單將導(dǎo)致特定負(fù)數(shù)的欠缺頭寸妖爷。

提示: 如果對函數(shù)或?qū)ο笥腥魏纹渌鼏栴},請查看 Quantopian 的幫助頁面, 它涵蓋的內(nèi)容比本教程多得多。

當(dāng)你在界面左手邊的控制臺中使用 initialize()handle_data() 函數(shù)創(chuàng)建了策略(或者粘貼-復(fù)制上述代碼)絮识,然后只要點擊 “Build Algorithm” 按鈕即可編譯代碼來運行回溯測試绿聘。如果點擊 “Run Full Backtest” 按鈕,就會運行一項完全的回溯測試次舌,基本上和之前的 “Build Algorithm” 相同熄攘,只是返回更多的細(xì)節(jié)。無論是簡單還是完全的回溯測試彼念,都需要運行一段時間挪圾,一定要注意頁面頂部的進(jìn)度條!

這里能獲取更多 Quantopian 的入門知識逐沙。

注意 Quantopian 可以讓你輕松上手 zipline哲思,但是你總可以在本地(比如 Jupyter Notebook中)繼續(xù)使用該庫。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吩案,一起剝皮案震驚了整個濱河市棚赔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌徘郭,老刑警劉巖靠益,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異残揉,居然都是意外死亡胧后,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門抱环,熙熙樓的掌柜王于貴愁眉苦臉地迎上來壳快,“玉大人,你說我怎么就攤上這事江醇”舯铮” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵陶夜,是天一觀的道長。 經(jīng)常有香客問我裆站,道長条辟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任宏胯,我火速辦了婚禮羽嫡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肩袍。我一直安慰自己杭棵,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著魂爪,像睡著了一般先舷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上滓侍,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天蒋川,我揣著相機與錄音,去河邊找鬼撩笆。 笑死捺球,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的夕冲。 我是一名探鬼主播氮兵,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼歹鱼!你這毒婦竟也來了泣栈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤醉冤,失蹤者是張志新(化名)和其女友劉穎秩霍,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚁阳,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡铃绒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了螺捐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颠悬。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖定血,靈堂內(nèi)的尸體忽然破棺而出赔癌,到底是詐尸還是另有隱情,我是刑警寧澤澜沟,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布灾票,位于F島的核電站,受9級特大地震影響茫虽,放射性物質(zhì)發(fā)生泄漏刊苍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一濒析、第九天 我趴在偏房一處隱蔽的房頂上張望正什。 院中可真熱鬧,春花似錦号杏、人聲如沸婴氮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽主经。三九已至荣暮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旨怠,已是汗流浹背渠驼。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鉴腻,地道東北人迷扇。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像爽哎,于是被迫代替她去往敵國和親蜓席。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容