ccxt: Bybit V5 - buggy stoploss behavior
Operating System
linux
Programming Languages
Python
CCXT Version
4.0.101
Description
Creating a stop-order on bybit futures now requires a slightly different paremter (we used to use stopPrice - which is now triggering a “params error: TriggerDirection invalid” error.
Now adopting this to stopLossPrice is not a problem - but if i create an order with stopLossPrice, i’d expect to get this same price back. Unfortunately, that’s not the case, and stopLossPrice fails to parse.
import ccxt
exchange = ccxt.bybit({
'apiKey': '<yourApiKey>',
'secret': '<yoursecret>',
'options': {'defaultType': 'swap'}
})
pair = 'ETH/USDT:USDT'
params = {
'stopPrice': 1401,
}
# o = exchange.create_order(pair, 'limit', 'sell', amount=0.01, price=1401, params=params)
# Exception (see below detail block) for output
# Moving on
params = {
'stopLossPrice': 1401,
}
o = exchange.create_order(pair, 'limit', 'sell', amount=0.01, price=1401, params=params)
o
exchange.fetch_order(o['id'], pair)
Output
Exception output ...
fetch Request: bybit POST https://api.bybit.com/v5/order/create RequestHeaders: {'Content-Type': 'application/json', 'X-BAPI-API-KEY': '', 'X-BAPI-TIMESTAMP': '1695399145735', 'X-BAPI-RECV-WINDOW': '5000', 'X-BAPI-SIGN': '', 'Referer': 'CCXT', 'User-Agent': 'python-requests/2.31.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'} RequestBody: {"symbol":"ETHUSDT","side":"Sell","orderType":"Limit","category":"linear","qty":"0.01","price":"1401","triggerPrice":"1401"}
fetch Response: bybit POST https://api.bybit.com/v5/order/create 200 ResponseHeaders: {'Content-Type': 'application/json; charset=utf-8', 'Content-Length': '116', 'Connection': 'keep-alive', 'Date': 'Fri, 22 Sep 2023 16:12:26 GMT', 'Ret_code': '10001', 'Cache-Control': 'no-store', 'Traceid': 'e76f7b0639c9a53d8714e1f1415b7c3c', 'Timenow': '1695399146142', 'Server': 'Openresty', 'X-Cache': 'Miss from cloudfront', 'Via': '1.1 387adc951beb5181d840dfb5d1f09488.cloudfront.net (CloudFront)', 'X-Amz-Cf-Pop': 'FRA56-P4', 'X-Amz-Cf-Id': '=='} ResponseBody: {"retCode":10001,"retMsg":"params error: TriggerDirection invalid","result":{},"retExtInfo":{},"time":1695399146142}
---------------------------------------------------------------------------
BadRequest Traceback (most recent call last)
[/home/xmatt/devel/cryptos/stuff/ccxt_bybit.ipynb](https://file+.vscode-resource.vscode-cdn.net/home/xmatt/devel/cryptos/stuff/ccxt_bybit.ipynb) Cell 12 line 1
----> [1](vscode-notebook-cell:/home/xmatt/devel/cryptos/stuff/ccxt_bybit.ipynb#X14sZmlsZQ%3D%3D?line=0) o = ct.create_order(pair, 'limit', 'sell', amount=0.01, price=1401, params=params)
[2](vscode-notebook-cell:/home/xmatt/devel/cryptos/stuff/ccxt_bybit.ipynb#X14sZmlsZQ%3D%3D?line=1) o
File [~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/bybit.py:3579](https://file+.vscode-resource.vscode-cdn.net/home/xmatt/devel/cryptos/stuff/~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/bybit.py:3579), in bybit.create_order(self, symbol, type, side, amount, price, params)
3577 request['orderLinkId'] = self.uuid16()
3578 params = self.omit(params, ['stopPrice', 'timeInForce', 'stopLossPrice', 'takeProfitPrice', 'postOnly', 'clientOrderId', 'triggerPrice', 'stopLoss', 'takeProfit'])
-> 3579 response = self.privatePostV5OrderCreate(self.extend(request, params))
3580 #
3581 # {
3582 # "retCode": 0,
(...)
3590 # }
3591 #
3592 order = self.safe_value(response, 'result', {})
File [~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/base/types.py:26](https://file+.vscode-resource.vscode-cdn.net/home/xmatt/devel/cryptos/stuff/~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/base/types.py:26), in Entry.__init__.<locals>.unbound_method(_self, params)
25 def unbound_method(_self, params={}):
---> 26 return _self.request(self.path, self.api, self.method, params, config=self.config)
File [~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/base/exchange.py:3051](https://file+.vscode-resource.vscode-cdn.net/home/xmatt/devel/cryptos/stuff/~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/base/exchange.py:3051), in Exchange.request(self, path, api, method, params, headers, body, config)
3050 def request(self, path, api: Any = 'public', method='GET', params={}, headers: Optional[Any] = None, body: Optional[Any] = None, config={}):
-> 3051 return self.fetch2(path, api, method, params, headers, body, config)
File [~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/base/exchange.py:3048](https://file+.vscode-resource.vscode-cdn.net/home/xmatt/devel/cryptos/stuff/~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/base/exchange.py:3048), in Exchange.fetch2(self, path, api, method, params, headers, body, config)
3046 self.lastRestRequestTimestamp = self.milliseconds()
3047 request = self.sign(path, api, method, params, headers, body)
-> 3048 return self.fetch(request['url'], request['method'], request['headers'], request['body'])
File [~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/base/exchange.py:654](https://file+.vscode-resource.vscode-cdn.net/home/xmatt/devel/cryptos/stuff/~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/base/exchange.py:654), in Exchange.fetch(self, url, method, headers, body)
651 else:
652 raise ExchangeError(details) from e
--> 654 self.handle_errors(http_status_code, http_status_text, url, method, headers, http_response, json_response, request_headers, request_body)
655 if json_response is not None:
656 return json_response
File [~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/bybit.py:6917](https://file+.vscode-resource.vscode-cdn.net/home/xmatt/devel/cryptos/stuff/~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/bybit.py:6917), in bybit.handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody)
6915 feedback = self.id + ' ' + body
6916 self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
-> 6917 self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
6918 raise ExchangeError(feedback) # unknown message
6919 return None
File [~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/base/exchange.py:3414](https://file+.vscode-resource.vscode-cdn.net/home/xmatt/devel/cryptos/stuff/~/.pyenv/versions/3.11.4/envs/trade_3114/lib/python3.11/site-packages/ccxt/base/exchange.py:3414), in Exchange.throw_exactly_matched_exception(self, exact, string, message)
3412 def throw_exactly_matched_exception(self, exact, string, message):
3413 if string in exact:
-> 3414 raise exact[string](message)
BadRequest: bybit {"retCode":10001,"retMsg":"params error: TriggerDirection invalid","result":{},"retExtInfo":{},"time":1695399146142}
-- Create order
{'info': {'orderId': 'b8273344-3013-4522-8f76-cde643747434',
'orderLinkId': ''},
'id': 'b8273344-3013-4522-8f76-cde643747434',
'clientOrderId': None,
'timestamp': None,
'datetime': None,
'lastTradeTimestamp': None,
'lastUpdateTimestamp': None,
'symbol': 'ETH/USDT:USDT',
'type': None,
'timeInForce': None,
'postOnly': None,
'reduceOnly': None,
'side': None,
'price': None,
'stopPrice': None,
'triggerPrice': None,
'takeProfitPrice': None,
'stopLossPrice': None,
'amount': None,
'cost': None,
'average': None,
'filled': None,
'remaining': None,
'status': None,
'fee': None,
'trades': [],
'fees': []}
--- subsequent fetch
{'info': {'orderId': 'b8273344-3013-4522-8f76-cde643747434',
'orderLinkId': '',
'blockTradeId': '',
'symbol': 'ETHUSDT',
'price': '1401.00',
'qty': '0.01',
'side': 'Sell',
'isLeverage': '',
'positionIdx': '0',
'orderStatus': 'Untriggered',
'cancelType': 'UNKNOWN',
'rejectReason': 'EC_NoError',
'avgPrice': '0',
'leavesQty': '0.01',
'leavesValue': '14.01',
'cumExecQty': '0.00',
'cumExecValue': '0',
'cumExecFee': '0',
'timeInForce': 'GTC',
'orderType': 'Limit',
'stopOrderType': 'Stop',
'orderIv': '',
'triggerPrice': '1401.00',
'takeProfit': '0.00',
'stopLoss': '0.00',
'tpTriggerBy': 'UNKNOWN',
'slTriggerBy': 'UNKNOWN',
'triggerDirection': '2',
'triggerBy': 'LastPrice',
'lastPriceOnCreated': '0.00',
'reduceOnly': True,
'closeOnTrigger': False,
'smpType': 'None',
'smpGroup': '0',
'smpOrderId': '',
'tpslMode': '',
'tpLimitPrice': '',
'slLimitPrice': '',
'placeType': '',
'createdTime': '1695398854285',
'updatedTime': '1695398854285',
'nextPageCursor': 'page_token%3D12397685%26'},
'id': 'b8273344-3013-4522-8f76-cde643747434',
'clientOrderId': None,
'timestamp': 1695398854285,
'datetime': '2023-09-22T16:07:34.285Z',
'lastTradeTimestamp': 1695398854285,
'lastUpdateTimestamp': 1695398854285,
'symbol': 'ETH/USDT:USDT',
'type': 'limit',
'timeInForce': 'GTC',
'postOnly': False,
'reduceOnly': True,
'side': 'sell',
'price': 1401.0,
'stopPrice': 1401.0,
'triggerPrice': 1401.0,
'takeProfitPrice': None,
'stopLossPrice': None, <-- i used this parameter to create the order - shouldn't it be filled?
'amount': 0.01,
'cost': 0.0,
'average': None,
'filled': 0.0,
'remaining': 0.01,
'status': 'open',
'fee': {'cost': 0.0, 'currency': 'USDT'},
'trades': [],
'fees': [{'cost': 0.0, 'currency': 'USDT'}]}
About this issue
- Original URL
- State: closed
- Created 9 months ago
- Comments: 17 (12 by maintainers)
@xmatthias I agree, that would be ideal (to fill in
stopLossPriceandtakeProfitPrice), thanks for your suggestion, I will run some tests and let you know if that is doable.@xmatthias the reasoning for deleting that param is because it’s not required so removing it gives the user more flexibility with creating orders, and stopLossPrice and takeProfitPrice use that param so its redundant to have stop orders behave the exact same as stopLossPrice and takeProfitPrice orders.
I’m not getting an error on my end so I don’t think it’s introducing a bug other than maybe parseOrder could be adjusted but the commit does have the fix() tag, I think the freqtrade user is confused because using the stopPrice or stopLossPrice params creates a conditional order not an actual stopLoss order attached to a position.
stopPrice or triggerPrice: https://github.com/ccxt/ccxt/wiki/Manual#trigger-orders stopLossPrice: https://github.com/ccxt/ccxt/wiki/Manual#stop-loss-orders stopLoss: https://github.com/ccxt/ccxt/wiki/Manual#stoploss-and-takeprofit-orders-attached-to-a-position