株価の時系列を取ってきて折れ線にすると、ある日だけ垂直に落ちていることがあります。何か起きたのかと調べても、決算は好調で、その日の前後に目立ったニュースも見当たらない。これはたいてい株式分割です。
1株を2株に分割すれば、1株あたりの値段は半分になります。持っている価値は変わっていませんが、株価という数字だけを見ると半額になっています。これを値動きとして扱うと、収益率の計算が壊れます。前日比マイナス50%という日が系列に混ざるので、変動の大きさを測る計算はすべて狂います。
のようなは、素の終値と調整済みの値を両方返してくれます。どちらを使っているかを意識せずに列を選ぶと、気づかないうちに間違ったほうを使うことになります。列名を確認する習慣を付けてください。
こうした企業側の行為をと呼びます。分割・併合・配当・合併など、株価の連続性に影響するものが含まれます。時系列で扱うなら、取得の段階でこれをどう処理するか決めておく必要があります。
調整後終値を使う
多くのデータ提供元は、こうした影響を取り除いたを用意しています。過去の価格を現在の株数に合わせて割り戻したもので、分割の日をまたいでも連続した系列になります。
素の終値と調整後終値のどちらを使うかは、目的で決まります。収益率を計算するなら調整後、その日の実際の取引価格を知りたいなら素の終値です。混ぜて使うと、分割前後で意味の違う数字を比較することになります。
| 目的 | 使う値 | 理由 |
|---|---|---|
| 収益率・変化率の計算 | 調整後終値 | 分割の影響を除かないと壊れる |
| 移動平均・テクニカル指標 | 調整後終値 | 同上 |
| その日いくらで売買されたか | 素の終値 | 実際の取引価格が知りたい |
| 時価総額の推移 | 素の終値 × 株数 | 株数側も変わるため両方要る |
配当を戻すかどうか、いつ時点の株数に揃えるか。提供元によって調整の定義が違います。異なる提供元のデータを繋いで1本の系列にすると、繋ぎ目で不連続になります。系列を作るなら、提供元は揃えてください。
で銘柄を指定して取る場合、同じ会社でも市場ごとに記号が違う点にも注意が要ります。調整の有無以前に、そもそも別の市場の価格を取っていた、という取り違えが起こりえます。
調整は後から変わる
見落としやすいのが、調整後終値は過去のぶんも書き換わるという性質です。今日新しい分割が発生すると、過去10年ぶんの調整後終値がすべて再計算されます。昨日保存した値と、今日取得した同じ日の値が違う、ということが起きます。
つまり調整後終値は「確定した過去のデータ」ではありません。差分取得の対象として扱うと、古い値が更新されずに残ります。分割の頻度は高くないので気づきにくく、気づいたときには系列の一部だけが古い定義のまま、という状態になります。
対策は、素の終値と分割の履歴を別々に保存して、調整は使うときに自分で計算するか、定期的に全期間を取り直すかのどちらかです。前者は手間がかかりますが、いつでも同じ結果を再現できます。
import pandas as pd
def adjust_for_splits(close: pd.Series, splits: pd.Series) -> pd.Series: """splits は分割日を索引、比率(1株が何株になったか)を値とする系列。
分割日より前の価格を、その比率で割って現在の株数に揃える。 複数回の分割がある場合は、累積で効かせる必要がある。 """ factor = pd.Series(1.0, index=close.index) for day, ratio in splits.sort_index(ascending=False).items(): # 分割日「より前」の価格に効かせる。分割日当日は調整後の価格 factor.loc[factor.index < day] *= ratio return close / factor欠損している日をどう扱うか
もう一つの落とし穴が、データが無い日です。休日は取引が無いので行が存在しません。これを欠損として埋めると、取引の無い日に価格があることになります。逆に無視すると、日数を数えたときにずれます。
市場ごとに休日が違うので、複数の市場を並べるとさらに面倒になります。日本とアメリカでは休みが違い、両方が開いている日だけを取ると日数が減ります。何を基準の日付にするかを決めてから並べてください。
import pandas as pd
def align_series(a: pd.Series, b: pd.Series, how: str = "inner") -> pd.DataFrame: """how を明示的に渡させる。既定値に頼ると、あとで根拠が分からなくなる。
inner : どちらも取引があった日だけ(最も安全) outer : どちらかに取引があれば残す(欠損が出る) """ if how not in ("inner", "outer"): raise ValueError("inner か outer を指定してください") df = pd.concat([a.rename("a"), b.rename("b")], axis=1, join=how) # outer の場合、埋めるかどうかは呼び出し側が決める。ここでは埋めない return df- 片方に合わせる — 基準にする市場の営業日だけを残す。分かりやすいが情報が落ちる
- 両方の和を取る — 片方が休みの日は直前の値を持ち越す。日数は増えるが、動いていない日が混ざる
- 共通の日だけ — どちらも開いていた日に限る。最も安全だが日数が最も減る
判断に困るのは、片方の市場が休みの日をどう埋めるかです。直前の値を持ち越すのが一般的ですが、これは「動かなかった」という情報を作り出していることになります。変動の大きさを測る計算では、この作られたゼロが結果を小さく歪めます。どれが正しいということはなく、何を計算したいかで決まります。相関を見るなら共通の日、指数を作るなら片方に合わせる、といった具合です。決めた基準は記録に残してください。次回は、もっと細かい粒度のデータを扱います。
この記事の感想を教えてください
あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。