みなさん、こんにちは!
「ALPHA FORGE 開発秘話」の第34話です。
「ニュース0件」。その記録の多くは、本当は「取りに行って失敗した」でした。
しかもエラーは1つも出ていません。偽のデータが、毎日約4,400行ずつ台帳にたまっていきました🕳️
日本株のデータ分析端末「ALPHA FORGE(アルファフォージ)」を、Claude Code と二人三脚で個人開発しています。今回は【事件簿】回。外部のサービスからデータを集めている人に、2回だまされて分かった「0件」の見分け方をお届けします。
【要約:この記事で伝えたいこと】
- yfinance は Yahoo 側で遮断されても例外を出さずに空のリストを返し、取得失敗が「ニュース0件」として台帳に記録されていた
- 失敗ログを拾って失敗は記録しない形にしたが、2回目は404が正しいJSONで返りログも出ず、最初の直しをすり抜けた。いつも取れる大型株6銘柄で先に確かめる検知を足した
- 持ち帰り:「0件」と「取れなかった」は、別の結果として扱う
※本記事はnote連載[ALPHA FORGE 開発秘話 #34] の技術的な部分を、エンジニア向けに再構成したものです。
🧭 ニュースの件数を、毎日記録している
ALPHA FORGEには、全銘柄のニュースの件数と、その明るさ・暗さ(センチメント)を毎日記録する台帳があります。「その日に何が分かっていたか」だけを残して、あとで学習に使うためのポイント・イン・タイム台帳です。
ニュースは yfinance 経由で Yahoo から取っています。1回の収集で約4,400銘柄を順に回ります。
ニュースが本当に無い銘柄なら、記録は「0件」で正しいです。問題は、取りに行って失敗したときも「0件」と書かれていたことでした。
💥 1回目:4,449銘柄のうち、取れたのは208件
2026年9月26日の早朝です。止まっていた裏方の処理(Celery の worker)を起こしたあと、人間が「workerはどこまで動いてる?」と聞きました。
AIは進み具合を表にまとめ、最後に「気になる点」を1つ書き添えます。ニュースの収集で4,449銘柄を試し、取れたのは208件だけ、というものです。
人間の返事は「yfinanceのエラー確認して」。AIが調べた結果はこうでした。
- Yahooのニュースの窓口が、HTTP 999(大量アクセスの遮断)を返していた
- 株価の取得は正常で、遮断はニュースだけ。収集の開始から約3分、200銘柄前後で始まっていた
- yfinance は遮断されてもエラーをログに出すだけで、例外を出さずに空のリストを返す
空のリストを受け取った収集処理は、失敗に気づきません。「この日はニュースが無かった」という行を、そのまま台帳に書いていました😱
ハマりどころ:ライブラリが「失敗」を「空」で返す
try/except で囲んでいても、例外が飛ばなければ何も起きません。空のリストは「0件」と区別がつかないので、呼び出し側でログや応答から失敗を拾う必要があります。
人間は「タスクを止めてからコード修正して」と返しました。AIは動いている収集を止め、先にテストを書いてから直しています。
- yfinance が出す失敗のログを拾い、取得の失敗(独自の例外)として区別する
- 失敗した銘柄は台帳に書かない。20件続けて失敗したら、その回の収集を打ち切る
- 銘柄と銘柄のあいだに0.5秒の待ち時間を入れる
🧹 汚れた行は、戻せなかった
次に人間が聞いたのは「汚れた台帳データは復旧できる?」。答えは、元の値には戻せない、でした。ニュースの窓口は「今の記事」しか返さないので、過去の日に見えていたニュースは取り直せません。
それでも、どの行が汚れているかは見分けられました。AIは銘柄コード順に500銘柄ずつ区切り、「ニュースあり」の件数を日付ごとに並べています。正常な日は1区間60〜120件、遮断のあとは0〜6件。境目がくっきり出ました。
AIが出した案は2つです。0件の行だけを消すか、その日の行を丸ごと消すか。人間は「1で進めて」と返し、0件の行だけを消すことになりました。本物の0件も混ざりますが、見分けられない以上まとめて消します。
ここで1つ、止まりました。バックアップの移動と削除をまとめたコマンドが、Claude Code の自動モードの判定で止められたんです。AIは回避策をとらずにコマンドを示し、人間が自分で実行しました。消えたのは4,330行、ニュースがあった120行は残しています。
この台帳はまだ学習に使っていなかったので、実害はありませんでした。
🛠️ 2回目:今度はエラーもログも出なかった
10月7日の朝、銘柄ごとの分析でセンチメントの欄が欠けていました。人間が「センチメント欠測の理由を調査して」と頼みます。
AIの報告では、10月2日から全銘柄で0件でした。日本株だけでなく、米国株でも0件です。ニュースの窓口は「Not Found」の404を返していて、Yahoo側で廃止か変更があったとみられます。
1回目の直しが効かなかった理由はこうです。前回拾えたのは、遮断のときの失敗ログと、JSONではない応答でした。今回の404は、ログも出さず、JSONとして正しい形で返ってきます。台帳には、1回の収集で約75分かけて、約4,400行の「0件」が毎日書かれていました。
AIは3つの案を出しました。英語の社名でニュースを検索する、別の取り先を探す、0件が続く障害を検知する。勧めたのは「まず検知だけ入れて、取り先は調べる」。人間の返事は「3から入れて、1か2も調べて」でした。
入った検知は、とても素朴です。実際のコミットからの抜粋です(確かめる銘柄のコードは伏せています)。
# 通常は大型株のどれかに必ずニュースがあるので、大型株が「全滅」なら取得障害として扱う。
NEWS_FEED_PROBE_CODES: tuple[str, ...] = (...) # いつもニュースがある大型株6銘柄
def is_news_feed_healthy() -> bool:
"""大型株のどれか 1 つでもニュースが取れれば True。全滅(0 件または取得失敗)なら False."""
global _probe_memo
now = time.monotonic()
if _probe_memo is not None and now - _probe_memo[0] < _PROBE_MEMO_SECONDS:
return _probe_memo[1]
healthy = False
for code in NEWS_FEED_PROBE_CODES:
try:
if get_news_sentiment(code, strict=True)["total"] > 0:
healthy = True
break
except NewsFetchError:
continue
if not healthy:
logger.error("ニュース取得障害の可能性: 大型株すべてでニュースが 0 件(または取得失敗)でした")
_probe_memo = (now, healthy)
return healthy
- いつも何かしらニュースがある大型株6銘柄を、先に取ってみる
- 6銘柄すべてが0件か失敗なら「障害」とみなし、エラーとして知らせる
- 障害のあいだは台帳の収集をしない。銘柄選びは止めず、ニュース抜きで続ける
⚖️ 使わなかった取り先と、残っている行
取り先の調査で、AIは最初、GoogleニュースのRSSを第一候補に挙げていました。人間が「Google Newsの規約を確認して」と頼むと、自動での取得を断る決まり(robots.txt)に当たると分かり、AIはその推奨を取り下げています。試しに取れたとしても、使ってよい理由にはなりません。
株の情報サイトや適時開示の取り先も、規約か費用の面で見送りました。代わりに人間が出したのが、銘柄コードと英語名の対応表を作り、確かめた銘柄だけ英語名で検索する案です。
片づいていないこと
10月2日〜10月6日に書かれた0件の行は、2026年10月24日の時点でも台帳に残っています(読み取りで確認)。学習に使い始める前に、消すか印を付けるかを決める必要があります。
🧰 「0件」を疑う頼み方
外部からデータを集める処理をAIに作ってもらうなら、最初からこう頼んでおけばよかった、という型です。今回の2回を整理した例で、当時の人間の発言の引用ではありません。
外部からデータを取る処理を作ってください。
「取れた結果が0件」と「取りに行って失敗した」は、別の結果として扱ってください。
失敗したときは、0件として記録しないでください。
いつもなら必ず取れるはずの対象を決めて先に取り、それも0件なら障害として知らせてください。
ライブラリが例外を出さずに空を返す場合があるか、作る前に調べて報告してください。
最後の1行が効きます。今回のライブラリは、失敗しても「空」を返す作りでした。それを知っているかどうかで、テストの書き方が変わります。
🤝 誰が気づき、誰が決めたか
1回目に気づいたのはAIです。進み具合の報告の中で、取れた件数の少なさを「気になる点」として挙げていました。2回目は、人間が欠けた欄を見て調査を頼んでいます。
原因の特定、汚れた範囲の見分け方、直し方の案、テストと実装はAIです。人間が決めたのは次のことです。
- 収集を止めるか、どの行を消すか
- どの案から入るか、どの取り先を使わないか
- コミットしてよいか
削除のコマンドが止められたとき、AIは止まったことを報告し、人間が自分の手で実行しました。消すかどうかを決めた人が、最後のボタンも押しています。
📌 保存用チェックリスト
- 「0件」と「取得失敗」を、別の結果として記録する
- 失敗したときは、0件の行を書かない
- いつも取れるはずの対象で、先に取れるか確かめる
- ライブラリが例外を出さずに空を返す場合を調べておく
- 汚れた行は、件数の並びで範囲を見分けてから消す
☕ note.comで開発秘話と毎朝の検証ログを連載中
note.comでは、「ALPHA FORGE」の開発秘話と、毎朝のアルゴリズム検証ログを公開しています。
👉 元記事(note連載第34話):
https://note.com/featured_stocks/n/nfedb6ba1b776
ニュースが全銘柄0件になった最初の事件は第7話です。今回はその続きにあたります。
エンジニアでない方向けに「AIへの仕事の頼み方」をまとめた連載(メンバーシップ「AIとつくるラボ」)もやっています。第0話は無料で読めます。
免責事項
本記事は個人開発およびAIエージェントを活用したシステム開発の技術記録です。特定の銘柄の売買推奨や投資助言を行うものではありません。本記事は2026年9月26日と10月7日の実装記録と会話ログを2026年10月24日に確認したものです。銘柄名と価格は掲載していません。ニュースの件数とセンチメントは分析上の材料であり、投資の判断基準や成果を保証するものではありません。本アプリは発注を行いません。
⚠️ 【システム検証記録に関する免責事項・注意事項】
- 本記事は、独自開発アルゴリズム「ALPHA FORGE」の動作検証およびデータ分析過程を公開する技術・運用の個人的な記録ログです。
- 掲載されているすべてのデータ(各スコア、基準観測値、統計的変動上限、シナリオ無効化水準など)は、過去の市場データに基づき数式(ATR等)により機械的に算出されたバックテスト・シミュレーション用のパラメータであり、特定の有価証券の売買勧誘、取引の推奨、投資助言・代理行為を目的としたものではありません。
- また、将来の株価変動や運用成果を保証するものではありません。実際の投資判断および最終決定は、必ずご自身の責任と判断において行っていただけますようお願いいたします。





