引き続き、Protraの出力をPythonで分析していきます。
その1では、Protraの出力をPythonで取り込みました。
その2では、取り込んだデータをグラフ表示しました。
今回は、取り込んだデータを統計的に解析していきます。
その2のまとめ
その2で、最終的に得られたデータフレームは以下の通りです。
引き続き、こちらのデータフレームを使用していきます。
信頼区間の表示
はじめに、全取引データの P/L を時系列で表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import matplotlib.pyplot as plt fig = plt.figure(figsize=(10,4)) ax = fig.add_subplot(111) # データを指定 ax.plot(td2.index, td2['P/L'], label='P/L', linewidth=0.4) #軸ラベル ax.set_ylabel('P/L') ax.legend() plt.plot([0,12000],[0.,0.],"r-", linestyle='dashed') plt.xlim(0,12000) #plt.ylim(-0.1,0.1) plt.show() |
※下図は線の太さを変更しています。
P/Lについて、平均値の信頼区間を表示させます。
95%の信頼区間とは、「母集団の平均から95%信頼区間を求める作業を100回やったとき、95回はその区間の中に母平均が含まれる」を意味します。
・・・わかりづらいですね。
例を挙げてみます。
100万個のデータの平均値を求めることは難しいです。そこで100個のデータだけを用いて平均値を求めてみましょう。当然 100万個の平均値とはズレが生じるでしょう。
「同様に100個のデータを用いて平均値を求める操作を100回行った際に、100回のうち95回は母平均を含む範囲」が信頼区間の意味です。
よく「得られたデータが95%の確率で、その区間内に含まれる」と誤解されるので注意です。
定義はややこしいですが、計算はそれほど難しくありません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import pandas as pd import numpy as np from scipy import stats pl_sum = 0.0 pl_var1 = 0.0 alpha = 0.95 #信頼係数 for i, rows in td2.iterrows(): pl_sum += rows['P/L'] pl_mean = pl_sum/(i+1) pl_var1 += (rows['P/L']-pl_mean)**2 pl_std = np.sqrt(pl_var1/(i+1)) t = stats.t.ppf(1-(1-alpha)/2, i) low = pl_mean - t*pl_std/np.sqrt(i) up = pl_mean + t*pl_std/np.sqrt(i) td2.at[i, 'Up-CI'], td2.at[i, 'Low-CI'] = up, low td2.at[i, 'P/L_sum'] = pl_sum print('finish') |
時系列データに対し、時系列順に信頼区間を計算しています。
詳しく見ていきます。
1 2 3 4 |
pl_sum += rows['P/L'] pl_mean = pl_sum/(i+1) pl_var1 += (rows['P/L']-pl_mean)**2 pl_std = np.sqrt(pl_var1/(i+1)) |
時系列順に信頼区間を計算するため、その時点での平均値と標準偏差を求めています。
1 2 3 |
t = stats.t.ppf(1-(1-alpha)/2, i) low = pl_mean - t*pl_std/np.sqrt(i) up = pl_mean + t*pl_std/np.sqrt(i) |
t を計算し、信頼区間の下限と上限を計算しています。
1 2 |
td2.at[i, 'Up-CI'], td2.at[i, 'Low-CI'] = up, low td2.at[i, 'P/L_sum'] = pl_sum |
データフレームに、計算した信頼区間値を入れています。
後のグラフ表示用に、累積P/Lも同様にデータフレームに追加しています。
得られたデータフレームがこちら。
1 |
td2.head() |
グラフに表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 信頼区間の表示 import matplotlib.pyplot as plt fig = plt.figure(figsize=(10,4)) ax = fig.add_subplot(111) # データと凡例を指定 ax.plot(td2.index, td2['P/L'], label='P/L', linewidth=0.4) ax.plot(td2.index, td2['Up-CI'], label='Up', color='orange') ax.plot(td2.index, td2['Low-CI'], label='Low', color='orange') #軸ラベル ax.set_ylabel('P/L') ax.legend() plt.plot([0,12000],[0.,0.],"r-", linestyle='dashed') plt.xlim(0,12000) plt.ylim(-1,1) plt.show() |
小さくて見えないので、表示範囲を変えます。
信頼区間を99%に変えると、制約が厳しくなる分 区間が広くなります(下図)。
時系列を経るに従って、精度が高くなり信頼区間の幅が小さくなっていくことがわかります。
ここで、信頼区間の下限が0以上になっていることに着目します。
このストラテジを長期的に運営すると平均P/Lはプラスになる、つまり損をする可能性がかなり低いことを意味します。(将来のプラスを保証する という意味ではありません。)
ストラテジの実力を統計的に評価できました。
さて、x軸の4500付近を見ると信頼区間がガクンと下がっています。どうやら この辺りで市場の状況が変わり、ストラテジが通用しづらくなったようです。
続いて、このようなストラテジの変調を評価する方法を見ていきます。
ストラテジの変調を発見する
先程データフレームに追加した累積P/Lを表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 累積利益の表示 import matplotlib.pyplot as plt fig = plt.figure(figsize=(10,4)) ax = fig.add_subplot(111) # データを指定 ax.plot(td2.index, td2['P/L_sum'], label='累積P/L', linewidth=1) #軸ラベル ax.set_ylabel('累積P/L') ax.legend() plt.show() |
上でも述べたように、4500付近でガクンと下がっていますね。
直近の勝率
直近の勝率をグラフに表示しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# 直近 n回の勝率 import pandas as pd import numpy as np win_cnt = 0 win_rate = 0.0 old_win_rate = win_rate trade_num = 20 for i, rows in td2.iterrows(): if rows['win/lose']=='win': win_cnt += 1 win_rate = win_cnt/trade_num*100 if i%trade_num == 0 and i != 0: td2.at[i, 'Win_rate'] = win_rate old_win_rate = win_rate win_cnt = 0 elif i>trade_num: td2.at[i, 'Win_rate'] = old_win_rate print('finish') |
20トレードごとに、過去20トレードの勝率を計算してデータフレームに追加しています。
得られたデータフレームはこんな感じになりました。
グラフに表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# 累積利益と直近トレードの勝率の表示 import matplotlib.pyplot as plt fig = plt.figure(figsize=(10,4)) ax1 = fig.add_subplot(111) ax1.plot(td2.index, td2['P/L_sum'], label='累積P/L', linewidth=1) ax2 = ax1.twinx() ax2.plot(td2.index, td2['Win_rate'], label='勝率', linewidth=0.2, color='green') #軸ラベル ax1.set_ylabel('累積P/L') ax2.set_ylabel('勝率') #plt.xlim(1000,2000) plt.show() |
※上:20トレード毎、下:100トレード毎の勝率
上のコードでは 20トレード(n+20)ごとに勝率を計算しました。n ~ n+19回目のトレードでは、直前の(n+20)の勝率を保持していました。
今度は、毎トレードごとに過去20トレードの勝率を計算するコードを書いてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# 直近 n回の勝率 import pandas as pd import numpy as np td2['Win_rate'] = 0.0 win_rate = 0.0 trade_num = 20 win_cnt = np.zeros(trade_num) for i, rows in td2.iterrows(): j = i%trade_num if rows['win/lose']=='win': win_cnt[j] = 1 else: win_cnt[j] = 0 win_rate = np.sum(win_cnt)/trade_num*100 if i > trade_num: td2.at[i, 'Win_rate'] = win_rate print(win_cnt) print('finish') |
配列要素に、過去20トレードの勝敗を記録しています。
print(win_cnt)
の出力は以下のようになっています。1が勝ち,0が負け トレードを意味しており、過去20トレードの勝数がわかります。
同様にグラフにしてみましょう。
※上:20トレード毎、下:100トレード毎の勝率
100トレードごとのグラフを見ると 違いがわかりやすいですね。こちらの形式の方が、トレード毎に変調を確認できて便利でしょうか。
さて前の記事で、このデータの通算勝率は約65%と述べました。しかし時系列を追っていくと勝率に変動があることがわかります。
勝率が下がると 累積P/Lも下がり、勝率が上がると 累積P/Lの傾きも上がります。
勝率によって、ストラテジの市場状況への対応能力を簡易的に見ることができました。
直近の勝率が通算勝率より下がってきたら、ストラテジの優先順位を下げたり、投資金額を抑えても良いかもしれません。
t検定
t検定を使用してみましょう。t検定では、以下を評価します。
さっそくコードを書いてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# t検定 import pandas as pd import numpy as np from scipy import stats td2['Ttest_p']=0.0 td2['mean']=0.0 trade_num = 20 pl_sum = 0.0 pl_var1 = 0.0 pl_sum_num = 0.0 pl_var1_num = 0.0 old_p = 0.0 for i, rows in td2.iterrows(): pl_mean = rows['P/L_sum']/(i+1) pl_var1 += (rows['P/L']-pl_mean)**2 pl_std = np.sqrt(pl_var1/(i+1)) pl_sum_num += rows['P/L'] pl_mean_num = pl_sum_num/trade_num pl_var1_num += (rows['P/L']-pl_mean_num)**2 pl_std_num = np.sqrt(pl_var1_num/trade_num) t, p = stats.ttest_ind_from_stats(mean1 = pl_mean, std1 = pl_std, nobs1 = i+1, mean2 = pl_mean_num, std2 = pl_std_num, nobs2 = trade_num, equal_var = False) if i%trade_num == 0 and i != 0: td2.at[i, 'Ttest_p'] = p old_p = p pl_sum_num = 0 pl_var1_num = 0 elif i>trade_num: td2.at[i, 'Ttest_p'] = old_p td2.at[i, 'mean'] = pl_mean print('finish') |
scipyライブラリを使用しています(26-28行目)。公式ページによる説明はこちら。
過去nトレードと、それまでの全トレードで分散は等しくないと想定されるため、equal_var は False として、ウェルチのt検定で計算しています。
データフレームはこんな感じになりました。
グラフ化してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# 累積p/Lと直近nトレードのt検定の表示 import matplotlib.pyplot as plt fig = plt.figure(figsize=(10,4)) ax1 = fig.add_subplot(111) ax1.plot(td2.index, td2['P/L_sum'], label='累積P/L', linewidth=1) ax2 = ax1.twinx() ax2.plot(td2.index, td2['Ttest_p'], label='t検定p値', linewidth=0.2, color='green') #軸ラベル ax1.set_ylabel('累積P/L') ax2.set_ylabel('t検定p値') #plt.ylim(0,0.1) plt.show() |
※上:20トレード毎、下:100トレード毎の p値
勝率と同じように、毎トレード 計算するコードも書いてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# t検定 import pandas as pd import numpy as np from scipy import stats td2['Ttest_p'] = 0.0 td2['mean'] = 0.0 trade_num = 20 pl_sum = 0.0 pl_var1 = 0.0 pl_sum_num = np.zeros(trade_num) pl_var1_num = np.zeros(trade_num) for i, rows in td2.iterrows(): pl_mean = rows['P/L_sum']/(i+1) pl_var1 += (rows['P/L']-pl_mean)**2 pl_std = np.sqrt(pl_var1/(i+1)) j = i%trade_num pl_sum_num[j] = rows['P/L'] pl_mean_num = np.sum(pl_sum_num)/trade_num pl_var1_num[j] = (rows['P/L']-pl_mean_num)**2 pl_std_num = np.sqrt(np.sum(pl_var1_num)/trade_num) t, p = stats.ttest_ind_from_stats(mean1 = pl_mean, std1 = pl_std, nobs1 = i+1, mean2 = pl_mean_num, std2 = pl_std_num, nobs2 = trade_num, equal_var = False) if i > trade_num: td2.at[i, 'Ttest_p'] = p td2.at[i, 'mean'] = pl_mean print(pl_sum_num) print('finish') |
グラフはこうなりました。
※上:20トレード毎、下:100トレード毎の p値
グラフを見ると、累積P/Lの傾きが変化する点で p値が小さくなることがわかります。
例えば p値が0.05(5%)より小さくなると、「過去nトレードと、これまでの通算のP/L平均値が、有意水準5%で統計的有意差がある」ことを意味します。
これまでのトレードと比較し、損益の水準が変わってきていることになります。
特に連続してp値が小さい状態が続くと、市場の状況が変わった可能性が高いので警戒すべきでしょう。
まとめ
protraの出力を統計的に評価してみました。
通算成績では見逃してしまう、時系列に沿ったストラテジの様子を確認できました。
すべての状況に通用するストラテジは残念ながら存在しません。
市場の状況を意識し ストラテジの鮮度を常に確認することで、損失を最小限に抑え、利益を最大限 獲得できるでしょう。
…まあ 大抵 損失を被ってから、変調に気付くんですけどね ^q^