😊
第二回:有価証券報告書を解析する機能を作ってみた
2024-12-08
2024-12-09
約6340字
この記事は、Python Advent Calendar 2024 シリーズ3の10日目の記事となります。
みなさま、ご無沙汰してます。
気づいたら、第一回の記事から半年くらい経ってました・・。
サービス自体の開発はゆっくり進めてましたが、全然記事が書けていなかったので、
現状の進捗状況も含めつつ、一つ作った機能を紹介できればと思います。
※前回の記事で気分次第で作る機能変わるかも、と記載しておりましたが、思いっきり変わってます・・。LLMを使うという点は変わっていません。
現状ですが、GCPクラウド上で下記のようなサービスを構築しています。
クライアントはブラウザ(Web UI)となっております。
インフラとしては、下記なようにバージョンアップされています。
※ステップ3となっているのは、ステップ1,2が実はこの前段階であったからなのですが、それは一旦割愛します。
認証周りについては、ちょっと自動的に構築するためのコード化が不十分なので、また第3回で詳細を触れればと思っていますが、アカウント管理をIdentity Platformを利用し、ユーザー認証後のトークン認証をIdentity Aware Proxyを使っております。IaPを利用することで、非認証のリクエストを弾くことが可能となります。(アクセス制御の実施)
※モバイルアプリなどのWeb Front以外のアクセスも今後考えているため、LB経由でIaPでアクセス制御を実施しています。
今回主題となるのは、「有価証券報告書の分析機能」となります。
ちなみに今作っているサービスの実装コードは下記のリポジトリにまとまっています。
※ドキュメント作成やIaCが間に合っていないので、作成中。
有価証券報告書は、端的にいうと、株式を発行する上場企業が定期的に開示する情報となります。情報の中には、企業の状況・事業の状況・財務三表などが含まれます。
企業への株式投資などの判断を行う際に、上記有価証券報告書を分析したりして、判断を行うケースがあります。ただし、慣れていないと、報告資料の量(100ページ以上超えるのは割と多い印象)も多いため、分析が大変です。
そこで、LLMを使って、有価証券報告書を解読してもらい、企業分析をしてもらうような機能があると良いな、と思ったのが、分析機能を作るきっかけとなります。
※geminiやGPTを使って、インターネットにアクセスさせての分析も可能ですが、その場合、ハルシネーションリスク(= 分析結果が明らかな嘘)も高まるため、企業の決算情報が含まれる有価証券報告書を入力とした分析を行いたいと考えました。
有価証券報告書の分析機能は以下のような処理フローとなります。
1に関しては、ログイン認証もありますが、詳細は今回は省略します。
有価証券報告書は、各企業のHPに掲載されていたりしますが、実は金融庁で提供しているEDINETのサービスを使うことで、Webブラウザ経由・API経由で取得が可能です。(参考:https://www.fsa.go.jp/search/20130917.html)
API仕様書は下記のWebサイト経由でドキュメントとして見ることが出来ます。
※利用規約についても、上記サイト経由で閲覧可能です。(無料利用可能、商用利用も可能そうかな、多分)
無料でアカウント登録ができ、APIキーを発行して、RESTfulなAPIにアクセスすることで、有価証券報告書を取得することが出来ます。
アカウント登録やAPIキー発行については、下記の記事が参考になります。
EDINETではざっくり以下のようなAPIが提供されています。
※2024/12/08時点
上記を見ると分かるのですが、ドキュメントをキーワード情報から一覧取得するためのAPIがありません。
日付をキーとしての一覧取得はできますが、キーワード(ex. 会社名など)を使った検索には非対応となります。
自分で作るWebサービスについて、ユーザー目線だと、キーワード検索で有価証券報告書が一覧化できないと、使い勝手が悪いと思ったので、キーワード検索ができるように対応することにしました。以下のような処理を実施しています。
pythonを用いたEDINETの書類一覧APIの利用コードは下記となります。
※DataFrameに一覧情報を格納しています。
class EdinetWrapper:
def __init__(self, api_key: str, output_folder: str = None) -> None:
self.__api_key = api_key
self.__output_folder = (
os.path.join(os.path.dirname(__file__), "output", datetime.now().strftime("%Y%m%d%H%M%S"))
if output_folder is None
else output_folder
)
os.makedirs(self.__output_folder, exist_ok=True)
def get_documents_info_dataframe(self, target_date: datetime) -> pd.DataFrame:
url = "https://disclosure.edinet-fsa.go.jp/api/v2/documents.json"
params = {
"date": target_date.strftime("%Y-%m-%d"),
"type": 2, # 2は有価証券報告書などの決算書類
"Subscription-Key": self.__api_key,
}
response = requests.get(url, params=params)
if response.status_code != 200:
raise Exception(f"failed to get document list! http status code is {response.status_code}")
json_data = response.json()
status_code = int(json_data["metadata"]["status"])
if status_code != 200:
raise Exception(f"failed to get document list! status code is {status_code}")
documents = json_data["results"]
df = pd.DataFrame(documents)
return df
また、同様にEDINETの書類取得APIの利用コードは下記となります。
有価証券報告書のファイルフォーマットをpdfに指定して、ダウンロードしています。gcsへの保存処理は別実装となっています。
class EdinetWrapper:
def __init__(self, api_key: str, output_folder: str = None) -> None:
self.__api_key = api_key
self.__output_folder = (
os.path.join(os.path.dirname(__file__), "output", datetime.now().strftime("%Y%m%d%H%M%S"))
if output_folder is None
else output_folder
)
os.makedirs(self.__output_folder, exist_ok=True)
def get_document_url(self, doc_id: str) -> str:
return f"https://api.edinet-fsa.go.jp/api/v2/documents/{doc_id}"
def download_pdf_of_financial_report(self, doc_id: str) -> str:
url = self.get_document_url(doc_id=doc_id)
params = {"type": 2, "Subscription-Key": self.__api_key} # PDFを取得する場合は2を指定
try:
res = requests.get(url, params=params, verify=False)
output_path = os.path.join(self.__output_folder, f"{doc_id}.pdf")
if res.status_code != 200:
raise Exception(f"fail to download {doc_id} document. status code is {res.status_code}")
with open(output_path, "wb") as file_out:
file_out.write(res.content)
return output_path
except urllib.error.HTTPError as e:
if e.code >= 400:
sys.stderr.write(e.reason + "\n")
else:
raise e
EDINET関連の実装コードは下記でまとめています。
書類(有価証券報告書)がgcsに保存できたら、保存したgcs uriを使って、LLM(gemini v1.5 flash)に分析をさせます。
サンプルコードについては、下記のリポジトリのコード(controller -> agent)となります。
今回は、有価証券報告書をLLMを使って分析するための機能について、EDINETを使って検索機能を実装した内容を含めて、記事に記載しました。
ログの保存周り、認証周りについても、次の記事で詳細触れられればと思います。
※何か不明点などあれば、コメントなどでいただけると幸いです。
©︎ 2024 - Yard