noteのタイトル画像

[Puppeteer] python製仮想通貨自動取引bot開発フレームワーク 絶賛強化中

[更新] 2019-04-12 candle, orderbookの取得時のパラメータを指定できるようにしました。
candle取得時のpartialパラメータが機能しない問題に対応しました。

前回ご紹介した 仮想通貨自動取引bot開発フレームワーク「Puppeteer」の強化をしております。
先日まではtickerとorderbook程度しか渡せるものがなかったのですが、徐々に拡張し、現在では

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

runメソッドにticker(Tick情報)、orderbook(板情報)、position(ポジション情報)、balance(資産情報)、candle(ローソク足)を渡すようにしました。
これだけあれば、大抵の取引には十分でしょう。

しかし「APIを発行しまくって遅くなるのでは?」という場合もありますので、JSONの定義ファイルにて、

   "//" : "ticker, orderbook, position, balance, candle のどれを利用するかを指定する。Falseを指定した場合はそのデータは取得しない",
   "USE" : {
        "TICKER" : true,
        "ORDERBOOK" : true,
        "POSITION" : true,
        "BALANCE" : true,
        "CANDLE" : true
   },

どの情報を取得して渡すかを指定できるようにしました。
これで余計な情報をカットして、必要最低限のAPI発行だけに絞れるはずです。

ccxtのfetch_ohlcvメソッドでデータを取得しているのですが、ちょっと予想と違う挙動をするので、一応対策しています。
おそらくですが、まだここら辺が治っていないのでしょう。

Puppeteer(傀儡師)は、つぎのgitlabにてMITライセンスで公開しています。
誰でも自由に使うことができます。
そのかわり無保証です。
(ドキュメントはボチボチ書きます)

gitlabからcloneでPuppeteerを取得すると、初期状態では以下のようなフォルダ構成になっています。

下から2つめのファイル「puppeteer.py」がメインロジックです。
カーソルでハイライトされている「puppets」フォルダが、ユーザ様が独自のストラテジ(戦略)を格納するフォルダです。
1つのストラテジにつき、1つのフォルダを作って、その下にpyファイルとjsonファイルを入れる形をとります。

上記の例では「sample」フォルダを作り、その下に
 ・sample.py ←実行ファイル
 ・sample.json ←定義ファイル
の2つのファイルを置いています。

sampleの内容は以下です。

sample.py

# -*- coding: utf-8 -*-
# ==========================================
# サンプル・ストラテジ
# ==========================================
import datetime

from puppeteer import Puppeteer

# ==========================================
# Puppet(傀儡) クラス
#   param:
#       puppeteer: Puppeteerオブジェクト
# ==========================================
class Puppet(Puppeteer):
    _exchange = None    # 取引所オブジェクト(ccxt.bitmex)
    _logger = None      # logger
    _config = None      # 定義ファイル

    # ==========================================================
    # 初期化
    #   param:
    #       puppeteer: Puppeteerオブジェクト
    # ==========================================================
    def __init__(self, Puppeteer):
        self._exchange = Puppeteer._exchange
        self._logger = Puppeteer._logger
        self._config = Puppeteer._config
        
    # ==========================================================
    # 売買実行
    #   param:
    #       ticker: Tick情報
    #       orderbook: 板情報
    #       position: ポジション情報
    #       balance: 資産情報
    #       candle: ローソク足
    # ==========================================================
    def run(self, ticker, orderbook, position, balance, candle):
        """
        self._logger.debug('last={}'.format(ticker['last']))
        self._logger.debug('bid={}, ask={}'.format(orderbook['bids'][0][0], orderbook['asks'][0][0]))
        self._logger.debug('position={}, avgPrice={}'.format(position[0]['currentQty'], position[0]['avgEntryPrice']))
        self._logger.debug('balance[walletBalance]={}'.format(balance['info'][0]['walletBalance'] * 0.00000001))
        """
        # --------------------------
        # ここに処理を記述します
        # --------------------------
        # ------------------------------------------------------
        # orderbookから最新のbid/askを取得する
        # ------------------------------------------------------
        bid = orderbook['bids'][0][0]
        ask = orderbook['asks'][0][0]
        # 値チェック
        if bid == 0 or ask == 0 or bid == None or ask == None :
            self._logger.error('orderbook error: bid={}, ask={}'.format(bid, ask))
            return

        # ------------------------------------------------------
        # ポジションサイズ、参入価格
        # ------------------------------------------------------
        pos_qty = position[0]['currentQty'] if position[0]['currentQty'] is not None else 0
        avg_price = position[0]['avgEntryPrice'] if position[0]['avgEntryPrice'] is not None else 0

        # ------------------------------------------------------
        # LOT取得
        # ------------------------------------------------------
        lot = self._config['LOT_SIZE']

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
    },

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

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

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

sample.pyはほとんど何もやっていません。
Puppet(傀儡)クラスが1つあり、初期設定ではPuppeteer(傀儡師)が内蔵する
・exchangeオブジェクト(ccxt.bitmexに固定)
・loggerオブジェクト
・configオブジェクト
をPuppetの内部変数に格納しています。
上記の他には、
・discordオブジェクト (discord通知用)
があります。

JSONファイルにはPuppetの動作を定義します。

取引所(今の所bitmex固定)のapiKey,secret
 "APIKEY" : "YOUR_APIKEY",
 "SECRET" : "YOUR_SECRET",

を指定します。

次の指定は変更しないでください。
 "SYMBOL" : "BTC/USD",
 "INFO_SYMBOL" : "XBTUSD",
 "COIN_BASE" : "BTC",
 "COIN_QUOTE" : "USD",
 "PRICE_UNIT" : 0.5,

bitmexのTestNetを使う場合には次のパラメータをTrueに設定します。
 "USE_TESTNET" : true,

Puppetのrunメソッドに渡すデータを制御します (true:取る、false:取らない)
 "USE" : {
  "TICKER" : true,
  "ORDERBOOK" : true,
  "POSITION" : true,
  "BALANCE" : true,
  "CANDLE" : true
 },

Candle(ローソク足)を取得するときの細かい指定をします。
 "CANDLE" : {
  "TIMEFRAME" : "1m",
  "SINCE" : null,
  "LIMIT" : null,
  "REVERSE" : false,
  "PARTIAL" : false
 },

orderbook(板情報)を取得するときの指定をします。
 "ORDERBOOK" : {
  "LIMIT" : null
 },

runメソッドを呼び出す周期を設定します。
 "INTERVAL" :30,
 (この指定は30秒以上を推奨します)

discord通知を行う場合は
 "DISCORD_WEBHOOK_URL" : "",
にURLを設定します。
(使い方は、self._discord.send('メッセージ') です)

これらのパラメータはプログラム中から

self._config['INTERVAL']

のような方法で取得できます。

sampleストラテジを実行させるには

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

をコンソール上で実行させます。

近日中に、自作のストラテジもここにUpしていこうと思います。

多少でも期待していただけたら、スキボタンをポチっとお願いいたします。



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