本記事の内容は「Python3ではじめるシステムトレード(pannrolling)」の内容を簡単にまとめたものです。

長期投資の基本
本記事では最も基本的なデータ分析の方法を長期投資を念頭に学びます。
- 手に入る長期間のデータで値動きの特徴を分析
- 市場規模の把握
- 見せかけのトレンドとランダムウォーク
- 破綻をさけるために
手に入る長期間のデータで値動きの特徴を分析
直近のそれなりのデータを使った方が適切な分析ができると思っていませんか?株式市場は生き物で、性質が変わるので短いデータで分析するべきという話を時たま耳にします。しかし、それは間違いです。このような分析の仕方では自分が期待しているような分析結果がすぐに出てしまいます。分析に使うデータは長いに越したことはありません。長すぎるということはありません。時にはモンテカルロ法で疑似的にデータを作る必要もあるほどですから。
市場規模の把握
長期投資の基本は相手をよく知ることです。それは他と比べることから始まります。投資をする際には正確にその市場規模を理解しておきましょう。
大体の規模感がつかめたのではないでしょうか?少なくとも米国の金融市場は日本よりもバカでかい。かつ日本の国債市場は株式市場よりも大きい。しかし、米国の場合にはそうではありません。国民一人当たりのGDPが両国で同じであるとすると、どちらの株式市場が上昇しやすいでしょうか?日本の場合には多くの新しく生産されたサービスや付加価値が金利の支払いに回されてしまいます。
見せかけのトレンドとランダムウォーク
多くの経済時系列や株価はランダムウォークにしたがいます。ランダムウォークにしたがう時系列は時間の経過にともない平均や分散が変化してしまいます。ランダムウォークでは時間の経過とともに平均は確率的に変化し、分散は時間の経過の平方根に比例して増大します。したがって、その株価の将来は予測が不可能です。予測ができないということは売買するタイミングを図っても意味がありません。ランダムウォークが酔っ払いの千鳥足にたとえられるように将来どこに行くの分からないのです。かつ時間の経過とともにその幅は大きくなります。つまり、大きく上昇するかもしれませんが、下落もします。
株価データがランダムウォークにしたがうかどうかを知ることはつぎの2つの点から重要です。
- 発生したトレンドが見せかけのトレンドか、トレンドはたまたま生じたものなのか、
- 2つの時系列が相関を示したときにそれらは見せかけの相関か、たまたま発生したものではないのか、
という点です。そうでないことを明確にする情報があったり、そうなる理由が明確でランダムな現象の奥に真のトレンドがあるのだという確信があるとき以外は投資できません。
Pythonでランダムウォークをつくってみましょう。つぎの図は乱数が作った株価の動きです。様々な動きをしますが、上昇トレンドや下落トレンドを作ることがあります。大体20回に一度そのような相場が現れます。これは株価がランダムに動いたとしてもトレンドが発生することを示しています。
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as ss
from matplotlib.animation import FuncAnimation
import pandas_datareader.data as web #データのダウンロードライブラリ
fig, ax = plt.subplots()
ax.set_xlim(0, 100)
ax.set_ylim(-50, 50)
ax.grid(True)
line, = ax.plot([], [],linestyle='-', linewidth=2)
locus, = ax.plot([], [], 'b.', linewidth=1,markersize=0.5)
def animate(i):
x = np.linspace(0, 100,100)
y = np.cumsum(np.random.randn(100,))
line.set_data(x, y)
xlocus.append(x)
ylocus.append(y)
locus.set_data(xlocus, ylocus)
return line,
xlocus, ylocus = [], []
anim = FuncAnimation(fig, animate, frames=np.arange(20), init_func=ud.init,
interval=1000, blit=False,repeat=False)
anim.save('randomwalk.gif', writer="pillow")
plt.show()
つぎに実際の株価を見てみましょう。株価がランダムウォークであるかどうかを判定する方法としてディッキーフラー検定があります。この検定には
- ドリフト無しランダムウォーク(rw)
- ドリフト付きランダムウォーク(drw)
- 時間トレンド・ドリフト付きランダムショーク(tdrw)
- 加速度付き時間トレンド・ドリフト付きランダムウォーク(qtdrw)
があります。これらの結果が0.1以下であれば、ランダムウォークでない可能性が高まります。
実際に、最小分散ポートフォリオ(usmv),ナスダック100(qqq),S&P500(spy),ダウ平均(dia)についてみてみましょう。
from statsmodels.tsa.stattools import adfuller
BASIC=['usmv','qqq','spy','dia']
ror = web.DataReader("DGS1","fred","1980/1/4")#米国連邦準備委員会のホームページから米国金利をダウンロード
#ror.plot()
ror = ror/250/100#jpy
def LongtermInvest(PORT,ror,start):
i=1
for asset in PORT:
tsd = web.DataReader(asset,"yahoo",start).dropna()#yahoo finance usより株価のダウンロード
dtsd=tsd.pct_change()
tmp=pd.concat([dtsd.loc[:,'Adj Close'],ror],axis=1).dropna()
tmp['adj']=(1+tmp.iloc[:,0]/(1+tmp.iloc[:,1]))
tsda=tmp.iloc[:,2].cumprod()
ts=np.log(tsda.dropna())
ts.plot(label=str(asset))
print(asset,adfuller((ts),regression='nc')[1:3],# ADF test
adfuller((ts),regression='c')[1:3],
adfuller((ts),regression='ct')[1:3],
adfuller((ts),regression='ctt')[1:3])
if i==5:
plt.legend()
plt.show()
i=0
i+=1
if i!=1:
plt.legend()
plt.show()
i=1
LongtermInvest(BASIC,ror,'2015/1/4')
usmv (0.9545542347136421, 23) (0.8638006294387173, 23) (0.0012513095084584341, 11) (0.005811135279764244, 11)
qqq (0.9917530792757916, 9) (0.9725398688611976, 9) (0.13498405344227182, 9) (0.09924300082908494, 9)
spy (0.8831775184401023, 9) (0.8368954755934965, 9) (0.00474390277885088, 9) (0.018407642576579095, 9)
dia (0.8438357944084672, 10) (0.7816330208711864, 10) (0.05496092342671481, 9) (0.08039004988307125, 9)
結果は、各行について順に、ドリフト無しランダムウォーク、ドリフト付きランダムウォーク、時間トレンド・ドリフト付きランダムショーク、加速度付き時間トレンド・ドリフト付きランダムウォークです。すべての場合において時間トレンド(tdrw,qtdrw)がある可能性を結果は示唆しています。つまり、これらの指数については確定的トレンドがある可能性が示されています。
破綻を避けるために
4つの基本株式売買戦略のうち破綻する確率が低いのはどれですか?
- リバランス:価格が動いたことにより、投資比率が目標値からずれたら元に戻す戦略。
- 損切:損が出たら、ポジションを閉じる。
- オプション:保険のようにプレミアムを払ってリスクを回避。
- 買い持ち(バイアンドホルド):買いポジションを保有し続ける。
この中で破綻する確率が低い戦略はリバランスと買い持ちだけです。
- オプションはオプション料に見合う収益が得られなれば破綻する。
- 損切は、その損が収益よりも大きければ破綻する。
アップル株とインテル株をつかってシミュレーションをしてみましょう。つぎのプログラムは最初に半々で投資した後にポジションを動かさないでいた場合のシミュレーションです。この場合には投資の価値の比率は株価が上がったほうの株が高くなります。
aapl=web.DataReader("AAPL", "yahoo","1981/12/31","2020/12/31")['Adj Close']
intc=web.DataReader("INTC", "yahoo","1981/12/31","2020/12/31")['Adj Close']
aapl=aapl/aapl.iloc[0]#株価の指数化
lnaapl=np.log(aapl)
dlnaapl=lnaapl.diff().dropna()
intc=intc/intc.iloc[0]
intc
lnintc=np.log(intc)
dlnintc=lnintc.diff().dropna()
lnaapl.plot(label='apple',style='-.')
lnintc.plot(label='intel',linestyle='--')
lnport=0.5*lnaapl+0.5*lnintc
lnport.plot(label='no rebalance')
plt.legend(loc='upper left')
つぎのシミュレーションは毎日リバランスを行って、つねに投資の価値が50%50%の比率になるように調整するプログラムです。
def portfolio_rebalance(tsd1,tsd2):
port=pd.concat([tsd1,tsd2],axis=1).dropna()
port.columns=('p1','p2')
port['a1']=0
port['a2']=0
port['v']=1
n=len(port)
p1=port['p1'].iloc[0]
p2=port['p2'].iloc[0]
v=port['v'].iloc[0]
a1=float(v/2/p1)
a2=float(v/2/p2)
port.iloc[0,2]=a1
port.iloc[0,3]=a2
for i in range(1,len(port)):
p1=port['p1'].iloc[i]#今日のアップルの株価
p2=port['p2'].iloc[i]#今日のインテルの株価
p1_0=port['p1'].iloc[i-1]#前日のアップルの株価
p2_0=port['p2'].iloc[i-1]#前日のインテルの株価
a1_0=port['a1'].iloc[i-1]#前日のアップルの保有株数
a2_0=port['a2'].iloc[i-1]#前日のインテルの保有枚数
v_0=port['v'].iloc[i-1]#前日のリバランスポートフォリオの価値
#v=a1_0*(p1-p1_0)+a2_0*(p2-p2_0)+v_0#今日のリバランスポートフォリオの価値
v=a1_0*p1+a2_0*p2#今日のリバランスポートフォリオの価値
port.iloc[i,4]=v#リバランスポートフォリオの価値のアップデート
a1=float(v/2/p1)#調整後のアップルの株数
a2=float(v/2/p2)#調整後のインテルの株数
port.iloc[i,2]=a1#アップルの株数のアップデート
port.iloc[i,3]=a2#インテルの株数のアップデート
port['v2']=0.5*port.p1+0.5*port.p2#リバランスの無いポートフォリオの価値
return port
port=portfolio_rebalance(aapl,intc)
lnport=np.log(port)
lnport.v.plot(label="port daily rebalance",linewidth=1.0)
lnaapl.plot(label='apple',style='-.')
lnintc.plot(label='intel',linestyle='--')
plt.legend(loc="upper left")
リバランスの投資効率についてイメージがつかめたと思います。
さて、これで破綻を避けられますか?2銘柄の株を選んだとして、2つの会社が倒産してしまう確率はゼロではありません。株価指数に投資することでそのリスクを回避できます。初心者に株価指数への投資を進める理由はここにあります。
もうひとつ、ドルコスト平均法(積み立て投資)で破綻を避けることはできるでしょうか?破綻はさけられます。その投資効率は「買い持ち」戦略に近いものがあります。しかし、長い間、投資対象資産が下げ続けたらどうなるでしょうか?大きなこころのプレッシャーになると思いませんか?バブル崩壊後の日経平均、1990年代の金市場を考えてみてください。10年以上も続けて下げたならあなたはその資産に継続して投資できますか?それができるのであればドルコスト平均法は戦略の1つになります。しかし、一般に意思のない他力本願の投資法に勝利の女神は微笑まない。
本記事そして本記事以外の分析の結果
- 株を購入したら利益が出るまで売らない。
- 損切はしない。
- 利益が出たらいつでも売っていいが、長く持った方が利益率は上がる。
- コロナ後の世界ではヘッジファンドは総じて運用に成功しているとはいえません。アクティブ運用、短期の運用には注意が必要です。
初心者は
- 分散投資された株価指数への投資を基本とする。
ドローダウン(2021/12/24に加筆)
「株を購入したら利益が出るまで売らない。」「損切はしない」を理解するにはドローダウンという概念を知ることが役に立ちます。ドローダウンは損が出始めてからそれを取り戻すまでの期間をいいます。また、同時にその期間にどの程度の最大損失を発生するかも理解する必要があります。アップル株には大体いままでに200を超えるドローダウンがあります。最大のドローダウンは1700日(営業日)で、最大損失は80%を超えます。これは約7年です。そのドローダウンの最大損失の10%は16%を超えます。つまり、アップル株は強いトレンドをもつ株と思ってもこの程度のドローダウンが発生するのです。そしてドローダウンが発生してからそれがどれくらい大きくなるのか、最大損失がどれくらい大きいのかを予測することは不可能です。つまり、的確に損切をしたり、底値を拾うことは不可能なのです。
def drawdown(Close):
price=Close
high=0
length=len(price)
ddhistory=[]
i=0
maxdd=0
maxddd=0
pofdowntrend=0
dd=0
for row in price.itertuples():
da=row[0]
pc=row[6]
if pc>high:
high=pc
pf1c=price["Adj Close"].iloc[i+1]
if pf1c<pc: #Draw dwon starts------------------------------------
low=pc
d_start=date(da.year,da.month,da.day) #drawdown start date
d_low=d_start
p_start=pc
p_low=pc
price2=price.iloc[i+1:]
jj=0
j=i
for row2 in price2.itertuples():
daf=row2[0]
pfc=row2[6]
if pfc<low:
low=pfc
pofdowntrend=j-i#the period of a price down
d_low=date(daf.year, daf.month,daf.day) #date of varry
p_low=pfc
if pfc>high: #drawdwon is finished
dd=(high-low)/high#the drawdown %
d_end=date(daf.year, daf.month,daf.day) #date of end of drawdwon
p_end=pfc
ddd=j-i#the drawdown duration
jj=i+pofdowntrend+1
break
j+=1
if j<length-1:
recovery=ddd-pofdowntrend
dd0=[d_start,d_low, d_end,ddd,pofdowntrend,recovery,dd,p_start,p_low,p_end]
ddhistory.append(dd0)
else:
jj=length-1
dd=(high-low)/high
d_end=np.NaN
ddd=j-i
p_end=np.NaN
#pofdowntrend=j-i
recovery=ddd-pofdowntrend
dd0=[d_start,d_low, d_end,ddd,pofdowntrend,recovery,dd,p_start,p_low,p_end]
#print(dd0)
ddhistory.append(dd0)
if dd>maxdd:
maxdd=dd
if ddd>maxddd:
maxddd=ddd
i+=1
if i>length-2:
break
ddhistory=pd.DataFrame(ddhistory, columns=['start','peak','end','dd0','dd5','dd9','pct_chg','p0','p5','p9'])
return maxdd,maxddd,ddhistory
import matplotlib.pyplot as plt #描画ライブラリ
import pandas_datareader.data as web #データのダウンロードライブラリ
tsd = web.DataReader("aapl","yahoo","1980/1/1").dropna()#jpy
maxdd,maxddd,ddhistory=drawdown(tsd)
a=ddhistory.sort_values('pct_chg')
a.iloc[-20:]
ここでpct_chgが最大ドローダウンです。