[Puppeteer] <初めてのPuppeteer> pandasを使ってローソク足を加工してみる
<初めてのPuppeteer>シリーズといいつつ何から書いていいやら迷いますね。
おそらくは誰でも使うであろう「ローソク足」の加工に幅広く使えるpandasの使い方でも書いてみましょう。
python使いの皆さまならおなじみの「pandas」です。
pandasの基本的な使い方は既に色々な人が執筆されているので、私がこと更に書くこともないので基本的な部分はQiita等をご覧ください。
puppeteerで提供しているローソク足を取得する機能は大きく2種類です。
・ccxtで提供しているREST APIで取得する。
タイムフレームは 1分、5分、1時間、1日 の4種類
・puppeteerに搭載したwebsocketでbitmexからリアルタイム通知を受け取り内部的にローソク足を生成する。
タイムフレームは5秒の1種類
データの形は以下のようになっています。
ccxt版(タイムフレームは1分)は
[1560007560000, 7956.5, 7956.5, 7956, 7956, 5896]
[1560007620000, 7956, 7957.5, 7956, 7957.5, 15967]
[1560007680000, 7957.5, 7957.5, 7957, 7957, 2256]
[1560007740000, 7957, 7957.5, 7954, 7954, 5875]
[1560007800000, 7954, 7954.5, 7940, 7940, 53541]
[1560007860000, 7940, 7950.5, 7940, 7950.5, 17462]
[1560007920000, 7950.5, 7952, 7934.5, 7934.5, 97526]
[1560007980000, 7934.5, 7940.5, 7900, 7917, 153158]
・・・(以下、略)・・・
のように
timestamp, open, high, low, close, volume
の値が配列となって取得されます。(timestampはUNIXミリ秒)
websocket版は
{'timestamp': 1560013800, 'open': 7872.5, 'high': 7872.5, 'low': 7872.5, 'close': 7872.5, 'volume': 75, 'buy': 0, 'sell': 75}
{'timestamp': 1560013805, 'open': 7872.5, 'high': 7872.5, 'low': 7872.5, 'close': 7872.5, 'volume': 0, 'buy': 0, 'sell': 0}
{'timestamp': 1560013810, 'open': 7872.5, 'high': 7872.5, 'low': 7872.5, 'close': 7872.5, 'volume': 0, 'buy': 0, 'sell': 0}
{'timestamp': 1560013815, 'open': 7872.5, 'high': 7872.5, 'low': 7872.5, 'close': 7872.5, 'volume': 1, 'buy': 0, 'sell': 1}
{'timestamp': 1560013820, 'open': 7872.5, 'high': 7872.5, 'low': 7872.5, 'close': 7872.5, 'volume': 22, 'buy': 0, 'sell': 22}
{'timestamp': 1560013825, 'open': 7872.5, 'high': 7872.5, 'low': 7872.5, 'close': 7872.5, 'volume': 0, 'buy': 0, 'sell': 0}
{'timestamp': 1560013830, 'open': 7872.5, 'high': 7872.5, 'low': 7872.5, 'close': 7872.5, 'volume': 0, 'buy': 0, 'sell': 0}
{'timestamp': 1560013835, 'open': 7872.5, 'high': 7872.5, 'low': 7872.5, 'close': 7872.5, 'volume': 0, 'buy': 0, 'sell': 0}
{'timestamp': 1560013840, 'open': 7872.5, 'high': 7872.5, 'low': 7872.5, 'close': 7872.5, 'volume': 0, 'buy': 0, 'sell': 0}
{'timestamp': 1560013845, 'open': 7872.5, 'high': 7873, 'low': 7872.5, 'close': 7873, 'volume': 25, 'buy': 25, 'sell': 0}
{'timestamp': 1560013850, 'open': 7873, 'high': 7873, 'low': 7873, 'close': 7873, 'volume': 0, 'buy': 0, 'sell': 0}
・・・(以下、略)・・・
のように
timestamp, open, high, low, close, volume, buy, sell
値がkey-value型となって取得されます。(timestampはUNIX秒)
このままでもデータを扱う上で問題はありませんが、NaN値が含まれていた場合にNaN値を除去したり、volumeの合計を取得したりする場合にpandasを使った方が便利なことが多いです。
そこで、生のローソク足データをpandasに格納することにします。
pandasのDataFrameにローソク足を取り込んだものをprintで出力すると
ccxt版は
open high low close volume
timestamp
2019-06-08 15:38:00 7913.0 7914.0 7913.0 7914.0 5832
2019-06-08 15:39:00 7914.0 7914.0 7911.0 7911.0 16181
2019-06-08 15:40:00 7911.0 7913.0 7913.0 7913.0 285
2019-06-08 15:41:00 7913.0 7913.5 7898.0 7898.0 9006
2019-06-08 15:42:00 7898.0 7913.5 7898.5 7912.0 26836
2019-06-08 15:43:00 7912.0 7912.0 7911.5 7911.5 5005
2019-06-08 15:44:00 7911.5 7912.0 7910.5 7911.5 24145
2019-06-08 15:45:00 7911.5 7911.5 7911.0 7911.0 755
2019-06-08 15:46:00 7911.0 7911.0 7911.0 7911.0 3677
2019-06-08 15:47:00 7911.0 7911.0 7910.5 7911.0 4426
2019-06-08 15:48:00 7911.0 7910.5 7886.5 7910.5 12694
2019-06-08 15:49:00 7910.5 7911.0 7910.5 7910.5 3007
2019-06-08 15:50:00 7910.5 7911.0 7910.5 7910.5 1446
2019-06-08 15:51:00 7910.5 7911.0 7910.5 7910.5 1132
・・・(以下略)・・・
のように。
websocket版は
open high low close volume buy sell
timestamp
2019-06-08 17:23:00 7873.5 7873.5 7873.5 7873.5 9 9 0
2019-06-08 17:23:05 7873.5 7873.5 7873.5 7873.5 0 0 0
2019-06-08 17:23:10 7873.5 7873.5 7873.5 7873.5 0 0 0
2019-06-08 17:23:15 7873.5 7873.5 7873.5 7873.5 0 0 0
2019-06-08 17:23:20 7873.5 7873.5 7873.5 7873.5 0 0 0
2019-06-08 17:23:25 7873.5 7873.5 7873.0 7873.0 1 0 1
2019-06-08 17:23:30 7873.0 7873.0 7873.0 7873.0 5 0 5
2019-06-08 17:23:35 7873.0 7873.0 7873.0 7873.0 0 0 0
2019-06-08 17:23:40 7873.0 7873.0 7873.0 7873.0 0 0 0
2019-06-08 17:23:45 7873.0 7873.5 7873.0 7873.5 9 9 0
2019-06-08 17:23:50 7873.5 7873.5 7873.5 7873.5 0 0 0
2019-06-08 17:23:55 7873.5 7873.5 7873.5 7873.5 0 0 0
・・・(以下略)・・・
のように、綺麗にわかりやすく格納されています。
pandasにデータを取り込んだことで、例えば、
最後から3個分のデータのvolumeを合計したい
と思った時には次のようにプログラムすれば簡単に取得できます。
(df変数のDataFrameが格納されているとします)
df.tail(3)['volume'].sum()
簡単ですね。
それでは最後にccxt版とwebsocket版のpuppetsのソースコードを載せておきます。
みなさんもぜひpuppeteerで試してみてください。
ccxt版
- pupptes/
- sample/
- sample.py
- sample.json
sample.py
# -*- coding: utf-8 -*-
# ==========================================
# サンプル Puppet
# ==========================================
import datetime
import pandas as pd
from puppeteer import Puppeteer
# ==========================================
# Puppet(傀儡) クラス
# param:
# puppeteer: Puppeteerオブジェクト
# ==========================================
class Puppet(Puppeteer):
# ==========================================================
# 初期化
# param:
# puppeteer: Puppeteerオブジェクト
# ==========================================================
def __init__(self, Puppeteer):
self._exchange = Puppeteer._exchange # 取引所オブジェクト(ccxt.bitmex)
self._logger = Puppeteer._logger # logger
self._config = Puppeteer._config # 定義ファイル
# ==========================================================
# 売買実行
# param:
# ticker: Tick情報
# orderbook: 板情報
# position: ポジション情報
# balance: 資産情報
# candle: ローソク足
# ==========================================================
def run(self, ticker, orderbook, position, balance, candle):
# -----------------------------------------------
# Pandasのデータフレームに
# -----------------------------------------------
df = pd.DataFrame(candle,
columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
# -----------------------------------------------
# 日時データをDataFrameのインデックスにする
# -----------------------------------------------
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df = df.set_index('timestamp')
print(df)
print(df.tail(3)['volume'].sum())
sample.json
{
"//" : "===============================================",
"//" : " システムで利用",
"//" : "===============================================",
"//" : "取引所のapiKey, secretを設定します",
"APIKEY" : "YOUR_APIKEY",
"SECRET" : "YOUR_SECRET",
"//" : "bitmex取引所で対応する通貨ペア等を記述",
"SYMBOL" : "BTC/USD",
"INFO_SYMBOL" : "XBTUSD",
"COIN_BASE" : "BTC",
"COIN_QUOTE" : "USD",
"//" : "bitmex取引所の価格の最小幅(0.5ドル)",
"PRICE_UNIT" : 0.5,
"//" : "TestNetを使うか?(使う: true, 使わない: false)",
"USE_TESTNET" : true,
"//" : "ticker, orderbook, position, balance, candle のどれを利用するかを指定する。Falseを指定した場合はそのデータは取得しない",
"USE" : {
"TICKER" : true,
"ORDERBOOK" : true,
"POSITION" : true,
"BALANCE" : true,
"CANDLE" : true
},
"//" : "ローソク足の収集定義。",
"CANDLE" : {
"//" : "ローソク足の足幅を設定する。設定値= 1m, 5m, 1h, 1d",
"TIMEFRAME" : "1m",
"//" : "データ取得開始時刻(UNIXTIME:1ミリ秒)、使用しない場合 もしくは自動の場合は null(None) を指定",
"SINCE" : null,
"//" : "取得件数(未指定:100、MAX:500)",
"LIMIT" : null,
"//" : "True(New->Old)、False(Old->New) 未指定時はFlase",
"REVERSE" : false,
"//" : "True(最新の未確定足を含む)、False(含まない) 未指定はTrue",
"PARTIAL" : false
},
"//" : "板情報の収集定義。",
"ORDERBOOK" : {
"//" : "取得件数(未指定:25、MAX:取引所による?)",
"LIMIT" : null
},
"//" : "websocketを使用するかどうかを指定",
"USE_WEBSOCKET" : false,
"//" : "ログレベルを指定。('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG')",
"LOG_LEVEL" : "INFO",
"//" : "インターバル(botの実行周期)を秒で設定",
"INTERVAL" :60,
"//" : "discord通知用URL",
"DISCORD_WEBHOOK_URL" : "",
"//" : "資産状況通知をするか",
"USE_SEND_BALANCE" : false,
"//" : "===============================================",
"//" : " ユーザで自由に定義",
"//" : "===============================================",
"//" : "",
"XXXX" : null
}
websocket版
- pupptes/
- sample1/
- sample1.py
- sample1.json
sample1.py
# -*- coding: utf-8 -*-
# ==========================================
# サンプル Puppet (websocket)
# ==========================================
import datetime
import pandas as pd
from puppeteer import Puppeteer
# ==========================================
# Puppet(傀儡) クラス
# param:
# puppeteer: Puppeteerオブジェクト
# ==========================================
class Puppet(Puppeteer):
# ==========================================================
# 初期化
# param:
# puppeteer: Puppeteerオブジェクト
# ==========================================================
def __init__(self, Puppeteer):
self._exchange = Puppeteer._exchange # 取引所オブジェクト(ccxt.bitmex)
self._logger = Puppeteer._logger # logger
self._config = Puppeteer._config # 定義ファイル
self._ws = Puppeteer._ws # websocket
self._bitmex = Puppeteer._bitmex # ccxt.bimexラッパーオブジェクト
# ==========================================================
# 売買実行
# param:
# ticker: Tick情報
# orderbook: 板情報
# position: ポジション情報
# balance: 資産情報
# candle: ローソク足
# ==========================================================
def run(self, ticker, orderbook, position, balance, candle):
# Socketの接続が活きている and 強制終了フラグがOFF の限り処理を続ける
if not self._ws.ws.sock and not self._ws.ws.sock.connected and self._ws.is_force_exit():
self._logger.warning('websocket not running / force exit')
return
ws_candle = self._ws.candle()
# ------------------------------------------------------
# DataFrame作成
# ------------------------------------------------------
df = pd.DataFrame(ws_candle,
columns=['timestamp', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell']
)
# ------------------------------------------------------
# 日時データをDataFrameのインデックスにする
# candleのtimestampデータがUNIXTIME(秒)なので、unit='s'を指定する。(ミリ秒なら 'ms'を指定する)
# ------------------------------------------------------
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='s')
df = df.set_index('timestamp')
print(df)
print(df.tail(3)['volume'].sum())
sample1.json
{
"//" : "===============================================",
"//" : " システムで利用",
"//" : "===============================================",
"//" : "取引所のapiKey, secretを設定します",
"APIKEY" : "YOUR_APIKEY",
"SECRET" : "YOUR_SECRET",
"//" : "bitmex取引所で対応する通貨ペア等を記述",
"SYMBOL" : "BTC/USD",
"INFO_SYMBOL" : "XBTUSD",
"COIN_BASE" : "BTC",
"COIN_QUOTE" : "USD",
"//" : "bitmex取引所の価格の最小幅(0.5ドル)",
"PRICE_UNIT" : 0.5,
"//" : "TestNetを使うか?(使う: true, 使わない: false)",
"USE_TESTNET" : true,
"//" : "ticker, orderbook, position, balance, candle のどれを利用するかを指定する。Falseを指定した場合はそのデータは取得しない",
"USE" : {
"TICKER" : false,
"ORDERBOOK" : false,
"POSITION" : false,
"BALANCE" : false,
"CANDLE" : false
},
"//" : "ローソク足の収集定義。",
"CANDLE" : {
"//" : "ローソク足の足幅を設定する。設定値= 1m, 5m, 1h, 1d",
"TIMEFRAME" : "1m",
"//" : "データ取得開始時刻(UNIXTIME:1ミリ秒)、使用しない場合 もしくは自動の場合は null(None) を指定",
"SINCE" : null,
"//" : "取得件数(未指定:100、MAX:500)",
"LIMIT" : null,
"//" : "True(New->Old)、False(Old->New) 未指定時はFlase",
"REVERSE" : false,
"//" : "True(最新の未確定足を含む)、False(含まない) 未指定はTrue",
"PARTIAL" : false
},
"//" : "板情報の収集定義。",
"ORDERBOOK" : {
"//" : "取得件数(未指定:25、MAX:取引所による?)",
"LIMIT" : null
},
"//" : "websocketを使用するかどうかを指定",
"USE_WEBSOCKET" : true,
"//" : "ログレベルを指定。('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG')",
"LOG_LEVEL" : "INFO",
"//" : "インターバル(botの実行周期)を秒で設定",
"INTERVAL" :5,
"//" : "discord通知用URL",
"DISCORD_WEBHOOK_URL" : "",
"//" : "資産状況通知をするか",
"USE_SEND_BALANCE" : false,
"//" : "===============================================",
"//" : " ユーザで自由に定義",
"//" : "===============================================",
"//" : "",
"XXXX" : null
}
楽しいbotライフを!
ソフトウェア・エンジニアを40年以上やってます。 「Botを作りたいけど敷居が高い」と思われている方にも「わかる」「できる」を感じてもらえるように頑張ります。 よろしくお願い致します。