本文翻譯自2018年最熱門的Python金融教程 Python For Finance: Algorithmic Trading寞酿。
本教程由以下五部分內(nèi)容構(gòu)成:
- Python金融入門
- 常見的金融分析方法
- 簡單的動量策略開發(fā)
- 回溯測試策略
- 評估交易策略
本文是該教程的第四部分有额。
既然你手頭已經(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)簡單使用過這個庫了栈暇。
最后麻裁,還有 IbPy 和 ZipLine 庫。前者為 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ù)包含 context
和 data
兩項參數(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ù)使用該庫。