在昨天的章節中我們有先窺視了一下策略優化的事,今天,我們將仔細談談這個,我們會學習如何在 Backtrader
中進行多次回測,並找到最佳的策略參數配置,進一步優化我們的策略以提高夏普比率。這將使我們能夠有效地篩選出最適合的參數組合。今日 Colab
首先,讓我們導入本範例所需的庫:
import backtrader as bt
import backtrader.analyzers as btanalyzers
import pandas as pd
from datetime import datetime
import yfinance as yf # 若要下載財經數據,可以使用 yfinance
我們使用之前介紹過得均線交叉策略為範例。參數最佳化本質為表格化去嘗試每個可能的組合並從中找到最佳組合。為了執行參數最佳化,我們需要在策略類別中定義參數,如下:
params = (
('fast_length', 10),
('slow_length', 50)
)
並在指標計算中使用這些參數,像是如下:
ma_fast = bt.ind.SMA(period=self.params.fast_length)
ma_slow = bt.ind.SMA(period=self.params.slow_length)
完整定義如下:
class MaCrossStrategy(bt.Strategy):
params = (
('fast_length', 10),
('slow_length', 50)
)
def __init__(self):
ma_fast = bt.ind.SMA(period=self.params.fast_length)
ma_slow = bt.ind.SMA(period=self.params.slow_length)
self.crossover = bt.ind.CrossOver(ma_fast, ma_slow)
def next(self):
if not self.position:
if self.crossover > 0:
self.buy()
elif self.crossover < 0:
self.close()
optstrategy
方法指定參數範圍cerebro = bt.Cerebro()
# 使用 yfinance 或 Yahoo Finance 資料
data = bt.feeds.YahooFinanceData(dataname='AAPL', fromdate=datetime(2010, 1, 1), todate=datetime(2020, 1, 1))
cerebro.adddata(data)
# 設定策略參數範圍
strats = cerebro.optstrategy(
MaCrossStrategy,
fast_length=range(1, 11),
slow_length=range(25, 76, 5)
)
# 設定初始資金與分析工具
cerebro.broker.setcash(1000000.0)
cerebro.addsizer(bt.sizers.PercentSizer, percents=10)
cerebro.addanalyzer(btanalyzers.SharpeRatio, _name="sharpe", timeframe=bt.TimeFrame.Days, annualize=True)
cerebro.addanalyzer(btanalyzers.DrawDown, _name="drawdown")
cerebro.addanalyzer(btanalyzers.Returns, _name="returns")
# 執行回測
back = cerebro.run(maxcpus=1)
執行優化後,我們需要將結果解析成方便的格式,以便篩選出最佳的策略參數組合。基本上你將優化過程想像成網格去搜尋最佳組合。
# 提取回測結果中需要的參數與分析結果
par_list = []
for run in back:
for strategy in run:
sharpe_ratio = strategy.analyzers.sharpe.get_analysis().get('sharperatio')
returns = strategy.analyzers.returns.get_analysis().get('rnorm100')
drawdown = strategy.analyzers.drawdown.get_analysis().get('max', {}).get('drawdown')
if sharpe_ratio is not None:
par_list.append([
strategy.params.fast_length,
strategy.params.slow_length,
returns,
drawdown,
sharpe_ratio
])
# 檢查是否有有效的結果
if not par_list:
print("無法計算有效的夏普比率,請檢查策略或回測資料。")
else:
# 將結果轉換成 DataFrame
par_df = pd.DataFrame(par_list, columns=['fast_length', 'slow_length', 'return', 'drawdown', 'sharpe'])
print("par_df:")
print(par_df)
print("\n")
# 以夏普比率排序,找到最佳參數組合
best_result = par_df.sort_values(by='sharpe', ascending=False).iloc[0]
print(f'最佳參數組合: 快速均線={best_result["fast_length"]}, 慢速均線={best_result["slow_length"]}')
print(f'對應的夏普比率: {best_result["sharpe"]:.2f}')
可以得到下面結果:
可以看到我們得到的 par_df
本質上就是各種排列組合的結果並且會紀錄其中對應到的觀測指標 (在這裡我們用 Sharpe
),由於整張表是 pandas DataFrame
所以舞們輕而易舉的用排序找到最好的 Sharpe
值以及對應的參數~
通過將回測結果存儲在 DataFrame 中,我們可以方便地對其進行篩選和排序,找到最適合的策略參數組合。這樣,我們就能夠有效地優化策略並提高夏普比率。另外我們也提供動量策略的優化在 Appendix
章節
今天我們學習了如何利用 Backtrader
進行策略參數的優化,並且成功找到了最佳的策略參數組合,進一步提高了策略的夏普比率。
今日作業:
RSI
、MACD
)進行策略優化,可以參考 Appendix
完整程式做修改。透過這些練習,你將能夠更熟練地進行策略優化,並能夠在真實市場中應用所學的量化投資技巧。
在這個附錄中,我們將進行動量策略的參數優化,並尋找出可以提高夏普比率的最佳參數組合。我們將設定動量策略的參數範圍,並使用 Backtrader
進行優化,尋找出最佳的 momentum_period
參數以提高夏普比率。。
class MomentumStrategy(bt.Strategy):
params = (('momentum_period', 10),)
def __init__(self):
# 定義動量指標
self.momentum = bt.indicators.Momentum(self.data.close, period=self.params.momentum_period)
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())
def stop(self):
# 在每次策略完成時,將結果存儲到策略屬性中
self.returns = pd.Series(self.daily_values).pct_change().dropna()
annual_return = self.returns.mean() * 252
annual_volatility = self.returns.std() * np.sqrt(252)
risk_free_rate = 0.01 # 假設無風險利率為1%
self.sharpe_ratio = (annual_return - risk_free_rate) / annual_volatility
# 計算最大回撤
cumulative_returns = (1 + self.returns).cumprod()
drawdown = cumulative_returns / cumulative_returns.cummax() - 1
self.max_drawdown = drawdown.min()
cerebro = bt.Cerebro()
# 使用 yfinance 下載 AAPL 的數據
data = yf.download('AAPL', start='2020-01-01', end='2021-01-01')
# 將數據轉換為 backtrader 可以使用的格式
data_bt = bt.feeds.PandasData(dataname=data)
cerebro.adddata(data_bt)
# 設定策略參數範圍
strats = cerebro.optstrategy(
MomentumStrategy,
momentum_period=range(5, 31, 5) # 設定動量週期從5到30,每5為一個間隔
)
# 設定初始資金與分析工具
cerebro.broker.setcash(1000000.0)
cerebro.addsizer(bt.sizers.PercentSizer, percents=10)
cerebro.addanalyzer(btanalyzers.SharpeRatio, _name="sharpe", timeframe=bt.TimeFrame.Days, annualize=True)
cerebro.addanalyzer(btanalyzers.DrawDown, _name="drawdown")
cerebro.addanalyzer(btanalyzers.Returns, _name="returns")
# 執行回測
back = cerebro.run(maxcpus=1)
# 提取回測結果中需要的參數與分析結果
par_list = []
for run in back:
for strategy in run:
sharpe_ratio = strategy.analyzers.sharpe.get_analysis().get('sharperatio')
returns = strategy.analyzers.returns.get_analysis().get('rnorm100')
drawdown = strategy.analyzers.drawdown.get_analysis().get('max', {}).get('drawdown')
if sharpe_ratio is not None:
par_list.append([
strategy.params.momentum_period,
returns,
drawdown,
sharpe_ratio
])
# 檢查是否有有效的結果
if not par_list:
print("無法計算有效的夏普比率,請檢查策略或回測資料。")
else:
# 將結果轉換成 DataFrame
par_df = pd.DataFrame(par_list, columns=['momentum_period', 'return', 'drawdown', 'sharpe'])
# 以夏普比率排序,找到最佳參數組合
best_result = par_df.sort_values(by='sharpe', ascending=False).iloc[0]
print(f'最佳參數組合: 動量週期={best_result["momentum_period"]}')
print(f'對應的夏普比率: {best_result["sharpe"]:.2f}')