過去幾天我們分別學習了基本均線指標來交易以及使用 Backtrader
來做回測,今天我們將綜合學習動量策略、均值回歸策略,以及利用技術指標(如 RSI 和 MACD)開發交易策略,並且會使用 Backtrader
框架進行策略的回測與優化。我們將從這些策略的數學基礎開始,逐步完成策略的開發與優化。今日 Colab
動量策略基於這樣的假設:價格在一段時間內將會繼續沿著目前的趨勢運行,即 強者恆強,弱者恆弱
。在統計學上,這代表資產價格呈現出自相關性。
動量公式:
其中,P_t
是第 t
天的收盤價,P_{t-N}
是 N
天前的收盤價。動量策略根據該值的正負判斷買入或賣出信號。
import backtrader as bt
import yfinance as yf
import pandas as pd
import numpy as np
# 使用 yfinance 下載 AAPL 的數據
data = yf.download('AAPL', start='2020-01-01', end='2021-01-01')
# 將數據轉換為 backtrader 可以使用的格式
data_bt = bt.feeds.PandasData(dataname=data)
# 建立動量策略類別
class MomentumStrategy(bt.Strategy):
params = (('period', 10),)
def __init__(self):
self.momentum = bt.indicators.Momentum(self.data.close, period=self.params.period)
self.starting_value = self.broker.getvalue()
self.daily_values = [] # 用於儲存每日資產價值
def next(self):
if not self.position: # 如果沒有持倉
if self.momentum[0] > 0: # 動量指標大於0,表示上升趨勢
self.buy()
elif self.momentum[0] <= 0: # 動量指標小於等於0,表示下降趨勢
self.sell()
# 每次更新時記錄當天的資產價值
self.daily_values.append(self.broker.getvalue())
# 設定回測環境
cerebro = bt.Cerebro()
cerebro.addstrategy(MomentumStrategy)
# 添加數據到 backtrader
cerebro.adddata(data_bt)
# 設定初始資金
cerebro.broker.setcash(100000)
# 執行回測
print(f'初始資金: {cerebro.broker.getvalue():.2f}')
results = cerebro.run()
print(f'回測後的資金: {cerebro.broker.getvalue():.2f}')
# 取得策略結果
strategy = results[0]
# 計算每日收益率
portfolio_values = pd.Series(strategy.daily_values)
returns = portfolio_values.pct_change().dropna()
# 計算年化夏普比率
annual_return = returns.mean() * 252
annual_volatility = returns.std() * np.sqrt(252)
risk_free_rate = 0.01 # 無風險利率設為1%
sharpe_ratio = (annual_return - risk_free_rate) / annual_volatility
# 計算最大回撤
cumulative_returns = (1 + returns).cumprod()
drawdown = cumulative_returns / cumulative_returns.cummax() - 1
max_drawdown = drawdown.min()
print(f'夏普比率: {sharpe_ratio:.2f}')
print(f'最大回撤: {max_drawdown:.2%}')
# 繪製圖表
## 如果在 local machine python terminal 之上:
# cerebro.plot()
## 如果在 Colab 之上:
#https://www.roelpeters.be/how-to-use-backtrader-plots-in-python-notebooks/
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = [15, 12]
plt.rcParams.update({'font.size': 12})
img = cerebro.plot(iplot = False)
img[0][0].savefig('backtrader_momentum.png')
可以得到下圖:
均值回歸策略基於這樣一種假設:價格圍繞某個均值上下波動,當價格偏離均值過多時,具有回歸的趨勢。在統計學上,這意味著價格的時間序列具有均值回歸性質。
Z-score 公式:
其中,P_t
是當前價格,\mu_t
是移動平均值,\sigma_t
是移動標準差。當 Z-score 大於某個閾值時,產生賣出信號;當 Z-score 小於某個閾值時,產生買入信號。
#均值回歸策略
class MeanReversionStrategy(bt.Strategy):
params = (('period', 20), ('zscore_entry', 1), ('zscore_exit', 0))
def __init__(self):
self.moving_avg = bt.indicators.SimpleMovingAverage(self.data.close, period=self.params.period)
self.stddev = bt.indicators.StandardDeviation(self.data.close, period=self.params.period)
self.zscore = (self.data.close - self.moving_avg) / self.stddev
self.daily_values = [] # 用於儲存每日資產價值
def next(self):
if self.zscore[0] < -self.params.zscore_entry and not self.position:
self.buy()
elif self.zscore[0] > self.params.zscore_exit and self.position:
self.sell()
# 每次更新時記錄當天的資產價值
self.daily_values.append(self.broker.getvalue())
# 設定回測環境
cerebro = bt.Cerebro()
cerebro.addstrategy(MeanReversionStrategy)
請將上述 Startegy 改至程式中,或者直接執行 Colab 相關章節可以得到下面圖形:
相對強弱指標(RSI)衡量價格變動的速度和幅度,計算公式為:
其中,( RS ) 為平均上漲收益與平均下跌收益之比。通常,RSI 超過 70 表示超買,低於 30 表示超賣。
使用 Backtrader 實現 RSI 策略:RSI
因為算是常用指標所以可以直接使用 bt.indicators.RelativeStrengthIndex
得到
#RSI策略
class RSIStrategy(bt.Strategy):
params = (('rsi_period', 14), ('rsi_overbought', 70), ('rsi_oversold', 30))
def __init__(self):
self.rsi = bt.indicators.RelativeStrengthIndex(self.data.close, period=self.params.rsi_period)
self.daily_values = [] # 用於儲存每日資產價值
def next(self):
if self.rsi < self.params.rsi_oversold and not self.position:
self.buy()
elif self.rsi > self.params.rsi_overbought and self.position:
self.sell()
# 每次更新時記錄當天的資產價值
self.daily_values.append(self.broker.getvalue())
cerebro = bt.Cerebro()
cerebro.addstrategy(RSIStrategy)
請將上述 Startegy 改至程式中,或者直接執行 Colab 相關章節可以得到下面圖形:
MACD 是利用兩條不同周期的指數移動平均線(EMA)的差值來衡量價格動量,計算公式為:x
與 y
分別為不同長度的時間。其中,例如常見的訊號線為 MACD
的 9 日 EMA。當 MACD
上穿訊號線(Signal
)時,產生買入信號;下穿時,產生賣出信號。
使用 Backtrader 實現 MACD 策略:MACD
一樣為常見訊號, 可以透過 bt.indicators.MACD
去取得 MACD
以及 signal
訊號
#MACD策略
class MACDStrategy(bt.Strategy):
def __init__(self):
self.macd = bt.indicators.MACD(self.data.close)
self.signal = self.macd.signal
self.daily_values = [] # 用於儲存每日資產價值
def next(self):
if self.macd.macd[0] > self.signal[0] and not self.position:
self.buy()
elif self.macd.macd[0] < self.signal[0] and self.position:
self.sell()
# 每次更新時記錄當天的資產價值
self.daily_values.append(self.broker.getvalue())
cerebro = bt.Cerebro()
cerebro.addstrategy(MACDStrategy)
請將上述 Startegy 改至程式中,或者直接執行 Colab 相關章節可以得到下面圖形:
使用 Backtrader
可以對策略參數進行優化,以找到最佳的參數組合,而我們就需要用到 cerebro.optstrategy
。例如,可以對 RSI 策略進行優化,尋找最適合的 RSI 週期和超買、超賣閾值。
對 RSI 策略進行參數優化:
將程式中對應的地方的修改如下即可:
cerebro = bt.Cerebro()
cerebro.addstrategy(RSIStrategy)
cerebro.optstrategy(RSIStrategy, rsi_period=range(10, 21), rsi_overbought=range(65, 76), rsi_oversold=range(25, 36))
cerebro.adddata(data)
cerebro.broker.setcash(100000)
cerebro.run()
第6天任務:
Backtrader
實現並優化自己的策略。