【Python】株価データ分析 株価チャートを描く～mplfinance編 その３～

## はじめに

前回は，mplfinance を使ってローソク足，ボリンジャーバンド，MACD，RSI チャートを描いてみた

https://qiita.com/tnozoo/items/ac390974e9c6586ec4ec

今回は一目均衡表チャートを描いてみる

## 一目均衡表チャートとは

五本の線とローソク足をあわせて一目均衡表チャートという
詳しい説明はこちらなどにおまかせるとして

https://info.monex.co.jp/technical-analysis/indicators/004.html

一目均衡表チャートを描く上での最低限の用語と計算式を整理しておく

## 一目均衡表の五線と抵抗帯

- 基準線： （当日を含めた過去26日間の最高値＋最安値） $\div$ ２
- 転換線： （当日を含めた過去9日間の最高値＋最安値） $\div$ ２
- 先行スパン１： ｛（転換値＋基準値） $\div$ ２｝を２６日先行させて表示
- 先行スパン２： ｛（当日を含めた過去５２日間の最高値＋最安値） $\div$ ２｝を２６日先行させて表示
- 遅行スパン： 当日の終値を２６日遅行させて表示
- 抵抗帯： 先行スパン１と先行スパン２の間（先行スパン１と先行スパン２の間の色を塗る）

## 一目均衡表の五線の値を求める関数

```python
def ichimoku(o, h, l, c):
    ## 当日を含めた過去26日間の最高値
    ## 当日を含めた過去 9日間の最高値
    ## 当日を含めた過去52日間の最高値
    max26 = h.rolling(window=26).max()
    max9  = h.rolling(window=9).max()
    max52 = h.rolling(window=52).max()
    ## 当日を含めた過去26日間の最安値
    ## 当日を含めた過去 9日間の最安値
    ## 当日を含めた過去52日間の最安値
    min26 = l.rolling(window=26).min()
    min9  = l.rolling(window=9).min()
    min52 = l.rolling(window=52).min()

    ## 基準線=（当日を含めた過去26日間の最高値+最安値）÷2
    ## 転換線=（当日を含めた過去9日間の最高値+最安値）÷2
    kijun = (max26 + min26) / 2
    tenkan = (max9 + min9) / 2
    ## 先行スパン1=｛（転換値+基準値）÷2｝を26日先行させて表示
    senkospan1 = (kijun + tenkan) / 2
    senkospan1 = senkospan1.shift(26)
    ## 先行スパン2=｛（当日を含めた過去52日間の最高値+最安値）÷2｝を26日先行させて表示
    senkospan2 = (max52 + min52) / 2
    senkospan2 = senkospan2.shift(26)
    ## 遅行スパン= 当日の終値を26日遅行させて表示
    chikouspan = c.shift(-26)

    return kijun, tenkan, senkospan1, senkospan2, chikouspan
```

## 

前回の，[【Python】株価データ分析 株価チャートを描く～mplfinance編 その２～](https://qiita.com/tnozoo/items/ac390974e9c6586ec4ec)のコードからの主な変更点を書いておく

一目均衡表の五線の値を求める関数を呼び出し

```python
# 一目均衡表
kijun, tenkan, senkospan1, senkospan2, chikouspan = ichimoku(o, h, l, c)
```

プロットを作成する際に，`mplfinance.make_addplot` を呼ぶが，
一目均衡表の五線をプロットする際のパラメータ辞書を用意して，プロットする
ボリンジャーバンドは ±2σ のみの表示に変更する（チャートがゴチャゴチャするので）

```python
# 一目均衡表 axes No.1 に描く
ichimokuargs = dict(ax=ax1, width=.5)

# プロットを作成する(ボリンジャーバンド，MACD，RSI，一目均衡表)
ap = [
##    mpf.make_addplot(bbh1, **bbargs),
##    mpf.make_addplot(bbl1, **bbargs),
    mpf.make_addplot(bbh2, **bbargs),
    mpf.make_addplot(bbl2, **bbargs),
##    mpf.make_addplot(bbh3, **bbargs),
##    mpf.make_addplot(bbl3, **bbargs),
    mpf.make_addplot(macd_, **macdargs, color='blue'),
    mpf.make_addplot(macdsignal, **macdargs, color='orange'),
    mpf.make_addplot(histogramplus, **macdargs, color='red', type='bar'),
    mpf.make_addplot(histogramminus, **macdargs, color='green', type='bar'),
    mpf.make_addplot(rsi_, **rsiargs, color='blue'),
    mpf.make_addplot(kijun, **ichimokuargs, color='orange'),
    mpf.make_addplot(tenkan, **ichimokuargs, color='royalblue'),
    mpf.make_addplot(senkospan1, **ichimokuargs, color='black'),
    mpf.make_addplot(senkospan2, **ichimokuargs, color='purple'),
    mpf.make_addplot(chikouspan, **ichimokuargs, color='red')
]
```

先行スパン１と先行スパン２の間を塗りつぶす

```python
# 一目均衡表(axes=1)の先行スパン１と先行スパン２の間を塗りつぶす
ax1.fill_between(x=range(0, len(df.index)), y1=senkospan1.values, y2=senkospan2.values, alpha=0.5, color='gray')
```

## まとめ


```python
import numpy as np
import pandas as pd
import pandas_datareader.data as pdr
import datetime
from dateutil.relativedelta import relativedelta
import matplotlib.pyplot as plt
import mplfinance as mpf

# 12か月チャート
month = 12
# チャートの基本設定
kwargs = dict(type = 'candle', style = 'yahoo') ## starsandstripes, yahoo

# 12か月前から本日までのデータを取得する
ed = datetime.datetime.now()
st = ed - relativedelta(months = month)
# トヨタ
df = pdr.DataReader('7203.T', 'yahoo', st, ed)

def bollingerband(c, period):
    bbma = c.rolling(window=period).mean() ## 平均
    bbstd = c.rolling(window=period).std() ## 標準偏差
    bbh1 = bbma + bbstd * 1
    bbl1 = bbma - bbstd * 1
    bbh2 = bbma + bbstd * 2
    bbl2 = bbma - bbstd * 2
    bbh3 = bbma + bbstd * 3
    bbl3 = bbma - bbstd * 3
    return bbh1,bbl1,bbh2,bbl2,bbh3,bbl3

def macd(c, n1, n2, ns):
    ema_short = c.ewm(span=n1,adjust=False).mean()
    ema_long = c.ewm(span=n2,adjust=False).mean()
    macd = ema_short - ema_long
    signal = macd.ewm(span=ns,adjust=False).mean()
    histogram = macd - signal
    histogramplus = histogram.where(histogram > 0, 0)
    histogramminus = histogram.where(histogram < 0, 0)
    return macd,signal,histogram,histogramplus,histogramminus

def rsi(c, period):
    diff = c.diff() #前日比
    up = diff.copy() #上昇
    down = diff.copy() #下落
    up = up.where(up > 0, np.nan) #上昇以外はnp.nan
    down = down.where(down < 0, np.nan) #下落以外はnp.nan
    #upma = up.rolling(window=period).mean() #平均
    #downma = down.abs().rolling(window=period).mean() #絶対値の平均
    upma = up.ewm(span=period,adjust=False).mean() #平均
    downma = down.abs().ewm(span=period,adjust=False).mean() #絶対値の平均
    rs = upma / downma
    rsi = 100 - (100 / (1.0 + rs))
    return rsi

def ichimoku(o, h, l, c):
    ## 当日を含めた過去26日間の最高値
    ## 当日を含めた過去 9日間の最高値
    ## 当日を含めた過去52日間の最高値
    max26 = h.rolling(window=26).max()
    max9  = h.rolling(window=9).max()
    max52 = h.rolling(window=52).max()
    ## 当日を含めた過去26日間の最安値
    ## 当日を含めた過去 9日間の最安値
    ## 当日を含めた過去52日間の最安値
    min26 = l.rolling(window=26).min()
    min9  = l.rolling(window=9).min()
    min52 = l.rolling(window=52).min()

    ## 基準線=（当日を含めた過去26日間の最高値+最安値）÷2
    ## 転換線=（当日を含めた過去9日間の最高値+最安値）÷2
    kijun = (max26 + min26) / 2
    tenkan = (max9 + min9) / 2
    ## 先行スパン1=｛（転換値+基準値）÷2｝を26日先行させて表示
    senkospan1 = (kijun + tenkan) / 2
    senkospan1 = senkospan1.shift(26)
    ## 先行スパン2=｛（当日を含めた過去52日間の最高値+最安値）÷2｝を26日先行させて表示
    senkospan2 = (max52 + min52) / 2
    senkospan2 = senkospan2.shift(26)
    ## 遅行スパン= 当日の終値を26日遅行させて表示
    chikouspan = c.shift(-26)

    return kijun, tenkan, senkospan1, senkospan2, chikouspan

# float 型に
df['Open'] = df['Open'].astype(float)
df['High'] = df['High'].astype(float)
df['Low'] = df['Low'].astype(float)
df['Close'] = df['Close'].astype(float)
o = df['Open']
c = df['Close']
l = df['Low']
h = df['High']

'''
チャートを描く
'''
# ボリンジャーバンド（移動平均25日線）
bbh1, bbl1, bbh2, bbl2, bbh3, bbl3 = bollingerband(c, 25)
# MACD（短期=12，長期=26，シグナル=9）
macd_, macdsignal, histogram, histogramplus, histogramminus = macd(c, 12, 26, 9)
# RSI（14日）
rsi_ = rsi(c, 14)
# 一目均衡表
kijun, tenkan, senkospan1, senkospan2, chikouspan = ichimoku(o, h, l, c)
    
# 高さの比を 3:1:1 で GridSpec を用意する
fig = mpf.figure(figsize=(9.6, 9.6), style='starsandstripes')
gs = fig.add_gridspec(3, 1, hspace=0, wspace=0, height_ratios=(3, 1, 1))
(ax1,ax2,ax3) = gs.subplots(sharex='col')

# ボリンジャーバンドは axes No.1 に描く
bbargs = dict(ax=ax1, width=.5, linestyle='dashdot', color='black')
# MACD は axes No.2 に描く
macdargs = dict(ax=ax2, width=1, ylabel='MACD')
# RSI は axes No.3 に描く
rsiargs = dict(ax=ax3, width=1, ylabel='RSI')
# 一目均衡表 axes No.1 に描く
ichimokuargs = dict(ax=ax1, width=.5)

# プロットを作成する(ボリンジャーバンド，MACD，RSI，一目均衡表)
ap = [
    mpf.make_addplot(bbh2, **bbargs),
    mpf.make_addplot(bbl2, **bbargs),
    mpf.make_addplot(macd_, **macdargs, color='blue'),
    mpf.make_addplot(macdsignal, **macdargs, color='orange'),
    mpf.make_addplot(histogramplus, **macdargs, color='red', type='bar'),
    mpf.make_addplot(histogramminus, **macdargs, color='green', type='bar'),
    mpf.make_addplot(rsi_, **rsiargs, color='blue'),
    mpf.make_addplot(kijun, **ichimokuargs, color='orange'),
    mpf.make_addplot(tenkan, **ichimokuargs, color='royalblue'),
    mpf.make_addplot(senkospan1, **ichimokuargs, color='black'),
    mpf.make_addplot(senkospan2, **ichimokuargs, color='purple'),
    mpf.make_addplot(chikouspan, **ichimokuargs, color='red')
]

# RSI(axes=3) の25%と75%に線を引く
ax3.hlines(xmin=0, xmax=len(df.index), y=25, linewidth=1, color='red')
ax3.hlines(xmin=0, xmax=len(df.index), y=75, linewidth=1, color='red')

# 一目均衡表(axes=1)の先行スパン１と先行スパン２の間を塗りつぶす
ax1.fill_between(x=range(0, len(df.index)), y1=senkospan1.values, y2=senkospan2.values, alpha=0.5, color='gray')

# ローソク足を描く，用意したプロットを渡す
mpf.plot(df, ax=ax1, addplot=ap, style='starsandstripes', type='candle', xrotation=30, ylabel='Price')
mpf.show()
```

このようなチャートが描かれる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/385512/6cb2d6f1-cc25-2668-f359-1a3492889d82.png)

## おわりに

一目均衡表を描くことはできたが，
抵抗帯の未来の部分が描けていないのがいけてない…

次回はこの問題を解決してみたい