【Python】株価データをAPIで取得してグラフ化⇒StreamlitでWebアプリっぽくしてみた
前書き
筆者が普段業務で使っている言語はjavaなので、Pythonのpandasやmatplotlibとあまり仲が良くありません。そこで今回は外部からdataframe形式でデータを受け取り、そのデータをmatplotlibの入力形式に合うように加工してPythonのライブラリに慣れてみたいと思います。
入力に使うデータ
今回はdataframe形式で受け取れるデータという点と可視化した際に面白そうなデータが良いなと思ったので株価データを取得して加工してみたいと思う。
Yahoo! Finance API(yfinance)
Yahoo!では株の情報を取り扱っているヤフー・ファイナンスというサイトを運営しており、このサイトから株価や会社の概要といった情報を取得するAPI(yfinance)が存在するため今回はこれを使ってみようと思います。
Pythonのライブラリなのでpipで取得を行います。
> pip install yfinance
取得が出来たら簡単なコードで動作確認をしてみます。特に難しい点はありませんが、取得対象とする株価コードのみ指定する必要があるのでYahoo! Financeのサイト上なので事前に確認しておきましょう。
import yfinance as yf
# 銘柄(APPLE社)を指定
apl = "AAPL"
# 株価データ取得
stock = yf.download(apl)
# 表示
print(stock)
上記を実行すると以下のように日毎の株価情報が出力されます。筆者は株に疎いので細かく掘り下げませんが、該当日の開始値(Open)、該当日の最高値(High)、該当日の最低値(Low)、該当日の終了値(Close)などが取得できているようですね。
[*********************100%***********************] 1 of 1 completed
Open High Low Close Adj Close Volume
Date
1980-12-12 0.128348 0.128906 0.128348 0.128348 0.099584 469033600
1980-12-15 0.122210 0.122210 0.121652 0.121652 0.094388 175884800
1980-12-16 0.113281 0.113281 0.112723 0.112723 0.087461 105728000
1980-12-17 0.115513 0.116071 0.115513 0.115513 0.089625 86441600
1980-12-18 0.118862 0.119420 0.118862 0.118862 0.092224 73449600
... ... ... ... ... ... ...
2023-06-28 187.929993 189.899994 187.600006 189.250000 189.250000 51216800
2023-06-29 189.080002 190.070007 188.940002 189.589996 189.589996 46347300
2023-06-30 191.630005 194.479996 191.259995 193.970001 193.970001 85069600
2023-07-03 193.779999 193.880005 191.759995 192.460007 192.460007 31458200
2023-07-05 191.570007 192.979996 190.619995 191.330002 191.330002 46860000
[10729 rows x 6 columns]
matplotlibに合わせてデータを加工する
せっかくなので株っぽいグラフを作ってみます!上述の通りで株周りは門外漢なのですが、株の世界では5日間、25日間、75日間の株価平均をグラフにしてみたり、該当日の最安値や最高値あるいは出来高の値を指標として株を売り買いしているようなのでネット上で株周りの数式を探してPythonへ落とし込んでみました。
移動平均
該当日を基準にn日前までのデータを用いて平均値を求める方法。列名に出てくるsmaはsimple moving average、ようは移動平均の略です。
# 株価取得
data = yf.download(ticker, start=start, end=end)
# 日時と調整後の終値をそれぞれ切り出しておく
x = data.index
y = data["Adj Close"]
# 過去データの移動平均(simple moving average)を取る
# 平均化する期間は過去の5,25,75日分
data = data.assign(sma5 = y.rolling(window=5).mean())
data = data.assign(sma25 = y.rolling(window=25).mean())
data = data.assign(sma75 = y.rolling(window=75).mean())
移動平均の値が計算出来たらmatplotlibでグラフにしていきます。
plotでデータをグラフ化、xlabel及びylabelでグラフの軸タイトルを付け、xticks及びyticksで目盛に表示する文言のフォントサイズを修正、gridで目盛線を付けてlegendで凡例を表示しています。
fig = plt.figure(figsize=(40, 30))
# 各データをプロット
plt.plot(x, y, label=ticker)
plt.plot(x, data["sma5"], label="sma5")
plt.plot(x, data["sma25"], label="sma25")
plt.plot(x, data["sma75"], label="sma75")
# ラベルや目盛の設定
plt.xlabel("date", fontsize=40)
plt.ylabel("price", fontsize=40)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.grid()
plt.legend(fontsize=32)
せっかくなのでstreamlit上にグラフを出してみます。streamlitではmatplotlibで作成した図を表示するメソッドがあるのでこれを利用します。
st.pyplot(fig)
実行するとそれっぽいグラフが表示されました!横軸が広すぎて少し期間を絞って表示してみましたが実際に株をする方はどれくらいの期間を表示しているのでしょうね?
ロウソクチャート
こちらは該当日の最安値や最高値を使用するようですが、matplotlibでは描画出来ないようだったので別のライブラリを使ってみました。
> pip install mpl_finance
どうやらmpl_financeのcandlestick_ohlcメソッドに合うフォーマットでデータを渡せばそのまま描画してくれるらしいので加工して渡してみるとグラフ表示はすぐ出来たのだが横軸が潰れて見えない&数値形式になっていて日付形式になっていない。。結果、locatorなどを設定すると解消した。matplotlibの目盛周りは使いづらいので慣れが必要になりそう。
fig2 = plt.figure(figsize=(40, 30))
# 横軸を調整
fig2.autofmt_xdate(rotation=90, ha="center")
ax = fig2.add_subplot(1,1,1)
# フォーマットに合うように加工
data.insert(0, "index", [i for i in range (len(data))])
# 描画
mpl.candlestick_ohlc(ax, data.values, width=0.5, colorup="r", colordown="b")
# 横軸設定
plt.xticks([x for x in range(len(data))], [x.strftime('%Y-%m-%d') for x in data.index])
locator = mdates.AutoDateLocator()
ax.xaxis.set_major_locator(locator)
# ラベルや目盛の設定
plt.xlabel("date", fontsize=40)
plt.ylabel("price", fontsize=40)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.grid()
plt.legend(fontsize=32)
st.pyplot(fig2)
グラフはこんな感じ。ザ・株って感じの赤青グラフですね。
出来高の棒グラフ
移動平均で折れ線グラフは出力しているので出来高は棒グラフで出力してみる。とはいっても折れ線の時に使っていたplotメソッドをbarメソッドに変えるだけ。
fig3 = plt.figure(figsize = (40,30))
# barメソッドにするだけ
plt.bar(x, data['Volume'], label="Volume")
# ラベルや目盛の設定
plt.xlabel("date", fontsize=40)
plt.ylabel("Volume", fontsize=40)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.grid()
plt.legend(fontsize=32)
st.pyplot(fig3)
普通のグラフですね(ここから一体株の何を読み取るというのか)。
MACD
MACD(Moving Average Convergence and Divergence)、通称マックディーと呼ばれている株の世界で株価を予測するのに使われている指数のようです。数学的な書き方をすれば"短期の指数移動平均値"から"長期の指数移動平均値"を差し引いた値をマックディーというらしく、その値をさらに単純移動平均で求めた値をマックディーのシグナルというんだとかなんだとか。
これらをグラフ化したときに線が交わるタイミングが売り時だったり買い時だったりするようです(へぇーー)。
さて、実際にコーディングです。あれこれ書きましたがメソッド自体は用意されているので値を渡すだけ。ちなみにEMAはExponential Moving Average、ようは指数移動平均の略です。
# 指数移動平均を求めるため期間
FastEMA = 12
SlowEMA = 26
SignalSMA =9
# MACDとシグナルを計算
data = data.assign(MACD = data["Adj Close"].ewm(span=FastEMA).mean() - data["Adj Close"].ewm(span=SlowEMA).mean())
data = data.assign(Signal = data["MACD"].rolling(SignalSMA).mean())
# プロット
fig4 = plt.figure(figsize = (40,30))
plt.plot(x, data["MACD"], label="MACD")
plt.plot(x, data["Signal"], label="Signal")
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.grid()
plt.legend(fontsize=32)
st.pyplot(fig4)
グラフ化したものがこちら。半年強の期間を出力しましたが線が交わるタイミングが月一程度の場合や短期間で複数回交わるタイミングもあるので実際の株売買にはこれ以外にもいろんな側面から判断する必要があるのでしょうね。
株の銘柄と期間をstreamlitから任意に変えれるようにしてみる
最後はpandasやmatplotlibから脱線しますが、グラフ化する株の銘柄や対象期間をstreamlitから入力して上記4つのグラフを好き勝手作れるようにしてみます。
今回は今まで使ってこなかったstreamlitのサイドバーを活用してみました。サイドバーに銘柄や対象期間を設定したらメイン画面にグラフが出るようになっています。
import datetime as dt
import yfinance as yf
import streamlit as st
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import mpl_finance as mpl
import warnings
from matplotlib.dates import DateFormatter
warnings.simplefilter('ignore')
st.title("株価情報集計サイト")
ticker = st.sidebar.text_input("銘柄", "AMZN")
start = st.sidebar.date_input("開始日", dt.date(2020, 1, 1))
end = st.sidebar.date_input("終了日", dt.date(2021, 1, 1))
data = yf.download(ticker, start=start, end=end)
# 日時と調整後の終値をそれぞれ切り出しておく
x = data.index
y = data["Adj Close"]
# 過去データの移動平均(simple moving average)を取る
# 平均化する期間は過去の5,25,75日分
data = data.assign(sma5 = y.rolling(window=5).mean())
data = data.assign(sma25 = y.rolling(window=25).mean())
data = data.assign(sma75 = y.rolling(window=75).mean())
# matplotlibを使ってグラフ化
st.header("調整後の終値及び移動平均株価")
fig = plt.figure(figsize=(40, 30))
plt.plot(x, y, label=ticker)
plt.plot(x, data["sma5"], label="sma5")
plt.plot(x, data["sma25"], label="sma25")
plt.plot(x, data["sma75"], label="sma75")
plt.xlabel("date", fontsize=40)
plt.ylabel("price", fontsize=40)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.grid()
plt.legend(fontsize=32)
# streamlitにはmatplotlibで描画した図を出力する関数がある
st.pyplot(fig)
# ロウソクチャート
st.header("ロウソクチャート")
fig2 = plt.figure(figsize=(40, 30))
fig2.autofmt_xdate(rotation=90, ha="center")
ax = fig2.add_subplot(1,1,1)
data.insert(0, "index", [i for i in range (len(data))])
mpl.candlestick_ohlc(ax, data.values, width=0.5, colorup="r", colordown="b")
plt.xticks([x for x in range(len(data))], [x.strftime('%Y-%m-%d') for x in data.index])
locator = mdates.AutoDateLocator()
ax.xaxis.set_major_locator(locator)
plt.xlabel("date", fontsize=40)
plt.ylabel("price", fontsize=40)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.grid()
st.pyplot(fig2)
# 出来高の棒グラフ
st.header("出来高グラフ")
fig3 = plt.figure(figsize = (40,30))
plt.bar(x, data['Volume'], label="Volume")
plt.xlabel("date", fontsize=40)
plt.ylabel("Volume", fontsize=40)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.grid()
plt.legend(fontsize=32)
st.pyplot(fig3)
# MACDの計算
st.header("MACD")
FastEMA = 12
SlowEMA = 26
SignalSMA =9
data = data.assign(MACD = data["Adj Close"].ewm(span=FastEMA).mean() - data["Adj Close"].ewm(span=SlowEMA).mean())
data = data.assign(Signal = data["MACD"].rolling(SignalSMA).mean())
fig4 = plt.figure(figsize = (40,30))
plt.plot(x, data["MACD"], label="MACD")
plt.plot(x, data["Signal"], label="Signal")
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.grid()
plt.legend(fontsize=32)
st.pyplot(fig4)
中々悪くないんじゃないでしょうか!特にStreamlitはダッシュボードとしての機能が優れているフレームワークなので、今回作成したグラフ以外にもいろんな情報を載せて自分だけの情報サイトを作ってみるのもよいかもしれません。
使ってみた感想
門外漢のデータを使うと新しい発見が多くて楽しいですね。ただ、当初目的のpandasに慣れる!matplotlibに慣れる!といったところは正直まだまだ足りていないなと実感していますので別のデータを使って何か試してみたり、シンプルに参考書をイチから読むなどしてみた方がよいのかな、と感じました。
この記事が気に入ったらサポートをしてみませんか?