日経先物分析その1 ... Sessionの割り当て
何をするのか
日経先物を対象にデータの前処理や分析を行います。一度に全部を書くのが大変なので、シリーズ化しちょこちょこ書いて行こうと思っていますが、勉強中の素人なので間違いは勘弁してください。
その1では何をするのか
日経先物miniの1分足データを使用し、session名を割り当て、sessionごとにuniqueなIDを割り当てる方法を解説します。
日経先物について
以下は生成AIに聞いた回答のコピペです。
日経先物とは
日経先物とは、日本経済新聞社が発行する日本経済指数(日経平均株価)に連動する先物取引です¹. 日経平均株価の値動きを予想し、その値動きに応じて売買を行うことで、利益を得ることができます¹. 日経平均株価が上昇すれば、日経先物の価格も上昇し、逆に下落すれば、価格も下落します¹. なお、日経先物は大阪取引所の他にCMEとSGXで取引されています¹.
¹: 日経平均先物 CME SGX 大取 夜間 リアルタイム チャート
(1) 日経平均先物 CME SGX 大取 夜間 リアルタイム チャート. https://nikkei225jp.com/cme/.
(2) 日経225先物:5日22時=100円高、3万3070円 - みんかぶ. https://minkabu.jp/news/3708471.
(3) 先物・オプション価格情報 | 日本取引所グループ. https://www.jpx.co.jp/markets/derivatives/quotes/index.html.
DaySessionとNightSessionについて
日中取引は8:45から15:15、夜間取引は16:30から翌日の6:00までの時間帯です¹. 取引時間の流れを私たちの生活時間に即してあてはめると「朝の取引から始まり翌日未明までの取引」が1日と考えがちですが、先物取引での「取引日」は「夕方の夜間取引から始まり翌営業日の日中取引終了まで」が1計算区域として「取引日」となります¹.
また、日中取引と夜間取引では、値段の決め方が異なります。日中取引では「板寄せ方式」で始値として基準となる値段を決定し、その後は「時間優先・価格優先」の原則に基づくオークション方式により取引が行われます。一方、夜間取引ではオークション方式によって値段が決定されます¹.
¹: フィリップ証券
(1) 取引時間について:日経225先物を学ぼう | 日経225先物・NYダウ .... https://www.phillip.co.jp/futures/market_hours.php.
(2) ナイト・セッション | 日本取引所グループ. https://www.jpx.co.jp/derivatives/rules/trading-hours/01.html.
(3) 日経平均先物とは?特徴や基本的なルールをわかりやすく解説 .... https://news.mynavi.jp/kabu/nikkeiheikin-sakimono/.
準備
使用するデータ
DataFrame
shape: (2_874_147, 6)
┌─────────────────────┬───────┬───────┬───────┬───────┬────────┐
│ datetime ┆ op ┆ hi ┆ lw ┆ cl ┆ volume │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ datetime[μs] ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 │
╞═════════════════════╪═══════╪═══════╪═══════╪═══════╪════════╡
│ 2013-01-04 09:00:00 ┆ 10750 ┆ 10765 ┆ 10745 ┆ 10750 ┆ 101269 │
│ 2013-01-04 09:01:00 ┆ 10750 ┆ 10765 ┆ 10735 ┆ 10745 ┆ 17336 │
│ 2013-01-04 09:02:00 ┆ 10740 ┆ 10750 ┆ 10740 ┆ 10745 ┆ 5341 │
│ 2013-01-04 09:03:00 ┆ 10745 ┆ 10750 ┆ 10740 ┆ 10745 ┆ 7193 │
│ … ┆ … ┆ … ┆ … ┆ … ┆ … │
│ 2023-06-16 15:12:00 ┆ 33600 ┆ 33600 ┆ 33600 ┆ 33600 ┆ 0 │
│ 2023-06-16 15:13:00 ┆ 33600 ┆ 33600 ┆ 33600 ┆ 33600 ┆ 0 │
│ 2023-06-16 15:14:00 ┆ 33600 ┆ 33600 ┆ 33600 ┆ 33600 ┆ 0 │
│ 2023-06-16 15:15:00 ┆ 33645 ┆ 33645 ┆ 33645 ┆ 33645 ┆ 24124 │
└─────────────────────┴───────┴───────┴───────┴───────┴────────┘
環境
今回はpandasではなくpolarsを使用しますので、インストールしていない方は下記を参考にインストールしてください。
実行
Import
import datetime
import polars
データの読み込み
データはparquetファイルで保存しています。csvと比べて容量も小さく、読み書きも高速なのでオススメです。
fp = r'../datasets/NK225F.parquet'
data = pl.read_parquet(fp)
Session名の割り当て
現在の日経先物の取引時間は8:45から15:15までですが、今回使用するデータは2013年からのものを使用しています。昔とは取引時間が変わっているので、余裕を見て適当に割り当てます。
data = data\
.with_columns([
pl.col('datetime').dt.time().alias('time') ])\
.with_columns([
pl.when((pl.col('time') < datetime.time(7, 0, 0))
| (datetime.time(16, 0, 0) < pl.col('time')))
.then('NightSession')
.otherwise('DaySession')
.alias('session'), ])\
.drop('time')
session名を割り当てたDataFrame
shape: (2_874_147, 7)
┌─────────────────────┬───────┬───────┬───────┬───────┬────────┬────────────┐
│ datetime ┆ op ┆ hi ┆ lw ┆ cl ┆ volume ┆ session │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ datetime[μs] ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ str │
╞═════════════════════╪═══════╪═══════╪═══════╪═══════╪════════╪════════════╡
│ 2013-01-04 09:00:00 ┆ 10750 ┆ 10765 ┆ 10745 ┆ 10750 ┆ 101269 ┆ DaySession │
│ 2013-01-04 09:01:00 ┆ 10750 ┆ 10765 ┆ 10735 ┆ 10745 ┆ 17336 ┆ DaySession │
│ 2013-01-04 09:02:00 ┆ 10740 ┆ 10750 ┆ 10740 ┆ 10745 ┆ 5341 ┆ DaySession │
│ 2013-01-04 09:03:00 ┆ 10745 ┆ 10750 ┆ 10740 ┆ 10745 ┆ 7193 ┆ DaySession │
│ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │
│ 2023-06-16 15:12:00 ┆ 33600 ┆ 33600 ┆ 33600 ┆ 33600 ┆ 0 ┆ DaySession │
│ 2023-06-16 15:13:00 ┆ 33600 ┆ 33600 ┆ 33600 ┆ 33600 ┆ 0 ┆ DaySession │
│ 2023-06-16 15:14:00 ┆ 33600 ┆ 33600 ┆ 33600 ┆ 33600 ┆ 0 ┆ DaySession │
│ 2023-06-16 15:15:00 ┆ 33645 ┆ 33645 ┆ 33645 ┆ 33645 ┆ 24124 ┆ DaySession │
SessionごとにUniqueなIDを割り当てる
単純に時間単位でresamplingするのではなく、今回はsessionごとにresamplingを行いたいので、sessionごとにuniqueなIDを割り当てる関数を作成します。
def unique_id_by_session(
datetimes: Iterable[datetime.datetime],
sessions: Iterable[str],
use_first: bool=True
) -> pl.DataFrame:
if isinstance(datetimes, pl.Series):
dt_col = datetimes.name
else:
dt_col = 'datetime'
if isinstance(sessions, pl.Series):
ses_col = sessions.name
else:
ses_col = 'session'
# DataFrameを作成してSessionの変わり目を判断
df = pl\
.DataFrame([
datetimes,
sessions])\
.with_columns(
pl.col(ses_col).shift().alias('shift_session'),
pl.col(dt_col).dt.time().alias('time'))\
.with_columns([
pl.when(pl.col(ses_col) != pl.col('shift_session'))
.then(True)
.otherwise(False)
.alias('change_session')
])
if use_first:
# 最初の行がshiftする結果nullになってしまうが、使用したい場合
df = df.with_columns([
pl.when(pl.col('shift_session') == None)
.then(pl.lit(True))
.otherwise(pl.col('change_session'))
.alias('change_session')
])
# Sessionの変わり目になる行のみ抽出し、0からIDを割り当てる
filtered = df\
.filter(
pl.col('change_session') == True)
unique_ids = list(range(0, filtered.shape[0]))
filtered = filtered\
.with_columns(
pl.Series(name='session_id', values=unique_ids))\
.select([dt_col, 'session_id'])
# DataFrameを結合してNull値を埋める
result = df\
.join(other=filtered, on=dt_col, how='outer')\
.with_columns(
pl.col('session_id').fill_null(strategy='forward'))\
.select([dt_col, 'session_id'])
return result
sessions = data\
.join(
unique_id_by_session(data['datetime'], data['session']),
on='datetime',
how='outer')\
.groupby('session_id')\
.agg([
pl.col('datetime').first(),
pl.col('session').first(),
pl.col('op').first(),
pl.col('hi').max(),
pl.col('lw').min(),
pl.col('cl').last(),
pl.col('volume').sum()
])\
.sort('session_id')
IDを割り当てたDataFrame
shape: (5_125, 8)
┌────────────┬─────────────────────┬──────────────┬───────┬───────┬───────┬───────┬─────────┐
│ session_id ┆ datetime ┆ session ┆ op ┆ hi ┆ lw ┆ cl ┆ volume │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i32 ┆ datetime[μs] ┆ str ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 │
╞════════════╪═════════════════════╪══════════════╪═══════╪═══════╪═══════╪═══════╪═════════╡
│ 0 ┆ 2013-01-04 09:00:00 ┆ DaySession ┆ 10750 ┆ 10765 ┆ 10650 ┆ 10685 ┆ 1076582 │
│ 1 ┆ 2013-01-04 16:30:00 ┆ NightSession ┆ 10705 ┆ 10805 ┆ 10670 ┆ 10750 ┆ 620544 │
│ 2 ┆ 2013-01-07 09:00:00 ┆ DaySession ┆ 10740 ┆ 10745 ┆ 10585 ┆ 10620 ┆ 1250704 │
│ 3 ┆ 2013-01-07 16:30:00 ┆ NightSession ┆ 10615 ┆ 10625 ┆ 10535 ┆ 10575 ┆ 452390 │
│ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │
│ 5121 ┆ 2023-06-14 16:30:00 ┆ NightSession ┆ 33680 ┆ 33770 ┆ 33210 ┆ 33600 ┆ 897015 │
│ 5122 ┆ 2023-06-15 08:45:00 ┆ DaySession ┆ 33600 ┆ 33750 ┆ 33325 ┆ 33410 ┆ 1280238 │
│ 5123 ┆ 2023-06-15 16:30:00 ┆ NightSession ┆ 33515 ┆ 33540 ┆ 33105 ┆ 33450 ┆ 841521 │
│ 5124 ┆ 2023-06-16 08:45:00 ┆ DaySession ┆ 33385 ┆ 33725 ┆ 33130 ┆ 33645 ┆ 1189195 │
└────────────┴─────────────────────┴──────────────┴───────┴───────┴───────┴───────┴─────────┘
終わりに
今回はSession名の割り当てとIDの割り当てを行いました。
次回はMSQまでの残存時間を計算する予定です。
Discussion