このプロジェクトについて
EDINET(有価証券報告書等)の開示データをもとに、日本株の財務指標・投資判断スコアを日次で算出・公開しているサービスです。
公開サイトはこちら →
https://stocks.happy-life-design.net
前回の記事はこちら →
https://qiita.com/rightcross-ae86/items/6d20ff3265c580c8dd1c
今回やったこと
残タスクを2件片付けようとしたら、どちらも自動処理が既に大部分を終わらせていました。自動化(cronによる定期実行)が課題を解決し続けていても、自分の管理・引き継ぎの記録はそれを自動では反映してくれません。同じ日に独立して2回同じパターンにはまったことで、単発のうっかりではなく仕組み上の見落としだと気づけた話です。
前提: まだ完了していない残タスクのうち、2件に着手した
1件目は、過去の決算データを遡って反映する処理(EDINETのPrior年データ)の本番投入です。2件目は、自己資本(equity)の値が外部データ(kabutan)と僅かにズレている562社への対応です。
1件目: 実は対象の99.2%が既に本番反映済みだった
DBの実行履歴列(restated_at)を確認したところ、対象3,835社のうち99.2%(3,806社)が2026/06/30〜07/28の間に既に本番反映されていた形跡がありました。ターミナルの作業ログでも裏取りしたところ、別の修正作業のついでに実行されていたと確認できました。
この処理を関数名だけで参照し続けていたことで、自分の管理番号との結び付きを見失い、「まだやっていない」という認識のまま残タスク扱いにしていたのが原因でした。残っていた未処理分(restated_at皆無29社+最新書類の反映により再実行が必要な93社の計122社)を含めて全社再実行し、カバー率を3,828社(99.9%)まで引き上げてクローズしました。
2件目: 既存のcronが発見前から自然に解消していた
続けて2件目を確認すると、こちらも発見時点の562社から36社まで、既に93.6%が自動収束していました。収束させていたのは、この課題に気づくよりも前から動いていた既存のcron処理(平日16:45に自己資本比率等を再計算する定期実行)でした。
残る36社を調べ直したら、別の実データバグが見つかった
残る36社を1件ずつedinet_xbrl_factsのcontext単位で確認したところ、計算ロジックのSubscriptionRightsToShares(新株予約権)を取得するSQLに、連結/個別の優先順位バグを発見しました。非支配株主持分と異なり新株予約権は親会社単体(個別)でも開示されるため、同一書類・同一日付で「連結」と「個別」の2つの値が並存するケースがあり、既存のORDER BY filed_date DESCだけではタイブレークが不定で、個別側の小さい値を誤って拾ってしまうことがあると判明しました(198社に潜在的影響。実際に2社で連結777百万円のところ個別16百万円を誤取得)。
「個別を無条件除外」する案をまず試しましたが、全社dry-run比較を行ったところ28社で逆に正常データを壊す回帰が発生。「連結を優先し、無ければ個別にフォールバック」という優先順位をSQLのCASE式で表現する方式に直し、全社dry-run比較で回帰0件・改善2件のみであることを確認してから本番反映しました。
この修正で2社(3681・5103)は解消しましたが、残る34社は乖離の原因が多様なため、個別調査が必要な課題として持ち越しました。1件目はカバー率99.9%でクローズ、2件目はこの2社解消分を含めてクローズとせず、残課題として引き続き扱っています。
技術メモ
1件目: 進捗確認に使った列
SELECT count(*) FROM financials WHERE restated_at IS NOT NULL;
restated_atは該当処理を実行した日時が入る列で、値が入っていればそのレコードは既に処理済みという意味になります。「関数を実行したかどうか」を自分の記憶やメモではなく、この列の実データで判定したのが今回の発見のきっかけです。
2件目: 新株予約権の連結/個別優先順位バグ
修正前のクエリはDISTINCT ON (company_id, fiscal_year)とORDER BY filed_date DESCだけでタイブレークしていたため、同一書類・同一filed_dateに連結値と個別値が並存すると、どちらが採用されるかが不定でした。
-- 修正前
ORDER BY company_id, fiscal_year, filed_date DESC
-- 修正後(連結を優先、無ければ個別にフォールバック)
ORDER BY company_id, fiscal_year,
CASE WHEN context_id LIKE '%NonConsolidatedMember%' THEN 1 ELSE 0 END,
filed_date DESC
実際に誤取得していた値は次の通りです。
| 銘柄コード | 連結側の正しい値 | 誤って採用していた個別側の値 |
|---|---|---|
| 3681 | 777,122,000円 | 16,762,000円 |
| 5103 | 116,422,000円 | 45,462,000円 |
修正の妥当性は、全社分をdry-runで新旧ロジックの計算結果を突き合わせることで検証しました(既存の一致データを壊していないか=回帰0件、対象2社だけが新たに一致したか=改善2件、をそれぞれ確認)。
まとめ
残タスクだと思って着手した2件が、実はどちらも自動化された定期処理によって大部分は既に解決済みでした。自動化そのものは正しく機能していても、それを追跡する自分の管理記録が追いついていなければ、「まだ終わっていない」という誤った認識のまま何日も残り続けます。同じ日に2回連続でこのパターンにはまったことで、単発のミスではなく仕組みとして見落としやすい構造だと気づけました。気になる点・ご質問があればコメントでお知らせください。