市場データを複数の国から取ると、時刻の扱いが最初の関門になります。1つの市場だけを見ているうちは表に出ませんが、2つ目を足した瞬間に問題になります。しかも足した直後ではなく、集計して数字が合わない段になって発覚することが多い。の取り違えは、集計が数時間ずれるだけでなく、日付そのものが変わるので影響が大きくなります。
海外の提供元から日本株のデータを取ると、時刻が UTC で返ってくることがあります。UTC は日本時間より9時間遅いので、日本の午前9時は UTC の午前0時です。UTC のまま日付で区切ると、日本時間の午前と午後が別の日に割れます。
のように期間でまとめたデータでも、その期間の区切りが現地時間か UTC かで中身が変わります。日足なら日付だけなので問題ないように見えますが、その日付が現地日付なのか UTC 基準なのかで1日ずれることがあります。ずれていても数字自体は正常に見えるので、他のデータと突き合わせたときに初めて分かります。
時刻には必ずタイムゾーンを付ける
取得した生の応答をに残しておけば、解釈を間違えても付け直せます。対策の基本は、タイムゾーンの付いていない時刻を持ち回らないことです。取得した時点で明示的に付け、保存も付けたまま行います。どこかで外すと、その先では判別できなくなります。
内部では UTC で揃えておき、表示や日付での区切りのときだけ現地時間に直すのが扱いやすい形です。逆に現地時間のまま持ち回ると、市場を追加するたびに変換の場所が増えていきます。UTC で揃えておけば、異なる市場のデータを時系列として並べられます。
from datetime import datetimefrom zoneinfo import ZoneInfo
import pandas as pd
MARKET_TZ = { "TSE": ZoneInfo("Asia/Tokyo"), "NYSE": ZoneInfo("America/New_York"), "LSE": ZoneInfo("Europe/London"),}
def localize(ts: pd.Series, market: str) -> pd.Series: """タイムゾーンの付いていない時刻に、市場の現地時間として印を付けてから UTC へ。""" tz = MARKET_TZ[market] if ts.dt.tz is not None: raise ValueError("すでにタイムゾーンが付いている。二重に付けない") return ts.dt.tz_localize(tz).dt.tz_convert("UTC")
def trading_date(ts_utc: pd.Series, market: str) -> pd.Series: """「どの営業日か」は現地時間で決まる。UTC の日付で切らない。""" return ts_utc.dt.tz_convert(MARKET_TZ[market]).dt.dateアメリカやヨーロッパの市場には夏時間があります。日本との時差が季節によって変わるので、固定の時差で計算すると年に2回ずれます。時差を数字で持たず、地域名で持ってください。ライブラリが切り替えを扱ってくれます。
立会時間の外にもデータがある
の外にも取引が行われる市場があります。時間外の取引は別枠で扱われることが多く、通常の値動きと同じ列に混ぜると分布が変わります。出来高が極端に少ない時間帯の価格が、そのまま高値や安値になってしまう。
を扱う場合は、さらに細かい単位でこの問題が出ます。日本市場のように昼休みがある市場では、その時間帯にデータが無いのが正常です。分足を連続した時系列として扱うと、昼休みの空白を欠損とみなして埋めてしまうことがあります。埋めた値は実在しません。
| 症状 | 原因 |
|---|---|
| 日付が1日ずれる | UTC の日付で区切っている |
| 年に2回だけずれる | 時差を固定値で持っている |
| 昼に価格が横ばい | 昼休みの空白を前の値で埋めた |
| 高値・安値が極端 | 時間外の取引を混ぜている |
| 日数が市場ごとに違う | 休日が国ごとに違う |
どれも例外を出さずに通過します。取得直後に、1日ぶんの時刻の並びを目で見るのが、いちばん確実な確認方法です。最初と最後の時刻が市場の開始・終了と合っているか。空白の位置が昼休みと合っているか。
取得した時刻も一緒に残す
データそのものの時刻とは別に、こちらが取得した時刻も残しておいてください。データの時刻が現地時間で、取得した時刻が UTC だと、後から見たときにどちらがどちらか分からなくなります。両方に印を付けておくのが安全です。
取得時刻は、EP.06 で扱った差分取得の起点としても使います。ここが現地時間と UTC で混ざっていると、差分の範囲が数時間ずれます。取りこぼしか二重取得のどちらかが起きます。
from datetime import datetime, timezone
def fetch_stamp() -> dict: """取得時刻は必ずタイムゾーン付きの UTC で残す。 datetime.now() を引数なしで呼ぶと、印の無い現地時間になる。""" now = datetime.now(timezone.utc) return { "fetched_at_utc": now.isoformat(), "fetched_at_epoch": int(now.timestamp()), }
def assert_aware(dt: datetime) -> datetime: """タイムゾーンの印が無い時刻を、そのまま先へ流さない。""" if dt.tzinfo is None: raise ValueError(f"タイムゾーンが付いていない時刻: {dt}") return dt確認は1回で済みますが、提供元を変えたときは必ずやり直してください。同じデータに見えても、時刻の扱いは提供元ごとに違います。次回は、指数の構成銘柄を扱います。
時刻の問題は、原因が分かれば直すのは簡単です。難しいのは、ずれていることに気づく段階です。取得したデータの最初と最後の時刻を毎回出力するだけでも、気づける確率は大きく上がります。
この記事の感想を教えてください
あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。