外部分析チームの Google スプレッドシートを開きながら「ハイパースケーラーの CapEx をまとめたページがあったはず。あれに営業CFも並べたい」と頼んだのが朝の最初の依頼だった。たった1セクション足すだけの軽い作業のつもりだったのに、終わってみれば Koyfin の TSV 取り込みパイプラインの隠れバグを2本踏み抜いて、半日が溶けていた。
ページの構成を思い出す
/memory-makers/hyperscaler-capex の中身を Claude Code に追わせて、データの出所を整理させた。
- 年次 CapEx は外部分析チームのスプレッドシート由来(
hyperscalerCapexAnnual) - 四半期 CapEx は Koyfin EAC 由来(
hyperscalerCapexQuarterly)
CapEx と同じ Koyfin EAC テーブル(eac_quarterly)にメトリクス Cash-from-Operations がそのまま入っているのが分かったので、generate-hyperscaler-capex-quarterly.mjs をコピーしてメトリクス名だけ差し替えた generate-hyperscaler-ocf-quarterly.mjs を生成させた。
ページ側は CapEx の「四半期テーブル」直後・「年次 CapEx」の前に、同じ構造(個社別小チャート → 全社合計 → 比較テーブル)でセクションを差し込んだ。ついでに「CapEx / 営業CF 比率」テーブルも追加して、設備投資が営業 CF の何 % を食っているかを並べた。
ここまでは30分で済んだ。崩れたのはこの後の「実データ更新」だった。
DB の最新実績が半年止まっていた
スクショで dev サーバー上のページを見ると、最新実績クォーターが CY2025Q3 までしか出ていない。MSFT は5月に CY2026Q1(= 3Q FY2026A)の決算を出しているはずなので、画面の数字がちょうど半年遅れている。
/check-earnings の日次更新対象は NVDA / MU / SNDK の3銘柄だけで、ハイパースケーラー7社は 手動で Koyfin EAC を引いて取り込む運用だった、と Claude Code が思い出させてくれた。
Chrome 拡張の postMessage ブリッジで7社一気にDL
chrome-extension-kofyin には Phase 2 で実装した postMessage ブリッジ(window.__kofyin.runSingleDownload(...))が既にある。Chrome DevTools MCP 経由で MSFT タブを操り、
- ticker ごとに EAC ページの URL を解決
runSingleDownloadを呼んで TSV をダウンロード
を Claude Code に順次回させた。1社あたり約30秒。MSFT → GOOGL → AMZN → META → AAPL → ORCL → CRWV の7社で3分強。並列にしたい欲を抑えて直列で回したのは、Koyfin 側の rate limit を踏みたくなかったから。
DL したファイルが「ダウンロード」「ダウンロード (1)」…「ダウンロード (8)」という汎用名で保存されていて、subfolder/filename 指定が効いていない様子だった。中身の TSV は正しく ticker と timestamp を持っていたので、リネーマ経由で actual-and-consensus/2026-06-26/ に正規ファイル名で並べ直してから SQLite に流し込んだ。
「TSV さえ正しければ取り込みは通るはず」とこの時は思っていた。
取り込みが通ったのに数字がズレる
eac_tsv_to_sqlite.py が「7社インポート成功」と出して、apps/web/data/koyfin.db に同期したあと、念のため MSFT の最新行を直接 SELECT した。出てきた結果が、
1Q FY2026A = Mar-31-2026
Koyfin の画像と照らすと、画像側では同じ 1Q FY2026A が Sep-30-2025。6ヶ月ズレている。画面の数字がうまそうに見えても、period_ending の月日が違うので意味が反対になっている。**取り込みが「通った」のに「正しくない」**というのが一番たちが悪い。
バグ1: CY(Calendar Year)ラベルを弾いていた
eac_tsv_to_sqlite.py を grep で読んでもらうと、200行目あたりで
if header_row[0] not in ("Fiscal Years", "Fiscal Quarters"):
return # スキップ
という早期 return が刺さっていた。今回 Koyfin から落ちてきた TSV のヘッダは Calendar Years / Calendar Quarters。条件にヒットせず 全行スキップ されていた。「7社インポート成功」のログは出るのに DB は1行も更新されない、という嫌な状態。
修正は ("Fiscal Years", "Fiscal Quarters", "Calendar Years", "Calendar Quarters") の4本立てに広げるだけ。fetched_at が 2025-12-14 のまま固まっていた既存6社のレコードを一旦削除してから、再インポートさせた。
バグ2: period_ending が「全社共有」の設計だった
これでも MSFT の period_ending は妙な日付のまま。さらに掘ると get_or_create_eac_period(period_label, ...) が ticker を引数に取らず、period_label だけで periods 行を共有していた。
具体的には、ORCL は5月決算なので 1Q FY2026A の period_ending は Aug-31-2025。それが先に DB に入っていたために、後から取り込んだ MSFT / GOOGL / AMZN の 1Q FY2026A も同じ Aug-31-2025 を参照してしまう。Koyfin の 1Q FYxxxxA というラベルは会計年度を全社で共有してはいけない概念なのに、テーブル設計が共有を許してしまっていた。
「設計を直す」のが正攻法だが、ratio table が壊れたまま明日に持ち越したくなかったので、最短のワークアラウンドとして CY ラベル(1Q CY2026A 等)の period_ending を カレンダー末日に一括 UPDATE した。CY ラベルは定義上 calendar quarter なので、ticker 横断で同じ日付になっても問題が起きない。
UPDATE eac_periods
SET period_ending = '2026-03-31'
WHERE period_label = '1Q CY2026A';
これで MSFT の最新実績が 1Q CY2026A = Mar-31-2026 = 46,679 に揃って、Koyfin の画像と数字も日付も完全一致した。
dev サーバーの HMR が取りこぼした
生成スクリプトを再実行して TS データを吐かせたあと、ページをリロードしても「最新実績: 2025Q3」のまま動かない。データ TS を cat で直接確認すると 2026Q2 まで入っている。dev サーバーが app/data/memory-makers/ 配下のファイル変更を拾い切れていない様子だった。
Windows + pnpm dev でたまにある現象なので、3000番のプロセスだけを Get-NetTCPConnection -LocalPort 3000 で特定して落とし、pnpm dev を立て直した。リロード後、「最新実績: 2026Q2」表示で MSFT の46,679も乗った。OCFセクションも個社別チャート・全社合計・比率テーブルが全部描画された。
CapEx / 営業CF 比率テーブルは2025Q1〜Q3 の3行表示。AAPL と CRWV の Freeプラン制限で2025Q4以降が全社揃わないので絞り込みが効いて3行に減っているのが原因で、ロジックは正しい。
おまけ: 簿記3級ページの HTML 仕様違反
作業中、dev サーバーのコンソールに
<table> 直下に <tr> があります(<tbody> で包んでください)
という警告が accounting-app-play-gimmicks.vue から流れていた。ハイパースケーラーの作業とは無関係だが、目に入った警告は今のうちに潰しておきたいタチなので、該当箇所2つを <tbody> で包んで終わらせた。「ハイパースケーラーの確認のためにそのページ開いてくれてたんですよね?」と聞かれて慌てて軌道修正したが、警告ゼロにはなった。
学び
- 「取り込みが通った」ログを信用しない。画面の数字と外部の正本(今回は Koyfin の画像)を1組だけでも目視で突き合わせると、period_ending のズレのような「成功扱いのバグ」を1分で踏める
eac_periodsのような「外部キーで共有される寸法表」は、共有していい単位(CY ラベル)と共有してはいけない単位(FY ラベル)を、テーブル設計の段階で分けるのが正しい。今回は CY ラベルの一括 UPDATE で逃げたが、FY ラベル側を ticker ごとに分離する改修が宿題として残っている- Chrome 拡張の postMessage ブリッジは「Chrome DevTools MCP からポップアップをクリックできない」問題の優れた回避策。7社×30秒で3分強で済むので、月次運用ならこれで十分
明日に残したこと
進捗は memo/2026-06-26/hyperscaler-capex-ocf-progress.md に置いた。明日やりたいのは、
-
get_or_create_eac_periodを ticker 単位に分離する設計改修(FYラベル側) - AAPL / CRWV の Freeプラン制限で歯抜けになる列の扱い方(直近4Qのみ表示にする等)
- CapEx / 営業CF 比率テーブルの空欄行を「N/A」で埋めて行数を保つ
関連ファイル
- ページ:
apps/web/app/pages/memory-makers/hyperscaler-capex.vue - CapEx 四半期スクリプト(元ネタ):
apps/web/scripts/generate-hyperscaler-capex-quarterly.mjs - 今回作った営業CF スクリプト:
apps/web/scripts/generate-hyperscaler-ocf-quarterly.mjs - 取り込みバグ本体:
chrome-extension-kofyin/scripts/eac_tsv_to_sqlite.py - 進捗メモ:
memo/2026-06-26/hyperscaler-capex-ocf-progress.md