0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

株式分割の翌朝、決着17件がまとめて「無効化」に 〜yfinanceの分割調整と、台帳の「物差し」がずれた話〜

0
Posted at

みなさん、こんにちは!
「ALPHA FORGE 開発秘話」の第27話です。

約定した、その日のうちに「シナリオ無効化」。そんな判定が、1つの銘柄だけで17件もそろっていました📏
犯人はAIでもバグだらけのコードでもなく、「株式分割」と「物差しのずれ」でした。

日本株のAI銘柄スクリーニング&継続学習端末「ALPHA FORGE(アルファフォージ)」を、Claude Code と二人三脚で個人開発しています。今回は【事件簿】回。時系列のデータを扱う人なら、いつか踏むかもしれない罠の話です。

【要約:この記事で伝えたいこと】

  1. yfinance の日次足は、株式分割があると過去の値までさかのぼって調整して返す。一方で台帳の価格は、抽出した時点(分割前)のまま。物差しがずれて、決着17件が「約定直後に無効化」と誤判定されていた
  2. 判定の直前に分割の累積比率で台帳の価格を割り戻すよう直し、17件は本番DBをバックアップしてから書き直した。「先に直してから」「過去の記事は書き換えずに訂正する」と決めたのは人間
  3. 持ち帰り:外から取ってくるデータは「いつの物差しか」を確かめる。そして、記録を書き直す前には、確認・バックアップ・変わった件数の一覧をセットにする

※本記事はnote連載[ALPHA FORGE 開発秘話 #27] の技術的な部分を、エンジニア向けに再構成したものです。

🧭 「決着」は、記録した価格とあとの株価を比べる仕組み

ALPHA FORGE は、毎朝AIが抽出した銘柄を、すべて台帳(prediction_ledger)に記録しています。記録するのは、抽出した時点で分かっていたことだけ。3つの価格(シミュレーション起点価格・シナリオ無効化水準・想定レンジ上限)も、その時点の値のまま残します。

そのあと毎日、夕方の決着処理が、台帳の価格とその後の日次足を比べます。

  1. 起点価格で約定したか
  2. 約定したあと、無効化水準と想定レンジ上限のどちらに先に届いたか
  3. 何営業日後に、どれだけ動いたか

この「どちらに先に届いたか」が、今回の主役です🏁

💥 約定した直後に「無効化」が17件

02_wall.jpg
台帳は分割前の物差し、株価データは分割後の物差しだった

10月5日の朝、Claude Code が台帳の決着の数字を確かめていて、1つの銘柄だけ様子がおかしいことに気づきました。約定したその日に、無効化水準に先に届いた判定が並んでいたのです😳

調べると、その銘柄は9月29日に1株を3株に分ける株式分割をしていました。

fig_ruler.jpg
分割の前に記録した価格を、分割の後の物差しで比べていた

yfinance の日次足は、分割があると過去の日の値までさかのぼって3分の1に調整して返します。チャートがつながって見えるようにするための、よくある親切な仕様です。

一方で台帳の価格は、抽出した時点の値のまま。分割前の物差しで書いた価格を、分割後の物差しの株価と比べていました。比べた瞬間に株価が3分の1に「急落」したように見えるので、約定直後に無効化水準へ先に届いた、という判定になっていたわけです📉

誤った判定は17件。ほかに決着済みの24銘柄も確かめて、ずれは見つかりませんでした。

ハマりどころ
分割の調整は、取り寄せた日次足の「過去の値」に入ります。だから分割の前に動き出した処理(ここでは台帳への記録)と、分割の後に取り寄せた足を突き合わせたときだけ、ずれが出ます。分割のない銘柄や、分割の前に終わった決着では何も起きないので、1銘柄だけがおかしく見えます。

🛠️ どう直したか

03_fix.jpg
比べる直前に、分割の比率で台帳の価格を割り戻す

比べる直前に、物差しをそろえる

AIは、抽出した日より後(当日を含む)に効力が出た分割の比率をかけ合わせ、台帳の価格をその比率で割り戻してから判定するように直しました。分割の列(Stock Splits)が付いていない足(予備のデータ源から取った分など)は、補正しません。実際のコードはこうです(抜粋、docstring は一部省略)。

def split_factor_since(bars: pd.DataFrame, issued_day: str) -> float:
    """抽出日以降(当日を含む)に効力が発生した株式分割の累積比率を返す(無ければ 1.0).

    Why: yfinance の日次足は分割を遡って調整した値で返る一方、台帳の entry/stop/target は抽出時点の
    (分割前の)価格のまま。比率で割って足と同じ単位にそろえないと、約定・到達判定が壊れる。
    entry は寄り前に前日終値から決まるので、抽出日当日が効力発生日の分割も含める。
    """
    if "Stock Splits" not in bars.columns:
        return 1.0
    day_obj = date.fromisoformat(issued_day[:10])
    factor = 1.0
    for ts, ratio in bars["Stock Splits"].items():
        value = to_float(ratio)
        if ts.date() >= day_obj and value > 0.0:
            factor *= value
    return factor

決着の計算では、約定を判定する前にこの比率で3つの価格を割り戻します。

split = split_factor_since(bars, issued_at)
entry, stop, target = entry / split, stop / split, target / split
fill_day, fill_price = simulate_fill(bars, issued_at, entry)

先にテストを書いてから直す順番で進め、コードの検査とテスト1,305件がすべて通ったことを確かめています。

台帳の価格そのものは書き換えない

台帳には、予測した時点の値をそのまま残すことにしています。だから直したのは「比べ方」の方です。分割があっても、台帳を見れば抽出した朝に何を考えたかが、そのまま読めます。

17件は、バックアップを取ってから書き直す

すでに出ていた17件の決着は、本番のDBをまるごとバックアップしてから、直した計算で書き直しました。決着した時刻(resolved_at)は、最初の時刻のまま残しています。

fig_rewrite.jpg
書き直す前にバックアップ、書き直したら変わった件数を数える

書き直した17件のうち、値が変わったのは9件。通算の数字も変わりました(10月5日時点。短期の1営業日後は23件中12件→13件、3営業日後は15件中9件→10件)。数字がよく見える方向に動いたからこそ、変わった件数と前後の値を残しておくことが大事でした。

⚖️ やったこと/やらなかったこと

  1. やったこと:判定の直前に、分割の比率で台帳の価格を割り戻す。17件をバックアップのあとで書き直す
  2. やったこと:直すのを先にして、その日の記録はあとに回す(決めたのは人間)
  3. やらなかったこと:台帳の3つの価格を、分割後の値に書き換えること。予測した時点の記録を守るためです
  4. やらなかったこと:過去の記事を書き換えること。誤りの訂正は、新しい記事の中で出すと決めたのは人間です

🧰 今回のハーネス術:データを書き換える前に、3つをセットにする

私のAIへの指示書には、「削除・上書きの前は確認をとる」という決まりがあります。今回の書き直しでは、それに2つを足して、3つをセットにしました🧷

順番 AIにやってもらうこと
1 書き換える前に、人間に確認をとる
2 確認がとれたら、まずバックアップを取って、その場所を報告する
3 書き換えたら、変わった件数と、変わる前と後の値を並べて見せる

3つめがあると、「直したら数字がよくなった」ときにも、何がどれだけ動いたのかを人間が自分の目で確かめられます。

fig_roles.jpg
見つけて直したのはAI、順番と訂正の出し方を決めたのは人間

📌 保存用チェックリスト

  1. 外から取ってくる時系列データは、「いつの物差しか」(分割・併合のあとに調整されるか)を確かめる
  2. 記録した値は書き換えず、比べる直前に物差しをそろえる
  3. 1つの銘柄だけおかしいときは、ほかの銘柄にずれがないかも数える
  4. 記録を書き直す前に、確認・バックアップ・変わった件数の一覧をセットにする
  5. 直して数字がよくなったときほど、前と後の値を残す

☕ note.comで開発秘話と毎朝の検証ログを連載中

note.comでは、「ALPHA FORGE」の開発秘話と、毎朝のアルゴリズム検証ログを公開しています。

👉 元記事(note連載第27話):
https://note.com/featured_stocks/n/n6e7d48391856

台帳に「予測した時点の情報だけ」を残す理由は、第10話で書いています。

エンジニアでない方向けに「AIへの仕事の頼み方」をまとめた連載(メンバーシップ「AIとつくるラボ」)もやっています。第0話は無料で読めます。


免責事項
本記事は個人開発およびAIエージェントを活用したシステム開発の技術記録です。特定の銘柄の売買推奨や投資助言を行うものではありません。記事中の件数は、2026年10月5日時点の開発時の記録にもとづくもので、変更される可能性があります。過去の記録の割合は、将来の値動きを示すものではありません。

⚠️ 【システム検証記録に関する免責事項・注意事項】

  • 本記事は、独自開発アルゴリズム「ALPHA FORGE」の動作検証およびデータ分析過程を公開する技術・運用の個人的な記録ログです。
  • 掲載されているすべてのデータ(各スコア、基準観測値、統計的変動上限、シナリオ無効化水準など)は、過去の市場データに基づき数式(ATR等)により機械的に算出されたバックテスト・シミュレーション用のパラメータであり、特定の有価証券の売買勧誘、取引の推奨、投資助言・代理行為を目的としたものではありません。
  • また、将来の株価変動や運用成果を保証するものではありません。実際の投資判断および最終決定は、必ずご自身の責任と判断において行っていただけますようお願いいたします。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?