※本記事のコードや情報は執筆時点の仕様に基づいています。投資は自己責任であり、必ずデモ環境や少額資金でテストした上で運用してください。
テクニカル分析の中で最も基本的かつ広く使われている指標が移動平均線(Moving Average)です。株価のトレンド方向を視覚的に把握でき、売買シグナルの判定にも活用される、すべてのテクニカル指標の土台となる存在です。
しかし、Excelで手動計算していては銘柄数が増えた際に対応しきれません。Pythonとpandasを使えば、わずか数行のコードで移動平均の算出からCSV出力まで自動化できます。
この記事では、移動平均線の基本概念から、pandasのrolling()メソッドを使った実装、5日・25日・75日移動平均の同時算出、そしてゴールデンクロス・デッドクロスの自動検出ロジックまで、段階的に解説します。
移動平均線の基本知識
コードを書く前に、移動平均線の仕組みと種類を正確に理解しておく必要があります。
移動平均線とは何か
移動平均線とは、一定期間の終値の平均値を日ごとに算出し、その値を線で結んだものです。直近の株価の「ノイズ」を平滑化し、トレンドの方向性を明確にする役割を持っています。
たとえば「5日移動平均」であれば、当日を含む直近5営業日の終値を合計し、5で割った値がその日の移動平均値になります。
主要な移動平均線の期間設定
日本の株式市場では、以下の期間設定が一般的に使用されています。
| 期間 | 用途 | 特徴 |
|---|---|---|
| 5日 | 短期トレンド | 株価への追従が速いがノイズも多い |
| 25日 | 中期トレンド | 約1か月の営業日数に相当。最も汎用的 |
| 75日 | 長期トレンド | 約3か月に相当。大きなトレンド転換の判断に使う |
| 200日 | 超長期トレンド | 機関投資家が重視する指標 |
単純移動平均(SMA)と指数平滑移動平均(EMA)
移動平均には複数の計算方式が存在します。この記事では最も基本的な単純移動平均(SMA: Simple Moving Average)を扱います。
- SMA(単純移動平均): 期間内のすべての終値を等しい重みで平均する
- EMA(指数平滑移動平均): 直近のデータほど重みを大きくして平均する
SMAは計算がシンプルで理解しやすく、初めてテクニカル分析を実装する場合に最適です。EMAへの拡張は、SMAの実装を完了してから取り組んでください。
pandasのrolling()メソッドを理解する
pandasには、移動平均の算出に最適なrolling()メソッドが標準搭載されています。
rolling()の基本構文
rolling() は「ウィンドウ関数」と呼ばれる機能で、データフレームの各行に対して、指定した行数分の窓(ウィンドウ)を適用し、その範囲内で集計処理を行います。
df["カラム名"].rolling(window=期間).mean()
window: ウィンドウサイズ(期間)を整数で指定.mean(): ウィンドウ内の平均値を算出
rolling()の動作イメージ
5日移動平均の場合、以下のように計算されます。
| 営業日 | 終値 | 5日移動平均 |
|---|---|---|
| 1日目 | 1000 | NaN(データ不足) |
| 2日目 | 1020 | NaN |
| 3日目 | 1010 | NaN |
| 4日目 | 1030 | NaN |
| 5日目 | 1050 | 1022.0 |
| 6日目 | 1040 | 1030.0 |
ウィンドウサイズに満たない最初の数行は NaN(欠損値)になります。これは正常な動作です。
min_periodsオプション
デフォルトでは、ウィンドウ内のデータがすべて揃っていないとNaNを返します。min_periods パラメータを指定すると、最低限のデータ数で計算を開始できます。
df["Close"].rolling(window=5, min_periods=1).mean()
ただし、データ不足の状態で算出された移動平均は精度が低いため、通常はデフォルト設定のまま使用することを推奨します。
【コピペOK】yfinanceで株価を取得し移動平均を算出するコード
ここからは、実際にyfinanceで株価データを取得し、移動平均線を算出する完全なコードを提示します。
基本実装:5日・25日・75日移動平均の同時算出
以下のコードをコピーして moving_average.py として保存・実行してください。
import yfinance as yf
import pandas as pd
# ==============================
# 設定エリア
# ==============================
SYMBOL = "7203.T" # 銘柄コード(トヨタ自動車)
PERIOD = "1y" # 取得期間(1年間)
SHORT_WINDOW = 5 # 短期移動平均(5日)
MID_WINDOW = 25 # 中期移動平均(25日)
LONG_WINDOW = 75 # 長期移動平均(75日)
# ==============================
# データ取得
# ==============================
def fetch_data(symbol, period):
print(f"--- {symbol} の株価データを取得中 ---")
ticker = yf.Ticker(symbol)
df = ticker.history(period=period)
if df.empty:
print("データの取得に失敗しました。銘柄コードとネットワークを確認してください。")
return None
print(f"取得件数: {len(df)}件")
return df
# ==============================
# 移動平均の算出
# ==============================
def calc_moving_averages(df):
df[f"SMA_{SHORT_WINDOW}"] = df["Close"].rolling(window=SHORT_WINDOW).mean()
df[f"SMA_{MID_WINDOW}"] = df["Close"].rolling(window=MID_WINDOW).mean()
df[f"SMA_{LONG_WINDOW}"] = df["Close"].rolling(window=LONG_WINDOW).mean()
# 小数点以下を1桁に丸める
for col in [f"SMA_{SHORT_WINDOW}", f"SMA_{MID_WINDOW}", f"SMA_{LONG_WINDOW}"]:
df[col] = df[col].round(1)
return df
# ==============================
# 結果の表示と保存
# ==============================
def display_and_save(df, symbol):
display_cols = ["Close", f"SMA_{SHORT_WINDOW}", f"SMA_{MID_WINDOW}", f"SMA_{LONG_WINDOW}"]
print("n--- 直近10日間のデータ ---")
print(df[display_cols].tail(10).to_string())
filename = f"{symbol.replace('.', '_')}_sma.csv"
df.to_csv(filename)
print(f"n--- CSV保存完了: {filename} ---")
# ==============================
# メイン処理
# ==============================
def main():
df = fetch_data(SYMBOL, PERIOD)
if df is None:
return
df = calc_moving_averages(df)
display_and_save(df, SYMBOL)
if __name__ == "__main__":
main()
実行結果のイメージ
上記コードを実行すると、以下のような出力が得られます。
--- 7203.T の株価データを取得中 ---
取得件数: 245件
--- 直近10日間のデータ ---
Close SMA_5 SMA_25 SMA_75
Date
2026-02-06 2845.0 2832.0 2801.5 2756.3
2026-02-07 2860.0 2841.0 2808.2 2759.1
...
コードのポイント解説
このコードで押さえるべきポイントは以下の3つです。
- 設定エリアの分離: 銘柄コードや期間を上部にまとめることで、変更箇所が明確になる
- 関数の分離: データ取得・計算・出力を別関数にすることで、保守性とテスト性が向上する
- round()による丸め処理: 移動平均値の小数桁を統一し、出力を見やすくする
【コピペOK】複数銘柄の移動平均を一括算出するコード
実際の運用では、複数の銘柄を同時に分析したいケースがほとんどです。以下のコードは、銘柄リストを指定するだけで一括処理を行います。
複数銘柄対応版
import yfinance as yf
import pandas as pd
import os
# ==============================
# 設定エリア
# ==============================
SYMBOLS = [
"7203.T", # トヨタ自動車
"6758.T", # ソニーグループ
"9984.T", # ソフトバンクグループ
"8306.T", # 三菱UFJフィナンシャル・グループ
]
PERIOD = "1y"
SHORT_WINDOW = 5
MID_WINDOW = 25
LONG_WINDOW = 75
OUTPUT_DIR = "output_sma"
# ==============================
# 移動平均算出処理
# ==============================
def process_symbol(symbol):
print(f"n{'='*40}")
print(f" {symbol} の処理開始")
print(f"{'='*40}")
ticker = yf.Ticker(symbol)
df = ticker.history(period=PERIOD)
if df.empty:
print(f" → {symbol}: データ取得失敗。スキップします。")
return None
df[f"SMA_{SHORT_WINDOW}"] = df["Close"].rolling(window=SHORT_WINDOW).mean().round(1)
df[f"SMA_{MID_WINDOW}"] = df["Close"].rolling(window=MID_WINDOW).mean().round(1)
df[f"SMA_{LONG_WINDOW}"] = df["Close"].rolling(window=LONG_WINDOW).mean().round(1)
# 最新日のサマリーを表示
latest = df.iloc[-1]
print(f" 終値 : {latest['Close']:.1f}")
print(f" SMA_{SHORT_WINDOW} : {latest[f'SMA_{SHORT_WINDOW}']:.1f}")
print(f" SMA_{MID_WINDOW} : {latest[f'SMA_{MID_WINDOW}']:.1f}")
print(f" SMA_{LONG_WINDOW} : {latest[f'SMA_{LONG_WINDOW}']:.1f}")
return df
# ==============================
# メイン処理
# ==============================
def main():
os.makedirs(OUTPUT_DIR, exist_ok=True)
for symbol in SYMBOLS:
df = process_symbol(symbol)
if df is not None:
filename = os.path.join(OUTPUT_DIR, f"{symbol.replace('.', '_')}_sma.csv")
df.to_csv(filename)
print(f" → 保存完了: {filename}")
print(f"n=== 全銘柄の処理が完了しました(出力先: {OUTPUT_DIR}/) ===")
if __name__ == "__main__":
main()
SYMBOLS リストに銘柄コードを追加するだけで、分析対象を自由に拡張できます。
ゴールデンクロスとデッドクロスの自動検出
移動平均線の実践的な活用法として、ゴールデンクロス(GC)とデッドクロス(DC)の自動検出があります。
GC・DCの定義
- ゴールデンクロス: 短期移動平均線が中期移動平均線を下から上に突き抜ける現象。買いシグナルとされる
- デッドクロス: 短期移動平均線が中期移動平均線を上から下に突き抜ける現象。売りシグナルとされる
【コピペOK】クロス検出ロジック
以下の関数を前述のコードに追加することで、GC・DCを自動検出できます。
# ==============================
# ゴールデンクロス・デッドクロス検出
# ==============================
def detect_cross(df, short_col, mid_col):
signals = []
for i in range(1, len(df)):
prev_short = df[short_col].iloc[i - 1]
prev_mid = df[mid_col].iloc[i - 1]
curr_short = df[short_col].iloc[i]
curr_mid = df[mid_col].iloc[i]
if pd.isna(prev_short) or pd.isna(prev_mid):
continue
# ゴールデンクロス:前日は短期 < 中期、当日は短期 >= 中期
if prev_short < prev_mid and curr_short >= curr_mid:
signals.append({
"Date": df.index[i],
"Signal": "ゴールデンクロス",
"Close": df["Close"].iloc[i],
short_col: curr_short,
mid_col: curr_mid,
})
# デッドクロス:前日は短期 >= 中期、当日は短期 < 中期
elif prev_short >= prev_mid and curr_short < curr_mid:
signals.append({
"Date": df.index[i],
"Signal": "デッドクロス",
"Close": df["Close"].iloc[i],
short_col: curr_short,
mid_col: curr_mid,
})
return pd.DataFrame(signals)
呼び出し方は以下のとおりです。
cross_df = detect_cross(df, f"SMA_{SHORT_WINDOW}", f"SMA_{MID_WINDOW}")
if not cross_df.empty:
print("n--- 検出されたクロスシグナル ---")
print(cross_df.to_string(index=False))
else:
print("n--- クロスシグナルは検出されませんでした ---")
ゴールデンクロス・デッドクロスは単独では精度が低いシグナルです。出来高やRSIなど、他の指標と組み合わせて判断することが重要です。シグナルの発生だけで売買を自動執行する設計は避けてください。
よくあるエラーと対処法
rolling()の結果がすべてNaNになる
ウィンドウサイズがデータ件数を超えている場合に発生します。たとえば、取得期間が「1mo」(約20営業日)で75日移動平均を計算しようとすると、すべての行がNaNになります。
対処法は以下のとおりです。
- 取得期間を十分に長く設定する(75日移動平均なら最低でも
period="6mo"を推奨) df[カラム名].notna().sum()でNaN以外のデータ件数を確認する
銘柄コードの指定方法が分からない
yfinanceで日本株を指定する場合は、証券コードの末尾に .T(東証)を付ける必要があります。
# 正しい指定
"7203.T" # トヨタ自動車(東証)
# 誤った指定
"7203" # データが取得できない
CSVファイルの日本語が文字化けする
Excelで開いた際に文字化けする場合は、CSV出力時にエンコーディングを指定してください。
df.to_csv(filename, encoding="utf-8-sig")
utf-8-sig を指定すると、BOM(Byte Order Mark)が付加され、Excelが正しくUTF-8として認識します。
まとめ
Pythonで移動平均線を算出するために必要な知識とコードは以下のとおりです。
- 移動平均線は
pandasのrolling(window=期間).mean()で算出できる - 日本株の分析では5日・25日・75日の3本が標準的な組み合わせとなる
- yfinanceで株価データを取得し、pandasで加工・CSV出力するパイプラインが基本形
- ゴールデンクロス・デッドクロスは前日と当日の大小関係の反転で検出できる
- クロスシグナルは単独での利用は推奨されず、他指標との併用が前提となる
移動平均線はテクニカル分析の最も基本的な指標です。この実装を土台にして、RSI・MACD・ボリンジャーバンドなどの高度な指標へと段階的に拡張していくことで、本格的な分析システムを構築できます。

