DAY 7
0
AI & Data

# 量化交易30天 Day7 - RSI交易參數最佳化

1. RSI函數，這個在之前已經寫好了
# RSI函數
def RSI(Close, period=12):
# 整理資料
import pandas as pd
Chg = Close - Close.shift(1)
Chg_pos = pd.Series(index=Chg.index, data=Chg[Chg>0])
Chg_pos = Chg_pos.fillna(0)
Chg_neg = pd.Series(index=Chg.index, data=-Chg[Chg<0])
Chg_neg = Chg_neg.fillna(0)
# 計算12日平均漲跌幅度
import numpy as np
up_mean = []
down_mean = []
for i in range(period+1, len(Chg_pos)+1):
up_mean.append(np.mean(Chg_pos.values[i-period:i]))
down_mean.append(np.mean(Chg_neg.values[i-period:i]))
# 計算 RSI
rsi = []
for i in range(len(up_mean)):
rsi.append( 100 * up_mean[i] / ( up_mean[i] + down_mean[i] ) )
rsi_series = pd.Series(index = Close.index[period:], data = rsi)
return rsi_series
1. RSI訊號函數：參數包含RSI資料，以及指定的買賣點(例如上上一篇的80/30)。
# RSI策略函數
def RSI_Trading_Sig(RSI, upper = 80, lower = 20):
import pandas as pd
# 訊號標籤
sig = []
# 庫存標籤，只會是0或1，表示每次交易都是買進或賣出所有部位
stock = 0
# 偵測RSI訊號
for i in range(len(RSI)):
if RSI[i] > upper and stock == 1:
stock -= 1
sig.append(-1)
elif RSI[i] < lower and stock == 0:
stock += 1
sig.append(1)
else:
sig.append(0)
# 將格式轉成 time series
rsi_sig = pd.Series(index = RSI.index, data = sig)
return rsi_sig
1. 回測函數：參數包含買賣訊號、開盤價資料。
# 每次買賣的報酬率
rets = []
# 是否仍有庫存
stock = 0
# 當次交易買入價格
# 當次交易賣出價格
sell_price = 0
# 每次買賣的報酬率
# 隔日開盤買入
stock += 1
# 隔日開盤賣出
stock -= 1
sell_price = 0
# 如果最後手上有庫存，就用回測區間最後一天的開盤價賣掉
if stock == 1 and buy_price != 0 and sell_price == 0:
sell_price = Open_Price[-1]
# 總報酬率
total_ret = 1
for ret in rets:
total_ret *= 1 + ret

# 串接API取資料
import os
SPY = pdr.get_data_tiingo('SPY', api_key='6af47abd76fbc371d5606fca0694502b866c7bcf')
SPY = SPY.reset_index(level=[0,1])
SPY.index = SPY['date']

# 篩選2019年開收盤價資料

# 參數最佳化 No1，所有參數皆可調整
max_total_ret, max_period, max_upper, max_lower = 0, 0, 0, 0
for period in range(6,25):
for upper in range(70,91):
for lower in range(10,31):
ret = RSI_backtest(RSI_Trading_Sig(RSI(Close2019, period), upper, lower), Open2019)
if ret > max_total_ret:
max_total_ret, max_period, max_upper, max_lower = ret, period, upper, lower

# 將求出來的結果印出參數及圖看看
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from matplotlib import gridspec

x = RSI(Close2019, max_period).index
y = RSI(Close2019, max_period).values

fig = plt.figure(figsize=(15,10))
# set height ratios for sublots
gs = gridspec.GridSpec(2, 1, height_ratios=[2, 1])

# the fisrt subplot
ax0 = plt.subplot(gs[0])
# line0 = ax0.plot(x, y, color='r')
ax0.plot(RSI(Close2019, max_period))
ax0.axhline(y=max_upper, color='red')
ax0.axhline(y=max_lower, color='green')

#the second subplot
# shared axis X
ax1 = plt.subplot(gs[1], sharex = ax0)
rsi_sig = pd.Series(index = RSI(Close2019, max_period).index, data = list(RSI_Trading_Sig(RSI(Close2019, max_period), max_upper, max_lower).values))
ax1.plot(rsi_sig)

print('總報酬率：' + str(round(100*(max_total_ret-1),2)) + '%')
print('參數：' + 'RSI計算天數: ' + str(period) + ' ,Upper bond: ' + str(max_upper) + ' ,Lower bond: ' + str(max_lower))
plt.show()

1. 以2019年的數據得到的這個參數，在其他年度不會是報酬最好的，在未來也不確定能夠持續有最佳的表現，不過它帶來的是一個概念，讓我們知道RSI可以怎麼用。
2. 這次是只回測2019年的資料，或許同一個標的而言，多回測幾個年份，有可能抓出它大概適合用什麼樣的參數來交易。
3. 這篇將三個參數同時做最佳化，但其實也可以一次只最佳化一個參數，例如：直接固定Upper bond與Lower bond，指定一組比較可能出現的數字(例如：70/40)，去取代這次回測的90/24這種比較極限的數字，然後只調整RSI天數，去實驗看看哪個天數比較有效果。