noteのタイトル画像

[Puppeteer] OHLCVデータの足幅を別の足幅に変更する

以前、pythonのpandasモジュールを使ってSMA,EMAを計算させてみました。
pandasは結構色々なことができるので、今回は

bitmexのOHLCVデータの足幅を別の足幅に変更する小技

を紹介したいと思います。
例によってPuppeteerのPuppetとして実装しています。
Puppeteerの取得は、こちらをご覧になって行ってください。
(MITライセンスで無償でご利用いただけます)

Puppeteerをgitからcloneした後、puppetsフォルダの下に

- pupptes/
  - ohlcv/
    - ohlcv.py
    - ohlcv.json

のように「ohlcv」フォルダと「ohlcv.py」「ohlcv.json」ファイルを作成します。

● ohlcv.py

# -*- coding: utf-8 -*-
# ==========================================
# サンプル・ストラテジ
# ==========================================
import numpy as np
import pandas as pd

from puppeteer import Puppeteer

# ==========================================
# Puppet(傀儡) クラス
#   param:
#       puppeteer: Puppeteerオブジェクト
# ==========================================
class Puppet(Puppeteer):
    _exchange = None    # 取引所オブジェクト(ccxt.bitmex)
    _logger = None      # logger
    _config = None      # 定義ファイル
    _discord = None     # discord
    _name = 'ohlcv'     # 自分の名称

    # ==========================================================
    # 初期化
    #   param:
    #       puppeteer: Puppeteerオブジェクト
    # ==========================================================
    def __init__(self, Puppeteer):
        self._exchange = Puppeteer._exchange
        self._logger = Puppeteer._logger
        self._config = Puppeteer._config
        self._discord = Puppeteer._discord

    # ==========================================================
    # 売買実行
    #   param:
    #       ticker: Tick情報
    #       orderbook: 板情報
    #       position: ポジション情報
    #       balance: 資産情報
    #       candle: ローソク足
    # ==========================================================
    def run(self, ticker, orderbook, position, balance, candle):

        # --------------------------
        # ここに処理を記述します
        # --------------------------

        df_ohlcv1m = self.get_candleDF(candle)

        # for DEBUG
        self._logger.debug(df_ohlcv1m)

        df_ohlcv15m = self.change_candleDF(df_ohlcv1m, '15m')

        # for DEBUG
        self._logger.debug(df_ohlcv15m)

    # ==========================================================
    # ローソク足DataFrame
    #   params:
    #       candle: ローソク足の配列[timestamp(UNIXTIME(ミリ秒), open, high, low, close, volume)]
    #   return:
    #       df: ローソク足DataFrame (pandas)
    # ==========================================================
    def get_candleDF(self, candle):
        # ------------------------------------------------------
        # Pandasのデータフレームに
        # ------------------------------------------------------
        df = pd.DataFrame(candle,
                  columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])

        # ------------------------------------------------------
        # 日時データをDataFrameのインデックスにする
        # ------------------------------------------------------
        df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms') # UNIX時間(ミリ秒)を変換
        df = df.set_index('timestamp')

        return df

    # ==========================================================
    # ローソク足の足幅変換
    #   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	分	0秒
        S	秒
        """
        
        """
        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 = pd.concat([ohlcv[['open', 'high', 'low', 'close']].resample(period[resolution], label='left', closed='left').agg({'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last'}),
                        ohlcv['volume'].resample(period[resolution], label='left', closed='left').sum()], 
                        axis=1
                    )  # ohlcを再度ohlcに集計するにはaggメソッド
        
        return df

● ohlcv.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" : 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
    },

    "//" : "インターバルを秒で設定",
    "INTERVAL" :60,

    "//" : "discord通知用URL",
    "DISCORD_WEBHOOK_URL" : "",

    "//" : "===============================================",
    "//" : " ユーザで自由に定義",
    "//" : "===============================================",
    "//" : "売買するサイズ",
    "LOT_SIZE" :50
}

上記の内容をコピペで記述してください。

ohlcv.jsonの apikey, secret にbitmex取引所のapikey,secretを設定します。

puppeteerフォルダに移動して、コンソールから

python3 puppeteer.py puppets/ohlcv/ohlcv.py puppets/ohlcv/ohlcv.json

と実行します。

以下のような結果が出力されると思います。
1つ目は1分足のohlcv、2つ目は15分足のohlcvです。

1分足

2019-04-21 02:21:03, DEBUG   ,                        open    high     low   close    volume
timestamp                                                    
2019-04-20 15:42:00  5308.0  5308.0  5307.5  5307.5    2321.0
2019-04-20 15:43:00  5307.5  5307.5  5307.5  5307.5       0.0
2019-04-20 15:44:00  5307.5  5307.5  5307.5  5307.5       1.0
2019-04-20 15:45:00  5307.5  5307.5  5307.5  5307.5       0.0
2019-04-20 15:46:00  5307.5  5307.5  5307.0  5307.5    1021.0
2019-04-20 15:47:00  5307.5  5307.5  5307.5  5307.5       0.0
2019-04-20 15:48:00  5307.5  5317.0  5295.5  5305.5   67271.0
2019-04-20 15:49:00  5305.5  5305.5  5305.5  5305.5       0.0
2019-04-20 15:50:00  5305.5  5317.5  5303.0  5309.0  111805.0
2019-04-20 15:51:00  5309.0  5319.0  5295.0  5309.5  124239.0
2019-04-20 15:52:00  5309.5  5319.5  5293.5  5306.5  120170.0
2019-04-20 15:53:00  5306.5  5307.0  5307.0  5307.0      26.0
2019-04-20 15:54:00  5307.0  5318.0  5293.5  5306.0  108303.0
2019-04-20 15:55:00  5306.0  5306.5  5306.0  5306.5      29.0
2019-04-20 15:56:00  5306.5  5306.5  5306.0  5306.5    1255.0
2019-04-20 15:57:00  5306.5  5306.5  5306.0  5306.5    4000.0
2019-04-20 15:58:00  5306.5  5306.5  5306.0  5306.0      22.0
2019-04-20 15:59:00  5306.0  5306.0  5306.0  5306.0       0.0
2019-04-20 16:00:00  5306.0  5306.0  5304.5  5304.5    5800.0
2019-04-20 16:01:00  5304.5  5306.5  5304.0  5304.0    1613.0
2019-04-20 16:02:00  5304.0  5304.5  5304.5  5304.5       1.0
2019-04-20 16:03:00  5304.5  5304.0  5304.0  5304.0      61.0
2019-04-20 16:04:00  5304.0  5304.5  5304.5  5304.5      33.0
2019-04-20 16:05:00  5304.5  5304.5  5304.0  5304.5    1015.0
2019-04-20 16:06:00  5304.5  5304.5  5304.0  5304.0     410.0
2019-04-20 16:07:00  5304.0  5304.5  5304.0  5304.0      20.0
2019-04-20 16:08:00  5304.0  5304.5  5304.0  5304.0     340.0
2019-04-20 16:09:00  5304.0  5304.5  5304.5  5304.5      21.0
2019-04-20 16:10:00  5304.5  5304.5  5304.0  5304.0    4850.0
2019-04-20 16:11:00  5304.0  5304.5  5304.5  5304.5       2.0
...                     ...     ...     ...     ...       ...
2019-04-20 16:52:00  5307.0  5307.5  5304.5  5304.5   12414.0
2019-04-20 16:53:00  5304.5  5307.5  5303.5  5304.0    8362.0
2019-04-20 16:54:00  5304.0  5304.5  5304.0  5304.5     937.0
2019-04-20 16:55:00  5304.5  5305.5  5304.5  5304.5    4314.0
2019-04-20 16:56:00  5304.5  5304.5  5304.0  5304.5    2639.0
2019-04-20 16:57:00  5304.5  5306.0  5304.0  5306.0    6700.0
2019-04-20 16:58:00  5306.0  5308.0  5306.0  5308.0    4205.0
2019-04-20 16:59:00  5308.0  5307.0  5306.5  5306.5       2.0
2019-04-20 17:00:00  5306.5  5307.0  5306.5  5307.0      15.0
2019-04-20 17:01:00  5307.0  5306.5  5306.5  5306.5     350.0
2019-04-20 17:02:00  5306.5  5308.5  5306.5  5308.5    3057.0
2019-04-20 17:03:00  5308.5  5308.0  5307.0  5308.0     860.0
2019-04-20 17:04:00  5308.0  5308.5  5308.5  5308.5     200.0
2019-04-20 17:05:00  5308.5  5308.5  5308.0  5308.0     592.0
2019-04-20 17:06:00  5308.0  5308.5  5308.0  5308.5    1010.0
2019-04-20 17:07:00  5308.5  5308.0  5308.0  5308.0     155.0
2019-04-20 17:08:00  5308.0  5308.0  5308.0  5308.0       0.0
2019-04-20 17:09:00  5308.0  5308.0  5308.0  5308.0       0.0
2019-04-20 17:10:00  5308.0  5308.0  5308.0  5308.0       0.0
2019-04-20 17:11:00  5308.0  5308.0  5308.0  5308.0       0.0
2019-04-20 17:12:00  5308.0  5308.0  5308.0  5308.0       0.0
2019-04-20 17:13:00  5308.0  5308.5  5308.0  5308.5     250.0
2019-04-20 17:14:00  5308.5  5308.0  5306.5  5306.5    1310.0
2019-04-20 17:15:00  5306.5  5307.0  5304.0  5304.0    6794.0
2019-04-20 17:16:00  5304.0  5306.5  5306.0  5306.0    4280.0
2019-04-20 17:17:00  5306.0  5306.0  5306.0  5306.0       0.0
2019-04-20 17:18:00  5306.0  5306.0  5306.0  5306.0    2866.0
2019-04-20 17:19:00  5306.0  5306.5  5306.0  5306.0       2.0
2019-04-20 17:20:00  5306.0  5306.5  5305.5  5306.5     509.0
2019-04-20 17:21:00  5306.5  5306.0  5305.5  5305.5    1055.0

[100 rows x 5 columns]

15分足

2019-04-21 02:21:03, DEBUG   ,                        open    high     low   close    volume
timestamp                                                    
2019-04-20 15:30:00  5308.0  5308.0  5307.5  5307.5    2322.0
2019-04-20 15:45:00  5307.5  5319.5  5293.5  5306.0  538141.0
2019-04-20 16:00:00  5306.0  5306.5  5304.0  5305.0   17695.0
2019-04-20 16:15:00  5305.0  5313.0  5294.0  5304.5  198628.0
2019-04-20 16:30:00  5304.5  5310.0  5304.0  5308.5   30591.0
2019-04-20 16:45:00  5308.5  5310.5  5303.5  5306.5   64034.0
2019-04-20 17:00:00  5306.5  5308.5  5306.5  5306.5    7799.0
2019-04-20 17:15:00  5306.5  5307.0  5304.0  5305.5   15506.0

足幅の変更にはpandasの「resample」メソッドを使っています。
このメソッドはなかなか優秀です。
tickerの価格データをohlcデータに変更するときに「ohlcメソッド」を使うケースはあると思いますが、すでにohlc形式データになっているDataFrameにohlcメソッドを適用すると、へんてこりんなことになるので、

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

のようにして、ohlcデータを加工します。

参考にしたサイトは

です。

今回volumeを別途算出し、わざわざconcatで連結していますが、そんなことをせずに

.agg({'open': 'first',
      'high': 'max',
      'low': 'min',
      'close': 'last',
      'volume': 'sum'})

とすれば良いことに後から気がつきました。

ですが、DataFrameの連結方法を勉強する意味(axisの考え方がいまいち良く理解できていないので)でも、このまま掲載しておきます。

楽しいbotライフを!




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