本文翻譯自2018年最熱門的Python金融教程 Python For Finance: Algorithmic Trading。
本教程由以下五部分內(nèi)容構(gòu)成:
- Python金融入門
- 常見的金融分析方法
- 簡單的動量策略開發(fā)
- 回溯測試策略
- 評估交易策略
本文是該教程的第三部分庸追。
既然你已對數(shù)據(jù)做了初步分析,那么是時(shí)候創(chuàng)建你的第一個(gè)交易策略了劈愚。但在深入研究之前,為什么不先來了解一些最常用的交易策略呢?下面簡短的介紹琢锋,無疑會讓你更容易開始交易策略的開發(fā)绎秒。
常見的交易策略
你應(yīng)該還記得在本教程開篇的介紹中浦妄,我們講了交易策略是在市場上做多或做空的既定計(jì)劃,但不僅限于此见芹,你還有更多的信息沒有真正掌握剂娄。一般來說,有兩種常見的交易策略:動量策略(momentum strategy)和回歸策略(reversion strategy)玄呛。
第一種是動量策略阅懦,也被稱為背離(divergence)或趨勢(trend)交易。當(dāng)遵循該策略時(shí)把鉴,你相信數(shù)值會沿著當(dāng)前的方向繼續(xù)移動故黑。也就是說你相信股票具有慣性,即有上升或下降的趨勢庭砍,能被你發(fā)現(xiàn)并利用场晶。
這一策略的實(shí)例包括移動均線交叉策略(moving average crossover)、雙均線交叉策略(dual moving average crossover)和海龜交易策略(turtle trading)怠缸。
- 移動均線交叉是指資產(chǎn)的價(jià)格從移動均線的一側(cè)移動到了另一側(cè)诗轻。這一交叉代表了動量的改變,可以作為進(jìn)入或退出市場的決策點(diǎn)揭北。在本教程的后面你會學(xué)到量化交易中使用該策略的入門級案例扳炬。
- 雙均線交叉發(fā)生在短期均線與長期均線交叉處吏颖。這一信號被用來識別在短期均線上的變化。買入信號發(fā)生在短期均線從下方上升超越長期均線時(shí)恨樟,而賣出信號則由短期均線從上方下降越過長期均線而觸發(fā)半醉。
- 海龜交易是一個(gè)眾所周知的趨勢跟蹤策略,最初是由理查德·丹尼斯教授給大家的劝术。它的基本策略是以20天高點(diǎn)買入期貨缩多,并在20天低點(diǎn)賣出。
第二種是回歸策略养晋,也被稱為收斂(convergence)或循環(huán)(cycle)交易衬吆。這一策略相信數(shù)值的運(yùn)動最終會逆轉(zhuǎn)。這可能有點(diǎn)抽象绳泉,讓我們來舉個(gè)例子逊抡。在均值回歸策略(mean reversion strategy)中,你確信股價(jià)會回到它的均值零酪,當(dāng)它偏離均值時(shí)就可以利用這一點(diǎn)來做決策冒嫡。
聽起來已經(jīng)很實(shí)用了,對吧蛾娶?
除了均值回歸策略以外灯谣,另一個(gè)例子是與之相似的配對交易均值回歸(pairs trading mean-reversion)。均值回歸策略的基本思想是股價(jià)會回到其均值蛔琅,而配對交易策略對此進(jìn)行了擴(kuò)展胎许,認(rèn)為如果能識別出兩只高度相關(guān)的股票,當(dāng)其中之一偏離了與另一方的相關(guān)性罗售,兩者價(jià)格差的改變就能作為交易的信號辜窑。這意味著如果這兩只股票的相關(guān)性下降,那么可以做空價(jià)格較高的股票寨躁。高價(jià)格的股票最終會回到其均值穆碎,所以它應(yīng)該被賣出。另一方面职恳,做多低價(jià)格的股票所禀,因?yàn)殡S著相關(guān)性恢復(fù)正常,其價(jià)格將會上升放钦。
除了這兩種最常用的策略色徘,還有其他你偶爾可能會碰上的策略,比如預(yù)測策略操禀,它試圖基于某些歷史因素預(yù)測股票在隨后一段時(shí)間內(nèi)的方向和價(jià)值褂策。還有高頻交易策略(HFT),它利用了亞毫秒市場的微觀結(jié)構(gòu)來做決策。
這都是今后的樂章了斤寂,現(xiàn)在讓我們聚焦于開發(fā)你的第一個(gè)交易策略耿焊。
一個(gè)簡單的交易策略
正如上面所述,你將從量化交易的“hello world”:移動均線交叉策略開始遍搞。你將要開發(fā)的策略很簡單:對時(shí)間序列創(chuàng)建兩個(gè)獨(dú)立的簡單移動均值(Simple Moving Averages, SMA)罗侯,它們具有不同的回望周期,比如40天和100天尾抑。如果短期移動均值超過了長期移動均值歇父,那么就做多;如果長期移動均值超過了短期移動均值再愈,那么就退出。
記住當(dāng)做多時(shí)护戳,你認(rèn)為股價(jià)會上漲翎冲,并且將來會以更高的價(jià)格賣出(=買入信號);當(dāng)賣空時(shí)媳荒,你賣出你的股票抗悍,希望將來以更低的價(jià)格買回并實(shí)現(xiàn)盈利(=賣出信號)。
當(dāng)你剛開始的時(shí)候钳枕,這一簡單的策略可能看起來相當(dāng)復(fù)雜缴渊。但是讓我們一步一步來:
- 首先,定義兩個(gè)不同的回望周期:一個(gè)短期窗口和一個(gè)長期窗口鱼炒。設(shè)置兩個(gè)變量衔沼,并對每一個(gè)變量賦值一個(gè)整數(shù)。一定要讓短期窗口的值小于長期窗口的值昔瞧。
- 接著指蚁,創(chuàng)建一個(gè)空的數(shù)據(jù)框
signals
,并確保復(fù)制了aapl
數(shù)據(jù)的索引自晰,以便能開始計(jì)算aapl
數(shù)據(jù)每日的買賣信號凝化。
- 在空的
signals
數(shù)據(jù)框中創(chuàng)建一列名為signal
的數(shù)據(jù),并將其每一行的數(shù)值初始化為0.0
酬荞。
- 做好了準(zhǔn)備工作后搓劫,是時(shí)候在各自的時(shí)間窗口上創(chuàng)建短期和長期的簡單移動均值了。使用
rolling()
函數(shù)開始滑動窗口計(jì)算:在該函數(shù)中混巧,設(shè)置window
枪向、min_period
和center
參數(shù)。在實(shí)際中牲剃,窗口大小window
分別設(shè)置為short_window
和long_window
遣疯。min_period
設(shè)為1
,作為窗口中觀測量的最小值。center
的值為False
缠犀,這樣標(biāo)簽就不會設(shè)置在窗口的中心数苫。接著,不要忘了在其后連接mean()
函數(shù)以便能計(jì)算滾動均值辨液。
- 當(dāng)計(jì)算了短期和長期窗口的均值后虐急,你應(yīng)該在短期移動均線與長期移動均線交叉處創(chuàng)建信號,但這僅針對大于最短移動均值窗口的時(shí)期滔迈。在Python中止吁,這對應(yīng)著如下條件不等式:
signals['short_mavg'][short_window:] > signals['long_mavg'][short_window:]
。注意加上[short_window:]
是為了遵守條件“僅針對大于最短移動均值窗口的時(shí)期”燎悍。當(dāng)上述條件不等式為真時(shí)敬惦,signal
列中的初值0.0
將被替換為1.0
。 這樣一個(gè)“信號”就產(chǎn)生了谈山。如果條件不等式為假俄删,將繼續(xù)保留原始值0.0
,也沒有信號產(chǎn)生奏路。使用 NumPy 中的where()
函數(shù)來設(shè)置這一條件不等式畴椰。就像你剛才讀到的那樣,將這一結(jié)果賦值給變量signals['signal'][short_window]
鸽粉,因?yàn)槲覀儍H想針對大于最短移動均值窗口的時(shí)期創(chuàng)建信號斜脂。
- 最后,計(jì)算信號的差值触机,從而生成實(shí)際的交易訂單帚戳。換句話說,在
signals
數(shù)據(jù)框的positions
這一列中威兜,你將能區(qū)分多頭和空頭頭寸销斟,無論是買入或賣出股票。
嘗試下方的代碼:
# 導(dǎo)入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))
# 導(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()
# 輸出`signals`
print(signals)
signal short_mavg long_mavg positions
Date
2006-10-02 0.0 10.694285 10.694285 NaN
2006-10-03 0.0 10.638571 10.638571 0.0
2006-10-04 0.0 10.681905 10.681905 0.0
2006-10-05 0.0 10.683928 10.683928 0.0
2006-10-06 0.0 10.667714 10.667714 0.0
2006-10-09 0.0 10.666667 10.666667 0.0
2006-10-10 0.0 10.649184 10.649184 0.0
2006-10-11 0.0 10.625714 10.625714 0.0
2006-10-12 0.0 10.639683 10.639683 0.0
2006-10-13 0.0 10.647429 10.647429 0.0
2006-10-16 0.0 10.658701 10.658701 0.0
2006-10-17 0.0 10.654881 10.654881 0.0
2006-10-18 0.0 10.654286 10.654286 0.0
2006-10-19 0.0 10.699286 10.699286 0.0
2006-10-20 0.0 10.747429 10.747429 0.0
2006-10-23 0.0 10.803036 10.803036 0.0
2006-10-24 0.0 10.848655 10.848655 0.0
2006-10-25 0.0 10.894206 10.894206 0.0
2006-10-26 0.0 10.938797 10.938797 0.0
2006-10-27 0.0 10.966214 10.966214 0.0
2006-10-30 0.0 10.991088 10.991088 0.0
2006-10-31 0.0 11.017987 11.017987 0.0
2006-11-01 0.0 11.030621 11.030621 0.0
2006-11-02 0.0 11.041131 11.041131 0.0
2006-11-03 0.0 11.046857 11.046857 0.0
2006-11-06 0.0 11.059945 11.059945 0.0
2006-11-07 0.0 11.076296 11.076296 0.0
2006-11-08 0.0 11.101378 11.101378 0.0
2006-11-09 0.0 11.129113 11.129113 0.0
2006-11-10 0.0 11.153952 11.153952 0.0
... ... ... ... ...
2011-11-17 1.0 56.485857 54.946829 0.0
2011-11-18 1.0 56.381000 55.005257 0.0
2011-11-21 1.0 56.259000 55.052886 0.0
2011-11-22 1.0 56.177750 55.100386 0.0
2011-11-23 1.0 56.070536 55.125471 0.0
2011-11-25 1.0 55.974107 55.142343 0.0
2011-11-28 1.0 55.955536 55.169371 0.0
2011-11-29 1.0 55.950536 55.188643 0.0
2011-11-30 1.0 55.985179 55.228929 0.0
2011-12-01 1.0 56.019750 55.277757 0.0
2011-12-02 1.0 56.063786 55.323014 0.0
2011-12-05 1.0 56.146679 55.373357 0.0
2011-12-06 1.0 56.154322 55.410543 0.0
2011-12-07 1.0 56.114322 55.432386 0.0
2011-12-08 1.0 56.073143 55.452114 0.0
2011-12-09 1.0 56.020250 55.461714 0.0
2011-12-12 1.0 55.912536 55.468214 0.0
2011-12-13 1.0 55.801179 55.461800 0.0
2011-12-14 1.0 55.651000 55.435643 0.0
2011-12-15 1.0 55.580715 55.400686 0.0
2011-12-16 1.0 55.529679 55.384157 0.0
2011-12-19 1.0 55.491607 55.370429 0.0
2011-12-20 1.0 55.456536 55.378243 0.0
2011-12-21 1.0 55.451822 55.377814 0.0
2011-12-22 1.0 55.444500 55.391586 0.0
2011-12-23 1.0 55.439643 55.406957 0.0
2011-12-27 0.0 55.445286 55.448614 -1.0
2011-12-28 0.0 55.437643 55.490072 0.0
2011-12-29 0.0 55.468393 55.564229 0.0
2011-12-30 0.0 55.495500 55.608500 0.0
[1323 rows x 4 columns]
不是很難蚂踊,對吧?輸出 signals
數(shù)據(jù)框并檢查其結(jié)果笔宿。這里重要的是要掌握該數(shù)據(jù)框中 positions
和 signal
這兩列的含義犁钟。當(dāng)你繼續(xù)下去時(shí),會發(fā)現(xiàn)這一點(diǎn)變得非常重要泼橘。
當(dāng)你花時(shí)間去理解該交易策略的結(jié)果時(shí)涝动,可以用 Matplotlib 將短期和長期的移動均線,以及買入和賣出的信號炬灭,快速繪制在圖中醋粟。
# 導(dǎo)入`pyplot` 模塊
import matplotlib.pyplot as plt
# 初始化圖形
fig = plt.figure(figsize=(12,8))
# 增加子圖并設(shè)置y軸標(biāo)簽
ax1 = fig.add_subplot(111, ylabel='Price in $')
# 繪制收盤價(jià)曲線
aapl['Close'].plot(ax=ax1, color='r', lw=2.)
# 繪制短期和長期移動均線
signals[['short_mavg', 'long_mavg']].plot(ax=ax1, lw=2.)
# 繪制買入信號
ax1.plot(signals.loc[signals.positions == 1.0].index,
signals.short_mavg[signals.positions == 1.0],
'^', markersize=10, color='m')
# 繪制賣出信號
ax1.plot(signals.loc[signals.positions == -1.0].index,
signals.short_mavg[signals.positions == -1.0],
'v', markersize=10, color='k')
# 顯示做圖
plt.show()
結(jié)果很酷,不是嗎?