K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

EDGARのデータを取得

01.6.2

EDGARのデータを取得

最終更新 2020-02-12 読了時間 7 分
まとめ
EDGAR(Electronic Data Gathering, Analysis, and Retrieval system)は米国証券取引委員会(SEC)が運営する法定開示書類のデータベースで、米国上場企業の年次報告書 10-K に含まれる財務諸表もここから取得できます。このページでは sec_edgar_downloader で Microsoft(ティッカー MSFT)の 2021 年の 10-K をダウンロードし、pandas.read_html で HTML 内のすべての表を DataFrame にしてから、“Cash and cash equivalents, end of period” という文言を含む表だけをキャッシュフロー計算書として選びます。そのあと replace(regex=True) で “(”, “)”, “,” を消し、NaN が 8 割を超える列を落として、Net income など 10 項目の 2019〜2021 年の推移を折れ線で描きます。読み終えると、HTML から取った表が「文字列の表」であること、数値に使える形まで掃除する手順がなぜ必要かを説明できるようになります。

直感 #

企業の財務データは、有料のデータベースを使わなくても SEC の EDGAR から無料で手に入ります。ただし 10-K は人が読むための HTML なので、そのままでは「表」として扱えません。pd.read_html は HTML 内の <table> をすべて DataFrame のリストとして返してくれますが、1 つの 10-K には数十〜数百の表が含まれ、セルには “$” や “(” のような記号と大量の空セルが混ざっています。このページの流れは「取得(Downloader)→ 表の一括抽出(read_html)→ 目的の表の選択(文言で検索)→ 掃除(記号・NaN の除去)→ 数値化してプロット」の 5 段階で、後半の掃除の部分がコードの大半を占めます。

10-K をダウンロードする #

最初のブロックは、Microsoft の 2021 年の 10-K を ./data/ 以下にダウンロードします。既にダウンロード済みなら通信せずに済むように分岐しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import os
from sec_edgar_downloader import Downloader

dl = Downloader("./data/")
ticker_symbol = "MSFT"

if os.path.exists(f"./data/sec-edgar-filings/{ticker_symbol}/10-K/"):
    print("ダウンロード済みです。")
elif dl.get("10-K", ticker_symbol, after="2021-01-01", before="2021-12-31") > 0:
    print("ダウンロードに成功しました。")
else:
    print("ダウンロードに失敗しました。")
ダウンロード済みです。

10-K に含まれる表を抜き出す #

次のブロックは、ダウンロードした HTML(filing-details.html)を pandas.read_html に渡し、ページ内のすべての表を DataFrame のリストとして受け取ります。

1
2
3
4
5
6
7
import glob
import pandas as pd

filing_details_filepath = glob.glob(
    f"./data/sec-edgar-filings/{ticker_symbol}/10-K/*/filing-details.html"
)[0]
tables = pd.read_html(filing_details_filepath)

CASH FLOWS STATEMENTS のテーブルを抽出 #

数十個の表の中からキャッシュフロー計算書(CASH FLOWS STATEMENTS)を選びます。表番号で指定する方法もありますが、ここではキャッシュフロー計算書の最終行に必ず現れる「Cash and cash equivalents, end of period」という文言を含む表を探す方法にしています。

1
2
3
4
5
6
7
cs_table = None  # CASH FLOWS STATEMENTSのページのテーブル
for table in tables:
    tab_html = table.to_html()
    if "Cash and cash equivalents, end of period" in tab_html:
        cs_table = table

cs_table.head(10)

0123456789101112
0(In millions)NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
1NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
3Year Ended June 30,NaN20212021NaNNaN20202020NaNNaN20192019NaN
4NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
5OperationsNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
6Net incomeNaN$61271NaNNaN$44281NaNNaN$39240NaN
7Adjustments to reconcile net income to net cas...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
8Depreciation, amortization, and otherNaNNaN11686NaNNaNNaN12796NaNNaNNaN11682NaN
9Stock-based compensation expenseNaNNaN6118NaNNaNNaN5289NaNNaNNaN4652NaN

データの前処理 #

上の出力を見ると、13 列のうち金額が入っているのは 3 列だけで、残りは NaN か “$” だけの列です。しかも “61271” は文字列で、"(In millions)" のように括弧やカンマも混ざっています。次の 3 ブロックで、記号の削除 → NaN の多い列の削除 → 列名の付け替えの順に掃除して、数値として読める 4 列の表にします。

1
2
3
4
5
6
import numpy as np

# 「(」「)」「,」の文字列を削除する
cs_table = cs_table.replace([",", "\\)", "\\("], "", regex=True)
cs_table = cs_table.replace([""], np.nan)  # 空白セルはnan扱いにする
cs_table.head(10)

0123456789101112
0In millionsNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
1NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
3Year Ended June 30NaN20212021NaNNaN20202020NaNNaN20192019NaN
4NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
5OperationsNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
6Net incomeNaN$61271NaNNaN$44281NaNNaN$39240NaN
7Adjustments to reconcile net income to net cas...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
8Depreciation amortization and otherNaNNaN11686NaNNaNNaN12796NaNNaNNaN11682NaN
9Stock-based compensation expenseNaNNaN6118NaNNaNNaN5289NaNNaNNaN4652NaN
1
2
3
4
5
6
7
# データがない列を削除する
row_num = cs_table.shape[0]

for colname in cs_table.columns:
    if cs_table[colname].isna().sum() > row_num * 0.8:  # 8割以上の行がNaNの列は
        cs_table.drop(colname, inplace=True, axis=1)  # 不要な列として削除する
cs_table.head(10)

03711
0In millionsNaNNaNNaN
1NaNNaNNaNNaN
2NaNNaNNaNNaN
3Year Ended June 30202120202019
4NaNNaNNaNNaN
5OperationsNaNNaNNaN
6Net income612714428139240
7Adjustments to reconcile net income to net cas...NaNNaNNaN
8Depreciation amortization and other116861279611682
9Stock-based compensation expense611852894652
1
2
3
4
5
6
# NaNの多い行も削除する
col_num = cs_table.shape[1]
cs_table[cs_table.isna().sum(axis=1) < col_num * 0.8]
cs_table.columns = ["item", "2021", "2020", "2019"]
cs_table = cs_table[["item", "2019", "2020", "2021"]]
# years = cs_table.fillna('').query("item.str.contains('Year Ended')").iloc[0, 1:]

データをプロットする #

最後のブロックは、掃除した表の先頭 10 項目について、2019〜2021 年の値を float に変換して 1 項目 1 枚の折れ線グラフにします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import matplotlib.pyplot as plt

years = ["2019", "2020", "2021"]

for item_name in cs_table["item"][:10]:
    try:
        data = [float(v) for v in cs_table.query(f"item=='{item_name}'").iloc[0, 1:]]
        plt.plot(data)
        plt.xticks([i for i in range(len(years))], years)
        plt.title(item_name)
        plt.grid()
        plt.show()
    except IndexError:
        pass

データをプロットする figure

データをプロットする figure

データをプロットする figure

データをプロットする figure

データをプロットする figure

データをプロットする figure

データをプロットする figure

実行結果の読み方 #

Net income の図は 39,240 → 44,281 → 61,271(百万ドル)と右上がりで、Microsoft の純利益が 3 年で 1.5 倍以上になったことが読めます。Depreciation, amortization, and other は 11,682 → 12,796 → 11,686 とほぼ横ばい、Stock-based compensation expense は 4,652 → 5,289 → 6,118 と毎年増えています。項目名が NaN の行は IndexError で飛ばされるので、図の枚数は 10 枚より少なくなります。

使いどころと落とし穴 #

財務諸表の “(1,234)” は負の値の表記なので、このページのように括弧を単に消すと符号が失われます。本番で使うなら str.replace(r"\((.*)\)", r"-\1", regex=True) で先にマイナスに変換してください。表の選択を文言で行っているため、“Cash and cash equivalents, end of period” の表記が違う企業(“end of year” など)では cs_table が None のまま head を呼んで AttributeError になります。列名の ["item", "2021", "2020", "2019"] も Microsoft の 2021 年の 10-K に合わせた手書きなので、他社や他年度では head(10) の出力を見て付け直します。EDGAR はアクセス頻度の制限があり、sec_edgar_downloader の新しい版では Downloader に会社名とメールアドレスの指定が必須になっているので、ライブラリの版に合わせて最初のブロックを読み替えてください。