上一篇文章中,帶大家寫了一個簡單的策略,
然而,在現實生活中並沒有這麼管用,20年才賺三倍!?
所以這篇文章將帶介紹如何利用修改參數,來調整策略,進而達到更好的績效
但是人工調整參數很浪費時間,所以我們先使用簡單暴力法,來調整參數試試看。
先回顧上次的策略
由於這是系列文章,要完成到上次的步驟其實有點煩瑣,所以這邊就簡單的前情提要一下,總共有三個步驟:
- 下載台股大盤資料
- 編寫台股的sharpe ratio
- 利用sharpe ratio製作回測
這邊就不厭其煩的先把上次的code拿來,方便大家直接複製貼上
1. 下載台股大盤資料
以下這段程式,已經於「全球指數一次抓」講過了,
假如想瞭解的話,可以去爬個文,這邊就不贅述了。
import io
import json
import requests
import numpy as np
import pandas as pd
import datetime
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')
def crawl_price(stock_id):
d = datetime.datetime.now()
url = "https://query1.finance.yahoo.com/v8/finance/chart/"+stock_id+"?period1=0&period2="+str(int(d.timestamp()))+"&interval=1d&events=history&=hP2rOschxO0"
res = requests.get(url)
data = json.loads(res.text)
df = pd.DataFrame(data['chart']['result'][0]['indicators']['quote'][0], index=pd.to_datetime(np.array(data['chart']['result'][0]['timestamp'])*1000*1000*1000))
return df
twii = crawl_price("^TWII")
twii.head()
2. 編寫台股的sharpe ratio
接下來我們就來計算sharpe ratio,這邊同樣於「Python新手教學:風險與報酬」講過了
就請有興趣的大家多多複習囉!
mean = twii['close'].pct_change().rolling(252).mean()
std = twii['close'].pct_change().rolling(252).std()
sharpe = mean / std
twii.close.plot()
sharpe.plot(secondary_y=True)
3. 編寫台股sharpe ratio策略
接下來就是編寫sharpe ratio 的策略了,同樣可以到python新手教學:夏普指數策略
這篇文章中,得到更詳細的解釋
import numpy as np
# sharpe ratio 平滑
sr = sharpe
srsma = sr.rolling(60).mean()
# sharpe ratio 的斜率
srsmadiff = srsma.diff()
# 計算買入賣出點
buy = (srsmadiff > 0) & (srsmadiff.shift() < 0)
sell = (srsmadiff < 0) & (srsmadiff.shift() > 0)
# 計算持有時間
hold = pd.Series(np.nan, index=buy.index)
hold[buy] = 1
hold[sell] = 0
hold.ffill(inplace=True)
hold.plot()
# 持有時候的績效
adj = twii['close'][buy.index]
(adj.pct_change().shift(-1)+1).fillna(1)[hold == 1].cumprod().plot()
別轉台,終於要開始參數最佳化了
我們將上面的程式碼包成一個函示如下
def backtest(a, b, c, d):
sr = sharpe
srsma = sr.rolling(a).mean()
srsmadiff = srsma.diff() * 100
ub = srsmadiff.quantile(b)
lb = srsmadiff.quantile(c)
buy = ((srsmadiff.shift(d) < lb) & (srsmadiff > ub))
sell = ((srsmadiff.shift(d) > ub) & (srsmadiff < lb))
hold = pd.Series(np.nan, index=buy.index)
hold[buy] = 1
hold[sell] = 0
hold.ffill(inplace=True)
adj = twii['close'][buy.index]
# eq = (adj.pct_change().shift(-1)+1).fillna(1)[hold == 1].cumprod().plot()
# hold.plot()
eq = (adj.pct_change().shift(-1)+1).fillna(1)[hold == 1].cumprod()
if len(eq) > 0:
return eq.iloc[-1]
else:
return 1
backtest(252,0.4,0.6,4)
可以發現,這個function傳入了四個參數「a,b,c,d」,
而這四個參數是做什麼的呢?是拿來取代原本的數字的,
可以發現原本的常數部分,都被換成了代數,這樣我們到時候在呼叫時,就可以帶入不同的參數
而我們最後的回傳值,原本是一張圖片,但此function中被改成了這20年的報酬率
所以當我們執行「backtest(252,0.4,0.6,4)」的時候,
這20年的報酬就是9.1%,非常爛
所以我們才需要做參數優化
參數枚舉優化
我們使用暴力法,將所有的可能的參數都找一遍:
maxeq = 0
for a in range(100,200,20):
for b in np.arange(0.3, 0.9, 0.03):
for c in np.arange(0.3, 0.6, 0.03):
for d in range(60, 180, 10):
eq = backtest(a,b,c,d)
if maxeq < eq:
maxeq = eq
print(eq, a,b,c,d)
上面第8行,即是我們執行backtest的結果,
假如我們發現eq,有最高報酬率,
則將新的最高報酬率print出來,並且print它的參數
我們就可以看到數字不斷增加的感覺,滿開心的!
不過上述程式要跑滿久的,請耐心等待,
最後成果滿不錯的,算是一個懶人投資策略,請參考程式碼來獲得最近的報酬率
有興趣的可以到粉絲團按讚,才不會錯過接下來精彩文章喔!