NVIDIA EPS予測ページ リファクタリング計画
新規セッションへの指示(最重要)
Claude Codeへ: このドキュメントを読んでいる場合、以下の手順で作業を進めてください。
1. 現状確認
# メインファイルは読み込み不可(34,075トークン > 25,000上限)
# 部分読み込みで対応
Read apps/web/app/pages/financial-quiz/nvidia-eps-forecast.vue offset=1 limit=100
Read apps/web/app/pages/financial-quiz/nvidia-eps-forecast.vue offset=950 limit=100
2. 進捗確認
このドキュメントの「完了ステータス」セクションを確認し、未完了のPhaseから作業開始。
3. 作業時のルール
- 1つのPhaseが完了したら必ず「完了ステータス」を更新
- 問題が発生したら「作業ログ」に記録
- 動作確認はChrome DevTools MCPで実施
4. 作業完了後
このドキュメントの「完了ステータス」と「作業ログ」を更新してからセッション終了。
クイックスタート
このドキュメントの目的
Claude Codeで読み込めないほど大きいnvidia-eps-forecast.vue(2586行)を、切り出し済みコンポーネントを活用してリファクタリングする。
作業開始コマンド
cd apps/web
pnpm dev
開発サーバー起動後、http://localhost:3000/financial-quiz/nvidia-eps-forecast で動作確認。
メインファイルの部分読み込み方法
# template部分(感応度パネル)
Read apps/web/app/pages/financial-quiz/nvidia-eps-forecast.vue offset=1 limit=100
# template部分(チャート)
Read apps/web/app/pages/financial-quiz/nvidia-eps-forecast.vue offset=80 limit=200
# script部分(import)
Read apps/web/app/pages/financial-quiz/nvidia-eps-forecast.vue offset=950 limit=50
# script部分(composable使用)
Read apps/web/app/pages/financial-quiz/nvidia-eps-forecast.vue offset=1414 limit=50
# script部分(forecastData)
Read apps/web/app/pages/financial-quiz/nvidia-eps-forecast.vue offset=1550 limit=100
# style部分
Read apps/web/app/pages/financial-quiz/nvidia-eps-forecast.vue offset=2009 limit=100
進捗トラッキング(重要)
セッション間での引き継ぎ方法
新規セッションでの開始手順:
- このドキュメントを読む:
Read apps/web/content/2025-12-18/nvidia-eps-forecast-refactoring-plan.md - 下の「完了ステータス」を確認
- 未完了のPhaseから作業再開
完了ステータス
各Phase完了時に [ ] を [x] に更新してください。
Phase 1: script部分の整理
[x] 1-1. importの整理
[x] 1-2. composable呼び出しの統一
[x] 1-3. 重複ロジックの削除
[x] 1-4. 動作確認
Phase 2: SensitivityPanelの統合
[x] 2-1. コンポーネント置き換え
[x] 2-2. スタイル削除
[x] 2-3. 動作確認
Phase 3: ForecastSummaryの統合
[x] 3-1. コンポーネント置き換え
[x] 3-2. スタイル削除
[x] 3-3. 動作確認
Phase 4: DualSeriesChartの統合
[x] 4-1. データ変換computed追加
[x] 4-2. 年間EPSチャート置き換え
[x] 4-3. 年間売上チャート置き換え
[x] 4-4. 四半期EPSチャート置き換え
[x] 4-5. 四半期売上チャート置き換え
[x] 4-6. 動作確認
Phase 5: ChartModal新規作成
[x] 5-1. ChartModal.vue作成
[x] 5-2. メインファイルのモーダル部分を置き換え
[x] 5-3. 動作確認
Phase 6: 最終クリーンアップ
[x] 6-1. 不要なスタイル削除
[x] 6-2. 全体動作確認
[x] 6-3. ファイルサイズ確認(最終: 630行、77%削減)
Phase 7: チャート表示修正
[x] 7-1. 4つのチャートに成長率ライン(折れ線グラフ)のデータラベルを追加
- 年間EPSチャート(YoY成長率%)
- 年間売上高チャート(YoY成長率%)
- 四半期EPSチャート(QoQ成長率%)
- 四半期売上チャート(QoQ成長率%)
[x] 7-2. 実績/予測の区切り線の位置修正(FY25とFY26の間に移動)
- FY26の最後の四半期(4Q'26)が予測なので、FY25が最後の完全実績年度
- getDividerX computed追加で中間位置を計算
[x] 7-3. QoQオーバーライドの初期値修正
- EPSベース→売上ベースに変更(calculateAnalystQoQRates関数を修正)
- 4Q'26: 12.3%→14.9%(売上QoQ)に修正
[x] 7-4. 動作確認
[x] 7-5. QoQオーバーライドを年間サマリーに連動
- annualForecastDataがスライダー値から独立計算していた問題を修正
- quarterlyForecastDataから年度別売上を集計するように変更
- QoQ変更時にサマリー(上昇率、CAGR)が連動更新されるように
[x] 7-6. QoQオーバーライドテーブルを横向きに変更
- 四半期を列、アナリスト予測/修正版を行に配置
- アナリスト予測がない期間(4Q'28以降)は「-」表示、修正版は10.7%で編集可能
- 年間売上成長率スライダーを削除(不要になったため)
Phase 8: 計算詳細テーブルの追加
[x] 8-1. QoQオーバーライドテーブルのカラムをFY28までに制限
- FY29以降(1Q'29~4Q'30)のカラムを削除
- 予測期間をFY30まで(FY31削除)に変更
- FY29以降は一律10.7%を適用(内部計算のみ、UI表示なし)
※ Phase 8-2以降は別ドキュメントに移行:
content/2025-12-18/nvidia-eps-forecast-phase8-calculation-table.md
作業ログ
各セッションで作業した内容を記録してください:
[2025-12-18 セッション1]
- 計画ドキュメント作成
- 現状分析完了
[2025-12-18 セッション2]
- Phase 1 完了: script部分の整理
- eacLightData importを削除(composable内で使用)
- composable変数のエイリアス(composableGrowthRate等)を削除し直接使用
- nvdaData重複取得を削除
- ローカルquarterlyForecastData computedを削除(composable版を使用)
- forecastDataをannualForecastDataベースに変更しYoY計算を追加
- ファイルサイズ: 2754行 → 2596行(158行削減)
- ブラウザ動作確認: 全機能正常動作
[2025-12-18 セッション3]
- Phase 2 完了: SensitivityPanelの統合
- Phase 3 完了: ForecastSummaryの統合
- Phase 4 完了: DualSeriesChartの統合(4チャート全て置き換え)
- ファイルサイズ: 2596行 → 1619行(38%削減)
[2025-12-18 セッション4]
- Phase 5 完了: ChartModal新規作成
- ChartModal.vue作成(~580行、4種類のチャート対応)
- メインファイルのTeleportモーダル部分を置き換え
- ESCキーハンドリングをChartModal内に移動
- ファイルサイズ: 1619行 → 1094行(32%削減)
[2025-12-18 セッション5]
- Phase 6 完了: 最終クリーンアップ
- 未使用のチャート定数削除(chartWidth, chartHeight, padding等)
- 未使用のチャート計算関数・computed削除(~345行)
- 未使用のスタイル削除(chart-section, legend関連、expand-hint等)
- 未使用のimport削除(formatQuarterLabel)
- 最終ファイルサイズ: 1094行 → 630行
- 総削減: 2754行 → 630行(77%削減、2124行削減)
- ブラウザ動作確認: 全機能正常動作(モーダル含む)
[2025-12-18 セッション6]
- Phase 7 完了: チャート表示修正
- 7-1: DualSeriesChart.vueに成長率ラインのデータラベル追加
- アナリスト成長率(青):ライン上部に%表示
- 修正版成長率(赤):ライン下部に%表示
- 7-2: 区切り線位置をFY25とFY26の中間に修正
- getDividerX computed追加
- 7-3: QoQ初期値計算をEPSベース→売上ベースに変更
- useNvidiaForecast.tsのcalculateAnalystQoQRates関数を修正
- 4Q'26のQoQ: 12.3%→14.9%に修正
- 7-4: ブラウザ動作確認完了
- 7-5: QoQオーバーライドを年間サマリーに連動
- 問題: annualForecastDataがスライダー値から独立計算、QoQ変更が反映されず
- 解決: quarterlyForecastDataから年度別売上を集計するaggregateByFiscalYear関数追加
- 検証: QoQ 14.9%→30%変更時にサマリーが$647→$732、CAGR 24.3%→26.9%に連動更新
[2025-12-18 セッション7]
- Phase 7-6 完了: QoQオーバーライドテーブル横向き変更
- SensitivityPanel.vue: テーブルレイアウト変更(四半期=列、アナリスト/修正版=行)
- 年間売上成長率スライダー削除(不要)
- Phase 8-1 完了: 予測期間の調整
- useNvidiaForecast.ts: calculateDisplayQoQRates関数作成
- QoQオーバーライドUI表示をFY28まで(4Q'26〜4Q'28)に制限
- 予測期間をFY30まで(FY31削除)に変更
- FY29〜30は内部計算で一律10.7% QoQを使用
- Phase 8用の新規ドキュメント作成
- nvidia-eps-forecast-phase8-calculation-table.md
ファイル構成
メインファイル(リファクタリング完了)
apps/web/app/pages/financial-quiz/nvidia-eps-forecast.vue
- 630行(リファクタリング前: 2754行、77%削減)
切り出し済みコンポーネント(使用中)
apps/web/app/components/financial-quiz/nvidia-eps-forecast/
├── index.ts (7行) - エクスポート定義
├── types.ts (90行) - 型定義
├── useNvidiaForecast.ts (303行) - ビジネスロジック
├── DualSeriesChart.vue (473行) - 汎用チャートコンポーネント
├── SensitivityPanel.vue (214行) - 感応度パネル
├── ForecastSummary.vue (113行) - サマリーカード
└── ChartModal.vue (580行) - 拡大モーダル(新規作成)
メインファイルの構造マップ
template部分(1-950行頃)
| 行番号 | 内容 | 対応コンポーネント |
|---|---|---|
| 1-22 | ページヘッダー、イントロ | なし(残す) |
| 24-79 | 感応度パネル(スライダー4つ) | SensitivityPanel.vue |
| 82-230 | 年間EPSチャート(SVG直書き) | DualSeriesChart.vue |
| 232-380 | 年間売上チャート | DualSeriesChart.vue |
| 382-600 | 四半期EPSチャート | DualSeriesChart.vue |
| 602-780 | 四半期売上チャート | DualSeriesChart.vue |
| 782-850 | サマリーセクション | ForecastSummary.vue |
| 852-950 | モーダル(4チャート拡大表示) | 新規ChartModal.vue |
script部分(950-2007行)
| 行番号 | 内容 | 状態 |
|---|---|---|
| 950-970 | import文 | useNvidiaForecastインポート済み |
| 970-1000 | composableからの変数取得 | 部分的に使用 |
| 1000-1420 | チャート座標計算(computed多数) | 重複 |
| 1414-1423 | composable変数のエイリアス | 中途半端 |
| 1425-1700 | forecastData計算ロジック | composableと重複 |
| 1700-2007 | 追加のcomputed、ユーティリティ | 一部残す必要あり |
style部分(2009-2586行)
- 約577行のスタイル
- 各コンポーネントに移動済みのものと重複あり
各コンポーネントの詳細
1. useNvidiaForecast.ts
パス: apps/web/app/components/financial-quiz/nvidia-eps-forecast/useNvidiaForecast.ts
返り値:
return {
// パラメータ(ref)
annualGrowthRate, // ref(50) - 年間成長率 10-100%
netProfitMargin, // ref(55) - 純利益率 40-70%
perMultiple, // ref(25) - PER倍率 15-45x
dilutionRate, // ref(2.5) - 希釈率 0-5%
currentStockPrice, // ref(175) - 現在株価
targetQoQGrowth, // computed - QoQ成長率
// データ
historicalQuarters, // computed - 過去8四半期履歴
historicalAvgGrowth, // computed - 過去平均成長率
historicalAvgMargin, // computed - 過去平均利益率
quarterlyForecastData, // computed - 四半期予測データ
annualForecastData, // computed - 年間予測データ
// サマリー
finalYear, // computed - 最終年度データ
yearsToFinal, // computed - 最終年度までの年数
epsDiff, // computed - EPS差異%
stockDiff, // computed - 株価差異%
finalModifiedStockPrice, // computed - 修正版最終株価
upside, // computed - 上昇率%
cagr // computed - CAGR%
}
2. SensitivityPanel.vue
パス: apps/web/app/components/financial-quiz/nvidia-eps-forecast/SensitivityPanel.vue
Props:
interface Props {
annualGrowthRate: number
netProfitMargin: number
perMultiple: number
dilutionRate: number
currentStockPrice: number
targetQoQGrowth: number
historicalAvgGrowth: number
historicalAvgMargin: number
}
Emits:
'update:annualGrowthRate': [value: number]
'update:netProfitMargin': [value: number]
'update:perMultiple': [value: number]
'update:dilutionRate': [value: number]
使用例:
<SensitivityPanel
v-model:annualGrowthRate="annualGrowthRate"
v-model:netProfitMargin="netProfitMargin"
v-model:perMultiple="perMultiple"
v-model:dilutionRate="dilutionRate"
:currentStockPrice="currentStockPrice"
:targetQoQGrowth="targetQoQGrowth"
:historicalAvgGrowth="historicalAvgGrowth"
:historicalAvgMargin="historicalAvgMargin"
/>
3. ForecastSummary.vue
パス: apps/web/app/components/financial-quiz/nvidia-eps-forecast/ForecastSummary.vue
Props:
interface Props {
epsDiff: number
stockDiff: number
upside: number
cagr: number
currentStockPrice: number
finalModifiedStockPrice: number
yearsToFinal: number
}
使用例:
<ForecastSummary
:epsDiff="epsDiff"
:stockDiff="stockDiff"
:upside="upside"
:cagr="cagr"
:currentStockPrice="currentStockPrice"
:finalModifiedStockPrice="finalModifiedStockPrice"
:yearsToFinal="yearsToFinal"
/>
4. DualSeriesChart.vue
パス: apps/web/app/components/financial-quiz/nvidia-eps-forecast/DualSeriesChart.vue
Props:
interface Props {
title: string
metricLabel: string
data: DualSeriesDataItem[]
xAxisLabel?: string // default: '期間'
yAxisLabel?: string // default: '値'
growthAxisLabel?: string // default: '成長率 (%)'
growthLabel?: string // default: '成長率'
showGrowthLine?: boolean // default: false
isEps?: boolean // default: false
isQuarterly?: boolean // default: false
}
DualSeriesDataItem型(types.tsで定義):
interface DualSeriesDataItem {
label: string
isActual: boolean
hasAnalystData: boolean
analystValue: number | null
modifiedValue: number
analystGrowth?: number | null
modifiedGrowth?: number | null
}
Emits:
'chart-click': []
実装手順(Phase別)
Phase 1: script部分の整理
目標: 重複ロジックを削除し、composableを完全活用
手順:
- メインファイルの950-1000行を確認(import部分)
- 以下のimportを追加/確認:
import {
useNvidiaForecast,
SensitivityPanel,
ForecastSummary,
DualSeriesChart
} from '~/components/financial-quiz/nvidia-eps-forecast'
- composableの呼び出し:
const {
annualGrowthRate,
netProfitMargin,
perMultiple,
dilutionRate,
currentStockPrice,
targetQoQGrowth,
historicalQuarters,
historicalAvgGrowth,
historicalAvgMargin,
quarterlyForecastData,
annualForecastData,
epsDiff,
stockDiff,
finalModifiedStockPrice,
upside,
cagr,
yearsToFinal
} = useNvidiaForecast()
- 削除対象(composableと重複):
- 1414-1423行: composable変数のエイリアス
- 1425-1700行: forecastData計算ロジック
- 1000-1420行の大部分: チャート座標計算
- 残す必要があるもの:
- モーダル制御(modalChart, openModal, closeModal)
- チャート用のデータ変換computed
Phase 2: SensitivityPanelの統合
削除対象: メインファイル24-79行
置き換え:
<!-- 削除: 24-79行の<div class="sensitivity-panel">...</div> -->
<!-- 追加 -->
<SensitivityPanel
v-model:annualGrowthRate="annualGrowthRate"
v-model:netProfitMargin="netProfitMargin"
v-model:perMultiple="perMultiple"
v-model:dilutionRate="dilutionRate"
:currentStockPrice="currentStockPrice"
:targetQoQGrowth="targetQoQGrowth"
:historicalAvgGrowth="historicalAvgGrowth"
:historicalAvgMargin="historicalAvgMargin"
/>
Phase 3: ForecastSummaryの統合
削除対象: メインファイル782-850行付近のサマリーセクション
置き換え:
<ForecastSummary
:epsDiff="epsDiff"
:stockDiff="stockDiff"
:upside="upside"
:cagr="cagr"
:currentStockPrice="currentStockPrice"
:finalModifiedStockPrice="finalModifiedStockPrice"
:yearsToFinal="yearsToFinal"
/>
Phase 4: DualSeriesChartの統合
追加が必要: データ変換computed
// 年間EPSチャート用データ変換
const annualEpsChartData = computed((): DualSeriesDataItem[] => {
return annualForecastData.value.map(item => ({
label: item.year,
isActual: item.isActual,
hasAnalystData: item.analystEps !== null,
analystValue: item.analystEps,
modifiedValue: item.modifiedEps,
analystGrowth: null, // YoYが必要なら計算
modifiedGrowth: null
}))
})
// 年間売上チャート用
const annualRevenueChartData = computed((): DualSeriesDataItem[] => {
return annualForecastData.value.map(item => ({
label: item.year,
isActual: item.isActual,
hasAnalystData: item.analystRevenue !== null,
analystValue: item.analystRevenue ? item.analystRevenue / 1000 : null, // Billionsに変換
modifiedValue: item.modifiedRevenue / 1000,
analystGrowth: null,
modifiedGrowth: null
}))
})
// 四半期も同様に変換
置き換え:
<DualSeriesChart
title="年間EPS予測チャート"
metricLabel="EPS"
:data="annualEpsChartData"
xAxisLabel="会計年度"
yAxisLabel="EPS ($)"
:isEps="true"
@chart-click="openModal('annual-eps')"
/>
Phase 5: ChartModal新規作成
新規ファイル: apps/web/app/components/financial-quiz/nvidia-eps-forecast/ChartModal.vue
基本構造:
<template>
<div v-if="open" class="modal-overlay" @click.self="close">
<div class="modal-content">
<button class="close-btn" @click="close">×</button>
<DualSeriesChart
:title="chartTitle"
:data="chartData"
:metricLabel="metricLabel"
v-bind="chartProps"
/>
</div>
</div>
</template>
<script setup lang="ts">
import DualSeriesChart from './DualSeriesChart.vue'
import type { DualSeriesDataItem } from './types'
interface Props {
open: boolean
chartType: 'annual-eps' | 'annual-revenue' | 'quarterly-eps' | 'quarterly-revenue' | null
// 各チャートタイプに対応するデータ
annualEpsData?: DualSeriesDataItem[]
annualRevenueData?: DualSeriesDataItem[]
quarterlyEpsData?: DualSeriesDataItem[]
quarterlyRevenueData?: DualSeriesDataItem[]
}
const props = defineProps<Props>()
const emit = defineEmits<{ 'update:open': [value: boolean] }>()
const close = () => emit('update:open', false)
// chartTypeに応じてデータとpropsを切り替え
const chartData = computed(() => { /* ... */ })
const chartTitle = computed(() => { /* ... */ })
</script>
動作確認チェックリスト
各Phase完了後に確認
- スライダー4つが動作する
- スライダー変更でチャートが更新される
- サマリーカードの数値が更新される
- チャートクリックでモーダルが開く
- モーダル内のチャートが正しく表示される
- レスポンシブ(モバイル)で崩れない
ブラウザ確認コマンド
# Chrome DevTools MCP使用
start "" "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="%TEMP%\chrome-profile-stable" "http://localhost:3000/financial-quiz/nvidia-eps-forecast"
注意点・トラブルシューティング
1. composableのref vs computed
annualGrowthRate等はrefなので.valueで読み書き可能- v-modelで直接バインド可能
2. データ型の違い
メインファイルのforecastDataとcomposableのannualForecastDataで型が微妙に異なる可能性:
メインファイル(ForecastItem):
interface ForecastItem {
year: string
analystEpsYoY: number | null // YoYを含む
modifiedEpsYoY: number
// ...
}
composable(AnnualForecastItem):
interface AnnualForecastItem {
year: string
// YoYは含まない
// ...
}
→ YoYが必要な場合はデータ変換computedで計算
3. モーダルのチャートサイズ
DualSeriesChart.vueはisQuarterlyでサイズが変わる:
isQuarterly: false→ chartWidth: 800isQuarterly: true→ chartWidth: 1200
モーダル用に別のサイズが必要な場合はpropsを追加
完了後の予想ファイルサイズ
| ファイル | Before | After |
|---|---|---|
| nvidia-eps-forecast.vue | 2586行 | 〜200-300行 |
| useNvidiaForecast.ts | 303行 | 〜350行(YoY計算追加) |
| ChartModal.vue | なし | 〜150行 |
| 他 | 変更なし | - |
トークン数: 34,075 → 〜3,000(読み込み可能に)
新規セッション用プロンプト(コピペ用)
新規セッションを開始する際、以下をコピペしてください:
nvidia-eps-forecast.vueのリファクタリングを続けてください。
計画ドキュメント:
apps/web/content/2025-12-18/nvidia-eps-forecast-refactoring-plan.md
このドキュメントを読んで、「完了ステータス」を確認し、未完了のPhaseから作業を再開してください。
作業が完了したら、ドキュメントの「完了ステータス」と「作業ログ」を更新してください。