せっかく自作の株価分析ツールを作るなら、証券会社のチャートのように動かしたい。
前回までに算出した移動平均線やRSI、ボリンジャーバンド!これらを静止画として出力するのではなく、今回は[Plotly.js]を使い、インタラクティブなWebチャートとして実装します。
「マウスホバーで値を一括表示」「特定範囲のズーム」「株価とRSIの2軸重ね合わせ」などこだわりたいUI/UXの構築プロセスを詳しく解説します。
この記事では、実際の株価分析ツールで使用しているコードをもとに、下の情報を1つの画面にまとめて表示する方法を解説します。
- 株価(終値)
- 移動平均線
- ボリンジャーバンド
- RSI(別軸表示)
Plotlyとは?
Plotlyは、ブラウザ上で動作するグラフ描画ライブラリです。
特徴は下の通りになります。
① matplotlibなどの静止画グラフとの最大の違いは、ブラウザ上で直感的に操作できる点です。
- ホバー表示: マウスを乗せるだけで、その地点の正確な数値が表示される。
- ズーム: ドラッグして特定期間を拡大したり、表示範囲を移動したりできる。
- 表示切り替え: 凡例をクリックすることで、特定の指標(例:25日線だけ消す)を即座にON/OFFできる。
② Plotlyは「Webで表示すること」を前提に設計されています。
FlaskなどのWebフレームワークとの相性は最強です。
③ 表現力が異常に高い
株価チャートはもちろん、3Dグラフ、地図データ(散布図)、バイオリン図など、学術論文からビジネスダッシュボードまで対応できるほど種類が豊富です。
サーバーサイド
データベースから値を取得し、JavaScriptが読み取りやすい「リスト形式」に変換してフロント側に渡します。
stock = Watchlist.query.filter_by(symbol=symbol, serial_num=serial_num).first()
scores_data = WatchlistDetail.query.filter_by(symbol=symbol, serial_num=serial_num).order_by(WatchlistDetail.date.asc()).all()
# --- データリストの作成 ---
def safe_list(data_list, attr):
return [getattr(d, attr) if getattr(d, attr) is not None else 0 for d in data_list]
dates = [d.date.strftime('%Y-%m-%d') for d in scores_data] # 日付
prices = safe_list(scores_data, 'price') # 価格
# 移動平均線
sma5 = safe_list(scores_data, 'sma5')
sma25 = safe_list(scores_data, 'sma25')
# RSI
rsi = safe_list(scores_data, 'rsi')
# ボリンジャーバンド
bb_up2 = safe_list(scores_data, 'bb_up2')
bb_up1 = safe_list(scores_data, 'bb_up1')
bb_low1 = safe_list(scores_data, 'bb_low1')
bb_low2 = safe_list(scores_data, 'bb_low2')
return render_template('detail.html',
stock=stock, scores=scores_data,
dates=dates, prices=prices, sma5=sma5, sma25=sma25, rsi=rsi,
bb_up2=bb_up2, bb_up1=bb_up1, bb_low1=bb_low1, bb_low2=bb_low2)① データベースからのデータ抽出
データベースから指定されている銘柄のデータを取得します。
stock = Watchlist.query.filter_by(symbol=symbol, serial_num=serial_num).first()
scores_data = WatchlistDetail.query.filter_by(symbol=symbol, serial_num=serial_num).order_by(WatchlistDetail.date.asc()).all()- Watchlist:特定の銘柄の基本情報を取得しています。
- WatchlistDetail:日次の株価やテクニカル指標が保存されている履歴テーブルからデータを取得しています。
- .order_by(WatchlistDetail.date.asc()):日付順でデータを取得します。



② 汎用関数 [safe_list]
データベースの欠損値(None)によるエラーを防ぎつつ、JavaScript用のリストを生成する関数です。
def safe_list(data_list, attr):
return [getattr(d, attr) if getattr(d, attr) is not None else 0 for d in data_list]- データの抽出:getattr(d, attr)を使い、複数のデータ(終値、RSI、移動平均など)の中から、指定した項目だけを抜き出します。
- None(Null)対策:もしデータが空(None)だった場合、そのままJavaScriptに渡すと描画エラーの原因になるため、自動的に「0」へ置き換えます。
- リスト化: 抜き出したデータを、Plotlyなどのライブラリがそのまま読み取れるリスト形式にします。
③ 各指標のリスト化
各指標(株価、移動平均線、ボリンジャーバンド)を「② 汎用関数 [safe_list]」を使ってPlotlyなどのライブラリがそのまま読み取れるPythonのリスト形式に変換します。
dates = [d.date.strftime('%Y-%m-%d') for d in scores_data]
prices = safe_list(scores_data, 'price')
# ... sma5, sma25, rsi, ボリンジャーバンド各線 ...- 日付のフォーマット:Pythonの日付オブジェクトのままではJSに渡せないので、strftime(‘%Y-%m-%d’) でISO 8601形式の文字列に変換しています。Plotlyはこの形式を自動で「時系列軸」として正しく認識してくれます。
- 変換:株価、移動平均線、ボリンジャーバンドの計8種類のデータをすべて同じ[safe_list]関数を通すことで、全てのリストの要素数が一致(同期)することを保証しています。
フロントサイド
HTML
<div id="stockChart" style="width:100%;height:400px;"></div>後述するJavaScriptの Plotly.newPlot がID(id=”stockChart”)を探してグラフを表示します。
JavaScript
const dates = {{ dates|tojson }};
// --- 1. 株価・MA・ボリンジャーバンド (左軸) ---
const tracePrice = { x: dates, y: {{ prices|tojson }}, name: '終値', type: 'scatter', mode: 'lines+markers', line: {color: '#1f77b4', width: 3} };
const traceSMA5 = { x: dates, y: {{ sma5|tojson }}, name: '5日線', type: 'scatter', line: {color: '#ff7f0e', width: 1} };
const traceSMA25 = { x: dates, y: {{ sma25|tojson }}, name: '25日線', type: 'scatter', line: {color: '#d62728', width: 1} };
const traceUp2 = { x: dates, y: {{ bb_up2|tojson }}, name: '+2σ', type: 'scatter', line: {color: 'rgba(255,0,0,0.2)', width: 1} };
const traceUp1 = { x: dates, y: {{ bb_up1|tojson }}, name: '+1σ', type: 'scatter', line: {color: 'rgba(255,0,0,0.1)', width: 1} };
const traceLow1 = { x: dates, y: {{ bb_low1|tojson }}, name: '-1σ', type: 'scatter', line: {color: 'rgba(0,128,0,0.1)', width: 1} };
const traceLow2 = { x: dates, y: {{ bb_low2|tojson }}, name: '-2σ', type: 'scatter', line: {color: 'rgba(0,128,0,0.2)', width: 1} };
// --- 2. RSI (右軸 / yaxis2) ---
const traceRSI = { x: dates, y: {{ rsi|tojson }}, name: 'RSI', type: 'scatter', yaxis: 'y2', line: {color: 'purple', width: 2, dash: 'dot'} };
const data = [tracePrice, traceSMA5, traceSMA25, traceUp2, traceUp1, traceLow1, traceLow2, traceRSI];
const layout = {
title: 'テクニカル多機能チャート',
hovermode: 'x unified',
xaxis: { autorange: true, gridcolor: '#eee' },
yaxis: { title: '株価', gridcolor: '#eee' },
yaxis2: { title: 'RSI', overlaying: 'y', side: 'right', range: [0, 100], gridcolor: 'rgba(128,0,128,0.05)', zeroline: false },
legend: { orientation: 'h', y: -0.2 },
margin: { t: 50, b: 50, l: 50, r: 50 }
};
Plotly.newPlot('stockChart', data, layout);コード解説
① 株価・移動平均線・ボリンジャーバンドの描画
const tracePrice = { x: dates, y: {{ prices|tojson }}, name: '終値', type: 'scatter', mode: 'lines+markers', line: {color: '#1f77b4', width: 3} };const tracePrice で定義されているオブジェクトは、Plotlyにおいて「1つのデータ系列(グラフの1本の線)」をどう描くかを指定する非常に重要な設定です。
Plotlyでは、このような「trace(トレース)」を複数作成し、それらを組み合わせることで複雑なチャートを作ることができます。
各プロパティが何を担当しているのか、整理して解説します。
プロパティ一覧と役割
| プロパティ名 | 設定値の例 | 説明 |
|---|---|---|
| x | dates | 横軸(時間軸)のデータ配列です。 どの日付に点を打つかを決定します。 |
| y | {{ prices|tojson }} ※ | 縦軸のデータ配列です。 そのグラフの高さ(値)がどこにあるかを決定します。 |
| name | 終値 | 凡例の名称です。 グラフ上の線が何を示しているかを伝えます。 |
| type | scatter | グラフの種類です。 Plotlyでは散布図(scatter)をベースに折れ線グラフを構成します。 [scatter]:折れ線グラフ [bar]:棒グラフ [candlestick]:ローソク足 |
| mode | lines+markers | データの表現方法です。 lines(線)で全体の流れを、markers(点)で各日の正確な位置を同時に表現します。 [lines]:線のみ [markers]:点のみ [lines+markers]:線+点 |
| line | {color:…, width:…} | 線のデザイン設定です。 色(color)や太さ(width)を指定します。 |
※ {{ prices|tojson }}:tojson を使うことで、PythonのデータをJavaScriptに渡しています
- コード上: y:{{ prices|tojson }}
- ブラウザ上: y: [2350.5, 2380.0, 2375.2, …]
② RSIを別軸で表示
const traceRSI = { x: dates, y: {{ rsi|tojson }}, name: 'RSI', type: 'scatter', yaxis: 'y2', line: {color: 'purple', width: 2, dash: 'dot'} };RSIを別軸で表示します。
yaxis: ‘y2’ という指定がポイントで、「これは普通の株価の軸(左側)ではなく、2番目の軸(右側)を使うよ」と宣言しています。
これで「価格」と「指標」を同時に見れるようになります。
③ データのまとめ
const data = [tracePrice, traceSMA5, traceSMA25, traceUp2, traceUp1, traceLow1, traceLow2, traceRSI];表示する全データを配列にまとめます。
④ レイアウト設定
const layout = {
title: 'テクニカル多機能チャート',
hovermode: 'x unified',
xaxis: { autorange: true, gridcolor: '#eee' },
yaxis: { title: '株価', gridcolor: '#eee' },
yaxis2: { title: 'RSI', overlaying: 'y', side: 'right', range: [0, 100], gridcolor: 'rgba(128,0,128,0.05)', zeroline: false },
legend: { orientation: 'h', y: -0.2 },
margin: { t: 50, b: 50, l: 50, r: 50 }
};グラフのデータそのものではなく、「グラフの外枠や表示ルール(デザイン・軸の設定)」を定義します。
| プロパティ名 | 設定値の例 | 説明 |
|---|---|---|
| title | テクニカル多機能チャート | グラフの最上部に表示されるタイトルテキストです。 |
| hovermode | x unified | マウスを合わせた瞬間に「同じ日付の全指標」を一つの吹き出しで表示する設定です。 [closest]:一番近いデータだけ表示 [x]:同じX軸のデータを表示 [x unified]:まとめて1つの吹き出しで表示 |
| xaxis | autorange: true, gridcolor: ‘#eee’ | X軸(横軸)の設定 autorange: true:データの期間(日付)に合わせて、表示範囲を自動で最適化します。 gridcolor: ‘#eee’:縦のグリッド線(背景の網掛け)の色を薄いグレーに設定します。 |
| yaxis | { title: ‘株価’, gridcolor: ‘#eee’ } | Y軸(主軸・左側)の設定 autorange:true: データの期間(日付)に合わせて、表示範囲を自動で最適化します。 gridcolor:’#eee’: 縦のグリッド線(背景の網掛け)の色を薄いグレーに設定します。 |
| yaxis2 | { title: ‘RSI’,… } | 第2のY軸(右側)の設定 ※下ので説明 |
| legend | { orientation: ‘h’, y: -0.2 } | グラフの各線の説明(凡例)の表示位置です。 orientation: ‘h’:凡例を「水平(Horizontal)」に並べます。 y: -0.2:グラフの下側に少し離して配置しています。指定して、他の指標との識別性を高めます。 |
| margin | margin: { t: 50, b: 50, l: 50, r: 50 } | グラフエリアと外枠の間の余白(Top, Bottom, Left, Right)をピクセル単位で調整しています。 |
※ 下の表がyaxis2のプロパティの説明になります。
| プロパティ名 | 設定値の例 | 説明 |
|---|---|---|
| title | RSI | 右軸のラベルです。 |
| overlaying | ‘y’ | 「主軸(株価の軸)の上に重ねて表示せよ」という命令です。 これがないと、グラフが上下2段に分かれてしまいます。 |
| side | ‘right’ | 目盛りをグラフの右側に配置します。株価(左)とRSI(右)で分離します。 |
| range | [0, 100] | 表示範囲を0から100に固定します。 RSIは0〜100以外の値を取らないため、範囲を固定することで変動が分かりやすくなります。 |
| gridcolor | ‘rgba(128,0,128,0.05)’ | グリッド線の色(透明度付き)になります。 |
| zeroline | false | 値が0の位置に太い線を引かない設定です。 |
⑤ 描画実行
Plotly.newPlot('stockChart', data, layout);「③ データ(data)」と「④ レイアウト(layout)」を、指定した場所(HTML内の id=”stockChart”)にグラフを描画します。
グラフ表示
実際に動かしてみると下のグラフが表示されます。


まとめ
Plotlyを採用することで、自作ツールの完成度は一気に「プロ仕様」へと跳ね上がります。数値だけでなくグラフとして見えるようになることで、分析の質も変わってくるはずです。





【投資に関する免責事項】
本ブログで紹介している株価解析ツールによるスコアリングおよび銘柄分析は、あくまで個人の学習・研究および技術検証を目的としたものであり、特定の銘柄の売買を推奨するものではありません。
ツールの算出結果や掲載情報の正確性については万全を期しておりますが、その内容を保証するものではありません。投資の最終決定は、必ずご自身の判断と責任において行っていただきますようお願いいたします。
万一、本ブログの情報に基づいて被ったいかなる損害についても、当サイトおよび運営者は一切の責任を負いかねますのであらかじめご了承ください。








