※結果の見方の記述を修正しました (2024/04/26)
前々回(パラボリックSAR)と前回(順位相関指数RCI)の投稿の指標を組み合わせた取引戦略が、ある書籍に載っていました。今回はこれをPythonで実装してみます。
尚、パラボリックSARについてはta-libを用いたコードに変更しました。RCIについては、別のネット記事のコードで計算結果が一致するものを見つけたので、これで正しいということにしておきます。ここではFXを投資対象とします。
まず、投資対象銘柄のリストを与え、両指標の計算関数を定義します。
# ライブラリのインポート import yfinance as yf from datetime import datetime, timedelta from datetime import date import pandas as pd import numpy as np from pandas_datareader import data as pdr import matplotlib.pyplot as plt import mplfinance as mpf import seaborn import mplfinance as mpf import talib as ta # シグナル判定する通貨ペアのリスト list_ticker = ['USDJPY=X', 'EURJPY=X', 'GBPJPY=X', 'AUDJPY=X', 'CADJPY=X', 'NZDJPY=X', 'MXNJPY=X', 'CHFJPY=X', 'NOKJPY=X',\ 'EURUSD=X', 'GBPUSD=X', 'AUDUSD=X', 'NZDUSD=X', 'AUDNZD=X', 'EURGBP=X', 'GBPAUD=X'] # パラボリックSARの戦略関数 def parabolic_sar_strategy(data): # パラボリックSARの計算 data['SAR'] = ta.SAR(data['High'], data['Low'], acceleration=0.02, maximum=0.2) # シグナルの生成 data['Signal'] = 0 # 0 means no signal data.loc[data['Close'] > data['SAR'], 'Signal'] = 1 # Buy signal data.loc[data['Close'] < data['SAR'], 'Signal'] = -1 # Sell signal # ポジションの生成 data['Position'] = data['Signal'].diff() return data # RCIの計算関数 def rci(close, timeperiod=9): rci = np.full_like(close, np.nan) rank_period = np.arange(1, timeperiod + 1) for i in range(timeperiod - 1, len(close)): rank_price = close[i - timeperiod + 1:i + 1] rank_price = np.argsort(np.argsort(rank_price)) + 1 aa = 6 * sum((rank_period - rank_price)**2) bb = timeperiod * (timeperiod**2 - 1) rci[i] = (1 - aa / bb) * 100 return rci
そして、銘柄のリストの要素毎にシグナルを判定して、指標毎に別々のデータフレームに格納します。
# リスト内の銘柄毎にシグナルを判定する df_sar = pd.DataFrame() df_rci = pd.DataFrame() end = datetime.today() start = end - timedelta(days=180) # 6 months before today for ticker in list_ticker: end = datetime.today() start = end - timedelta(days=182) # 6 months before today yf.pdr_override() # yahooサイトからデータをダウンロード data = pdr.get_data_yahoo(ticker, start, end) # パラボリック計算用 df = data.copy() # RCI計算用 # SAR諸指標の計算 df_parab = parabolic_sar_strategy(data) # 順位相関指数(RCI) df['RCI'] = rci(data['Close']) df['ticker'] = ticker df_parab['ticker'] = ticker # 最終行を付け足す df_sar = pd.concat([df_sar,df_parab.tail(1)],axis=0) df_rci = pd.concat([df_rci,df.tail(1)],axis=0)
そして、両指標について、シグナルがあるケースに絞り込んで横連結します。
# SARのシグナルがある行に絞り込む df1 = df_sar.loc[df_sar['Signal']==1] df2 = df_sar.loc[df_sar['Signal']==-1] df3 = pd.concat([df1, df2], axis=0) # RCIがLB以下かUB以上の行に絞り込む df11 = df_rci[df_rci['RCI'] <= -50] df12 = df_rci[df_rci['RCI'] >= 50] df13 = pd.concat([df11, df12], axis=0) # SARとRCIの結果を横連結する df4 = df3.reset_index()[['SAR','Signal','Position','ticker']] df14 = df13.reset_index()[['RCI','ticker']] df20 = pd.merge(df4, df14, on='ticker', how='right') df20
ここで注目するケースは、Positon=2.0 (買いシグナルにチェンジ)でかつRCIが下方バンドを割っているケース、又はPosition=-2.0 (売りシグナルにチェンジ)でかつRCIが上方バンドを超えているケースです。実際にはRCIのバンド絶対値を80にすると該当ケースがほとんどないため、ここでは50に設定しています。またSARがチェンジするケースも出現頻度は少ないです。