前回の記事では、日経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ではじめるシステムトレード ──環境構築と売買戦略」を参考にしながら、統計量をベースとした取引戦略を構築しました。
トータルでプラスのモデルを構築できたもののトータルリターンはごくわずかなものでした。
ただし、取引コストを踏まえると、実質マイナスです。
今回は勝てる戦略は作ることはできませんでしたが、モデル開発~検証までの一連の基本動作は身についたのではないでしょうか。
当記事の内容がトレード戦略作成の参考になりましたら幸いです。
コメント