【コピペOK】mplfinanceで美しいローソク足チャートをPythonで描画する

Python実装・コード

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

Pythonで株価データを取得し、matplotlibで折れ線グラフを描画するところまでは経験済みではないでしょうか。

ローソク足チャート(Candlestick Chart)は、始値・高値・安値・終値の4本値を1本のローソクで表現する相場分析の基本です。証券会社のアプリに頼らず、自分のPython環境で自在にカスタマイズできる価値は非常に大きいものがあります。

しかし、matplotlibだけでローソク足を描こうとすると、四角形やヒゲの描画処理が煩雑になり、コード量が膨れ上がるという壁にぶつかります。

その問題を解決するのがmplfinanceライブラリです。わずか1行の関数呼び出しで、移動平均線や出来高を含む本格的なローソク足チャートを描画できます。ただし、DataFrameのインデックス形式や列名の指定を間違えると即エラーになる点が初中級者のつまずきポイントです。

本記事では、mplfinanceの基本描画から、スタイルカスタマイズ、テクニカル指標の重ね描き、チャートの画像保存までを段階的に解説します。

コードはすべてコピペで動作します。銘柄コードや配色を差し替えるだけで、自分だけの分析用チャートをすぐに作成できます。

mplfinanceの基本知識とデータ要件

mplfinanceが求めるDataFrameの形式

mplfinanceは、pandasのDataFrameに対して厳格なフォーマットを要求します。以下の条件をすべて満たす必要があります。

* インデックス:DatetimeIndex型であること。文字列型やRangeIndexでは描画に失敗します

* 列名:OpenHighLowCloseの4列が必須です。大文字始まりのスペルが正確でないとKeyErrorが発生します

* 出来高:Volume列があれば出来高バーを自動描画できます。なくてもエラーにはなりません

yfinancedownload関数は、auto_adjust=True指定でこの形式に合致したDataFrameを返します。ただしMultiIndex化されるバージョンがあるため、フラット化処理を入れておくのが安全です。

チャートタイプの種類

mplfinancetype引数で描画スタイルを切り替えられます。

* candle:標準的なローソク足チャートです。本記事のメインで使用します

* ohlc:バーチャート形式(OHLC Bar)で、欧米のトレーダーに好まれるスタイルです

* line:終値の折れ線グラフです。トレンドの大局観を掴む用途に適しています

* renko:練行足(Renko Chart)で、値幅ベースのノイズ除去チャートです

【コピペOK】基本のローソク足チャート描画コード

まず必要なライブラリをインストールしてください。


pip install mplfinance yfinance pandas

以下がメインコードです。


import yfinance as yf
import pandas as pd
import mplfinance as mpf
from typing import Any

# ==============================
# 設定エリア
# ==============================
TICKER: str = "6758.T"  # 銘柄コード(ソニーグループ)
START_DATE: str = "2025-01-01"
END_DATE: str = "2025-12-31"
MA_PERIODS: list[int] = [5, 25, 75]  # 移動平均線の期間リスト
CHART_TYPE: str = "candle"  # チャートタイプ(candle / ohlc / line / renko)
VOLUME_ENABLED: bool = True  # 出来高バーの表示有無
SAVE_FILENAME: str = "candlestick_chart.png"  # 保存ファイル名
SAVE_DPI: int = 150  # 保存画像の解像度
FIGSIZE: tuple[int, int] = (16, 9)  # チャートサイズ


# ==============================
# データ取得
# ==============================
def fetch_stock_data(ticker: str, start: str, end: str) -> pd.DataFrame:
    '株価データを取得しmplfinance用に整形する'
    df: pd.DataFrame = yf.download(ticker, start=start, end=end, auto_adjust=True)
    if df.empty:
        raise ValueError(f"データを取得できませんでした: {ticker}")
    # MultiIndex対策でカラムをフラット化
    if isinstance(df.columns, pd.MultiIndex):
        df.columns = df.columns.get_level_values(0)
    # DatetimeIndex確認
    if not isinstance(df.index, pd.DatetimeIndex):
        df.index = pd.to_datetime(df.index)
    return df


# ==============================
# カスタムスタイル定義
# ==============================
def create_custom_style() -> Any:
    'ダーク系のカスタムチャートスタイルを生成する'
    market_colors: Any = mpf.make_marketcolors(
        up="#e74c3c",    # 陽線(赤):日本式の配色
        down="#3498db",   # 陰線(青):日本式の配色
        edge="inherit",
        wick="inherit",
        volume="in",
        ohlc="inherit",
    )
    custom_style: Any = mpf.make_mpf_style(
        base_mpf_style="nightclouds",
        marketcolors=market_colors,
        figcolor="#1a1a2e",
        facecolor="#16213e",
        gridcolor="#2c3e6b",
        gridstyle="--",
        gridaxis="both",
        y_on_right=True,
        rc={
            "font.size": 10,
            "axes.labelsize": 11,
            "axes.titlesize": 14,
        },
    )
    return custom_style


# ==============================
# 移動平均線の追加プロット生成
# ==============================
def create_ma_addplots(
    df: pd.DataFrame, periods: list[int]
) -> list[Any]:
    '指定期間の移動平均線をaddplot用リストとして返す'
    colors: list[str] = ["#f39c12", "#2ecc71", "#e056fd", "#00cec9"]
    addplots: list[Any] = []
    for i, period in enumerate(periods):
        ma_series: pd.Series = df["Close"].rolling(window=period).mean()
        color: str = colors[i % len(colors)]
        ap: Any = mpf.make_addplot(
            ma_series, color=color, width=0.8, label=f"MA{period}"
        )
        addplots.append(ap)
    return addplots


# ==============================
# チャート描画(画面表示)
# ==============================
def plot_candlestick(
    df: pd.DataFrame,
    ticker: str,
    style: Any,
    addplots: list[Any],
) -> None:
    'ローソク足チャートを画面に表示する'
    mpf.plot(
        df,
        type=CHART_TYPE,
        style=style,
        title=f"n{ticker} Candlestick Chart",
        ylabel="Price (JPY)",
        ylabel_lower="Volume",
        volume=VOLUME_ENABLED,
        addplot=addplots if addplots else None,
        figsize=FIGSIZE,
        tight_layout=True,
        datetime_format="%Y-%m-%d",
        xrotation=15,
    )


# ==============================
# チャート保存(ファイル出力)
# ==============================
def save_candlestick(
    df: pd.DataFrame,
    ticker: str,
    style: Any,
    addplots: list[Any],
    filename: str,
) -> None:
    'ローソク足チャートをPNG画像として保存する'
    mpf.plot(
        df,
        type=CHART_TYPE,
        style=style,
        title=f"n{ticker} Candlestick Chart",
        ylabel="Price (JPY)",
        ylabel_lower="Volume",
        volume=VOLUME_ENABLED,
        addplot=addplots if addplots else None,
        figsize=FIGSIZE,
        tight_layout=True,
        datetime_format="%Y-%m-%d",
        xrotation=15,
        savefig=dict(fname=filename, dpi=SAVE_DPI, bbox_inches="tight"),
    )
    print(f"チャートを {filename} に保存しました。")


# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
    df: pd.DataFrame = fetch_stock_data(TICKER, START_DATE, END_DATE)
    print(f"取得データ: {len(df)}行 ({df.index[0].date()} ~ {df.index[-1].date()})")
    print(f"カラム: {list(df.columns)}")

    style: Any = create_custom_style()
    addplots: list[Any] = create_ma_addplots(df, MA_PERIODS)

    save_candlestick(df, TICKER, style, addplots, SAVE_FILENAME)
    plot_candlestick(df, TICKER, style, addplots)

コードの処理フロー解説

上記のコードは、以下の5ステップで構成されています。

* データ取得:yfinanceで株価をダウンロードし、MultiIndexのフラット化とDatetimeIndex型の保証を行います

* スタイル定義:make_marketcolorsで陽線を赤・陰線を青の日本式配色にし、make_mpf_styleでダーク系の背景テーマを構築します

* 移動平均生成:設定エリアのMA_PERIODSリストに基づき、任意の本数の移動平均線をmake_addplotで生成します

* チャート保存:savefig引数にファイル名とDPIを渡し、PNG画像として出力します

* チャート表示:mpf.plotで画面上にローソク足チャートを描画します

MA_PERIODSのリストに値を追加するだけで移動平均線の本数を増やせます。CHART_TYPEohlcに変えればバーチャートへの切り替えも即座に可能です。

スタイルカスタマイズの実践テクニック

配色と背景の調整ポイント

mplfinanceにはbinancecharlesnightcloudsyahooなど複数のビルトインスタイルが用意されています。mpf.available_styles()で一覧を取得できます。

日本市場のチャートでは、以下の配色が広く使われています。

* 陽線(上昇)を赤、陰線(下落)を青にするのが日本式です。欧米式は逆(陽線が緑、陰線が赤)のため、ビルトインスタイルをそのまま使うと混乱を招く場合があります

* 背景をダーク系にすると長時間のチャート監視で目の負担が軽減されます

* グリッド線はgridstyle="--"(破線)かつ低めの透明度が視認性と情報量のバランスに優れています

配色の好みは人によって異なるため、make_marketcolorsupdownのカラーコードを自由に差し替えてください。

出来高バーの色分け

デフォルトでは出来高バーも陽線・陰線と同じ色で描画されます。make_marketcolorsvolume="in"を指定すると、ローソク足の色がそのまま出来高バーに適用されます。

出来高を別の色にしたい場合は、volume引数に個別のカラーコードを渡してください。出来高急増を視覚的に強調したいときに有効なテクニックです。

【コピペOK】ボリンジャーバンドとRSI付きの発展版コード

以下は、ローソク足チャートにボリンジャーバンド(Bollinger Bands)とRSI(Relative Strength Index:相対力指数)のサブチャートを追加した発展版です。メインコードと同じファイルに追記して使用してください。


# ==============================
# 設定エリア(発展版追加分)
# ==============================
BB_PERIOD: int = 20  # ボリンジャーバンドの期間
BB_STD_DEV: float = 2.0  # 標準偏差の倍率
RSI_PERIOD: int = 14  # RSIの期間
RSI_UPPER: float = 70.0  # RSI買われすぎライン
RSI_LOWER: float = 30.0  # RSI売られすぎライン


# ==============================
# ボリンジャーバンド計算
# ==============================
def calc_bollinger_bands(df: pd.DataFrame, period: int, std_dev: float) -> pd.DataFrame:
    'ボリンジャーバンドの上限・中央・下限を計算する'
    df["BB_Mid"] = df["Close"].rolling(window=period).mean()
    bb_std: pd.Series = df["Close"].rolling(window=period).std()
    df["BB_Upper"] = df["BB_Mid"] + (bb_std * std_dev)
    df["BB_Lower"] = df["BB_Mid"] - (bb_std * std_dev)
    return df


# ==============================
# RSI計算
# ==============================
def calc_rsi(df: pd.DataFrame, period: int) -> pd.DataFrame:
    'RSIを計算してカラムに追加する'
    delta: pd.Series = df["Close"].diff()
    gain: pd.Series = delta.where(delta > 0, 0.0)
    loss: pd.Series = (-delta).where(delta < 0, 0.0)
    avg_gain: pd.Series = gain.rolling(window=period).mean()
    avg_loss: pd.Series = loss.rolling(window=period).mean()
    rs: pd.Series = avg_gain / avg_loss
    df["RSI"] = 100 - (100 / (1 + rs))
    return df


# ==============================
# 発展版チャート描画
# ==============================
def plot_advanced_chart(df: pd.DataFrame, ticker: str) -> None:
    'ボリンジャーバンド+RSIサブチャート付きローソク足を描画・保存する'
    style: Any = create_custom_style()

    # ボリンジャーバンド addplot
    ap_bb_upper: Any = mpf.make_addplot(
        df["BB_Upper"], color="#e67e22", width=0.6, linestyle="--"
    )
    ap_bb_mid: Any = mpf.make_addplot(
        df["BB_Mid"], color="#ecf0f1", width=0.5, linestyle="-"
    )
    ap_bb_lower: Any = mpf.make_addplot(
        df["BB_Lower"], color="#e67e22", width=0.6, linestyle="--"
    )

    # RSI addplot(サブパネル)
    ap_rsi: Any = mpf.make_addplot(
        df["RSI"], panel=2, color="#9b59b6", width=0.8,
        ylabel="RSI", ylim=(0, 100),
    )

    # RSI閾値ライン用のSeriesを作成
    rsi_upper_line: pd.Series = pd.Series(RSI_UPPER, index=df.index)
    rsi_lower_line: pd.Series = pd.Series(RSI_LOWER, index=df.index)
    ap_rsi_upper: Any = mpf.make_addplot(
        rsi_upper_line, panel=2, color="#e74c3c", width=0.5, linestyle="--"
    )
    ap_rsi_lower: Any = mpf.make_addplot(
        rsi_lower_line, panel=2, color="#2ecc71", width=0.5, linestyle="--"
    )

    all_addplots: list[Any] = [
        ap_bb_upper, ap_bb_mid, ap_bb_lower,
        ap_rsi, ap_rsi_upper, ap_rsi_lower,
    ]

    mpf.plot(
        df,
        type="candle",
        style=style,
        title=f"n{ticker} Advanced Chart (BB + RSI)",
        ylabel="Price (JPY)",
        ylabel_lower="Volume",
        volume=True,
        addplot=all_addplots,
        figsize=(18, 11),
        tight_layout=True,
        panel_ratios=(5, 2, 2),
        datetime_format="%Y-%m-%d",
        xrotation=15,
        savefig=dict(fname="advanced_chart.png", dpi=SAVE_DPI, bbox_inches="tight"),
    )
    print("チャートを advanced_chart.png に保存しました。")


# ==============================
# 発展版メイン処理
# ==============================
if __name__ == "__main__":
    df = fetch_stock_data(TICKER, START_DATE, END_DATE)
    df = calc_bollinger_bands(df, BB_PERIOD, BB_STD_DEV)
    df = calc_rsi(df, RSI_PERIOD)
    plot_advanced_chart(df, TICKER)

コードの処理フロー解説

上記のコードは、以下の4ステップで構成されています。

* ボリンジャーバンド計算:rollingで移動平均と標準偏差を算出し、上限・中央・下限の3本をDataFrameに追加します

* RSI計算:終値の前日比から上昇幅と下落幅を分離し、それぞれの移動平均からRSIを算出します

* addplot構築:ボリンジャーバンド3本はメインパネル(panel=0)に、RSIと閾値ラインはサブパネル(panel=2)に配置します

* 描画・保存:panel_ratios=(5, 2, 2)で株価・出来高・RSIのパネル比率を指定し、1枚の統合チャートとして出力します

panel_ratiosの数値を変えるとパネルの高さ比率を自由に調整できます。RSIの代わりにMACDを配置する場合は、MACD計算関数を追加してpanel番号を差し替えてください。

よくあるエラーと対処法

AttributeError: ‘RangeIndex’ object has no attribute ‘to_pydatetime’

DataFrameのインデックスがDatetimeIndex型ではないことが原因です。mplfinanceはインデックスの日付情報を使って横軸を生成するため、数値インデックスや文字列インデックスでは動作しません。

以下を試してください。

* df.index = pd.to_datetime(df.index)で明示的にDatetimeIndex型に変換する

* CSVから読み込んだ場合はpd.read_csv("file.csv", index_col=0, parse_dates=True)を指定する

* print(type(df.index))でインデックスの型を確認する

KeyError: ‘Open’ または列名不一致

mplfinanceOpenHighLowCloseの列名を厳密に参照します。小文字(openclose)や日本語列名では認識されません。

以下を試してください。

* df.columnsを出力し、列名のスペルと大文字小文字を確認する

* 列名が異なる場合はdf.rename(columns={"始値": "Open", "高値": "High", "安値": "Low", "終値": "Close"}, inplace=True)で変換する

* yfinanceのMultiIndex問題が原因の場合はdf.columns = df.columns.get_level_values(0)でフラット化する

savefigで画像が保存されない

mpf.plotsavefig引数とshowの併用に関する問題です。Jupyter Notebook環境ではshowが自動実行されるため、savefigが無視される場合があります。

returnfig=Trueを指定してFigureオブジェクトを受け取り、fig.savefig()で明示的に保存するのが確実です。スクリプト実行(.pyファイル)の場合は本記事のコードで正常に保存されます。

まとめ

この記事では、mplfinanceによるローソク足チャートの描画方法を、基本コードからボリンジャーバンド+RSI付きの発展版まで解説しました。

要点を整理します。

* mplfinanceはDatetimeIndex型かつOpen/High/Low/Close列を持つDataFrameを要求するため、データ整形が最初の関門です

* make_marketcolorsmake_mpf_styleを組み合わせることで、日本式の配色やダーク系背景を自由にカスタマイズできます

* make_addplotで移動平均線やボリンジャーバンドをメインチャートに重ね、panel引数でRSI等をサブチャートとして分離配置できます

* savefig引数を辞書形式で渡すことで、DPIや余白を指定した高品質なPNG画像として保存できます

* 設定エリアのTICKERMA_PERIODSCHART_TYPEを変更するだけで、あらゆる銘柄・スタイルに対応可能です

次のステップとして、複数銘柄のチャートを一括生成するバッチ処理の構築を検討してください。銘柄リストをループで回し、銘柄ごとにPNG画像を保存するだけで、毎朝の相場チェック用レポートを自動化できます。

さらに、mpf.plotreturnfig=TrueオプションでFigureオブジェクトを受け取れば、matplotlibのAPIで任意のアノテーション(矢印やテキスト)を追加する高度なカスタマイズも実現できます。

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