PySide (Qt for Python) は、Qt(キュート)の Python バインディングで、GUI などを構築するためのクロスプラットフォームなライブラリです。Linux/X11, macOS および Microsoft Windows をサポートしています。配布ライセンスは LGPL で公開されています。
以前、本ブログで PySide2 と mplfinance を組み合わせた GUI サンプルを紹介しました [1]。また、PySide6 に GUI ライブラリを更新したサンプルも既に紹介しています [2]。
今回は yfinance の API を利用して株価データを取得する部分を、QThreadPool / QRunnable で別スレッドにして読み込むようにしました。データの読み込みにそれほど時間が掛かるわけでもないのですが、スレッドの練習題材にしたくて、ローソク足チャートのサンプルを再び取り上げました。
下記の OS 環境で動作確認をしています。
![]() |
Fedora Workstation 39 | x86_64 |
Python | 3.12.1 | |
PySide6 | 6.6.1 | |
matplotlib | 3.8.2 | |
mplfinance | 0.12.10b0 | |
pandas | 2.2.0 | |
yfinance | 0.2.36 |
サンプル
サンプルはあまりコンパクトにできなかったので、下記の4つのファイルに分けました。
- qt_mpl_finance_2.py
- GUI サンプルのメイン
- qt_mpl_finance_2_func.py
- 関数処理
- qt_mpl_finance_2_sub.py
- GUI のサブ・コンポーネント
- qt_mpl_finance_2_thread.py
- QRunner によるスレッド処理
qt_mpl_finance_2.py は、本サンプルのメイン部分で、下記の GUI を QMainWindow で構成しています。
import pandas as pd | |
import sys | |
from PySide6.QtCore import ( | |
QThreadPool, | |
Qt, | |
) | |
from PySide6.QtWidgets import ( | |
QApplication, | |
QMainWindow, | |
QWidget, | |
) | |
from qt_mpl_finance_2_func import draw_chart | |
from qt_mpl_finance_2_sub import ( | |
DockNavigator, | |
MyToolBar, | |
StockChart, | |
) | |
from qt_mpl_finance_2_thread import ThreadWorker | |
class Example(QMainWindow): | |
def __init__(self): | |
super().__init__() | |
self.threadpool = QThreadPool() | |
self.init_ui() | |
self.setWindowTitle('Candlestick chart') | |
self.resize(800, 600) | |
def init_ui(self): | |
toolbar = MyToolBar() | |
toolbar.tickerEntered.connect(self.on_ticker_entered) | |
self.addToolBar(toolbar) | |
chart = StockChart() | |
self.setCentralWidget(chart) | |
dock_bottom = DockNavigator(chart) | |
self.addDockWidget( | |
Qt.DockWidgetArea.BottomDockWidgetArea, | |
dock_bottom | |
) | |
def on_ticker_entered(self, ticker: str): | |
worker = ThreadWorker(ticker) | |
worker.finished.connect(self.on_draw_chart) | |
self.threadpool.start(worker) | |
def on_draw_chart(self, ticker: str, df: pd.DataFrame): | |
chart: QWidget | StockChart = self.centralWidget() | |
draw_chart(chart, ticker, df) | |
def main(): | |
app = QApplication(sys.argv) | |
ex = Example() | |
ex.show() | |
sys.exit(app.exec()) | |
if __name__ == '__main__': | |
main() |
qt_mpl_finance_2_func.py は、yfinance で株価データを読み込む処理と、mplfinance でデータフレームをキャンバス上に描画する処理を関数としてまとめてあります。
import mplfinance as mpf | |
import pandas as pd | |
import yfinance as yf | |
from qt_mpl_finance_2_sub import StockChart | |
def draw_chart(chart:StockChart, ticker:str, df:pd.DataFrame): | |
chart.clearAxes() | |
chart.ax.set_title(ticker) | |
mpf.plot( | |
df, | |
type='candle', | |
datetime_format='%m/%d', | |
mav=(5, 25), | |
tight_layout=False, | |
style='yahoo', | |
ax=chart.ax, | |
volume=chart.ax2 | |
) | |
chart.ax.grid() | |
chart.ax2.grid() | |
chart.refreshDraw() | |
def get_trade_info(ticker: str) -> pd.DataFrame: | |
df = yf.download(ticker, period='3mo') | |
return df |
qt_mpl_finance_2_sub.py は、メインの QMainWindow の GUI を構成する、ツールバー、中心部のチャート用のキャンバス、Matplotlib のナビゲーション・ツールバーを表示する下部のドックを記述しています。
from PySide6.QtCore import Signal | |
from PySide6.QtWidgets import ( | |
QDockWidget, | |
QLineEdit, | |
QToolBar, | |
QWidget, | |
) | |
from matplotlib import pyplot as plt | |
from matplotlib.backends.backend_qtagg import ( | |
FigureCanvasQTAgg as FigureCanvas, | |
NavigationToolbar2QT as NavigationToolbar, | |
) | |
from matplotlib.figure import Figure | |
class DockNavigator(QDockWidget): | |
def __init__(self, canvas): | |
super().__init__() | |
self.setTitleBarWidget(QWidget(None)) | |
navtoolbar = NavigationToolbar(canvas, self) | |
self.setWidget(navtoolbar) | |
class MyToolBar(QToolBar): | |
tickerEntered = Signal(str) | |
def __init__(self): | |
super().__init__() | |
ent_ticker = QLineEdit() | |
ent_ticker.setFixedWidth(100) | |
ent_ticker.returnPressed.connect(self.on_ticker_entered) | |
self.addWidget(ent_ticker) | |
def on_ticker_entered(self): | |
entry: QLineEdit = self.sender() | |
ticker = entry.text() | |
self.tickerEntered.emit(ticker) | |
class StockChart(FigureCanvas): | |
plt.rcParams['font.family'] = plt.rcParams['font.monospace'][0] | |
plt.rcParams['font.size'] = 9 | |
def __init__(self): | |
self.fig = Figure() | |
super().__init__(self.fig) | |
grid = plt.GridSpec(3, 1, wspace=0.0, hspace=0.0) | |
self.ax = self.fig.add_subplot(grid[0:2, 0]) | |
self.ax2 = self.fig.add_subplot(grid[2, 0], sharex=self.ax) | |
def clearAxes(self): | |
self.ax.cla() | |
self.ax2.cla() | |
def refreshDraw(self): | |
self.fig.canvas.draw() |
qt_mpl_finance_2_thread.py は、yfinance で株価データを読み込む処理を別スレッドで実行する QRunnable クラスを継承した ThreadWorker クラスです。
import pandas as pd | |
from PySide6.QtCore import ( | |
QObject, | |
QRunnable, | |
Signal, | |
) | |
from qt_mpl_finance_2_func import get_trade_info | |
class ThreadWorkerSignal(QObject): | |
finished = Signal(str, pd.DataFrame) | |
class ThreadWorker(QRunnable, ThreadWorkerSignal): | |
def __init__(self, ticker: str): | |
super().__init__() | |
self.ticker = ticker | |
def run(self): | |
df = get_trade_info(self.ticker) | |
self.finished.emit(self.ticker, df) |
QRunnable クラスを継承した ThreadWorker のインスタンスでは、別スレッドで処理を実行させることができますが、そのままでは、処理の終了や結果を Signal を発して親スレッドへ送ることができません。そのため、このサンプルでは、QObject を継承したシグナル専用のクラス ThreadWorkerSignal を作成しておいて、QRunnable と一緒に多重継承して、ThreadWorkerSignal で定義したシグナルを利用できるようにしています。
class ThreadWorkerSignal(QObject): finished = Signal( str , pd.DataFrame) class ThreadWorker(QRunnable, ThreadWorkerSignal): def __init__( self , ticker: str ): super ().__init__() self .ticker = ticker def run( self ): df = get_trade_info( self .ticker) self .finished.emit( self .ticker, df) |
この ThreadWorker は、本体 qt_mpl_finance_2.py 側で、下記のメソッド内でインスタンス worker を生成して、QThreadPool のインスタンス self.threadpool の start メソッドにインスタンス worker を渡します。この start メソッドが実行されると、別スレッドで worker.run() が実行されます。
def on_ticker_entered( self , ticker: str ): worker = ThreadWorker(ticker) worker.finished.connect( self .on_draw_chart) self .threadpool.start(worker) |
worker の処理が終わると、finished シグナルが発せられるので、これを self.on_draw_chart メソッド(スロット)で処理します。
def on_draw_chart( self , ticker: str , df: pd.DataFrame): chart: QWidget | StockChart = self .centralWidget() draw_chart(chart, ticker, df) |
サンプルの使い方
東証に上場している銘柄であれば、ツールボックスの QLineEdit のインスタンスに、銘柄コードに .T を付加した文字列(例 1301.T)を入力して Enter すると、過去3ヶ月分の日足チャートが出力されます。
日経平均の指数は ^N255 と入力して Enter します。米国銘柄はアルファベットのシンボルを入力します。
サンプルということで、存在しないコードを入力したときなどのエラー処理は一切していません。🙇🏻
参考サイト
- bitWalk's: ローソク足チャート ~ python/mplfinance [2021-05-02]
- bitWalk's: ローソク足チャート ~ PySide6, mplfinance [2023-08-15]
- QThreadPool - Qt for Python
- QRunnable - Qt for Python
- ranaroussi/yfinance: Download market data from Yahoo! Finance's API
- matplotlib/mplfinance: Financial Markets Data Visualization using Matplotlib

にほんブログ村
#オープンソース

0 件のコメント:
コメントを投稿