2017年08月18日

日経平均&個別株の騰落率グラフ生成(Python+matplotlib+Flask) メインコード

というわけで、前回の説明に引き続いて日経平均&個別株の騰落率グラフ生成ページの詳細スクリプトです。

BootStrapとFlaskは以前のRTチェッカー同様に使っているので、下記のような構成になっています。

StockGraphFlask
├─static
│  ├─css
│  ├─fonts
│  ├─image
│  └─js
└─templates

上記に格納されているコードの中でメインとなるものに続きます。

templates/index.html

<!DOCTYPE html>
<script>window.twttr = (function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0],
t = window.twttr || {};
if (d.getElementById(id)) return t;
js = d.createElement(s);
js.id = id;
js.src = "https://platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);

t._e = [];
t.ready = function(f) {
t._e.push(f);
};
return t;
}(document, "script", "twitter-wjs"));</script>

<html lang="ja">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>KumaKuma Web Site</title>
<link href="static/css/bootstrap.min.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="static/js/bootstrap.min.js"></script>
</head>

<body>
<nav class="navbar navbar-default navbar-fixed-top">
<div class="navbar-header">
<a class="navbar-brand" href="http://kumanabe.s1003.xrea.com/cgi/StockGraphFlask/">KumaKuma Stock Graph[証券コードと期間を入力⇒騰落率グラフ]</a>
</div>
</nav>

<div class ="container" style="padding:60px 0 0 0">
<form class="form-inline">
<div class="form-group">
<label class="control-label" for="Tweet StatusId">Stock Code(Max:5):</label>
<input type="text" size="4" class="form-control" id="stockID_A" name="stockID_A" placeholder="0123">
<input type="text" size="4" class="form-control" id="stockID_B" name="stockID_B" placeholder="0123">
<input type="text" size="4" class="form-control" id="stockID_C" name="stockID_C" placeholder="0123">
<input type="text" size="4" class="form-control" id="stockID_D" name="stockID_D" placeholder="0123">
<input type="text" size="4" class="form-control" id="stockID_E" name="stockID_E" placeholder="0123">

<select class="form-control" id="period" name="period">
<option value="10d">2週</option>
<option value="1M">1ヶ月</option>
<option value="3M">3ヶ月</option>
<option value="6M">6ヶ月</option>
<option value="1Y" selected="selected">1年</option>
<option value="2Y">2年</option>
<option value="3Y">3年</option>
</select>
 </div>
<button type="submit" class="btn btn-primary">Make Graph</button>
</form>
</div>
<img src="{{ img_name }}" class="img-thumbnail img-responsive">

<footer>
</footer>
</body>
</html>

app.py

#!/usr/local/bin/python3.5

from flask import Flask, session, redirect, render_template, request
import requests
import numpy as np

# matplotlib.use('Agg')はpltの前に実施する必要あり
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm

# seabornはインストールでFailしたため今回は不使用
#import seaborn as sns

import re
import random,string
import datetime

# 文字コード(UTF-8)関連
import io,sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

# KEY Information
SECRET_KEY ="******************************************"

# Flask の起動
app = Flask(__name__)

# flask の session を使うにはkeyを設定する必要がある.(今回は不要かも)
app.secret_key = SECRET_KEY

# 折れ線グラフ生成
def makeGraph(stockID,plt,period):
if stockID:
graphData=[]
if stockID == "NI225": #日経平均
res = requests.get("https://www.google.com/finance/getprices?q="+stockID+"&x=INDEXNIKKEI&p="+period+"&f=d,c&df=cpct&auto=1")
else: #個別株(StockIDから証券コードを取得)
res = requests.get("https://www.google.com/finance/getprices?q="+stockID+"&x=TYO&p="+period+"&f=d,c&df=cpct&auto=1")

# Google financeから株価を取得
lines = res.text.split("\n")
for line in lines[9:-1]:
columns = line.split(",")
stockNum = columns[1]
graphData.append(float(stockNum))

#基準日(一番初めの日)を1として、各日の騰落率を計算
graphDataS = [(data - graphData[0])*100/graphData[0] for data in graphData]
height = np.array(graphDataS)

# 折れ線グラフのプロット
if stockID == "NI225" : #日経平均
plt.plot(height,label="日経平均")
else: # 個別株の場合は証券コードから会社名を取得する
res = requests.get("http://kabu-data.info/all_code/all_code_code.htm")
res.encoding = 'Shift_JIS'
res.text.encode('utf-8')
lines = res.text.split("\n")
for line in lines:
#print(line)
lineSearch = re.search("<tr><td>%s</td><td>([^<]+)</td><td>" % stockID,line)
if lineSearch:
stockName = lineSearch.group(1)
plt.plot(height,label=stockName)
break

@app.route('/')
def index():
""" root ページの表示 """
# 5つ証券コードをFormから取得
stockID_A = request.args.get('stockID_A')
stockID_B = request.args.get('stockID_B')
stockID_C = request.args.get('stockID_C')
stockID_D = request.args.get('stockID_D')
stockID_E = request.args.get('stockID_E')
# 表示期間をFormから取得
period = request.args.get('period')

if period:# ボタンを押すと少なくとも期間(period)はGETで受け渡される
plt.clf() #一度プロットをクリア
graphOn = 1
makeGraph("NI225",plt,period)
makeGraph(stockID_A,plt,period)
makeGraph(stockID_B,plt,period)
makeGraph(stockID_C,plt,period)
makeGraph(stockID_D,plt,period)
makeGraph(stockID_E,plt,period)

# x軸のメモリの計算。ざっとなので正確ではないけれど、それなりに見えれば良しと言う前提
### ---------Period: 1年-3年 -----------
today = datetime.datetime.now()
dt = datetime.datetime.now() - datetime.timedelta(365*3)
dt_0p5 = datetime.datetime.now() - datetime.timedelta(364*2.5)
dt_1p = datetime.datetime.now() - datetime.timedelta(365*2)
dt_1p5 = datetime.datetime.now() - datetime.timedelta(364*1.5)
dt_2p = datetime.datetime.now() - datetime.timedelta(365*1)
dt_2p25 = datetime.datetime.now() - datetime.timedelta(364*0.75)
dt_2p5 = datetime.datetime.now() - datetime.timedelta(364*0.5)
dt_2p75 = datetime.datetime.now() - datetime.timedelta(364*0.25)
### ---------Period: 6か月-3か月 -----------
dt_0m = datetime.datetime.now() - datetime.timedelta(30*6)
dt_1m = datetime.datetime.now() - datetime.timedelta(30*5)
dt_2m = datetime.datetime.now() - datetime.timedelta(30*4)
dt_3m = datetime.datetime.now() - datetime.timedelta(30*3)
dt_4m = datetime.datetime.now() - datetime.timedelta(30*2)
dt_5m = datetime.datetime.now() - datetime.timedelta(30*1)
### ---------Period: 1ヶ月-2週間 -----------
dt_0w = datetime.datetime.now() - datetime.timedelta(5*4)
dt_1w = datetime.datetime.now() - datetime.timedelta(5*3)
dt_2w = datetime.datetime.now() - datetime.timedelta(5*2)
dt_3w = datetime.datetime.now() - datetime.timedelta(5*1)
# 下記から実際にx軸のメモリ表示の設定
if period == "10d":
plt.xticks([0,5], [str(dt_2w.year)+"-"+str(dt_2w.month)+"-"+str(dt_2w.day),
str(dt_3w.year)+"-"+str(dt_3w.month)+"-"+str(dt_3w.day)])
if period == "1M":
plt.xticks([0,5,10,15], [str(dt_0w.year)+"-"+str(dt_0w.month)+"-"+str(dt_0w.day),
str(dt_1w.year)+"-"+str(dt_1w.month)+"-"+str(dt_1w.day),
str(dt_2w.year)+"-"+str(dt_2w.month)+"-"+str(dt_2w.day),
str(dt_3w.year)+"-"+str(dt_3w.month)+"-"+str(dt_3w.day)])
elif period == "3M":
plt.xticks([0,22,44], [str(dt_3m.year)+"-"+str(dt_3m.month),
str(dt_4m.year)+"-"+str(dt_4m.month),
str(dt_5m.year)+"-"+str(dt_5m.month)])
elif period == "6M":
plt.xticks([0,22,44,66,88,110], [str(dt_0m.year)+"-"+str(dt_0m.month),
str(dt_1m.year)+"-"+str(dt_1m.month),
str(dt_2m.year)+"-"+str(dt_2m.month),
str(dt_3m.year)+"-"+str(dt_3m.month),
str(dt_4m.year)+"-"+str(dt_4m.month),
str(dt_5m.year)+"-"+str(dt_5m.month)])
elif period == "1Y":
plt.xticks([0,65,130,195], [str(dt_2p.year)+"-"+str(dt_2p.month),
str(dt_2p25.year)+"-"+str(dt_2p25.month),
str(dt_2p5.year)+"-"+str(dt_2p5.month),
str(dt_2p75.year)+"-"+str(dt_2p75.month)])

elif period == "2Y":
plt.xticks([0,130,260,390], [str(dt_1p.year)+"-"+str(dt_1p.month),
str(dt_1p5.year)+"-"+str(dt_1p5.month),
str(dt_2p.year)+"-"+str(dt_2p.month),
str(dt_2p5.year)+"-"+str(dt_2p5.month)])


elif period == "3Y":
plt.xticks([0,130,260,390,520,650], [str(dt.year)+"-"+str(dt.month),
str(dt_0p5.year)+"-"+str(dt_0p5.month),
str(dt_1p.year)+"-"+str(dt_1p.month),
str(dt_1p5.year)+"-"+str(dt_1p5.month),
str(dt_2p.year)+"-"+str(dt_2p.month),
str(dt_2p5.year)+"-"+str(dt_2p5.month)])

# サーバー上に日本語Fontを置いてlabelや凡例を表示させている
prop = fm.FontProperties(fname='static/fonts/ipaexg.ttf')
plt.ylabel("基準日からの騰落率(%)", fontproperties=prop)
plt.legend(prop=prop)

# pltで生成する画像名はランダムで生成する
chars = string.digits + string.ascii_letters
img_name = "static/image/"+''.join(random.choice(chars) for i in range(64)) + '.png'
plt.savefig(img_name)

else: #defaultの表示
# 初めは画像のところに説明用の画像を表示させる
graphOn = 0
img_name = "static/image/introduction.png"

# templates/index.html を使ってレンダリング.
return render_template('index.html', graphOn=graphOn, img_name=img_name)

if __name__ == '__main__':
app.run(debug=True)

スポンサーリンク
posted by くまなべ at 20:42 | Comment(0) | TrackBack(0) | Python
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/180674759
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック