[ Data Infrastructure ]

launchd × TDnet — 決算速報を毎朝自動追従する金融データ基盤

macOS の launchd で毎朝5時に TDnet を自動取得し、決算短信 PDF をパースしてスプレッドシートに蓄積するまでの設計と、運用でわかった落とし穴を公開する。

Author: 森本拓見
#claude-code #ai-driven-dev #launchd #financial-data

決算速報は毎朝出る。上場企業の決算短信・業績修正・四半期開示は、東証の適時開示システム TDnet(Timely Disclosure network)に随時掲載される。3月決算企業が集中する4月〜5月は1日あたり数十社が開示を行い、発表は朝8時台が多いが前日夜や日中に差し込んでくることもある。人間が毎朝 TDnet を開いて対象銘柄を拾い、PDF をダウンロードし、数値を転記する作業は、銘柄数が増えるほど現実的でなくなる。

課題: TDnet と各社 IR の発表タイミングの違い

TDnet は東証が運営する一元的な開示窓口で、決算短信はここに集約される。実際に使ってみると3つの難しさがある。

土日祝はデータがない。一覧ページは営業日だけ更新され、前日が祝日だと翌営業日に繰り越されることもある。単純に「今日の日付で取得する」設計では取りこぼしが発生する。

発表予定日と掲載日がずれる。予定日の翌営業日に掲載されることも珍しくなく、直近2〜3日を遡るロジックが必要になる。

開示の種類が多い。決算短信のほかに業績予想修正・配当予想修正なども TDnet に流れてくる。フィルタリングしないと処理対象が膨らむ。

アプローチ: launchd で毎朝5時に自動実行

八雲の内部金融データ基盤 medallion では、macOS の launchd を使って毎朝5時に決算データ取得を自動実行している。plist の核心部分はこうなる。

<key>StartCalendarInterval</key>
<dict>
    <key>Hour</key><integer>5</integer>
    <key>Minute</key><integer>0</integer>
</dict>
<key>EnvironmentVariables</key>
<dict>
    <key>GOOGLE_WORKSPACE_CLI_CONFIG_DIR</key>
    <string>/path/to/gws/profiles/project</string>
    <key>PATH</key>
    <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
</dict>

時刻を5時にしているのは「前日分の取りこぼし確認」を早朝に走らせるためだ。--days-back 2 オプションで直近2日を遡って補完する設計になっている。

EnvironmentVariables に GWS 認証プロファイルのパスと PATH を明示的に設定しているのは、launchd では .zshrc が読み込まれないからだ。これを省くと gws CLI が認証プロファイルを見つけられず、スプレッドシートへの書き込みがサイレントに失敗する。

速報 → スプレッドシート蓄積 → 後段の分析

取得パイプラインの流れはシンプルだ。

TDnet HTML → 開示一覧パース → 対象銘柄フィルタ
    → PDF ダウンロード → XBRL / PDF パーサ
    → GWS スプレッドシートへ書き込み(gws CLI)

スプレッドシートは8タブ構成(pl / bs / cf / per_share / forecast / segments / plans / metrics)で、1行 = 1銘柄 × 1期間の粒度で蓄積される。期間は 2024.03-Q1 形式で年度別スプレッドシートにルーティングして書き込む。

TDnet の HTML 解析では一覧テーブルのクラス属性(kjCode / kjTitle / kjTime)を拠り所に表題・PDF リンクを抽出し、キーワードマッチで開示種別を判定する。後段の分析基盤は共有スプレッドシートを読み取り専用クライアントとして参照する設計だ。

失敗時の通知設計

launchd で自動化すると、失敗が見えにくくなる。ターミナルが開いていないため、エラーは標準エラーに吐き出されて消えるだけだ。StandardOutPath / StandardErrorPath にログファイルパスを設定し、さらに実行スクリプトの末尾で取得結果のサマリ(success / no_pdf / fail 件数)を gws CLI 経由で Gmail に送信する仕組みを組み込んでいる。失敗件数がゼロでないときだけメール送信し、正常終了のノイズを減らしている。

PDF パースが失敗した場合は再試行せず「翌日の開示を待つ」方針にした。ThrottleInterval=3600 を設定することで、実行が失敗してもその後1時間は再実行されず、TDnet への過負荷を防いでいる。

運用してわかった効果と落とし穴

効果として最も実感するのは、開示タイミングを気にしなくなったことだ。対象銘柄の決算を毎朝確認する作業がなくなり、スプレッドシートを開けば蓄積済みのデータが待っている。XBRL が取得できた場合は売上高・営業利益・純利益・配当等が自動入力され、PDF のみの場合は主要指標が部分的にパースされる。

落とし穴は3点あった。

1点目は TCC(フルディスクアクセス)の問題だ。launchd 経由で起動した /bin/bash はターミナルアプリの TCC 権限を継承しない。ファイル読み書きで Operation not permitted が出続け、System Settings の Privacy & Security で実行バイナリを明示的に許可して解消した。

2点目は 発表予定日と TDnet 掲載日のずれだ。JPX が公開する決算発表予定日と実際の掲載日は一致しないことがある。直近2〜3日を遡るスキャンを入れて取りこぼしを減らしたが、ticker フィルタと kind フィルタ(決算短信のみ)を組み合わせてノイズを絞り込む工夫も必要だった。

3点目は XBRL の取得経路だ。TDnet の直接リンクは PDF を指すことが多く、XBRL は別途 EDINET API 経由で取得する必要がある。現在は PDF パーサを主経路とし、XBRL は補助的な位置づけになっている。

まとめ

決算速報の自動追従は「取得→パース→蓄積」のパイプラインを launchd で毎朝回すことで実現できる。設計のポイントは、複数日スキャンによる発表日ずれの吸収・環境変数の明示注入・失敗通知の3点だ。PDF にテキストレイヤーがない書類や XBRL が未整備の開示では精度にばらつきがあり、これらは継続的な改善課題だ。

関連記事: Yakumo の AI 駆動開発のリアル / launchd × Notion Tasks — Claude Code を本番運用に乗せるスケジュール自動化 / 決算短信 XBRL パーサの設計と実装

ShareShare on X