これは、日次株式データファイルから取り出した1行です。NVDA、2024年6月7日金曜日、10対1の株式分割が発効する前の最終取引日です。
| 列 | 値 |
|---|---|
| 日付 | 2024-06-07 |
| 終値 | 1208.88 |
| 出来高 | 42,800,000 (概算) |
| 分割係数 | 1.0 |
| 配当 | 0.00 |
| 調整後終値 | 120.8781 |
ここでは意図的にOHLC列を省いています。記事の終盤で戻しますが、静かに不具合を引き起こすのはこの列です。
この行で注目すべき点は、2列目と6列目の差にあります。6月7日の市場価格は1208.88でした。その価格で再び取引されることはありません。価格が指していた株式単位は、もう存在しないからです。120.8781は再構成された値で、単に1208.88を10で割ったものではありません。列ごとに見ていきましょう。
日付:2024-06-07、そしてさらに重要な2つの日付
この株式分割は、決算発表とともに2024年5月22日に公表されました。基準日は6月6日です。分割調整後の株価で取引が始まったのは、6月10日月曜日でした。日付は3つありますが、データベンダーのsplit_factor列が示すのはそのうち1つだけで、通常は分割調整後の最初の取引日です。ここでの1日ずれは、私が目にするコーポレートアクション関連の不具合で最もよくあるものです。集計値では見えません。1日だけ90%の急落か900%の急騰が起き、2,000日分のSharpeではほとんど目立たなくても、最大ドローダウンや損切りルールには確実に影響します。
配当にも日付の問題があり、2024年にその前提が変わりました。分割後のNVDAの四半期配当$0.01の基準日は6月11日でした。米国の決済期間は2024年5月28日にT+1へ移行したため、権利落ち日と基準日が同じ日になりました。何十年も正しかった「基準日の1営業日前」を権利落ち日として導くローダーは、2024年6月から配当調整を1取引日早く、しかも気づかれないまま適用するようになりました。エラーは何も出ません。係数が誤った行に付くだけです。
コーポレートアクション処理コードの不具合は、例外として現れることはありません。1日ずれた、完全にもっともらしい数字として現れます。
終値:1208.88 — ファイルに載っていない価格
これは実際に付いた価格で、多くのリサーチ用パイプラインでは誰も読まない列にしか残っていません。シグナルはadj_closeを使って計算されます。また、ドル建ての1株あたり価格水準を参照するルールは、市場参加者が誰も目にしなかった数字を参照することになります。
2024年春、NVDAが$1,000に迫る展開は、実際の注文フローを伴う現象でした。節目の価格に引き寄せられる動き、950や1000に集中したオプションの権利行使価格、節目に近づくたびに生じるヘッドラインリスクがありました。調整後データでは、この一連の動きがすべて$100で起きたことになります。心理的な価格水準への反応、節目付近のギャップ埋めロジック、権利行使価格との近さを使う特徴量を検証するなら、まだ起きていなかったイベントによってスケール変更された価格系列を使っていることになります。
ユニバースのフィルターも同じです。「$5未満の銘柄を除外する」は、相対スプレッドの広さ、証拠金取引の可否、呼値の制度といった、実際の市場構造上の問題が集中する銘柄を除くための妥当な代理条件です。調整後の過去データに適用すると、後に逆分割された企業を、株価が$30だった期間まで除外してしまいます。「株価が$500超」を適用すると、2024年6月以前のNVDAがすべて気づかないまま外れます。株価フィルターには未調整の実価格を使いましょう。これに尽きます。
分割係数:1.0 — そして次の行の0.1
係数は、今日から過去に向かって計算する累積積です。2024年6月10日より前の各行には、この分割による0.1が付き、さらに2021年の4対1分割による0.25や、それ以前の係数が掛け合わされます。NVDAの2021年以前の履歴は0.025倍されるため、2019年に$200で取引されたバーは$5.00と表示されます。
覚えておきたい影響が2つあります。1つ目は、新たなコーポレートアクションが起きるたびに、調整後の過去データが変わることです。2024年5月に実行したバックテストと今日実行するバックテストでは、同じコードを同じ日付に適用しても異なる数字が出ます。データのバージョンを記録せずに、銘柄と日付だけをキーにして特徴量をキャッシュしていたら、異なるスケールのデータが混在するキャッシュができあがっています。このため、私たちは調整の基準時点を特徴量キーに含めています。これを学ぶのに、ひどく混乱した午後を1度費やしました。
2つ目は、0.025倍の調整では、$0.01刻みで取引されていた株価が$0.00025刻みで動くように見えることです。呼値への丸め処理、「新高値か」の比較、浮動小数点数の等値判定は、どれも実際の市場の精度を大きく下回る水準で動作することになります。
配当:0.01 — それ以前の価格すべてを1セント刻みからずらす数字
1セント。ノイズのように見えますが、adj_closeがすっきりした120.888ではなく120.8781になる理由です。
6月10日の終値は121.79でした。権利落ち配当の係数は(121.79 − 0.01) / 121.79 = 0.99991789です。この係数は6月11日より前のすべての価格に掛かるため、分割調整後の120.888は120.878074になります。わずかな差ですが、永続的に反映されます。また、NVDAの過去にさかのぼって60回余りある権利落ち日に繰り返し適用されます。重要なのはその累積です。2015年の価格を見る頃には、合理的とは言いにくい係数が何十個も掛け合わされています。調整後の過去価格で、1セント刻みに収まるものは1つもありません。
つまり、節目の価格との==比較はすべて不正確になり、「指値価格に触れたか」の判定には許容誤差が必要です。また、再構成したOHLCは、小数点以下4桁に単純に丸めると「安値
≤ 始値 ≤
高値」という不変条件を満たさなくなります。さらに、adj_closeはトータルリターン系列である一方、closeは価格リターン系列です。一方からリターンを計算し、もう一方からドローダウンを計算すれば、何が起きたかを異なる定義で混ぜてしまいます。
出来高:42,800,000 — 一貫して適用されない除数
分割後の取引日と比較するには、株数ベースの出来高に10を掛ける必要があります。そうするベンダーもあれば、しないベンダーもあります。OHLCは調整する一方、出来高を未調整のままにするベンダーもあります。これは3つの中で最悪です。価格と出来高を掛け合わせるまでは、ファイルの内部整合性が取れているように見えるからです。
確実なのは、株数ベースの出来高を特徴量に使うのをやめ、定義上、分割の影響を受けないドル建て売買代金を使うことです。20日平均のドル建て売買代金で流動性を判定すれば、調整ロジックは一切必要なく、コーポレートアクションによって壊れることもありません。特徴量の不変性は、正しい調整コードに勝ります。正しい調整コードは、常に正しく保ち続けなければならないものだからです。
ベンダーのファイル形式は3種類、不具合も3種類。
| 提供データ | 起きる問題 |
|---|---|
| 調整後終値のみ、OHLCは未調整 | 異なる2つの価格スケールをまたいでギャップや日中値動きのロジックを計算してしまう |
| 調整後OHLC、出来高は未調整 | ドル建て売買代金が分割比率分ずれる。ADVフィルターが誤作動する |
| 未調整OHLCVと係数列 | 係数を適用すれば問題なし。誤った行に適用すればすべてが問題になる |
ここにない列:調整後始値、調整後高値、調整後安値
ここでOHLCを戻しましょう。フィードが終値だけを調整していて、戦略が翌日寄り付きのギャップをopen / prev_adj_close − 1として計算する場合、2024年6月10日には120.37 / 120.8781 −
1を計算します。約−0.4%で、妥当な値です。同じコードを2024年6月7日に適用すると、始値には元ファイルの未調整値(約1207)を使う一方、終値には過去の分割による0.025倍の調整値を使い、数千%のギャップを算出します。
未調整列と調整済み列を混ぜる処理は、2つの異なる座標系をまたぐ計算です。今では、混在した調整データの読み込みは研究者が覚えておくものではなく、ローダーの段階でアサーションにしています。私を含め、研究者は忘れるからです。
どの列にも書かれていないこと:実際に何株買えたか
これはバックテストのコストモデルで実際に費用差を生みます。私が株式分割について書いている理由もここにあります。
米国株の手数料は、1株あたりで設定されていることがよくあります。よくある個人向け証券会社の固定料金プランとして、1注文あたり最低$1.00、1株につき$0.005としましょう。では、株価が約$400で、調整後データでは$40と表示されていた分割の1年前に、NVDAを$400,000分保有したと仮定してバックテストします。
| 株数 | 手数料 | |
|---|---|---|
| 実際の取引(未調整価格$400) | 1,000 | $5.00 |
| バックテスト上の取引(調整後価格$40) | 10,000 | $50.00 |
同じエクスポージャー、同じ取引なのに、モデル上のコストは10倍です。片道あたり架空の$45、往復で$90の差が生じ、毎日売買する戦略では、本当は存在しなかったコストが年間約$22,000も加算されます。$400kのポートフォリオなら、年間リターンを5.6ポイントも押し下げます。すべて除数が作り出した数字です。この例では実態より悪く見えるだけなので、まだ対処可能です。
逆分割では逆方向に動き、そちらが危険です。1対10の逆分割では、調整後の過去データに表示される価格は10倍高く、株数は実際に取引した場合の10分の1になります。コストモデルは、1株あたり手数料を気づかれないまま10分の1にします。逆分割が多いのは、小型株、株価$5未満、スプレッドの広い銘柄の世界です。そこでは1株あたり手数料がコスト全体の中で最大の項目になります。バックテストは、まさに本来なら厳しく評価すべき銘柄を実態以上に良く見せてしまいます。
対処法は単純で、仕組みとして実装できます。ポジションは金額で決め、該当日の未調整価格を使って株数に換算し、その株数に基づいて1株あたり手数料を課します。未調整価格の列は、監査用の記録ではなく約定エンジンへの実入力として、ローダーに常に残してください。
- シグナルとリターン:調整後系列。
- 株数、1株あたり手数料、呼値への丸め、価格水準フィルター:未調整系列。
- 流動性と売買可能額:ドル建て売買代金。調整は不要です。
3週間前に公表され、誰も驚かなかった、ごくありふれたコーポレートアクション。それを表すのは6列の1行です。市場は午前中に対応しました。それをリサーチ用パイプラインで忠実に再現するのは、はるかに難しい作業です。そして、パイプラインに問題があることを示す兆候は、ほとんどの場合、エラーメッセージではありません。
← 記事一覧


