verata-veritatis / pybit Goto Github PK
View Code? Open in Web Editor NEWPython3 API connector for Bybit's HTTP and Websockets APIs.
License: MIT License
Python3 API connector for Bybit's HTTP and Websockets APIs.
License: MIT License
The 'close_position' custom function is great; however, I can't seem to find a way to close with a limit order.
There seems to be a mismatch between document @ https://bybit-exchange.github.io/docs/linear/?python--pybit#t-getrisklimit vs actual pybit v1.3.2
Document mentioned Get risk limit. This endpoint does not require authentication but when I run the example Python code below,
from pybit import HTTP
session = HTTP("https://api-testnet.bybit.com")
print(session.get_risk_limit(
symbol="BTCUSDT"
))
authentication is expected:
Traceback (most recent call last):
File "", line 3, in
File "D:\xxx\venv\lib\site-packages\pybit_init_.py", line 1259, in get_risk_limit
return self.submit_request(
File "D:\xxx\venv\lib\site-packages\pybit_init.py", line 1734, in _submit_request
signature = self.auth(
File "D:\xxx\venv\lib\site-packages\pybit_init.py", line 1655, in _auth
raise PermissionError('Authenticated endpoints require keys.')
PermissionError: Authenticated endpoints require keys.
I trust website is correct whereas Pybit requires fix. Rationale is that Get risk limit is mapped to /public/linear/risk-limit logically should not require authentication.
This bug is related to spot sub accounts only.
Make sure the spot section of a sub account has no balances, then try to query its balance with get_wallet_balance(spot=True) using the sub account's API keys. Instead of getting a well formed output showing zero balances you will get the following empty output instead:
{'ret_code': 0, 'ret_msg': '', 'ext_code': None, 'ext_info': None, 'result': {'balances': []}}
I don't know how I am supposed to pass the parameter "from" on the query_kline() method, as "from" is a python keyword.
I have tried passing it anyway, and it obviously gives a syntax error:
File "path/to/file.py", line 19
session.query_kline(symbol="BTCUSD", interval=interval, from=timestamp)
^
SyntaxError: invalid syntax
I am probably missing something, since no one pointed out this issue, so I am hoping someone will help me.
I'm running your example, and got time out on ssl or refused. And I found that even if I use:
import requests
h = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36",
}
r = requests.get('https://api-testnet.bybit.com/spot/quote/v1/kline?symbol=ETHUSDT&interval=5m', headers=h)
I still got refused.
while if I use:
import urllib.request
req = urllib.request.Request(web, headers=headers_)
data = urllib.request.urlopen(req).read().decode("utf-8",'ignore')
print(data)
I could get the result.
We know that the kline is unauthorized url. So I wonder if requests library is treated as ROBOT ?
I'm having trouble using the Websockets feature support.
If there were enhanced docs, or a strong example, or test case that would help alot.
The best progress I made was running the pybit/test.py file which I found browsing the code. This seems like a leftover prototype for the Websockets class that exists, but it gives me some output that works.
Part of the problem could be that I'm running it as part of a Flask application, and managing the background thread is problematic. I'm narrowing in on a solution of running it in Cloud Run as its own application but that requires using some background persistence like Redis: https://cloud.google.com/run/docs/triggering/websockets
Alternatively, I've played with https://kendhia.medium.com/run-python-webserver-flask-as-a-websocket-client-also-175c130f7ca4 to run multiple processes together but it hasn't come together yet.
Any enhanced docs, or a strong example, or test case that would help alot. As I explore this on my own side I will try to contribute back.
Issue described here: https://t.me/BybitAPI/57492
Hi Pybit Admin,
It has been discovered that the max_leverage value from Query Symbol always exhibit the highest maximum leverage value despite the set_risk_limit has been switched from lowest Risk ID to highest Risk ID.
import inspect
from pprint import pprint
from pybit import HTTP
bybit_testnet_authentication = {
'key' : api_key,
'secret' : api_secret
}
def print_symbol_leverage(session_auth):
'''
Reference: https://help.bybit.com/hc/en-us/articles/360039749753-Risk-Limit-Inverse-Contract-
Reference: https://help.bybit.com/hc/en-us/articles/900000170023-Risk-Limit-USDT-Contract-
Reference: https://bybit-exchange.github.io/docs/inverse/?python#t-querysymbol
Reference: https://bybit-exchange.github.io/docs/linear/#t-querysymbol
'''
response = session_auth.query_symbol(
symbol=symbol_id
)
for result in response['result']:
if result['name'] != symbol_id:
continue
print("symbol_id: {}, leverage_filter:".format(symbol_id))
pprint(result['leverage_filter'])
if __name__ == "__main__":
api_key = bybit_testnet_authentication['key']
api_secret = bybit_testnet_authentication['secret']
mainnet_url = "https://api.bybit.com"
testnet_url = "https://api-testnet.bybit.com"
symbol_name = "UNI/USDT"
symbol_id = symbol_name.replace("/", "")
# Unauthenticated
session_unauth = HTTP(endpoint=testnet_url)
# Authenticated
session_auth = HTTP(
endpoint=testnet_url,
api_key=api_key,
api_secret=api_secret,
)
response = session_auth.get_risk_limit(
symbol=symbol_id
)
# print("Risk Limit:")
# pprint(response['result'])
# for result in response['result']:
# print("symbol: {}, risk_id: {}, max_leverage: {}".format(
# result['symbol'], result['id'], result['max_leverage']
# ))
min_risk_id = response['result'][0]['id']
max_risk_id = response['result'][-1]['id']
print("symbol: {}, min_risk_id: {}, max_risk_id: {}".format(
symbol_id, min_risk_id, max_risk_id
))
print_symbol_leverage(session_auth)
'''
Reference: https://bybit-exchange.github.io/docs/inverse/#t-myposition
'''
position_response = session_auth.my_position(
symbol=symbol_id
)
# pprint(response['result'])
for result in position_response['result']:
side = result['side']
print("symbol: {}, side: {}, get risk_id: {}".format(
result['symbol'], side, result['risk_id']
))
current_risk_id = int(result['risk_id'])
risk_id = min_risk_id
if current_risk_id == min_risk_id:
risk_id = max_risk_id
'''
Reference: https://bybit-exchange.github.io/docs/inverse/#t-setrisklimit
'''
risk_limit_response = session_auth.set_risk_limit(symbol=symbol_id, side=side, risk_id=risk_id)
print("symbol: {}, side: {}, set risk_id: {}".format(
symbol_id, side, risk_limit_response['result']['risk_id']
))
print_symbol_leverage(session_auth)
Sample output:
symbol: UNIUSDT, min_risk_id: 146, max_risk_id: 160
symbol_id: UNIUSDT, leverage_filter:
{'leverage_step': '0.01', 'max_leverage': 25, 'min_leverage': 1}
symbol: UNIUSDT, side: Buy, get risk_id: 146
symbol: UNIUSDT, side: Buy, set risk_id: 160
symbol: UNIUSDT, side: Sell, get risk_id: 146
symbol: UNIUSDT, side: Sell, set risk_id: 160
symbol_id: UNIUSDT, leverage_filter:
{'leverage_step': '0.01', 'max_leverage': 25, 'min_leverage': 1}
"max_leverage" seems to be stuck at value of 25 forever, which when looking back at Query Symbol, it is designated as public instead of private API which makes sense for the misalignment.
Then the applicability of this "max_leverage" value in "leverage_filter" is in question as user cannot rely on this "max_leverage" value as reliable information to trade as it could be in conflict with "max_leverage" in risk limit provided IF the risk ID is set to different value other that lowest risk ID. We know assuming symbol to exercise lowest risk ID is NOT always guaranteed as user could set risk limit to other risk ID.
Since "max_leverage" value in "leverage_filter" of Query Symbol does not serve any valid use case other than capturing the maximum leverage value of lowest risk ID, do you think it make more sense to remove "max_leverage" entry from "leverage_filter"?
Please feel free to comment if you have other thoughts.
Running tests on the master
branch like python setup.py test
yields error output like this, but no tests fail.
.... 2021-12-27 16:11:35 - pybit - ERROR - You aren't subscribed to the ['instrument_info.100ms.BTCUSD'] topic. ERROR
This is a failure situation that should yield an error. Currently this situation is only logged to error with no return value. This clearly should instead throw an exception like elsewhere in the code.
>>> ws.fetch(['position'])
Traceback (most recent call last):
return [self.data[i].pop() for i in topics]
IndexError: pop from empty list
Need to handle this Exception by returning an empty list instead.
Hey there, thanks a lot for building a solid Bybit connector!
I found it very easy to get set up and running, but it seems there is a key usage issue that was counter-intuitive to detect that might be useful to address:
After subscribing to data that gets updated via deltas (such as the orderbook), the returned object is a reference to the underlying Websocket.data
structure, which means that if you have code akin to this:
ws = WebSocket(
endpoint='wss://stream.bybit.com/realtime',
subscriptions=['orderBookL2_25.BTCUSD']
)
snapshots = []
for i in range(10):
snapshot = ws.fetch('orderBookL2_25.BTCUSD')
snapshots.append(snapshot)
time.sleep(1)
Then snapshots[0]
will actually resolve to the same values as snapshots[1]
even if the data has updated between the snapshots, since they resolve to the same object in pybit unless there has been a delete
or insert
.
I suspect this might be desired behavior if one is only ever interested in the most recent data, but that is not always the case. There would of course be some performance penalty to returning copies each time. Nonetheless, it might be worth highlighting this behaviour in the README if leaving it unchanged.
a) orderBook25 (please replace with topic orderBookL2_25 or
orderBook_200.100ms)
b) kline (please replace with topic klineV2)
c) instrument (please replace with topic instrument_info)
a) /open-api/order/list (please replace with /v2/private/order/list)
b) /open-api/stop-order/create (please replace with /v2/private/stop-order/create)
c) /open-api/stop-order/list (please replace with /v2/private/stop-order/list)
d) /open-api/stop-order/cancel (please replace with /v2/private/stop-order/cancel)
e) /open-api/order/replace (please replace with /v2/private/order/replace)
f) /open-api/stop-order/replace (please replace with /v2/private/stop-order/replace)
g) /open-api/order/create (please replace with /v2/private/order/create)
h) /open-api/order/cancel (please replace with /v2/private/order/cancel)
i) /position/list (please replace with /v2/private/position/list)
j) /user/leverage (please replace with /v2/private/position/list)
Hi! I was trying to get kline data via the perpetual websocket, it connects without error but the responses thereafter are just empty dicts. Don't know if your supporting perpetual klines?
This is the code I'm using:
from pybit import WebSocket
endpoint = 'wss://stream.bybit.com/realtime_public'
sub = 'candle.1.BTCUSDT'
ws_unauth = WebSocket(endpoint, subscriptions=sub)
while True:
try:
snap=ws_unauth.fetch('candle.1.BTCUSDT')
pprint(snap)
sleep(1)
except: print('-')
and this is the response:
2020-10-20 18:45:58 - pybit - INFO - Initializing WebSocket.
{}
2020-10-20 18:46:00 - pybit - INFO - Subscription to ['candle.1.BTCUSDT'] successful.
{}
{}
{}
{}
It continues without change.
Also the orderbookL2 endpoint is spitting out this:
2020-10-20 18:48:57 - websocket - ERROR - error from callback <bound method WebSocket._on_message of <pybit.WebSocket object at 0x7fe3d9c760d0>>: string indices must be integers
Thanks in advance and for the awesome library my man!
Greets,
I'm getting the following error from the logging module when a stoploss order is placed or modified on the testnet. I can only presume that the bybit websocket is passing back 'stop_order_id' as a parameter which is not expected?
ERROR - error from callback <bound method WebSocket._on_message of <pybit.WebSocket object at 0x1a1b442ad0>>: 'stop_order_id'
From how I'm reading the docs - it sends back the order id as 'order_id' rather than 'stop_order_id'
Conditional order placement and modify orders are working fine for me BTW, the error just appears when orders are sent / modified.
Any idea why this is happening?
Thanks
As symbol
is not a required parameter for all Futures position/list
endpoints, this means that /v2/private/position/list
is called by default; but this is not necessarily intended by the user.
Also, this means that it's not possible to call other endpoints without specifying a symbol, so it's impossible to receive a response detailing multiple symbols.
This is unfortunately as it means this method will need a special way to know which endpoint path to use.
Looks like the websockets.py example filename was renamed but the README.md was not updated. I'll send a PR.
I am sure there was something missing, Any help or ideas?
Regards,
Just came over from the Binance API and I'm just trying to place an order.
Exeption reads as : TypeError: "encoding without a string argument"
def placeTrade(self, params) -> dict: """place trade""" try: res = self._client.place_conditional_order( symbol=params["symbol"], order_type=params["order_type"], side=params["side"], qty=params["qty"], price=params["price"], base_price = params["base_price"], stop_px=params["price"], time_in_force='GoodTillCancel', close_on_trigger=True, reduce_only = False, take_profit=params["take_profit"], stop_loss=params["stop_loss"], trigger_by='last_price') except Exception as e: print(e) return {} else: return res
When using websockets to get the content of the orderbook (orderBookL2_25.Symbol)
the content of the orderbook obtained when calling fetch()
is out of order. When Bybit
sends the orderbook the first time on the websocket it is ordered properly and then on subsequent delta pushes, pybit
updates the local version of the orderbook, but does not re-sort it after delta updates. So when we call fetch()
to get the orderbook we might assume the orderbook is sorted, but that is not the case and reading only the top rows for buyers/sellers sometime gives inaccurate results. Here is an example of what fetch()
returns after a few delta updates.
For example:
Orderbook Top 25
price symbol id side size
0 41426.00 BTCUSDT 414260000 Buy 11.807
1 41426.50 BTCUSDT 414265000 Buy 0.049
2 41428.00 BTCUSDT 414280000 Buy 0.003
3 41429.50 BTCUSDT 414295000 Buy 0.027
4 41430.00 BTCUSDT 414300000 Buy 0.505
5 41430.50 BTCUSDT 414305000 Buy 0.404
6 41432.00 BTCUSDT 414320000 Buy 0.012
7 41432.50 BTCUSDT 414325000 Buy 0.540
8 41433.50 BTCUSDT 414335000 Buy 1.052
9 41434.50 BTCUSDT 414345000 Buy 6.034
10 41435.00 BTCUSDT 414350000 Buy 1.110
11 41435.50 BTCUSDT 414355000 Buy 2.517
12 41436.00 BTCUSDT 414360000 Buy 0.774
13 41436.50 BTCUSDT 414365000 Buy 1.093
14 41437.00 BTCUSDT 414370000 Buy 1.283
15 41437.50 BTCUSDT 414375000 Buy 1.052
16 41438.50 BTCUSDT 414385000 Buy 0.616
17 41439.00 BTCUSDT 414390000 Buy 0.034
18 41439.50 BTCUSDT 414395000 Buy 2.354
19 41440.00 BTCUSDT 414400000 Buy 0.854
20 41441.00 BTCUSDT 414410000 Buy 3.300
21 41441.50 BTCUSDT 414415000 Buy 78.825
22 41442.00 BTCUSDT 414420000 Sell 7.434
23 41444.50 BTCUSDT 414445000 Sell 0.081
24 41447.50 BTCUSDT 414475000 Sell 16.000
25 41449.00 BTCUSDT 414490000 Sell 0.744
26 41450.00 BTCUSDT 414500000 Sell 1.097
27 41450.50 BTCUSDT 414505000 Sell 0.035
28 41451.00 BTCUSDT 414510000 Sell 0.677
29 41451.50 BTCUSDT 414515000 Sell 0.331
30 41452.00 BTCUSDT 414520000 Sell 1.350
31 41453.00 BTCUSDT 414530000 Sell 5.000
32 41455.50 BTCUSDT 414555000 Sell 0.679
33 41456.50 BTCUSDT 414565000 Sell 0.020
34 41458.00 BTCUSDT 414580000 Sell 0.837
35 41458.50 BTCUSDT 414585000 Sell 0.334
36 41459.00 BTCUSDT 414590000 Sell 0.003
37 41459.50 BTCUSDT 414595000 Sell 1.687
38 41460.00 BTCUSDT 414600000 Sell 0.641
39 41460.50 BTCUSDT 414605000 Sell 1.337
40 41462.00 BTCUSDT 414620000 Sell 4.463
41 41462.50 BTCUSDT 414625000 Sell 1.948
42 41438.00 BTCUSDT 414380000 Buy 0.719 <--
43 41452.50 BTCUSDT 414525000 Sell 0.570
44 41425.50 BTCUSDT 414255000 Buy 0.050 <--
45 41449.50 BTCUSDT 414495000 Sell 0.777
46 41446.00 BTCUSDT 414460000 Sell 0.010
47 41454.50 BTCUSDT 414545000 Sell 0.007
48 41425.00 BTCUSDT 414250000 Buy 0.362 <--
49 41463.00 BTCUSDT 414630000 Sell 0.010
Maybe pybit
should re-sort the local orderbook in _on_message()
after each delta updates, or prior to returning the orderbook to the user in the fetch()
method and if not, at least tell the user he needs to re-sort the orderbook himself prior to using it.
After ca. 6-12 hours the ws exits automatically with winerror 10054 "An existing connection was forcibly closed by the remote host".
Log.txt
below is the error I get when I try to connect to websocket on widows server 2016
the code works fine on my personal PC
ERROR:
2021-03-14 11:28:44 - websocket - ERROR - error from callback <bound method WebSocket._on_message of <pybit.WebSocket object at 0x000002064BE7FE50>>: _on_message() takes 2 positional arguments but 3 were given
I literally didn't change anything in my code and from one day to another, I cant execute any place_active_order() because this is the error i get.
Hello,
I'm using the official example as-is and I'm getting "{}" from all subscriptions.
Thanks in advance,
e.g. "BTCUSD0625"
Might add if I find the time in the next couple of days.
This is similar to issue #72 in that error states are just logged instead of raised for handling.
I'll provide a PR.
Recently, position mode switch was released for USDT perp, which means position_idx is a required parameter when in single position mode. https://bybit-exchange.github.io/docs/linear/#2021-12-28
In pybit Custom Endpoints there is a command close_position(), but it seems need modification, it dosent accept 'position_idx' and without it returns error:
pybit.exceptions.InvalidRequestError: Position idx not match position mode (ErrCode: 130001)
Greets,
A puzzling problem here - I am hoping it's something small I have missed... if so please let me know!
Trying: send 3 post only limit orders to open a position, using place_active_order_bulk()
As reduce_only is not necessary, or even logical to have in a limit order that would increse a position of 0 to 0.008 BTC if filled - this error makes no sense to me!
The orders being passed to place_active_order_bulk() are:
[{'symbol': 'BTCUSDT', 'order_type': 'Limit', 'side': 'Buy', 'qty': 0.008, 'price': 45555.0, 'time_in_force': 'PostOnly'}, {'symbol': 'BTCUSDT', 'order_type': 'Limit', 'side': 'Buy', 'qty': 0.008, 'price': 44321.0, 'time_in_force': 'PostOnly'}, {'symbol': 'BTCUSDT', 'order_type': 'Limit', 'side': 'Buy', 'qty': 0.008, 'price': 43434.0, 'time_in_force': 'PostOnly'}]
error is:
Param validation for 'reduce_only' failed on the 'exists' tag (ErrCode: 10001) (ErrTime: 12:46:31).
Request → POST https://api-testnet.bybit.com/private/linear/order/create: {'api_key': 'xxxxxxx', 'order_type': 'Limit', 'price': 45555, 'qty': 0.008, 'recv_window': 5000, 'side': 'Buy', 'symbol': 'BTCUSDT', 'time_in_force': 'PostOnly', 'timestamp': 1619786791191, 'sign': 'xxxxxxx'}.
Any help welcome!
System: macOS
pybit version: 1.3.0
My code is working for for coin based perpetual contracts. Tried adapting for the ETCUSDT perpetual contract.
Authentication and active order queries work fine. However, creating an order fails with an "Error sign!" exception
Traceback (most recent call last):
File "/Users/user/Dropbox/Projects/PTrader/PTrader.py", line 237, in <module>
close_on_trigger="False")
File "/Users/user/Library/Python/3.7/lib/python/site-packages/pybit/__init__.py", line 492, in place_active_order
auth=True
File "/Users/user/Library/Python/3.7/lib/python/site-packages/pybit/__init__.py", line 1870, in _submit_request
time=dt.utcnow().strftime("%H:%M:%S")
pybit.exceptions.InvalidRequestError: Error sign! origin_string[api_key=valid_api_key&close_on_trigger=false&order_type=limit&price=52.11&qty=0.1&recv_window=5000&reduce_only=true&side=sell&symbol=etcusdt&time_in_force=goodtillcancel×tamp=1633520617145] (ErrCode: 10004) (ErrTime: 11:43:37).
Request → POST https://api.bybit.com/private/linear/order/create: {'api_key': 'valid_api_key', 'close_on_trigger': 'False', 'order_type': 'Limit', 'price': 52.11, 'qty': 0.1, 'recv_window': 5000, 'reduce_only': 'True', 'side': 'Sell', 'symbol': 'ETCUSDT', 'time_in_force': 'GoodTillCancel', 'timestamp': 1633520617145, 'sign': '1c52731c9926946bc22332592dd31abeba4bedc017caed64528c79facb2428f2'}.
Hi! Thank you for alle the work in this project.
I am not quite sure how to use the endpoints right, i assume that any endpoint, such as get_wallet_balance() requires the "session." as a prefix? If so, I do not understand how the use the parameters "self" and "kwargs". For instance, session.get_wallet_balance() returns the same bunch of data as session.get_wallet_balance (symbol='BTCUSD', kwargs='equity'). Is it possible to restrict the output to relevant information and how is the "self" and "kwargs" parameter meant to be used. Do you have an example?
Kind regards
Paul
Nice project !
When running this minimal example, there is no output, according to the api docs I would expect to see output:
from pybit import WebSocket
from time import sleep
ws = WebSocket(
endpoint='wss://stream.bybit.com/realtime',
subscriptions=['orderBookL2_25.UNIUSDT'],
)
while True:
for j in ws.fetch('orderBookL2_25.UNIUSDT'):
print(j)
sleep(0.1)
Could you clarify on what I might be doing wrong ? Otherwise I think there is a bug with getting the orderbook for UNIUSDT,
Api docs : https://bybit-exchange.github.io/docs/inverse/#t-websocketorderbook25
Hi Pybit/Bybit API Admin,
We've noticed a strange behavior in Pybit/Bybit API where certain range of leverage value is constantly rejected by Bybit API despite the new leverage value is different from the existing leverage value. We believe it is an issue Pybit/Bybit API team is interested to look into.
Below is the code to reproduce the issue:
import pybit
from pybit import HTTP
import sys
import time
if __name__ == "__main__":
mainnet_url = "https://api.bybit.com"
testnet_url = "https://api-testnet.bybit.com"
symbol_name = "UNI/USDT"
symbol_id = symbol_name.replace("/", "")
# Unauthenticated
session_unauth = HTTP(endpoint=testnet_url)
# Authenticated
session_auth = HTTP(
endpoint=testnet_url,
api_key=api_key,
api_secret=api_secret,
)
'''
Reference: https://help.bybit.com/hc/en-us/articles/360039749753-Risk-Limit-Inverse-Contract-
Reference: https://help.bybit.com/hc/en-us/articles/900000170023-Risk-Limit-USDT-Contract-
Reference: https://bybit-exchange.github.io/docs/inverse/?python#t-querysymbol
Reference: https://bybit-exchange.github.io/docs/linear/#t-querysymbol
'''
response = session_auth.query_symbol(
symbol=symbol_id
)
min_leverage = None
leverage_step = None
for result in response['result']:
if result['name'] != symbol_id:
continue
min_leverage = float(result['leverage_filter']['min_leverage'])
leverage_step = float(result['leverage_filter']['leverage_step'])
assert min_leverage is not None
assert leverage_step is not None
print("symbol: {}, min_leverage: {}, leverage_step: {}".format(
symbol_id, min_leverage, leverage_step,
))
response = session_auth.get_risk_limit(
symbol=symbol_id
)
lowest_risk_id = response['result'][0]['id']
highest_risk_id = response['result'][-1]['id']
lowest_risk_max_leverage = response['result'][0]['max_leverage']
highest_risk_max_leverage = response['result'][-1]['max_leverage']
print("symbol: {}, lowest_risk_id: {}, lowest_risk_max_leverage: {}, highest_risk_id: {}, highest_risk_max_leverage: {}".format(
symbol_id, lowest_risk_id, lowest_risk_max_leverage, highest_risk_id, highest_risk_max_leverage,
))
'''
Reference: https://bybit-exchange.github.io/docs/inverse/#t-myposition
'''
position_response = session_auth.my_position(
symbol=symbol_id
)
for result in position_response['result']:
side = result['side']
leverage = result['leverage']
print("symbol: {}, side: {}, leverage: {}, get risk_id: {}".format(
result['symbol'], side, leverage, result['risk_id']
))
current_risk_id = int(result['risk_id'])
if current_risk_id != highest_risk_id:
'''
Reference: https://bybit-exchange.github.io/docs/inverse/#t-setrisklimit
'''
risk_limit_response = session_auth.set_risk_limit(symbol=symbol_id, side=side, risk_id=highest_risk_id)
print("symbol: {}, side: {}, set risk_id: {}".format(
symbol_id, side, risk_limit_response['result']['risk_id']
))
# Convert to integer as Python range only accept integer value
int_min_leverage = int(min_leverage * 100)
int_highest_risk_max_leverage = int(highest_risk_max_leverage * 100)
int_leverage_step = int(leverage_step * 100)
for int_new_leverage in range(int_min_leverage, int_highest_risk_max_leverage, int_leverage_step):
# Convert from integer back to float
float_new_leverage = float(int_new_leverage / 100)
try:
'''
Reference: https://bybit-exchange.github.io/docs/linear/#t-setleverage
'''
response = session_auth.set_leverage(
symbol=symbol_id,
buy_leverage=float_new_leverage,
sell_leverage=float_new_leverage,
)
except pybit.exceptions.InvalidRequestError as e:
print("symbol: {}, configuring leverage: {} failed with: {}".format(
symbol_id, float_new_leverage, e.message,
))
time.sleep(1)
Sample output screen:
symbol: UNIUSDT, min_leverage: 1.0, leverage_step: 0.01
symbol: UNIUSDT, lowest_risk_id: 146, lowest_risk_max_leverage: 25, highest_risk_id: 160, highest_risk_max_leverage: 5.56
symbol: UNIUSDT, side: Buy, leverage: 4.01, get risk_id: 160
symbol: UNIUSDT, side: Sell, leverage: 4.01, get risk_id: 160
symbol: UNIUSDT, configuring leverage: 1.13 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 2.01 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 2.03 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 2.05 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 2.07 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 2.26 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 2.28 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 2.3 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 2.32 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 2.51 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 2.53 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 2.55 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.02 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.06 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.1 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.14 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.27 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.31 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.35 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.39 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.52 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.56 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.6 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.64 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.77 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.81 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.85 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 4.89 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 5.02 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 5.06 failed with: leverage not modified
symbol: UNIUSDT, configuring leverage: 5.1 failed with: leverage not modified
It seems like quite a long list of failure here. Hence, thought process have been considered to first post the issue here prior to Bybit API.
Please allow us to summarize the code logic here:
Hypothesis
There seems to be some kind of previous leverage versus new leverage checking failed behind Pybit/Bybit API. When the max_leverage is 5.56, the precision on the leverage_step is crucial when comparing previous leverage versus new leverage value. We suspect it is related to precision of the checking performed. It is not clear to us how these values are related to each other but chances are they are related to precision that do not correlate to leverage_step value. Naturally, Bybit API team should be in better position to confirm and comment than us.
Hello,
Firstly thanks for pybit, I'm building code around it nicely, but I have one area of confusion when getting position data via the websocket.
During tests, I had the bot enter 20 contracts long and then 40 contracts short to have the position be short/sell side.
However, ws.fetch("position") returned data for both the Buy and Sell sides of 20 contracts each. Is there something I'm missing? This doesn't reflect the actual position of the account at that time, which should only have data for the Sell side?
Here is the result of a fetch on the (BTCUSD) position so you can see what I mean.
`
'Buy':
{'user_id': 11111, 'symbol': 'BTCUSD', 'size': 20, 'side': 'Buy', 'position_value': '0.00059908',
'entry_price': '33384.52293517', 'liq_price': '16734.5', 'bust_price': '16692.5', 'leverage': '1', 'order_margin': '0', 'position_margin': '0.00059908',
'available_balance': '1.59463741', 'take_profit': '0', 'stop_loss': '0', 'realised_pnl': '0.000002', 'trailing_stop': '0', 'trailing_active': '0',
'wallet_balance': '1.59523739', 'risk_id': 1, 'occ_closing_fee': '0.0000009', 'occ_funding_fee': '0', 'auto_add_margin': 0,
'cum_realised_pnl': '0.00013621', 'position_status': 'Normal', 'position_seq': 0, 'Isolated': True, 'mode': 0, 'position_idx': 0
},
'Sell':
{'user_id': 11111, 'symbol': 'BTCUSD', 'size': 20, 'side': 'Sell', 'position_value': '0.00059725', 'entry_price': '33486.81456676',
'liq_price': '999999', 'bust_price': '999999', 'leverage': '1', 'order_margin': '0', 'position_margin': '0.00059723', 'available_balance': '1.59464108',
'take_profit': '0', 'stop_loss': '0', 'realised_pnl': '0.00000294', 'trailing_stop': '0', 'trailing_active': '0', 'wallet_balance': '1.59523833',
'risk_id': 1, 'occ_closing_fee': '0.00000002', 'occ_funding_fee': '0', 'auto_add_margin': 0, 'cum_realised_pnl': '0.00013715', 'position_status': 'Normal',
'position_seq': 0, 'Isolated': True, 'mode': 0, 'position_idx': 0
}
`
Any help / pointers as to why the data returned does not only have the "Sell" side information gladly received!
Executing
session.get_active_order(symbol="BTCUSD")
pybit raises
pybit.exceptions.InvalidRequestError: Params error! (ErrCode: 10001) (ErrTime: 19:14:19).
Request → GET https://api-testnet.bybit.com/futures/private/order/list: {'api_key': 'api_key', 'recv_window': 5000, 'symbol': 'BTCUSD', 'timestamp': 1623179659029, 'sign': 'sign'}.
(sensitive info replaced)
pybit called the incorrect endpoint, /futures/private/order/list
, which does not support the BTCUSD symbol. The problem lies here: https://github.com/verata-veritatis/pybit/blob/master/pybit/__init__.py#L410
When using the from parameter for the query_mark_kline method it is saying that it is invalid syntax.
I think it is because from is a keyword.
Also, i noticed the Websocket.md page is no longer maintained.
Is anyone maintaining that?
I am getting a Websocket object does not have a send method error.
Can someone please assist?
Is this codebase being actively maintained?
Greets,
I'm getting the following error from the logging module when a stoploss order is placed or modified on the testnet. I can only presume that the bybit websocket is passing back 'stop_order_id' as a parameter which is not expected?
ERROR - error from callback <bound method WebSocket._on_message of <pybit.WebSocket object at 0x1a1b442ad0>>: 'stop_order_id'
Conditional order placement and modify orders are working fine for me BTW, the error just appears when orders are sent / modified.
Any idea why this is happening?
Thanks
Hi,
I am trying to subscribe to position or order and I don't get data because the auth failed. The problem is that the websocket not raise any exception. I debug the websocket and I can see the error:
msg_json: {'success': False, 'ret_msg': 'error:request expired', 'conn_id': '31950833-ab7d-4fd2-8b1c-1dc1b7205ae2', 'request': {'op': 'auth', 'args': ['bqZTxSi30b', '1622325037946', '2dab68953beb4d27bc4c1257f1f594dabc9abb631']}}
msg_json: {'success': False, 'ret_msg': 'error:topic:order needs auth first', 'conn_id': '31950833-ab7d-4f1c-1dc1b7205ae2', 'request': {'op': 'subscribe', 'args': ['order', 'position']}}
If I change the "def _auth(self)" and increase the expires variable the websocket return data correctly.
I thing that is a good idea implement the auto increase recv window like HTTP that work fine, because is very difficult to sync the timestamp. What do you think?
if s_json['ret_code'] == 10002:
error_msg += '. Added 2.5 seconds to recv_window'
recv_window += 2500
@all-contributors please add @verata-veritatis for code
Line 1819 should be changed
self.data[topic] = msg_json['data'] -> self.data[topic] = msg_json['data']['order_book']
Problem Statement
Often for take profit (TP) and stop loss (SL) calculation, if they are certain percentage away from the order price, the result will not be matching with the tick size (step size for quantity).
Observation
Chances are users would have created their own version of round_tick and round_step function call to change TP, SL and quantity in order to cater for need to comply with exchange's rick size and step size requirements.
Opportunity
I've came across one of the exchanges providing roundStep API to their users, I would like to extend the request for PyBit to consider providing both round_tick and round_step to lead the crypto market instead.
Example Use Case
take_profit = round_tick(ask * (1 + 10/100))
stop_loss = round_tick(bid * (1 + 5/100))
single_quantity = round_step(total_quantity / 5))
Things to Watch Out
Benefits for PyBit Users
Prevent non-obvious error such as the following that I've spend an hour and eventually figured out it is caused by TP and SL are not complying with tick size:
Traceback (most recent call last):
File "D:\venv\lib\site-packages\pybit\__init__.py", line 1812, in _submit_request
s_json = s.json()
File "D:\venv\lib\site-packages\requests\models.py", line 900, in json
return complexjson.loads(self.text, **kwargs)
File "C:\Users\xxx\AppData\Local\Programs\Python\Python39\lib\json\__init__.py", line 346, in loads
return _default_decoder.decode(s)
File "C:\Users\xxx\AppData\Local\Programs\Python\Python39\lib\json\decoder.py", line 340, in decode
raise JSONDecodeError("Extra data", s, end)
json.decoder.JSONDecodeError: Extra data: line 1 column 194 (char 193)
Hey i have setup my websocket and its working great.
But i cant seem to get the data unpackked in the correct manner.
It seems that the data is a JSON dictonary, and when i try to unpack it, i get to this point.
[{'start': 1641475140, 'end': 1641475200, 'period': '1', 'open': 464.3, 'close': 464.8, 'high': 464.8, 'low': 464.3, 'volume': '11.62', 'turnover': '5397.945', 'confirm': False, 'cross_seq': 9713056403, 'timestamp': 1641475159431119}]
i use JSON.loads to unload the data, and then move in to the data by using: msg['data'] and get the above mentioned result.
Does anybody know how to reach into this block of data, makeing columns and stuff, or can anybody point me in the right direction.
I havent worked much with dictonaries before.
Thanks.
Btw i appreciate you making this library this is very good to work with.
Mathias.
Just stumbled upon this repo toady, really appreciate everyone's work!
When using query_kline
, query_mark_price_kline
on the HTTP class, trying to use from
in **kwargs will throw a SyntaxError since from
is a reserved keyword in Python.
Steps to reproduce:
import time
from pybit import HTTP
if __name__ == "__main__":
session = HTTP("https://api.bybit.com")
session.query_kline(symbol="BTCUSD", interval="1", from=int(time.time()) - 60 * 60)
I noticed you've used from_time
in the tests, but it appears the tests have gone stale. Will make a PR today.
Could you provide an example of how to parse data for pybit in Python? To get just the order_id from session.get_active_order(symbol="XRPUSDT") what would I need to do?
Hi there,
I have pybit v1.1.8 installed in my virtual environment. When I try to run my app I get the following error:
python data.py
python(2411,0x112488e00) malloc: can't allocate region
:*** mach_vm_map(size=18446744071964418048, flags: 100) failed (error code=3)
python(2411,0x112488e00) malloc: *** set a breakpoint in malloc_error_break to debug
init_dgelsd failed init
Traceback (most recent call last):
File "/Users/rr/Developer/Python/bybit/data.py", line 1, in <module>
from classes.Client import Client
File "/Users/rr/Developer/Python/bybit/classes/Client.py", line 2, in <module>
from pybit import WebSocket
File "/Users/rr/Developer/Python/bybit/env/lib/python3.9/site-packages/pybit/__init__.py", line 24, in <module>
import websocket
File "/Users/rr/Developer/Python/bybit/env/lib/python3.9/site-packages/websocket/__init__.py", line 22, in <module>
from ._abnf import *
File "/Users/rr/Developer/Python/bybit/env/lib/python3.9/site-packages/websocket/_abnf.py", line 34, in <module>
import numpy
File "/Users/rr/Developer/Python/bybit/env/lib/python3.9/site-packages/numpy/__init__.py", line 286, in <module>
raise RuntimeError(msg)
RuntimeError: Polyfit sanity test emitted a warning, most likely due to using a buggy Accelerate backend. If you compiled yourself, see site.cfg.example for information. Otherwise report this to the vendor that provided NumPy.
RankWarning: Polyfit may be poorly conditioned
My data.py has the following code. When I commented line 2 the erorr disappears:
import bybit
from pybit import WebSocket
from config import API_KEY, API_SECRET
Do you have any idea what the issue can be?
Running python 3.9.0
Thanks for th help.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.