CryptowatchのAPIを使用したBTC価格の取得と保存



# やること
- [CryptowatchのAPI](https://cryptowatch.jp/docs/api)を使用して，ビットコインの価格データ(始値・高値・安値・終値)を取得し，csv形式で保存します．
- n番煎じ？聞こえません！！！！！！！！！$ (n \geq 2, n \in N) $

# 環境
- Python3系

# 単刀直入にソースコード

```getBTCprice.py
#coding: utf-8

#sys.stderr.writeはただの標準エラー出力によるログなので消しても動く．
import sys
import urllib.request
from datetime import datetime, timedelta, timezone
import json

#python3 getBTCprice.py [period] [start(YYYY-mm-dd)] [end(YYYY-mm-dd/0)] [csvfile]

if len(sys.argv) != 5 or (sys.argv[1] != "1m" and sys.argv[1] != "5m" and sys.argv[1] != "1h" and sys.argv[1] != "4h" and sys.argv[1] != "1d" and sys.argv[1] != "1w"):
    sys.stderr.write("コマンドライン引数が不正です．\n")
    sys.exit()

#timezone
JST = timezone(timedelta(hours=+9), 'JST')

#取引所等パラメタ指定
market = "bitflyer/"
pricetype = "btcjpy/"
periodslist = {"1m":"60", "5m":"300", "1h":"3600", "4h":"14400", "1d":"86400", "1w":"604800"}
periods = ""#データの間隔

#期間指定
after = datetime.now(JST).timestamp()#ほんの初期化
#ここから
tstr = sys.argv[2].split("-")
after = datetime(int(tstr[0]), int(tstr[1]), int(tstr[2]), 0, 0, 0, 0, JST).timestamp()#コマンドライン引数の日付
#ここまで
before = datetime.now(JST).timestamp()#デフォルトは現在時刻
if sys.argv[3] != "0":
    tstr = sys.argv[3].split("-")
    before = datetime(int(tstr[0]), int(tstr[1]), int(tstr[2]), 0, 0, 0, 0, JST).timestamp()#コマンドライン引数の日付

#範囲・間隔・url指定
periods = periodslist[sys.argv[1]]
url = "https://api.cryptowat.ch/markets/" + market + pricetype + "ohlc?periods=" + periods + "&after=" + str(int(after)) + "&before=" + str(int(before))

#ただのログ
sys.stderr.writelines("after:" + str(datetime.fromtimestamp(after)) + "(" + str(after) + ")" + "\n")
sys.stderr.writelines("before:" + str(datetime.fromtimestamp(before)) + "(" + str(before) + ")" + "\n")
sys.stderr.write(url + "\n")

#apiにリクエストを送信し，レスポンスをcsv形式に変換し標準出力する．
with urllib.request.urlopen(url) as response:
    dat = response.read().decode("utf-8")
    dat = json.loads(dat)
    rows = dat['result'][periods]
    #売買代金を除いてcsvに出力したい．
    fp = open(sys.argv[4], "w")
    fp.write("time,start,high,low,close,Volume\n")
    for row in rows:#type(row)=list[int,int,int,int,int,float,float]
        dt = str(datetime.fromtimestamp(row[0])).split(" ")
        #価格取得失敗時には飛ばす
        if row[1] == 0 or row[2] == 0 or row[3] == 0 or row[4] == 0:
            pass
        #メンテ中の謎データは飛ばす
        elif dt[1] == "04:05:00" or dt[1] == "04:10:00":
            pass
        else:
            fp.write(str(datetime.fromtimestamp(row[0])) + "," + str(int(row[1])) + "," + str(int(row[2])) + "," + str(int(row[3])) + "," + str(int(row[4])) + "," + str(row[5]) + "\n")
    
    status = response.getcode()
    sys.stderr.write("Status code: " + str(status) + "\n")
    headers = response.info()
    sys.stderr.write(str(headers))

```


# 使い方

```
python3 getBTCprice.py [period] [start] [end/0] [csvfile]
```

## コマンドライン引数について
- period : データの間隔を指します．次のコマンドライン引数で指定する開始日から，
  - 1m : 1分ごと(1分足)
  - 5m : 5分ごと(5分足)
  - 1h : 1時間ごと(1時間足)
  - 4h : 4時間ごと(4時間足)
  - 1d : 1日(24時間)ごと(日足)
  - 1w : 1週間(7日)ごと(週足)

の価格データを取得します．

- start : 指定された日付から価格データを取得します．日付の形式は，YYYY-mm-dd(2019年1月1日ならば2019-01-01)です．

- end : 指定された日付までの価格データを取得します．日付の形式は`[start]`と同様です．このコマンドライン引数に，`0`を指定した場合は，現在日時までの価格データが取得されます．

- csvfile : 出力先のcsvファイルのディレクトリを指します．

## 使用上の注意
- およそ1年8ヶ月以前のデータは取得できません．(APIの仕様?)
- およそ6000時点以前(1分ならおよそ6000分以上過去)のデータは取得できません．(APIの仕様?)
- APIに送信するリクエストとしましては，`start 00:00:00`から`end 00:00:00`までの価格データですが，データの間隔が`1m`，`5m`ではうまく取得できませんでした．(`start 00:00:00`以前からのデータを取得し，`end 00:00:00`まで取得しきれない．)

# 仕様・解説
　ソースコードの解説が主となります．

## リクエストの送信
APIによるリクエストの送信方法は以下の通り．

```
https://api.cryptowat.ch/markets/(取引所)/(取得対象データ)/(データの種類)?periods=(期間[sec])&after=(取得開始日時のUNIX時間)&before=(取得終了日時のUNIX時間)
```

ソースコード上での指定は以下の通り．詳細はコメントを参照ください．

```Python
#取引所等パラメタ指定
market = "bitflyer/" #取引所bitFlyer
pricetype = "btcjpy/" #取得対象データはBTC/JPY
periodslist = {"1m":"60", "5m":"300", "1h":"3600", "4h":"14400", "1d":"86400", "1w":"604800"} #このAPIでの間隔の単位は[秒]
periods = "" #データの間隔

#期間指定
after = datetime.now(JST).timestamp() #ほんの初期化
#ここから
tstr = sys.argv[2].split("-") #コマンドライン引数から年，月，日の値をsplit()で取得
after = datetime(int(tstr[0]), int(tstr[1]), int(tstr[2]), 0, 0, 0, 0, JST).timestamp() #コマンドライン引数の日付
#ここまで
before = datetime.now(JST).timestamp() #デフォルトは現在時刻
#終了日も指定している場合
if sys.argv[3] != "0":
    tstr = sys.argv[3].split("-") #コマンドライン引数から年，月，日の値をsplit()で取得
    before = datetime(int(tstr[0]), int(tstr[1]), int(tstr[2]), 0, 0, 0, 0, JST).timestamp() #コマンドライン引数の日付


#範囲・間隔・url指定
periods = periodslist[sys.argv[1]]
url = "https://api.cryptowat.ch/markets/" + market + pricetype + "ohlc?periods=" + periods + "&after=" + str(int(after)) + "&before=" + str(int(before))

```

データの種類 : ohlcに関しては，始値(Open)・高値(High)・安値(Low)・終値(Close)の4種類のデータを取得することを指します．

## レスポンスとデータ保存．
[レスポンスの例(日足 2019-01-01 00:00:00から2019-02-01 00:00:00の実行結果)](https://api.cryptowat.ch/markets/bitflyer/btcjpy/ohlc?periods=86400&after=1546268400&before=1548946800)
ソースコードを通して解説します．分かりにくかったらスンマセン．

```Python
#apiにリクエストを送信し，レスポンスをcsv形式に変換し標準出力する．
with urllib.request.urlopen(url) as response:
    dat = response.read().decode("utf-8") #レスポンスの取得，json形式のデータ構造ではあるが文字列である．
    dat = json.loads(dat) #文字列のレスポンスをjsonレスポンス通りの辞書型に変換
    rows = dat['result'][periods] #価格に関するデータのみを選択
    #売買代金を除いてcsvに出力したい．
    fp = open(sys.argv[4], "w")
    fp.write("time,start,high,low,close,Volume\n")
    for row in rows: #type(row)=list[int(UNIX時間),int(始値),int(高値),int(安値),int(終値),float(出来高),float(売買代金)]
        dt = str(datetime.fromtimestamp(row[0])).split(" ")
        #価格取得失敗時には飛ばす
        if row[1] == 0 or row[2] == 0 or row[3] == 0 or row[4] == 0:
            pass
        #メンテ中の謎データは飛ばす
        elif dt[1] == "04:05:00" or dt[1] == "04:10:00":
            pass
        else:
            fp.write(str(datetime.fromtimestamp(row[0])) + "," + str(int(row[1])) + "," + str(int(row[2])) + "," + str(int(row[3])) + "," + str(int(row[4])) + "," + str(row[5]) + "\n")
    
    #以下単なるステータスコードとレスポンスヘッダ
    status = response.getcode()
    sys.stderr.write("Status code: " + str(status) + "\n")
    headers = response.info()
    sys.stderr.write(str(headers))

```

# 終わりに
暗号通貨が近い将来どのように扱われているのか気になって夜と朝方しか眠れません．