👋

Vanilla JS × Python × Vercelで株価分析ダッシュボードを個人開発して一般公開するまでにやったこと(と、動的OGPで

に公開

Vanilla JS × Python × Vercelで株価分析ダッシュボードを個人開発して一般公開するまでにやったこと(と、動的OGPで挫折した話)

はじめに

「個人投資家でも、機関投資家と同水準の財務分析UIが欲しい」

そんな動機から、Strategic Investment Portal という株価・財務データ可視化ダッシュボードを個人開発し、Vercelで一般公開しました。

https://strategic-investment-portal.vercel.app

この記事では、設計の意思決定から実装の苦労話、そして「動的OGPを断念した」リアルな葛藤まで、開発の全記録を残します。


何を作ったか

日本株20社・米国株・ETFを含む100銘柄の財務データを可視化するSPAです。

主な機能

機能 詳細
ローソク足チャート MA5/25/75日線・出来高バーをトグル切替
財務3表グラフ BS / PL / CF を Chart.js で可視化
KPIカード 営業利益率・ROE・PER等を3年度比較
セクターフィルター 日本株・米国株・ETF・ウォッチリストで絞り込み
スクリーニング PER/PBR/自己資本比率/営業利益率でフィルタ
ウォッチリスト ☆ボタンで登録、localStorageで永続化
CSVエクスポート 財務3年分のデータをワンクリックで出力
多通貨対応 日本株(JPY/百万円)・米国株(USD/百万ドル)を自動切替

技術スタック

フロントエンド  : HTML / CSS / Vanilla JS (SPA, 1ファイル構成)
バックエンド    : Python (yfinance + EDINET → SQLite)
データ生成      : SQLite → data.js (静的JSONファイル)
デプロイ        : Vercel (静的ホスティング)
グラフライブラリ : Lightweight Charts 4.2.3(ローソク足)
                  Chart.js v4 + chartjs-plugin-datalabels(財務グラフ)

なぜNext.jsではなくVanilla JSにしたのか

最初はNext.js (App Router) での実装を検討しました。しかし今回の要件を整理すると:

  • データは静的(バッチ処理で生成したJSファイル)
  • APIサーバー不要
  • リアルタイム更新不要

動的なサーバーが不要なら、Vanilla JS + 静的ホスティングが最速」という結論に至りました。Reactのビルドパイプラインを抱えず、index.html 1ファイルで動く構成はデバッグも速く、Vercelへのデプロイも一瞬です。


バックエンド:Pythonパイプラインの構成

yfinance API

auto_terminal_filter.py(データ取得・クレンジング)

weather.db(SQLite)

get_stock_multi.py(data.js生成)

index.html が読み込む

DBテーブル設計

-- 銘柄マスタ
ticker_master (
  ticker TEXT,
  company_name TEXT,
  industry TEXT,
  currency TEXT,  -- JPY / USD
  country TEXT    -- JP / US
)

-- 財務データ
financial_data_v2 (
  ticker TEXT,
  fiscal_year INTEGER,
  fiscal_period TEXT,
  -- 財務3表の各カラム(売上高・営業利益・純利益 等)
)

日本株と米国株でそれぞれ通貨・国コードを持たせることで、フロントエンド側が通貨バッジや単位(百万円/百万ドル)を自動で切り替えられるようにしています。


フロントエンド:苦労した実装3選

1. BSグラフの「極太バー」問題

Chart.jsの棒グラフで貸借対照表を表現する際、デフォルト設定だとバーが細すぎて財務データが読みにくい問題がありました。

// 解決策: categoryPercentage / barPercentage を両方1.0に
datasets: [{
  categoryPercentage: 1.0,
  barPercentage: 1.0,
  // ...
}]

さらに「Stack0」という仮想データセットを統合することで、積み上げグラフの視認性を大幅に改善しました。

2. データラベルの文字重なり

chartjs-plugin-datalabels でバー上に金額を表示すると、バーの高さによってラベルが重なる問題が発生。

// 役割ごとにanchor/alignを動的に切り替え
formatter: (value, context) => {
  // 値が小さい場合は外側、大きい場合は内側に配置
  return value > threshold ? value : value;
},
anchor: (context) => context.dataset.data[context.dataIndex] > threshold ? 'end' : 'start'

3. グラフの「見切れ」対策

PL・CFグラフでラベルがキャンバス上部に飛び出す問題。

// layout.paddingでキャンバス上部にスペースを確保
options: {
  layout: {
    padding: { top: 60 }
  }
}

加えて、グラフ生成を150msディレイ後に実行することで、DOMの描画完了タイミングを待つようにしました(これに気づくまでに数時間溶けました)。


断念した機能:動的OGP

「銘柄名をURLパラメータで渡して、OGP画像を動的生成したい」というアイデアがありました。

/dashboard?ticker=7203 → トヨタの財務サマリ画像が生成されてSNSでシェアできる

試したこと

  1. Vercel OG (@vercel/og) → App Router前提のため、静的SPA構成と相性が悪い
  2. Cloudflare Workers + Canvas API → WorkersでCanvas APIが使えない制約に当たった
  3. Lambda + puppeteer → コールドスタートが重く、無料枠内での運用が難しい

断念の理由と今後

フェーズ1の目標は「財務データの可視化機能を動かすこと」であり、OGP対応に工数をかけすぎるより、コンテンツを充実させる方が優先度が高いと判断しました。

静的なOGP画像(ダッシュボード全体のスクリーンショット)をog:imageに設定する妥協案で現在は対応済みです。

動的OGPについては、フェーズ2でNext.js App Routerへの移行とともに再挑戦する予定です。


デプロイ:VercelへのCI/CD

GitHubリポジトリと連携することで、mainブランチへのpushが自動デプロイに。

git add data.js
git commit -m "データ更新: 2026-05-19"
git push origin main
# → Vercelが自動でビルド・デプロイ

静的ファイルのみの構成なのでビルド時間は数秒。データ更新のたびにこれだけでサイトが最新化されます。


現在のデータ構成

カテゴリ 銘柄数
日本株 20社(トヨタ・ソニー・任天堂等)
米国株 5社(AAPL・NVDA・MSFT・GOOGL・AMZN)
ETF 5本(SPY等、財務データなし)
合計 100銘柄

AI分析コメントも全企業・全年度で292件生成済みです。


今後のロードマップ

  • SEO対応(sitemap.xml / robots.txt)
  • 動的OGP(フェーズ2)
  • 複数銘柄比較チャート
  • データ更新の自動化(GitHub Actions + cron)

おわりに

個人開発は「完璧を目指して止まる」より「動くものを出して反応を見る」方が絶対に良いと今回改めて実感しました。

動的OGPの断念も、「フェーズ1の範囲ではここまで」と割り切ったことで、他の機能を充実させる時間が生まれました。

もし同じような財務データ可視化ツールを作りたい方や、Vanilla JS + Vercelの静的SPA構成に興味がある方は、コメントでぜひ教えてください!


使用ツール・サービス

  • yfinance(株価・財務データ取得)
  • SQLite(データ管理)
  • Lightweight Charts(ローソク足)
  • Chart.js + datalabels(財務グラフ)
  • Vercel(ホスティング)
  • GitHub(CI/CD)

Discussion