第3回(最終回) matplotlibでローソク足を描いてみる 〜S◯I証券風のチャートを描いてみる〜
第3回では、matplotlibでローソク足を描いてみようと思います。
ちなみにこれで最終回なので、このエントリーを読み終われば、こんな感じのグラフが描けるはずです。
今回のソースコードは200行くらいになってしまうので、まとめて最後に載せたいと思います。
では、今日載せたソースコードの内容の概要をざざっと説明すると。
5日移動平均線とは、5日間の調整後終値の平均をとった値です。
これを描くことで、簡単な株価の流れが読み取れます。
チャートを描くなら、必須ですね。必須。
他にも25日、60日などいろいろと種類があります。
株価の中期的な流れを見たいなら、25日。長期的に見たいなら60日などなど、自分のトレード方法に合わせて使ってください。
私は、5日線、25日線、60日線、100日線、300日線の5つを使います。
人によって、60日線ではなく75日線などあると思いますが、そこらへんは個人の自由です。
ソースコードの数字をぺっと変えてもらえば、変更できるので、それで対応してください。
最終的なコードには、5つ全ての移動平均線が載っていますが、長いのでここでは5日移動平均線のみを解説します。
5日移動平均線を計算し、その計算結果をデータフレームに追加する
第2回でこんなデータを作ったと思います。
このAdj Closeを使います。
sma5 = pandas.Series.rolling(self.data['Adj Close'], window=5,center=False).mean() sma5 = pandas.DataFrame(sma5) sma5.columns = ["sma5"] sma5 = sma5.dropna(subset=["sma5"]) kago = [go for go in range(len(sma5))] sma5.index = kago new_data = pandas.concat([self.data, sma5], axis=1)
Pythonには便利なもんで、5日移動平均線を計算してくれる関数があります。
それがpandas.Series.rollingです。
window = 5 の数字の部分を変えるだけで、それに応じた移動平均線を計算してくれます。
計算した結果をsma5に入れるのですが、この時に新しくデータフレームをこしらえます。
最終的には、5日移動平均線のデータフレームを作ったあとに、始値や終値などが入っているデータフレームにくっつけます。
なぜこうするかというと、例えば、60日分の5日移動平均線を計算すると56個の計算結果が得られます。
5日移動平均線は、5日分のデータが必要なので、始めから4日間は計算することができません。
なので始めから4日間は欠損値になり、NaNとなります。
このNaNがすごくやっかいなのです。
もし、このままmatplotlibでプロットすると欠損値を詰めて計算してしまいます。
するとどうなるかというと、本来はグラフの始めから4日間がデータとして消えているはずなのですが、NaNを詰めて計算するので直近の4日間のデータが消えてしまいます。
直近の4日間がないと移動平均線としての存在が無です。
この解決策として、私はmatplotlibには欠損値を渡さないことにしました。
だから、データフレームに入れ、matplotolibは欠損値のない部分を抜き出して、プロットしています。
sma5 = sma5.dropna(subset=["sma5"])
ここらへんは今読んでも、若干実感がわかないと思うので、とりあえずそういうもんだと思ってくれてかまわないと思います。
とりあえず、欠損値を消すために上のコードを実行しています。
new_data = pandas.concat([self.data, sma5], axis=1)
そして、元のデータに今計算した5日移動平均線を追加しています。
先程も言ったとおり、5日以外の移動平均線は数字を変えてるだけで同じ処理を行います。
choice_data = new_data.ix[:, ["Date","Open","Close","High","Low",'Adj Close',"Volume","Hiduke","sma5","sma25","sma60","sma100","sma300"]] self.choice_data = choice_data
すべての移動平均線が入ったら、それをchoice_dataとしてまとめます。
これでデータは完成です。
あとはmatplotlibに投げるのみ!
始値、終値もろもろ入ったデータフレームをmatplotlibでプロットする
from matplotlib.finance import candlestick_ochl ax1 = plt.subplot() candlestick_ochl(ax1, choice_data.values, width=0.7, colorup='#ff1e1e', colordown='#1eff1e')
Pythonは本当に便利なもんで、ローソク足を描く関数も用意されています。
とてもありがたいです。
しかし、このcandlestick_ochlは少々やっかいで、こいつにかなりエラーを出されました。
第2回でデータを整えていたのもこいつのためです。
candlestick_ochlには2つの引数が必要です。
1つは、プロットするフィールド。
2つめは、日付や始値、終値などが入ったデータ。
まず、プロットするフィールドとしてax1を与えます。
次にデータを渡すのですが、この時データの順番を守らないといけません。
データの中の順番が、日付→始値→終値→高値→安値、でないといけません。
このあとにデータが何個入ってても構わないようです。
実際、私の場合は5日移動平均線などのデータがこの後ろに散在してます。
そして、日付の形式もわりとエラーの原因となります。
日付が計算できるかたちでないと行けないようです。
そのため、私はfloat型になおしています。
plt.plot(self.choice_data["Date"], self.choice_data["sma5"], color="#1e8eff", label = "sma5") plt.legend(loc='best') plt.xticks(self.choice_data["Date"][::40], self.choice_data["Hiduke"][::40]) plt.grid(color='#f5f5f5') ax1.patch.set_facecolor('#333333') try: x_left = self.choice_data["Date"][120] except KeyError: left = len(self.choice_data["Date"]) x_left = left - 1 x_right = self.choice_data["Date"][0] plt.xlim(x_left - 5, x_right + 5) filename_candle_day = self.code + "_candle_day.svg" plt.savefig(filename_candle_day) return plt.clf()
あとはプロットするのみです。
plt.legendで、ラベルの位置をベストな配置にしています。
plt.xticksで、日付を["Date"]から["Hiduke"]に置き換えて、表示しています。
元の["Date"]では、型を変えすぎて本当の日付が読み取れないので、こうしています。
try:で行っているのは、データが足りなかった時のためです。
120日分のデータが欲しいが、上場してから80日しか経っていない場合は、データが足りないためKeyErrorが発生します。
そのためエラーが出た場合は、exceptでデータがある分だけを計算するようにしています。
私の場合は、plt.show()ではなく、そのまま画像として保存したいので、plt.savefigしたあとにplt.clf()でpltを初期化しています。
画像の形式もpngやらあったのですが、ズームしても鮮度が変わらないsvgにしました。
ここでの、一番のこだわりは色ですね笑
S◯I風にするために色は精査しました。
このサイトからトライ&エラーで色をチョイスしました。
カッコよさという割とどうでもいいことに時間を使いましたね!
エラーなくプロットできれば、こんな感じになると思います。
出来高もmatplotlibで別のグラフにプロットする
最後です。
チャートを見るときは、合わせて出来高もみたいですよね。
出来高は変わらないのに、株価だけ伸びていたり、その逆もあったりと、異変がシグナルとしてよくでてきます。
ax1 = plt.subplot() ax1.bar(self.data["Date"], self.data["Volume"], color='#1e8eff', edgecolor="#333333") ax2 = ax1.twinx() ax1.patch.set_facecolor('#333333') plt.grid(color='#f5f5f5') try: x_left = self.choice_data["Date"][120] except KeyError: left = len(self.choice_data["Date"]) x_left = left - 1 x_right = self.choice_data["Date"][0] plt.xlim(x_left - 5, x_right + 5) ax2.plot(self.data["Date"], self.data["Adj Close"], color="#ff1e8e") filename_values_day = self.code + "_values_day.svg" plt.savefig(filename_values_day) return plt.clf()
コードの内容は変わらないので、解説はしませんね。
もし、エラーなくプロットできればこんな感じになると思います。
出来高と株価を比べやすいように、Adj Closeを同じフィールドにプロットしています。
長かったですが、以上で終了です。
まとめてソースコードを貼っておきます。
ちなみに今回紹介した部分は、class Score:から下ですね。
上の部分は第1回と第2回で紹介しました。
import pandas import datetime import matplotlib.pyplot as plt def scraping_yahoo(code, start, end, term): base = "http://info.finance.yahoo.co.jp/history/?code={0}.T&{1}&{2}&tm={3}&p={4}" start = str(start) start = start.split("-") start = "sy={0}&sm={1}&sd={2}".format(start[0], start[1], start[2]) end = str(end) end = end.split("-") end = "ey={0}&em={1}&ed={2}".format(end[0], end[1], end[2]) page = 1 result = [] while True: url = base.format(code, start, end, term, page) df = pandas.read_html(url, header=0) if len(df[1]) == 0: break result.append(df[1]) page += 1 result = pandas.concat(result) result.columns = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume', 'Adj Close'] return result def generate(data): data_frame = data[["Date","Open","Close","High","Low",'Adj Close',"Volume",]] hiduke = pandas.DataFrame(data_frame["Date"]) hiduke.columns = ["Hiduke"] dates = [] for day in data_frame["Date"]: day = day.replace("年", "-") day = day.replace("月", "-") day = day.replace("日", "") time = datetime.datetime.strptime(day, '%Y-%m-%d') dates.append(time) data_frame["Date"] = dates tmp = data_frame["Date"].values.astype("datetime64[D]") d = tmp data_frame["Date"] = tmp.astype(float) index = len(data_frame["Date"]) index_data = [] for x in range(index): index_data.append(x) data_frame.index = index_data hiduke.index = index_data data_frame = pandas.concat([data_frame, hiduke], axis=1) return data_frame class Score: def __init__(self, data, code): self.data = data self.code = code def sma_addition(self): sma5 = pandas.Series.rolling(self.data['Adj Close'], window=5,center=False).mean() sma5 = pandas.DataFrame(sma5) sma5.columns = ["sma5"] sma5 = sma5.dropna(subset=["sma5"]) kago = [go for go in range(len(sma5))] sma5.index = kago new_data = pandas.concat([self.data, sma5], axis=1) sma25 = pandas.Series.rolling(self.data['Adj Close'], window=25,center=False).mean() sma25 = pandas.DataFrame(sma25) sma25.columns = ["sma25"] sma25 = sma25.dropna(subset=["sma25"]) kago = [go for go in range(len(sma25))] sma25.index = kago new_data = pandas.concat([new_data, sma25], axis=1) sma60 = pandas.Series.rolling(self.data['Adj Close'], window=60,center=False).mean() sma60 = pandas.DataFrame(sma60) sma60.columns = ["sma60"] sma60 = sma60.dropna(subset=["sma60"]) kago = [go for go in range(len(sma60))] sma60.index = kago new_data = pandas.concat([new_data, sma60], axis=1) sma100 = pandas.Series.rolling(self.data['Adj Close'], window=100,center=False).mean() sma100 = pandas.DataFrame(sma100) sma100.columns = ["sma100"] sma100 = sma100.dropna(subset=["sma100"]) kago = [go for go in range(len(sma100))] sma100.index = kago new_data = pandas.concat([new_data, sma100], axis=1) sma300 = pandas.Series.rolling(self.data['Adj Close'], window=300,center=False).mean() sma300 = pandas.DataFrame(sma300) sma300.columns = ["sma300"] sma300 = sma300.dropna(subset=["sma300"]) kago = [go for go in range(len(sma300))] sma300.index = kago new_data = pandas.concat([new_data, sma300], axis=1) choice_data = new_data.ix[:, ["Date","Open","Close","High","Low",'Adj Close',"Volume","Hiduke","sma5","sma25","sma60","sma100","sma300"]] self.choice_data = choice_data return self.choice_data def ochl(self): from matplotlib.finance import candlestick_ochl ax1 = plt.subplot() try: candlestick_ochl(ax1, self.choice_data.values, width=0.7, colorup='#ff1e1e', colordown='#1eff1e') except TypeError: pass plt.plot(self.choice_data["Date"], self.choice_data["sma5"], color="#1e8eff", label = "sma5") plt.plot(self.choice_data["Date"], self.choice_data["sma25"], color="#ff8e1e", label = "sma25") plt.plot(self.choice_data["Date"], self.choice_data["sma60"], color="#ff1e8e", label = "sma60") plt.plot(self.choice_data["Date"], self.choice_data["sma100"], color="#1effff", label = "sma100") plt.plot(self.choice_data["Date"], self.choice_data["sma300"], color="#ff7fbf", label = "sma300") plt.legend(loc='best') plt.xticks(self.choice_data["Date"][::40], self.choice_data["Hiduke"][::40]) plt.grid(color='#f5f5f5') ax1.patch.set_facecolor('#333333') try: x_left = self.choice_data["Date"][120] except KeyError: left = len(self.choice_data["Date"]) x_left = left - 1 x_right = self.choice_data["Date"][0] plt.xlim(x_left - 5, x_right + 5) filename_candle_day = self.code + "_candle_day.svg" plt.savefig(filename_candle_day) return plt.clf() def values(self): ax1 = plt.subplot() ax1.bar(self.data["Date"], self.data["Volume"], color='#1e8eff', edgecolor="#333333") ax2 = ax1.twinx() ax1.patch.set_facecolor('#333333') plt.grid(color='#f5f5f5') try: x_left = self.choice_data["Date"][120] except KeyError: left = len(self.choice_data["Date"]) x_left = left - 1 x_right = self.choice_data["Date"][0] plt.xlim(x_left - 5, x_right + 5) ax2.plot(self.data["Date"], self.data["Adj Close"], color="#ff1e8e") filename_values_day = self.code + "_values_day.svg" plt.savefig(filename_values_day) return plt.clf() if __name__ == "__main__": company = 4312 EndDate = datetime.date.today() StartDate = EndDate - datetime.timedelta(days=60)#640 data = scraping_yahoo(company, StartDate, EndDate, "d") generate_data = generate(data) big_company = Score(generate_data, str(company)) big_company.sma_addition() big_company.ochl() big_company.values()