[python] 仮想通貨APIからローソク足チャートを作る簡単な方法



仮想通貨のAPIから日足データを取得してローソク足チャートを作ろうと調べてみたら、意外と簡単にできたので記事にしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/870772/ad172488-1bae-3501-9d69-212d385bf68b.png)

大まかな手順は以下の通り

1. APIからデータ取得
2. データをpandasのDataFrameに変換、成形
3. mplfinanceで描画

ローソク足チャートを作るには描画部分で色々と複雑なのですが、mplfinanceというパッケージを使ったら簡単でした。

- [matplotlib/mplfinance: Financial Markets Data Visualization using Matplotlib](https://github.com/matplotlib/mplfinance)
- [pandas, Matplotlib（mplfinance）でローソク足チャートを作成 | note.nkmk.me](https://note.nkmk.me/python-pandas-matplotlib-candlestick-chart/)

チャート画像を保存する方法もありますが、ここではJupiterを使う前提で行います。

完成したコードは以下の通りです。

```python
#%%
import json
import urllib.parse
import urllib.request
import pandas as pd
import mplfinance as mpf

# 1. APIからデータ取得
def get_api(symbol: str, year: int) -> str:
    endPoint = "https://api.coin.z.com/public"
    klines = "/v1/klines"
    params = {"symbol": symbol, "interval": "1day", "date": year}
    url = endPoint + klines + "?" + urllib.parse.urlencode(params)
    req = urllib.request.Request(url)
    with urllib.request.urlopen(req) as response:
        return response.read()

# 2. データをpandasのDataFrameに変換、成形
def json_to_df(json_str: str) -> pd.DataFrame:
    d = json.loads(json_str)
    df = pd.json_normalize(d, record_path="data")
    df = df.astype("float64")
    df["openTime"] = pd.to_datetime(df["openTime"], unit="ms")
    df.set_index("openTime", inplace=True)
    df.columns = ["Open", "High", "Low", "Close", "Volume"]
    return df.tz_localize("UTC").tz_convert("Asia/Tokyo")

# 3. mplfinanceで描画
def plot(symbol: str, year: int, length: int) -> None:
    json_str = get_api(symbol, year)
    df = json_to_df(json_str)
    kwargs = dict(type="candle", volume=True, figratio=(12, 4))
    mpf.plot(df[-length:], **kwargs, style="yahoo")


plot("ETH", 2021, 50)
```

いくつかポイントがあるので解説します。

### 1. APIからデータ取得

```python
def get_api(symbol: str, year: int) -> str:
    endPoint = "https://api.coin.z.com/public"
    klines = "/v1/klines"
    params = {"symbol": symbol, "interval": "1day", "date": year}
    url = endPoint + klines + "?" + urllib.parse.urlencode(params)
    req = urllib.request.Request(url)
    with urllib.request.urlopen(req) as response:
        return response.read()
```
GMOコインのAPIを使っています。

- [KLine情報の取得 – APIドキュメント| GMOコイン](https://api.coin.z.com/docs/#klines)

特に難しいことはしていません。取得したJSON文字列をそのまま返しています。

### 2. データをpandasのDataFrameに変換、成形

```python
def json_to_df(json_str: str) -> pd.DataFrame:
    d = json.loads(json_str)
    df = pd.json_normalize(d, record_path="data")
    df = df.astype("float64")
    df["openTime"] = pd.to_datetime(df["openTime"], unit="ms")
    df.set_index("openTime", inplace=True)
    df.columns = ["Open", "High", "Low", "Close", "Volume"]
    return df.tz_localize("UTC").tz_convert("Asia/Tokyo")
```

まず`json.loads`で文字列をオブジェクト化します。

取得したJSONは以下のような形式です。

```json
{
  "status": 0,
  "data": [
    {
      "openTime": "1609448400000",
      "open": "2976000",
      "high": "3035000",
      "low": "2960005",
      "close": "3016650",
      "volume": "420.6594"
    },
    {
      "openTime": "1609534800000",
      "open": "3012280",
      "high": "3437650",
      "low": "3000000",
      "close": "3300001",
      "volume": "838.9068"
    },#　以下略
  ]
}
```
`data`の部分だけ欲しいので取り出します。

```python
df = pd.json_normalize(d, record_path="data")
```

`df = df.astype("float64")`で型をfloat64にしています。bitcoinなんかはintで良さそうなのですが、銘柄によっては日本円換算で1円以下のものもあるのでfloatにしています。

- [pandasのデータ型dtype一覧とastypeによる変換（キャスト） | note.nkmk.me](https://note.nkmk.me/python-pandas-dtype-astype/)

次にAPIの`openTime`をTimestamp型にしてからindexにします。
`pd.to_datetime`で型変換しますが、APIの`openTime`はUNIX時間のミリ秒なので引数に`unit="ms"`と加えます。

- [Pandasで時間や日付データに変換するto_datetime関数の使い方 - DeepAge](https://deepage.net/features/pandas-to-datetime.html)

`df.set_index`でインデックスにします。

```python
df["openTime"] = pd.to_datetime(df["openTime"], unit="ms")
df.set_index("openTime", inplace=True)
```

mplfinanceでチャート化する時に読み込めるようにカラム名を変更、最後にタイムゾーンを設定してdataframeの成形は完了です。

```python
df.columns = ["Open", "High", "Low", "Close", "Volume"]
return df.tz_localize("UTC").tz_convert("Asia/Tokyo")
```


```txt
                               Open      High       Low     Close    Volume
openTime                                                                   
2021-01-01 06:00:00+09:00   76525.0   77695.0   73635.0   75270.0   2512.41
2021-01-02 06:00:00+09:00   75259.0   80970.0   74100.0   79199.0   3681.04
2021-01-03 06:00:00+09:00   78200.0  100000.0   77761.0   97404.0   7977.53
2021-01-04 06:00:00+09:00   97893.0  120419.0   90315.0  106086.0  18817.36
2021-01-05 06:00:00+09:00  105853.0  116900.0  100535.0  113100.0   7078.96
```
## 3. mplfinanceで描画


```python
def plot(symbol: str, year: int, length: int) -> None:
    json_str = get_api(symbol, year)
    df = json_to_df(json_str)
    kwargs = dict(type="candle", volume=True, figratio=(12, 4))
    mpf.plot(df[-length:], **kwargs, style="yahoo")
```

先程の関数を使い、APIから取得してdataframeに変換します。
あとは`mpf.plot`するだけです。`style="yahoo"`としていますが、ここで色々なデザインを設定できます。

- [mplfinance/styles.ipynb at master · matplotlib/mplfinance](https://github.com/matplotlib/mplfinance/blob/master/examples/styles.ipynb)

## 実行

```python
plot("ETH", 2021, 50)
```

この例ではイーサリアムの2021年データを取得して50日分表示しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/870772/ad172488-1bae-3501-9d69-212d385bf68b.png)

事前に`pip install mplfinance`するのを忘れるとエラーが出るので注意。