PySimpleGUIを使って上場企業の年度毎の売上を可視化してみた

勉強

お金が欲しいなと思って、「バフェットの財務諸表を読む力」を読んでみました。
永続的な競争優位性を持つ企業に投資したらいいらしい!

売り上げがずっと上昇してる企業に投資するのがとりあえずいいらいいので、上場企業の売り上げを視覚的に全部確認してみようと思います!
ただ、調べてみると売り上げを企業一つ一つ確認していくのは非常に骨の折れる作業ですごく大変で…

そこで見つけたのがこちらのサイト
こちらのサイトでは、上場企業の決算データをcsvファイルとしてまとめたものをアップロードしてくれています!
最初は金融庁のEDINETや証券取引所のTDNETからいろんな情報を取得しようと思ったのですが、非常にめんどくさそうで諦め。
ということで、PythonとPythonのライブラリの一つのPySimpleGUIを使ってサクッとGUIを作ってみました。

では、早速作成したGUIをご紹介。

最終的に作成したGUI

今回作成したのは上記のようなGUIです。
見たい上場企業のコード番号を決めたら、株価、企業名をそれぞれ、yahoo_finance_api2と日本取引所グループ様のHPから取得して右上に表示します。

その後、Plotボタンを押すか、Auto Plotにチェックが入っていれば、IR BANK様から取得したデータをもとに、各種データをプロットします。

あとは、スピンボックスの上ボタンをポチポチしていくだけで視覚的に各種企業の年次売り上げデータが確認できます!
残りは勉強した知識をもとに、売上高が伸びていて、高ROE、低PER、低PBRの株を適当に購入していくだけでお金持ちに…なんて単純じゃないと思いますが、とりあえず個人的には作って満足。

PySimpleGUIでGUIウィンドウの作成

まず、以下のウィンドウを作成しました。

以下のコードでこちらは作成できます。

import PySimpleGUI as sg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt

"""
matplotlibで作成したfigureをcanvasに表示する関数
"""
def draw_figure(canvas, figure):
    figure_canvas = FigureCanvasTkAgg(figure, canvas)
    figure_canvas.draw()
    figure_canvas.get_tk_widget().pack(side='top', fill='both', expand=1)
    return figure_canvas

"""
GUIのデザイン部分の作成
"""
sg.change_look_and_feel('DarkAmber')

Upper =[sg.Button('Plot', key='DataPlot', enable_events=True),
        sg.Text('コード'),
        sg.Spin(values=[i for i in range(100)], initial_value=0, size=(8,1), key='StockCodeSpin', enable_events=True),
        sg.Slider(range=(0,100), orientation='h',size=(60,10), change_submits=True, key='StockCodeSlider',enable_events=True),
        sg.Text('株価'),
        sg.Input(0, size=(8,1), key='StockPrice', enable_events=True),
        sg.Text('企業名'),
        sg.Input('Japan', size=(15,1), key='CompanyName', enable_events=True),
        sg.Checkbox('Auto Plot', key='AutoPlot', enable_events=True, default=True)
        ]

layout = [[Upper],[sg.Canvas(size=(500, 500), key='DataPlotter')]]
window = sg.Window('Buffett', layout, resizable=True, finalize=True)

"""
プロットの作成
"""
fig = plt.figure(figsize=(7, 7))
ax_1 = fig.add_subplot(2, 2, 1) # 1行3列の1個目
ax_2 = fig.add_subplot(2, 2, 2) # 1行3列の2個目
ax_3 = fig.add_subplot(2, 2, 3) # 1行3列の3個目
ax_4 = fig.add_subplot(2, 2, 4) # 1行3列の2個目
ax_1.set_title('売上高', fontname="MS Gothic")
ax_2.set_title('営業利益率/純利益率', fontname="MS Gothic")
ax_3.set_title('ROE/ROA', fontname="MS Gothic")
ax_4.set_title('PER/PBR', fontname="MS Gothic")

# figとCanvasを関連付ける
fig_agg = draw_figure(window['DataPlotter'].TKCanvas, fig)

"""
GUIの実行部分
"""
Code = 0
#GUI表示実行部分
while True:
    # ウィンドウ表示
    event, values = window.read()

    #クローズボタンの処理
    if event is None:
        print('exit')
        break

    CodeSpin = int(values['StockCodeSpin'])
    CodeSlider = int(values['StockCodeSlider'])
    if CodeSpin != Code:
        Code_ = CodeSpin 
    elif CodeSlider != Code:
        Code_ = CodeSlider

    if Code != Code_:
        window['StockCodeSpin'].update(Code_)
        window['StockCodeSlider'].update(Code_)
        Code = Code_

    fig_agg.draw()

window.close()

PySimpleGUIのスライダーとスピンを同期させる処理がちょっと大変ですが、それ以外は問題なくできるかと。matplotlibのグラフ表示はこちらのサイトを参考にさせていただきました。

株価と企業名の取得

IR BANKのcsvだけでは株価がわからなくて少し物足りなかったので、yahoo-finance-api2を使った株価の自動取得を次に実装しました。こちらを参考にします。

さらに、コードから企業名も出力したいので、こちらを参考に銘柄コード一覧を取得し、企業名とコードの対応付けも行います!

これらを実装した結果、こんな感じに。

import PySimpleGUI as sg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
from yahoo_finance_api2 import share
from yahoo_finance_api2.exceptions import YahooFinanceError
import requests
import pandas as pd

"""
銘柄コードの情報を取得
"""
url = "https://www.jpx.co.jp/markets/statistics-equities/misc/tvdivq0000001vg2-att/data_j.xls"
r = requests.get(url)
with open('data_j.xls', 'wb') as output:
    output.write(r.content)
stocklist = pd.read_excel("./data_j.xls")
stocklist.loc[stocklist["市場・商品区分"]=="市場第一部(内国株)",
              ["コード","銘柄名","33業種コード","33業種区分","規模コード","規模区分"]]
stocklist = stocklist.values.tolist()

"""
銘柄コードから企業名を取得
"""
def GetCompanyName(code):
    ret = 'No Name'
    for i in stocklist:
        if int(code) in i:
            ret = i[2]
    return ret

"""
現在の株価を取得
"""
def GetStockPrice(code):
    Share_path = code+'.T'
    my_share = share.Share(Share_path)
    symbol_data = None
    try:
        symbol_data = my_share.get_historical(
            share.PERIOD_TYPE_DAY, 3,
            share.FREQUENCY_TYPE_DAY, 1)
        print(symbol_data)
        stock_price=symbol_data['close'][0] if symbol_data!=None else 0
    except YahooFinanceError as e:
        print(e.message)
        stock_price = 0
    return stock_price

"""
matplotlibで作成したfigureをcanvasに表示する関数
"""
def draw_figure(canvas, figure):
    figure_canvas = FigureCanvasTkAgg(figure, canvas)
    figure_canvas.draw()
    figure_canvas.get_tk_widget().pack(side='top', fill='both', expand=1)
    return figure_canvas

"""
GUIのデザイン部分の作成
"""
sg.change_look_and_feel('DarkAmber')

Upper =[sg.Button('Plot', key='DataPlot', enable_events=True),
        sg.Text('コード'),
        sg.Spin(values=[i for i in range(10000)], initial_value=0, size=(8,1), key='StockCodeSpin', enable_events=True),
        sg.Slider(range=(0,10000), orientation='h',size=(60,10), change_submits=True, key='StockCodeSlider',enable_events=True),
        sg.Text('株価'),
        sg.Input(0, size=(8,1), key='StockPrice', enable_events=True),
        sg.Text('企業名'),
        sg.Input('Japan', size=(15,1), key='CompanyName', enable_events=True),
        sg.Checkbox('Auto Plot', key='AutoPlot', enable_events=True, default=True)
        ]

layout = [[Upper],[sg.Canvas(size=(500, 500), key='DataPlotter')]]
window = sg.Window('Buffett', layout, resizable=True, finalize=True)

"""
プロットの作成
"""
fig = plt.figure(figsize=(7, 7))
ax_1 = fig.add_subplot(2, 2, 1) # 1行3列の1個目
ax_2 = fig.add_subplot(2, 2, 2) # 1行3列の2個目
ax_3 = fig.add_subplot(2, 2, 3) # 1行3列の3個目
ax_4 = fig.add_subplot(2, 2, 4) # 1行3列の2個目
ax_1.set_title('売上高', fontname="MS Gothic")
ax_2.set_title('営業利益率/純利益率', fontname="MS Gothic")
ax_3.set_title('ROE/ROA', fontname="MS Gothic")
ax_4.set_title('PER/PBR', fontname="MS Gothic")

# figとCanvasを関連付ける
fig_agg = draw_figure(window['DataPlotter'].TKCanvas, fig)


"""
GUIの実行部分
"""
Code = 0
#GUI表示実行部分
while True:
    # ウィンドウ表示
    event, values = window.read()

    #クローズボタンの処理
    if event is None:
        print('exit')
        break

    CodeSpin = int(values['StockCodeSpin'])
    CodeSlider = int(values['StockCodeSlider'])
    if CodeSpin != Code:
        Code_ = CodeSpin 
    elif CodeSlider != Code:
        Code_ = CodeSlider

    if Code != Code_:
        window['StockCodeSpin'].update(Code_)
        window['StockCodeSlider'].update(Code_)
        window['StockPrice'].update(GetStockPrice(str(Code_)))
        window['CompanyName'].update(GetCompanyName(Code_))
        Code = Code_

    fig_agg.draw()

window.close()

正直に言うと、pandasとかよくわかっていないんですよね。ちょっと見苦しいコードになっているかと思いますが、許して下さい。

いろんなプロットを追加

あとは、IR BANK様のサイトから取得したcsvファイルから各年度の売上、営業利益率、ROE/ROA、PER/PBRなんかをプロットしていきます。

それらを実装したのがこちら!

import PySimpleGUI as sg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
from yahoo_finance_api2 import share
from yahoo_finance_api2.exceptions import YahooFinanceError
import requests
import pandas as pd
import csv

"""
銘柄コードの情報を取得
"""
url = "https://www.jpx.co.jp/markets/statistics-equities/misc/tvdivq0000001vg2-att/data_j.xls"
r = requests.get(url)
with open('data_j.xls', 'wb') as output:
    output.write(r.content)
stocklist = pd.read_excel("./data_j.xls")
stocklist.loc[stocklist["市場・商品区分"]=="市場第一部(内国株)",
              ["コード","銘柄名","33業種コード","33業種区分","規模コード","規模区分"]]
stocklist = stocklist.values.tolist()

"""
銘柄コードから企業名を取得
"""
def GetCompanyName(code):
    ret = 'No Name'
    for i in stocklist:
        if int(code) in i:
            ret = i[2]
    return ret

"""
現在の株価を取得
"""
def GetStockPrice(code):
    Share_path = code+'.T'
    my_share = share.Share(Share_path)
    symbol_data = None
    try:
        symbol_data = my_share.get_historical(
            share.PERIOD_TYPE_DAY, 3,
            share.FREQUENCY_TYPE_DAY, 1)
        print(symbol_data)
        stock_price=symbol_data['close'][0] if symbol_data!=None else 0
    except YahooFinanceError as e:
        print(e.message)
        stock_price = 0
    return stock_price

"""
IR BANK様のサイトからダウンロードしたcsvファイルを年度ごとのフォルダに分けて保存し、データを取得
"""
years = ['2013', '2014', '2015', '2016', '2017', '2018', '2019', '2020', '2021']
data = []

for i in years:
    filepath=i+'/fy-profit-and-loss.csv'
    with open(filepath, encoding="utf-8") as f:
        reader = csv.reader(f)
        l = [row for row in reader]
        data.append(l)

# コード一覧の取得
Codes=[data[len(years)-1][i+2][0] for i in range(len(data[len(years)-1][2:]))]
Code=Codes[0]

"""
codeの情報を以下の情報の辞書型に分けて取得
[売上高] [営業利益] [経常利益] [純利益] [EPS] [ROE] [ROA]
"""
def GetData(code):
    Sales={}
    Operating_income={}
    Ordinary_profit={}
    Net_income={}
    EPS={}
    ROE={}
    ROA={}
    for i in range(len(years)):
        for j in range(len(data[i])):
            if code in data[i][j]:
                Sales[years[i]]=float(data[i][j][2]) if data[i][j][2]!='-' else 0
                Operating_income[years[i]]=float(data[i][j][3]) if data[i][j][3]!='-' else 0
                Ordinary_profit[years[i]]=float(data[i][j][4]) if data[i][j][4]!='-' else 0
                Net_income[years[i]]=float(data[i][j][5]) if data[i][j][5]!='-' else 0
                EPS[years[i]]=float(data[i][j][6]) if data[i][j][6]!='-' else 0
                ROE[years[i]]=float(data[i][j][7]) if data[i][j][7]!='-' else 0
                ROA[years[i]]=float(data[i][j][8]) if data[i][j][8]!='-' else 0
    return Sales, Operating_income, Ordinary_profit, Net_income, EPS, ROE, ROA


"""
matplotlibで作成したfigureをcanvasに表示する関数
"""
def draw_figure(canvas, figure):
    figure_canvas = FigureCanvasTkAgg(figure, canvas)
    figure_canvas.draw()
    figure_canvas.get_tk_widget().pack(side='top', fill='both', expand=1)
    return figure_canvas

"""
GUIのデザイン部分の作成
"""
sg.change_look_and_feel('DarkAmber')

Upper =[sg.Button('Plot', key='DataPlot', enable_events=True),
        sg.Text('コード'),
        sg.Spin(values=Codes, initial_value=Codes[0], size=(8,1), key='StockCodeSpin', enable_events=True),
        sg.Slider(range=(1,len(Codes)), orientation='h',size=(60,10), change_submits=True, key='StockCodeSlider',enable_events=True),
        sg.Text('株価'),
        sg.Input(GetStockPrice(str(Code)), size=(8,1), key='StockPrice', enable_events=True),
        sg.Text('企業名'),
        sg.Input(GetCompanyName(Code), size=(15,1), key='CompanyName', enable_events=True),
        sg.Checkbox('Auto Plot', key='AutoPlot', enable_events=True, default=True)
        ]

layout = [[Upper],[sg.Canvas(size=(500, 500), key='DataPlotter')]]
window = sg.Window('Buffett', layout, resizable=True, finalize=True)

"""
プロットの作成
"""
fig = plt.figure(figsize=(7, 7))
ax_1 = fig.add_subplot(2, 2, 1) # 1行3列の1個目
ax_2 = fig.add_subplot(2, 2, 2) # 1行3列の2個目
ax_3 = fig.add_subplot(2, 2, 3) # 1行3列の3個目
ax_4 = fig.add_subplot(2, 2, 4) # 1行3列の2個目
ax_1.set_title('売上高', fontname="MS Gothic")
ax_2.set_title('営業利益率/純利益率', fontname="MS Gothic")
ax_3.set_title('ROE/ROA', fontname="MS Gothic")
ax_4.set_title('PER/PBR', fontname="MS Gothic")

# figとCanvasを関連付ける
fig_agg = draw_figure(window['DataPlotter'].TKCanvas, fig)

"""
GUIの実行部分
"""
Code = 0
#GUI表示実行部分
while True:
    # ウィンドウ表示
    event, values = window.read()

    #クローズボタンの処理
    if event is None:
        print('exit')
        break

    CodeSpin = int(values['StockCodeSpin'])
    CodeSlider = int(values['StockCodeSlider'])
    if CodeSpin != Code:
        Code_ = CodeSpin 
    elif CodeSlider != Code:
        Code_ = CodeSlider

    if Code != Code_:
        window['StockCodeSpin'].update(Code_)
        window['StockCodeSlider'].update(Code_)
        window['StockPrice'].update(GetStockPrice(str(Code_)))
        window['CompanyName'].update(GetCompanyName(Code_))
        Code = Code_

    if event=='DataPlot' or values['AutoPlot']:
        StockPrice=GetStockPrice(str(Code))
        # Sales, Operating_income, Ordinary_profit, Net_income, EPS, ROE, ROA
        PlotList = GetData(str(Code))
        ax_1.cla()
        ax_2.cla()
        ax_3.cla()
        ax_4.cla()
        ax_1.plot([i[2:] for i in PlotList[0].keys()], PlotList[0].values())
        try:
            ax_2.plot([i[2:] for i in PlotList[0].keys()], [list(PlotList[1].values())[i]/list(PlotList[0].values())[i]*100 for i in range(len(PlotList[0].values()))], label='Oper. income')
            ax_2.plot([i[2:] for i in PlotList[0].keys()], [list(PlotList[3].values())[i]/list(PlotList[0].values())[i]*100 for i in range(len(PlotList[0].values()))], label='Net Income')
            ax_2.legend()
            ax_3.plot([i[2:] for i in PlotList[0].keys()], PlotList[5].values(), label='ROE')
            ax_3.plot([i[2:] for i in PlotList[0].keys()], PlotList[6].values(), label='ROA')
            ax_3.legend()
            ax_4.plot([i[2:] for i in PlotList[0].keys()], [StockPrice/i for i in PlotList[4].values()], label='PER')
            ax_4.plot([i[2:] for i in PlotList[0].keys()], [StockPrice/list(PlotList[4].values())[i]*list(PlotList[5].values())[i]/100 for i in range(len(PlotList[4].values()))], label='PBR')
            ax_4.legend()
        except:
            print('Error')
        ax_1.set_title('売上高', fontname="MS Gothic")
        ax_2.set_title('営業利益率/純利益率', fontname="MS Gothic")
        ax_3.set_title('ROE/ROA', fontname="MS Gothic")
        ax_4.set_title('PER/PBR', fontname="MS Gothic")

        fig_agg.draw()

window.close()

ようやく完成です!
特にこれ以上書くことはないですかね。何か気になることがあればコメントしてください。

では、ぜひ活用していただければと思います。

コメント

タイトルとURLをコピーしました