DXテーマポートフォリオの検証
DX銘柄
近年、AI活用によるDX(デジタルトランスフォーメーション)は業種を問わず盛んに取り上げられているホットトピックかと思います。経産省は東証と共同で以下のように年に一回の頻度でDX推進を積極的に行っている銘柄をDX銘柄として選定し公表しています。
今回はそのように選ばれたDX銘柄に絞って投資をするDXテーマポートフォリオの有効性について簡単に検証します。
データと検証方法について
データは以前の記事で取り上げたJQuantsというAPIを用いて取得することにします。最も古いデータでも2017年1月からでしたので、2017年1月末から2022年9月末までの株価の月次データを用います。検証方法については、現在時点での東証プライム銘柄に対して組んだポートフォリオと2022年のDX銘柄に選出された33銘柄に対して組んだポートフォリオのパフォーマンスを比較することで、DXテーマポートフォリオがユニバースに対してアウトパフォームするかどうかを検証します(将来の情報を使って過去を検証するのでこれはインサンプルテストとなりますが、厳密にやろうとするならば現時点で使える情報のみを使うアウトオブサンプルテストを行う必要があります)。ポートフォリオの組み方は月次リバランスで等ウェイトとします。
検証
JQuants APIを用いてデータを取得するために、以下のようなクラスを定義します。APIの仕様上、全銘柄の株価を取得するには取得したい営業日リストを事前に用意し逐次リクエストする必要があります。ここでは月末営業日を取得するget_end_month_bizdays関数を定義し、これを利用してからリクエスト間で1秒遅延を入れながら月末営業日の全上場銘柄の株価を取得するようにしています。
# モジュールのインポート
import pandas as pd
import requests
import json
import time
from tqdm import tqdm
# データ取得用クラスの定義
class JQuants():
def __init__(self, mail_address, password):
self.mail_address = mail_address
self.password = password
self.get_refresh_token()
self.get_id_token()
self.headers = {'Authorization': 'Bearer {}'.format(self.id_token)}
def get_refresh_token(self):
"""リフレッシュトークンの取得"""
data = {"mailaddress":self.mail_address, "password":self.password}
r_post = requests.post("https://api.jpx-jquants.com/v1/token/auth_user", data=json.dumps(data))
self.refresh_token = r_post.json()['refreshToken']
def get_id_token(self):
"""IDトークンの取得"""
data = {"mailaddress":self.mail_address, "password":self.password}
r_post = requests.post(f"https://api.jpx-jquants.com/v1/token/auth_refresh?refreshtoken={self.refresh_token}")
self.id_token = r_post.json()['idToken']
def get_listed_stock(self):
"""上場銘柄リストの取得"""
r = requests.get("https://api.jpx-jquants.com/v1/listed/info", headers=self.headers)
listed_df = pd.DataFrame(r.json()['info'])
return listed_df
def get_daily_price(self, code):
"""指定コードの日次株価を取得"""
r = requests.get(f"https://api.jpx-jquants.com/v1/prices/daily_quotes?code={code}", headers=self.headers)
price_df = pd.DataFrame(r.json()['daily_quotes'])
return price_df
def get_end_month_bizdays(self):
"""月末営業日の取得"""
end_month_bizdays = []
dateymds = list(self.get_daily_price(code='86970')['Date'])
for i, dateymd in enumerate(dateymds):
if i < (len(dateymds) - 1):
if dateymd[:6] != dateymds[i+1][:6]:
end_month_bizdays.append(dateymd)
return end_month_bizdays
def get_all_monthly_price(self):
"""全銘柄の月次株価を取得"""
end_month_bizdays = self.get_end_month_bizdays()
price_df = pd.DataFrame()
for dateymd in tqdm(end_month_bizdays):
time.sleep(1)
r = requests.get(f"https://api.jpx-jquants.com/v1/prices/daily_quotes?date={dateymd}", headers=self.headers)
price_df = pd.concat([price_df, pd.DataFrame(r.json()['daily_quotes'])], ignore_index=True)
return price_df
では、このクラスを用いて実際にデータを取得していきます。先のクラスに自身が登録したメールアドレス、パスワードを渡してインスタンスを生成してから、上場銘柄リストと株価を取得しそれぞれlisted_dfとprice_dfという変数に格納します。
mail_address = "xxxxxxxx"
password = "xxxxxxxx"
jquants = JQuants(mail_address, password)
listed_df = jquants.get_listed_stock()
price_df = jquants.get_all_monthly_price()
次に各ポートフォリオのユニバースを取得します。東証プライム銘柄に関しては、listed_dfのMarketCodeがAとなる銘柄リストを取ってくることによって取得、DX銘柄については経産省のサイトから2022年に選ばれた33銘柄のティッカーをコピペし整形します。
# 東証プライム市場ユニバース
prime_univ = list(listed_df.loc[listed_df['MarketCode'] == 'A', 'Code'])
# DX銘柄ユニバース
dx_univ = [
'45190',
'81740',
'18030',
'25870',
'28020',
'34070',
'49010',
'50200',
'51080',
'52010',
'59380',
'63010',
'70130',
'65010',
'77520',
'77320',
'79110',
'79360',
'90860',
'91430',
'91040',
'92020',
'94330',
'94340',
'98300',
'83540',
'86160',
'73260',
'87660',
'84390',
'34910',
'88010',
'97550'
]
次にユニバースデータと株価データを使って、各ポートフォリオのリターンを計算します。具体的にはprice_dfのデータをユニバースで絞った後に、pandas.pivot_tableを用いてインデックスが日付、カラムが銘柄コードとなるように株価データを整形し、プライスリターンを計算して日付ごとに平均することでポートフォリオのリターンを計算しています。こうすることで実質的にユニバース内等ウェイトポートフォリオのリターンを計算していることになります。
# プライムユニバース等ウェイト
prime_price_df = pd.pivot_table(
price_df[price_df['Code'].isin(prime_univ)],
index='Date',
columns='Code',
values='AdjustmentClose'
)
prime_return_df = prime_price_df.pct_change()
prime_port_return_se = prime_return_df.mean(axis=1)
# DXユニバース等ウェイト
dx_price_df = pd.pivot_table(
price_df[price_df['Code'].isin(dx_univ)],
index='Date',
columns='Code',
values='AdjustmentClose'
)
dx_return_df = dx_price_df.pct_change()
dx_port_return_se = dx_return_df.mean(axis=1)
最後に、各ポートフォリオの累和リターンをプロットすることでパフォーマンスの比較を行います。
fig = plt.figure(figsize=(8,5))
ax = fig.add_subplot(111)
prime_mean_return_se.cumsum().plot(linewidth=3, label='Prime portfolio', ax=ax)
dx_mean_return_se.cumsum().plot(linewidth=3, label='DX portfolio', ax=ax)
ax.set_title('cumsum return')
ax.grid(linestyle='--')
ax.legend()
朱色のDXテーマポートフォリオのリターンはコロナショックが起こる前までは大きく負け越していましたが、その後は差を縮め最終的には同水準まで回復しています。この結果については、2022年に選ばれた銘柄をベースにしてしまっているので、過去は改革のために必要なコストを掛けていたり、DX推進に対する企業努力が表面化していなかったため市場で評価されずに株価に織り込まれていない可能性や、そもそも過去財務状況が悪かったから足元DXに取り組んで評価されているなど色々な仮説が考えられると思います。そもそもDX推進の目的に立ち戻って考えると中長期的な企業経営や競争力の強化のための取り組みであるので、DXへの努力が実際に財務や株価に織り込まれるのは少し足の長い話なのかもしれません。
簡単でしたが、今回の検証は以上にしたいと思います。ここまでお付き合いいただきありがとうございました!
この記事が気に入ったらサポートをしてみませんか?