見出し画像

"error sign!"が出たら"文字列"を渡せ - Bybit API -

Bybitで動くチャネルブレイクアウト戦略bot(@cRe5520)を作っていて悩まされたのが、CCXTを介してREST APIから返るエラーの {"ret_code":10004,"ret_msg":"error sign!"} でした。

---- scroll ---->

bybit {"ret_code":10004,"ret_msg":"error sign! origin_string[api_key=6xKkxD9bYrqNna&base_price=9080&close_on_trigger=true&order_type=Limit&price=7744&qty=411&recv_window=5000&side=Sell&stop_px=7749&symbol=BTCUSD&time_in_force=GoodTillCancel&timestamp=1588758164866&trigger_by=MarkPrice]","ext_code":"","result":null,"ext_info":null,"time_now":"1588758164.975705"}

コード10004はシグネチャのエラーということなので、当初はオーセンティケーションかエンコードの誤りかと思いました。思いますよね。しかしそのままでも注文が問題なく通ることがあるので変数である引数の誤りだろうと考え直し、様々な条件でポストして結果を比較してみました。

そして分かったのが、注文価格priceを文字列で渡せばよいということでした。

結論:
浮動小数点数である注文価格を文字列で渡せばエラーは出ません。

以上です。解散。

---

お時間とご興味のある方は以下もご覧ください。

case1:    ---- scroll ---->

2020-05-20 18:14:46,687 - DEBUG - post_orders_00: {'market': 'ETH/USD', 'type': 'Limit', 'side': 'Buy', 'size': 5759, 'price': None, 'params': {'stop_px': 217.20000000000002, 'base_price': 213.9, 'close_on_trigger': False, 'trigger_by': 'MarkPrice', 'price': 217.25000000000003}}
2020-05-20 18:14:46,910 - ERROR - entry_orders_error_00[0,0]: bybit {"ret_code":10004,"ret_msg":"error sign! origin_string[api_key=5FFFLcAuinB7br&base_price=213.9&close_on_trigger=false&order_type=Limit&price=217.25&qty=5759&recv_window=10000&side=Buy&stop_px=217.2&symbol=ETHUSD&time_in_force=GoodTillCancel&timestamp=1589966086688&trigger_by=MarkPrice]","ext_code":"","result":null,"ext_info":null,"time_now":"1589966086.790254"}

エラーになるケース1: priceの浮動小数点数の桁数が、契約の呼び値の有効桁数を上回る場合
e.g. {'price': 217.25000000000003}
一般にはこのような数値をそのまま扱っても問題にはなりません。


case2:    ---- scroll ---->

2020-05-21 20:09:15,594 - DEBUG - post_orders_10: {'market': 'BTC/USD', 'type': 'Limit', 'side': 'Sell', 'size': 5038, 'price': None, 'params': {'stop_px': 8119.0, 'base_price': 9352.5, 'close_on_trigger': True, 'trigger_by': 'MarkPrice', 'price': 8119.0}}
2020-05-21 20:09:15,833 - ERROR - exit_orders_error_10[0,0]: bybit {"ret_code":10004,"ret_msg":"error sign! origin_string[api_key=JtD2AHmgQVqM8W&base_price=9352.5&close_on_trigger=true&order_type=Limit&price=8119&qty=5038&recv_window=10000&side=Sell&stop_px=8119&symbol=BTCUSD&time_in_force=GoodTillCancel&timestamp=1590059355594&trigger_by=MarkPrice]","ext_code":"","result":null,"ext_info":null,"time_now":"1590059355.714679"}

エラーになるケース2: priceの値が整数になる場合
e.g. {'price': 8119.0}

case3:    ---- scroll ---->

2020-05-20 00:10:57,882 - DEBUG - amend_orders_LF: {'market': 'BTC/USDT', 'type': 'Limit', 'size': 0.246, 'price': None, 'params': {'stop_px': 6763.0, 'base_price': 9669.5, 'close_on_trigger': False, 'trigger_by': 'MarkPrice', 'price': 6762.5, 'reduce_only': False}, 'side': 'Sell'}
2020-05-20 00:10:58,885 - DEBUG - amend_orders_result_LF: {'info': {'user_id': 110110, 'stop_order_id': '83e03ba-29df-4a3a-5fa5-55850b0ceff2', 'symbol': 'BTCUSDT', 'side': 'Sell', 'order_type': 'Limit', 'price': 6762.5, 'qty': 0.246, 'time_in_force': 'GoodTillCancel', 'order_status': 'Untriggered', 'trigger_price': 6763, 'order_link_id': '', 'created_time': '2020-05-19T15:10:58Z', 'updated_time': '2020-05-19T15:10:58Z'}, 'id': None, 'clientOrderId': None, 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'BTC/USDT', 'type': 'limit', 'side': 'sell', 'price': 6762.5, 'amount': 0.246, 'cost': None, 'average': None, 'filled': None, 'remaining': None, 'status': 'open', 'fee': None, 'trades': None}

エラーにならないケース3: priceの値が、浮動小数点数かつ桁数が規定の呼び値単位に収まっている場合
e.g. {'price': 6762.5}

case4:    ---- scroll ---->

2020-05-20 15:04:25,049 - DEBUG - post_orders_10: {'market': 'BTC/USD', 'type': 'Market', 'side': 'Sell', 'size': 5038, 'price': None, 'params': {'stop_px': 8119.0, 'base_price': 9748.0, 'close_on_trigger': True, 'trigger_by': 'MarkPrice'}}
2020-05-20 15:04:25,265 - DEBUG - post_orders_result_10: {'info': {'user_id': 110110, 'symbol': 'BTCUSD', 'side': 'Sell', 'order_type': 'Market', 'price': 0, 'qty': 5038, 'time_in_force': 'GoodTillCancel', 'stop_order_type': 'Stop', 'trigger_by': 'MarkPrice', 'base_price': 9748, 'order_status': 'Untriggered', 'ext_fields': {'stop_order_type': 'Stop', 'trigger_by': 'MarkPrice', 'base_price': 9748, 'expected_direction': 'Falling', 'trigger_price': 8119, 'close_on_trigger': True, 'op_from': 'api', 'remark': '192.168.0.1', 'o_req_num': 0}, 'leaves_qty': 5038, 'leaves_value': 0.62051976, 'reject_reason': None, 'cross_seq': -1, 'created_at': '2020-05-20T06:04:25.000Z', 'updated_at': '2020-05-20T06:04:25.000Z', 'stop_px': 8119, 'stop_order_id': '3b66e338-dd77-44df-9731-893741e01d26'}, 'id': None, 'clientOrderId': None, 'timestamp': 1589954665000, 'datetime': '2020-05-20T06:04:25.000Z', 'lastTradeTimestamp': None, 'symbol': 'BTC/USD', 'type': 'market', 'side': 'sell', 'price': 0.0, 'amount': 0.62051976, 'cost': 0.0, 'average': None, 'filled': 0.0, 'remaining': 0.62051976, 'status': 'open', 'fee': None, 'trades': None}

エラーにならないケース4: priceを渡さない。すなわち成行注文の場合。この時にstop_pxとbase_priceは整数値であってもエラーにはならない
e.g. {'stop_px': 8119.0, 'base_price': 9748.0}

早い段階で、ケース4のように成行注文ではエラーコード10004が出ないことに気がついたので、とりあえずコード設定の初期値をそっとStopMarketに変更しました。

リリース後もエラーのケースを拾い続け、ケース1から4に分類できるところまでサンプルが集まったところで、まちゅけんさんのnoteのコメント欄を思い出します。てすてすてすさんのコメントです。私、スキしてます。

BybitのREST APIは時にブーリアン型を文字列で渡す必要があるという、

はっ?

と聞き返してしまう貴重な情報です。

""" system'cRe5520' ver.3.3.0520a より抜粋 """

# StopLimitに書き換える
if STOPLIMIT:
    if order_side == 'Buy':
        price = [
            break_price2 + SLIPPAGE,
            break_price1 + SLIPPAGE,
        ]
    elif order_side == 'Sell':
       price = [
            break_price2 - SLIPPAGE,
            break_price1 - SLIPPAGE,
        ]
    for i in range(-2, 0):
        order_params[i]['type'] = 'Limit'
        order_params[i]['params']['price'] = str(price[i])  """ here! """

""" 略 """

exchange.createOrder(
    order_params[i]['market'],
    order_params[i]['type'],
    order_params[i]['side'],
    order_params[i]['size'],
    order_params[i]['price'],       """ NOT here! """
    order_params[i]['params'],      """ here! """
)

そこで、エンドポイントに渡す数値のうちとりあえずpriceだけを文字列にしてみると、StopLimitの場合でも {"ret_code":10004,"ret_msg":"error sign!"} が返ることはなくなりました。動いているので他の数値は数値型のまま渡しています。

""" 更新 2020年5月24日
分かりやすいサンプルに差し替えて見やすく改行しました """

""" 引数 """
2020-05-24 09:06:53,878 - DEBUG - amend_orders_LF: {
    'market': 'ETH/USD', 
    'type': 'Limit', 
    'size': 5564, 
    'price': None,                      """ NOT here! """
    'params': {
        'stop_px': 185.7, 
        'base_price': 206.65, 
        'close_on_trigger': False, 
        'trigger_by': 'MarkPrice', 
        'price': '185.64999999999998'   """ here! """
    }, 
    'side': 'Sell'
}

""" 戻り値 """
2020-05-24 09:06:54,870 - DEBUG - amend_orders_result_LF: {
    'info': {
        'user_id': 110110, 
        'symbol': 'ETHUSD', 
        'side': 'Sell', 
        'order_type': 'Limit',  
        'price': 185.65,                """ here! """
        'qty': 5564, 
        'time_in_force': 'GoodTillCancel', 
        'stop_order_type': 'Stop', 
        'trigger_by': 'MarkPrice', 
        'base_price': 206.65, 
        'order_status': 'Untriggered', 
        'ext_fields': {
            'stop_order_type': 'Stop', 
            'trigger_by': 'MarkPrice', 
            'base_price': 206.65, 
            'expected_direction': 'Falling', 
            'trigger_price': 185.7, 
            'op_from': 'api', 
            'remark': '172.16.0.1', 
            'o_req_num': 0
        }, 
        'leaves_qty': 5564, 
        'leaves_value': 29.96230479, 
        'reject_reason': None, 
        'cross_seq': -1, 
        'created_at': '2020-05-24T00:06:54.000Z', 
        'updated_at': '2020-05-24T00:06:54.000Z', 
        'stop_px': 185.7, 
        'stop_order_id': 'f5ef55d1-66cb-4192-b77e-15a25dfc35c3'
    }, 
    'id': None, 
    'clientOrderId': None, 
    'timestamp': 1590278814000, 
    'datetime': '2020-05-24T00:06:54.000Z', 
    'lastTradeTimestamp': None, 
    'symbol': 'ETH/USD', 
    'type': 'limit', 
    'side': 'sell', 
    'price': 185.65,                    """ here! """
    'amount': 29.96230479, 
    'cost': 0.0, 
    'average': None, 
    'filled': 0.0, 
    'remaining': 29.96230479, 
    'status': 'open', 
    'fee': None, 
    'trades': None
}

ご覧のとおりpriceを文字列で渡すと数値で返してくれます。

""" 引数 """
2020-05-24 09:06:02,271 - DEBUG - amend_orders_LF: {
    'market': 'BTC/USDT', 
    'type': 'Limit', 
    'size': 0.132, 
    'price': None,                          """ post None! """
    'params': {
        'stop_px': 4999.5, 
        'base_price': 9163.5, 
        'close_on_trigger': False, 
        'trigger_by': 'MarkPrice', 
        'price': '4999.0',                  """ post str! """
        'reduce_only': False
    }, 
    'side': 'Sell'
}

""" 戻り値 """
2020-05-24 09:06:03,274 - DEBUG - amend_orders_result_LF: {
    'info': {
        'user_id': 110110, 
        'stop_order_id': 'd1a59533-1192-4192-7974-960e3d3bce7a', 
        'symbol': 'BTCUSDT', 
        'side': 'Sell', 
        'order_type': 'Limit', 
        'price': 4999,                      """ got int! """
        'qty': 0.132, 
        'time_in_force': 'GoodTillCancel', 
        'order_status': 'Untriggered', 
        'trigger_price': 4999.5, 
        'order_link_id': '', 
        'created_time': '2020-05-24T00:06:02Z', 
        'updated_time': '2020-05-24T00:06:02Z'
    }, 
    'id': None, 
    'clientOrderId': None, 
    'timestamp': None, 
    'datetime': None, 
    'lastTradeTimestamp': None, 
    'symbol': 'BTC/USDT', 
    'type': 'limit', 
    'side': 'sell', 
    'price': 4999.0,                        """ got float! """
    'amount': 0.132, 
    'cost': None, 
    'average': None, 
    'filled': None, 
    'remaining': None, 
    'status': 'open', 
    'fee': None, 
    'trades': None
}

同じ値でも整数で戻ったり浮動小数点数で戻ったり。

なおbool値もbool型のまま渡していますが、エラーなく通っています。

(引用したログは実際のものですがidやapi_keyを加工しています)

RESOLUTION = '1d' FIRST_PERIOD_STICKS = 14 SECOND_PERIOD_STICKS = 3 MARKET_ID = 'ETHUSD' OC_MODE = True