以前ただMACDだけでトレードのバックテストをする記事を書きましたが、有名なインジケーターの組み合わせだけでで勝てるか検証してみました。
githubにプログラムをupしています。記事を読めば簡単に試せるので良い結果が出せるかいろんな通貨で試してみてください。
利用インジケーター
RSI (Relative Strength Index)
- RSIは、価格の動きの勢いを測定する指標です。
一定期間の値動きから、買われ過ぎ/売られ過ぎを判断します。 - 通常、14日間のデータを用いて計算されます。
- RSIが70を超えると「買われ過ぎ」、30を下回ると「売られ過ぎ」とされます。
ボリンジャーバンド
- トレンドの強弱を判定するのに使用されます。
- 中心の移動平均線と、この平均線の周囲の上下に設定された標準偏差線から成り立っています。
MACD (Moving Average Convergence Divergence)
- 2つの移動平均線(通常は12日と26日の指数平滑移動平均)の差を示します。
- MACDラインがシグナルラインを上回ると「買い」、下回ると「売り」と見なされます。
戦略概説
基本的には、トレンドが出て押し目で入るという方針です。ボリンジャーバンドはトレンドを判定するのにいいですが、トレンド終了判定ができないのでRSIで判定します。また、トレンドの騙しに引っかからないようにMACDで押し目を見てから入ります。
stragegy/macd_rsi_bb.py
class MacdBbRsi(StrategyInterface):
def is_buy(self, df_candle):
buy = False
df_candle = bb(df_candle, timeperiod=60*6)
last_row = df_candle.iloc[-1]
if last_row.bb2p < last_row.low:
rsi_value = rsi(df_candle, timeperiod=60*3)
if rsi_value.values[-1] < 70 and macd_up(df_candle, slow=50, fast=20, smooth=7, limitv=10*0.01):
buy = True
return buy
def is_sell(self, df_candle):
sell = False
df_candle = bb(df_candle, timeperiod=60*6)
last_row = df_candle.iloc[-1]
if last_row.bb2m > last_row.high:
rsi_value = rsi(df_candle, timeperiod=60*3)
if rsi_value.values[-1] > 30 and macd_dw(df_candle, slow=50, fast=20, smooth=7, limitv=10*0.01):
sell = True
return sell
ここでは1分足を使っています。
ボリンジャーバンドは2σにしています。標準偏差計算に使う間隔は60×6の6時間です。これはグラフを眺めて6時間くらいのレンジからの抜けが取れたらいいかなということでこの値にしました。
RSIは過去の値動きから売られすぎ、買われすぎを判定する指標です。ボリンジャーバンドの標準偏差の間隔以下くらいがいいかなと思って、とりあえず60×3の3時間にしました。
MACDはトレンド中の小さな押し目に反応させるために、slow=50, fast=20, smooth=7と1時間以内の押し目判定するようにしています。
プログラム解説
プログラム全容はgithubにアップしているのでそちらをご確認ください。
環境構築方法はこちらをご確認ください。
import abc
class StrategyInterface(metaclass=abc.ABCMeta):
@abc.abstractmethod
def is_buy(self, df_candle):
raise NotImplementedError()
@abc.abstractmethod
def is_sell(self, df_candle):
raise NotImplementedError()
バックテスト実行プログラムはこの記事のプログラムをベースにmacd_dw()/macd_up()をこのクラスに置き換えています。これはオブジェクト指向GoFのデザインパターンのstragetyパターンを採用し、トレード戦略をいろいろ簡単に試せるように、戦略と、バックテスト実行を切り分けています。
MacdBbRsiクラスはStrategyInterfaceクラスの拡張クラスになります。
このStrategyInterfaceクラスの拡張クラスで簡単に実際のトレードができるプログラムも準備中ですので、現時点ではバックテストで成績が残せるように頑張ってみてください。
trading_test.py main()
# stratgy = SimpleMacd()
stratgy = MacdBbRsi()
↑
stragetyにセットするクラスを切り替えることで戦略を切り替えることができる。
利確するpipsは30pips、損切りするpipsは15pipsにしました。
trading_test.py main()
getpips = 30
losspips = 15
macdのゴールデンクロス、デッドクロスの判定
analyzer/indicator.py
def get_macd(df_candle, slow=26, fast=12, smooth=9):
'''
macd を得る
col:macd, signal, hist
macd:macdライン、slow(長期EMA)とfast(短期EMA)の指数移動平均の差
signal:macdシグナル、MACDのEMA
hist:macdラインと、macdシグナルの差
ゴールデンクロス:買い:signalをmacdがクロスして、macdが下から上に抜ける
デッドデンクロス:売り:signalをmacdがクロスして、macdが上から下に抜ける
追従買い:ゴールデンクロスの後、macdがマイナスからプラスに転換
追従売り:デッドクロスの後、macdがプラスからマイナスに転換
:param df_candle:
:param slow:
:param fast:
:param smooth:
:return:
'''
price = df_candle.close
exp1 = price.ewm(span=fast, adjust=False).mean()
exp2 = price.ewm(span=slow, adjust=False).mean()
macd = exp1 - exp2
signal = macd.ewm(span=smooth, adjust=False).mean()
hist = macd - signal
df = pd.concat([macd, signal, hist], axis=1)
df.columns = ['macd', 'signal', 'hist']
return df
def macd_dw(df_candle, slow=26, fast=12, smooth=9, limitv=None):
if 'macd' not in df_candle.columns:
df_macd = get_macd(df_candle, slow=slow, fast=fast, smooth=smooth)
else:
df_macd = df_candle
df_up = df_macd[df_macd.signal < df_macd.macd]
last_up = df_up.index[-1]
df_dw = df_macd.loc[last_up:]
if 4 > len(df_dw) > 1:
df_dw = df_dw.iloc[1:]
if (df_dw.signal > df_dw.macd).all():
if limitv is None or (df_candle.iloc[-1*smooth:].high.max() - df_candle.iloc[-1].close) < limitv:
print("MACD DW")
return True
return False
def macd_up(df_candle, slow=26, fast=12, smooth=9, limitv=None):
if 'macd' not in df_candle.columns:
df_macd = get_macd(df_candle, slow=slow, fast=fast, smooth=smooth)
else:
df_macd = df_candle
df_dw = df_macd[df_macd.signal > df_macd.macd]
last_dw = df_dw.index[-1]
df_up = df_macd.loc[last_dw:]
if 4 > len(df_up) > 1:
df_up = df_up.iloc[1:]
if (df_up.signal < df_up.macd).all():
if limitv is None or (df_candle.iloc[-1].close - df_candle.iloc[-1*smooth:].low.min()) < limitv:
print("MACD UP")
return True
return False
macd_dw() macd_up()でゴールデンクロス、デッドクロスを判定します。また行き過ぎ防止のため、損切り幅の15pipsより短い、10pipsを山谷の切り替え地点から行き過ぎている場合は判定をFalseにします。
実行結果
USDJPYの2023-12-05~2024-01-04 11時のMT5から吸い出したデータで実行した結果
利益が420 pis、損失が285pipsで、合計利益は135pipsでした。
勝率は14勝19敗でした。勝率はよくありませんが、33回トレードした結果135pips利益が出ているので、ノイズではなくそこそこ統計的に安定した結果のようにも思います。
streamlitを使って結果を確認してみます。streamlitでの確認方法はこちらの記事をご確認ください。
青がBUYのGET、パープルがBUYのLOSS、黄色がSELLのGET、緑がSELLのLOSSです。
グラフを見ると、売りの反応が悪いようです。下に落ちるのが速くて、macdが反応するころにはRSIが売られすぎ反応が出ているのかもしれません。このあたりを調整すればもっと利益が上がるかもしれません。また、トレンドと逆の買いでも利益が出ているので、ファンダメンタルに関係なく利益が出せそうです。ファンダメンタルを気にしなくてよいのが短期トレードの強み。
コメント