【Python】mplfinance で複数グラフを描くとワーニング


はじめに

株価データの分析のために,複数銘柄のチャートを描きたいと思った
例えば,TOPIX Core30 だと30銘柄ある
1銘柄ずつループで回していると,チャートは表示されるものの,ワーニングが表示される

/usr/local/lib/python3.9/site-packages/mplfinance/_mplwraps.py:60:
RuntimeWarning: More than 20 figures have been opened.
Figures created through the pyplot interface
(`matplotlib.pyplot.figure`) are retained until explicitly closed
and may consume too much memory. 
(To control this warning, see the rcParam `figure.max_open_warning`).
  f = plt.figure(FigureClass=Mpf_Figure,*args,**kwargs)

※本記事中,見やすさのため,出力結果の一部に改行をいれているところがあります

20個より多くのグラフを描くとワーニングになるようで,
このワーニングがでなくなる方法を備忘録として残しておく

ワーニングが出たコード

こちらのデータ(SPY_20110701_20120630_Bollinger.csv)を使って同じチャートを30回描くコードだ

warn_code.py
import pandas as pd
import mplfinance as mpf
df = pd.read_csv('SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True)
# 30回チャートを描く
for index in range(30):
    # チャート描画
    fig = mpf.figure(figsize=(3, 3),style='yahoo')
    ax1 = fig.add_subplot(1,1,1)
    mpf.plot(df, ax=ax1, style='yahoo', type='candle', xrotation=30)

グラフは30個表示されるが,

上述のワーニングが表示される

問題解決のため調査をはじめる

このワーニングの対処法を調査すると,matplotlib の解決策ばかりだった

plt.clf()
plo.close()

matplotlib では clf と close を呼べばいいらしい
しかし,mplfinance の場合は,closeやclfがない
試しに読んでみると

>>> mpl.close()
AttributeError: module 'mplfinance' has no attribute 'close'
>>> mpl.clf()
AttributeError: module 'mplfinance' has no attribute 'clf'

モジュール内の一覧を表示してみると

>>> import mplfinance as mpl
>>> dir(mpl)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__',
'__package__', '__path__', '__spec__', '__version__', '_arg_validators', '_helpers',
'_mpf_warnings', '_mplwraps', '_panels', '_styledata', '_styles', '_utils', '_version',
'_widths', 'available_styles', 'figure', 'make_addplot', 'make_marketcolors',
'make_mpf_style', 'mplfinance', 'plot', 'plotting', 'show', 'write_style_file']

確かになさそうだ
fig を作った plt を得るしかなさそうである

mplfinance のソースを見てみる

mplfinance.figure が定義されている,_mplwraps.py を見ると plt がいた

_mplwraps.py
def figure(*args,**kwargs):

    style = _check_for_and_apply_style(kwargs)

    f = plt.figure(FigureClass=Mpf_Figure,*args,**kwargs)
    f.mpfstyle = style
    return f

class Mpf_Figure(mplfigure.Figure):
    

matplotlib.pyplot.figure()関数を呼び出している

引数FigureClass=Mpf_FigureのMpf_Figureクラスは,_mplwraps.py 内で定義されているクラスだ

The wrapper function is the same as `matplotlib.pyplot.figure()`
except that it additionally accepts kwarg `style=` to set the mplfinance style.

_mplwraps.py のヘッダに書かれているが,mplfinance.figure()関数は基本 matplotlib.pyplot.figure()関数と同じだ
ただし,mplfinance の style を設定できるところだけが違う

style = _check_for_and_apply_style(kwargs)

_check_for_and_apply_style()関数で matplotlib.pyplot.figure()関数 に渡す kwargs から style を削除しているようだ

f.mpfstyle = style

そして,matplotlib.pyplot.figure()関数でできた mplfinance.figure に後からスタイルを設定している

解決案

matplotlib.pyplot と,mplfinance._mplwraps.Mpf_Figure をインポート

import matplotlib.pyplot as plt
import mplfinance as mpf
from mplfinance._mplwraps import Mpf_Figure

figure の生成を,mplfinance.figure()関数から,matplotlib.pyplot.figure()関数に変更する
style は後から設定してあげる

# もともとの figure 生成
fig = mpf.figure(figsize=(3, 3),style='yahoo')
# 新しい figure の生成方法
fig = plt.figure(FigureClass=Mpf_Figure,figsize=(3, 3))
fig.mpfstyle = 'yahoo'

plot が終わったら,clf と close を呼んであげる

plt.show()
plt.clf()
plt.close()

まとめると以下のようだ

ok_code.py
import pandas as pd
import matplotlib.pyplot as plt
import mplfinance as mpf
from mplfinance._mplwraps import Mpf_Figure
df = pd.read_csv('SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True)
# 30回チャートを描く
for index in range(30):
    # チャート描画
    fig = plt.figure(FigureClass=Mpf_Figure,figsize=(3, 3))
    fig.mpfstyle = 'yahoo'
    ax1 = fig.add_subplot(1,1,1)
    mpf.plot(df, ax=ax1, style='yahoo', type='candle', xrotation=30)
    plt.show()
    plt.clf()
    plt.close()

めでたくワーニングが消えた

おわりに

matplotlib でのワーニングを消す方法を参考に mplfinance で行ってみた

うまいやり方,良い方法かどうかわからない…

これは違う,こうしたほうが良いなどあれば,コメントいただければと思う

追記

単にこれでよかったのかもしれない

インポートについて理解していなかったが,
Pythonのモジュールインポートのしくみを読むと,
モジュールオブジェクトはシングルトンなため,
自分で import した plt(matplotlib.pyplot)を使って clr,close を呼ぶだけ

# matplotlib.pyplot をインポート
import matplotlib.pyplot as plt

# plot,show の後に,pltのclf,closeを呼ぶ
plt.clf()
plt.close()

まとめると

ok_code2.py
import pandas as pd
import matplotlib.pyplot as plt
import mplfinance as mpf
df = pd.read_csv('SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True)
# 30回チャートを描く
for index in range(30):
    # チャート描画
    fig = mpf.figure(figsize=(3, 3),style='yahoo')
    ax1 = fig.add_subplot(1,1,1)
    mpf.plot(df, ax=ax1, style='yahoo', type='candle', xrotation=30)
    plt.show()
    plt.clf()
    plt.close()