LLM(GPT / Gemini / Claude / Grok など)が週初に日本株を2銘柄ずつピックし、その週のリターンを比較するゲーム用ツールです。実際の売買は行いません。ピック対象の銘柄は LLM が自分で考えて選びます。
- 週の最初(日曜日の朝・JST想定)に、その週に値上がりしそうな 2銘柄 を予想します。
- 採点は「月曜日の始値で買い、金曜日の終値で売る」と仮定した週次リターンで行い、2銘柄の合計で競います。
- LLMは必要に応じて、利用可能な範囲で Web検索 / URL参照 / X検索 などのツールを使ってOKです(モデル/SDKによって実行痕跡の出方は異なります)。
- データ取得:
yfinanceを使用。日本株ティッカーは7203.Tのようにコード+取引所を使用します。 - マーケットカレンダー: 平日かつ
jpholiday非対象日のみ取引日とみなし、さらにdata/calendar/manual_closed_dates.jsonに列挙した半休日・臨時休場日もスキップします。月曜が祝日の場合は次の取引日に始値を取り、週末側(通常は金曜)が休場の場合はその週(Mon→Fri)の範囲内で最後に取引があった日の終値を採用します(例: 金曜休場なら木曜終値)。 - タイムゾーン: 内部では
Asia/Tokyoを基準に計算し、記録時刻は UTC へ変換します(JSON に ISO 8601 で保存)。 - LLMピック: 各 LLM クライアントを実際に呼び出してピックを生成します。銘柄ユニバースは固定せず、LLM が自ら選びます。
- データ保存: GitHub リポジトリ内にファイルとして保存(DB 不使用)。週次ピック/日次価格/日次結果はいずれもフラットな日付付きファイル名(JSON・Markdown)で管理。
- GitHub Actions: uv を使って3本の定期バッチを実行し、自動コミット/Pushします。
src/llm_trader_battle/: アプリ本体data/picks/picks-<week>.json: ピック(model ごとに銘柄・理由を含むオブジェクト形式)data/prices/prices-<YYYY-MM-DD>.json: その日1日の OHLC ロング形式data/result/result-<YYYY-MM-DD>.json: 日次リザルト(llm_avg と銘柄別リターン)data/result/result-<YYYY-MM-DD>.md: 日次リザルト(Markdown)reports/<YYYYMM>/summary.md: 月次サマリ(summary.png を埋め込み).github/workflows/weekly-picks.yml: 週末ピック(手動時は week_start / llms / skip_current を任意指定可).github/workflows/daily-prices.yml: 日次価格取得(手動時は date を任意指定可).github/workflows/daily-aggregate.yml: 日次集計(手動時は date を任意指定可)
- 週次ピック(週末実行):
uv run llm-trader-battle predict --week-start 2025-01-06(省略時は次の月曜を自動推定) ---skip-currentを付けるとdata/picks/picks-<YYYY-MM-DD>.jsonは生成しつつ、data/picks/current.jsonを上書きしません(検証用) - 日次価格取得(16:00以降・取引日だけ実行):
uv run llm-trader-battle fetch-daily --date 2025-01-06 - 日次集計(17:00以降・取引日だけ実行):
uv run llm-trader-battle aggregate-daily --date 2025-01-06
一部の環境(例: Codespaces / Dev Container のワークスペースマウント)では、.venv をリポジトリ直下に作ると
ファイルシステムの都合で uv sync が No such file or directory (os error 2) で失敗することがあります。
その場合は、仮想環境の作成先をホームディレクトリ側に逃がすと解消します:
export UV_PROJECT_ENVIRONMENT="$HOME/.venv-llm-trader-battle"uv sync
Dev Containers にはOSごとの条件分岐がないため、リポジトリ既定は Windows/Codespaces 向けにして、Mac だけローカルoverrideで差し替える運用にしています。
- 既定:
.devcontainer/devcontainer.json(mcr.microsoft.com/devcontainers/universal:linux) - Mac用override:
.devcontainer/devcontainer.local.json(git管理しません)
Macでは次を実行してください:
.devcontainer/devcontainer.local.json.exampleを.devcontainer/devcontainer.local.jsonにコピーDev Containers: Rebuild and Reopen in Container
predict実行時に--llmsで1件だけ指定します(例: GPT だけ動かす): -uv run llm-trader-battle predict --week-start 2025-12-15 --llms gpt- 同様に--llms gemini/--llms claude/--llms grokで単体実行できます。
LLMが「検索/URL取得」などの外部ツールを使える設定かと、実行時に実際に使ったかは別です。 このプロジェクトでは、実行ログ(stderr)にツール使用痕跡を出すためのデバッグ用環境変数を用意しています。
LLM_TRADER_BATTLE_LOG_LLM_OUTPUT=1- LLMの生出力(raw)を stderr に出します。
LLM_TRADER_BATTLE_LOG_LLM_TOOL=1tool_usedを stderr に1行で出します。
LLM_TRADER_BATTLE_LOG_LLM_TOOL_TRACE=1tool_trace(プロバイダごとの詳細痕跡)を JSON で stderr に出します。
例:
LLM_TRADER_BATTLE_LOG_LLM_TOOL=1 \
LLM_TRADER_BATTLE_LOG_LLM_TOOL_TRACE=1 \
uv run llm-trader-battle predicttool_used は「このLLM呼び出しで、外部ツール(検索等)を実行した痕跡が取れたか」を表します。
tool_used=True: 何らかのツール実行痕跡がレスポンスに含まれていたtool_used=False: ツール実行痕跡が見当たらなかったtool_used=None: そのプロバイダのレスポンスからは判定できなかった(フィールドが無い等)
注意:
tool_used=Falseは「ツールが無効」という意味ではなく、「痕跡が取れなかった」という意味です。- プロバイダによっては、ツールの実行がサーバ側で完結し、クライアントには詳細な tool calls が返らないことがあります。
実装は src/llm_trader_battle/llm_clients/ 配下にあります。
-
GPT(Azure OpenAI Responses API)
- 有効化:
web_searchツールをリクエストに含めます。 - 痕跡:
response.output内のweb_search_callなどを拾ってtool_traceに記録します。
- 有効化:
-
Claude(claude_agent_sdk)
- 有効化: preset(
claude_code)を使いつつ、allowed_tools=["WebSearch", "WebFetch"]で WebSearch/WebFetch を許可します。 - 痕跡:
ToolUseBlock(WebSearch/WebFetch)をtool_trace.tool_usesに記録します。
- 有効化: preset(
-
Gemini(Google Generative AI SDK)
- 有効化:
toolsにgoogle_searchとurl_contextを設定します。 - 痕跡:
grounding_metadata/citation_metadata/url_context_metadataの有無をtool_traceに記録します。- これらが出ていない場合でも、将来SDK/モデル挙動が変わる可能性はあります。
- 有効化:
-
Grok(xAI SDK)
- 有効化:
search_parametersで Live Search を有効化し、sources=["web","x"]、mode="auto"を指定します。 - 痕跡:
usage.num_sources_used(検索で使われたソース数)や citations の件数をtool_traceに記録します。tool_callsが空でもnum_sources_used>0なら「検索が動いた」と判定できます。
- 有効化:
- 週IDは 月曜日の日付(JST) を
YYYY-MM-DDで用います(週末のピック時に翌週の月曜を自動算出)。
- 取引日: 平日かつ
jpholidayで祝日判定されない日。 - 追加の休場日:
data/calendar/manual_closed_dates.jsonに ISO 日付 (YYYY-MM-DD) の配列で記述すると、その日も非取引日扱いになります(半休日や臨時休場日を想定)。 - 週内の最初に取得できた始値を「購入価格」とみなし、以降の日次終値でリターンを算出。
- 週末ピック:
0 23 * * SAT(JST 日曜 08:00) - 日次価格取得:
0 7 * * *(JST 16:00) - 日次集計:
0 8 * * *(JST 17:00)
すべて contents: write 権限で自動コミット/Pushします。失敗時は GitHub Actions の通知に任せます。
- 日次レポート (
data/result/result-<date>.md/.json): 当日の始値(週内の初回取引日)と終値から銘柄別リターン・LLM平均を掲載。 - 月次サマリ (
reports/<YYYYMM>/summary.md): 日ごと×LLMごとの平均リターン表、折れ線グラフ(summary.png)、週ごとのホールディング一覧(週初→週末の銘柄)が含まれます。
-
LLM 呼び出しロジック:
src/llm_trader_battle/llm_clients/配下の各クライアントを運用環境に合わせて調整してください(プロンプトやフォーマット変更など)。 -
レポート形式: 必要に応じてチャートやより詳細な指標(勝率、ドローダウン等)を追加してください。
-
GPT (Azure OpenAI Responses API):
AZURE_OPENAI_ENDPOINT,AZURE_OPENAI_API_KEY,AZURE_OPENAI_API_VERSION,AZURE_OPENAI_DEPLOYMENT_GPT(例:gpt-5.1-thinking) -
Grok (公式SDK/Responses互換):
GROK_ENDPOINT,GROK_API_KEY,GROK_MODEL(例:grok-4) — 別名としてXAI_ENDPOINT,XAI_API_KEYも使用可。 -
Gemini (Google Generative AI SDK):
GEMINI_API_KEY, optionalGEMINI_MODEL(例:gemini-2.5-pro) -
Claude (Anthropic SDK):
ANTHROPIC_API_KEY, optionalCLAUDE_MODEL(例:claude-4.5-sonnet) いずれか欠けた場合はエラーになります。
開発時はリポジトリ直下の .env に上記キーを記載すれば、自動で読み込まれます(python-dotenv を利用)。