MACDで相場のトレンド転換を掴む!Pythonで計算・シグナル検知する方法

Python実装・コード

※本記事のコードや情報は執筆時点の仕様に基づいています。投資は自己責任であり、必ずデモ環境や少額資金でテストした上で運用してください。

トレンドの転換点をいち早く捉えるテクニカル指標として、MACD(Moving Average Convergence Divergence)は世界中のトレーダーに使われています。

しかし、証券会社のチャートツールに表示されるMACDをただ眺めるだけでは、独自の売買ルールに組み込んだり、複数銘柄を一括スクリーニングしたりすることはできません。

この記事では、PythonとpandasだけでMACDライン・シグナルライン・ヒストグラムを計算し、ゴールデンクロス・デッドクロスの売買シグナルを自動検出するコードを提供します。

すべてのコードはコピペでそのまま動作しますので、Python初心者でも安心して取り組めます。


MACDの基本理論を理解する

コードを書く前に、MACDが「何を計算しているのか」を正確に把握しておくことが重要です。

理論を理解していれば、パラメータの調整やダマシへの対処も自力で行えるようになります。

MACDを構成する3つの要素

MACDは、以下の3つの要素で構成されています。

  • MACDライン: 短期EMA(指数平滑移動平均)から長期EMAを引いた値です
  • シグナルライン: MACDラインのEMA(平滑化した値)です
  • ヒストグラム: MACDラインとシグナルラインの差分です

一般的に使用されるデフォルトパラメータは以下の通りです。

要素 計算内容 デフォルト期間
短期EMA 終値の指数平滑移動平均 12日
長期EMA 終値の指数平滑移動平均 26日
MACDライン 短期EMA − 長期EMA
シグナルライン MACDラインのEMA 9日
ヒストグラム MACDライン − シグナルライン

EMA(指数平滑移動平均)と単純移動平均の違い

MACD計算の核となるEMAは、単純移動平均(SMA)とは異なり、直近の価格に大きなウェイトを置く平均手法です。

SMAはすべての日の終値を均等に扱いますが、EMAは新しいデータほど重みが大きくなるため、価格変動への反応が早くなります。

この「反応の速さ」こそが、MACDがトレンド転換の初動を捉えられる理由です。

pandasには ewm() というEMA計算用のメソッドが標準搭載されており、数式を自分で実装する必要はありません。

MACDは「トレンドフォロー型」の指標です。レンジ相場(横ばい)ではダマシが頻発するため、RSIやボリンジャーバンドなど他の指標と組み合わせて使うことが推奨されます。


【コピペOK】MACDの計算コードをゼロから実装する

ここからは、実際にPythonコードを書いてMACDを計算します。

まずは純粋な計算ロジックだけを関数化し、次のセクションでデータ取得と統合します。

MACDを計算する関数

以下のコードは、pandasのDataFrameに「Close」列(終値)が含まれていれば、どんなデータソースにも適用できる汎用的なMACD計算関数です。


import pandas as pd

def calculate_macd(
    df: pd.DataFrame,
    short_period: int = 12,
    long_period: int = 26,
    signal_period: int = 9
) -> pd.DataFrame:
    """
    MACDライン、シグナルライン、ヒストグラムを計算してDataFrameに追加する。

    Parameters
    ----------
    df : pd.DataFrame
        'Close' 列を含むDataFrame
    short_period : int
        短期EMAの期間(デフォルト12)
    long_period : int
        長期EMAの期間(デフォルト26)
    signal_period : int
        シグナルラインのEMA期間(デフォルト9)

    Returns
    -------
    pd.DataFrame
        MACD_Line, Signal_Line, Histogram 列が追加されたDataFrame
    """
    # 短期EMAと長期EMAを算出
    df["EMA_Short"] = df["Close"].ewm(span=short_period, adjust=False).mean()
    df["EMA_Long"] = df["Close"].ewm(span=long_period, adjust=False).mean()

    # MACDライン = 短期EMA - 長期EMA
    df["MACD_Line"] = df["EMA_Short"] - df["EMA_Long"]

    # シグナルライン = MACDラインのEMA
    df["Signal_Line"] = df["MACD_Line"].ewm(span=signal_period, adjust=False).mean()

    # ヒストグラム = MACDライン - シグナルライン
    df["Histogram"] = df["MACD_Line"] - df["Signal_Line"]

    return df

計算結果を確認するテストコード

関数が正しく動作するか、ダミーデータで検証します。


import pandas as pd

# ==============================
# ダミーデータで動作確認
# ==============================
def test_macd_calculation():
    """ランダムな終値データでMACDを計算して表示する"""
    import numpy as np
    np.random.seed(42)

    # 100日分のダミー株価を生成
    dates = pd.date_range(start="2025-01-01", periods=100, freq="B")
    prices = 1000 + np.cumsum(np.random.randn(100) * 10)
    df = pd.DataFrame({"Close": prices}, index=dates)

    # MACD計算
    df = calculate_macd(df)

    # 末尾10行を表示
    display_cols = ["Close", "MACD_Line", "Signal_Line", "Histogram"]
    print(df[display_cols].tail(10).round(2))

if __name__ == "__main__":
    test_macd_calculation()

実行すると、以下のような出力が得られます。


              Close  MACD_Line  Signal_Line  Histogram
2025-05-09  1027.42       3.15         2.87       0.28
2025-05-12  1035.61       4.02         3.10       0.92
...

MACD_Line・Signal_Line・Histogramの3列が正しく追加されていれば成功です。


【コピペOK】yfinanceと組み合わせて実際の株価でMACDを計算する

ダミーデータでの検証が完了したら、実際の株価データにMACDを適用します。

ここでは、yfinanceライブラリを使って日本株のデータを取得します。

環境準備

以下のコマンドで必要なライブラリをインストールしてください。


pip install yfinance pandas

日本株のMACDを一気通貫で算出するコード

以下のコードは、銘柄コードを指定するだけで「データ取得 → MACD計算 → 結果表示 → CSV保存」までを自動で行います。


import yfinance as yf
import pandas as pd

# ==============================
# 設定エリア(★ここを編集してください)
# ==============================
SYMBOL = "6758.T"       # ソニーグループ(東証の場合は末尾に.T)
PERIOD = "1y"           # 取得期間(1y=1年, 6mo=6ヶ月)
SHORT_PERIOD = 12       # 短期EMA
LONG_PERIOD = 26        # 長期EMA
SIGNAL_PERIOD = 9       # シグナルライン

# ==============================
# MACD計算関数
# ==============================
def calculate_macd(
    df: pd.DataFrame,
    short_period: int = 12,
    long_period: int = 26,
    signal_period: int = 9
) -> pd.DataFrame:
    """MACDライン、シグナルライン、ヒストグラムを計算する"""
    df["EMA_Short"] = df["Close"].ewm(span=short_period, adjust=False).mean()
    df["EMA_Long"] = df["Close"].ewm(span=long_period, adjust=False).mean()
    df["MACD_Line"] = df["EMA_Short"] - df["EMA_Long"]
    df["Signal_Line"] = df["MACD_Line"].ewm(span=signal_period, adjust=False).mean()
    df["Histogram"] = df["MACD_Line"] - df["Signal_Line"]
    return df

# ==============================
# データ取得 → 計算 → 保存
# ==============================
def main():
    print(f"=== {SYMBOL} のMACDを計算します ===")

    # データ取得
    ticker = yf.Ticker(SYMBOL)
    df = ticker.history(period=PERIOD)

    if df.empty:
        print("エラー: データが取得できませんでした。銘柄コードを確認してください。")
        return

    print(f"取得件数: {len(df)}件")

    # MACD計算
    df = calculate_macd(df, SHORT_PERIOD, LONG_PERIOD, SIGNAL_PERIOD)

    # 結果表示
    display_cols = ["Close", "MACD_Line", "Signal_Line", "Histogram"]
    print("n--- 直近10日間のMACD ---")
    print(df[display_cols].tail(10).round(2))

    # CSV保存
    filename = f"{SYMBOL.replace('.', '_')}_macd.csv"
    df.to_csv(filename)
    print(f"n保存完了: {filename}")

if __name__ == "__main__":
    main()

yfinanceで東証銘柄を取得する際は、銘柄コードの末尾に必ず「.T」を付けてください。例:トヨタ自動車は「7203.T」、ソフトバンクグループは「9984.T」となります。


【コピペOK】MACDの売買シグナルを自動検出する

MACDの数値が計算できたら、次はその値から売買シグナルを自動で検出するロジックを実装します。

MACDクロスの売買ルール

MACDにおける最も基本的な売買シグナルは「MACDラインとシグナルラインのクロス」です。

  • 買いシグナル(ゴールデンクロス): MACDラインがシグナルラインを下から上に突き抜けたとき
  • 売りシグナル(デッドクロス): MACDラインがシグナルラインを上から下に突き抜けたとき
  • ヒストグラムの符号変化: プラスからマイナス(またはその逆)に変わるタイミングもクロスと同義です

ゼロライン・ヒストグラムを組み合わせた高精度フィルター

単純なクロスだけではダマシが多くなるため、以下のフィルターを追加するとシグナルの精度が向上します。

フィルター条件 内容 効果
ゼロライン上でのクロス MACDラインが0より上でゴールデンクロス 上昇トレンド中の押し目買いを検出
ヒストグラム拡大 ヒストグラムが3日連続で拡大 トレンドの勢いを確認
ゼロライン下でのクロス MACDラインが0より下でデッドクロス 下降トレンド中の戻り売りを検出

売買シグナル検出の完全コード

以下のコードは、データ取得からMACD計算、シグナル検出、結果一覧表示までを統合したものです。


import yfinance as yf
import pandas as pd

# ==============================
# 設定エリア
# ==============================
SYMBOL = "6758.T"
PERIOD = "1y"
SHORT_PERIOD = 12
LONG_PERIOD = 26
SIGNAL_PERIOD = 9

# ==============================
# MACD計算
# ==============================
def calculate_macd(df: pd.DataFrame) -> pd.DataFrame:
    """MACDライン、シグナルライン、ヒストグラムを追加する"""
    df["EMA_Short"] = df["Close"].ewm(span=SHORT_PERIOD, adjust=False).mean()
    df["EMA_Long"] = df["Close"].ewm(span=LONG_PERIOD, adjust=False).mean()
    df["MACD_Line"] = df["EMA_Short"] - df["EMA_Long"]
    df["Signal_Line"] = df["MACD_Line"].ewm(span=SIGNAL_PERIOD, adjust=False).mean()
    df["Histogram"] = df["MACD_Line"] - df["Signal_Line"]
    return df

# ==============================
# シグナル検出
# ==============================
def detect_macd_signals(df: pd.DataFrame) -> pd.DataFrame:
    """MACDクロスによる売買シグナルを検出する"""
    df["Signal"] = ""

    for i in range(1, len(df)):
        prev_macd = df["MACD_Line"].iloc[i - 1]
        prev_signal = df["Signal_Line"].iloc[i - 1]
        curr_macd = df["MACD_Line"].iloc[i]
        curr_signal = df["Signal_Line"].iloc[i]

        # NaNチェック
        if pd.isna(prev_macd) or pd.isna(curr_macd):
            continue

        # ゴールデンクロス(買い)
        if prev_macd <= prev_signal and curr_macd > curr_signal:
            df.iloc[i, df.columns.get_loc("Signal")] = "BUY"

        # デッドクロス(売り)
        elif prev_macd >= prev_signal and curr_macd < curr_signal:
            df.iloc[i, df.columns.get_loc("Signal")] = "SELL"

    return df

# ==============================
# メイン実行
# ==============================
def main():
    print(f"=== {SYMBOL} のMACDシグナルを検出します ===n")

    ticker = yf.Ticker(SYMBOL)
    df = ticker.history(period=PERIOD)

    if df.empty:
        print("データ取得に失敗しました。銘柄コードを確認してください。")
        return

    df = calculate_macd(df)
    df = detect_macd_signals(df)

    # シグナルが出た日だけ抽出
    signals = df[df["Signal"] != ""]
    display_cols = ["Close", "MACD_Line", "Signal_Line", "Histogram", "Signal"]

    if signals.empty:
        print("指定期間内にシグナルは検出されませんでした。")
    else:
        print(f"検出されたシグナル数: {len(signals)}件n")
        print(signals[display_cols].round(2).to_string())

    # 全データCSV保存
    filename = f"{SYMBOL.replace('.', '_')}_macd_signals.csv"
    df.to_csv(filename)
    print(f"n保存完了: {filename}")

if __name__ == "__main__":
    main()

【コピペOK】複数銘柄を一括スクリーニングする

実際の投資では、1銘柄だけでなく複数の銘柄を同時に監視してシグナルを検出したいケースが大半です。

ここでは、ウォッチリストの全銘柄に対してMACDスクリーニングを行うコードを紹介します。

ウォッチリスト一括スクリーニングコード


import yfinance as yf
import pandas as pd
import time

# ==============================
# 設定エリア
# ==============================
WATCHLIST = [
    ("7203.T", "トヨタ自動車"),
    ("9984.T", "ソフトバンクG"),
    ("6758.T", "ソニーG"),
    ("8306.T", "三菱UFJ"),
    ("9432.T", "NTT"),
]
PERIOD = "6mo"
SHORT_PERIOD = 12
LONG_PERIOD = 26
SIGNAL_PERIOD = 9

# ==============================
# MACD計算(再掲)
# ==============================
def calculate_macd(df: pd.DataFrame) -> pd.DataFrame:
    df["EMA_Short"] = df["Close"].ewm(span=SHORT_PERIOD, adjust=False).mean()
    df["EMA_Long"] = df["Close"].ewm(span=LONG_PERIOD, adjust=False).mean()
    df["MACD_Line"] = df["EMA_Short"] - df["EMA_Long"]
    df["Signal_Line"] = df["MACD_Line"].ewm(span=SIGNAL_PERIOD, adjust=False).mean()
    df["Histogram"] = df["MACD_Line"] - df["Signal_Line"]
    return df

# ==============================
# 直近シグナル判定
# ==============================
def check_latest_cross(df: pd.DataFrame) -> str:
    """直近日にMACDクロスが発生したかを判定する"""
    if len(df) < 2:
        return "データ不足"

    prev_macd = df["MACD_Line"].iloc[-2]
    prev_signal = df["Signal_Line"].iloc[-2]
    curr_macd = df["MACD_Line"].iloc[-1]
    curr_signal = df["Signal_Line"].iloc[-1]

    if pd.isna(prev_macd) or pd.isna(curr_macd):
        return "算出不可"

    if prev_macd <= prev_signal and curr_macd > curr_signal:
        return "🔵 BUY"
    elif prev_macd >= prev_signal and curr_macd < curr_signal:
        return "🔴 SELL"
    else:
        return "— なし"

# ==============================
# 一括スクリーニング
# ==============================
def screening():
    print("=== MACDスクリーニング開始 ===n")
    results = []

    for symbol, name in WATCHLIST:
        print(f"処理中: {name}({symbol})")
        ticker = yf.Ticker(symbol)
        df = ticker.history(period=PERIOD)

        if df.empty:
            results.append({"銘柄": name, "コード": symbol, "終値": "-", "MACD": "-", "シグナル": "取得失敗"})
            continue

        df = calculate_macd(df)
        signal = check_latest_cross(df)
        latest = df.iloc[-1]

        results.append({
            "銘柄": name,
            "コード": symbol,
            "終値": round(latest["Close"], 1),
            "MACD": round(latest["MACD_Line"], 2),
            "シグナル": signal,
        })
        time.sleep(1)  # サーバー負荷軽減

    result_df = pd.DataFrame(results)
    print("n=== スクリーニング結果 ===n")
    print(result_df.to_string(index=False))

if __name__ == "__main__":
    screening()

スクリーニング結果の読み方

出力は以下のような一覧表形式になります。

銘柄 コード 終値 MACD シグナル
トヨタ自動車 7203.T 2850.0 15.32 — なし
ソニーG 6758.T 3200.0 -8.45 🔵 BUY

「🔵 BUY」や「🔴 SELL」が表示された銘柄が、直近営業日にMACDクロスが発生した銘柄です。

この結果をもとに、チャートを目視確認してからエントリー判断を行ってください。

スクリーニング結果はあくまでフィルタリングの第一段階です。必ず出来高やファンダメンタルズも確認した上で投資判断を行ってください。


MACDパラメータのチューニング指針

デフォルトの「12-26-9」は万能ではありません。

銘柄の値動きの特性や投資スタイルに合わせてパラメータを調整することで、シグナルの精度を向上させられます。

投資スタイル別の推奨パラメータ

投資スタイル 短期EMA 長期EMA シグナル 特徴
デイトレード(短期) 5 13 4 シグナル発生頻度が高い。ダマシも増加
スイングトレード(中期) 12 26 9 最も標準的。バランスが良い
ポジショントレード(長期) 19 39 9 シグナル発生頻度が低い。大きなトレンドを捕捉

パラメータ変更時の注意点

パラメータを短くすると感度が上がりシグナルが増えますが、ノイズ(ダマシ)も同時に増加します。

逆に長くすると信頼性は上がりますが、エントリーが遅れてしまいます。

過去データでバックテストを行い、勝率・プロフィットファクター・最大ドローダウンなどの指標を確認してからパラメータを確定させてください。


よくあるエラーと対処法

MACD実装時に初心者がつまずきやすいポイントをQ&A形式で解説します。

ewm() の adjust パラメータは何を意味しますか?

adjust=True(デフォルト)の場合、初期値のバイアスを補正する計算が行われます。

adjust=False にすると、再帰的な計算式がそのまま適用され、処理速度がわずかに向上します。

MACD計算においては adjust=False が一般的に使われており、証券会社のチャートツールとも数値が一致しやすくなります。

ヒストグラムの値が小さすぎて判定しにくいです

株価水準が低い銘柄や、ボラティリティが極端に小さい銘柄では、MACDやヒストグラムの絶対値が非常に小さくなります。

その場合は、「ヒストグラムの符号変化」ではなく「ヒストグラムの変化率」を閾値として使う方法が有効です。

以下のように変化率ベースの判定を追加できます。


# ヒストグラムの前日比変化率
df["Hist_Change"] = df["Histogram"].diff()

yfinanceのデータ取得でタイムアウトします

ネットワーク環境が不安定な場合や、Yahoo Financeのサーバーが混雑している場合にタイムアウトが発生します。

以下の対策を試してください。

  • time.sleep(2) でリクエスト間隔を広げます
  • 取得期間を「1y」→「6mo」に短縮してデータ量を減らします
  • VPNを使用している場合は一時的にオフにします

MACDの数値が証券会社のチャートと微妙に異なります

これはEMAの計算開始日とadjustパラメータの違いによるものです。

yfinanceから取得できるデータの開始日と、証券会社のチャートが参照しているデータの開始日が異なる場合、EMAの初期値にわずかなズレが生じます。

実用上はこの程度のズレはシグナル判定に影響しませんが、気になる場合は取得期間を「2y」以上に設定して十分なウォーミングアップ期間を確保してください。


まとめ

MACDをPythonで計算し、売買シグナルを自動検出するまでの流れを整理します。

  1. MACDの理論理解: 短期EMA・長期EMA・シグナルラインの3要素で構成されることを把握します
  2. 計算関数の実装: pandasの ewm() メソッドを使い、MACDライン・シグナルライン・ヒストグラムを算出します
  3. 実データへの適用: yfinanceで日本株の株価を取得し、MACDを計算します
  4. シグナル検出: MACDラインとシグナルラインのクロスを自動判定します
  5. 複数銘柄スクリーニング: ウォッチリストを一括処理し、シグナル発生銘柄を一覧表示します

MACDは単体でも強力な指標ですが、RSIやボリンジャーバンドと組み合わせることでダマシを大幅に削減できます。

まずは今回のコードをそのまま実行してMACDの動きを体感し、その後パラメータの調整や他の指標との統合に進んでください。

タイトルとURLをコピーしました