このプロジェクトについて
EDINET(有価証券報告書等)の開示データをもとに、日本株の財務指標・投資判断スコアを日次で算出・公開しているサービスです。
公開サイトはこちら →
https://stocks.happy-life-design.net
前回の記事はこちら →
https://qiita.com/rightcross-ae86/items/30128400434320c2aa3e
きっかけ
株価データを参照している別プロジェクトと、日々の情報を突き合わせる運用を行っています。今回、連携先から届いた「ニュース記事への企業ID付与件数」の集計結果は5,642件・2,609銘柄でした。こちらでも同じ条件で再集計し、一致することを確認するはずでした。
見つかった差異
実際に再集計すると5,773件となり、131件のズレがありました。連携先の集計とこちらの集計、どちらかが間違っているのか、あるいは他に原因があるのか、切り分けが必要な状態でした。
そもそもの機能: 企業IDは元々どう決まっていたか
ニュース記事を保存するテーブルのcompany_id列は、以前はどの記事でも一律でNULLを書き込む実装でした。
変更前
cursor.execute(
"""
INSERT INTO news_articles (
company_id, published_at, title, body,
source_name, source_url, article_type, created_at
)
SELECT
NULL, %(published_at)s, %(title)s, %(body)s,
%(source_name)s, %(source_url)s, %(article_type)s, NOW()
...
""",
dict(item),
)
数日前に投入した変更
連携先の別プロジェクトからの依頼を受け、記事本文から証券コードを読み取って企業IDを自動で解決するロジックを投入しました。外部の参考サイトの記事本文には「企業名+証券コード」の形式でコードが埋め込まれているため、これを正規表現で抜き出し、1記事の中でコードが1種類だけに絞れる場合に限りその企業IDを採用します。複数の銘柄コードが並ぶ「まとめ記事」は対象企業が一意に定まらないため、あえて対象外にしています。
変更後
_COMPANY_CODE_PATTERN = re.compile(r"<\s*([0-9A-Za-z]{4})\s*>")
def _resolve_company_id(self, body):
if not body:
return None
codes = {c.upper() for c in _COMPANY_CODE_PATTERN.findall(body)}
if len(codes) != 1:
return None # 複数銘柄が並ぶ「まとめ記事」等は対象外
ticker = codes.pop()
cursor.execute("SELECT id FROM companies WHERE ticker = %s", (ticker,))
row = cursor.fetchone()
return row[0] if row else None
# INSERT時にcompany_idへ反映
params = dict(item)
params["company_id"] = self._resolve_company_id(item.get("body"))
原因調査: 差異はどこから生まれたか
差分の原因を調べたところ、この変更を投入したコミットの直後、定期実行される情報収集処理が2回動作し、新たに210件のニュース記事が追加されていたことが分かりました。連携先が集計を行った時点は、この2回の定期実行より前だったため、集計に含まれていなかったのです。
新たに追加された210件の内訳と、企業IDが付与されたかどうかを確認すると次の通りでした。
| 記事の種類 | 新規追加件数 | 企業ID自動付与 |
|---|---|---|
| 材料 | 55件 | 55件(全件) |
| 決算 | 63件 | 63件(全件) |
| 速報 | 13件 | 13件(全件) |
| 材料・決算・速報のうち証券コードが0件/複数件 | 12件 | 0件(対象外仕様通り) |
| 市況・市場ニュース・注目・特集(元々対象外の種別) | 67件 | 0件(対象外仕様通り) |
| 合計 | 210件 | 131件 |
131件という自動付与件数は、材料55件・決算63件・速報13件の合計と完全に一致し、ちょうどズレの131件と同じ数字でした。企業IDを付与すべきでない記事種別(67件)には、誤って付与された記事が1件もないことも確認しています。
結論
数字が合わない、という違和感から始まった調査でしたが、たどってみると不具合ではなく、投入したばかりの変更が本番の定期実行の中で正しく機能し続けていることの実地証明になりました。実は前回時点で「次の定期実行が走った後に、企業IDが正しく自動付与されるか確認する」という項目が確認できないまま残っていたのですが、今回の差異調査そのものによって、期せずしてこの確認が完了した形になります。
次にやること
今回は連携先との突き合わせで見つかった差異でしたが、次はこれとは別に、財務データのある列を検証用の一部企業に絞った状態から全社に展開する作業の記事化を予定しています。
まとめ
別プロジェクトとの数字の突き合わせで見つかった131件のズレは、バグではなく、直前に投入したばかりの機能が本番環境で正しく動作し続けていることを示す結果でした。データを扱うシステムでは、数字が一致しないときにまず疑うべきは自分のコードですが、今回のように「集計タイミングの違い」という単純な理由であることも珍しくないと実感しました。気になる点・ご質問があればコメントでお知らせください。