データを蓄積する場所の選定は、システムの寿命を決める。
3,000社 × 5年 × 4四半期 = 60,000行。1枚のシートに収めようとすると、スクロールが重くなり、AI がコンテキストとして読む際にも余分なデータが乗り続ける。八雲の金融データ収集基盤 Medallion では「BigQuery を立てるほどでもないが、単一シートに詰め込むには多すぎる」という中間地点の設計を選んだ。本稿ではその構造と落とし穴を共有する。
課題: 全データを 1 シートに置くと描画が重くなる
日本の上場企業数は約 3,800 社。四半期ベースで過去 5 年を持つと 1 社あたり最大 20 行になり、PL だけで 76,000 行、BS・CF・1株指標・業績予想まで含めると数十万セルになる。
1 枚のシートにこれを収める問題点は 3 つある。描画の遅延(ブラウザのスクロールが重くなり、AI エージェントが GWS CLI で広いレンジを取得する際もレスポンスが落ちる)、更新の競合(当年度のスクリプトが過去年度の行を上書きするリスクと、スキーマ変更時の影響範囲が不明確になること)、スキーマの硬直性(新年度から列を追加したくても過去行との整合性チェックが必要になること)だ。
アプローチ: master + 年度別 8 タブ構成
Medallion が採用した設計は、スプレッドシートを年度ごとに分割し、各年度スプレッドシートを 8 タブで構成するものだ。
スプレッドシート構成:
| スプレッドシート | 用途 |
|---|---|
master | 銘柄マスター(会計基準・セクター・市場区分) |
2021 〜 2026 | 各年度の決算データ(年度ごとに独立) |
各年度スプレッドシートの 8 タブ:
| タブ | 内容 |
|---|---|
pl | 損益計算書(Revenue, Op_Income, Net_Income 等) |
bs | 貸借対照表(Total_Assets, Equity 等) |
cf | キャッシュフロー計算書(Operating_CF, FCF, Capex 等) |
per_share | 1株指標・配当統合(EPS, BPS, Dividend_Annual 等) |
forecast | 業績予想(Forecast_Revenue, Forecast_Op_Income 等) |
segments | セグメント別データ(1社1期間で複数行) |
plans | 中期経営計画(Target_ROE, Progress_Revenue 等) |
metrics | GAS 計算指標(ROE, YoY 前年同期比 等) |
行のキーは Ticker(証券コード)と Period(2024.03-Q1 形式)の 2 列。前年同期比は metrics タブの GAS 計算式が前年行を VLOOKUP して自動算出するため _prev 列は持たない。行数は「実績期間数」と一致するのでシンプルだ。会計基準(JGAAP / IFRS / US-GAAP)は master.meta タブにのみ置き、各年度タブから参照する設計で、基準変更があっても 1 箇所の更新で済む。
蓄積するスクリプトとビューする人間・AI の動線
書き込み側: history_runner.sh が Period の fiscal_year(例: 2024.03-Q1 → 2024)を見て、書き込み先を config/services/gws.json の sheets.earnings.ja.{FY} から引く。スプレッドシート ID はこの設定ファイルの SSOT に集約し、スクリプト内へのハードコードは禁止だ。
Period "2024.03-Q1" → fiscal_year = 2024 → sheets.earnings.ja.2024
Period "2021.06-FY" → fiscal_year = 2021 → sheets.earnings.ja.2021
読み取り側: AI エージェント(montage 等)が参照する場合は GWS CLI で年度・タブ・行範囲を絞って取得する。年度別分割により「2024年度のトヨタの PL 全期間」のような最小レンジ取得が最初から可能で、不要な年度のデータをコンテキストに乗せなくて済む。launchd が毎朝 5 時に TDnet 速報を追従して当年度シートを更新し、過去年度は原則として触らない。
共有ドライブ配下に置く運用ルール
スプレッドシートは全て八雲共有ドライブ(planningEquityJa フォルダ)配下に作成する。My Drive に置くとアカウント変更時にアクセスが失われるリスクがある。
gws sheets spreadsheets create は親フォルダを直接指定できないため、作成直後に drive files update --addParents=<フォルダID> --removeParents=root で移動するのが必須手順だ。移動後は drive files get --fields=parents で配置先を確認する。スプレッドシートを再作成した際も gws.json の ID を差し替えるだけで他スクリプトへの影響がゼロになる。
運用してわかった効果と落とし穴
効果: 新年度シートの作成スクリプト(create_earnings_sheets.py)を実行するだけで翌年度の受け皿が自動生成される。8タブのヘッダー定義はコードで管理するのでスキーマの一貫性が保たれる。GAS の metrics タブは年度内で pl / bs タブを参照するため、年度をまたぐクロス参照は前年同期比の 1 列だけに限定される。
落とし穴: segments タブは 1社1期間で複数行になる。他のタブは「Ticker + Period = 1行」なのに、セグメントだけ 1対多の構造になる。この非対称性を見落として Ticker + Period キーで VLOOKUP を組んだところ、最初のセグメント行しか取れなかった。現在は FILTER 関数でセグメント名を含む全行を返す設計に修正している。また metrics タブの前年同期比は最古年度(2021年)に前年シートが存在せず YoY が空になる。スクリプト側に「前年シート未存在なら空白」の処理が必要だった。
まとめ
BigQuery を構築・運用する工数を払えないが、単一シートではスケールしない。その中間地点として「GWS スプレッドシート × 年度別タブ設計」は実用的な選択肢になる。
設計のポイントは 3 点だ。年度単位で分割して更新競合とスキャン範囲の爆発を防ぐ。科目ごとにタブを分けて列の増減を局所化する。スプレッドシート ID は gws.json の SSOT に集約してハードコードを排除する。
この構造の上に launchd 自動収集と montage 等の読み取りクライアントが接続している。収集の自動化の詳細は launchd × TDnet 速報追従 を、XBRL パーサの実装は xbrl-parser 詳説 を参照してほしい。