ビートモニタリングに通年ガイダンス・EPSビート率・直近株価を足した日
決算モニタリングのページに、3つの可視化を足した。通年ガイダンスの軌跡チャート、次QのEPSビート率の推移、そして各銘柄の直近株価。どれも「数字を取ってくる」ところが本番で、チャートを描く側はほぼ既存の仕組みに乗せるだけだった。逆に言うと、半日のほとんどは数字の出どころを潰す作業に溶けた。
通年ガイダンスをチャートに足す
これまでのチャートは「次Q売上ガイド」「次QのEPSガイド」しか描いていなかった。会社が出す通年(full-year)の見通しは持っていなかったので、その軌跡を足したかった。
チャートコンポーネントは ChartCfg(key / title / unit / rows / lineSeries)という汎用の設定オブジェクトを受け取る形になっていて、新しい系列を増やすときは新しい key を1つ生やすだけで済む設計だった。これを活かして guide-fy-rev(通年売上ガイド)と guide-fy-eps(通年EPSガイド)の2キーを追加した。
<BeatExpectationsChart
v-if="hasFullYearGuidance"
:only="['guide-fy-rev', 'guide-fy-eps']"
:show-header="false"
/>
v-if="hasFullYearGuidance" を付けたのは、通年ガイドを公表していない銘柄では2枚のチャートをまるごと出さないため。データを持つ銘柄だけ描画する。
DELL のデータで言うと、通年売上ガイドが各発表のたびに 97B → … → 167B へ更新され、通年EPSガイドは 7.80 から 17.90 まで伸びている。1株のバーが1回の決算発表時点の通年見通しを表すので、横に並べると「会社が自分のレンジをどう引き上げていったか」がそのまま軌跡になる。最後のQ1 FY27で通年売上を 140B → 167B へ一気に引き上げた点が、バーの段差で一目で分かる。
各バーの下には対象会計年度のラベル(FY25 / FY26 / FY27)を出すようにした。通年ガイドは途中で対象年度が切り替わる(FY26を見ていたのが、ある四半期からFY27の見通しに変わる)ので、ラベルが無いと「何年度の数字か」が読めなくなる。データ側に fullYearLabel を持たせて、バーごとに添える形にした。
EPSビート率を8四半期つなぐ
次に、次QのEPSガイドがコンセンサス予想をどれだけ上回ったか(ビート率)を、過去8四半期分つなげて見えるようにした。問題はコンセンサス予想で、これは自前では持っていない。
Web(CNBC / LSEG / MarketBeat など)を主軸に、X(Twitter)サーチで補完して各四半期のコンセンサスを拾い集めた。ここで自分に課したのは「取れなかった値は埋めない」というルール。投稿者がどのデータソースを引いているかで数字は多少ブレるので、複数ソースが収束するところだけ採用し、決まらないものは n/a のままにした。捏造して埋めると、あとでチャートの傾きを信じられなくなる。
結果、DELL では -9% 〜 +62% のレンジで8四半期がつながった。直近のQ1 FY27では次QのEPSガイドがコンセンサスを約 +62% 上回っていて、これがビート率チャートの右端で跳ね上がっている。整合性を守るユニットテストも足して、beatPct が (company − consensus) / consensus × 100 の手計算と 0.3pt 以内で一致するか8四半期すべてで検証するようにした。
単一ソース依存だった銘柄を複数ソースで取り直す
ある半導体メモリ銘柄(SNDK)のアナリスト予想が、単一ソース頼みになっていて怪しかった。x-search スキルで5四半期分を並列に取り直し、複数ソースで相互検証させた。
ここで効いたのが「数字が割れたところを潰す」やり方だった。多くの四半期は4〜5ソースが 0.1〜0.2 程度の幅に収束していたが、**Q1 FY26 のEPS予想だけ2倍近く割れていた**。手元の旧データは 0.58(MarketBeat)。一方でX投稿群は「1.22 が38%ビート」と言っていて、逆算すると予想は 0.88 になる。乖離が大きすぎたので、ここだけ追加で検索をかけた。
FactSet を明示する高品質なソースが複数出てきて、正しいコンセンサスは 0.88、旧データの 0.58 は誤りだと決着した。訂正してデータに反映。1ソースを鵜呑みにしていたら、ビート率が実態と逆向きに出るところだった。
この作業では細かいハマりも踏んだ。バックグラウンド検索の出力先を相対パスで書いたら、永続cwdの違いでリダイレクト先のディレクトリが無く、検索は走ったのに結果が捨てられていた(絶対パスで再実行して回収)。さらに抽出スクリプトでは、ls 系は /c/ を解釈するのに Windows ネイティブの Python は C:/ 形式を要求する点と、標準出力が cp932 で落ちる点を踏んで、PYTHONIOENCODING=utf-8 を設定して通した。
直近株価をKoyfin内部APIから取る
株価反応チャートの末尾に、決算とは独立した「直近株価」の点を1つ足したかった。決算後の翌日終値だけだと「今いくらか」が分からないので、現在地を1点だけ中立色・小サイズで添える設計にした。
// 直近株価を末尾に1点だけ足す(決算とは無関係の「現在地」。ビート率は無いので null=中立色)
const ls = data.value.latestStock
// ...
beatPcts.push(null)
肝心の価格時系列は、Koyfin の内部APIのどのキーに入っているかが分からず、ここが一番試行錯誤した。
- 最初に当てた
KOY_103系のキーは全弾き(no-value) - 別のキー候補(
f_ccc系)も外れ - エンドポイント自体を変えて
/api/v3/data/graphを当てると、ようやくヒット - その中の
p_candle_rangeのadjCloseが正解だった
NVDA で直近終値 $214.25 が取れた瞬間に経路が確定したので、取り込みスクリプトを p_candle_range ベースに書き換え、14銘柄の直近終値(2026-05-28時点)を一括取得して、各銘柄のJSONに latestStock(日付+終値)を付与した。
3.3倍の異常値はDOM照合してから採用する
取り込み中、ある銘柄(ALAB)が3週間で約3.3倍(105 → 349)になっていて、最初は取得ミスを疑った。倍率がおかしいときは値を信じる前に出どころを確認する。
Koyfin のリアルタイムクオートと p_candle_range の中身をDOMと突き合わせて照合したところ、これは取得バグではなく決算後の実際の急騰だと確認できた。裏が取れてから採用した。異常値を見たら「まずデータを疑い、画面の数字と照合し、本物だと確認してから入れる」という順番を守れたのは良かった。
テスト
通年ガイダンスのデータ整合性を守るユニットテストを足し、EPSビート率の整合テストも追加した。全22件がパス。チャートはこのデータを描くだけなので、データ側の構造崩れと beatPct の手計算ズレをテストで止められれば、画面の傾きは信じられる。
主な検証項目:
- 通年売上ガイドの
beatPctが手計算と一致(数値コンセンサスがある四半期のみ) - 通年ガイドを持つ四半期には必ず
fullYearLabel(FY2x形式)がある - 通年EPSガイドが
7.80 → … →17.90 へ単調増加 - 次QのEPSガイドが8四半期すべてでコンセンサス+ビート率を持ち、手計算と一致
学び
- チャートの設定を
key単位の汎用オブジェクトにしておくと、系列を足すコストが「キーを1本生やす」だけになる。可視化の仕事の重さは描画側ではなく、ほぼ常にデータ収集側に寄る - アナリスト予想は1ソースを鵜呑みにすると逆符号の事故が起きる。割れたところだけ追加検索して潰すと費用対効果が高い(全部を多重検証する必要はない)
- 外部APIのキー名は推測ではなく、当てて外して当て直して特定するしかない。
p_candle_rangeのadjCloseにたどり着くまで3〜4回外した - 倍率の異常値は取得バグの可能性をまず疑い、画面の数字と照合してから採用する