※本記事のコードや情報は執筆時点の仕様に基づいています。投資は自己責任であり、必ずデモ環境や少額資金でテストした上で運用してください。
テクニカル分析の世界で最も使われているオシレーター系指標のひとつが「RSI(Relative Strength Index:相対力指数)」です。
RSIは相場の「買われすぎ」「売られすぎ」を0〜100の数値で可視化できるため、逆張り戦略やエントリー判断の補助として幅広く活用されています。
しかし、RSIの計算ロジックを正しく理解し、Pythonで再現できている人は意外と多くありません。
特に「単純平均(SMA)」と「指数平滑移動平均(EMA/Wilder方式)」の違いを無視すると、TradingViewやSBI証券のチャートツールと数値がズレる原因になります。
この記事では、pandasを使ってRSIをゼロから計算するコードを提示し、計算ロジックの違い・実践的な活用法・よくあるエラーまで網羅的に解説します。
コードはすべてコピペでそのまま動作するものに限定していますので、安心して試してください。
RSIとは何か ― 基本の仕組みを理解する
RSIを正しくコードに落とし込むためには、まず計算式の構造を理解することが不可欠です。
ここでは、RSIの定義と代表的なパラメータについて整理します。
RSIの計算式
RSIは、一定期間における「値上がり幅の平均」と「値下がり幅の平均」の比率から算出されます。
基本的な計算式は以下の通りです。
RSI = 100 - (100 / (1 + RS))
RS = 平均上昇幅 / 平均下落幅
ここでいう「平均」の取り方によって、RSIの数値は変わります。
一般的には以下の2種類が使われます。
| 平均の種類 | 計算方法 | 特徴 |
|---|---|---|
| SMA(単純移動平均) | 直近N日間の上昇幅・下落幅を単純平均 | 計算がシンプルで理解しやすい |
| Wilder方式(指数平滑) | 前日の平均値に重みを加えて平滑化 | TradingView等で標準採用。滑らかな曲線になる |
TradingViewやMetaTrader、国内証券ツールの多くは「Wilder方式」を採用しています。他のツールと数値を一致させたい場合は、必ずWilder方式で計算してください。
RSIの一般的なパラメータと判断基準
RSIのデフォルト期間は14日間です。
これはRSIの考案者であるJ・ウェルズ・ワイルダー(J. Welles Wilder)が推奨した値であり、世界的な標準となっています。
判断基準の目安は以下の通りです。
- RSI ≧ 70:買われすぎゾーン → 反落(売り)のシグナル候補
- RSI ≦ 30:売られすぎゾーン → 反発(買い)のシグナル候補
- RSI = 50 付近:方向感なし(トレンドの中立状態)
ただし、強いトレンド相場ではRSIが70以上・30以下に張り付くことがあるため、RSI単体での売買判断は危険です。
移動平均線やMACDなど、トレンド系指標との併用が推奨されます。
【コピペOK】PythonでRSIを計算する実装コード
ここからは実際のPythonコードを提示します。
株価データの取得には、世界中で利用されているオープンソースライブラリ「yfinance」を使用します。
事前準備:ライブラリのインストール
以下のコマンドで必要なライブラリをインストールしてください。
pip install yfinance pandas matplotlib
- yfinance:Yahoo Financeから株価データを無料取得できるライブラリです
- pandas:データ加工・分析の定番ライブラリです
- matplotlib:グラフ描画用のライブラリです
【コピペOK】SMA方式でRSIを計算するコード
まずはシンプルなSMA(単純移動平均)方式のRSI計算コードです。
RSIの基本構造を理解するのに最適な実装となっています。
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
# ==============================
# 設定エリア
# ==============================
SYMBOL = "7203.T" # トヨタ自動車(東証)
PERIOD = "1y" # 取得期間(1年間)
RSI_WINDOW = 14 # RSI計算期間
# ==============================
# 株価データ取得
# ==============================
def fetch_data(symbol: str, period: str) -> pd.DataFrame:
""yfinanceから株価データを取得します。""
ticker = yf.Ticker(symbol)
df = ticker.history(period=period)
if df.empty:
raise ValueError(f"{symbol} のデータを取得できませんでした。")
return df
# ==============================
# RSI計算(SMA方式)
# ==============================
def calc_rsi_sma(df: pd.DataFrame, window: int = 14) -> pd.Series:
""SMA(単純移動平均)方式でRSIを計算します。""
delta = df["Close"].diff()
gain = delta.where(delta > 0, 0.0)
loss = (-delta).where(delta < 0, 0.0)
avg_gain = gain.rolling(window=window).mean()
avg_loss = loss.rolling(window=window).mean()
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
return rsi
# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
df = fetch_data(SYMBOL, PERIOD)
df["RSI_SMA"] = calc_rsi_sma(df, RSI_WINDOW)
print(f"=== {SYMBOL} RSI(SMA方式・直近5日) ===")
print(df[["Close", "RSI_SMA"]].tail())
# --- グラフ描画 ---
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
ax1.plot(df.index, df["Close"], label="Close Price", color="black")
ax1.set_title(f"{SYMBOL} - Stock Price")
ax1.set_ylabel("Price (JPY)")
ax1.legend()
ax2.plot(df.index, df["RSI_SMA"], label="RSI (SMA)", color="blue")
ax2.axhline(70, color="red", linestyle="--", alpha=0.7, label="Overbought (70)")
ax2.axhline(30, color="green", linestyle="--", alpha=0.7, label="Oversold (30)")
ax2.set_title("RSI (SMA Method)")
ax2.set_ylabel("RSI")
ax2.set_ylim(0, 100)
ax2.legend()
plt.tight_layout()
plt.show()
このコードを実行すると、トヨタ自動車の過去1年分の終値チャートと、その下にRSIのラインチャートが描画されます。
70ライン(赤)と30ライン(緑)の破線も自動表示されるため、過熱感を視覚的に確認できます。
【コピペOK】Wilder方式でRSIを計算するコード
次に、TradingView等と数値を一致させるためのWilder方式(指数平滑移動平均)の実装です。
SMA方式との違いは、rolling().mean() の代わりに ewm() を使う点にあります。
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
# ==============================
# 設定エリア
# ==============================
SYMBOL = "6758.T" # ソニーグループ(東証)
PERIOD = "1y"
RSI_WINDOW = 14
# ==============================
# 株価データ取得
# ==============================
def fetch_data(symbol: str, period: str) -> pd.DataFrame:
ticker = yf.Ticker(symbol)
df = ticker.history(period=period)
if df.empty:
raise ValueError(f"{symbol} のデータを取得できませんでした。")
return df
# ==============================
# RSI計算(Wilder方式)
# ==============================
def calc_rsi_wilder(df: pd.DataFrame, window: int = 14) -> pd.Series:
""Wilder方式(指数平滑移動平均)でRSIを計算します。""
delta = df["Close"].diff()
gain = delta.where(delta > 0, 0.0)
loss = (-delta).where(delta < 0, 0.0)
# Wilder方式: alpha = 1/window の指数平滑移動平均
avg_gain = gain.ewm(alpha=1/window, min_periods=window, adjust=False).mean()
avg_loss = loss.ewm(alpha=1/window, min_periods=window, adjust=False).mean()
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
return rsi
# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
df = fetch_data(SYMBOL, PERIOD)
df["RSI_Wilder"] = calc_rsi_wilder(df, RSI_WINDOW)
print(f"=== {SYMBOL} RSI(Wilder方式・直近5日) ===")
print(df[["Close", "RSI_Wilder"]].tail())
# --- グラフ描画 ---
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
ax1.plot(df.index, df["Close"], label="Close Price", color="black")
ax1.set_title(f"{SYMBOL} - Stock Price")
ax1.set_ylabel("Price (JPY)")
ax1.legend()
ax2.plot(df.index, df["RSI_Wilder"], label="RSI (Wilder)", color="purple")
ax2.axhline(70, color="red", linestyle="--", alpha=0.7, label="Overbought (70)")
ax2.axhline(30, color="green", linestyle="--", alpha=0.7, label="Oversold (30)")
ax2.set_title("RSI (Wilder Method)")
ax2.set_ylabel("RSI")
ax2.set_ylim(0, 100)
ax2.legend()
plt.tight_layout()
plt.show()
Wilder方式では
ewm(alpha=1/window)を使用します。これは「前日の平均値を (window-1)/window の重みで引き継ぐ」動作を再現しており、TradingViewの標準RSIと同等の結果を得られます。
SMA方式とWilder方式の違いを比較する
2つの計算方式の違いを理解しておくことは、他のツールとの検証やバックテストの精度向上に直結します。
数値のズレが発生する理由
SMA方式は直近14日間を「等しい重み」で平均します。
一方、Wilder方式は直近のデータにより大きな重みを置くため、相場の変化に対してより敏感に反応します。
結果として、同じ日の同じ銘柄でも1〜5ポイント程度のズレが生じることがあります。
このズレは特に以下の場面で顕著になります。
- 急騰・急落の直後
- ボラティリティが高い銘柄
- 計算期間を短く設定した場合(RSI期間=7など)
どちらを使うべきか
結論として、特別な理由がない限りWilder方式を推奨します。
理由は以下の通りです。
- TradingView、MetaTrader、国内証券ツールの大半がWilder方式を採用しています
- 他のトレーダーと同じ基準でシグナルを判断できます
- バックテスト結果と実際のチャートの乖離を最小限に抑えられます
SMA方式は「RSIの仕組みを学習する教材」として有用ですが、実運用ではWilder方式を標準としてください。
RSIを活用した実践的な売買シグナルの考え方
RSIの数値を計算できるようになったら、次は実際のトレード判断への応用を考えます。
基本の逆張りシグナル
最もシンプルな使い方は、RSIの閾値を超えたタイミングでエントリーを検討する方法です。
- 買いシグナル:RSIが30以下に落ちた後、30を上抜けた瞬間
- 売りシグナル:RSIが70以上に上がった後、70を下抜けた瞬間
ポイントは「30以下になった瞬間」ではなく、「30を上抜けた瞬間」にエントリーする点です。
これにより、下落トレンドの最中に飛び込む「落ちるナイフ」を回避できます。
ダイバージェンス(逆行現象)の活用
RSIの中でも精度が高いとされるシグナルが「ダイバージェンス」です。
- 強気ダイバージェンス:株価は安値を更新しているのに、RSIは安値を更新していない → 反発の兆候
- 弱気ダイバージェンス:株価は高値を更新しているのに、RSIは高値を更新していない → 反落の兆候
ダイバージェンスは視覚的にも判別しやすく、トレンド転換の初動を捉えるのに有効です。
ただし、自動検出のコード化は複雑になるため、まずは目視で確認する運用から始めてください。
よくあるエラーと対処法
RSIの実装時に初心者がつまずきやすいポイントをまとめます。
yfinanceでデータが取得できない場合はどうすればいいですか?
以下の原因が考えられます。
- ティッカーシンボルの間違い:日本株は末尾に
.Tを付ける必要があります(例:トヨタ →7203.T) - ネットワークエラー:インターネット接続を確認してください
- Yahoo Finance側の一時障害:時間を置いて再実行してください
正しいティッカーシンボルは、Yahoo Financeの公式サイトで銘柄を検索すると確認できます。
RSIの値がNaN(欠損値)になるのはなぜですか?
RSIの計算には最低でも「期間+1」日分のデータが必要です。
デフォルト(14日)の場合、最初の14行はNaNになります。
これは正常な挙動ですので、グラフ描画やシグナル判定の際は .dropna() で欠損値を除外してください。
# NaNを除外してからシグナル判定
df_valid = df.dropna(subset=["RSI_Wilder"])
TradingViewのRSIと数値が微妙にズレます
以下のチェックリストを確認してください。
- 計算方式:Wilder方式を使用していますか?(SMA方式だとズレます)
- データソース:yfinanceとTradingViewでは配信元が異なるため、終値自体が微妙に異なることがあります
- 株式分割・配当調整:yfinanceはデフォルトで調整済み終値を返しますが、TradingView側の設定と一致しているか確認してください
数値が1〜2ポイント程度のズレであれば、データソースの差異による正常な範囲です。5ポイント以上ズレている場合は、計算方式の違いを疑ってください。
まとめ
この記事では、PythonとpandasでRSI(相対力指数)を計算する方法を、SMA方式・Wilder方式の2パターンで解説しました。
要点を整理します。
- RSIは相場の過熱感を0〜100の数値で示すオシレーター系指標です
- 計算方式はSMA方式とWilder方式の2種類があり、実運用ではWilder方式が標準です
- yfinanceを使えば、無料かつ登録不要で日本株の株価データを取得できます
- RSI単体での売買判断は危険であり、移動平均線やMACDとの併用が推奨されます
次のステップとして、今回のRSI計算コードにMACDやボリンジャーバンドを組み合わせた複合シグナルの構築に挑戦してみてください。
複数のテクニカル指標を掛け合わせることで、売買判断の精度は格段に向上します。

