【Pythonバックテスト】最適パラメータ探索

Python

システムトレードをするうえでどのパラメータが最適か総当たりで探索する方法について記載します。使用するシステムは前回同様単純移動平均線のゴールデンクロス・デッドクロスとします。

1.データ前処理

まずはデータの前処理をします。今回は2012年から2018年までの日足を使用します。移動平均線の出力は関数にしておきます。

import numpy as np
import pandas as pd
import matplotlib as mpl

df = pd.read_csv('^N225m.csv',
                     sep=',',
                     names=('Date','Open','High','Low','Close'),
                     usecols=range(5),
                     index_col=0,
                     parse_dates=True,
                     header=0)

def SMA(data,period):
    sma = data['Close'].rolling(period).mean()
    return sma

df1=df['2012':'2018']

2.バックテスト・テスト結果出力

バックテストは前回の記事のBacktest_rev()を使用します。テスト結果出力をEquityから総損益、最大ドローダウン、プロフィットファクターに変更します。

def BacktestReport_list(Trade, PL):
    LongPL = PL['Long']
    LongTrades = np.count_nonzero(Trade['Long'])//2
    LongWinTrades = np.count_nonzero(LongPL.clip(lower=0))
    LongLoseTrades = np.count_nonzero(LongPL.clip(upper=0))

    ShortPL = PL['Short']
    ShortTrades = np.count_nonzero(Trade['Short'])//2
    ShortWinTrades = np.count_nonzero(ShortPL.clip(lower=0))
    ShortLoseTrades = np.count_nonzero(ShortPL.clip(upper=0))

    Trades = LongTrades + ShortTrades
    WinTrades = LongWinTrades+ShortWinTrades
    LoseTrades = LongLoseTrades+ShortLoseTrades

    GrossProfit = LongPL.clip(lower=0).sum()+ShortPL.clip(lower=0).sum()
    GrossLoss = LongPL.clip(upper=0).sum()+ShortPL.clip(upper=0).sum()
    Profit = GrossProfit+GrossLoss
    PF=-GrossProfit/GrossLoss
    Equity = (LongPL+ShortPL).cumsum()
    MDD = (Equity.cummax()-Equity).max()
    
    return [round(Profit,2),round(MDD,2),round(PF,2)]

3.パラメータ変更して総当たりバックテスト

移動平均線のパラメータを変更してどの組み合わせが最も総損益が大きいかを確認します。短期線をparamA,長期線をparamBで定義してすべての組み合わせでテストします。listAとしてリスト形式で出力します。

i=0
listA=[]
paramA = [ 5,7,9,15,25 ]
paramB = [ 30,45,60,120,200 ]
combinations = [(a, b)
    for a in paramA
    for b in paramB]
for a,b in combinations:
    BuyEntry,SellEntry,BuyExit,SellExit=GCDCsignal(SMA(df1,a),SMA(df1,b))
    Trade, PL = Backtest_rev(df1, BuyEntry, SellEntry, BuyExit, SellExit, TP=20, SL=1, Limit=0.5)
    Profit,MDD,PF=BacktestReport_list(Trade, PL)
    listA.append([a,b,Profit,MDD,PF])
listA
[[5, 30, -610492.6, 813337.0, 0.4],
 [5, 45, -447328.18, 559521.53, 0.38],
 [5, 60, 442512.13, 226842.83, 1.89],
 [5, 120, -304356.3, 452784.13, 0.38],
 [5, 200, -32845.69, 227811.4, 0.91],
 [7, 30, -498700.19, 748292.98, 0.42],
 [7, 45, -648605.63, 648605.63, 0.09],
 [7, 60, -283779.42, 383539.29, 0.44],
 [7, 120, 26119.84, 365404.17, 1.06],
 [7, 200, -97533.48, 284018.96, 0.67],
 [9, 30, -624753.92, 630809.67, 0.26],
 [9, 45, -450860.13, 492051.18, 0.36],
 [9, 60, 416118.56, 212903.85, 1.9],
 [9, 120, -173304.79, 320918.2, 0.57],
 [9, 200, -104120.46, 290692.52, 0.64],
 [15, 30, -252234.99, 446860.35, 0.67],
 [15, 45, 144473.25, 268956.45, 1.26],
 [15, 60, -206251.45, 429728.97, 0.58],
 [15, 120, -153878.36, 197697.89, 0.5],
 [15, 200, -46835.93, 235571.52, 0.8],
 [25, 30, 296951.36, 277971.02, 1.28],
 [25, 45, 480664.06, 234067.24, 1.88],
 [25, 60, -303508.68, 478101.74, 0.38],
 [25, 120, -82306.63, 157451.49, 0.65],
 [25, 200, 83234.35, 106525.88, 1.78]]

最後にpandasデータフレームに変換します。

df2=pd.DataFrame(listA,
                columns=['FastSMA','SlowSMA','Profit','MDD','PF'])
df2
	FastSMA	SlowSMA	Profit	MDD	PF
0	5	30	-610492.60	813337.00	0.40
1	5	45	-447328.18	559521.53	0.38
2	5	60	442512.13	226842.83	1.89
3	5	120	-304356.30	452784.13	0.38
4	5	200	-32845.69	227811.40	0.91
5	7	30	-498700.19	748292.98	0.42
6	7	45	-648605.63	648605.63	0.09
7	7	60	-283779.42	383539.29	0.44
8	7	120	26119.84	365404.17	1.06
9	7	200	-97533.48	284018.96	0.67
10	9	30	-624753.92	630809.67	0.26
11	9	45	-450860.13	492051.18	0.36
12	9	60	416118.56	212903.85	1.90
13	9	120	-173304.79	320918.20	0.57
14	9	200	-104120.46	290692.52	0.64
15	15	30	-252234.99	446860.35	0.67
16	15	45	144473.25	268956.45	1.26
17	15	60	-206251.45	429728.97	0.58
18	15	120	-153878.36	197697.89	0.50
19	15	200	-46835.93	235571.52	0.80
20	25	30	296951.36	277971.02	1.28
21	25	45	480664.06	234067.24	1.88
22	25	60	-303508.68	478101.74	0.38
23	25	120	-82306.63	157451.49	0.65
24	25	200	83234.35	106525.88	1.78

最後に総損益を降順で表示させます。sort_valueメソッドでascending=falseで降順に、Trueで昇順になります。

df2.sort_values('Profit', ascending=False)
	FastSMA	SlowSMA	Profit	MDD	PF
21	25	45	480664.06	234067.24	1.88
2	5	60	442512.13	226842.83	1.89
12	9	60	416118.56	212903.85	1.90
20	25	30	296951.36	277971.02	1.28
16	15	45	144473.25	268956.45	1.26
24	25	200	83234.35	106525.88	1.78
8	7	120	26119.84	365404.17	1.06
4	5	200	-32845.69	227811.40	0.91
19	15	200	-46835.93	235571.52	0.80
23	25	120	-82306.63	157451.49	0.65
9	7	200	-97533.48	284018.96	0.67
14	9	200	-104120.46	290692.52	0.64
18	15	120	-153878.36	197697.89	0.50
13	9	120	-173304.79	320918.20	0.57
17	15	60	-206251.45	429728.97	0.58
15	15	30	-252234.99	446860.35	0.67
7	7	60	-283779.42	383539.29	0.44
22	25	60	-303508.68	478101.74	0.38
3	5	120	-304356.30	452784.13	0.38
1	5	45	-447328.18	559521.53	0.38
11	9	45	-450860.13	492051.18	0.36
5	7	30	-498700.19	748292.98	0.42
0	5	30	-610492.60	813337.00	0.40
10	9	30	-624753.92	630809.67	0.26
6	7	45	-648605.63	648605.63	0.09
​

短期が25日、長期が45日の組み合わせが最大で総損益が480664円という結果が得られました。システムトレードでよく議論されるカーブフィッティングという考えがありますが。パラメータをさらに細かくして増やして最適な組み合わせを見たとしても将来もそのパラメータが最適といえる保証はありませんので参考程度にとどめておくのでいいと思います。

コメント

タイトルとURLをコピーしました