【譯】Python 金融:演算法交易 (3)用Python構建交易策略
本文翻譯自2018年最熱門的Python金融教程 ofollow,noindex">Python For Finance: Algorithmic Trading 。
本教程由以下五部分內容構成:
- Python金融入門
- 常見的金融分析方法
- 簡單的動量策略開發
- 回溯測試策略
- 評估交易策略
本文是該教程的第三部分。
既然你已對資料做了初步分析,那麼是時候建立你的第一個交易策略了。但在深入研究之前,為什麼不先來了解一些最常用的交易策略呢?下面簡短的介紹,無疑會讓你更容易開始交易策略的開發。
常見的交易策略
你應該還記得在本教程開篇的介紹中,我們講了交易策略是在市場上做多或做空的既定計劃,但不僅限於此,你還有更多的資訊沒有真正掌握。一般來說,有兩種常見的交易策略:動量策略(momentum strategy)和迴歸策略(reversion strategy)。
第一種是 動量策略 ,也被稱為背離(divergence)或趨勢(trend)交易。當遵循該策略時,你相信數值會沿著當前的方向繼續移動。也就是說你相信股票具有慣性,即有上升或下降的趨勢,能被你發現並利用。
這一策略的例項包括移動均線交叉策略(moving average crossover)、雙均線交叉策略(dual moving average crossover)和海龜交易策略(turtle trading)。
- 移動均線交叉是指資產的價格從移動均線的一側移動到了另一側。這一交叉代表了動量的改變,可以作為進入或退出市場的決策點。在本教程的後面你會學到量化交易中使用該策略的入門級案例。
- 雙均線交叉發生在短期均線與長期均線交叉處。這一訊號被用來識別在短期均線上的變化。買入訊號發生在短期均線從下方上升超越長期均線時,而賣出訊號則由短期均線從上方下降越過長期均線而觸發。
- 海龜交易是一個眾所周知的趨勢跟蹤策略,最初是由理查德·丹尼斯教授給大家的。它的基本策略是以20天高點買入期貨,並在20天低點賣出。
第二種是迴歸策略,也被稱為收斂(convergence)或迴圈(cycle)交易。這一策略相信數值的運動最終會逆轉。這可能有點抽象,讓我們來舉個例子。在均值迴歸策略(mean reversion strategy)中,你確信股價會回到它的均值,當它偏離均值時就可以利用這一點來做決策。
聽起來已經很實用了,對吧?
除了均值迴歸策略以外,另一個例子是與之相似的配對交易均值迴歸(pairs trading mean-reversion)。均值迴歸策略的基本思想是股價會回到其均值,而配對交易策略對此進行了擴充套件,認為如果能識別出兩隻高度相關的股票,當其中之一偏離了與另一方的相關性,兩者價格差的改變就能作為交易的訊號。這意味著如果這兩隻股票的相關性下降,那麼可以做空價格較高的股票。高價格的股票最終會回到其均值,所以它應該被賣出。另一方面,做多低價格的股票,因為隨著相關性恢復正常,其價格將會上升。
除了這兩種最常用的策略,還有其他你偶爾可能會碰上的策略,比如預測策略,它試圖基於某些歷史因素預測股票在隨後一段時間內的方向和價值。還有高頻交易策略(HFT),它利用了亞毫秒市場的微觀結構來做決策。
這都是今後的樂章了,現在讓我們聚焦於開發你的第一個交易策略。
一個簡單的交易策略
正如上面所述,你將從量化交易的“hello world”:移動均線交叉策略開始。你將要開發的策略很簡單:對時間序列建立兩個獨立的簡單移動均值(Simple Moving Averages, SMA),它們具有不同的回望週期,比如40天和100天。如果短期移動均值超過了長期移動均值,那麼就做多;如果長期移動均值超過了短期移動均值,那麼就退出。
記住當做多時,你認為股價會上漲,並且將來會以更高的價格賣出(=買入訊號);當賣空時,你賣出你的股票,希望將來以更低的價格買回並實現盈利(=賣出訊號)。
當你剛開始的時候,這一簡單的策略可能看起來相當複雜。但是讓我們一步一步來:
- 首先,定義兩個不同的回望週期:一個短期視窗和一個長期視窗。設定兩個變數,並對每一個變數賦值一個整數。一定要讓短期視窗的值小於長期視窗的值。
- 接著,建立一個空的資料框
signals
,並確保複製了aapl
資料的索引,以便能開始計算aapl
資料每日的買賣訊號。
- 在空的
signals
資料框中建立一列名為signal
的資料,並將其每一行的數值初始化為0.0
。
- 做好了準備工作後,是時候在各自的時間視窗上建立短期和長期的簡單移動均值了。使用
rolling()
函式開始滑動視窗計算:在該函式中,設定window
、min_period
和center
引數。在實際中,視窗大小window
分別設定為short_window
和long_window
。min_period
設為1
,作為視窗中觀測量的最小值。center
的值為False
,這樣標籤就不會設定在視窗的中心。接著,不要忘了在其後連線mean()
函式以便能計算滾動均值。
- 當計算了短期和長期視窗的均值後,你應該在短期移動均線與長期移動均線交叉處建立訊號,但這僅針對大於最短移動均值視窗的時期。在Python中,這對應著如下條件不等式:
signals['short_mavg'][short_window:] > signals['long_mavg'][short_window:]
。注意加上[short_window:]
是為了遵守條件“僅針對大於最短移動均值視窗的時期”。當上述條件不等式為真時,signal
列中的初值0.0
將被替換為1.0
。 這樣一個“訊號”就產生了。如果條件不等式為假,將繼續保留原始值0.0
,也沒有信號產生。使用 NumPy 中的where()
函式來設定這一條件不等式。就像你剛才讀到的那樣,將這一結果賦值給變數signals['signal'][short_window]
,因為我們僅想針對大於最短移動均值視窗的時期建立訊號。
- 最後,計算訊號的差值,從而生成實際的交易訂單。換句話說,在
signals
資料框的positions
這一列中,你將能區分多頭和空頭頭寸,無論是買入或賣出股票。
嘗試下方的程式碼:
# 匯入apple公司股票資料 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))
# 匯入pandas,numpy import pandas as pd import numpy as np # 初始化短期和長期視窗 short_window = 40 long_window = 100 # 初始化 `signals` 資料庫,增加 `signal` 列 signals = pd.DataFrame(index=aapl.index) signals['signal'] = 0.0 # 建立短期簡單移動均值 signals['short_mavg'] = aapl['Close'].rolling(window=short_window, min_periods=1, center=False).mean() # 建立長期簡單移動均值 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)
signalshort_mavglong_mavgpositions Date 2006-10-020.010.69428510.694285NaN 2006-10-030.010.63857110.6385710.0 2006-10-040.010.68190510.6819050.0 2006-10-050.010.68392810.6839280.0 2006-10-060.010.66771410.6677140.0 2006-10-090.010.66666710.6666670.0 2006-10-100.010.64918410.6491840.0 2006-10-110.010.62571410.6257140.0 2006-10-120.010.63968310.6396830.0 2006-10-130.010.64742910.6474290.0 2006-10-160.010.65870110.6587010.0 2006-10-170.010.65488110.6548810.0 2006-10-180.010.65428610.6542860.0 2006-10-190.010.69928610.6992860.0 2006-10-200.010.74742910.7474290.0 2006-10-230.010.80303610.8030360.0 2006-10-240.010.84865510.8486550.0 2006-10-250.010.89420610.8942060.0 2006-10-260.010.93879710.9387970.0 2006-10-270.010.96621410.9662140.0 2006-10-300.010.99108810.9910880.0 2006-10-310.011.01798711.0179870.0 2006-11-010.011.03062111.0306210.0 2006-11-020.011.04113111.0411310.0 2006-11-030.011.04685711.0468570.0 2006-11-060.011.05994511.0599450.0 2006-11-070.011.07629611.0762960.0 2006-11-080.011.10137811.1013780.0 2006-11-090.011.12911311.1291130.0 2006-11-100.011.15395211.1539520.0 ............... 2011-11-171.056.48585754.9468290.0 2011-11-181.056.38100055.0052570.0 2011-11-211.056.25900055.0528860.0 2011-11-221.056.17775055.1003860.0 2011-11-231.056.07053655.1254710.0 2011-11-251.055.97410755.1423430.0 2011-11-281.055.95553655.1693710.0 2011-11-291.055.95053655.1886430.0 2011-11-301.055.98517955.2289290.0 2011-12-011.056.01975055.2777570.0 2011-12-021.056.06378655.3230140.0 2011-12-051.056.14667955.3733570.0 2011-12-061.056.15432255.4105430.0 2011-12-071.056.11432255.4323860.0 2011-12-081.056.07314355.4521140.0 2011-12-091.056.02025055.4617140.0 2011-12-121.055.91253655.4682140.0 2011-12-131.055.80117955.4618000.0 2011-12-141.055.65100055.4356430.0 2011-12-151.055.58071555.4006860.0 2011-12-161.055.52967955.3841570.0 2011-12-191.055.49160755.3704290.0 2011-12-201.055.45653655.3782430.0 2011-12-211.055.45182255.3778140.0 2011-12-221.055.44450055.3915860.0 2011-12-231.055.43964355.4069570.0 2011-12-270.055.44528655.448614-1.0 2011-12-280.055.43764355.4900720.0 2011-12-290.055.46839355.5642290.0 2011-12-300.055.49550055.6085000.0 [1323 rows x 4 columns]
不是很難,對吧?輸出 signals
資料框並檢查其結果。這裡重要的是要掌握該資料框中 positions
和 signal
這兩列的含義。當你繼續下去時,會發現這一點變得非常重要。
當你花時間去理解該交易策略的結果時,可以用 Matplotlib 將短期和長期的移動均線,以及買入和賣出的訊號,快速繪製在圖中。
# 匯入`pyplot` 模組 import matplotlib.pyplot as plt # 初始化圖形 fig = plt.figure(figsize=(12,8)) # 增加子圖並設定y軸標籤 ax1 = fig.add_subplot(111,ylabel='Price in $') # 繪製收盤價曲線 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()

結果很酷,不是嗎?