pandas-datareaderでTOPIX500銘柄の株価上昇率を算出
<pandas-datareaderでTOPIX500銘柄の株価上昇率を算出>*1
前回はpandas-datareaderで米国株の株価を取得しました。
今回は、東証一部上場銘柄の株価を取得し、業種ごとの株価変動を可視化しました。
[1] 東証上場銘柄を取得する
[1-1] JPX(日本取引所グループ)から取得
東証上場銘柄の一覧は、JPX(日本取引所グループ)のページから取得します。
中身は次のようなデータになっています。
[1-2] CSVに変換して読み込み
ExcelをCSV形式に保存したのち、PythonからpandasのDataFrameとして読み込みます。
import pandas as pd tse_list = pd.read_csv('data_j.csv', header=0)
[2] TOPIX500銘柄の業種ごとの株価上昇率を算出
東証上場銘柄のうち、TOPIX500銘柄にターゲットを絞ります。ちなみに、全銘柄だと4,086銘柄あります*2。
[2-1] TOPIX500銘柄のデータを抽出
'市場・商品区分'で東証一部上場銘柄を抽出します。次に、'規模区分'でTOPIX500銘柄を抽出します。
# TOPIX500の銘柄を抽出する tse1 = tse_list[tse_list['市場・商品区分'] == '市場第一部(内国株)'] topix500 = tse1[(tse1['規模区分'] == 'TOPIX Core30') | (tse1['規模区分'] == 'TOPIX Large70') | (tse1['規模区分'] == 'TOPIX Mid400')]
いったん東証一部上場銘柄を抽出していますが、省略してTOPIX500銘柄を抽出することも可能です。
なお、TOPIXについては、下記ページに詳しく載っています。
[2-2] 業種ごとの銘柄数を確認
業種ごとの銘柄数を確認すると、電気機器の49銘柄が最多です。
# 33業種コード,33業種区分ごとの銘柄数を算出 category_count = topix500.groupby(['33業種コード','33業種区分']).size() print(category_count) # 33業種コード 33業種区分 # 1050 鉱業 1 # 2050 建設業 20 # 3050 食料品 28 # 3100 繊維製品 5 # 3150 パルプ・紙 3 # 3200 化学 46 # 3250 医薬品 22 # 3300 石油・石炭製品 3 # 3350 ゴム製品 4 # 3400 ガラス・土石製品 8 # 3450 鉄鋼 7 # 3500 非鉄金属 7 # 3550 金属製品 6 # 3600 機械 32 # 3650 電気機器 49 # 3700 輸送用機器 20 # 3750 精密機器 10 # 3800 その他製品 10 # 4050 電気・ガス業 13 # 50 水産・農林業 2 # 5050 陸運業 24 # 5100 海運業 2 # 5150 空運業 2 # 5200 倉庫・運輸関連業 2 # 5250 情報・通信業 32 # 6050 卸売業 23 # 6100 小売業 33 # 7050 銀行業 26 # 7100 証券、商品先物取引業 5 # 7150 保険業 6 # 7200 その他金融業 9 # 8050 不動産業 12 # 9050 サービス業 25 # dtype: int64
[2-3] 各銘柄の株価上昇率を計算
ここでは、2020/11/2〜2021/2/5の株価上昇率を計算します。
[2-3-1] 業種コード・業種区分を抽出
まずは、業種コード・業種区分を抽出します。
# 33業種コード,33業種区分を抽出 industry_category = topix500.groupby(['33業種コード','33業種区分']).groups.keys() print(industry_category) # dict_keys([('1050', '鉱業'), ('2050', '建設業'), ('3050', '食料品'), # ('3100', '繊維製品'), ('3150', 'パルプ・紙'), ('3200', '化学'), # ('3250', '医薬品'), ('3300', '石油・石炭製品'), ('3350', 'ゴム製品'), # ('3400', 'ガラス・土石製品'), ('3450', '鉄鋼'), ('3500', '非鉄金属'), # ('3550', '金属製品'), ('3600', '機械'), ('3650', '電気機器'), # ('3700', '輸送用機器'), ('3750', '精密機器'), ('3800', 'その他製品'), # ('4050', '電気・ガス業'), ('50', '水産・農林業'), ('5050', '陸運業'), # ('5100', '海運業'), ('5150', '空運業'), ('5200', '倉庫・運輸関連業'), # ('5250', '情報・通信業'), ('6050', '卸売業'), ('6100', '小売業'), # ('7050', '銀行業'), ('7100', '証券、商品先物取引業'), ('7150', '保険業'), # ('7200', 'その他金融業'), ('8050', '不動産業'), ('9050', 'サービス業')])
[2-3-2] 業種ごとに株価を取得
抽出した、業種コード・業種区分ごとに株価を取得します。期間は3ヶ月分よりも長く取得したいのですが、Stooqの株価取得には制限があります。具体的な基準は分かりませんが、一定回数か容量を超えると空のDataFrameが返ってくるので、3ヶ月分に留めています。
# 2020/11/2〜2021/2/5の株価を取得する base_date = datetime.datetime(2020, 1, 6) end_date=datetime.datetime(2021, 2, 5) # 業種単位の株価上昇率格納用のDataFrameを用意する category_df = None # 業種ごとに変動率を計算する for category in industry_category: category_code = category[0] # 33業種コード category_class = category[1] # 33業種区分 # 指定した業種の銘柄を抽出 brands = topix500[topix500['33業種区分'] == category_class] # 銘柄コードの末尾に.JPを付加する symbols = [] for code in brands['コード']: symbols.append('{0:d}.JP'.format(code)) # 指定銘柄コードの株価を取得する stock_price = web.DataReader(symbols, 'stooq', start=base_date, end=end_date) print(stock_price) # Attributes Close ... Volume ... # Symbols 1414.JP 1721.JP 1801.JP ... 1883.JP 1893.JP 1911.JP ... # Date ... ... # 2021-02-05 4600.0 3315.0 3545.0 ... 262000 877200 476600 ... # 2021-02-04 4630.0 3245.0 3510.0 ... 304100 864200 547700 ... # ... ... ... ... ... ... ... ... ... # 2020-11-04 5200.0 2789.0 3325.0 ... 85900 1494600 520500 ... # 2020-11-02 5130.0 2678.0 3280.0 ... 140500 647000 409200 ...
TOPIX500銘柄すべての株価を一度に取得し、業種単位に分割することも可能と思われます。しかし、一度に大量のデータは扱いたくないので、業種単位で取得しています。
[2-3-3] 各銘柄の株価上昇率を計算
株価を取得した銘柄ごとに株価の上昇率を計算します。計算結果はディショクナリにいったん格納し、最後にDataFrameに変換します。
# 銘柄ごとに上昇率を計算し、ディショクナリに格納する dict = {} for symbol in symbols: # 基準日付からの上昇率を計算する base_date_str = base_date.strftime('%04Y-%02m-%02d') base_price = stock_price.loc[base_date_str][('Close',symbol)] increase_rate = stock_price[('Close',symbol)] / base_price.iloc[0] # ディクショナリに格納する dict[symbol] = increase_rate # ディクショナリからDataFrame作成 df = pd.DataFrame(dict)
[2-4] 業種ごとの平均値, 標準偏差を算出
業種ごとの株価上昇率について、平均値と標準偏差を計算します。計算結果はDataFrameに追加していきます。
# 業種内銘柄の上昇率の平均値を計算する mean = df.mean(axis='columns') std = df.std(axis='columns') # DataFrameに業種単位の上昇率と、上昇率の標準偏差を格納する if category_df is None: category_df = pd.concat([mean, std], axis=1) category_df.columns = pd.MultiIndex.from_tuples( [(category_class, '上昇率'), (category_class, '標準偏差')]) else: category_df[(category_class, '上昇率')] = mean category_df[(category_class, '標準偏差')] = std print(category_df) # 鉱業 建設業 # 上昇率 標準偏差 上昇率 標準偏差 # Date # 2021-02-05 1.308617 NaN 1.170162 0.130083 # 2021-02-04 1.274549 NaN 1.157672 0.130904 # ... ... ... ... ... # 2020-11-04 1.054108 NaN 1.007299 0.013290 # 2020-11-02 1.000000 NaN 1.000000 0.000000 # # [65 rows x 4 columns]
鉱業はTOPIX500銘柄が「国際石油開発帝石」しかないため、標準偏差が計算できずNaNとなっています。複数銘柄ある場合には標準偏差が計算されます。
[2-5] 折れ線グラフで株価上昇率を可視化
まずは、業種ごとに株価上昇率を折れ線グラフで可視化します。2020年11月〜2021年2月は、バイデン大統領の当選や金融緩和の継続もあり、全体的に株価が上昇していました。
import matplotlib.pyplot as plt import matplotlib.dates as mdates # 上昇率のみを抽出し、業種の数を取得する increase_rate_df = category_df.loc[:, pd.IndexSlice[:, '上昇率']] industry_num = len(increase_rate_df.columns) # rows, colsを決定し、Figureを取得 rows, cols = (6, 6) fig = plt.figure(figsize=(20, 16)) # 上昇率の最大値と最小値を取得 min_y = increase_rate_df.min().min() max_y = increase_rate_df.max().max() # 業種ごとに折れ線グラフを表示する for i in range(industry_num): # Axesを取得 ax = fig.add_subplot(rows, cols, i+1) # 銘柄名 category_name = increase_rate_df.columns[i][0] # 上昇率を取得 increate_rate = increase_rate_df.loc[:, pd.IndexSlice[category_name, '上昇率']] # 折れ線グラフを表示 x = increase_rate_df.index y = increate_rate ax.plot(x, y, label=category_name) # 目盛り線を表示 ax.grid(color='gray', linestyle='--', linewidth=0.5) # X軸の目盛り位置を設定 ax.xaxis.set_major_locator(mdates.MonthLocator()) # Y軸の範囲を設定 ax.set_ylim(min_y, max_y) # グラフのタイトルを追加 ax.set_title(category_name) # 不要な余白を削る plt.tight_layout() # グラフを表示 fig.show()
[2-6] 棒グラフで株価上昇率を可視化
次は上昇率を棒グラフで可視化します。
棒グラフを表示する前に、一度DataFrameを作成し直しています。これは、上昇率でソートするためです。元のDataFrameはMultiIndexになっており、そのままの形ではソートが難しかったためです。
# 最新日付を取得する latest_date = category_df.index.max() latest_date_str = latest_date.strftime('%04Y-%02m-%02d') # 最新日付の上昇率, 標準偏差を抽出する increase_rate = category_df.loc[latest_date_str, pd.IndexSlice[:, '上昇率']] std = category_df.loc[latest_date_str, pd.IndexSlice[:, '標準偏差']] # 業種名をリストで取得 categories= [col[0] for col in increase_rate.columns] # 上昇率と標準偏差を列にもつDataFrameを作成し、上昇率で降順ソートする df = pd.DataFrame({'上昇率': increase_rate.values[0], '標準偏差': std.values[0]}, index=categories) df = df.sort_values('上昇率', ascending=False) print(df) # 上昇率 標準偏差 # 海運業 1.329285 0.148943 # 電気機器 1.313898 0.194788 # 非鉄金属 1.312967 0.141995 # 鉱業 1.308617 NaN # 石油・石炭製品 1.291704 0.1598 # ...(以下略)...
barh
で可視化します。xerr
を使用して、標準偏差をエラーバーとして表示しています。
# 図と座標軸を取得 fig = plt.figure(figsize=(10, 10)) ax = fig.add_subplot(1,1,1) # 棒グラフに表示するデータを準備 x = np.arange(len(categories)) y = df['上昇率'] yerr = df['標準偏差'] label = df.index # 棒グラフ表示 ax.barh(x, y, xerr=yerr, tick_label=label) # 目盛り線を表示 ax.grid(axis='x', color='gray', linestyle='--', linewidth=0.5) # 不要な余白を削る plt.tight_layout() # グラフを表示 fig.show()
[3] TOPIX500銘柄の銘柄ごとの株価上昇率を算出
[3-1] 銘柄ごとの株価上昇率を算出
業種ごとの平均値, 標準偏差を算出したのと同様の方法で行います。pandas-datareaderによる株価取得は業種単位で行い、そののち銘柄ごとの株価上昇率を計算します。
# 33業種コード,33業種区分を抽出 industry_category = topix500.groupby(['33業種コード','33業種区分']).groups.keys() # 銘柄単位の株価上昇率格納用のディクショナリを用意する brand_dict = {} brand_count = 0 # 業種ごとに変動率を計算する for category in industry_category: category_code = category[0] # 33業種コード category_class = category[1] # 33業種区分 # 指定した業種の銘柄を抽出 brands = topix500[topix500['33業種区分'] == category_class] # 銘柄コードの末尾に.JPを付加する symbols = [] for code in brands['コード']: symbols.append('{0:d}.JP'.format(code)) # 指定銘柄コードの株価を取得する stock_price = web.DataReader(symbols, 'stooq', start=base_date) # 銘柄ごとに上昇率を計算し、ディショクナリに格納する dict = {} for symbol in symbols: # 銘柄ごとに上昇率を計算し、ディショクナリに格納する # 基準日付からの上昇率を計算する base_date_str = base_date.strftime('%04Y-%02m-%02d') base_price = stock_price.loc[base_date_str][('Close',symbol)] increase_rate = stock_price[('Close',symbol)] / base_price.iloc[0] # ディクショナリに格納する dict[symbol] = increase_rate # ディクショナリからDataFrame作成 df = pd.DataFrame(dict) # 最新日付を取得する latest_date = stock_price.index.max() latest_date_str = latest_date.strftime('%04Y-%02m-%02d') # 銘柄単位の株価上昇率をディクショナリに格納する for i, symbol in enumerate(symbols): code = brands['コード'].iloc[i] name = brands['銘柄名'].iloc[i] increase_rate = df.loc[latest_date_str, symbol].iloc[0] # '33業種コード','33業種区分', 'コード', '銘柄名', '上昇率']) brand_dict[brand_count] = [category_code, category_class, code, name, increase_rate] brand_count += 1 # 銘柄単位の株価上昇率を格納したDataFrameを作成する brand_df = pd.DataFrame.from_dict( brand_dict, orient="index", columns=['33業種コード','33業種区分', 'コード', '銘柄名', '上昇率'])
業種ごとの株価上昇率は日単位で算出していましたが、銘柄については最新日付の株価上昇率のみとしています。
[3-2] 株価上昇率と上位10, 下位10銘柄を確認
[3-2-1] 上位10銘柄
まずは上位10銘柄を確認します。
# 上昇率で降順ソート df_sorted = brand_df.sort_values('上昇率', ascending=False) # 上位銘柄を抽出 df_top = df_sorted.head(10)
電気機器・輸送用機器銘柄の上昇が目立ちます。
33業種区分 | 銘柄名 | 上昇率 |
---|---|---|
電気機器 | コニカミノルタ | 1.854610 |
電気機器 | シャープ | 1.839968 |
電気機器 | ジーエス・ユアサ コーポレーション | 1.838553 |
輸送用機器 | 川崎重工業 | 1.784906 |
輸送用機器 | マツダ | 1.675393 |
情報・通信業 | コナミホールディングス | 1.661905 |
その他金融業 | 東京センチュリー | 1.648956 |
輸送用機器 | 日産自動車 | 1.634646 |
輸送用機器 | ヤマハ発動機 | 1.589777 |
機械 | IHI | 1.581 |
[3-2-2] 下位10銘柄
次に下位10銘柄を確認します。
# 上昇率で昇順ソート df_sorted = brand_df.sort_values('上昇率', ascending=True) # 下位銘柄を抽出 df_bottom = df_sorted.head(10)
銀行業が多めであり、最大で17%弱の減少です。2020年11月〜2021年2月は、TOPIXが17%近く上昇していることもあってか、極端に大きな下落にはなっていませんでした。
33業種区分 | 銘柄名 | 上昇率 |
---|---|---|
銀行業 | 滋賀銀行 | 0.835329 |
銀行業 | 九州フィナンシャルグループ | 0.857708 |
繊維製品 | ゴールドウイン | 0.859155 |
情報・通信業 | 光通信 | 0.867015 |
サービス業 | ベネッセホールディングス | 0.871878 |
食料品 | 東洋水産 | 0.872659 |
小売業 | ウエルシアホールディングス | 0.874092 |
銀行業 | 山口フィナンシャルグループ | 0.886364 |
食料品 | カゴメ | 0.888743 |
銀行業 | 中国銀行 | 0.894793 |
終わりに
pandas-datareaderでTOPIX500銘柄の株価を取得し、業種ごとの株価上昇率を可視化しました。以前にスクレイピングで株価を取得しましたが、pandas-datareaderの方が簡単に株価を取得できました。
ただ、取得できるデータ数が限られるという利用制限があります。一度取得したデータを保存しておく等の工夫は必要だとは感じました。過去データはpandas-datareaderで取得・保存しておいて、最新の株価はスクレイピングで取得するのがよいのかもしれません。