【第3話】【データ取得・シグナル生成】AIと作る日本株自動売買ツール~株価データ取得からテクニカル分析まで全コード公開

前回の記事では、自動売買システムに必要な環境構築を全部終わらせました。Cursor・Python仮想環境・GitHub・4つのAPI(J-Quants・EDINET・Claude・kabu STATION)の設定まで完了しています。

今回はいよいよコードを書いていきます。「全APIの接続テスト」「株価データを取得する」「テクニカル指標を計算する」「シグナルを生成する」という4つの内容を一気にやります。全コードを公開します。

正直に言うと、今回もエラーが何度か出ました。うまくいかなかった部分も隠さず書いています。同じところでハマっている方の参考になれば嬉しいです。

自動売買システム開発の全体像——今どこにいるか

まず現在地を確認します。このシステムの開発ロードマップはこうなっています。

Phase 0:環境構築 🔄進行中(今回)
Cursor・Python・GitHub・各種APIの設定(前回の記事でほぼ完了)
・✅ Cursor インストール完了
・✅ プロジェクトフォルダ作成完了
・✅ Python仮想環境の作成・有効化完了
・✅ 必要なライブラリのインストール完了
・✅ .envファイル作成完了
・✅ .gitignoreファイル作成完了
・✅ GitHub連携完了
・✅ J-Quants API 接続成功(200)
・✅ Claude API 登録完了(クレジットチャージ済み)
・🔄 EDINET APIの登録(メール到着待ち)
・🔄 三菱UFJeスマート証券 口座開設申し込み済み(審査待ちだと思っていた)

Phase 1:データ取得・分析 ⬜未着手(今回)
▶️ 全API接続テスト完了
▶️ 株価データ取得
▶️ テクニカル指標計算
▶️ テクニカルシグナル生成
⬜ ファンダメンタルシグナル(EDINET APIで財務データ)←次回
⬜ センチメントシグナル(Claude APIでニュース感情分析)
⬜ シグナルの統合・重み付け

Phase 2:バックテスト ⬜未着手
過去データで戦略を検証。勝率・プロフィットファクター・最大ドローダウンを計算します。

Phase 3:注文実行 ⬜未着手
kabu STATION APIで実際に注文を出します。ポジション管理・損切り・利確ロジックを実装します。

Phase 4:自動化 ⬜未着手
GitHub Actionsで毎日15時に自動実行。LINE Notifyでシグナルを通知。翌朝9時に自動注文を出します。

Phase 5:運用・改善 ⬜未着手
月次成績レポートの公開。戦略の見直しと改善を繰り返します。

今回はPhase 1の前半を一気に実装します。

kabu STATION APIの接続テスト——検証用ポートは18081

三菱UFJeスマート証券の口座開設が完了したので、まずkabu STATION APIの接続テストをしました。

kabu STATION APIの仕組みはほかのAPIとは少し違います。PCにkabu STATIONというアプリケーションをインストールして起動した状態でないと、APIが使えません。いわばkabu STATIONが中継サーバーの役割を果たしています。

設定手順はこちらです。

① kabu STATIONをインストール・起動してログイン
② 画面右上の「</>」アイコンを右クリック→「APIシステム設定」を選択
③「APIを利用する」にチェックを入れる
④ APIパスワードを設定する(本番用と検証用の2つ)
⑤「OK」をクリックしてkabu STATIONを再起動
⑥ 再起動後に右上のアイコンが緑になれば準備完了

接続テストはPythonのrequestsでPOSTリクエストを送ります。ここで一つはまりました。ポート番号が本番用と検証用で違います。

・本番用:18080
・検証用:18081

最初に18080で試したところ401エラーが返ってきました。検証用の18081に変えたところ、トークンが返ってきて接続成功です。

{"ResultCode":0,"Token":"xxxxxxxxxxxx"}

トークンが返ってきたら成功です。このトークンを使って注文や残高照会などのAPIを叩きます。なおkabu STATIONは毎朝6時15分に強制ログアウトされるので、自動化するときは再ログインの仕組みも必要になります。これはPhase 4で対応します。

取得したポート番号は.envファイルに保存しました。

KABU_API_PORT=18081

EDINET APIの接続テスト——ポップアップ許可を忘れずに

EDINET APIは、金融庁が提供する有価証券報告書などの開示情報を取得できるAPIです。PER・ROE・営業利益成長率などの財務データを自動で取得するために使います。

https://disclosure2.edinet-fsa.go.jp でAPIキーを申請します。

ここで一つ注意点があります。登録画面でブラウザのポップアップを許可する必要があります。ブラウザのアドレスバー右端にポップアップブロックのアイコンが出たら、クリックして「許可」を選択してください。これをしないと登録が先に進みません。許可さえすれば即日APIキーが取得できました。

取得したAPIキーを.envファイルに保存して接続テストをしました。

import requests
import os
from dotenv import load_dotenv

load_dotenv()
api_key = os.getenv('EDINET_API_KEY')
res = requests.get(
    'https://disclosure.edinet-fsa.go.jp/api/v2/documents.json',
    params={'date': '2024-01-04', 'type': 2, 'Subscription-Key': api_key}
)
print(res.status_code)

「200」と件数150件が返ってきて接続成功です。次回はこのAPIを使って財務データを取得してファンダメンタルシグナルを作ります。

Claude API接続テスト——クレジットチャージ後に反映待ちがある

Claude APIはAnthropicが提供するAIのAPIです。このシステムではGoogle News RSSから取得した日本語のニュース記事を読み込んで、株価にとってポジティブかネガティブかを判断する感情分析に使います。

接続テストのコードはこちらです。

import requests
import os
from dotenv import load_dotenv

load_dotenv()
api_key = os.getenv('CLAUDE_API_KEY')
headers = {
    'x-api-key': api_key,
    'anthropic-version': '2023-06-01',
    'content-type': 'application/json'
}
data = {
    'model': 'claude-haiku-4-5-20251001',
    'max_tokens': 100,
    'messages': [{'role': 'user', 'content': 'こんにちは'}]
}
res = requests.post('https://api.anthropic.com/v1/messages', headers=headers, json=data)
print(res.status_code)

最初にクレジット残高不足のエラーが返ってきました。「Plans & Billing」から5ドルチャージしましたが、すぐには反映されませんでした。少し時間をおいてから再試行したところ200が返ってきて接続成功です。チャージ直後に試して失敗しても、しばらく待ってから再試行してみてください。

日本語で「こんにちは」と送ったところ、日本語で返答が返ってきました。感情分析に使うのが楽しみです。

今回実装するコードの全体像

API接続テストが全部完了したので、次はコードを書いていきます。今回作るファイルは3つです。

・src/fetch_prices.py:株価データを取得してCSVに保存
・src/calc_indicators.py:テクニカル指標を計算
・src/generate_signals.py:シグナルスコアを生成

まずpandas-taをインストールします。テクニカル指標の計算に使うライブラリです。

pip install pandas-ta

株価データ取得(fetch_prices.py)

J-Quants APIを使って株価の日足データを取得します。

import requests
import pandas as pd
import os
from dotenv import load_dotenv
import time

load_dotenv()
api_key = os.getenv('JQUANTS_API_KEY')
headers = {'x-api-key': api_key}

def get_daily_prices(code, start_date, end_date):
    url = 'https://api.jquants.com/v2/equities/bars/daily'
    params = {'code': code, 'from': start_date, 'to': end_date}
    res = requests.get(url, headers=headers, params=params)
    if res.status_code != 200:
        print(f"エラー: {code} {res.status_code}")
        return None
    df = pd.DataFrame(res.json()['data'])
    return df

def get_multiple_prices(codes, start_date, end_date):
    all_data = []
    for code in codes:
        print(f"取得中:{code}")
        df = get_daily_prices(code, start_date, end_date)
        if df is not None:
            all_data.append(df)
            print(f"  → {len(df)}件取得")
        time.sleep(0.5)
    if all_data:
        return pd.concat(all_data, ignore_index=True)
    return None

ここで一つ注意点があります。J-Quants APIはV2になってから、レスポンスのキーが「bars」から「data」に変わっています。古い記事のコードをそのまま使うとKeyErrorが出るので気をつけてください。

また無料プランは直近データは取得できません。利用可能な期間内の日付を指定する必要があります。

トヨタ(7203)・ソフトバンクG(9984)・ソニー(6758)・キーエンス(6861)・三菱UFJ(8306)の5銘柄・3ヶ月分を取得したところ、合計300件のデータを取得してCSVに保存できました。

テクニカル指標の計算(calc_indicators.py)

pandas-taを使って以下の指標を計算します。

import pandas as pd
import pandas_ta as ta

def calc_indicators(df):
    df = df.rename(columns={
        'O': 'open', 'H': 'high', 'L': 'low', 'C': 'close', 'Vo': 'volume'
    })
    df['SMA5']  = ta.sma(df['close'], length=5)
    df['SMA25'] = ta.sma(df['close'], length=25)
    df['SMA75'] = ta.sma(df['close'], length=75)
    df['RSI14'] = ta.rsi(df['close'], length=14)
    macd = ta.macd(df['close'], fast=12, slow=26, signal=9)
    df['MACD']        = macd['MACD_12_26_9']
    df['MACD_signal'] = macd['MACDs_12_26_9']
    df['MACD_hist']   = macd['MACDh_12_26_9']
    bbands = ta.bbands(df['close'], length=20, std=2)
    df['BB_upper'] = bbands['BBU_20_2.0_2.0']
    df['BB_mid']   = bbands['BBM_20_2.0_2.0']
    df['BB_lower'] = bbands['BBL_20_2.0_2.0']
    return df

計算した指標は4種類です。

・SMA(移動平均線):5日・25日・75日。トレンドの方向を判断します。
・RSI(相対力指数):14日。70以上で買われすぎ、30以下で売られすぎのサインです。
・MACD:トレンドの強さと方向を示します。ヒストグラムが正なら上昇トレンド、負なら下降トレンドです。
・ボリンジャーバンド:価格の変動範囲を示します。上限・下限を超えたときがエントリーのサインになります。

ここでもはまりポイントがありました。ボリンジャーバンドの列名がpandas-taのバージョンによって変わります。「BBU_20_2.0」ではなく「BBU_20_2.0_2.0」(末尾に_2.0が2つ)でした。KeyErrorが出たら実際の列名を確認してみてください。

また指標の計算には一定のデータ数が必要です。MACDは最低26件必要なので、1ヶ月分のデータでは足りません。今回は3ヶ月分(60件)のデータで計算しています。

シグナル生成ロジック(generate_signals.py)

このシステムのシグナル重み付けはこうなっています。

・テクニカル:40% ←今回実装
・ファンダメンタル:35% ←次回実装
・センチメント:25% ←次々回実装

今回はテクニカルシグナルをスコア制で実装しました。スコアの計算ルールはこちらです。

・ゴールデンクロス(SMA5がSMA25を上抜け):+30点
・デッドクロス(SMA5がSMA25を下抜け):-30点
・SMA5がSMA25より上のトレンド:+20点
・SMA5がSMA25より下のトレンド:-20点
・RSI売られすぎ(30以下):+25点
・RSI買われすぎ(70以上):-25点
・RSI中立(40〜60):+10点
・MACDヒストグラム正:+20点
・MACDヒストグラム負:-20点
・ボリンジャーバンド下限割れ:+25点
・ボリンジャーバンド上限超え:-25点

合計スコアが+40以上で「買い」、-40以下で「売り」、それ以外は「様子見」と判定します。

トヨタ(7203)の2025年9月〜11月で検証した結果はこうなりました。

買い・売りシグナル発生日:16件

2025-10-08  3009円  score:50   買い  ゴールデンクロス+30 / 短期MA>長期MA+20
2025-10-16  2954円  score:-40  売り  デッドクロス-30 / 短期MA<長期MA-20 / RSI中立+10
2025-10-20  3003円  score:60   買い  ゴールデンクロス+30 / 短期MA>長期MA+20 / RSI中立+10
...
2025-11-20  3044円  score:-60  売り  デッドクロス-30 / 短期MA<長期MA-20 / RSI中立+10 / MACDヒスト負-20

10月8日にゴールデンクロスで買いシグナルが出て(3009円)、11月20日にデッドクロスで売りシグナルが出ています(3044円)。この期間で見るとプラスになっています。ただしこれはあくまでテストデータでの結果です。バックテストをしっかりやってから判断します。

今回のまとめ

・✅ kabu STATION API 接続成功(検証用ポート18081)
・✅ EDINET API 接続成功(ポップアップ許可が必要)
・✅ Claude API 接続成功(チャージ後に少し待つ)
・✅ pandas-taのインストール完了
・✅ 株価データ取得コード作成(fetch_prices.py)
・✅ 複数銘柄一括取得・CSV保存機能追加
・✅ テクニカル指標計算コード作成(calc_indicators.py)
・✅ シグナル生成ロジック作成(generate_signals.py)
・✅ トヨタ3ヶ月分で買い15件・売り2件のシグナルを検出
・✅ 全コードをGitHubに保存

次回はEDINET APIで財務データを取得して、ファンダメンタルシグナルを実装します。PER・ROE・営業利益成長率などを使ってスコアを計算する予定です。

また読みにきてください。

ほろ酔いカバ🦛

免責事項

本記事は筆者個人の体験・記録を目的としたものであり、特定の銘柄・金融商品への投資を推奨するものではありません。投資はご自身の判断と責任のもとで行ってください。過去の運用実績は将来の成果を保証するものではありません。

コメント

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