mplfinanceの凡例の表示が狂う件
私はpythonで株価チャートの移動平均線を表示する際にmplfinanceを使っているのですが、凡例を意図した通りに表示できずにハマったので記録を残しておこうと思います。
mplfinanceで凡例を表示する方法を解説している記事が既にありますが、移動平均"線"ではなく、ロウソク足の太い線を表示してしまうケースを紹介している記事が多いかと思います。
1は無駄話のため、結論だけ知りたい方は2に飛んでください。
4でもう一つの案を紹介しています。
1. 昔の方式の実装例
記事執筆時点で2023年ですが、数年前の記事や書籍を参照した場合、凡例の表示方法は次のようなやり方で紹介されていることがあると思います。
import datetime as dt import pandas_datareader.data as pdr import matplotlib.pyplot as plt import mplfinance as mpf df = pdr.DataReader('7203.JP', 'stooq').sort_index() #トヨタ # 移動平均 df['ma5'] = df['Close'].rolling(window=5).mean() df['ma25'] = df['Close'].rolling(window=25).mean() df['ma75'] = df['Close'].rolling(window=75).mean() cdf = df[dt.datetime(2023, 1, 1):] apd = [mpf.make_addplot(cdf['ma5'], color='blue'), mpf.make_addplot(cdf['ma25'], color='green'), mpf.make_addplot(cdf['ma75'], color='red')] # 描画情報を取得 fig, axes = mpf.plot(cdf, type='candle', addplot=apd, returnfig=True) # ラベルを追加 legend_list = ['MA5', 'MA25', 'MA75'] axes[0].legend(legend_list) plt.show()
現在の環境で実行すると凡例は全然合っていません。線の太さもおかしいです。
しかし、試しにラベルの追加の部分を次のようにして0~6の連番になるように変更してみると、、、
axes[0].legend(range(7))
2,3,4 が移動平均線に対応、5以降は無視されることがわかりました。すると、0はロウソク足のヒゲ、1はロウソク本体のようです。
そこで、0,1を無視して2,3,4を残すような方法がわかると嬉しいですね。。。
2. 1の改善案
こちらを参考にして解決しました。
how to add legend to the plot? · Issue #181 · matplotlib/mplfinance · GitHub
import datetime as dt import pandas_datareader.data as pdr import matplotlib.pyplot as plt import mplfinance as mpf df = pdr.DataReader('7203.JP', 'stooq').sort_index() #トヨタ # 移動平均 df['ma5'] = df['Close'].rolling(window=5).mean() df['ma25'] = df['Close'].rolling(window=25).mean() df['ma75'] = df['Close'].rolling(window=75).mean() cdf = df[dt.datetime(2023, 1, 1):] apd = [mpf.make_addplot(cdf['ma5'], color='blue'), mpf.make_addplot(cdf['ma25'], color='green'), mpf.make_addplot(cdf['ma75'], color='red')] # 描画情報を取得 fig, axes = mpf.plot(cdf, type='candle', addplot=apd, returnfig=True) # ラベルを追加 legend_list = ['MA5', 'MA25', 'MA75'] ######## ここから変更 ################################################# axes[0].legend([None]*2 + legend_list) handles = axes[0].get_legend().legend_handles axes[0].legend(handles=handles[2:], labels=legend_list) ######## ここまで ################################################# plt.show()
無事、細い線3本の凡例を表示できるようになりました。
凡例のハンドルを取得してから、最初の2つを除いて表示するようにしています。
3. ググるとよく出てくる方法
2の方法に行き着く前に軽くググっていたらPatchを使う方法がいくつか出てきました。
import datetime as dt import pandas_datareader.data as pdr import matplotlib.pyplot as plt import mplfinance as mpf import matplotlib.patches as mpatches #追加 df = pdr.DataReader('7203.JP', 'stooq').sort_index() #トヨタ # 移動平均 df['ma5'] = df['Close'].rolling(window=5).mean() df['ma25'] = df['Close'].rolling(window=25).mean() df['ma75'] = df['Close'].rolling(window=75).mean() cdf = df[dt.datetime(2023, 1, 1):] apd = [mpf.make_addplot(cdf['ma5'], color='blue'), mpf.make_addplot(cdf['ma25'], color='green'), mpf.make_addplot(cdf['ma75'], color='red')] # 描画情報を取得 fig, axes = mpf.plot(cdf, type='candle', addplot=apd, returnfig=True) ######## ここから変更 ################################################# patches = [ mpatches.Patch(color='blue', label='MA5'), mpatches.Patch(color='green', label='MA25'), mpatches.Patch(color='red', label='MA75')] axes[0].legend(handles=patches) ######## ここまで ################################################# plt.show()
ロウソク本体の太い線に色がついたような凡例になっています。細線ではないので惜しいです。
4. 3の改善案
こちらの記事を参考にして改善しました。
python - How to manually create a legend - Stack Overflow
import datetime as dt import pandas_datareader.data as pdr import matplotlib.pyplot as plt import mplfinance as mpf import matplotlib.patches as mpatches #追加 from matplotlib.lines import Line2D #追加 df = pdr.DataReader('7203.JP', 'stooq').sort_index() #トヨタ # 移動平均 df['ma5'] = df['Close'].rolling(window=5).mean() df['ma25'] = df['Close'].rolling(window=25).mean() df['ma75'] = df['Close'].rolling(window=75).mean() cdf = df[dt.datetime(2023, 1, 1):] apd = [mpf.make_addplot(cdf['ma5'], color='blue'), mpf.make_addplot(cdf['ma25'], color='green'), mpf.make_addplot(cdf['ma75'], color='red')] # 描画情報を取得 fig, axes = mpf.plot(cdf, type='candle', addplot=apd, returnfig=True) ######## ここから変更 ################################################# patches = [ Line2D([], [], color='blue', label='<1>MA5'), Line2D([], [], color='green', label='<1>MA25'), Line2D([], [], color='red', label='<1>MA75'), mpatches.Patch(color='blue', label='<2>MA5'), mpatches.Patch(color='green', label='<2>MA25'), mpatches.Patch(color='red', label='<2>MA75'), mpatches.Rectangle((0,0), 0, 0, color='blue', label='<3>MA5'), mpatches.Rectangle((0,0), 0, 0, color='green', label='<3>MA25'), mpatches.Rectangle((0,0), 0, 0, color='red', label='<3>MA75'), ] axes[0].legend(handles=patches) ######## ここまで ################################################# plt.show()
<1>:Line2Dを使用した細線の表示例
<2>:上述の大項目3と同じ。mpatches.Patchを使用した場合
<3>:mpatches.Rectangleを使用して<2>と同じ表示をしてみた例
<1>が表示したかった細線で、<2><3>はおまけです。
matplotlib.lines.Line2Dオブジェクトを使用すれば細線の凡例を自作できることがわかりました。
リンク先を見るとわかりますが、Line2Dを使用してポイントの凡例を作ることもできるようです。
.