先日、保有していた日立の株がMACDでデッドクロスを出していたのに、子供の寝かしつけをしている間に気づかず、翌朝には-3%になっていました。。。「もう目視でチャートを追うのは無理だ」と感じて、PythonでMACDとRSIの複合シグナルを自動生成する仕組みを作ることにしました。
なぜMACDとRSIを組み合わせるのか
テクニカル指標を一つだけ使うと「だまし」が多いという話を聞いたことがあるでしょうか。MACDは中期トレンドを捉えるのが得意ですが、RSIと合わせることで「トレンドが出ていてかつ売られすぎ・買われすぎ」という条件を絞り込めます。
単純なMACDゴールデンクロスだけで仕掛けたBOTを一度動かしたことがあるんですが、バックテストでは良かったのに実際の相場では横ばい相場で何度もだまし食らいました笑。そこで「RSIが40以下のときだけMACD買いシグナルを有効にする」という条件を加えたところ、だましが減りました。
実装に必要なライブラリのインストール
pip install yfinance pandas-ta matplotlib
pandas-taはテクニカル指標を一発で計算してくれるライブラリです。自分でMACDの計算式を書く必要がないのでとても便利です。
PythonでMACD×RSI複合シグナルを生成するコード
import yfinance as yf
import pandas_ta as ta
import pandas as pd
from datetime import datetime, timedelta
def get_signals(ticker: str, period_days: int = 365) -> pd.DataFrame:
"""
指定銘柄のMACD×RSI複合シグナルを生成する
ticker: 銘柄コード(例:'7203.T' = トヨタ)
"""
end = datetime.today()
start = end - timedelta(days=period_days)
# 株価データ取得
df = yf.download(ticker, start=start, end=end, progress=False)
if df.empty:
print(f"{ticker}: データ取得失敗")
return pd.DataFrame()
close = df["Close"].squeeze()
# MACD計算(デフォルト:短期12, 長期26, シグナル9)
macd_df = ta.macd(close, fast=12, slow=26, signal=9)
df["MACD"] = macd_df["MACD_12_26_9"]
df["MACD_signal"] = macd_df["MACDs_12_26_9"]
df["MACD_hist"] = macd_df["MACDh_12_26_9"]
# RSI計算(期間14日)
df["RSI"] = ta.rsi(close, length=14)
# シグナル生成
# 買いシグナル: MACDがシグナル線をゴールデンクロス かつ RSI < 50
df["prev_MACD"] = df["MACD"].shift(1)
df["prev_signal"] = df["MACD_signal"].shift(1)
df["buy_signal"] = (
(df["prev_MACD"] < df["prev_signal"]) & # 前日: MACD < シグナル
(df["MACD"] > df["MACD_signal"]) & # 本日: MACD > シグナル (ゴールデンクロス)
(df["RSI"] < 50) # RSIが50以下(過熱していない)
)
# 売りシグナル: MACDがデッドクロス かつ RSI > 55
df["sell_signal"] = (
(df["prev_MACD"] > df["prev_signal"]) & # 前日: MACD > シグナル
(df["MACD"] < df["MACD_signal"]) & # 本日: MACD < シグナル (デッドクロス)
(df["RSI"] > 55) # RSIが55以上
)
return df
# 製造メーカー株でシグナルを確認
tickers = {
"7203.T": "トヨタ自動車",
"6501.T": "日立製作所",
"6503.T": "三菱電機",
"5401.T": "日本製鉄",
}
print("=== 本日のMACD×RSI複合シグナル ===")
for code, name in tickers.items():
df = get_signals(code)
if df.empty:
continue
latest = df.iloc[-1]
if latest["buy_signal"]:
rsi_val = f"{latest['RSI']:.1f}"
print(f"🟢 買いシグナル: {name}({code})RSI={rsi_val}")
elif latest["sell_signal"]:
rsi_val = f"{latest['RSI']:.1f}"
print(f"🔴 売りシグナル: {name}({code})RSI={rsi_val}")
else:
print(f"⬜ シグナルなし: {name}({code})")
シグナルをLINE Notifyで自動通知する
シグナルが出たときだけ通知が来るようにすれば、常にチャートを見ている必要がなくなります。LINE Notifyのトークンを取得して以下のように組み合わせます。
import requests
def notify_line(message: str, token: str):
"""LINE Notifyでメッセージ送信"""
url = "https://notify-api.line.me/api/notify"
headers = {"Authorization": f"Bearer {token}"}
data = {"message": message}
requests.post(url, headers=headers, data=data)
LINE_TOKEN = "あなたのLINE Notifyトークン"
for code, name in tickers.items():
df = get_signals(code)
if df.empty:
continue
latest = df.iloc[-1]
if latest["buy_signal"]:
notify_line(f"\n🟢 買いシグナル\n{name}({code})\nRSI={latest['RSI']:.1f}", LINE_TOKEN)
elif latest["sell_signal"]:
notify_line(f"\n🔴 売りシグナル\n{name}({code})\nRSI={latest['RSI']:.1f}", LINE_TOKEN)
バックテストでの注意点
このシグナルをバックテストしてみると、製造メーカー株では直近1年でそこそこの精度が出ました。ただし注意点があります。RSIの閾値(ここでは50と55)は銘柄や市場環境によって最適値が変わります。自分の保有銘柄でパラメータを調整してみてください。
まとめ
MACDとRSIを組み合わせた複合シグナルをPythonで自動生成し、LINE通知と組み合わせることで「チャートを常に見ていなくてもシグナルを逃さない」仕組みが作れます。子育て中の僕にとって、これは本当に助かっています。
個人的な感想としては、シグナルの精度より「見逃しゼロにする」ことのほうが最初のゴールとして正解だと思っています。次はこれをスケジューラーで毎日自動実行する仕組みに繋げる予定です。ぜひ試してみてください!

