開発mdx-playground

決算ビートモニタリングを13→20銘柄へ拡張し、3区分の親子構造へ作り替えた

朝に画面を開いたら、モニタリング中の銘柄が13個並んでいた。半導体とソフトウェアが入り混じったカードの列を見て、「これはもう銘柄を足すだけじゃ追いつかない、棚の組み方ごと変えるタイミングだ」と判断した。一日かけて20銘柄まで広げ、見出しの軸を「セクター」から「モニタリング区分」へ丸ごとひっくり返した。

やったこと(全体像)

  • 銘柄を 13 → 20 へ拡張した
  • DELL を8四半期分(Q2 FY25〜Q1 FY27)新規作成し、ServiceNow(NOW) と SNDK / INTC / STX / WDC / AMD を追加した
  • 表示構造を「セクター大見出し」から「区分(tier)大見出し+セクターはカード内タグ」へ逆転させた
  • 途中で DELL がインデックスから消える事故、新規5銘柄が dev で 404 になる事象を踏んだ

DELL を8四半期分まるごと組む

きっかけは Q1 FY27 の決算で、売上 43.8B(+88%)・非GAAP EPS 4.86(+64%ビート)・通年ガイドを 140B→167B へ上方修正という桁違いの数字を見たことだった。これは継続観察に入れるべきだと決めて、過去8四半期(Q2 FY25〜Q1 FY27)を一気に組ませた。

このページに銘柄を足す作業は、毎回この3点セットで決まっている。

  1. app/data/tripleBeat/{TICKER}.json を新規作成(四半期データ本体)
  2. tickerMeta.ts にエントリ登録(区分・セクター・概算時価総額)
  3. summaries.ts を再生成(インデックス用のサマリ)

数値は SEC の 8-K 一次情報を中心に裏取りさせた。Q1 FY27 だけは「翌日終値」がまだ確定していなかった。朝の時点で米国は 5/28 の時間外取引中で、5/29 通常取引の終値が存在しない。いったん時間外値 $439.67(+38.7%、5/28 19:46 EDT時点)を暫定値として入れ、注記に「暫定」と書き残しておいた。

5社は5つのリサーチエージェントを並列で走らせた

SNDK / INTC / STX / WDC / AMD の5社は互いに独立している。「サンディスクは構造転換済みだと思うが、他はそこまでガイダンスが良かったか確認できていない」という疑問もあったので、確認も兼ねて5つのリサーチエージェントを並列起動し、各社の決算履歴を分担して調べさせた。

精度が命なので、指示には2つの縛りを入れた。

  • 各数値にソースを付けること
  • 見つからない値は推測せず n/a にすること

返ってきた結論はこうだった。

  • SNDK / STX / WDC → ガイダンスが実際に大幅ビート&レイズ。構造転換は数字で裏付けが取れた
  • INTC → 株価は2026 YTDで大きく伸びているが、ガイダンスのビートは直近1回のみ。期待先行と判断し「継続ウォッチ」へ

後日、SNDK のアナリスト予想が単一ソース依存で怪しい箇所が見つかり、x-search で複数ソースから取り直した。Q1 FY26 の EPS 予想だけが既存データ 0.58(MarketBeat)に対しX投稿群が予想 0.88 を示唆し、2倍近く乖離していた。複数の高品質ソース(FactSet 明示)で決着させ、旧データ 0.58 は誤りで 0.88 が正しいと確定して直した。「1ソースだけで数字を置くと事故る」を地で踏んだ。

株式分割への対応

ServiceNow(NOW) は2025年12月に5対1の株式分割を実施している。EPS も株価も、分割前の四半期は実値÷5 にして全期間を分割調整後ベースに統一した。ここは新しく作った仕組みではなく、先に NVDA で10対1分割を入れたときの前例にそのまま揃えた。

  • epsLabel / stockLabel に「分割調整後」を明記する
  • tableNotes に分割の事実と「率(ビート率・YoY・騰落率)は分割の影響を受けない」を注記する

前例があると、こういう例外処理を毎回ゼロから悩まずに済む。

表示構造の親子逆転リファクタ

これが今日の本丸だった。これまでは「セクター」が大見出しで、その下に銘柄カードが並んでいた。半導体だけで GPU・光通信・メモリ・CPU と分かれていて、見出しがどんどん細かくなる。一方で本当に見たいのは「この銘柄は構造転換まで行ったのか、まだビート継続の途中なのか、それとも様子見か」という温度感だった。

そこでデータモデルに区分(tier)を足し、見出しの軸を入れ替えた。

export const TIER_LIST = [
  { name: '構造転換済み', note: 'AIインフラの本命' },
  { name: 'ビート継続・成長期待', note: '' },
  { name: '継続ウォッチ', note: 'ビート鈍化・様子見' },
] as const

index.vue 側は「区分でグルーピングし、各区分の中はセクター順 → 時価総額降順で並べる」computed に書き換えた。セクターは大見出しから降格させ、各カードの中のタグ(バッジ)として残した。CSS も .sector-*.tier-* へ書き換えた。同じセクターのカードが隣り合うように2段階ソートにしてあるので、降格してもセクターのまとまりは視覚的に保てている。

最終的な区分はこうなった。

  • 構造転換済み(9): NVDA / AMD / MU / ALAB / CRDO / SNDK / STX / WDC / DELL
  • 継続ウォッチ: INTC / 4062 / SMCI / APP / NOW ほか

踏んだ事故と切り分け

DELL がインデックスから消えた

tier を導入して tickerMeta.ts を再構成した際、DELL のエントリが丸ごと消えていた。インデックスは TICKER_META を回して銘柄を並べる作りなので、メタに無い銘柄は黙って表示されなくなる。「Dell 入れたはずなのに出ない」と気づいて grep したら tickerMeta に DELL が引っかからず、再構成のときに落ちたのが原因だと特定できた。構造転換済み区分へ再追加して復活させた。リストを手で組み直すと、こういう取りこぼしが起きる。

AMD だけ EPS ガイダンスが null

AMD は会社が次Q EPS ガイドを出さない方針で、guidance.epsnull という既存銘柄に無いパターンだった。テーブルのコンポーネントを確認すると、hasEpsGuidance が四半期の guidance.eps を見て false になり、「次Q EPSガイダンスを公表していない」という注記行に切り替わる作りになっていた。実際に AMD ページを開いて、その注記行が出ていることを目で確認した。consensus: "n/a"beatPct: null も optional chaining で安全に描画されていた。

新規5銘柄が dev で 404

JSON は正しくパースされ配置も合っているのに、新規5銘柄のページだけ 404 を返した。既存銘柄は 200 で、ファイル内容も検証済み。ここで「データの問題ではない」と切り分けられた。原因は dev サーバーの Vite が import.meta.glob を再スキャンしていなかったこと。loader.tsimport.meta.glob('./*.json') で銘柄を自動 discovery するが、ファイルを足しても glob のキャッシュが更新されず、新規分が見えていなかった。loader.ts に HMR トリガーを与えて glob を再評価させたら、5銘柄すべて 200 になった。

学び

  • 銘柄が増えてカードが横に伸びてきたら、見出しの軸そのものを疑う。今回は「セクター → 区分」へ逆転させたら、見たい温度感が一目で分かるようになった
  • リストを手で組み直すリファクタは、既存エントリが黙って消える。DELL 消失は grep で一発だったが、表示が消えるだけでエラーは出ないので気づきにくい
  • データが正しいのにページが 404 のときは、まず「ビルドツールが新ファイルを認識しているか」を疑う。import.meta.glob は黙ってキャッシュを返す
  • 数値は1ソースで置かない。SNDK の 0.58 → 0.88 は、複数ソースで突き合わせなければ間違ったまま公開していた

明日以降やること

  • DELL Q1 FY27 の翌日終値を、5/29 の通常取引終値が確定したら暫定値から差し替える
  • import.meta.glob の再スキャン対応を、新銘柄追加のたびに HMR で踏まないか手順として残すか検討する