EDINETのデータを利用して、BS/PLを可視化する

# 概要
## 有価証券報告書について
- 上場企業など、一定の基準を満たした企業は、金融商品取引法（金商法）第二十四条により、有価証券報告書を内閣総理大臣に提出することが義務付けられています。
- 有価証券報告書には、貸借対照表（BS）や損益計算書（PL）などを通して、その会社がどのように経営され、どのように利益を出しているのかといった情報が掲載されています。
- 各会社から提出された有価証券報告書は、[EDINET](https://disclosure2.edinet-fsa.go.jp/)というサイトを通じて、誰でも閲覧することができます。

## この記事について
この記事では、EDINETに掲載されている有価証券報告書のデータを利用し、BS/PLを可視化するための手順について説明します。具体的にはこちらです：
1. EDINETから各会社の有価証券報告書（3年分）をダウンロード
2. ダウンロードしたファイルから、BS/PLの各勘定科目データを切り出し
3. 可視化用のデータを作成

ちなみに、可視化はTableauを利用しています。Tableauの使い方はこの記事の範囲外として省略しますが、興味のある方は、[リンク](https://public.tableau.com/app/profile/koji.yokoyama/viz/EDINet/BS)からTableauワークブックをダウンロードし、中を覗いてみてください。

尚、すべての会社について有価証券報告書をダウンロードするのは、負荷が重くなるため、ここでは対象企業を「銀行業」に限って処理をしています。

## EDINETのAPIについて
EDINETからは、APIを使ってデータをダウンロードすることができます。
APIを使うためには、
- アカウントの作成
- APIキーの発行

が必要になります。
この部分の解説は省略します。こちらの[API仕様書](https://disclosure2dl.edinet-fsa.go.jp/guide/static/disclosure/WZEK0110.html)をみて、各自設定を行ってください。

以下、この記事ではAPIのバージョン2に従って処理を行います。

# EDINETから有価証券報告書をダウンロードする
EDINETから有価証券報告書をダウンロードするためには、以下の2ステップが必要です。
1. 日付を指定して、提出文書のリストをダウンロードする
2. 1.でダウンロードしたリストを基に、各社の「有価証券報告書」をダウンロードする

EDINETでは、1, 2の処理のために、それぞれAPIが用意されています。

## 日付を指定して、提出文書のリストをダウンロードする
EDINETから提出文書をダウンロードするためには、まず文書のリストを取得する必要があります。
リストは日にち指定してダウンロードが可能で、APIは以下の通りです：
```
https://api.edinet-fsa.go.jp/api/v2/documents.json?date=yyyy-mm-dd&type=2&Subscription-Key=xxxxx
```
API内の変数の意味はこちらです：
| 変数 | 内容 |
|:-|:-|
| 「yyyy-mm-dd」 | リストを取得する対象日付 |
| 「xxxxx」 | EDINETのサイトで発行したAPIキー。認証のために使用します | 

2023.4.1データをダウンロードするリクエストは以下のようになります（xxxxxの部分は各自のAPIキーで置き換えてください）
```
https://api.edinet-fsa.go.jp/api/v2/documents.json?date=2023-04-01&type=2&Subscription-Key=xxxxx
```

一定期間に提出されたリストを取得するためには、日にち分だけAPIを実行する必要があります。例えば、2022.4.1～2023.3.31の期間のデータを取得するプログラムは、以下のような実装になります。

<details><summary>get_list.py</summary>

```py
import sys
import datetime
import time
import urllib.request
import urllib.error

this_date = datetime.date(2022, 4, 1)
date_to = datetime.date(2023, 3, 31)

while this_date <= date_to:
	url = f'https://api.edinet-fsa.go.jp/api/v2/documents.json?date={this_date}&type=2&Subscription-Key=xxxxx'

	try:
		with urllib.request.urlopen(url) as res:
			content = res.read().decode('utf-8')
		output_path = f'./list/list_{this_date}.json'
		with open(output_path, 'w') as fp:
			fp.write(content)
	except urllib.error.HTTPError as e:
		if e.code >= 400:
			print(e.reason)
		else:
			raise e
	time.sleep(1)

	this_date += datetime.timedelta(days=1)
```
</details>

取得したファイルは、json形式になっていて、提出文書はresultタグにリストされています。主な項目は以下の通りです。
| ラベル | 意味 |
|:-|:-|
| edinetCode | 提出者の会社コード |
| filerName | 提出者の会社名 |
| docID | 文書ID（⇒ 文書のダウンロード時に必要） |
| docDescription | 提出文書の説明 |

上のプログラムでは、ダウンロードした各ファイルを、「list」フォルダ以下に、list_(yyyy-mm-dd).jsonというファイル名で保存します。
以下、list_(yyyy-mm-dd).jsonが3年分ダウンロード済という前提で話を進めます。

## 提出文書のリストから、「有価証券報告書」をダウンロードする
提出文書は「docID」で特定します。APIは以下の通りです。
```
https://api.edinet-fsa.go.jp/api/v2/documents/docID?type=5&Subscription-Key=xxxxx
```
変数の意味はこちらです：
| 変数 | 意味 |
|:-|:-|
| docID | 文書ID。docIDによって、ダウンロードする文書を特定します |
| xxxxx | EDINETサイトで発行したAPIキー。認証のために使用します |

例えば、docIDが「S111ABCD」の文書のダウンロードは、以下のようなリクエストになります（xxxxxの部分は各自のAPIキーで置き換えてください）
```
https://api.edinet-fsa.go.jp/api/v2/documents/S111ABCD?type=5&Subscription-Key=xxxxx
```

docIDは、上でダウンロードしてきた日別の文書リスト（list_(yyyy-mm-dd).json）にリストされています。
ただし、上で取得したリストには、
- 色々な業種の会社データが含まれている（今回は「銀行業」のみを取得したい）、
- 「有価証券報告書」のほかに、「大量保有報告書」など、色々な種類の文書が含まれている

ことから、目的の文書を選別してダウンロードを行う必要があります。

### 「銀行業」の会社を選別
- EDINETでは、会社を「EDINETコード」（=会社ID）で管理し、すべての会社のリストは[EDINETコードリスト](https://disclosure2.edinet-fsa.go.jp/weee0010.aspx)にあります。
- この記事では、[EDINETコードリスト](https://disclosure2.edinet-fsa.go.jp/weee0010.aspx)の「提出者業種」列が「銀行業」になっている会社を抽出対象としています。
- プログラムでは、「銀行業」のEDINETコードをリスト化したものを、bank_master.txtとして持っておき、bank_master.txt内のEDINETコードと、文書リスト（list_(yyyy-mm-dd).json）内のedinetCodeとマッチングさせることで、「銀行業」の会社データを選別します。

### 「有価証券報告書」の選別
- 文書リスト（list_(yyyy-mm-dd).txt）では、「docDescription」タグに、文書の説明が書いてあります。
- 例えば、「有価証券報告書」・「有価証券届出書（内国投資信託受益証券）」・「大量保有報告書」などが挙げられます。
- プログラムでは、「docDescription」内に
  - 「有価証券報告書」という文字列が含まれ、
  - 「内国投資信託受益証券」という、文字列が含まれない
  という条件でフィルタリングすることにします。

以上を踏まえて実装したのが、以下のプログラムです。ここでは、
- 各日付の文書リスト（list_(yyyy-mm-dd).json）を読み、
- ダウンロード対象の文書を選別し、
- APIを利用して文書をダウンロードする

という手順で、ファイルをダウンロードしています。

<details><summary>get_report.py</summary>

```py
# -*- encoding:utf-8 -*-
import sys
import json
import re
import datetime
import time
import urllib.request
import urllib.error

this_date = datetime.date(2022, 4, 1)
date_to = datetime.date(2023, 3, 31)

bank_master_path = 'bank_master.txt'
edinetCode_dict = {}
with open(bank_master_path, 'r') as f:
	line = f.readline()
	for line in f:
		data = line[:-1].split('\t')
		edinetCode_dict[data[0]] = 1

while this_date <= date_to:
	list_path = f'./list/list_{this_date}.json'
	sys.stderr.write(list_path+'\n')
	with open(list_path, 'r') as fp_in:
		list_data = json.load(fp_in)
		results = list_data['results']
		loop = 0
		for doc in results:
			docID = doc['docID']
			edinetCode = doc['edinetCode']
			docDescription = str(doc['docDescription'])
			m1 = re.search('有価証券報告書', docDescription)
			m2 = re.search('内国信託受益証券', docDescription)

			if edinetCode in edinetCode_dict and m1 is not None and m2 is None:
				url = f'https://api.edinet-fsa.go.jp/api/v2/documents/{docID}?type=5&Subscription-Key=xxxxxxxx'
				print(this_date, loop, doc['edinetCode'], doc['JCN'], doc['issuerEdinetCode'], doc['docID'], doc['filerName'], doc['docDescription'], sep='\t')

				try:
					with urllib.request.urlopen(url) as res:
						content = res.read()
					output_path = f'./docs/{docID}.zip'
					with open(output_path, 'wb') as fp_out:
						fp_out.write(content)
				except urllib.error.HTTPError as e:
					if e.code >= 400:
						sys.stderr.write(e.reason+'\n')
					else:
						raise e

				loop += 1
				time.sleep(1)
	this_date += datetime.timedelta(days=1)
```
</details>

上のプログラムでは、ダウンロードしたファイルはを、カレントディレクトリ以下のdocsディレクトリに、(docID).zipというファイル名で保存します。

ダウンロードしたデータファイル（(docID).zip）について：
- 殆どの銀行では、会計年度は4月～翌年3月となっており、有価証券報告書は翌年6月に提出しているところが多いようです。
- プログラムでは、2022.4.1～2023.3.31までに提出された有価証券報告書から、2021年度の決算資料を取得しています（その他の年度データも同様にしてダウンロードできる）。
- また、有価証券報告書については提出後に訂正が入るケースがあるため、データダウンロード後に、後処理（一番新しいものを一つ選ぶ）が必要になる場合があることに注意してください。


# ダウンロードしたファイルから、BS/PLの各勘定科目データを切り出す
ダウンロードしたファイル（csv）を開くと、以下のようなレイアウトになっています：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193817/9ccce199-b504-63b8-1878-31d52f3a813e.png)

ファイルには、BS/PL以外のデータや、前期などの過去データが含まれているため、目的のものだけ抽出する必要があります。
ここでは、要素ID・コンテキストID・相対年度を使って絞り込みを行います。

## 要素IDによる絞り込み
上で説明した通り、取得した有価証券報告書データには、勘定科目別の財務情報以外に様々な情報が含まれており、BS/PLを可視化するためには、目的にあったものに絞る必要があります。

ここでは、「要素ID」が勘定科目コードにあたるものです。
例えば、「総資産額」の要素IDは「jpcrp_cor:TotalAssetsSummaryOfBusinessResults」です。

ファイルから必要な勘定科目の情報を抽出するためには、予め要素IDのリストをあらかじめ用意しておく必要があります。
勘定科目の情報は、[タクソノミのページ](https://www.fsa.go.jp/search/20221108.html)のページの「勘定科目リスト」というエクセルファイルから取得できるので、これを元に銀行業のBS/PLに現れる要素IDのリストを作っておきます（[elementID_master.txt](Pending)）。

また、可視化にあたっては、勘定科目間の親子関係についての情報（例えば、「現金預け金」は「総資産額」の下にくっついている）が必要になりますが、データには親子関係の情報がないため、この情報も[elementID_master.txt](Pending)に持たせておきます。

【elementID_master.txtのサンプル】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193817/8b94a935-8dd4-cea9-6cc9-42fa0953d653.png)

## 相対年度による絞り込み
データには前期・前々期等、過去の年度のデータが含まれるため、ここでは「時点」が「当期」で始まるデータのみ集計対象とします。

## コンテキストIDによる絞り込み
コンテキストIDは、連結・単独の別等の情報が含まれています。ここでは、以下のコンテキストIDのみ抽出対象とします：
- CurrentYearDuration
- CurrentYearDuration_NonConsolidatedMember
- CurrentYearInstant
- CurrentYearInstant_NonConsolidatedMember 

以上を反映させ、有価証券報告書データを一つのファイルにまとめるプログラムが、以下のmerge_csv.pyです。

<details><summary>merge_csv.py</summary>

```py
# -*- encoding:utf-8 -*-

import sys
import zipfile
import csv
import json
import re
import os
import datetime
import time

contextID_list = ['CurrentYearDuration','CurrentYearDuration_NonConsolidatedMember','CurrentYearInstant','CurrentYearInstant_NonConsolidatedMember'] 

elementID_master_path = './master/elementID_master.txt'
elementID_dict = {}
with open(elementID_master_path, 'r') as f:
	line = f.readline()
	for line in f:
		data = line[:-1].split('\t')
		elementID_dict[data[0]] = 1
		
loop = 0
with open('report_list_2020.txt', 'r') as f_list:
	header = f_list.readline()
	for line in f_list:
		data1 = line[:-1].split('\t')
		zip_path = os.path.join('./docs', data1[5] + '.zip')
		try:
			with zipfile.ZipFile(zip_path) as fz:
				#print(fz.filename)
				for f in fz.namelist():
					m = re.search('asr.*\.csv', f)
					if m is not None:
						#print(f)
						fz.extract(f)
						with open(f, 'r', encoding='utf-16') as f_rep:
							reader = csv.reader(f_rep, delimiter='\t')
							for data2 in reader:
								if loop == 0:
									print('edinetCode', 'docID', '\t'.join(data2), sep='\t')
								elif data2[0] in elementID_dict and data2[3][:2] == '当期' and data2[2] in contextID_list:
									#print(data)
									print(data1[2], data1[5], '\t'.join(data2), sep='\t')
								loop += 1
		except:
			sys.stderr.write('Error: ' + data1[5]+'\n')
```

</details>

出力結果をエクセル等で開くと、以下のようになります：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193817/be1986d5-7f1a-fc53-3247-5cb9afbcae6d.png)

# 可視化用のデータを作成
上で作った勘定科目別データに、ディメンジョンデータ（銀行情報（銀行名など）・勘定科目情報（勘定科目の親子関係等））を紐づれば、可視化に必要なデータが揃います。紐づけはTableauで行います。

上で作成した勘定科目別データから、BS/PL分だけとり、これとディメンジョンデータを以下の結合キーで結合します：
| ディメンジョンデータ | ファイル名 | 結合キー |
|:-|:-|:-|
| 銀行情報 | bank_master.txt | Edinet_Code |
| 勘定科目情報 | elementID_master.txt | 要素ID |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193817/7b1879a4-7b3c-5d20-193b-e5dc52bd2bf6.png)

PLについても、同様です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193817/a7670ea1-e9e9-b042-4ebc-0ccba878df23.png)

【elementID_master.txt】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193817/8b94a935-8dd4-cea9-6cc9-42fa0953d653.png)

【bank_master.txt】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193817/ab4be586-4952-4647-94dc-9ad70ba37ed5.png)


（Tableauを使った具体的な可視化の作業は、この記事の範囲外なので省略します）。

# 可視化結果を眺めてみる
最後に、簡単な分析例として、ここで作った可視化結果を眺めてみます。
銀行は預金者からお金を集めて、会社などのお金を貸し、会社から利息を得ることで利益を得ています。
例えば、各銀行について、集めたお金（総資産）の何割を貸出にまわし、何割を現金のまま持っているか、銀行別にみてみます。

【銀行別 総資産に占める現金の割合】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193817/64a612d5-8d6b-af6a-2491-e44e825dffd8.png)
ここでみると、セブン銀行が圧倒的に現金の割合が際立って大きくなっていることがわかります。
現金の比率が大きいということは、それだけ貸し出しにまわす額が小さくなり、収益が出にくくなります。
それではセブン銀行はどうやって儲けを出しているのか？ということで、次はセブン銀行のPLをみてみます。

【セブン銀行のPL】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193817/8432e54b-7da4-0306-fb27-8906046c478d.png)
これをみると、役務取引等収益の割合が経常収益の殆どを占めていることがわかります。

銀行間で比較しても、セブン銀行の役務取引等収益の割合がずば抜けて高くなっていることがわかります。
【銀行別 経常収益に占める役務取引等収益の割合】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193817/9455a655-5230-cda8-ffda-d47cfb0f2417.png)

なお、「役務取引等収益」というのは、ATMでの現金の引出や・口座振替・為替等による手数料収入のことです。
セブン銀行は、全国に広がるコンビニネットワークを利用して、ATMサービスを展開しています。そして、このATMサービスによる手数料が銀行としての収益のほとんどを占めていることを、読み取ることができました。

こんな感じで、各社の財務指標をみるだけでも、その会社がどんなビジネスモデルを基に収益を挙げているのか等、色々な知識を得ることができます。興味がある方は、他業種等についてもデータを取得し、分析をしてみてください。