このプロジェクトについて
EDINET(有価証券報告書等)の開示データをもとに、日本株の財務指標・投資判断スコアを日次で算出・公開しているサービスです。
公開サイトはこちら →
https://stocks.happy-life-design.net
前回の記事はこちら →
https://qiita.com/rightcross-ae86/items/1ccd0ab3f51792ad61dc
今回やったこと
前提: 投資判断スコアの算出自体に実害が出ていたわけではない
先に断っておくと、財務データを使った投資判断スコアの算出処理自体はこれまでも問題なく動いていました。ただし「この数値が実際にいつ世の中に公開されたものか」を示す情報には、以前から欠落がありました。
何が欠けていたか: 四半期データには公開日の情報が一切なかった
決算期末日(period_end)は決算対象期間の終わりの日付であって公開日ではなく、自社DBへの取り込み日時(created_at)も実世界での公開タイミングとは無関係です。先月、この欠落を埋めるためにEDINET(有価証券報告書の開示システム)側の提出日を複製する列を追加していましたが、これには2つの限界がありました。
- 有価証券報告書(年次report)にしか存在しない値のため、四半期データの行には反映できず常にNULLのまま
- 提出日自体が、決算短信による実際の市場への最初の公開から1〜3ヶ月遅れることがある保守的な代理値
つまり四半期データについては、この数値がいつ公開されたものかを示す手がかりが文字通り何も無い状態が続いていました。
気づき: 解決策は既に取得可能な場所にあった
決算情報を集めている外部の参考サイトの財務テーブルを改めて確認したところ、本決算・四半期のいずれの表にも「発表日」列(決算短信の公表日そのもの)が同じ列位置に存在していました。ところが既存の情報収集処理を確認すると、この列にはコメントだけ付けて中身を一切パースせず捨てていたことが分かりました。
# Before: 発表日の列はコメントのみで中身を使っていなかった
"""
7: 発表日(無視)
"""
row = {
"ticker": ticker,
"period_end": parse_period(texts[0]),
# ...他の指標...
}
# After: 発表日をパースして新規列に保存
"""
7: 発表日(決算短信の公表日。本決算・四半期共通の位置)
"""
row = {
"ticker": ticker,
"period_end": parse_period(texts[0]),
"announced_date": parse_announced_date(texts[7]) if len(texts) > 7 else None,
# ...他の指標...
}
新たな取得元を探す必要はなく、既存の情報収集処理が毎回取得していたページの中に答えがそのまま存在していた、というオチです。日付フォーマットの変換だけを追加する実装で、四半期データの欠落・提出日の1〜3ヶ月遅れという2つの限界を同時に解消できました。
実装・全社バックフィル
マイグレーションで新規DATE列を追加し、パース処理・INSERT/UPDATE文への反映を実装しました。少数銘柄での実地検証で本決算・四半期とも正しく取得できることを確認したのち、バックアップを取得したうえで全社(4,442社)を対象にバックフィルを実行し、新規280件・更新44,089件を反映しました。
残る制約
参照元サイト自体が表示する期間には制限(本決算は直近4期分、四半期は直近8期分)があるため、それより古い既存データにはこの発表日を遡って反映できません。過去の古いデータについては、引き続き提出日ベースの保守的な代理値(年次のみ)に頼るしかない状態が残ります。
検証
反映前にバックアップを取得し、既存のテストスイートが反映前後で全件通過することを確認しました。少数銘柄での実地検証・全社バックフィル後の新規/更新件数の集計でも異常は見つかっていません。
「特に困っていなかった」データの欠落でも、将来の分析で必要になった時に初めて問題として顕在化することがあります。今回は幸い、既に取得済みのページの中に使っていない列が残っているだけの、コストの低い解決で済みました。
気になる点・ご質問があればコメントでお知らせください。