開発financial-dataメモ

EDINET財務データの可視化ページを新規実装した

朝からEDINET財務データの可視化ページを組み始めて、夜には3カラムレイアウトにウォーターフォール・比例縮尺BS/PL・時系列チャートが並ぶところまで到達した。途中、BSの左右が合わない数字をターミナルに貼り付けて睨む時間が長かった。丸め誤差だと思っていた不一致が、按分ロジックの構造的な問題だと気づいたのは昼過ぎ。テストを113件書いて、ようやく全ケースで貸借が一致した。6コミット。


Jリーグページのアーキテクチャを踏襲

既存のJリーグ財務可視化ページと同じ構造を採用した。composable層でデータ変換を担い、Vueコンポーネントは描画に専念する分離。EDINET用に新規作成したファイルは以下の5つ。

  • composables/edinet/useEdinetData.ts -- 生データ→表示用データ変換
  • composables/edinet/useEdinetChart.ts -- チャート設定の生成
  • composables/edinet/useEdinetCompanies.ts -- 企業マスタ管理
  • composables/edinet/useEdinetLayout.ts -- 3カラムレイアウト制御
  • types/edinet.ts -- 型定義

Jリーグ版の composable を丸コピしてからEDINET固有の差分だけ書き換える方式で進めた。型定義を先に書いてからcomposableに着手したので、変換関数の引数と戻り値の型が最初から固まっていた。


3カラムレイアウト

画面構成はサイドバー + ウォーターフォール + チャートパネルの3カラム。

  • 左カラム(サイドバー): 企業選択と期間スライダー
  • 中央カラム: 営業利益増減ウォーターフォールと比例縮尺BS/PL
  • 右カラム: 時系列チャートパネル

ホバー連動の仕組みを入れた。比例縮尺BSの「現預金」にカーソルを合わせると、右カラムに現預金の年次推移チャートが浮かぶ。指標ごとに色が対応しているので、どの棒がどの時系列に紐づいているか視線を動かすだけでわかる。


営業利益増減ウォーターフォール

売上高から始まって、売上原価・販管費・営業外損益を差し引き、営業利益に着地するウォーターフォール。増減の方向(プラスは上、マイナスは下)でバーが伸びるので、どの費目が利益を削っているか一目で読める。


比例縮尺BS/PLとBS内訳の詳細表示

BS(貸借対照表)を資産・負債・純資産の面積比で描画する。PLも同様に売上高を100%として各費目の比率を面積で表現。

BS内訳の追加

最初は流動資産・固定資産の2区分だけだったが、投資判断に使うには粒度が足りない。以下の内訳を追加した。

  • 流動資産: 現預金、売掛金、棚卸資産、その他流動資産
  • 固定資産: 有形固定資産、のれん、ソフトウェア、その他無形固定資産、投資その他

内訳を足すと、比例縮尺のブロック数が増えてレイアウトが窮屈になる。小さすぎるブロック(全体の3%未満)はラベルを非表示にして、ホバーで確認する方式にした。


貸借不一致バグとの格闘

BSの左右が合わない。資産合計と負債純資産合計が数百万円ずれる。

第1段階: 丸め誤差を疑う

最初に疑ったのは百万円単位への丸め。APIが円単位で返すデータを百万円に変換する際、各項目を個別に丸めると合計がずれる。Math.round のタイミングを「項目ごと」から「合計算出後」に変更したが、差額は縮まったものの消えなかった。

第2段階: 按分ロジックの問題に気づく

流動資産の内訳(現預金・売掛金・棚卸資産・その他)を按分で算出していた箇所に問題があった。APIが返す内訳の合計が、流動資産合計と一致しないケースがある。このとき按分比率で配分すると、端数の切り捨てが積み重なって合計が親科目を超過する。

// 問題: 各内訳をround()すると合計が親科目を超えることがある
const cash = Math.round(rawCash / total * parentAmount)
const receivable = Math.round(rawReceivable / total * parentAmount)
// ... 最後の項目で調整しないと合計が合わない

第3段階: 残余項目で吸収

最後の内訳項目を「残余」として扱い、親科目合計から他の内訳の合計を差し引いた値を割り当てる方式に変更した。これで按分による端数の超過が構造的に発生しなくなった。

テスト113件で検証

修正後、全企業・全年度のデータでBSの貸借一致を検証するテストを書いた。113テストケース、全パス。テストが通った瞬間、椅子の背もたれに体を預けて天井を見上げた。


会計ソフトBのR&Dデータ桁間違い

会計ソフトBの研究開発費が桁違いに小さい。前日(4/10)に発見した「EDINET APIの千円単位問題」と同じ症状で、TursoのリモートDBに格納済みのデータが千円単位のままだった。

DBを直接叩いて修正した。

UPDATE financials
SET rnd_expenses = rnd_expenses * 1000
WHERE edinet_code = 'XXXXX' AND rnd_expenses < 1000000000;

ローカルのEmbedded Replicaを sync() して反映。チャート上のR&D推移が正常な値に戻った。


販管費とR&Dの二重計上表示

ウォーターフォールで販管費と研究開発費を別項目として表示したところ、合計が営業利益と合わなくなった。原因は単純で、研究開発費は販管費の内訳だった。販管費に含まれているR&Dを別途バーに立てると二重計上になる。

テーブル表示での解決

ウォーターフォールからR&Dを除外し、代わりにPLテーブルで販管費の内訳としてインデント付きで表示する方式に変更した。

売上高          192,199
売上原価        △96,100
売上総利益       96,099
販管費          △88,456
  うちR&D       △8,346   ← インデント付き内訳
営業利益          7,643

内訳行はグレーの小さいフォントで、親科目と視覚的に区別できるようにした。


チャートの色とラベル

ブルー濃淡への変更

最初はChart.jsのデフォルトカラー(赤・青・緑・黄のカラフルなパレット)を使っていたが、財務データには落ち着いたトーンの方が読みやすい。全チャートの配色をブルーの濃淡に統一した。売上高が最も濃い紺、利益系が中間のブルー、費用系が薄いスカイブルー。

数値ラベルの追加

バーの上端に実数値を表示するラベルを追加した。目盛線だけだと概算しか読めないが、ラベルがあれば具体的な金額がわかる。小数点以下は切り捨て、億円単位で表示。


コードレビュー(simplify)

一通り動いた段階で、composableの重複コードを整理した。

  • useEdinetData.tsuseEdinetChart.ts で同じ変換ロジック(円→百万円)を二重に持っていたのを、共通の toMillions() に集約
  • チャート設定で毎回再計算していた年度ラベル配列を、computed一本にまとめた
  • 使われていないimportと未参照の変数を削除

CF Waterfall追加の計画立案

営業CF・投資CF・財務CFの3区分でキャッシュフローの増減を表示するウォーターフォールを追加する計画を立てた。US版の CFWaterfallSection コンポーネントをそのまま再利用できる。EDINET APIにはCFの詳細内訳(配当・自社株買い等)がないため、3区分の簡略版で構成する。

計画は memo/2026-04-11/edinet-cf-waterfall-plan.md に保存した。明日実装する。


学びメモ

  • 按分の端数は「残余項目」で吸収する: 各内訳を個別にround()すると合計が親科目を超過する。最後の項目を差額で埋める方式なら構造的に合計が一致する。テスト113件が一発で通ったとき、この設計が正しかったと確信した
  • 内訳と合計の関係を先に確認する: R&Dが販管費の内訳だと知らずに別項目として立てて二重計上した。勘定科目の親子関係は、コードを書く前に会計基準で確認する
  • DBの単位は格納時に統一する: 千円単位のまま格納すると、後工程の全てに×1000が伝播する。入口で円単位に揃えておけば変換漏れが起きない
  • カラフルなチャートは情報を邪魔する: デフォルトパレットから濃淡1色に切り替えた瞬間、数字の大小関係が目に飛び込んできた。色数を減らすほど、データが前に出る

コミットログ

6コミットで以下の作業を実施した。

  1. EDINET用composable 4ファイル + 型定義の新規作成
  2. 3カラムレイアウト + ウォーターフォール + 比例縮尺BS/PLの実装
  3. BS内訳の詳細表示追加
  4. 貸借不一致バグ修正 + テスト113件作成
  5. チャート配色変更 + 数値ラベル追加
  6. composable共通化リファクタリング