チャート画像をそのままLINEへ!Pythonで売買シグナルを視覚的に通知する方法

自動化・運用

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

Pythonで株価の売買シグナルを自動検知できるようになっても、PCの前に張り付いていなければ意味がありません。

外出先や移動中でも「今、買いシグナルが出た」「RSIが30を割り込んだ」といった情報をリアルタイムで受け取れる仕組みが必要です。

その解決策として最も手軽なのが「LINE Notify」です。

LINE Notifyは、LINEが公式に提供している無料の通知APIであり、テキストメッセージだけでなく画像ファイルも一緒に送信できます。

【重要】LINE Notifyのサービス終了について

LINEは2025年3月31日をもってLINE Notifyのサービスを終了すると発表しました。ただし、後継として「LINE Messaging API」が利用可能です。本記事ではLINE Messaging APIを使った画像送信方法も併せて解説します。現時点でLINE Notifyがまだ利用可能な場合はそちらのコードも掲載していますが、新規実装にはMessaging APIを推奨します。

この記事では、Pythonで生成したチャート画像と売買シグナルのテキストを、LINEに自動送信するコードを提供します。

yfinanceでデータ取得 → mplfinanceでチャート描画 → 画像保存 → LINE送信という一連のパイプラインをすべてコピペで動作する形で実装します。

LINE通知の仕組みと準備

LINEに自動通知を送るためには、事前にトークンの発行とアカウントの設定が必要です。

ここでは、LINE Notify(既存ユーザー向け)とLINE Messaging API(新規推奨)の2つの方法について準備手順を説明します。

方法①:LINE Notifyのトークン発行手順(既存利用者向け)

LINE Notifyを既に利用している場合は、以下の手順でトークンを取得してください。

  1. ブラウザで LINE Notify の公式ページ(notify-bot.line.me)にアクセスします
  2. LINEアカウントでログインします
  3. 「マイページ」→「トークンを発行する」をクリックします
  4. トークン名(例:「株価通知」)を入力し、通知先のトークルームを選択します
  5. 「発行する」をクリックし、表示されたトークンをコピーして安全な場所に保管します

トークンは発行時の1回しか表示されません。紛失した場合は再発行が必要です。トークンは第三者に絶対に公開しないでください。

方法②:LINE Messaging APIの準備手順(新規推奨)

LINE Notifyの終了に備え、新規で実装する場合はLINE Messaging APIを使用してください。

無料プラン(コミュニケーションプラン)で月200通まで送信可能です。

  1. LINE Developersコンソール(developers.line.biz)にログインします
  2. 新規プロバイダーを作成します
  3. 「Messaging API」チャネルを作成します
  4. チャネル設定画面で「チャネルアクセストークン(長期)」を発行します
  5. 作成したBotを自分のLINEアカウントで友だち追加します
  6. 自分のユーザーIDは「チャネル基本設定」→「あなたのユーザーID」で確認できます
項目 LINE Notify LINE Messaging API
料金 無料 無料(月200通まで)
サービス状況 終了予定 現行サービス
画像送信 対応(multipart) 対応(画像URL or バイナリ)
セットアップ難易度 簡単 やや手間がかかる
推奨度 △(既存利用者のみ) ◎(新規実装向け)

【コピペOK】LINE Notifyでテキスト+画像を送信するコード

まずは、LINE Notifyを使ったシンプルな画像送信コードを紹介します。

既にトークンを発行済みの場合は、すぐに動作確認が可能です。

事前準備:ライブラリのインストール


pip install yfinance mplfinance requests
  • requests:HTTPリクエストを送信するための定番ライブラリです
  • LINE Notify専用のライブラリは不要で、requestsだけで実装できます

【コピペOK】LINE Notify画像送信コード


import yfinance as yf
import mplfinance as mpf
import requests
import os

# ==============================
# 設定エリア
# ==============================
LINE_NOTIFY_TOKEN = "YOUR_LINE_NOTIFY_TOKEN"  # ← 発行したトークンに置き換え
SYMBOL = "7203.T"       # トヨタ自動車
PERIOD = "3mo"
OUTPUT_DIR = "charts"
CHART_FILE = os.path.join(OUTPUT_DIR, "signal_chart.png")

# ==============================
# 保存先フォルダの作成
# ==============================
os.makedirs(OUTPUT_DIR, exist_ok=True)

# ==============================
# 株価データ取得
# ==============================
def fetch_data(symbol: str, period: str):
    ticker = yf.Ticker(symbol)
    df = ticker.history(period=period)
    if df.empty:
        raise ValueError(f"{symbol} のデータを取得できませんでした。")
    return df

# ==============================
# チャート描画+画像保存
# ==============================
def save_chart(df, symbol: str, filepath: str):
    ""ローソク足チャートをPNG画像として保存します。""
    custom_style = mpf.make_mpf_style(
        base_mpf_style="charles",
        rc={"font.size": 9},
    )
    mpf.plot(
        df,
        type="candle",
        style=custom_style,
        title=f"n{symbol} - Signal Chart",
        volume=True,
        mav=(5, 25),
        figsize=(14, 8),
        savefig=dict(
            fname=filepath,
            dpi=150,
            bbox_inches="tight",
            facecolor="white",
        ),
    )
    print(f"✔ チャート保存完了: {filepath}")

# ==============================
# LINE Notify送信(テキスト+画像)
# ==============================
def send_line_notify(token: str, message: str, image_path: str = None):
    ""LINE Notifyでテキストと画像を送信します。""
    url = "https://notify-api.line.me/api/notify"
    headers = {"Authorization": f"Bearer {token}"}
    payload = {"message": message}

    files = None
    if image_path and os.path.exists(image_path):
        files = {"imageFile": open(image_path, "rb")}

    response = requests.post(url, headers=headers, data=payload, files=files)

    if files:
        files["imageFile"].close()

    if response.status_code == 200:
        print("✔ LINE通知送信成功")
    else:
        print(f"✖ LINE通知送信失敗: {response.status_code} - {response.text}")

    return response.status_code

# ==============================
# 売買シグナル判定(RSI簡易版)
# ==============================
def check_rsi_signal(df, window: int = 14) -> str:
    ""RSIベースの簡易シグナルを判定します。""
    delta = df["Close"].diff()
    gain = delta.where(delta > 0, 0.0)
    loss = (-delta).where(delta < 0, 0.0)

    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))

    latest_rsi = rsi.iloc[-1]
    latest_close = df["Close"].iloc[-1]

    signal = "中立"
    if latest_rsi >= 70:
        signal = "🔴 買われすぎ(売りシグナル候補)"
    elif latest_rsi <= 30:
        signal = "🟢 売られすぎ(買いシグナル候補)"

    message = (
        f"n📊 {SYMBOL} シグナル通知"
        f"n━━━━━━━━━━━━━━"
        f"n終値: {latest_close:,.1f} 円"
        f"nRSI(14): {latest_rsi:.1f}"
        f"n判定: {signal}"
        f"n━━━━━━━━━━━━━━"
    )
    return message

# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
    # 1. データ取得
    df = fetch_data(SYMBOL, PERIOD)

    # 2. チャート画像保存
    save_chart(df, SYMBOL, CHART_FILE)

    # 3. シグナル判定
    message = check_rsi_signal(df)
    print(message)

    # 4. LINE送信
    send_line_notify(LINE_NOTIFY_TOKEN, message, CHART_FILE)

実行すると、LINEにローソク足チャートの画像と、RSIに基づく売買シグナルのテキストが同時に届きます。

YOUR_LINE_NOTIFY_TOKEN の部分を自分のトークンに置き換えるだけで動作します。

【コピペOK】LINE Messaging APIで画像を送信するコード

LINE Notifyの終了後も継続して運用するために、LINE Messaging APIを使った実装も用意しています。

Messaging APIでは画像をバイナリとして直接送信する方法と、画像URLを指定する方法の2つがありますが、ここではローカル画像をアップロードして送信する実践的なパターンを紹介します。

LINE Messaging APIの画像送信の仕組み

Messaging APIで画像を送信するには、以下の2ステップが必要です。

  1. 画像をインターネット上からアクセス可能なURLに配置する
  2. そのURLを imagemessage として送信する

ローカルファイルを直接送信する公式APIは限定的なため、ここでは画像をBase64エンコードしてテキストで送信する代わりに、一度Imgurなどの外部サービスにアップロードしてURLを取得する方法が一般的です。

しかし、外部サービスへの依存を避けたい場合のために、以下ではMessaging APIの「Push Message」でテキスト通知を送り、画像は別途保存済みのものをローカルで管理する構成を採用します。

【コピペOK】LINE Messaging APIテキスト通知コード


import requests
import json

# ==============================
# 設定エリア
# ==============================
CHANNEL_ACCESS_TOKEN = "YOUR_CHANNEL_ACCESS_TOKEN"  # ← Messaging APIのトークン
USER_ID = "YOUR_USER_ID"                             # ← 送信先のユーザーID

# ==============================
# LINE Messaging API プッシュ送信
# ==============================
def send_line_messaging_api(token: str, user_id: str, message: str):
    ""LINE Messaging APIでテキストメッセージを送信します。""
    url = "https://api.line.me/v2/bot/message/push"
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {token}",
    }
    payload = {
        "to": user_id,
        "messages": [
            {
                "type": "text",
                "text": message,
            }
        ],
    }

    response = requests.post(url, headers=headers, data=json.dumps(payload))

    if response.status_code == 200:
        print("✔ Messaging API送信成功")
    else:
        print(f"✖ 送信失敗: {response.status_code} - {response.text}")

    return response.status_code

# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
    test_message = (
        "📊 テスト通知n"
        "━━━━━━━━━━━━━━n"
        "銘柄: 7203.T(トヨタ自動車)n"
        "RSI(14): 28.5n"
        "判定: 🟢 売られすぎn"
        "━━━━━━━━━━━━━━"
    )
    send_line_messaging_api(CHANNEL_ACCESS_TOKEN, USER_ID, test_message)

Messaging APIで画像URLを送信する方法

画像をインターネット上で公開できるサーバー(S3、Firebase Storage、自前サーバーなど)を持っている場合は、以下のようにメッセージタイプを image に変更するだけで画像送信が可能です。


payload = {
    "to": user_id,
    "messages": [
        {
            "type": "image",
            "originalContentUrl": "https://example.com/charts/signal_chart.png",
            "previewImageUrl": "https://example.com/charts/signal_chart.png",
        }
    ],
}

画像URLはHTTPS必須であり、ファイルサイズは10MB以下、形式はJPEGまたはPNGに限定されます。HTTPのURLを指定するとエラーになるため注意してください。

実運用に向けた自動化のポイント

コードが動くようになったら、次は「毎日自動で実行される仕組み」を構築します。

ここでは、自動化に必要な設計上のポイントを整理します。

トークンのセキュリティ管理

APIトークンをソースコードに直接記述するのは、セキュリティ上のリスクがあります。

以下のいずれかの方法で管理してください。

  • 環境変数os.environ["LINE_NOTIFY_TOKEN"] で取得する方法が最も手軽です
  • .envファイルpython-dotenv ライブラリで .env ファイルから読み込みます
  • 秘密管理サービス:AWS Secrets Manager、GCP Secret Managerなどを使用します

import os

# 環境変数から取得する例
LINE_NOTIFY_TOKEN = os.environ.get("LINE_NOTIFY_TOKEN", ")
if not LINE_NOTIFY_TOKEN:
    raise ValueError("環境変数 LINE_NOTIFY_TOKEN が設定されていません。")

定期実行の設定方法

スクリプトを毎日決まった時刻に実行するには、OSのスケジューラ機能を使用します。

OS ツール 設定例
Linux / Mac cron 0 18 * * 1-5 python /home/user/signal.py
Windows タスクスケジューラ GUIから毎日18:00に実行を設定
クラウド GitHub Actions / AWS Lambda YAMLまたはコンソールから設定

上記のcronの例は「平日の18:00に実行」という意味です。

東証の大引け(15:00)後に十分なデータ確定時間を置いてから実行するのが一般的な運用パターンとなります。

通知頻度の制御

毎回通知を送ると「通知疲れ」が発生し、本当に重要なシグナルを見逃す原因になります。

以下のような条件分岐を入れることで、通知の質を維持できます。

  • RSIが30以下または70以上の場合のみ通知する
  • 前日と比較して5%以上の値動きがあった場合のみ通知する
  • シグナルが「中立」の場合は通知をスキップする

# シグナルが中立でなければ通知する例
if "中立" not in message:
    send_line_notify(LINE_NOTIFY_TOKEN, message, CHART_FILE)
else:
    print("シグナルなし。通知をスキップします。")

よくあるエラーと対処法

LINE通知の実装で初心者がつまずきやすいポイントをまとめます。

LINE Notifyで「401 Unauthorized」エラーが出ます

原因はトークンの設定ミスです。

以下を確認してください。

  • トークン文字列の前後に不要なスペースや改行が入っていませんか
  • トークンを再発行した場合、古いトークンは無効化されます
  • Bearer の後に半角スペース1つを入れてから、トークンを記述してください

# 正しい例
headers = {"Authorization": "Bearer abc123xyz"}

# 間違いの例(Bearerの後にスペースがない)
headers = {"Authorization": "Bearerabc123xyz"}

画像が送信されずテキストだけ届きます

LINE Notifyで画像を送信する際は、requests.post()files パラメータに正しくファイルオブジェクトを渡す必要があります。

以下のチェックリストを確認してください。

  • 画像ファイルのパスが正しいですか(os.path.exists() で確認)
  • ファイルをバイナリモード("rb")で開いていますか
  • 画像ファイルが壊れていませんか(手動で開いて表示されるか確認)
  • ファイルサイズが大きすぎませんか(LINE Notifyの上限は約1MB程度です)

Messaging APIで「400 Bad Request」が返ります

Messaging APIでは、ユーザーIDの形式やメッセージ構造に厳密なバリデーションが適用されます。

  • ユーザーIDの形式U で始まる33文字の英数字です(例:U1234567890abcdef1234567890abcdef
  • messagesは配列"messages" の値は必ずリスト形式にしてください
  • typeの指定漏れ:各メッセージオブジェクトに "type" キーが必須です

トークンやユーザーIDは絶対にGitHubなどの公開リポジトリにコミットしないでください。.gitignore.env ファイルを追加するか、環境変数で管理する運用を徹底してください。

LINE Notifyが終了したらどうすればいいですか

LINE Messaging APIへの移行を推奨します。

本記事で紹介したMessaging APIのコードをベースに、以下の手順で移行できます。

  1. LINE Developersコンソールでチャネルを作成します
  2. チャネルアクセストークンとユーザーIDを取得します
  3. send_line_notify() 関数を send_line_messaging_api() 関数に置き換えます
  4. 画像送信が必要な場合は、画像をHTTPS対応のストレージにアップロードする仕組みを追加します

テキスト通知だけであれば、関数の差し替えだけで移行が完了します。

まとめ

この記事では、Pythonで生成した株価チャート画像と売買シグナルをLINEに自動送信する方法を、LINE NotifyとLINE Messaging APIの2パターンで解説しました。

要点を整理します。

  • LINE Notifyは手軽ですがサービス終了予定のため、新規実装にはLINE Messaging APIを推奨します
  • 画像送信は requests.post()files パラメータでバイナリファイルを渡すだけで実現できます
  • RSIなどのテクニカル指標と組み合わせることで、「シグナル発生時のみ通知」という実用的な運用が可能です
  • トークンは環境変数で管理し、GitHubへのコミットを防止してください
  • cronやタスクスケジューラで定期実行を設定すれば、完全自動の通知システムが完成します

次のステップとして、複数銘柄のシグナルを一括監視し、条件を満たした銘柄だけをまとめてLINEに送信する「ウォッチリスト型通知システム」の構築に挑戦してみてください。

「データ取得 → 分析 → 描画 → 保存 → 通知」のパイプラインが完成すれば、相場監視の手間を大幅に削減できます。

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