noteのタイトル画像

[Puppeteer] <初めてのPuppeteer> pandasを使ってマルチタイムフレームのローソク足を作ってみる

ご無沙汰しております。
BTCが俄然元気になってきましたね。

先日まで動かしていたbot「ヘカトンケイル」は見事に焼かれました(泣)

めちゃくちゃ低原資でしたので、一気に弾切れして、放置を決め込んでいたら、あっさりゼロカットを喰らいました。

しかし、実戦投入型実証実験機としての役割は十分に果たしてくれて、貴重なデータも取れたので良しとしましょう。

次期実戦投入型bot「バーサーカー」にインジケータを2つほど組み込み、試運転をしています。

Puppeteerを作っておいて良かったと思っています。
機能追加がめちゃくちゃ楽ですから。

さて、今日のお題は「マルチタイムフレーム」です。

botでインジケータや各種指標を計算させるときに欠かせない「ローソク足」
bitmexが公開しているAPIで取得可能なローソク足のタイムフレームは、
 1分、5分、1時間、1日
の4種類に限られます。

しかし、bitmexのサイトで確認できるローソク足のタイムフレームの種類は以下に示すようにかなり多いです。

BTCの取引のスイング、デイ、スキャの方々の要望に応えるためか、タイムフレームの数は豊富に用意されています。

botで上記のタイムフレームのデータを用意しようと思ったら、1分、5分、1時間、1日の4種類のローソク足からその他のタイムフレームを「自前で演算」して作成するしかありません。

そんな時に便利な関数がpandasには用意されています。

前回の記事でローソク足データを取得する方法については述べましたので、今回はその取得したローソク足データをベースにして、他の分足に変換してみましょう。

その時に使うpandasのメソッドは

・resample

・agg

の2つです。

resampleに渡す引数は結構数があるので、次のページで調べました。

それでは、前回のプログラムに関数を一つ追加して、1分足を3分足に変換してみましょう。
前回同様、puppetsフォルダの下のsampleプログラムに手を加えます。
今回はccxt版のデータのみ変換してみます。websocket版についてはほとんど変更無いのでご自身で試してみてください。

- puppets/
  - 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(self.change_candleDF(df, '3m'))

       #print(df.tail(3)['volume'].sum())

   # ==========================================================
   # ローソク足の足幅変換
   #   params:
   #       ohlcv: DataFrame (pandas)
   #       resolution: 刻み幅(1m, 3m, 5m, 15m, 30m, 1h, 2h, 3h, 4h, 6h, 12h, 1d, 3d, 1w, 2w, 1M)
   # ==========================================================
   def change_candleDF(self, ohlcv, resolution='1m'):
       # 参考にしたサイト https://docs.pyq.jp/python/pydata/pandas/resample.html
       """
       -------+------+------
       引数    単位    区切り
       -------+------+------
       AS	    年      年初
       A	    年	    年末
       MS	    月	    月初
       M	    月	    月末
       W	    週	    日曜
       D	    日	    0時
       H	    時	    0分
       T,min	分	    0秒
       S	    秒
       L,ms    ミリ秒
       U,us    マイクロ秒
       N,ns    ナノ秒
       """
       
       """
       -------+------+------
       関数    説明
       -------+------+------
       min	    最小
       max	    最大
       sum	    合計
       mean	平均
       first	最初の値
       last    最後の値
       interpolate	補間        
       """

       period = {
           '1m' : '1T', 
           '3m' : '3T', 
           '5m' : '5T', 
           '15m' : '15T', 
           '30m' : '30T', 
           '1h' : '1H', 
           '2h' : '2H', 
           '3h' : '3H', 
           '4h' : '4H', 
           '6h' : '6H', 
           '12h' : '12H', 
           '1d' : '1D', 
           '3d' : '3D', 
           '1w' : '1W', 
           '2w' : '2W', 
           '1M' : '1M'
       }

       if resolution not in period.keys():
           return None

       # 他の分刻みに直す
       df = ohlcv[['open', 'high', 'low', 'close', 'volume']].resample(period[resolution], label='left', closed='left').agg({
               'open': 'first', 
               'high': 'max', 
               'low': 'min', 
               'close': 'last',
               'volume': 'sum'
           })
           # ohlcを再度ohlcに集計するにはaggメソッド
       
       return df

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
}

上記のファイルを配置して、次のコマンドを実行しましょう。
(apikey, secretはご自身のものを設定してください)

python3 puppeteer.py puppets/sample/sample.py puppets/sample/sample.json

実行すると以下のように2つのohlcv(ローソク足)情報がコンソール上にプリントされます。

1分足情報

                        open     high      low    close  volume
timestamp                                                      
2019-06-23 09:40:00  10772.0  10772.0  10771.5  10771.5     686
2019-06-23 09:41:00  10771.5  10816.0  10771.5  10784.5   42509
2019-06-23 09:42:00  10784.5  10785.0  10784.5  10784.5    2610
2019-06-23 09:43:00  10784.5  10786.0  10784.5  10786.0    4372
2019-06-23 09:44:00  10786.0  10785.5  10785.5  10785.5     200
2019-06-23 09:45:00  10785.5  10800.0  10785.5  10785.5    8989
2019-06-23 09:46:00  10785.5  10787.0  10770.0  10770.0   22631
2019-06-23 09:47:00  10770.0  10780.0  10727.0  10757.0   41686
2019-06-23 09:48:00  10757.0  10759.0  10703.5  10703.5   32461
2019-06-23 09:49:00  10703.5  10785.0  10680.0  10738.0   80350
2019-06-23 09:50:00  10738.0  10734.5  10686.0  10719.0   12115
2019-06-23 09:51:00  10719.0  10718.5  10690.0  10691.0   11796
2019-06-23 09:52:00  10691.0  10715.5  10691.0  10700.5   22728
2019-06-23 09:53:00  10700.5  10700.0  10691.5  10700.0   20962
2019-06-23 09:54:00  10700.0  10700.0  10691.0  10697.5   10134
2019-06-23 09:55:00  10697.5  10727.0  10697.5  10718.5   55277

・・・(以下略)・・・

3分足情報

                        open     high      low    close   volume
timestamp                                                       
2019-06-23 09:39:00  10772.0  10816.0  10771.5  10784.5    43195
2019-06-23 09:42:00  10784.5  10786.0  10784.5  10785.5     7182
2019-06-23 09:45:00  10785.5  10800.0  10727.0  10757.0    73306
2019-06-23 09:48:00  10757.0  10785.0  10680.0  10719.0   124926
2019-06-23 09:51:00  10719.0  10718.5  10690.0  10700.0    55486
2019-06-23 09:54:00  10700.0  10727.0  10691.0  10718.5    67624
2019-06-23 09:57:00  10718.5  10718.0  10698.0  10698.0    18109
2019-06-23 10:00:00  10698.0  10700.0  10691.0  10700.0    43952
2019-06-23 10:03:00  10700.0  10700.0  10691.5  10696.5    12419
2019-06-23 10:06:00  10696.5  10697.0  10690.0  10690.0    20724
2019-06-23 10:09:00  10690.0  10700.0  10653.5  10653.5   132172
2019-06-23 10:12:00  10653.5  10677.0  10653.5  10669.0    37998
2019-06-23 10:15:00  10669.0  10682.0  10654.0  10676.0    41637
2019-06-23 10:18:00  10676.0  10700.0  10653.5  10670.0   114294

・・・(以下略)・・・

プログラム中でohlcv(ローソク足)データを変換しているコマンド部分は

df = ohlcv[['open', 'high', 'low', 'close', 'volume']].resample(period[resolution], label='left', closed='left').agg({
       'open': 'first', 
       'high': 'max', 
       'low': 'min', 
       'close': 'last',
       'volume': 'sum'
   })

です。
resampleメソッドで単純にデータを変換すると、open、high、low等のデータがそれぞれ open, high, lowなどに分割されてしまい、うまく行きません。
そのために「agg」を使って値をまとめてあげます。

このように、一度ローソク足データをpandasのデータフレームに格納してしまえば、他の時間足に変換することは容易です。

もっとも、1分足を1時間足に変換する場合は60個の1分足データが1個にまとめられてしまいますので、元データの数が相当多くないと変換後のデータが少しだけになってしまいます。
どれくらいの数のデータが欲しいかによって、変換元データを何分足にするかを決めないとなりません。

みなさんもマルチタイムフレームを使って複数のインジケータで分析してみてはいかがでしょうか。

Puppeteerは無料でご利用になれます。

楽しいbotライフを!


ソフトウェア・エンジニアを40年以上やってます。 「Botを作りたいけど敷居が高い」と思われている方にも「わかる」「できる」を感じてもらえるように頑張ります。 よろしくお願い致します。