ファイナンス機械学習:人工データを使ったバックテスト:E-mini S&P 500 先物ドルバー

 E-mini S&P 500 先物のティックデータからドルバーを作成し、OU過程にフィットする。
 ここでは、2019年3月のみのデータを扱うとする。

import pandas as pd
from datetime import datetime,timedelta, date
from importlib import reload
import Bars as bars
import FracDiff as fdiff
reload(bars)

SP_data = pd.read_csv('SP.csv')
SP_data = SP_data[SP_data['volume'] > 0]
SP_data['datetime'] = SP_data['date'] + "/" + SP_data['time']
SP_data['datetime'] = SP_data['datetime'].apply(lambda dt: datetime.strptime(dt, '%m/%d/%Y/%H:%M:%S.%f'))
SP_data=SP_data[SP_data['datetime'] >= datetime(2019, 1, 1)]
#SP_data=SP_data[(SP_data['datetime'] < datetime(2009, 4, 1)) & (SP_data['datetime'] >= datetime(2009, 3, 1))]
SP_data.index=SP_data['datetime']
SP_data.drop(['date','time'],axis=1, inplace=True)
SP_data.drop(['datetime'],axis=1, inplace=True)
SP_data.volume=SP_data.groupby(SP_data.index).volume.sum()
SP_data = SP_data[~SP_data.index.duplicated(keep='first')]

dv=30_000
Dbar=bars.getDollarBars(SP_data,dv)
RD=Dbar['20190316':'20190331']['Close']
P0=RD.to_list()
XX = P0[:-1]
YY = P0[1:]
beta, alpha, _, _, _ = ss.linregress(XX, YY)  # OLS
theta_ols = alpha / (1 - beta)
res = np.float64(YY) - beta*np.float64(XX) - alpha  # residuals
std_resid = np.std(res, ddof=2)
tau_ols=-np.log(2)/np.log(beta)

print("SP OLS mu = ", theta_ols)
print("SP OLS tau = ", tau_ols)
print("SP OLS sigma = ", std_resid)
print("SP OLS Phi =", 2**(-1./tau_ols))

E0=theta_ols
tau=tau_ols
sigma=std_resid
phi=2**(-1./tau)
maxH=len(RD)
sigmaT=np.sqrt(sigma*sigma*phi_sum(phi=phi, J=maxH, a=2))
maxT = 1
T= np.linspace(0, maxT, maxH+1)
paths=1e5

print(E0,phi,sigma)
    
p0=RD.iloc[0]
P = np.zeros((maxH, int(paths)))
P[0, :] = p0
W=np.random.normal(0.,1.,size=(maxH-1,int(paths)))
for hp in range(0,maxH-1):
    P[hp+1]=E0+ phi * (P[hp,:] - E0)  + sigma*W[hp,:]
    
fig = plt.figure(figsize=(12, 7))
#plt.plot(T[1:], P[:, :10], linewidth=0.5)
plt.plot()
plt.plot(np.linspace(1,maxH,maxH), P[:, :10], linewidth=0.5)
plt.plot(np.linspace(1,maxH,maxH),RD,color='orange',linewidth=1.5)
plt.axhline(y=E0+sigmaT, xmin=T[1], xmax=T[-1],color='black')
plt.axhline(y=E0-sigmaT, xmin=T[1], xmax=T[-1],color='black',label='sigmaT')
plt.axhline(y=E0, xmin=T[1], xmax=T[-1],color='red',label='Estimate')
plt.legend()
plt.xlabel('bars')
plt.ylabel('P')
plt.title('2019-03-16:2019-03-31')
plt.show()
OU過程フィッティング係数

これから、2019年3月の予想平均$${E_0=2826.44}$$、半減期は$${\tau=2.82}$$、ウィナー過程の分散は$${\sigma=11.6}$$と計算される。この係数を使ったOU過程10本と実際のドルバーをグラフにすると、以下の様になる。

2019-03 OU過程

利益確定閾値と損切り閾値を分散を基準として決め、最大保有期間を$${10,20,\dots }$$とした。最大保有期間の最大値は全期間としている。

PTSLM=np.linspace(0,2,21)
rPT=rSLm=PTSLM*(sigmaT)
columns=['top','bottom','mean','std','sr']

coeffs={'forecast':E0,'hl':tau,'sigma':sigma}
maxH=[10,20,30,40,50,60,70,80,90,len(RD)]

Tvec,dt = np.linspace(0, 1,len(RD), retstep=True)

for mh in maxH:
    if mh > len(RD): break
    output=strtP(coeffs,paths=1e5, maxH=mh,dt=dt,rPT=rPT,rSLm=rSLm,p0=RD.iloc[0])

    df=pd.DataFrame(output,columns=columns)
    sr=df.sort_values(by=['top','bottom'])['sr']
    sr=sr.to_numpy().reshape(len(rPT),-1)
    plt.imshow(sr.T,cmap='viridis', extent=(rPT[0], rPT[-1], rSLm[0], rSLm[-1]), 
              aspect='auto',interpolation='nearest',origin='lower')
    plt.title(f'maxH:{mh}, Forcast:{round(coeffs["forecast"],2) }, tau:{round(coeffs["hl"],2)}, sigma:{round( coeffs["sigma"],2)}')
    plt.xlabel('Top Barrier')
    plt.ylabel('Bottom Barrier')
    plt.colorbar()
    filename=f'./2019March2_maxH{mh}.pdf'
    plt.savefig(filename)
    plt.show()
    plt.clf()

3月1日にロングポジションを取った時、最大保有期間を一月とした場合の最適戦略をヒートマップで表したのが以下の図である。

2019-03 最適戦略シャープレシオ ヒートマップ

2019年3月は、期待リターンが正のため、損切りの閾値を高く取ったほうが利益が出やすくなっている。保有期間が長いほど、シャープレシオも高くなっている。
 これを半期に分けて最適戦略の閾値を見てみる。

2019-03-01から2019-03-15までの閾値でのシャープレシオ

この2週間も長期リターンが正のため、長期で保有しいた方が同じ閾値でもシャープレシオは高い。
最後に後半2週間のシャープレシオのヒートマップが以下である。この場合、3月15日にロングポジションを取ったことになっており、この時の値は$${2026}$$から期待リターンはほぼゼロに近い。よって、利益の出ないグリーンゾーンが増え、シャープレシオもマイナスになる領域がある。

この記事が気に入ったらサポートをしてみませんか?