plotlyでキャンドルチャートプロット

# 下準備

## サンプルデータの作成

```python
np.random.seed(9)
from randomwalk import randomwalk
df = randomwalk(60 * 24 * 90, freq='T', tick=0.01, start=pd.datetime(2017, 3, 20)
                ).resample('B').ohlc() + 115  # 90日分の1分足を日足に直す
```

ランダムウォークの作成。
詳しくは[pythonでローソク足(candle chart)の描画](http://qiita.com/u1and0/items/1d9afdb7216c3d2320ef)をご覧ください。

## pd.DataFrame型をStockDataFrame型に変換


```python
from stockstats import StockDataFrame
sdf = StockDataFrame(df.copy())
```

# StockPlotクラスの使用方法

## stockplot.pyのインポート


```python
from stockplot import StockPlot
```


```python
# StockPlotクラスのインスタンス化
x = StockPlot(sdf)
```


```python
# キャンドルチャートのプロット
x.candle_plot()
```
![graph1.png](https://qiita-image-store.s3.amazonaws.com/0/113494/6242ebac-b62a-420d-5833-0a6337e9c4a0.png)

キャンドルチャートが描かれました。

plotlyは基本的に画像をhtml形式で吐き出します。
マウスオーバーすると値が表示されたり、拡大縮小ができます。
わざわざgifファイルを作るのが面倒だったので、ここではpng形式で貼り付けております。

plotlyでぐりぐり動かせるグラフを見たい方は参考までにこちらをご覧ください。

[Qiita - inoory様 - [Python] Plotlyでぐりぐり動かせるグラフを作る](http://qiita.com/inoory/items/12028af62018bf367722)



# 指標

## 指標の追加

指標の追加はリスト型と同様に`append`メソッドを使います。

```python
# 終値25日移動平均線追加
x.append('close_25_sma')
```




    2017-03-20    114.850000
    2017-03-21    114.805000
    2017-03-22    114.886667
    2017-03-23    114.912500
    2017-03-24    114.912000
    2017-03-27    114.940000
    2017-03-28    115.010000
    2017-03-29    115.016250
    2017-03-30    115.062222
    2017-03-31    115.063000
    2017-04-03    115.066364
    2017-04-04    115.082500
    2017-04-05    115.093846
    2017-04-06    115.075714
    2017-04-07    115.093333
    2017-04-10    115.111250
    2017-04-11    115.097059
    2017-04-12    115.071111
    2017-04-13    115.041579
    2017-04-14    115.037000
    2017-04-17    115.039048
    2017-04-18    115.047273
    2017-04-19    115.042609
    2017-04-20    115.075417
    2017-04-21    115.117200
    2017-04-24    115.165600
    2017-04-25    115.194000
    2017-04-26    115.216800
    2017-04-27    115.247600
    2017-04-28    115.262800
                     ...    
    2017-05-08    115.319600
    2017-05-09    115.306800
    2017-05-10    115.313600
    2017-05-11    115.328400
    2017-05-12    115.315600
    2017-05-15    115.297600
    2017-05-16    115.297600
    2017-05-17    115.314800
    2017-05-18    115.337600
    2017-05-19    115.330000
    2017-05-22    115.317200
    2017-05-23    115.296000
    2017-05-24    115.308800
    2017-05-25    115.302000
    2017-05-26    115.282800
    2017-05-29    115.256800
    2017-05-30    115.254000
    2017-05-31    115.238000
    2017-06-01    115.245200
    2017-06-02    115.272800
    2017-06-05    115.289600
    2017-06-06    115.318400
    2017-06-07    115.366000
    2017-06-08    115.425600
    2017-06-09    115.508400
    2017-06-12    115.579200
    2017-06-13    115.636800
    2017-06-14    115.686800
    2017-06-15    115.736000
    2017-06-16    115.798000
    Freq: B, Name: close_25_sma, dtype: float64



`add_indicator`関数の戻り値は`StockDataFrame`クラスで`get`メソッドを使った時と同じ結果が返ってきます。

裏でplotly形式に直され、インスタンス変数self._figに追加されます。


```python
# キャンドルチャートと追加された指標のプロット
x.candle_plot()
```

![graph2.png](https://qiita-image-store.s3.amazonaws.com/0/113494/d7a2f858-6e08-3dd3-30dd-01767e6edee4.png)



終値25日移動平均線が追加されました。


```python
# 終値25日指数移動平均線の追加とプロット
x.append('close_25_ema')
x.candle_plot()
```

![graph3.png](https://qiita-image-store.s3.amazonaws.com/0/113494/f56c8088-6271-30d8-5037-72698ff2b768.png)



先ほど追加された終値25日移動平均線に加え、終値25日指数移動平均線が追加されました。

## 指標の削除

指標の削除はリストの削除と同様に'remove'メソッドを使います。


```python
y = StockPlot(sdf)
# 10,11,12,13足移動平均線
for i in range(10, 14):
    y.append('close_{}_sma'.format(i))
y.candle_plot()
```

![graph4.png](https://qiita-image-store.s3.amazonaws.com/0/113494/a8587873-b5b0-e1dd-77a2-3e1e6f94dece.png)

新たなインスタンスを作成し、10, 11, 12, 13足移動平均線を追加しました。

ここでは省略されていますが、`remove`戻り値は削除したStockDataFrameのカラムです。


```python
# 10, 12足移動平均線の削除
for i in (10, 12):
    y.remove('close_{}_sma'.format(i))
y.candle_plot()
```

![graph5.png](https://qiita-image-store.s3.amazonaws.com/0/113494/f41b6fb7-a410-2006-ccb1-907466ea028a.png)

10足、12足移動平均線だけを指定して削除しました。


# ファイルへのエクスポート

StockPlotクラスはPlotlyのhtmlエクスポートする`plotly.offline.iplot`(Jupyter Notebook形式)で図を吐き出します。
つまり、ファイルへのエクスポートがされません。

ファイルとして吐き出したいときは以下のメソッドを使います。

* html形式: `plotly.offline.plot(figure_or_data, filename=<拡張子htmlにしないとワーニング(勝手に拡張子つけられちゃう)>, )`
* png, svg, jpeg, webp形式: `plotly.offline.plot(figure_or_data, image=<png|svg|jpeg|webp>, imagefilename=<拡張子抜きのファイル名>)`


```python
import plotly.offline as pyo
pyo.plot(y._fig, filename='candle_y.html', validate=False)  # 新しいタブを開いてhtml表示します
```


```python
pyo.plot(y._fig, image='png', image_filename='candle_y')
# 新しいtmpタブを開いて
# imageに指定した拡張子として
# デフォルトのダウンロードディレクトリに保存します
```

# パッケージ紹介

## Plotly

グラフ描画はPlotlyに行わせます。
簡単なことだったら無料で使えます。

インストールは

`conda install plotly`

または

`pip install plotly`

有名だと思うのでここでは言及しません。


## stockstats
データ操作はstockstatsというパッケージを使います。

インストールは

`pip install stockstats`

`stockstats`は金融指標を簡単に取得できる改造pandas.DataFrameクラスです。


```python
# 使い方
np.random.seed(2)
df = pd.DataFrame(np.random.randn(10,4), columns=['open', 'high', 'low', 'close'])
from stockstats import StockDataFrame
sdf = StockDataFrame(df)  # pandasデータフレームをStockDataFrameに入れてあげる
sdf
```




<div>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>open</th>
      <th>high</th>
      <th>low</th>
      <th>close</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>-0.416758</td>
      <td>-0.056267</td>
      <td>-2.136196</td>
      <td>1.640271</td>
    </tr>
    <tr>
      <th>1</th>
      <td>-1.793436</td>
      <td>-0.841747</td>
      <td>0.502881</td>
      <td>-1.245288</td>
    </tr>
    <tr>
      <th>2</th>
      <td>-1.057952</td>
      <td>-0.909008</td>
      <td>0.551454</td>
      <td>2.292208</td>
    </tr>
    <tr>
      <th>3</th>
      <td>0.041539</td>
      <td>-1.117925</td>
      <td>0.539058</td>
      <td>-0.596160</td>
    </tr>
    <tr>
      <th>4</th>
      <td>-0.019130</td>
      <td>1.175001</td>
      <td>-0.747871</td>
      <td>0.009025</td>
    </tr>
    <tr>
      <th>5</th>
      <td>-0.878108</td>
      <td>-0.156434</td>
      <td>0.256570</td>
      <td>-0.988779</td>
    </tr>
    <tr>
      <th>6</th>
      <td>-0.338822</td>
      <td>-0.236184</td>
      <td>-0.637655</td>
      <td>-1.187612</td>
    </tr>
    <tr>
      <th>7</th>
      <td>-1.421217</td>
      <td>-0.153495</td>
      <td>-0.269057</td>
      <td>2.231367</td>
    </tr>
    <tr>
      <th>8</th>
      <td>-2.434768</td>
      <td>0.112727</td>
      <td>0.370445</td>
      <td>1.359634</td>
    </tr>
    <tr>
      <th>9</th>
      <td>0.501857</td>
      <td>-0.844214</td>
      <td>0.000010</td>
      <td>0.542353</td>
    </tr>
  </tbody>
</table>
</div>



見た目に変化はありませんが金融指標を`stockstats`の文法に従って`get`メソッド、またはディクショナリの取得をすると、金融指標のカラムが追加されます。


```python
sdf.get('close_5_sma'); sdf
```




<div>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>open</th>
      <th>high</th>
      <th>low</th>
      <th>close</th>
      <th>close_5_sma</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>-0.416758</td>
      <td>-0.056267</td>
      <td>-2.136196</td>
      <td>1.640271</td>
      <td>1.640271</td>
    </tr>
    <tr>
      <th>1</th>
      <td>-1.793436</td>
      <td>-0.841747</td>
      <td>0.502881</td>
      <td>-1.245288</td>
      <td>0.197491</td>
    </tr>
    <tr>
      <th>2</th>
      <td>-1.057952</td>
      <td>-0.909008</td>
      <td>0.551454</td>
      <td>2.292208</td>
      <td>0.895730</td>
    </tr>
    <tr>
      <th>3</th>
      <td>0.041539</td>
      <td>-1.117925</td>
      <td>0.539058</td>
      <td>-0.596160</td>
      <td>0.522758</td>
    </tr>
    <tr>
      <th>4</th>
      <td>-0.019130</td>
      <td>1.175001</td>
      <td>-0.747871</td>
      <td>0.009025</td>
      <td>0.420011</td>
    </tr>
    <tr>
      <th>5</th>
      <td>-0.878108</td>
      <td>-0.156434</td>
      <td>0.256570</td>
      <td>-0.988779</td>
      <td>-0.105799</td>
    </tr>
    <tr>
      <th>6</th>
      <td>-0.338822</td>
      <td>-0.236184</td>
      <td>-0.637655</td>
      <td>-1.187612</td>
      <td>-0.094264</td>
    </tr>
    <tr>
      <th>7</th>
      <td>-1.421217</td>
      <td>-0.153495</td>
      <td>-0.269057</td>
      <td>2.231367</td>
      <td>-0.106432</td>
    </tr>
    <tr>
      <th>8</th>
      <td>-2.434768</td>
      <td>0.112727</td>
      <td>0.370445</td>
      <td>1.359634</td>
      <td>0.284727</td>
    </tr>
    <tr>
      <th>9</th>
      <td>0.501857</td>
      <td>-0.844214</td>
      <td>0.000010</td>
      <td>0.542353</td>
      <td>0.391392</td>
    </tr>
  </tbody>
</table>
</div>



close_5_sma: 終値の5足移動平均線が追加されました。


```python
sdf['close_5_sma']; sdf
```




<div>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>open</th>
      <th>high</th>
      <th>low</th>
      <th>close</th>
      <th>close_5_sma</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>-0.416758</td>
      <td>-0.056267</td>
      <td>-2.136196</td>
      <td>1.640271</td>
      <td>1.640271</td>
    </tr>
    <tr>
      <th>1</th>
      <td>-1.793436</td>
      <td>-0.841747</td>
      <td>0.502881</td>
      <td>-1.245288</td>
      <td>0.197491</td>
    </tr>
    <tr>
      <th>2</th>
      <td>-1.057952</td>
      <td>-0.909008</td>
      <td>0.551454</td>
      <td>2.292208</td>
      <td>0.895730</td>
    </tr>
    <tr>
      <th>3</th>
      <td>0.041539</td>
      <td>-1.117925</td>
      <td>0.539058</td>
      <td>-0.596160</td>
      <td>0.522758</td>
    </tr>
    <tr>
      <th>4</th>
      <td>-0.019130</td>
      <td>1.175001</td>
      <td>-0.747871</td>
      <td>0.009025</td>
      <td>0.420011</td>
    </tr>
    <tr>
      <th>5</th>
      <td>-0.878108</td>
      <td>-0.156434</td>
      <td>0.256570</td>
      <td>-0.988779</td>
      <td>-0.105799</td>
    </tr>
    <tr>
      <th>6</th>
      <td>-0.338822</td>
      <td>-0.236184</td>
      <td>-0.637655</td>
      <td>-1.187612</td>
      <td>-0.094264</td>
    </tr>
    <tr>
      <th>7</th>
      <td>-1.421217</td>
      <td>-0.153495</td>
      <td>-0.269057</td>
      <td>2.231367</td>
      <td>-0.106432</td>
    </tr>
    <tr>
      <th>8</th>
      <td>-2.434768</td>
      <td>0.112727</td>
      <td>0.370445</td>
      <td>1.359634</td>
      <td>0.284727</td>
    </tr>
    <tr>
      <th>9</th>
      <td>0.501857</td>
      <td>-0.844214</td>
      <td>0.000010</td>
      <td>0.542353</td>
      <td>0.391392</td>
    </tr>
  </tbody>
</table>
</div>



`sdf.get('close_5_sma')`と全く同じです。
getの方がありえない指標を打ち込んだときエラーが発生しません。
どちら良いかは用途次第でしょう。

指標できる指標は以下の通りです。

* change (in percent)
* delta
* permutation (zero based)
* log return
* max in range
* min in range
* middle = (close + high + low) / 3
* SMA: simple moving average
* EMA: exponential moving average
* MSTD: moving standard deviation
* MVAR: moving variance
* RSV: raw stochastic value
* RSI: relative strength index
* KDJ: Stochastic oscillator
* Bolling: including upper band and lower band.
* MACD: moving average convergence divergence. Including signal and histogram.
* CR:
* WR: Williams Overbought/Oversold index
* CCI: Commodity Channel Index
* TR: true range
* ATR: average true range
* line cross check, cross up or cross down.
* DMA: Different of Moving Average (10, 50)
* DMI: Directional Moving Index, including
* +DI: Positive Directional Indicator
* -DI: Negative Directional Indicator
* ADX: Average Directional Movement Index
* ADXR: Smoothed Moving Average of ADX
* TRIX: Triple Exponential Moving Average
* VR: Volatility Volume Ratio

詳しくは公式をご覧ください。

* [github - jealous/stockstats](https://github.com/jealous/stockstats)
* [PyPI - stockstats](https://pypi.python.org/pypi/stockstats)



## stockplot

私が作ったやつです。
stockstatsを楽にplotするためのクラスStockPlotを作成しました。

stockplot.pyのソースコード


```python
import numpy as np
import pandas as pd
# ----------User Module----------
from randomwalk import randomwalk
import stockstats as ss
# ----------Plotly Module----------
from plotly.tools import FigureFactory as FF
import plotly.offline as pyo
import plotly.graph_objs as go
pyo.init_notebook_mode(connected=True)


class StockPlot:
    """StockDataFrameの可視化ツール
    # TODO
    * heikin_plot
    * pop
    * subplot
    """

    def __init__(self, sdf: ss.StockDataFrame):
        self.StockDataFrame = sdf
        self._fig = FF.create_candlestick(self.StockDataFrame.open,
                                         self.StockDataFrame.high,
                                         self.StockDataFrame.low,
                                         self.StockDataFrame.close,
                                         dates=self.StockDataFrame.index)

    def candle_plot(self, filebasename='candlestick_and_trace'):
        """StockDataFrameをキャンドルチャート化する
        引数: dfs: StockDataFrame
        戻り値: plotly plot"""
        self._fig['layout'].update(xaxis={'showgrid': True})
        ax = pyo.iplot(self._fig, filename=filebasename + '.html', validate=False)
        # pyo.plot(self._fig, image='png', image_filename=filebasename, validate=False)
        return ax

    def append(self, indicator):
        indi = self.StockDataFrame.get(indicator)
        plotter = go.Scatter(x=indi.index, y=indi,
                             name=indicator.upper().replace('_', ' '))  # グラフに追加する形式変換
        self._fig['data'].append(plotter)
        return indi

    def remove(self, indicator):
        indi = indicator.lower().replace(' ', '_')
        INDI = indicator.upper().replace('_', ' ')
        rem = self.StockDataFrame.pop(indi)
        for dicc in self._fig['data']:
            if dicc['name'] == INDI:
                self._fig['data'].remove(dicc)
                return rem
```

**github**
[u1and0/stockplot](https://github.com/u1and0/stockplot)

**NEXT**
[Plotlyでぐりぐり動かせる為替チャートを作る(1)](http://qiita.com/u1and0/items/e2273bd8e03c670be45a#_reference-ff96b846704ace2673e8)