こんにちは。システムトレーダーの卵ことKenKenです。前回は、日経225先物の2013年から直近のデータについて分析しました。結果としては、夜間取引の寄付と引けで正のリターンが得られる可能性が得られました(詳細は前回の記事をご参照ください。)
そこで、今回は夜間取引における取引戦略について考えていきたいと思います。今回も「Python3ではじめるシステムトレード ──環境構築と売買戦略」を参考にしております。
リスクについての考察
night(夜間取引の寄り付き~引け)でロングポジションをとった場合のリスクについて考えてみる。まずは、この戦略を1日、2日、…、120日まで継続した場合の最大値、平均値、最小値をプロットしてみる。
# ポジションを継続した場合のグラフ high = [0] * 120 ave = [0] * 120 low = [0] * 120 for i in range(120): high[i] = night.rolling(i).sum().max() ave[i] = night.rolling(i).sum().mean() low[i] = night.rolling(i).sum().min() plt.figure() plt.plot(high, label='high') plt.plot(ave, label='ave') plt.plot(low, label='low') plt.legend() plt.xlabel('t') plt.ylabel('PL')

グラフから120日間の取引では利益が出ない確率があり、最大のドローダウンは4000ポイント近くになる可能性が読み取れる。lowに着目すると、20日近辺で一気に下落し、その後はポジションを継続することで損失は改善されている様子が見られる。おそらく、マイナスの経済イベント(コロナショック等)による影響をもろに受けてしまっている。
そこで、動的分析を用いてドローダウンの原因を探ってみる。その方法として、移動標準偏差(移動期間5日)を書いてみる。
# 移動標準偏差 plt.figure(figsize=(12, 6)) night.rolling(5).std().plot() plt.ylabel('t') plt.ylabel('std')

やはり、2015年~2016年にかけては中国ショックに伴い、ボラティリティが高い。この中だと特に、2020年~2021年のボラティリティの高さに目立つ。これは、皆さんがご存じの通り、新型コロナウイルスの影響だろう。他の期間のボラティリティの高さは、勉強不足のため、検討が付かない。。
次に移動T検定(期間5日)を行い、累積損益と共に表示することでドローダウンを探る。
# 移動T検定(期間5日) n = 5 t_values = night.rolling(n).mean() / night.rolling(n).std() * np.sqrt(n) # グラフを書く plt.figure(figsize=(12, 6)) ax = t_values.plot(color='lightgreen', label='t') plt.ylabel('t estimator') ax2 = ax.twinx() night.cumsum().plot(style='--', label='cum PL') plt.ylabel('PL')

このグラフからドローダウンは、2015年~2016年の中国ショック、2020年~2021年のコロナショックにあることがわかる。上で述べた2018年のボラティリティは、プラス方向であったことも読み取れ、ドローダウンの原因ではないことがわかった。
取引戦略の構築・シミュレーション
ここまでの結果を踏まえ、以下の取引戦略を考えてみた。
- 基本戦略:夜間取引の寄り付きで買い、引けで売る
- 条件
- トレンド判定:t値 ≧ \(t_{0.85, 5}\) (有意水準:15%)
- ボラティリティの安定性:ボラティリティが信頼区間の上側より低い値となる
(2点目の補足)
母集団が母分散\(\sigma^2\)の正規分布に従うとき、抽出された標本のサンプル数を\(n\)、不偏分散を\(s^2\)とすると、以下の統計量は自由度\((n-1)\)のカイ二乗分布\(\chi^2\)に従う。
$$\frac{(n-1)s^2}{\sigma^2} \sim \chi^2$$
これより、各時点における標本標準偏差\(s\)は以下を満たすとき、ボラティリティは安定とする。
$$s \leq \sqrt{\frac{\sigma^2 \cdot \chi^2_{(n-1)}}{(n-1)}}$$
今回、真の標準偏差\(\sigma\)は210としている。この辺は経験値で決めるようで、、、移動標準偏差のグラフを見ると400くらいが上限となっており、その7割の210というような形で決めて設定してみた。コードは以下の通り。
# 戦略のシミュレーション from scipy.stats import t, chi2 n = 5 t_values = night.rolling(n).mean() / night.rolling(n).std() * np.sqrt(n) # 統計量算出(採択域:0.8, 有意水準:1%) s0 = np.sqrt(chi2.ppf(0.80, n-1) * (210**2) / (n-1)) # 標準偏差の統計量を算出、210は真の標準偏差(なんとなく) t0 = t.ppf(0.95, n-1) def is_long(s, t): '''売買判定 s:標準偏差, t:t値''' long = False if s < s0 and t0 < t: long = True return long t1 = t_values.shift(1) # 5日前~前日で算出したt値 s1 = night.rolling(5).std().shift(1) # 5日前~前日で算出した標準偏差 # nigtに結合 night_merged = pd.concat([night, t1, s1], axis=1).dropna() night_merged.columns = ['価格差', 't', 's'] # 各行に関数を適用する mask = night_merged.apply(lambda x: is_long(x['s'], x['t']), axis=1) night_long = night_merged['価格差'][mask] night_long.cumsum().plot() plt.ylabel('PL')

2013年からの直近までの運用で、トータルはゼロとなってしまった。序盤は、順調に利益を重ねたものの2015年~2016年にかけての中国ショックでマイナスに転じている。その後は、順調に利益を重ねていったが、昨今のコロナショックにより積み上げてきた利益もゼロとなってしまう結果になった。
ボラティリティの安定性の調整
ここまでの結果から、ボラティリティの安定性が機能していないように思える。そこで、採択域を0.8から0.5に変えてみる。コードはほぼ同じのため、省略。結果は、以下の通り。

中国ショックを回避できていない。また、取引回数が減り、プラス・マイナスともにリターン幅が小さくなった。ボラティリティを制御したことにより、プラスの大幅リターン獲得の機会を失ってしまった。
トレンド判定の調整
そこで、トレンド判定に用いていたt値の有意水準を小さくしてみる。そうすることでこれまで以上に有意に正なものだけを抽出した取引となる。変更点は、有意水準の値のみ。


左が有意水準10%、右が有意水準5%である。左側は、序盤は上昇基調だったものの全体的にマイナス基調である。これまでのモデルの中では最もひどい。ただ、プラスマイナスの幅を見ると、これまでより価格差が広がっていることから、有意水準を厳しく設定することで、プラスのトレンドを捉えることができていた可能性も見られる。
一方で、右側は総じて悪くないモデルと思われる。序盤に安定してリターンを積み上げている。中国ショック時では、横ばいが続いており取引を制御できている。その後は、若干マイナスに転じたが2019年末にかけて安定してリターンを獲得できている。しかし、その後のコロナショックで大幅にリターンを落とし、直近では回復基調となっている。トータルでマイナスにならなかっただけ良いのではないか。
まとめ
今回は、「Python3ではじめるシステムトレード ──環境構築と売買戦略」を参考にしながら、統計量をベースにした取引戦略を構築しました。トータルでプラスのモデルを構築できたもののトータルリターンはごくわずかなものとなってしまいました。取引コストを踏まえると、あまり手元には残らないかもしれませんね。。。あと、コロナショックによるマイナスとプラスの影響を受けすぎ。
やはり、マーケットで勝つためには、そう簡単にはいかないと実感しました。ただ、ここまでの開発を通して、モデル開発~検証までの一連の基本動作は身についた気がします。
以上