├── .DS_Store
├── .all-contributorsrc
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── MANIFEST.in
├── README.md
├── examples
├── direct_session.py
├── http_example_explanatory.py
├── http_example_quickstart.py
├── http_example_rsa_authentication.py
├── websocket_example_explanatory.py
├── websocket_example_quickstart.py
├── websocket_example_rsa_authentication.py
├── websocket_http_example_spread_trading_quickstart.py
├── websocket_trading_example_quickstart.py
└── wrapper_class.py
├── pybit
├── .vscode
│ └── settings.json
├── __init__.py
├── _helpers.py
├── _http_manager.py
├── _v5_account.py
├── _v5_asset.py
├── _v5_broker.py
├── _v5_crypto_loan.py
├── _v5_earn.py
├── _v5_institutional_loan.py
├── _v5_market.py
├── _v5_misc.py
├── _v5_position.py
├── _v5_pre_upgrade.py
├── _v5_spot_leverage_token.py
├── _v5_spot_margin_trade.py
├── _v5_spread.py
├── _v5_trade.py
├── _v5_user.py
├── _websocket_stream.py
├── _websocket_trading.py
├── account.py
├── asset.py
├── broker.py
├── crypto_loan.py
├── earn.py
├── exceptions.py
├── helpers.py
├── institutional_loan.py
├── market.py
├── misc.py
├── position.py
├── pre_upgrade.py
├── spot_leverage_token.py
├── spot_margin_trade.py
├── spread.py
├── trade.py
├── unified_trading.py
└── user.py
├── requirements.txt
├── setup.cfg
├── setup.py
└── tests
├── __init__.py
└── test_pybit.py
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bybit-exchange/pybit/63cb8922ccae000cae0394c094ada3c07723b371/.DS_Store
--------------------------------------------------------------------------------
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "verata-veritatis",
10 | "name": "verata-veritatis",
11 | "avatar_url": "https://avatars0.githubusercontent.com/u/9677388?v=4",
12 | "profile": "https://github.com/verata-veritatis",
13 | "contributions": [
14 | "code",
15 | "doc"
16 | ]
17 | },
18 | {
19 | "login": "APF20",
20 | "name": "APF20",
21 | "avatar_url": "https://avatars.githubusercontent.com/u/74583612?v=4",
22 | "profile": "https://github.com/APF20",
23 | "contributions": [
24 | "code"
25 | ]
26 | }
27 | ],
28 | "contributorsPerLine": 7,
29 | "projectName": "pybit",
30 | "projectOwner": "bybit-exchange",
31 | "repoType": "github",
32 | "repoHost": "https://github.com",
33 | "skipCi": true,
34 | "commitConvention": "angular"
35 | }
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
131 | # PyCharm idea files
132 | *.idea
133 |
134 | # VSCode files
135 | *.vscode
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "3.6"
4 | - "3.7"
5 | - "3.8"
6 | - "3.9"
7 | - "3.10"
8 | cache: pip
9 | before_install:
10 | - "pip install -U pip"
11 | - "export PYTHONPATH=$PYTHONPATH:$(pwd)"
12 | install:
13 | - pip install -r requirements.txt
14 | script:
15 | - python -m unittest
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright for portions of pybit are held by verata-veritatis, 2020. Since
4 | version 2.0.0, all other copyright are held by bybit-exchange, 2022.
5 |
6 | Copyright (c) 2020 verata-veritatis
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy
9 | of this software and associated documentation files (the "Software"), to deal
10 | in the Software without restriction, including without limitation the rights
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | copies of the Software, and to permit persons to whom the Software is
13 | furnished to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in all
16 | copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 | SOFTWARE.
25 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE
2 | include README.md
3 | include CHANGELOG.md
4 | recursive-include examples *
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pybit
2 |
3 | [](#contributors-)
4 |
5 |
6 | [](https://www.python.org/downloads/)
7 | [](https://pypi.org/project/pybit/)
8 | 
9 |
10 | Official Python3 API connector for Bybit's HTTP and WebSockets APIs.
11 |
12 | ## Table of Contents
13 |
14 | - [About](#about)
15 | - [Development](#development)
16 | - [Installation](#installation)
17 | - [Usage](#usage)
18 | - [Contact](#contact)
19 | - [Contributors](#contributors)
20 | - [Donations](#donations)
21 |
22 | ## About
23 | Put simply, `pybit` (Python + Bybit) is the official lightweight one-stop-shop module for the Bybit HTTP and WebSocket APIs. Originally created by [Verata Veritatis](https://github.com/verata-veritatis), it's now maintained by Bybit employees – however, you're still welcome to contribute!
24 |
25 | It was designed with the following vision in mind:
26 |
27 | > I was personally never a fan of auto-generated connectors that used a mosh-pit of various modules you didn't want (sorry, `bravado`) and wanted to build my own Python3-dedicated connector with very little external resources. The goal of the connector is to provide traders and developers with an easy-to-use high-performing module that has an active issue and discussion board leading to consistent improvements.
28 |
29 | ## Development
30 | `pybit` is being actively developed, and new Bybit API changes should arrive on `pybit` very quickly. `pybit` uses `requests` and `websocket-client` for its methods, alongside other built-in modules. Anyone is welcome to branch/fork the repository and add their own upgrades. If you think you've made substantial improvements to the module, submit a pull request and we'll gladly take a look.
31 |
32 | ## Installation
33 | `pybit` requires Python 3.9.1 or higher. The module can be installed manually or via [PyPI](https://pypi.org/project/pybit/) with `pip`:
34 | ```
35 | pip install pybit
36 | ```
37 |
38 | ## Usage
39 | You can retrieve a specific market like so:
40 | ```python
41 | from pybit.unified_trading import HTTP
42 | ```
43 | Create an HTTP session and connect via WebSocket for Inverse on mainnet:
44 | ```python
45 | session = HTTP(
46 | testnet=False,
47 | api_key="...",
48 | api_secret="...",
49 | )
50 | ```
51 | Information can be sent to, or retrieved from, the Bybit APIs:
52 |
53 | ```python
54 | # Get the orderbook of the USDT Perpetual, BTCUSDT
55 | session.get_orderbook(category="linear", symbol="BTCUSDT")
56 |
57 | # Create five long USDC Options orders.
58 | # (Currently, only USDC Options support sending orders in bulk.)
59 | payload = {"category": "option"}
60 | orders = [{
61 | "symbol": "BTC-30JUN23-20000-C",
62 | "side": "Buy",
63 | "orderType": "Limit",
64 | "qty": "0.1",
65 | "price": i,
66 | } for i in [15000, 15500, 16000, 16500, 16600]]
67 |
68 | payload["request"] = orders
69 | # Submit the orders in bulk.
70 | session.place_batch_order(payload)
71 | ```
72 | Check out the example python files or the list of endpoints below for more information on available
73 | endpoints and methods. Usage examples on the `HTTP` methods can
74 | be found in the [examples folder](https://github.com/bybit-exchange/pybit/tree/master/examples).
75 |
76 |
77 | ## Contact
78 | Reach out for support on your chosen platform:
79 | - [Telegram](https://t.me/BybitAPI) group chat
80 | - [Discord](https://discord.com/invite/VBwVwS2HUs) server
81 |
82 | ## Contributors
83 |
84 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
85 |
86 |
87 |
88 |
89 |
103 |
104 |
105 |
106 |
107 |
108 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
109 |
--------------------------------------------------------------------------------
/examples/direct_session.py:
--------------------------------------------------------------------------------
1 | from pybit.unified_trading import HTTP
2 |
3 |
4 | BYBIT_API_KEY = "api_key"
5 | BYBIT_API_SECRET = "api_secret"
6 | TESTNET = True # True means your API keys were generated on testnet.bybit.com
7 |
8 |
9 | # Create direct HTTP session instance
10 |
11 | session = HTTP(
12 | api_key=BYBIT_API_KEY,
13 | api_secret=BYBIT_API_SECRET,
14 | testnet=TESTNET,
15 | )
16 |
17 | # Place order
18 |
19 | response = session.place_order(
20 | category="spot",
21 | symbol="ETHUSDT",
22 | side="Sell",
23 | orderType="Market",
24 | qty="0.1",
25 | timeInForce="GTC",
26 | )
27 |
28 | # Example to cancel orders
29 |
30 | response = session.get_open_orders(
31 | category="linear",
32 | symbol="BTCUSDT",
33 | )
34 |
35 | orders = response["result"]["list"]
36 |
37 | for order in orders:
38 | if order["orderStatus"] == "Untriggered":
39 | session.cancel_order(
40 | category="linear",
41 | symbol=order["symbol"],
42 | orderId=order["orderId"],
43 | )
44 |
45 |
46 | # Batch cancel orders
47 |
48 | orders_to_cancel = [
49 | {"category": "option", "symbol": o["symbol"], "orderId": o["orderId"]}
50 | for o in response["result"]["list"]
51 | if o["orderStatus"] == "New"
52 | ]
53 |
54 | response = session.cancel_batch_order(
55 | category="option",
56 | request=orders_to_cancel,
57 | )
58 |
--------------------------------------------------------------------------------
/examples/http_example_explanatory.py:
--------------------------------------------------------------------------------
1 | """
2 | To see which endpoints are available, check the Bybit API documentation:
3 | https://bybit-exchange.github.io/docs/v5/market/kline
4 | """
5 |
6 | # Import HTTP from the unified_trading module.
7 | from pybit.unified_trading import HTTP
8 |
9 | # Set up logging (optional)
10 | import logging
11 | logging.basicConfig(filename="pybit.log", level=logging.DEBUG,
12 | format="%(asctime)s %(levelname)s %(message)s")
13 |
14 |
15 | # You can create an authenticated or unauthenticated HTTP session.
16 | # You can skip authentication by not passing any value for the key and secret.
17 |
18 | session = HTTP(
19 | testnet=True,
20 | api_key="...",
21 | api_secret="...",
22 | )
23 |
24 | # Get the orderbook of the USDT Perpetual, BTCUSDT
25 | print(session.get_orderbook(category="linear", symbol="BTCUSDT"))
26 | # Note how the "category" parameter determines the type of market to fetch this
27 | # data for. Look at the docstring of the get_orderbook to navigate to the API
28 | # documentation to see the supported categories for this and other endpoints.
29 |
30 | # Get wallet balance of the Unified Trading Account
31 | print(session.get_wallet_balance(accountType="UNIFIED"))
32 |
33 | # Place an order on that USDT Perpetual
34 | print(session.place_order(
35 | category="linear",
36 | symbol="BTCUSDT",
37 | side="Buy",
38 | orderType="Market",
39 | qty="0.001",
40 | ))
41 |
42 | # Place an order on the Inverse Contract, ETHUSD
43 | print(session.place_order(
44 | category="inverse",
45 | symbol="ETHUSD",
46 | side="Buy",
47 | orderType="Market",
48 | qty="1",
49 | ))
50 |
51 | # Place an order on the Spot market, MNTUSDT
52 | print(session.place_order(
53 | category="spot",
54 | symbol="MNTUSDT",
55 | side="Buy",
56 | orderType="Market",
57 | qty="10",
58 | ))
59 |
--------------------------------------------------------------------------------
/examples/http_example_quickstart.py:
--------------------------------------------------------------------------------
1 | from pybit.unified_trading import HTTP
2 |
3 | session = HTTP(
4 | testnet=True,
5 | api_key="...",
6 | api_secret="...",
7 | )
8 |
9 | print(session.get_orderbook(category="linear", symbol="BTCUSDT"))
10 |
11 | print(session.place_order(
12 | category="linear",
13 | symbol="BTCUSDT",
14 | side="Buy",
15 | orderType="Market",
16 | qty="0.001",
17 | ))
18 |
--------------------------------------------------------------------------------
/examples/http_example_rsa_authentication.py:
--------------------------------------------------------------------------------
1 | """
2 | RSA authentication is an alternative way to create your API key.
3 | Learn about RSA authentication here:
4 | https://www.bybit.com/en-US/help-center/bybitHC_Article?id=000001923&language=en_US
5 | """
6 | from pybit.unified_trading import HTTP
7 |
8 | # The API key is given to you by Bybit's API management page after inputting
9 | # your RSA public key.
10 | api_key = "xxxxxxx"
11 | # The API secret is your RSA generated private key. It begins with the line:
12 | # -----BEGIN PRIVATE KEY-----
13 | with open("my_rsa_private_key.pem", "r") as private_key_file:
14 | api_secret = private_key_file.read()
15 |
16 | session = HTTP(
17 | testnet=True,
18 | rsa_authentication=True, # <-- Must be True.
19 | api_key=api_key,
20 | api_secret=api_secret,
21 | log_requests=True,
22 | )
23 |
24 | print(session.get_positions(category="linear", symbol="BTCUSDT"))
25 |
26 |
27 |
--------------------------------------------------------------------------------
/examples/websocket_example_explanatory.py:
--------------------------------------------------------------------------------
1 | """
2 | To see which WebSocket topics are available, check the Bybit API documentation:
3 | https://bybit-exchange.github.io/docs/v5/websocket/public/orderbook
4 | """
5 |
6 | from time import sleep
7 |
8 | # Import WebSocket from the unified_trading module.
9 | from pybit.unified_trading import WebSocket
10 |
11 | # Set up logging (optional)
12 | import logging
13 | logging.basicConfig(filename="pybit.log", level=logging.DEBUG,
14 | format="%(asctime)s %(levelname)s %(message)s")
15 |
16 |
17 | # Connect with authentication!
18 | # Here, we are connecting to the "linear" WebSocket, which will deliver public
19 | # market data for the linear (USDT) perpetuals.
20 | # The available channel types are, for public market data:
21 | # inverse – Inverse Contracts;
22 | # linear – USDT Perpetual, USDC Contracts;
23 | # spot – Spot Trading;
24 | # option – USDC Options;
25 | # and for private data:
26 | # private – Private account data for all markets.
27 |
28 | ws = WebSocket(
29 | testnet=True,
30 | channel_type="linear",
31 | )
32 |
33 | ws_private = WebSocket(
34 | testnet=True,
35 | channel_type="private",
36 | api_key="...",
37 | api_secret="...",
38 | trace_logging=True,
39 | )
40 |
41 |
42 | # Let's fetch the orderbook for BTCUSDT. First, we'll define a function.
43 | def handle_orderbook(message):
44 | # I will be called every time there is new orderbook data!
45 | print(message)
46 | orderbook_data = message["data"]
47 |
48 | # Now, we can subscribe to the orderbook stream and pass our arguments:
49 | # our depth, symbol, and callback function.
50 | ws.orderbook_stream(50, "BTCUSDT", handle_orderbook)
51 |
52 |
53 | # To subscribe to private data, the process is the same:
54 | def handle_position(message):
55 | # I will be called every time there is new position data!
56 | print(message)
57 |
58 | ws_private.position_stream(handle_position)
59 |
60 |
61 | while True:
62 | # This while loop is required for the program to run. You may execute
63 | # additional code for your trading logic here.
64 | sleep(1)
65 |
--------------------------------------------------------------------------------
/examples/websocket_example_quickstart.py:
--------------------------------------------------------------------------------
1 | from pybit.unified_trading import WebSocket
2 | from time import sleep
3 |
4 | ws = WebSocket(
5 | testnet=True,
6 | channel_type="linear",
7 | )
8 |
9 | def handle_message(message):
10 | print(message)
11 |
12 | ws.orderbook_stream(50, "BTCUSDT", handle_message)
13 |
14 | while True:
15 | sleep(1)
16 |
--------------------------------------------------------------------------------
/examples/websocket_example_rsa_authentication.py:
--------------------------------------------------------------------------------
1 | """
2 | RSA authentication is an alternative way to create your API key.
3 | Learn about RSA authentication here:
4 | https://www.bybit.com/en-US/help-center/bybitHC_Article?id=000001923&language=en_US
5 | """
6 | from pybit.unified_trading import WebSocket
7 | from time import sleep
8 |
9 | # The API key is given to you by Bybit's API management page after inputting
10 | # your RSA public key.
11 | api_key = "xxxxxxxx"
12 | # The API secret is your RSA generated private key. It begins with the line:
13 | # -----BEGIN PRIVATE KEY-----
14 | with open("my_rsa_private_key.pem", "r") as private_key_file:
15 | api_secret = private_key_file.read()
16 |
17 | ws = WebSocket(
18 | testnet=True,
19 | channel_type="private",
20 | rsa_authentication=True, # <-- Must be True.
21 | api_key=api_key,
22 | api_secret=api_secret,
23 | trace_logging=True,
24 | )
25 |
26 | def handle_message(message):
27 | print(message)
28 |
29 | ws.order_stream(
30 | callback=handle_message
31 | )
32 |
33 | while True:
34 | sleep(1)
35 |
--------------------------------------------------------------------------------
/examples/websocket_http_example_spread_trading_quickstart.py:
--------------------------------------------------------------------------------
1 | from pybit.unified_trading import WebSocket
2 | from pybit.unified_trading import WebsocketSpreadTrading
3 | from pybit.unified_trading import SpreadHTTP
4 | from time import sleep
5 |
6 |
7 | # The public websocket for spread trading
8 | ws = WebsocketSpreadTrading(
9 | testnet=True,
10 | trace_logging=True,
11 | )
12 |
13 | # The private websocket for spread trading (same connection as normal)
14 | ws_private = WebSocket(
15 | testnet=True,
16 | trace_logging=True,
17 | channel_type="private",
18 | api_key="xxxxxxxxxxxxxxxxxx",
19 | api_secret="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
20 | )
21 |
22 | def handle_message(message):
23 | print(message)
24 |
25 | ws.orderbook_stream(25, "SOLUSDT_SOL/USDT", handle_message)
26 | ws.trade_stream("SOLUSDT_SOL/USDT", handle_message)
27 | ws.ticker_stream("SOLUSDT_SOL/USDT", handle_message)
28 |
29 | ws_private.spread_order_stream(handle_message)
30 | ws_private.spread_execution_stream(handle_message)
31 |
32 |
33 | session = SpreadHTTP(
34 | testnet=True,
35 | api_key="xxxxxxxxxxxxxxxxxx",
36 | api_secret="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
37 | )
38 |
39 | symbol = "BTCUSDT_BTC/USDT"
40 | print(session.get_instruments_info(symbol=symbol))
41 | print(session.place_order(
42 | symbol=symbol,
43 | side="Buy",
44 | orderType="Market",
45 | qty="0.001",
46 | ))
47 |
48 |
49 | while True:
50 | sleep(1)
--------------------------------------------------------------------------------
/examples/websocket_trading_example_quickstart.py:
--------------------------------------------------------------------------------
1 | from pybit.unified_trading import WebSocketTrading
2 | from time import sleep
3 |
4 | ws_trading = WebSocketTrading(
5 | testnet=True,
6 | api_key="...",
7 | api_secret="...",
8 | )
9 |
10 | def handle_place_order_message(message):
11 | # Receive the orderId, amend the order, then cancel it.
12 | print(message)
13 | sleep(1)
14 | ws_trading.amend_order(
15 | handle_amend_order_message,
16 | category="linear",
17 | symbol="BTCUSDT",
18 | order_id=message["data"]["orderId"],
19 | qty="0.002"
20 | )
21 | sleep(1)
22 | ws_trading.cancel_order(
23 | handle_cancel_order_message,
24 | category="linear",
25 | symbol="BTCUSDT",
26 | order_id=message["data"]["orderId"]
27 | )
28 |
29 | def handle_amend_order_message(message):
30 | print(message)
31 |
32 | def handle_cancel_order_message(message):
33 | print(message)
34 |
35 | def handle_batch_place_order_message(message):
36 | print(message)
37 |
38 | while True:
39 | # Simplistic example; place an order every 10 seconds.
40 | ws_trading.place_order(
41 | handle_place_order_message,
42 | category="linear",
43 | symbol="BTCUSDT",
44 | side="Buy",
45 | orderType="Limit",
46 | price="60000",
47 | qty="0.001"
48 | )
49 | sleep(1)
50 | # Batch place two orders. Note that batch amend and cancel can be used in
51 | # the same way – by passing a list of dictionaries to the request parameter.
52 | request = [
53 | {
54 | "symbol": "BTCUSDT",
55 | "side": "Buy",
56 | "orderType": "Limit",
57 | "qty": "0.001",
58 | "price": "82000",
59 | "timeInForce": "GTC",
60 | "positionIdx": 0
61 | },
62 | {
63 | "symbol": "BTCUSDT",
64 | "side": "Buy",
65 | "orderType": "Limit",
66 | "qty": "0.002",
67 | "price": "82005",
68 | "timeInForce": "GTC",
69 | "positionIdx": 0
70 | }
71 | ]
72 | ws_trading.place_batch_order(
73 | handle_batch_place_order_message,
74 | category="linear",
75 | request=request
76 | )
77 | sleep(10)
78 |
--------------------------------------------------------------------------------
/examples/wrapper_class.py:
--------------------------------------------------------------------------------
1 | from pybit.unified_trading import HTTP
2 |
3 |
4 | BYBIT_API_KEY = "api_key"
5 | BYBIT_API_SECRET = "api_secret"
6 | TESTNET = True # True means your API keys were generated on testnet.bybit.com
7 |
8 |
9 | class BybitWrapper:
10 | def __init__(
11 | self,
12 | api_key: str = None,
13 | api_secret: str = None,
14 | testnet: bool = None,
15 | ):
16 | self.instance = HTTP(
17 | api_key=api_key,
18 | api_secret=api_secret,
19 | testnet=testnet,
20 | log_requests=True,
21 | )
22 |
23 | def get_max_leverage(self, category: str, symbol: str):
24 | """
25 | Get max leverage for symbol in category
26 | """
27 | symbols = self.instance.get_instruments_info(category=category)
28 | result = symbols["result"]["list"]
29 | return [d for d in result if d["symbol"] == symbol][0][
30 | "leverageFilter"
31 | ]["maxLeverage"]
32 |
33 | def get_kline_data(self, symbol: str = "BTCUSDT"):
34 | kline_data = self.instance.get_kline(
35 | category="linear",
36 | symbol=symbol,
37 | interval=60,
38 | )["result"]
39 |
40 | # Getting 0 element from list
41 | data_list = kline_data["list"][0]
42 |
43 | print(f"Open price: {data_list[1]}")
44 | print(f"High price: {data_list[2]}")
45 | print(f"Low price: {data_list[3]}")
46 | print(f"Close price: {data_list[4]}")
47 |
48 | def cancel_all_orders(
49 | self,
50 | category: str = "spot",
51 | symbol: str = "ETHUSDT",
52 | ) -> dict:
53 | """
54 | Cancel orders by category and symbol
55 | """
56 | return self.instance.cancel_all_orders(
57 | category=category, symbol=symbol
58 | )
59 |
60 | def get_realtime_orders(
61 | self,
62 | category: str,
63 | symbol: str = None,
64 | ) -> dict:
65 | """
66 | Get realtime orders
67 | """
68 | return self.instance.get_open_orders(
69 | category=category,
70 | symbol=symbol,
71 | )
72 |
73 | def get_order_history(self, **kwargs) -> dict:
74 | return self.instance.get_order_history(**kwargs)["result"]["list"]
75 |
76 |
77 | # Initialize wrapper instance
78 |
79 | wrapper = BybitWrapper(
80 | api_key=BYBIT_API_KEY,
81 | api_secret=BYBIT_API_SECRET,
82 | testnet=TESTNET,
83 | )
84 |
85 | # Actual usage
86 | response = wrapper.get_realtime_orders(category="linear", symbol="ETHUSDT")
87 |
--------------------------------------------------------------------------------
/pybit/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.analysis.typeCheckingMode": "off",
3 | "python.formatting.provider": "black",
4 | "python.formatting.blackArgs": ["--line-length", "79"],
5 | "python.linting.enabled": true
6 | }
--------------------------------------------------------------------------------
/pybit/__init__.py:
--------------------------------------------------------------------------------
1 | VERSION = "5.11.0"
2 |
--------------------------------------------------------------------------------
/pybit/_helpers.py:
--------------------------------------------------------------------------------
1 | import time
2 | import re
3 | import copy
4 |
5 |
6 | def generate_timestamp():
7 | """
8 | Return a millisecond integer timestamp.
9 | """
10 | return int(time.time() * 10**3)
11 |
12 |
13 | def identify_ws_method(input_wss_url, wss_dictionary):
14 | """
15 | This method matches the input_wss_url with a particular WSS method. This
16 | helps ensure that, when subscribing to a custom topic, the topic
17 | subscription message is sent down the correct WSS connection.
18 | """
19 | path = re.compile(r"(wss://)?([^/\s]+)(.*)")
20 | input_wss_url_path = path.match(input_wss_url).group(3)
21 | for wss_url, function_call in wss_dictionary.items():
22 | wss_url_path = path.match(wss_url).group(3)
23 | if input_wss_url_path == wss_url_path:
24 | return function_call
25 |
26 |
27 | def find_index(source, target, key):
28 | """
29 | Find the index in source list of the targeted ID.
30 | """
31 | return next(i for i, j in enumerate(source) if j[key] == target[key])
32 |
33 |
34 | def make_private_args(args):
35 | """
36 | Exists to pass on the user's arguments to a lower-level class without
37 | giving the user access to that classes attributes (ie, passing on args
38 | without inheriting the parent class).
39 | """
40 | args.pop("self")
41 | return args
42 |
43 |
44 | def make_public_kwargs(private_kwargs):
45 | public_kwargs = copy.deepcopy(private_kwargs)
46 | public_kwargs.pop("api_key", "")
47 | public_kwargs.pop("api_secret", "")
48 | return public_kwargs
49 |
50 |
51 | def are_connections_connected(active_connections):
52 | for connection in active_connections:
53 | if not connection.is_connected():
54 | return False
55 | return True
56 |
57 |
58 | def is_inverse_contract(symbol: str):
59 | if re.search(r"(USD)([HMUZ]\d\d|$)", symbol):
60 | return True
61 |
62 |
63 | def is_usdt_perpetual(symbol: str):
64 | if symbol.endswith("USDT"):
65 | return True
66 |
67 |
68 | def is_usdc_perpetual(symbol: str):
69 | if symbol.endswith("USDC"):
70 | return True
71 |
72 |
73 | def is_usdc_option(symbol: str):
74 | if re.search(r"[A-Z]{3}-.*-[PC]$", symbol):
75 | return True
76 |
--------------------------------------------------------------------------------
/pybit/_http_manager.py:
--------------------------------------------------------------------------------
1 | from collections import defaultdict
2 | from dataclasses import dataclass, field
3 | import time
4 | import hmac
5 | import hashlib
6 | from Crypto.Hash import SHA256
7 | from Crypto.PublicKey import RSA
8 | from Crypto.Signature import PKCS1_v1_5
9 | import base64
10 | import json
11 | import logging
12 | import requests
13 |
14 | from datetime import datetime as dt, timezone
15 |
16 | from .exceptions import FailedRequestError, InvalidRequestError
17 | from . import _helpers
18 |
19 | # Requests will use simplejson if available.
20 | try:
21 | from simplejson.errors import JSONDecodeError
22 | except ImportError:
23 | from json.decoder import JSONDecodeError
24 |
25 | HTTP_URL = "https://{SUBDOMAIN}.{DOMAIN}.{TLD}"
26 | SUBDOMAIN_TESTNET = "api-testnet"
27 | SUBDOMAIN_MAINNET = "api"
28 | DEMO_SUBDOMAIN_TESTNET = "api-demo-testnet"
29 | DEMO_SUBDOMAIN_MAINNET = "api-demo"
30 | DOMAIN_MAIN = "bybit"
31 | DOMAIN_ALT = "bytick"
32 | TLD_MAIN = "com"
33 | TLD_NL = "nl"
34 | TLD_HK = "com.hk"
35 |
36 |
37 | def generate_signature(use_rsa_authentication, secret, param_str):
38 | def generate_hmac():
39 | hash = hmac.new(
40 | bytes(secret, "utf-8"),
41 | param_str.encode("utf-8"),
42 | hashlib.sha256,
43 | )
44 | return hash.hexdigest()
45 |
46 | def generate_rsa():
47 | hash = SHA256.new(param_str.encode("utf-8"))
48 | encoded_signature = base64.b64encode(
49 | PKCS1_v1_5.new(RSA.importKey(secret)).sign(
50 | hash
51 | )
52 | )
53 | return encoded_signature.decode()
54 |
55 | if not use_rsa_authentication:
56 | return generate_hmac()
57 | else:
58 | return generate_rsa()
59 |
60 |
61 | @dataclass
62 | class _V5HTTPManager:
63 | testnet: bool = field(default=False)
64 | domain: str = field(default=DOMAIN_MAIN)
65 | tld: str = field(default=TLD_MAIN)
66 | demo: bool = field(default=False)
67 | rsa_authentication: str = field(default=False)
68 | api_key: str = field(default=None)
69 | api_secret: str = field(default=None)
70 | logging_level: logging = field(default=logging.INFO)
71 | log_requests: bool = field(default=False)
72 | timeout: int = field(default=10)
73 | recv_window: bool = field(default=5000)
74 | force_retry: bool = field(default=False)
75 | retry_codes: defaultdict[dict] = field(default_factory=dict)
76 | ignore_codes: dict = field(default_factory=dict)
77 | max_retries: bool = field(default=3)
78 | retry_delay: bool = field(default=3)
79 | referral_id: bool = field(default=None)
80 | record_request_time: bool = field(default=False)
81 | return_response_headers: bool = field(default=False)
82 |
83 | def __post_init__(self):
84 | subdomain = SUBDOMAIN_TESTNET if self.testnet else SUBDOMAIN_MAINNET
85 | domain = DOMAIN_MAIN if not self.domain else self.domain
86 | if self.demo:
87 | if self.testnet:
88 | subdomain = DEMO_SUBDOMAIN_TESTNET
89 | else:
90 | subdomain = DEMO_SUBDOMAIN_MAINNET
91 | url = HTTP_URL.format(SUBDOMAIN=subdomain, DOMAIN=domain, TLD=self.tld)
92 | self.endpoint = url
93 |
94 | if not self.ignore_codes:
95 | self.ignore_codes = set()
96 | if not self.retry_codes:
97 | self.retry_codes = {10002, 10006, 30034, 30035, 130035, 130150}
98 | self.logger = logging.getLogger(__name__)
99 | if len(logging.root.handlers) == 0:
100 | # no handler on root logger set -> we add handler just for this logger to not mess with custom logic from outside
101 | handler = logging.StreamHandler()
102 | handler.setFormatter(
103 | logging.Formatter(
104 | fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
105 | datefmt="%Y-%m-%d %H:%M:%S",
106 | )
107 | )
108 | handler.setLevel(self.logging_level)
109 | self.logger.addHandler(handler)
110 |
111 | self.logger.debug("Initializing HTTP session.")
112 |
113 | self.client = requests.Session()
114 | self.client.headers.update(
115 | {
116 | "Content-Type": "application/json",
117 | "Accept": "application/json",
118 | }
119 | )
120 | if self.referral_id:
121 | self.client.headers.update({"Referer": self.referral_id})
122 |
123 | @staticmethod
124 | def prepare_payload(method, parameters):
125 | """
126 | Prepares the request payload and validates parameter value types.
127 | """
128 |
129 | def cast_values():
130 | string_params = [
131 | "qty",
132 | "price",
133 | "triggerPrice",
134 | "takeProfit",
135 | "stopLoss",
136 | ]
137 | integer_params = ["positionIdx"]
138 | for key, value in parameters.items():
139 | if key in string_params:
140 | if type(value) != str:
141 | parameters[key] = str(value)
142 | elif key in integer_params:
143 | if type(value) != int:
144 | parameters[key] = int(value)
145 |
146 | if method == "GET":
147 | payload = "&".join(
148 | [
149 | str(k) + "=" + str(v)
150 | for k, v in sorted(parameters.items())
151 | if v is not None
152 | ]
153 | )
154 | return payload
155 | else:
156 | cast_values()
157 | return json.dumps(parameters)
158 |
159 | def _auth(self, payload, recv_window, timestamp):
160 | """
161 | Prepares authentication signature per Bybit API specifications.
162 | """
163 |
164 | if self.api_key is None or self.api_secret is None:
165 | raise PermissionError("Authenticated endpoints require keys.")
166 |
167 | param_str = str(timestamp) + self.api_key + str(recv_window) + payload
168 |
169 | return generate_signature(
170 | self.rsa_authentication, self.api_secret, param_str
171 | )
172 |
173 | @staticmethod
174 | def _verify_string(params, key):
175 | if key in params:
176 | if not isinstance(params[key], str):
177 | return False
178 | else:
179 | return True
180 | return True
181 |
182 | def _submit_request(self, method=None, path=None, query=None, auth=False):
183 | """
184 | Submits the request to the API.
185 |
186 | Notes
187 | -------------------
188 | We use the params argument for the GET method, and data argument for
189 | the POST method. Dicts passed to the data argument must be
190 | JSONified prior to submitting request.
191 |
192 | """
193 |
194 | if query is None:
195 | query = {}
196 |
197 | # Store original recv_window.
198 | recv_window = self.recv_window
199 |
200 | # Bug fix: change floating whole numbers to integers to prevent
201 | # auth signature errors.
202 | if query is not None:
203 | for i in query.keys():
204 | if isinstance(query[i], float) and query[i] == int(query[i]):
205 | query[i] = int(query[i])
206 |
207 | # Remove params with None value from the request.
208 | query = {key: value for key, value in query.items()
209 | if value is not None}
210 |
211 | # Send request and return headers with body. Retry if failed.
212 | retries_attempted = self.max_retries
213 | req_params = None
214 |
215 | while True:
216 | retries_attempted -= 1
217 | if retries_attempted < 0:
218 | raise FailedRequestError(
219 | request=f"{method} {path}: {req_params}",
220 | message="Bad Request. Retries exceeded maximum.",
221 | status_code=400,
222 | time=dt.now(timezone.utc).strftime("%H:%M:%S"),
223 | resp_headers=None,
224 | )
225 |
226 | retries_remaining = f"{retries_attempted} retries remain."
227 |
228 | req_params = self.prepare_payload(method, query)
229 |
230 | # Authenticate if we are using a private endpoint.
231 | if auth:
232 | # Prepare signature.
233 | timestamp = _helpers.generate_timestamp()
234 | signature = self._auth(
235 | payload=req_params,
236 | recv_window=recv_window,
237 | timestamp=timestamp,
238 | )
239 | headers = {
240 | "Content-Type": "application/json",
241 | "X-BAPI-API-KEY": self.api_key,
242 | "X-BAPI-SIGN": signature,
243 | "X-BAPI-SIGN-TYPE": "2",
244 | "X-BAPI-TIMESTAMP": str(timestamp),
245 | "X-BAPI-RECV-WINDOW": str(recv_window),
246 | }
247 | else:
248 | headers = {}
249 |
250 | if method == "GET":
251 | if req_params:
252 | r = self.client.prepare_request(
253 | requests.Request(
254 | method, path + f"?{req_params}", headers=headers
255 | )
256 | )
257 | else:
258 | r = self.client.prepare_request(
259 | requests.Request(method, path, headers=headers)
260 | )
261 | else:
262 | r = self.client.prepare_request(
263 | requests.Request(
264 | method, path, data=req_params, headers=headers
265 | )
266 | )
267 |
268 | # Log the request.
269 | if self.log_requests:
270 | if req_params:
271 | self.logger.debug(
272 | f"Request -> {method} {path}. Body: {req_params}. "
273 | f"Headers: {r.headers}"
274 | )
275 | else:
276 | self.logger.debug(
277 | f"Request -> {method} {path}. Headers: {r.headers}"
278 | )
279 |
280 | # Attempt the request.
281 | try:
282 | s = self.client.send(r, timeout=self.timeout)
283 |
284 | # If requests fires an error, retry.
285 | except (
286 | requests.exceptions.ReadTimeout,
287 | requests.exceptions.SSLError,
288 | requests.exceptions.ConnectionError,
289 | ) as e:
290 | if self.force_retry:
291 | self.logger.error(f"{e}. {retries_remaining}")
292 | time.sleep(self.retry_delay)
293 | continue
294 | else:
295 | raise e
296 |
297 | # Check HTTP status code before trying to decode JSON.
298 | if s.status_code != 200:
299 | if s.status_code == 403:
300 | error_msg = "You have breached the IP rate limit or your IP is from the USA."
301 | else:
302 | error_msg = "HTTP status code is not 200."
303 | self.logger.debug(f"Response text: {s.text}")
304 | raise FailedRequestError(
305 | request=f"{method} {path}: {req_params}",
306 | message=error_msg,
307 | status_code=s.status_code,
308 | time=dt.now(timezone.utc).strftime("%H:%M:%S"),
309 | resp_headers=s.headers,
310 | )
311 |
312 | # Convert response to dictionary, or raise if requests error.
313 | try:
314 | s_json = s.json()
315 |
316 | # If we have trouble converting, handle the error and retry.
317 | except JSONDecodeError as e:
318 | if self.force_retry:
319 | self.logger.error(f"{e}. {retries_remaining}")
320 | time.sleep(self.retry_delay)
321 | continue
322 | else:
323 | self.logger.debug(f"Response text: {s.text}")
324 | raise FailedRequestError(
325 | request=f"{method} {path}: {req_params}",
326 | message="Conflict. Could not decode JSON.",
327 | status_code=409,
328 | time=dt.now(timezone.utc).strftime("%H:%M:%S"),
329 | resp_headers=s.headers,
330 | )
331 |
332 | ret_code = "retCode"
333 | ret_msg = "retMsg"
334 |
335 | # If Bybit returns an error, raise.
336 | if s_json[ret_code]:
337 | # Generate error message.
338 | error_msg = f"{s_json[ret_msg]} (ErrCode: {s_json[ret_code]})"
339 |
340 | # Set default retry delay.
341 | delay_time = self.retry_delay
342 |
343 | # Retry non-fatal whitelisted error requests.
344 | if s_json[ret_code] in self.retry_codes:
345 | # 10002, recv_window error; add 2.5 seconds and retry.
346 | if s_json[ret_code] == 10002:
347 | error_msg += ". Added 2.5 seconds to recv_window"
348 | recv_window += 2500
349 |
350 | # 10006, rate limit error; wait until
351 | # X-Bapi-Limit-Reset-Timestamp and retry.
352 | elif s_json[ret_code] == 10006:
353 | self.logger.error(
354 | f"{error_msg}. Hit the API rate limit. "
355 | f"Sleeping, then trying again. Request: {path}"
356 | )
357 |
358 | # Calculate how long we need to wait in milliseconds.
359 | limit_reset_time = int(s.headers["X-Bapi-Limit-Reset-Timestamp"])
360 | limit_reset_str = dt.fromtimestamp(limit_reset_time / 10**3).strftime(
361 | "%H:%M:%S.%f")[:-3]
362 | delay_time = (int(limit_reset_time) - _helpers.generate_timestamp()) / 10**3
363 | error_msg = (
364 | f"API rate limit will reset at {limit_reset_str}. "
365 | f"Sleeping for {int(delay_time * 10**3)} milliseconds"
366 | )
367 |
368 | # Log the error.
369 | self.logger.error(f"{error_msg}. {retries_remaining}")
370 | time.sleep(delay_time)
371 | continue
372 |
373 | elif s_json[ret_code] in self.ignore_codes:
374 | pass
375 |
376 | else:
377 | raise InvalidRequestError(
378 | request=f"{method} {path}: {req_params}",
379 | message=s_json[ret_msg],
380 | status_code=s_json[ret_code],
381 | time=dt.now(timezone.utc).strftime("%H:%M:%S"),
382 | resp_headers=s.headers,
383 | )
384 | else:
385 | if self.log_requests:
386 | self.logger.debug(
387 | f"Response headers: {s.headers}"
388 | )
389 |
390 | if self.return_response_headers:
391 | return s_json, s.elapsed, s.headers,
392 | elif self.record_request_time:
393 | return s_json, s.elapsed
394 | else:
395 | return s_json
396 |
--------------------------------------------------------------------------------
/pybit/_v5_account.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .account import Account
3 |
4 |
5 | class AccountHTTP(_V5HTTPManager):
6 | def get_wallet_balance(self, **kwargs):
7 | """Obtain wallet balance, query asset information of each currency, and account risk rate information under unified margin mode.
8 | By default, currency information with assets or liabilities of 0 is not returned.
9 |
10 | Required args:
11 | accountType (string): Account type
12 | Unified account: UNIFIED
13 | Normal account: CONTRACT
14 |
15 | Returns:
16 | Request results as dictionary.
17 |
18 | Additional information:
19 | https://bybit-exchange.github.io/docs/v5/account/wallet-balance
20 | """
21 | return self._submit_request(
22 | method="GET",
23 | path=f"{self.endpoint}{Account.GET_WALLET_BALANCE}",
24 | query=kwargs,
25 | auth=True,
26 | )
27 |
28 | def get_transferable_amount(self, **kwargs):
29 | """Query the available amount to transfer of a specific coin in the Unified wallet.
30 |
31 | Required args:
32 | coinName (string): Coin name, uppercase only
33 |
34 | Returns:
35 | Request results as dictionary.
36 |
37 | Additional information:
38 | https://bybit-exchange.github.io/docs/v5/account/unified-trans-amnt
39 | """
40 | return self._submit_request(
41 | method="GET",
42 | path=f"{self.endpoint}{Account.GET_TRANSFERABLE_AMOUNT}",
43 | query=kwargs,
44 | auth=True,
45 | )
46 |
47 | def upgrade_to_unified_trading_account(self, **kwargs):
48 | """Upgrade Unified Account
49 |
50 | Returns:
51 | Request results as dictionary.
52 |
53 | Additional information:
54 | https://bybit-exchange.github.io/docs/v5/account/upgrade-unified-account
55 | """
56 | return self._submit_request(
57 | method="POST",
58 | path=f"{self.endpoint}{Account.UPGRADE_TO_UNIFIED_ACCOUNT}",
59 | query=kwargs,
60 | auth=True,
61 | )
62 |
63 | def get_borrow_history(self, **kwargs):
64 | """Get interest records, sorted in reverse order of creation time.
65 |
66 | Returns:
67 | Request results as dictionary.
68 |
69 | Additional information:
70 | https://bybit-exchange.github.io/docs/v5/account/borrow-history
71 | """
72 | return self._submit_request(
73 | method="GET",
74 | path=f"{self.endpoint}{Account.GET_BORROW_HISTORY}",
75 | query=kwargs,
76 | auth=True,
77 | )
78 |
79 | def repay_liability(self, **kwargs):
80 | """You can manually repay the liabilities of the Unified account
81 |
82 | Returns:
83 | Request results as dictionary.
84 |
85 | Additional information:
86 | https://bybit-exchange.github.io/docs/v5/account/repay-liability
87 | """
88 | return self._submit_request(
89 | method="POST",
90 | path=f"{self.endpoint}{Account.REPAY_LIABILITY}",
91 | query=kwargs,
92 | auth=True,
93 | )
94 |
95 | def get_collateral_info(self, **kwargs):
96 | """Get the collateral information of the current unified margin account, including loan interest rate, loanable amount, collateral conversion rate, whether it can be mortgaged as margin, etc.
97 |
98 | Returns:
99 | Request results as dictionary.
100 |
101 | Additional information:
102 | https://bybit-exchange.github.io/docs/v5/account/collateral-info
103 | """
104 | return self._submit_request(
105 | method="GET",
106 | path=f"{self.endpoint}{Account.GET_COLLATERAL_INFO}",
107 | query=kwargs,
108 | auth=True,
109 | )
110 |
111 | def set_collateral_coin(self, **kwargs):
112 | """You can decide whether the assets in the Unified account needs to be collateral coins.
113 |
114 | Required args:
115 | coin (string): Coin name
116 | collateralSwitch (string): ON: switch on collateral, OFF: switch off collateral
117 |
118 | Returns:
119 | Request results as dictionary.
120 |
121 | Additional information:
122 | https://bybit-exchange.github.io/docs/v5/account/set-collateral
123 | """
124 | return self._submit_request(
125 | method="POST",
126 | path=f"{self.endpoint}{Account.SET_COLLATERAL_COIN}",
127 | query=kwargs,
128 | auth=True,
129 | )
130 |
131 | def batch_set_collateral_coin(self, **kwargs):
132 | """You can decide whether the assets in the Unified account needs to be collateral coins.
133 |
134 | Required args:
135 | request (array): Object
136 | > coin (string): Coin name
137 | > collateralSwitch (string): ON: switch on collateral, OFF: switch off collateral
138 |
139 | Returns:
140 | Request results as dictionary.
141 |
142 | Additional information:
143 | https://bybit-exchange.github.io/docs/v5/account/batch-set-collateral
144 | """
145 | return self._submit_request(
146 | method="POST",
147 | path=f"{self.endpoint}{Account.BATCH_SET_COLLATERAL_COIN}",
148 | query=kwargs,
149 | auth=True,
150 | )
151 |
152 | def get_coin_greeks(self, **kwargs):
153 | """Get current account Greeks information
154 |
155 | Returns:
156 | Request results as dictionary.
157 |
158 | Additional information:
159 | https://bybit-exchange.github.io/docs/v5/account/coin-greeks
160 | """
161 | return self._submit_request(
162 | method="GET",
163 | path=f"{self.endpoint}{Account.GET_COIN_GREEKS}",
164 | query=kwargs,
165 | auth=True,
166 | )
167 |
168 | def get_fee_rates(self, **kwargs):
169 | """Get the trading fee rate of derivatives.
170 |
171 | Returns:
172 | Request results as dictionary.
173 |
174 | Additional information:
175 | https://bybit-exchange.github.io/docs/v5/account/fee-rate
176 | """
177 | return self._submit_request(
178 | method="GET",
179 | path=f"{self.endpoint}{Account.GET_FEE_RATE}",
180 | query=kwargs,
181 | auth=True,
182 | )
183 |
184 | def get_account_info(self, **kwargs):
185 | """Query the margin mode configuration of the account.
186 |
187 | Returns:
188 | Request results as dictionary.
189 |
190 | Additional information:
191 | https://bybit-exchange.github.io/docs/v5/account/account-info
192 | """
193 | return self._submit_request(
194 | method="GET",
195 | path=f"{self.endpoint}{Account.GET_ACCOUNT_INFO}",
196 | query=kwargs,
197 | auth=True,
198 | )
199 |
200 | def get_transaction_log(self, **kwargs):
201 | """Query transaction logs in Unified account.
202 |
203 | Returns:
204 | Request results as dictionary.
205 |
206 | Additional information:
207 | https://bybit-exchange.github.io/docs/v5/account/transaction-log
208 | """
209 | return self._submit_request(
210 | method="GET",
211 | path=f"{self.endpoint}{Account.GET_TRANSACTION_LOG}",
212 | query=kwargs,
213 | auth=True,
214 | )
215 |
216 | def get_contract_transaction_log(self, **kwargs):
217 | """Query transaction logs in Classic account.
218 |
219 | Returns:
220 | Request results as dictionary.
221 |
222 | Additional information:
223 | https://bybit-exchange.github.io/docs/v5/account/contract-transaction-log
224 | """
225 | return self._submit_request(
226 | method="GET",
227 | path=f"{self.endpoint}{Account.GET_CONTRACT_TRANSACTION_LOG}",
228 | query=kwargs,
229 | auth=True,
230 | )
231 |
232 | def set_margin_mode(self, **kwargs):
233 | """Default is regular margin mode. This mode is valid for USDT Perp, USDC Perp and USDC Option.
234 |
235 | Required args:
236 | setMarginMode (string): REGULAR_MARGIN, PORTFOLIO_MARGIN
237 |
238 | Returns:
239 | Request results as dictionary.
240 |
241 | Additional information:
242 | https://bybit-exchange.github.io/docs/v5/account/set-margin-mode
243 | """
244 | return self._submit_request(
245 | method="POST",
246 | path=f"{self.endpoint}{Account.SET_MARGIN_MODE}",
247 | query=kwargs,
248 | auth=True,
249 | )
250 |
251 | def set_mmp(self, **kwargs):
252 | """
253 | Market Maker Protection (MMP) is an automated mechanism designed to protect market makers (MM) against liquidity risks
254 | and over-exposure in the market. It prevents simultaneous trade executions on quotes provided by the MM within a short time span.
255 | The MM can automatically pull their quotes if the number of contracts traded for an underlying asset exceeds the configured
256 | threshold within a certain time frame. Once MMP is triggered, any pre-existing MMP orders will be automatically canceled,
257 | and new orders tagged as MMP will be rejected for a specific duration — known as the frozen period — so that MM can
258 | reassess the market and modify the quotes.
259 |
260 | Required args:
261 | baseCoin (strin): Base coin
262 | window (string): Time window (ms)
263 | frozenPeriod (string): Frozen period (ms). "0" means the trade will remain frozen until manually reset
264 | qtyLimit (string): Trade qty limit (positive and up to 2 decimal places)
265 | deltaLimit (string): Delta limit (positive and up to 2 decimal places)
266 |
267 | Returns:
268 | Request results as dictionary.
269 |
270 | Additional information:
271 | https://bybit-exchange.github.io/docs/v5/account/set-mmp
272 | """
273 | return self._submit_request(
274 | method="POST",
275 | path=f"{self.endpoint}{Account.SET_MMP}",
276 | query=kwargs,
277 | auth=True,
278 | )
279 |
280 | def reset_mmp(self, **kwargs):
281 | """Once the mmp triggered, you can unfreeze the account by this endpoint
282 |
283 | Required args:
284 | baseCoin (string): Base coin
285 |
286 | Returns:
287 | Request results as dictionary.
288 |
289 | Additional information:
290 | https://bybit-exchange.github.io/docs/v5/account/reset-mmp
291 | """
292 | return self._submit_request(
293 | method="POST",
294 | path=f"{self.endpoint}{Account.RESET_MMP}",
295 | query=kwargs,
296 | auth=True,
297 | )
298 |
299 | def get_mmp_state(self, **kwargs):
300 | """Get MMP state
301 |
302 | Required args:
303 | baseCoin (string): Base coin
304 |
305 | Returns:
306 | Request results as dictionary.
307 |
308 | Additional information:
309 | https://bybit-exchange.github.io/docs/v5/account/get-mmp-state
310 | """
311 | return self._submit_request(
312 | method="GET",
313 | path=f"{self.endpoint}{Account.GET_MMP_STATE}",
314 | query=kwargs,
315 | auth=True,
316 | )
317 |
--------------------------------------------------------------------------------
/pybit/_v5_asset.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .asset import Asset
3 |
4 |
5 | class AssetHTTP(_V5HTTPManager):
6 | def get_coin_exchange_records(self, **kwargs):
7 | """Query the coin exchange records.
8 |
9 | Returns:
10 | Request results as dictionary.
11 |
12 | Additional information:
13 | https://bybit-exchange.github.io/docs/v5/asset/exchange
14 | """
15 | return self._submit_request(
16 | method="GET",
17 | path=f"{self.endpoint}{Asset.GET_COIN_EXCHANGE_RECORDS}",
18 | query=kwargs,
19 | auth=True,
20 | )
21 |
22 | def get_option_delivery_record(self, **kwargs):
23 | """Query option delivery records, sorted by deliveryTime in descending order
24 |
25 | Required args:
26 | category (string): Product type. option
27 |
28 | Returns:
29 | Request results as dictionary.
30 |
31 | Additional information:
32 | https://bybit-exchange.github.io/docs/v5/asset/option-delivery
33 | """
34 | return self._submit_request(
35 | method="GET",
36 | path=f"{self.endpoint}{Asset.GET_OPTION_DELIVERY_RECORD}",
37 | query=kwargs,
38 | auth=True,
39 | )
40 |
41 | def get_usdc_contract_settlement(self, **kwargs):
42 | """Query session settlement records of USDC perpetual and futures
43 |
44 | Required args:
45 | category (string): Product type. linear
46 |
47 | Returns:
48 | Request results as dictionary.
49 |
50 | Additional information:
51 | https://bybit-exchange.github.io/docs/v5/asset/settlement
52 | """
53 | return self._submit_request(
54 | method="GET",
55 | path=f"{self.endpoint}{Asset.GET_USDC_CONTRACT_SETTLEMENT}",
56 | query=kwargs,
57 | auth=True,
58 | )
59 |
60 | def get_spot_asset_info(self, **kwargs):
61 | """Query asset information
62 |
63 | Required args:
64 | accountType (string): Account type. SPOT
65 |
66 | Returns:
67 | Request results as dictionary.
68 |
69 | Additional information:
70 | https://bybit-exchange.github.io/docs/v5/asset/asset-info
71 | """
72 | return self._submit_request(
73 | method="GET",
74 | path=f"{self.endpoint}{Asset.GET_SPOT_ASSET_INFO}",
75 | query=kwargs,
76 | auth=True,
77 | )
78 |
79 | def get_coins_balance(self, **kwargs):
80 | """You could get all coin balance of all account types under the master account, and sub account.
81 |
82 | Required args:
83 | memberId (string): User Id. It is required when you use master api key to check sub account coin balance
84 | accountType (string): Account type
85 |
86 | Returns:
87 | Request results as dictionary.
88 |
89 | Additional information:
90 | https://bybit-exchange.github.io/docs/v5/asset/all-balance
91 | """
92 | return self._submit_request(
93 | method="GET",
94 | path=f"{self.endpoint}{Asset.GET_ALL_COINS_BALANCE}",
95 | query=kwargs,
96 | auth=True,
97 | )
98 |
99 | def get_coin_balance(self, **kwargs):
100 | """Query the balance of a specific coin in a specific account type. Supports querying sub UID's balance.
101 |
102 | Required args:
103 | memberId (string): UID. Required when querying sub UID balance
104 | accountType (string): Account type
105 |
106 | Returns:
107 | Request results as dictionary.
108 |
109 | Additional information:
110 | https://bybit-exchange.github.io/docs/v5/asset/account-coin-balance
111 | """
112 | return self._submit_request(
113 | method="GET",
114 | path=f"{self.endpoint}{Asset.GET_SINGLE_COIN_BALANCE}",
115 | query=kwargs,
116 | auth=True,
117 | )
118 |
119 | def get_transferable_coin(self, **kwargs):
120 | """Query the transferable coin list between each account type
121 |
122 | Required args:
123 | fromAccountType (string): From account type
124 | toAccountType (string): To account type
125 |
126 | Returns:
127 | Request results as dictionary.
128 |
129 | Additional information:
130 | https://bybit-exchange.github.io/docs/v5/asset/transferable-coin
131 | """
132 | return self._submit_request(
133 | method="GET",
134 | path=f"{self.endpoint}{Asset.GET_TRANSFERABLE_COIN}",
135 | query=kwargs,
136 | auth=True,
137 | )
138 |
139 | def create_internal_transfer(self, **kwargs):
140 | """Create the internal transfer between different account types under the same UID.
141 |
142 | Required args:
143 | transferId (string): UUID. Please manually generate a UUID
144 | coin (string): Coin
145 | amount (string): Amount
146 | fromAccountType (string): From account type
147 | toAccountType (string): To account type
148 |
149 | Returns:
150 | Request results as dictionary.
151 |
152 | Additional information:
153 | https://bybit-exchange.github.io/docs/v5/asset/create-inter-transfer
154 | """
155 | return self._submit_request(
156 | method="POST",
157 | path=f"{self.endpoint}{Asset.CREATE_INTERNAL_TRANSFER}",
158 | query=kwargs,
159 | auth=True,
160 | )
161 |
162 | def get_internal_transfer_records(self, **kwargs):
163 | """Query the internal transfer records between different account types under the same UID.
164 |
165 | Returns:
166 | Request results as dictionary.
167 |
168 | Additional information:
169 | https://bybit-exchange.github.io/docs/v5/asset/inter-transfer-list
170 | """
171 | return self._submit_request(
172 | method="GET",
173 | path=f"{self.endpoint}{Asset.GET_INTERNAL_TRANSFER_RECORDS}",
174 | query=kwargs,
175 | auth=True,
176 | )
177 |
178 | def get_sub_uid(self, **kwargs):
179 | """Query the sub UIDs under a main UID
180 |
181 | Returns:
182 | Request results as dictionary.
183 |
184 | Additional information:
185 | https://bybit-exchange.github.io/docs/v5/asset/sub-uid-list
186 | """
187 | return self._submit_request(
188 | method="GET",
189 | path=f"{self.endpoint}{Asset.GET_SUB_UID}",
190 | query=kwargs,
191 | auth=True,
192 | )
193 |
194 | def enable_universal_transfer_for_sub_uid(self, **kwargs):
195 | """Transfer between sub-sub or main-sub
196 |
197 | Required args:
198 | subMemberIds (array): This list has a single item. Separate multiple UIDs by comma, e.g., "uid1,uid2,uid3"
199 |
200 | Returns:
201 | Request results as dictionary.
202 |
203 | Additional information:
204 | https://bybit-exchange.github.io/docs/v5/asset/enable-unitransfer-subuid
205 | """
206 | self.logger.warning("enable_universal_transfer_for_sub_uid() is depreciated. You no longer need to configure transferable sub UIDs. Now, all sub UIDs are automatically enabled for universal transfer.")
207 | return self._submit_request(
208 | method="POST",
209 | path=f"{self.endpoint}{Asset.ENABLE_UT_FOR_SUB_UID}",
210 | query=kwargs,
211 | auth=True,
212 | )
213 |
214 | def create_universal_transfer(self, **kwargs):
215 | """Transfer between sub-sub or main-sub. Please make sure you have enabled universal transfer on your sub UID in advance.
216 |
217 | Required args:
218 | transferId (string): UUID. Please manually generate a UUID
219 | coin (string): Coin
220 | amount (string): Amount
221 | fromMemberId (integer): From UID
222 | toMemberId (integer): To UID
223 | fromAccountType (string): From account type
224 | toAccountType (string): To account type
225 |
226 | Returns:
227 | Request results as dictionary.
228 |
229 | Additional information:
230 | https://bybit-exchange.github.io/docs/v5/asset/unitransfer
231 | """
232 | return self._submit_request(
233 | method="POST",
234 | path=f"{self.endpoint}{Asset.CREATE_UNIVERSAL_TRANSFER}",
235 | query=kwargs,
236 | auth=True,
237 | )
238 |
239 | def get_universal_transfer_records(self, **kwargs):
240 | """Query universal transfer records
241 |
242 | Returns:
243 | Request results as dictionary.
244 |
245 | Additional information:
246 | https://bybit-exchange.github.io/docs/v5/asset/unitransfer-list
247 | """
248 | return self._submit_request(
249 | method="GET",
250 | path=f"{self.endpoint}{Asset.GET_UNIVERSAL_TRANSFER_RECORDS}",
251 | query=kwargs,
252 | auth=True,
253 | )
254 |
255 | def get_allowed_deposit_coin_info(self, **kwargs):
256 | """Query allowed deposit coin information. To find out paired chain of coin, please refer coin info api.
257 |
258 | Returns:
259 | Request results as dictionary.
260 |
261 | Additional information:
262 | https://bybit-exchange.github.io/docs/v5/asset/deposit-coin-spec
263 | """
264 | return self._submit_request(
265 | method="GET",
266 | path=f"{self.endpoint}{Asset.GET_ALLOWED_DEPOSIT_COIN_INFO}",
267 | query=kwargs,
268 | auth=True,
269 | )
270 |
271 | def set_deposit_account(self, **kwargs):
272 | """Set auto transfer account after deposit. The same function as the setting for Deposit on web GUI
273 |
274 | Required args:
275 | accountType (string): Account type: UNIFIED,SPOT,OPTION,CONTRACT,FUND
276 |
277 | Returns:
278 | Request results as dictionary.
279 |
280 | Additional information:
281 | https://bybit-exchange.github.io/docs/v5/asset/set-deposit-acct
282 | """
283 | return self._submit_request(
284 | method="POST",
285 | path=f"{self.endpoint}{Asset.SET_DEPOSIT_ACCOUNT}",
286 | query=kwargs,
287 | auth=True,
288 | )
289 |
290 | def get_deposit_records(self, **kwargs):
291 | """Query deposit records.
292 |
293 | Returns:
294 | Request results as dictionary.
295 |
296 | Additional information:
297 | https://bybit-exchange.github.io/docs/v5/asset/deposit-record
298 | """
299 | return self._submit_request(
300 | method="GET",
301 | path=f"{self.endpoint}{Asset.GET_DEPOSIT_RECORDS}",
302 | query=kwargs,
303 | auth=True,
304 | )
305 |
306 | def get_sub_deposit_records(self, **kwargs):
307 | """Query subaccount's deposit records by MAIN UID's API key.
308 |
309 | Required args:
310 | subMemberId (string): Sub UID
311 |
312 | Returns:
313 | Request results as dictionary.
314 |
315 | Additional information:
316 | https://bybit-exchange.github.io/docs/v5/asset/sub-deposit-record
317 | """
318 | return self._submit_request(
319 | method="GET",
320 | path=f"{self.endpoint}{Asset.GET_SUB_ACCOUNT_DEPOSIT_RECORDS}",
321 | query=kwargs,
322 | auth=True,
323 | )
324 |
325 | def get_internal_deposit_records(self, **kwargs):
326 | """Query deposit records within the Bybit platform. These transactions are not on the blockchain.
327 |
328 | Returns:
329 | Request results as dictionary.
330 |
331 | Additional information:
332 | https://bybit-exchange.github.io/docs/v5/asset/internal-deposit-record
333 | """
334 | return self._submit_request(
335 | method="GET",
336 | path=f"{self.endpoint}{Asset.GET_INTERNAL_DEPOSIT_RECORDS}",
337 | query=kwargs,
338 | auth=True,
339 | )
340 |
341 | def get_master_deposit_address(self, **kwargs):
342 | """Query the deposit address information of MASTER account.
343 |
344 | Required args:
345 | coin (string): Coin
346 |
347 | Returns:
348 | Request results as dictionary.
349 |
350 | Additional information:
351 | https://bybit-exchange.github.io/docs/v5/asset/master-deposit-addr
352 | """
353 | return self._submit_request(
354 | method="GET",
355 | path=f"{self.endpoint}{Asset.GET_MASTER_DEPOSIT_ADDRESS}",
356 | query=kwargs,
357 | auth=True,
358 | )
359 |
360 | def get_sub_deposit_address(self, **kwargs):
361 | """Query the deposit address information of SUB account.
362 |
363 | Required args:
364 | coin (string): Coin
365 | chainType (string): Chain, e.g.,ETH
366 |
367 | Returns:
368 | Request results as dictionary.
369 |
370 | Additional information:
371 | https://bybit-exchange.github.io/docs/v5/asset/sub-deposit-addr
372 | """
373 | return self._submit_request(
374 | method="GET",
375 | path=f"{self.endpoint}{Asset.GET_SUB_DEPOSIT_ADDRESS}",
376 | query=kwargs,
377 | auth=True,
378 | )
379 |
380 | def get_coin_info(self, **kwargs):
381 | """Query coin information, including chain information, withdraw and deposit status.
382 |
383 | Returns:
384 | Request results as dictionary.
385 |
386 | Additional information:
387 | https://bybit-exchange.github.io/docs/v5/asset/coin-info
388 | """
389 | return self._submit_request(
390 | method="GET",
391 | path=f"{self.endpoint}{Asset.GET_COIN_INFO}",
392 | query=kwargs,
393 | auth=True,
394 | )
395 |
396 | def get_withdrawal_records(self, **kwargs):
397 | """Query withdrawal records.
398 |
399 | Returns:
400 | Request results as dictionary.
401 |
402 | Additional information:
403 | https://bybit-exchange.github.io/docs/v5/asset/withdraw-record
404 | """
405 | return self._submit_request(
406 | method="GET",
407 | path=f"{self.endpoint}{Asset.GET_WITHDRAWAL_RECORDS}",
408 | query=kwargs,
409 | auth=True,
410 | )
411 |
412 | def get_withdrawable_amount(self, **kwargs):
413 | """Get withdrawable amount
414 |
415 | Required args:
416 | coin (string): Coin name
417 |
418 | Returns:
419 | Request results as dictionary.
420 |
421 | Additional information:
422 | https://bybit-exchange.github.io/docs/v5/asset/delay-amount
423 | """
424 | return self._submit_request(
425 | method="GET",
426 | path=f"{self.endpoint}{Asset.GET_WITHDRAWABLE_AMOUNT}",
427 | query=kwargs,
428 | auth=True,
429 | )
430 |
431 | def withdraw(self, **kwargs):
432 | """Withdraw assets from your Bybit account. You can make an off-chain transfer if the target wallet address is from Bybit. This means that no blockchain fee will be charged.
433 |
434 | Required args:
435 | coin (string): Coin
436 | chain (string): Chain
437 | address (string): Wallet address
438 | tag (string): Tag. Required if tag exists in the wallet address list
439 | amount (string): Withdraw amount
440 | timestamp (integer): Current timestamp (ms). Used for preventing from withdraw replay
441 |
442 | Returns:
443 | Request results as dictionary.
444 |
445 | Additional information:
446 | https://bybit-exchange.github.io/docs/v5/asset/withdraw
447 | """
448 | return self._submit_request(
449 | method="POST",
450 | path=f"{self.endpoint}{Asset.WITHDRAW}",
451 | query=kwargs,
452 | auth=True,
453 | )
454 |
455 | def cancel_withdrawal(self, **kwargs):
456 | """Cancel the withdrawal
457 |
458 | Required args:
459 | id (string): Withdrawal ID
460 |
461 | Returns:
462 | Request results as dictionary.
463 |
464 | Additional information:
465 | https://bybit-exchange.github.io/docs/v5/asset/cancel-withdraw
466 | """
467 | return self._submit_request(
468 | method="POST",
469 | path=f"{self.endpoint}{Asset.CANCEL_WITHDRAWAL}",
470 | query=kwargs,
471 | auth=True,
472 | )
473 |
474 | def get_convert_coin_list(self, **kwargs):
475 | """Query for the list of coins you can convert to/from.
476 |
477 | Required args:
478 | accountType (string): Wallet type
479 |
480 | Returns:
481 | Request results as dictionary.
482 |
483 | Additional information:
484 | https://bybit-exchange.github.io/docs/v5/asset/convert/convert-coin-list
485 | """
486 | return self._submit_request(
487 | method="GET",
488 | path=f"{self.endpoint}{Asset.GET_CONVERT_COIN_LIST}",
489 | query=kwargs,
490 | auth=True,
491 | )
492 |
493 | def request_a_quote(self, **kwargs):
494 | """
495 | Required args:
496 | fromCoin (string): Convert from coin (coin to sell)
497 | toCoin (string): Convert to coin (coin to buy)
498 | requestCoin (string): Request coin, same as fromCoin
499 | requestAmount (string): request coin amount (the amount you want to sell)
500 |
501 | Returns:
502 | Request results as dictionary.
503 |
504 | Additional information:
505 | https://bybit-exchange.github.io/docs/v5/asset/convert/apply-quote
506 | """
507 | return self._submit_request(
508 | method="POST",
509 | path=f"{self.endpoint}{Asset.REQUEST_A_QUOTE}",
510 | query=kwargs,
511 | auth=True,
512 | )
513 |
514 | def confirm_a_quote(self, **kwargs):
515 | """
516 | Required args:
517 | quoteTxId (string): The quoteTxId from request_a_quote
518 |
519 | Returns:
520 | Request results as dictionary.
521 |
522 | Additional information:
523 | https://bybit-exchange.github.io/docs/v5/asset/convert/confirm-quote
524 | """
525 | return self._submit_request(
526 | method="POST",
527 | path=f"{self.endpoint}{Asset.CONFIRM_A_QUOTE}",
528 | query=kwargs,
529 | auth=True,
530 | )
531 |
532 | def get_convert_status(self, **kwargs):
533 | """
534 | Required args:
535 | quoteTxId (string): Quote tx ID
536 | accountType (string): Wallet type
537 |
538 | Returns:
539 | Request results as dictionary.
540 |
541 | Additional information:
542 | https://bybit-exchange.github.io/docs/v5/asset/convert/get-convert-result
543 | """
544 | return self._submit_request(
545 | method="GET",
546 | path=f"{self.endpoint}{Asset.GET_CONVERT_STATUS}",
547 | query=kwargs,
548 | auth=True,
549 | )
550 |
551 | def get_convert_history(self, **kwargs):
552 | """
553 | Returns:
554 | Request results as dictionary.
555 |
556 | Additional information:
557 | https://bybit-exchange.github.io/docs/v5/asset/convert/get-convert-history
558 | """
559 | return self._submit_request(
560 | method="GET",
561 | path=f"{self.endpoint}{Asset.GET_CONVERT_HISTORY}",
562 | query=kwargs,
563 | auth=True,
564 | )
565 |
--------------------------------------------------------------------------------
/pybit/_v5_broker.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .broker import Broker
3 |
4 |
5 | class BrokerHTTP(_V5HTTPManager):
6 | def get_broker_earnings(self, **kwargs) -> dict:
7 | """
8 | Returns:
9 | Request results as dictionary.
10 |
11 | Additional information:
12 | https://bybit-exchange.github.io/docs/v5/broker/earning
13 | """
14 | self.logger.warning(
15 | "get_broker_earnings() is deprecated. See get_exchange_broker_earnings().")
16 |
17 | return self._submit_request(
18 | method="GET",
19 | path=f"{self.endpoint}{Broker.GET_BROKER_EARNINGS}",
20 | query=kwargs,
21 | auth=True,
22 | )
23 |
24 | def get_exchange_broker_earnings(self, **kwargs) -> dict:
25 | """
26 | Returns:
27 | Request results as dictionary.
28 |
29 | Additional information:
30 | https://bybit-exchange.github.io/docs/v5/broker/exchange-earning
31 | """
32 |
33 | return self._submit_request(
34 | method="GET",
35 | path=f"{self.endpoint}{Broker.GET_EXCHANGE_BROKER_EARNINGS}",
36 | query=kwargs,
37 | auth=True,
38 | )
39 |
--------------------------------------------------------------------------------
/pybit/_v5_crypto_loan.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .crypto_loan import CryptoLoan
3 |
4 |
5 | class CryptoLoanHTTP(_V5HTTPManager):
6 | def get_collateral_coins(self, **kwargs):
7 | """
8 | Returns:
9 | Request results as dictionary.
10 |
11 | Additional information:
12 | https://bybit-exchange.github.io/docs/v5/crypto-loan/collateral-coin
13 | """
14 | return self._submit_request(
15 | method="GET",
16 | path=f"{self.endpoint}{CryptoLoan.GET_COLLATERAL_COINS}",
17 | query=kwargs,
18 | )
19 |
20 | def get_borrowable_coins(self, **kwargs):
21 | """
22 | Returns:
23 | Request results as dictionary.
24 |
25 | Additional information:
26 | https://bybit-exchange.github.io/docs/v5/crypto-loan/loan-coin
27 | """
28 | return self._submit_request(
29 | method="GET",
30 | path=f"{self.endpoint}{CryptoLoan.GET_BORROWABLE_COINS}",
31 | query=kwargs,
32 | )
33 |
34 | def get_account_borrowable_or_collateralizable_limit(self, **kwargs):
35 | """
36 | Query for the minimum and maximum amounts your account can borrow and
37 | how much collateral you can put up.
38 |
39 | Required args:
40 | loanCurrency (string): Loan coin name
41 | collateralCurrency (string): Collateral coin name
42 |
43 | Returns:
44 | Request results as dictionary.
45 |
46 | Additional information:
47 | https://bybit-exchange.github.io/docs/v5/crypto-loan/acct-borrow-collateral
48 | """
49 | return self._submit_request(
50 | method="GET",
51 | path=f"{self.endpoint}{CryptoLoan.GET_ACCOUNT_BORROWABLE_OR_COLLATERALIZABLE_LIMIT}",
52 | query=kwargs,
53 | auth=True,
54 | )
55 |
56 | def borrow_crypto_loan(self, **kwargs):
57 | """
58 | Required args:
59 | loanCurrency (string): Loan coin name
60 |
61 | Returns:
62 | Request results as dictionary.
63 |
64 | Additional information:
65 | https://bybit-exchange.github.io/docs/v5/crypto-loan/borrow
66 | """
67 | return self._submit_request(
68 | method="POST",
69 | path=f"{self.endpoint}{CryptoLoan.BORROW_CRYPTO_LOAN}",
70 | query=kwargs,
71 | auth=True,
72 | )
73 |
74 | def repay_crypto_loan(self, **kwargs):
75 | """
76 | Required args:
77 | orderId (string): Loan order ID
78 | amount (string): Repay amount
79 |
80 | Returns:
81 | Request results as dictionary.
82 |
83 | Additional information:
84 | https://bybit-exchange.github.io/docs/v5/crypto-loan/repay
85 | """
86 | return self._submit_request(
87 | method="POST",
88 | path=f"{self.endpoint}{CryptoLoan.REPAY_CRYPTO_LOAN}",
89 | query=kwargs,
90 | auth=True,
91 | )
92 |
93 | def get_unpaid_loans(self, **kwargs):
94 | """
95 | Query for your ongoing loans.
96 |
97 | Returns:
98 | Request results as dictionary.
99 |
100 | Additional information:
101 | https://bybit-exchange.github.io/docs/v5/crypto-loan/repay
102 | """
103 | return self._submit_request(
104 | method="GET",
105 | path=f"{self.endpoint}{CryptoLoan.GET_UNPAID_LOANS}",
106 | query=kwargs,
107 | auth=True,
108 | )
109 |
110 | def get_loan_repayment_history(self, **kwargs):
111 | """
112 | Query for loan repayment transactions. A loan may be repaid in multiple
113 | repayments.
114 |
115 | Returns:
116 | Request results as dictionary.
117 |
118 | Additional information:
119 | https://bybit-exchange.github.io/docs/v5/crypto-loan/repay-transaction
120 | """
121 | return self._submit_request(
122 | method="GET",
123 | path=f"{self.endpoint}{CryptoLoan.GET_LOAN_REPAYMENT_HISTORY}",
124 | query=kwargs,
125 | auth=True,
126 | )
127 |
128 | def get_completed_loan_history(self, **kwargs):
129 | """
130 | Query for the last 6 months worth of your completed (fully paid off)
131 | loans.
132 |
133 | Returns:
134 | Request results as dictionary.
135 |
136 | Additional information:
137 | https://bybit-exchange.github.io/docs/v5/crypto-loan/completed-loan-order
138 | """
139 | return self._submit_request(
140 | method="GET",
141 | path=f"{self.endpoint}{CryptoLoan.GET_COMPLETED_LOAN_ORDER_HISTORY}",
142 | query=kwargs,
143 | auth=True,
144 | )
145 |
146 | def get_max_allowed_collateral_reduction_amount(self, **kwargs):
147 | """
148 | Query for the maximum amount by which collateral may be reduced by.
149 |
150 | Returns:
151 | Request results as dictionary.
152 |
153 | Additional information:
154 | https://bybit-exchange.github.io/docs/v5/crypto-loan/reduce-max-collateral-amt
155 | """
156 | return self._submit_request(
157 | method="GET",
158 | path=f"{self.endpoint}{CryptoLoan.GET_MAX_ALLOWED_COLLATERAL_REDUCTION_AMOUNT}",
159 | query=kwargs,
160 | auth=True,
161 | )
162 |
163 | def adjust_collateral_amount(self, **kwargs):
164 | """
165 | You can increase or reduce your collateral amount. When you reduce,
166 | please obey the max. allowed reduction amount.
167 |
168 | Returns:
169 | Request results as dictionary.
170 |
171 | Additional information:
172 | https://bybit-exchange.github.io/docs/v5/crypto-loan/adjust-collateral
173 | """
174 | return self._submit_request(
175 | method="GET",
176 | path=f"{self.endpoint}{CryptoLoan.ADJUST_COLLATERAL_AMOUNT}",
177 | query=kwargs,
178 | auth=True,
179 | )
180 |
181 | def get_crypto_loan_ltv_adjustment_history(self, **kwargs):
182 | """
183 | You can increase or reduce your collateral amount. When you reduce,
184 | please obey the max. allowed reduction amount.
185 |
186 | Returns:
187 | Request results as dictionary.
188 |
189 | Additional information:
190 | https://bybit-exchange.github.io/docs/v5/crypto-loan/ltv-adjust-history
191 | """
192 | return self._submit_request(
193 | method="GET",
194 | path=f"{self.endpoint}{CryptoLoan.GET_CRYPTO_LOAN_LTV_ADJUSTMENT_HISTORY}",
195 | query=kwargs,
196 | auth=True,
197 | )
198 |
--------------------------------------------------------------------------------
/pybit/_v5_earn.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .earn import Earn
3 |
4 |
5 | class EarnHTTP(_V5HTTPManager):
6 | def get_earn_product_info(self, **kwargs):
7 | """
8 | Required args:
9 | category (string): FlexibleSaving
10 |
11 | Returns:
12 | Request results as dictionary.
13 |
14 | Additional information:
15 | https://bybit-exchange.github.io/docs/v5/earn/product-info
16 | """
17 | return self._submit_request(
18 | method="GET",
19 | path=f"{self.endpoint}{Earn.GET_EARN_PRODUCT_INFO}",
20 | query=kwargs,
21 | )
22 |
23 | def stake_or_redeem(self, **kwargs):
24 | """
25 | Required args:
26 | category (string): FlexibleSaving
27 | orderType (string): Stake, Redeem
28 | accountType (string): FUND, UNIFIED
29 |
30 | Returns:
31 | Request results as dictionary.
32 |
33 | Additional information:
34 | https://bybit-exchange.github.io/docs/v5/earn/create-order
35 | """
36 | return self._submit_request(
37 | method="POST",
38 | path=f"{self.endpoint}{Earn.STAKE_OR_REDEEM}",
39 | query=kwargs,
40 | auth=True,
41 | )
42 |
43 | def get_stake_or_redemption_history(self, **kwargs):
44 | """
45 | Required args:
46 | category (string): FlexibleSaving
47 |
48 | Returns:
49 | Request results as dictionary.
50 |
51 | Additional information:
52 | https://bybit-exchange.github.io/docs/v5/earn/order-history
53 | """
54 | return self._submit_request(
55 | method="GET",
56 | path=f"{self.endpoint}{Earn.GET_STAKE_OR_REDEMPTION_HISTORY}",
57 | query=kwargs,
58 | auth=True,
59 | )
60 |
61 | def get_staked_position(self, **kwargs):
62 | """
63 | Required args:
64 | category (string): FlexibleSaving
65 |
66 | Returns:
67 | Request results as dictionary.
68 |
69 | Additional information:
70 | https://bybit-exchange.github.io/docs/v5/earn/position
71 | """
72 | return self._submit_request(
73 | method="GET",
74 | path=f"{self.endpoint}{Earn.GET_STAKED_POSITION}",
75 | query=kwargs,
76 | auth=True,
77 | )
78 |
--------------------------------------------------------------------------------
/pybit/_v5_institutional_loan.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .institutional_loan import InstitutionalLoan as InsLoan
3 |
4 |
5 | class InstitutionalLoanHTTP(_V5HTTPManager):
6 | def get_product_info(self, **kwargs) -> dict:
7 | """
8 | Returns:
9 | Request results as dictionary.
10 |
11 | Additional information:
12 | https://bybit-exchange.github.io/docs/v5/otc/margin-product-info
13 | """
14 | return self._submit_request(
15 | method="GET",
16 | path=f"{self.endpoint}{InsLoan.GET_PRODUCT_INFO}",
17 | query=kwargs,
18 | )
19 |
20 | def get_margin_coin_info(self, **kwargs) -> dict:
21 | """
22 | Returns:
23 | Request results as dictionary.
24 |
25 | Additional information:
26 | https://bybit-exchange.github.io/docs/v5/otc/margin-coin-convert-info
27 | """
28 | return self._submit_request(
29 | method="GET",
30 | path=f"{self.endpoint}{InsLoan.GET_MARGIN_COIN_INFO}",
31 | query=kwargs,
32 | )
33 |
34 | def get_loan_orders(self, **kwargs) -> dict:
35 | """
36 | Get loan orders information
37 |
38 | Returns:
39 | Request results as dictionary.
40 |
41 | Additional information:
42 | https://bybit-exchange.github.io/docs/v5/otc/loan-info
43 | """
44 | return self._submit_request(
45 | method="GET",
46 | path=f"{self.endpoint}{InsLoan.GET_LOAN_ORDERS}",
47 | query=kwargs,
48 | auth=True,
49 | )
50 |
51 | def get_repayment_info(self, **kwargs) -> dict:
52 | """
53 | Get a list of your loan repayment orders (orders which repaid the loan).
54 |
55 | Returns:
56 | Request results as dictionary.
57 |
58 | Additional information:
59 | https://bybit-exchange.github.io/docs/v5/otc/repay-info
60 | """
61 | return self._submit_request(
62 | method="GET",
63 | path=f"{self.endpoint}{InsLoan.GET_REPAYMENT_ORDERS}",
64 | query=kwargs,
65 | auth=True,
66 | )
67 |
68 | def get_ltv(self, **kwargs) -> dict:
69 | """
70 | Get your loan-to-value ratio.
71 |
72 | Returns:
73 | Request results as dictionary.
74 |
75 | Additional information:
76 | https://bybit-exchange.github.io/docs/v5/otc/ltv-convert
77 | """
78 | return self._submit_request(
79 | method="GET",
80 | path=f"{self.endpoint}{InsLoan.GET_LTV}",
81 | query=kwargs,
82 | auth=True,
83 | )
84 |
85 | def bind_or_unbind_uid(self, **kwargs) -> dict:
86 | """
87 | For the institutional loan product, you can bind new UIDs to the risk
88 | unit or unbind UID from the risk unit.
89 | Returns:
90 | Request results as dictionary.
91 |
92 | Additional information:
93 | https://bybit-exchange.github.io/docs/v5/otc/bind-uid
94 | """
95 | return self._submit_request(
96 | method="POST",
97 | path=f"{self.endpoint}{InsLoan.BIND_OR_UNBIND_UID}",
98 | query=kwargs,
99 | auth=True,
100 | )
101 |
--------------------------------------------------------------------------------
/pybit/_v5_market.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .market import Market
3 |
4 |
5 | class MarketHTTP(_V5HTTPManager):
6 | def get_server_time(self) -> dict:
7 | """
8 | Returns:
9 | Request results as dictionary.
10 |
11 | Additional information:
12 | https://bybit-exchange.github.io/docs/v5/market/time
13 | """
14 | return self._submit_request(
15 | method="GET",
16 | path=f"{self.endpoint}{Market.GET_SERVER_TIME}",
17 | )
18 |
19 | def get_kline(self, **kwargs) -> dict:
20 | """Query the kline data. Charts are returned in groups based on the requested interval.
21 |
22 | Required args:
23 | category (string): Product type: spot,linear,inverse
24 | symbol (string): Symbol name
25 | interval (string): Kline interval.
26 |
27 | Returns:
28 | Request results as dictionary.
29 |
30 | Additional information:
31 | https://bybit-exchange.github.io/docs/v5/market/kline
32 | """
33 | return self._submit_request(
34 | method="GET",
35 | path=f"{self.endpoint}{Market.GET_KLINE}",
36 | query=kwargs,
37 | )
38 |
39 | def get_mark_price_kline(self, **kwargs):
40 | """Query the mark price kline data. Charts are returned in groups based on the requested interval.
41 |
42 | Required args:
43 | category (string): Product type. linear,inverse
44 | symbol (string): Symbol name
45 | interval (string): Kline interval. 1,3,5,15,30,60,120,240,360,720,D,M,W
46 |
47 | Returns:
48 | Request results as dictionary.
49 |
50 | Additional information:
51 | https://bybit-exchange.github.io/docs/v5/market/mark-kline
52 | """
53 | return self._submit_request(
54 | method="GET",
55 | path=f"{self.endpoint}{Market.GET_MARK_PRICE_KLINE}",
56 | query=kwargs,
57 | )
58 |
59 | def get_index_price_kline(self, **kwargs):
60 | """Query the index price kline data. Charts are returned in groups based on the requested interval.
61 |
62 | Required args:
63 | category (string): Product type. linear,inverse
64 | symbol (string): Symbol name
65 | interval (string): Kline interval. 1,3,5,15,30,60,120,240,360,720,D,M,W
66 |
67 | Returns:
68 | Request results as dictionary.
69 |
70 | Additional information:
71 | https://bybit-exchange.github.io/docs/v5/market/index-kline
72 | """
73 | return self._submit_request(
74 | method="GET",
75 | path=f"{self.endpoint}{Market.GET_INDEX_PRICE_KLINE}",
76 | query=kwargs,
77 | )
78 |
79 | def get_premium_index_price_kline(self, **kwargs):
80 | """Retrieve the premium index price kline data. Charts are returned in groups based on the requested interval.
81 |
82 | Required args:
83 | category (string): Product type. linear
84 | symbol (string): Symbol name
85 | interval (string): Kline interval
86 |
87 | Returns:
88 | Request results as dictionary.
89 |
90 | Additional information:
91 | https://bybit-exchange.github.io/docs/v5/market/preimum-index-kline
92 | """
93 | return self._submit_request(
94 | method="GET",
95 | path=f"{self.endpoint}{Market.GET_PREMIUM_INDEX_PRICE_KLINE}",
96 | query=kwargs,
97 | )
98 |
99 | def get_instruments_info(self, **kwargs):
100 | """Query a list of instruments of online trading pair.
101 |
102 | Required args:
103 | category (string): Product type. spot,linear,inverse,option
104 |
105 | Returns:
106 | Request results as dictionary.
107 |
108 | Additional information:
109 | https://bybit-exchange.github.io/docs/v5/market/instrument
110 | """
111 | return self._submit_request(
112 | method="GET",
113 | path=f"{self.endpoint}{Market.GET_INSTRUMENTS_INFO}",
114 | query=kwargs,
115 | )
116 |
117 | def get_orderbook(self, **kwargs):
118 | """Query orderbook data
119 |
120 | Required args:
121 | category (string): Product type. spot, linear, inverse, option
122 | symbol (string): Symbol name
123 |
124 | Returns:
125 | Request results as dictionary.
126 |
127 | Additional information:
128 | https://bybit-exchange.github.io/docs/v5/market/orderbook
129 | """
130 | return self._submit_request(
131 | method="GET",
132 | path=f"{self.endpoint}{Market.GET_ORDERBOOK}",
133 | query=kwargs,
134 | )
135 |
136 | def get_tickers(self, **kwargs):
137 | """Query the latest price snapshot, best bid/ask price, and trading volume in the last 24 hours.
138 |
139 | Required args:
140 | category (string): Product type. spot,linear,inverse,option
141 |
142 | Returns:
143 | Request results as dictionary.
144 |
145 | Additional information:
146 | https://bybit-exchange.github.io/docs/v5/market/tickers
147 | """
148 | return self._submit_request(
149 | method="GET",
150 | path=f"{self.endpoint}{Market.GET_TICKERS}",
151 | query=kwargs,
152 | )
153 |
154 | def get_funding_rate_history(self, **kwargs):
155 | """
156 | Query historical funding rate. Each symbol has a different funding interval.
157 | For example, if the interval is 8 hours and the current time is UTC 12, then it returns the last funding rate, which settled at UTC 8.
158 | To query the funding rate interval, please refer to instruments-info.
159 |
160 | Required args:
161 | category (string): Product type. linear,inverse
162 | symbol (string): Symbol name
163 |
164 | Returns:
165 | Request results as dictionary.
166 |
167 | Additional information:
168 | https://bybit-exchange.github.io/docs/v5/market/history-fund-rate
169 | """
170 | return self._submit_request(
171 | method="GET",
172 | path=f"{self.endpoint}{Market.GET_FUNDING_RATE_HISTORY}",
173 | query=kwargs,
174 | )
175 |
176 | def get_public_trade_history(self, **kwargs):
177 | """Query recent public trading data in Bybit.
178 |
179 | Required args:
180 | category (string): Product type. spot,linear,inverse,option
181 | symbol (string): Symbol name
182 |
183 | Returns:
184 | Request results as dictionary.
185 |
186 | Additional information:
187 | https://bybit-exchange.github.io/docs/v5/market/recent-trade
188 | """
189 | return self._submit_request(
190 | method="GET",
191 | path=f"{self.endpoint}{Market.GET_PUBLIC_TRADING_HISTORY}",
192 | query=kwargs,
193 | )
194 |
195 | def get_open_interest(self, **kwargs):
196 | """Get open interest of each symbol.
197 |
198 | Required args:
199 | category (string): Product type. linear,inverse
200 | symbol (string): Symbol name
201 | intervalTime (string): Interval. 5min,15min,30min,1h,4h,1d
202 |
203 | Returns:
204 | Request results as dictionary.
205 |
206 | Additional information:
207 | https://bybit-exchange.github.io/docs/v5/market/open-interest
208 | """
209 | return self._submit_request(
210 | method="GET",
211 | path=f"{self.endpoint}{Market.GET_OPEN_INTEREST}",
212 | query=kwargs,
213 | )
214 |
215 | def get_historical_volatility(self, **kwargs):
216 | """Query option historical volatility
217 |
218 | Required args:
219 | category (string): Product type. option
220 |
221 | Returns:
222 | Request results as dictionary.
223 |
224 | Additional information:
225 | https://bybit-exchange.github.io/docs/v5/market/iv
226 | """
227 | return self._submit_request(
228 | method="GET",
229 | path=f"{self.endpoint}{Market.GET_HISTORICAL_VOLATILITY}",
230 | query=kwargs,
231 | )
232 |
233 | def get_insurance(self, **kwargs):
234 | """
235 | Query Bybit insurance pool data (BTC/USDT/USDC etc).
236 | The data is updated every 24 hours.
237 |
238 | Returns:
239 | Request results as dictionary.
240 |
241 | Additional information:
242 | https://bybit-exchange.github.io/docs/v5/market/insurance
243 | """
244 | return self._submit_request(
245 | method="GET",
246 | path=f"{self.endpoint}{Market.GET_INSURANCE}",
247 | query=kwargs,
248 | )
249 |
250 | def get_risk_limit(self, **kwargs):
251 | """Query risk limit of futures
252 |
253 | Returns:
254 | Request results as dictionary.
255 |
256 | Additional information:
257 | https://bybit-exchange.github.io/docs/v5/market/risk-limit
258 | """
259 | return self._submit_request(
260 | method="GET",
261 | path=f"{self.endpoint}{Market.GET_RISK_LIMIT}",
262 | query=kwargs,
263 | )
264 |
265 | def get_option_delivery_price(self, **kwargs):
266 | """Get the delivery price for option
267 |
268 | Required args:
269 | category (string): Product type. option
270 |
271 | Returns:
272 | Request results as dictionary.
273 |
274 | Additional information:
275 | https://bybit-exchange.github.io/docs/v5/market/delivery-price
276 | """
277 | return self._submit_request(
278 | method="GET",
279 | path=f"{self.endpoint}{Market.GET_OPTION_DELIVERY_PRICE}",
280 | query=kwargs,
281 | )
282 |
283 | def get_long_short_ratio(self, **kwargs):
284 | """
285 | Required args:
286 | category (string): Product type. linear (USDT Perpetual only), inverse
287 | symbol (string): Symbol name
288 |
289 | Returns:
290 | Request results as dictionary.
291 |
292 | Additional information:
293 | https://bybit-exchange.github.io/docs/v5/market/long-short-ratio
294 | """
295 | return self._submit_request(
296 | method="GET",
297 | path=f"{self.endpoint}{Market.GET_LONG_SHORT_RATIO}",
298 | query=kwargs,
299 | )
300 |
--------------------------------------------------------------------------------
/pybit/_v5_misc.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .misc import Misc
3 |
4 |
5 | class MiscHTTP(_V5HTTPManager):
6 | def get_announcement(self, **kwargs) -> dict:
7 | """
8 | Required args:
9 | locale (string): Language symbol
10 |
11 | Returns:
12 | Request results as dictionary.
13 |
14 | Additional information:
15 | https://bybit-exchange.github.io/docs/v5/announcement
16 | """
17 | return self._submit_request(
18 | method="GET",
19 | path=f"{self.endpoint}{Misc.GET_ANNOUNCEMENT}",
20 | query=kwargs,
21 | )
22 |
23 | def request_demo_trading_funds(self) -> dict:
24 | """
25 | Returns:
26 | Request results as dictionary.
27 |
28 | Additional information:
29 | https://bybit-exchange.github.io/docs/v5/demo
30 | """
31 | if not self.demo:
32 | raise Exception(
33 | "You must pass demo=True to the pybit HTTP session to use this "
34 | "method."
35 | )
36 | return self._submit_request(
37 | method="POST",
38 | path=f"{self.endpoint}{Misc.REQUEST_DEMO_TRADING_FUNDS}",
39 | auth=True,
40 | )
41 |
--------------------------------------------------------------------------------
/pybit/_v5_position.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .position import Position
3 |
4 |
5 | class PositionHTTP(_V5HTTPManager):
6 | def get_positions(self, **kwargs):
7 | """Query real-time position data, such as position size, cumulative realizedPNL.
8 |
9 | Required args:
10 | category (string): Product type
11 | Unified account: linear, option
12 | Normal account: linear, inverse.
13 |
14 | Please note that category is not involved with business logic
15 |
16 | Returns:
17 | Request results as dictionary.
18 |
19 | Additional information:
20 | https://bybit-exchange.github.io/docs/v5/position
21 | """
22 | return self._submit_request(
23 | method="GET",
24 | path=f"{self.endpoint}{Position.GET_POSITIONS}",
25 | query=kwargs,
26 | auth=True,
27 | )
28 |
29 | def set_leverage(self, **kwargs):
30 | """Set the leverage
31 |
32 | Required args:
33 | category (string): Product type
34 | Unified account: linear
35 | Normal account: linear, inverse.
36 |
37 | Please note that category is not involved with business logic
38 | symbol (string): Symbol name
39 | buyLeverage (string): [0, max leverage of corresponding risk limit].
40 | Note: Under one-way mode, buyLeverage must be the same as sellLeverage
41 | sellLeverage (string): [0, max leverage of corresponding risk limit].
42 | Note: Under one-way mode, buyLeverage must be the same as sellLeverage
43 |
44 | Returns:
45 | Request results as dictionary.
46 |
47 | Additional information:
48 | https://bybit-exchange.github.io/docs/v5/position/leverage
49 | """
50 | return self._submit_request(
51 | method="POST",
52 | path=f"{self.endpoint}{Position.SET_LEVERAGE}",
53 | query=kwargs,
54 | auth=True,
55 | )
56 |
57 | def switch_margin_mode(self, **kwargs):
58 | """Select cross margin mode or isolated margin mode
59 |
60 | Required args:
61 | category (string): Product type. linear,inverse
62 |
63 | Please note that category is not involved with business logicUnified account is not applicable
64 | symbol (string): Symbol name
65 | tradeMode (integer): 0: cross margin. 1: isolated margin
66 | buyLeverage (string): The value must be equal to sellLeverage value
67 | sellLeverage (string): The value must be equal to buyLeverage value
68 |
69 | Returns:
70 | Request results as dictionary.
71 |
72 | Additional information:
73 | https://bybit-exchange.github.io/docs/v5/position/cross-isolate
74 | """
75 | return self._submit_request(
76 | method="POST",
77 | path=f"{self.endpoint}{Position.SWITCH_MARGIN_MODE}",
78 | query=kwargs,
79 | auth=True,
80 | )
81 |
82 | def set_tp_sl_mode(self, **kwargs):
83 | """Set TP/SL mode to Full or Partial
84 |
85 | Required args:
86 | category (string): Product type
87 | Unified account: linear
88 | Normal account: linear, inverse.
89 |
90 | Please note that category is not involved with business logic
91 | symbol (string): Symbol name
92 | tpSlMode (string): TP/SL mode. Full,Partial
93 |
94 | Returns:
95 | Request results as dictionary.
96 |
97 | Additional information:
98 | https://bybit-exchange.github.io/docs/v5/position/tpsl-mode
99 | """
100 | return self._submit_request(
101 | method="POST",
102 | path=f"{self.endpoint}{Position.SET_TP_SL_MODE}",
103 | query=kwargs,
104 | auth=True,
105 | )
106 |
107 | def switch_position_mode(self, **kwargs):
108 | """
109 | It supports to switch the position mode for USDT perpetual and Inverse futures.
110 | If you are in one-way Mode, you can only open one position on Buy or Sell side.
111 | If you are in hedge mode, you can open both Buy and Sell side positions simultaneously.
112 |
113 | Required args:
114 | category (string): Product type. linear,inverse
115 |
116 | Please note that category is not involved with business logicUnified account is not applicable
117 |
118 | Returns:
119 | Request results as dictionary.
120 |
121 | Additional information:
122 | https://bybit-exchange.github.io/docs/v5/position/position-mode
123 | """
124 | return self._submit_request(
125 | method="POST",
126 | path=f"{self.endpoint}{Position.SWITCH_POSITION_MODE}",
127 | query=kwargs,
128 | auth=True,
129 | )
130 |
131 | def set_risk_limit(self, **kwargs):
132 | """
133 | The risk limit will limit the maximum position value you can hold under different margin requirements.
134 | If you want to hold a bigger position size, you need more margin. This interface can set the risk limit of a single position.
135 | If the order exceeds the current risk limit when placing an order, it will be rejected. Click here to learn more about risk limit.
136 |
137 | Required args:
138 | category (string): Product type
139 | Unified account: linear
140 | Normal account: linear, inverse.
141 |
142 | Please note that category is not involved with business logic
143 | symbol (string): Symbol name
144 | riskId (integer): Risk limit ID
145 |
146 | Returns:
147 | Request results as dictionary.
148 |
149 | Additional information:
150 | https://bybit-exchange.github.io/docs/v5/position/set-risk-limit
151 | """
152 | return self._submit_request(
153 | method="POST",
154 | path=f"{self.endpoint}{Position.SET_RISK_LIMIT}",
155 | query=kwargs,
156 | auth=True,
157 | )
158 |
159 | def set_trading_stop(self, **kwargs):
160 | """Set the take profit, stop loss or trailing stop for the position.
161 |
162 | Required args:
163 | category (string): Product type
164 | Unified account: linear
165 | Normal account: linear, inverse.
166 |
167 | Please note that category is not involved with business logic
168 | symbol (string): Symbol name
169 |
170 | Returns:
171 | Request results as dictionary.
172 |
173 | Additional information:
174 | https://bybit-exchange.github.io/docs/v5/position/trading-stop
175 | """
176 | return self._submit_request(
177 | method="POST",
178 | path=f"{self.endpoint}{Position.SET_TRADING_STOP}",
179 | query=kwargs,
180 | auth=True,
181 | )
182 |
183 | def set_auto_add_margin(self, **kwargs):
184 | """Turn on/off auto-add-margin for isolated margin position
185 |
186 | Required args:
187 | category (string): Product type. linear
188 | symbol (string): Symbol name
189 | autoAddMargin (integer): Turn on/off. 0: off. 1: on
190 |
191 | Returns:
192 | Request results as dictionary.
193 |
194 | Additional information:
195 | https://bybit-exchange.github.io/docs/v5/position/auto-add-margin
196 | """
197 | return self._submit_request(
198 | method="POST",
199 | path=f"{self.endpoint}{Position.SET_AUTO_ADD_MARGIN}",
200 | query=kwargs,
201 | auth=True,
202 | )
203 |
204 | def add_or_reduce_margin(self, **kwargs):
205 | """Manually add or reduce margin for isolated margin position
206 |
207 | Required args:
208 | category (string): Product type. linear
209 | symbol (string): Symbol name
210 | margin (string): Add or reduce. To add, use 10. To reduce, use -10
211 |
212 | Returns:
213 | Request results as dictionary.
214 |
215 | Additional information:
216 | https://bybit-exchange.github.io/docs/v5/position/manual-add-margin
217 | """
218 | return self._submit_request(
219 | method="POST",
220 | path=f"{self.endpoint}{Position.ADD_MARGIN}",
221 | query=kwargs,
222 | auth=True,
223 | )
224 |
225 | def get_executions(self, **kwargs):
226 | """Query users' execution records, sorted by execTime in descending order
227 |
228 | Required args:
229 | category (string):
230 | Product type Unified account: spot, linear, option
231 | Normal account: linear, inverse.
232 |
233 | Please note that category is not involved with business logic
234 |
235 | Returns:
236 | Request results as dictionary.
237 |
238 | Additional information:
239 | https://bybit-exchange.github.io/docs/v5/order/execution
240 | """
241 | return self._submit_request(
242 | method="GET",
243 | path=f"{self.endpoint}{Position.GET_EXECUTIONS}",
244 | query=kwargs,
245 | auth=True,
246 | )
247 |
248 | def get_closed_pnl(self, **kwargs):
249 | """Query user's closed profit and loss records. The results are sorted by createdTime in descending order.
250 |
251 | Required args:
252 | category (string):
253 | Product type Unified account: linear
254 | Normal account: linear, inverse.
255 |
256 | Please note that category is not involved with business logic
257 |
258 | Returns:
259 | Request results as dictionary.
260 |
261 | Additional information:
262 | https://bybit-exchange.github.io/docs/v5/position/close-pnl
263 | """
264 | return self._submit_request(
265 | method="GET",
266 | path=f"{self.endpoint}{Position.GET_CLOSED_PNL}",
267 | query=kwargs,
268 | auth=True,
269 | )
270 |
--------------------------------------------------------------------------------
/pybit/_v5_pre_upgrade.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .pre_upgrade import PreUpgrade
3 |
4 |
5 | class PreUpgradeHTTP(_V5HTTPManager):
6 | def get_pre_upgrade_order_history(self, **kwargs) -> dict:
7 | """
8 | After the account is upgraded to a Unified account, you can get the
9 | orders which occurred before the upgrade.
10 |
11 | Required args:
12 | category (string): Product type. linear, inverse, option
13 |
14 | Returns:
15 | Request results as dictionary.
16 |
17 | Additional information:
18 | https://bybit-exchange.github.io/docs/v5/pre-upgrade/order-list
19 | """
20 | return self._submit_request(
21 | method="GET",
22 | path=f"{self.endpoint}{PreUpgrade.GET_PRE_UPGRADE_ORDER_HISTORY}",
23 | query=kwargs,
24 | auth=True,
25 | )
26 |
27 | def get_pre_upgrade_trade_history(self, **kwargs) -> dict:
28 | """
29 | Get users' execution records which occurred before you upgraded the
30 | account to a Unified account, sorted by execTime in descending order
31 |
32 | Required args:
33 | category (string): Product type. linear, inverse, option
34 |
35 | Returns:
36 | Request results as dictionary.
37 |
38 | Additional information:
39 | https://bybit-exchange.github.io/docs/v5/pre-upgrade/execution
40 | """
41 | return self._submit_request(
42 | method="GET",
43 | path=f"{self.endpoint}{PreUpgrade.GET_PRE_UPGRADE_TRADE_HISTORY}",
44 | query=kwargs,
45 | auth=True,
46 | )
47 |
48 | def get_pre_upgrade_closed_pnl(self, **kwargs) -> dict:
49 | """
50 | Query user's closed profit and loss records from before you upgraded the
51 | account to a Unified account. The results are sorted by createdTime in
52 | descending order.
53 |
54 | Required args:
55 | category (string): Product type linear, inverse
56 | symbol (string): Symbol name
57 |
58 | Returns:
59 | Request results as dictionary.
60 |
61 | Additional information:
62 | https://bybit-exchange.github.io/docs/v5/pre-upgrade/close-pnl
63 | """
64 | return self._submit_request(
65 | method="GET",
66 | path=f"{self.endpoint}{PreUpgrade.GET_PRE_UPGRADE_CLOSED_PNL}",
67 | query=kwargs,
68 | auth=True,
69 | )
70 |
71 | def get_pre_upgrade_transaction_log(self, **kwargs) -> dict:
72 | """
73 | Query transaction logs which occurred in the USDC Derivatives wallet
74 | before the account was upgraded to a Unified account.
75 |
76 | Required args:
77 | category (string): Product type. linear,option
78 |
79 | Returns:
80 | Request results as dictionary.
81 |
82 | Additional information:
83 | https://bybit-exchange.github.io/docs/v5/pre-upgrade/transaction-log
84 | """
85 | return self._submit_request(
86 | method="GET",
87 | path=f"{self.endpoint}{PreUpgrade.GET_PRE_UPGRADE_TRANSACTION_LOG}",
88 | query=kwargs,
89 | auth=True,
90 | )
91 |
92 | def get_pre_upgrade_option_delivery_record(self, **kwargs) -> dict:
93 | """
94 | Query delivery records of Option before you upgraded the account to a
95 | Unified account, sorted by deliveryTime in descending order
96 | Required args:
97 | category (string): Product type. option
98 |
99 | Returns:
100 | Request results as dictionary.
101 |
102 | Additional information:
103 | https://bybit-exchange.github.io/docs/v5/pre-upgrade/delivery
104 | """
105 | return self._submit_request(
106 | method="GET",
107 | path=f"{self.endpoint}{PreUpgrade.GET_PRE_UPGRADE_OPTION_DELIVERY_RECORD}",
108 | query=kwargs,
109 | auth=True,
110 | )
111 |
112 | def get_pre_upgrade_usdc_session_settlement(self, **kwargs) -> dict:
113 | """
114 | Required args:
115 | category (string): Product type. linear
116 |
117 | Returns:
118 | Request results as dictionary.
119 |
120 | Additional information:
121 | https://bybit-exchange.github.io/docs/v5/pre-upgrade/settlement
122 | """
123 | return self._submit_request(
124 | method="GET",
125 | path=f"{self.endpoint}{PreUpgrade.GET_PRE_UPGRADE_USDC_SESSION_SETTLEMENT}",
126 | query=kwargs,
127 | auth=True,
128 | )
129 |
130 |
131 |
--------------------------------------------------------------------------------
/pybit/_v5_spot_leverage_token.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .spot_leverage_token import SpotLeverageToken
3 |
4 |
5 | class SpotLeverageHTTP(_V5HTTPManager):
6 | def get_leveraged_token_info(self, **kwargs):
7 | """Query leverage token information
8 |
9 | Returns:
10 | Request results as dictionary.
11 |
12 | Additional information:
13 | https://bybit-exchange.github.io/docs/v5/lt/leverage-token-info
14 | """
15 | return self._submit_request(
16 | method="GET",
17 | path=f"{self.endpoint}{SpotLeverageToken.GET_LEVERAGED_TOKEN_INFO}",
18 | query=kwargs,
19 | )
20 |
21 | def get_leveraged_token_market(self, **kwargs):
22 | """Get leverage token market information
23 |
24 | Required args:
25 | ltCoin (string): Abbreviation of the LT, such as BTC3L
26 |
27 | Returns:
28 | Request results as dictionary.
29 |
30 | Additional information:
31 | https://bybit-exchange.github.io/docs/v5/lt/leverage-token-reference
32 | """
33 | return self._submit_request(
34 | method="GET",
35 | path=f"{self.endpoint}{SpotLeverageToken.GET_LEVERAGED_TOKEN_MARKET}",
36 | query=kwargs,
37 | )
38 |
39 | def purchase_leveraged_token(self, **kwargs):
40 | """Purchase levearge token
41 |
42 | Required args:
43 | ltCoin (string): Abbreviation of the LT, such as BTC3L
44 | ltAmount (string): Purchase amount
45 |
46 | Returns:
47 | Request results as dictionary.
48 |
49 | Additional information:
50 | https://bybit-exchange.github.io/docs/v5/lt/purchase
51 | """
52 | return self._submit_request(
53 | method="POST",
54 | path=f"{self.endpoint}{SpotLeverageToken.PURCHASE}",
55 | query=kwargs,
56 | auth=True,
57 | )
58 |
59 | def redeem_leveraged_token(self, **kwargs):
60 | """Redeem leverage token
61 |
62 | Required args:
63 | ltCoin (string): Abbreviation of the LT, such as BTC3L
64 | quantity (string): Redeem quantity of LT
65 |
66 | Returns:
67 | Request results as dictionary.
68 |
69 | Additional information:
70 | https://bybit-exchange.github.io/docs/v5/lt/redeem
71 | """
72 | return self._submit_request(
73 | method="POST",
74 | path=f"{self.endpoint}{SpotLeverageToken.REDEEM}",
75 | query=kwargs,
76 | auth=True,
77 | )
78 |
79 | def get_purchase_redemption_records(self, **kwargs):
80 | """Get purchase or redeem history
81 |
82 | Required args:
83 |
84 | Returns:
85 | Request results as dictionary.
86 |
87 | Additional information:
88 | https://bybit-exchange.github.io/docs/v5/lt/order-record
89 | """
90 | return self._submit_request(
91 | method="GET",
92 | path=f"{self.endpoint}{SpotLeverageToken.GET_PURCHASE_REDEMPTION_RECORDS}",
93 | query=kwargs,
94 | auth=True,
95 | )
96 |
--------------------------------------------------------------------------------
/pybit/_v5_spot_margin_trade.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .spot_margin_trade import SpotMarginTrade
3 |
4 |
5 | class SpotMarginTradeHTTP(_V5HTTPManager):
6 | def spot_margin_trade_get_vip_margin_data(self, **kwargs):
7 | """
8 | Returns:
9 | Request results as dictionary.
10 |
11 | Additional information:
12 | https://bybit-exchange.github.io/docs/v5/spot-margin-uta/vip-margin
13 | """
14 | return self._submit_request(
15 | method="GET",
16 | path=f"{self.endpoint}{SpotMarginTrade.VIP_MARGIN_DATA}",
17 | query=kwargs,
18 | )
19 |
20 | def spot_margin_trade_toggle_margin_trade(self, **kwargs):
21 | """UTA only. Turn spot margin trade on / off.
22 |
23 | Required args:
24 | spotMarginMode (string): 1: on, 0: off
25 |
26 | Returns:
27 | Request results as dictionary.
28 |
29 | Additional information:
30 | https://bybit-exchange.github.io/docs/v5/spot-margin-uta/switch-mode
31 | """
32 | return self._submit_request(
33 | method="POST",
34 | path=f"{self.endpoint}{SpotMarginTrade.TOGGLE_MARGIN_TRADE}",
35 | query=kwargs,
36 | auth=True,
37 | )
38 |
39 | def spot_margin_trade_set_leverage(self, **kwargs):
40 | """UTA only. Set the user's maximum leverage in spot cross margin
41 |
42 | Required args:
43 | leverage (string): Leverage. [2, 5].
44 |
45 | Returns:
46 | Request results as dictionary.
47 |
48 | Additional information:
49 | https://bybit-exchange.github.io/docs/v5/spot-margin-uta/set-leverage
50 | """
51 | return self._submit_request(
52 | method="POST",
53 | path=f"{self.endpoint}{SpotMarginTrade.SET_LEVERAGE}",
54 | query=kwargs,
55 | auth=True,
56 | )
57 |
58 | def spot_margin_trade_get_status_and_leverage(self):
59 | """
60 | Returns:
61 | Request results as dictionary.
62 |
63 | Additional information:
64 | https://bybit-exchange.github.io/docs/v5/spot-margin-uta/status
65 | """
66 | return self._submit_request(
67 | method="GET",
68 | path=f"{self.endpoint}{SpotMarginTrade.STATUS_AND_LEVERAGE}",
69 | auth=True,
70 | )
71 |
72 | def spot_margin_trade_get_historical_interest_rate(self, **kwargs):
73 | """UTA only. Queries up to six months of borrowing interest rates.
74 |
75 | Required args:
76 | currency (string): Coin name, uppercase only.
77 |
78 | Returns:
79 | Request results as dictionary.
80 |
81 | Additional information:
82 | https://bybit-exchange.github.io/docs/v5/spot-margin-uta/historical-interest
83 | """
84 | return self._submit_request(
85 | method="GET",
86 | path=f"{self.endpoint}{SpotMarginTrade.HISTORICAL_INTEREST}",
87 | query=kwargs,
88 | auth=True,
89 | )
90 |
91 | def spot_margin_trade_normal_get_vip_margin_data(self, **kwargs):
92 | """
93 | Returns:
94 | Request results as dictionary.
95 |
96 | Additional information:
97 | https://bybit-exchange.github.io/docs/v5/spot-margin-normal/vip-margin
98 | """
99 | return self._submit_request(
100 | method="GET",
101 | path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_MARGIN_COIN_INFO}",
102 | query=kwargs,
103 | )
104 |
105 | def spot_margin_trade_normal_get_margin_coin_info(self, **kwargs):
106 | """Normal (non-UTA) account only. Turn on / off spot margin trade
107 |
108 | Required args:
109 | switch (string): 1: on, 0: off
110 |
111 | Returns:
112 | Request results as dictionary.
113 |
114 | Additional information:
115 | https://bybit-exchange.github.io/docs/v5/spot-margin-normal/margin-data
116 | """
117 | return self._submit_request(
118 | method="GET",
119 | path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_MARGIN_COIN_INFO}",
120 | query=kwargs,
121 | )
122 |
123 | def spot_margin_trade_normal_get_borrowable_coin_info(self, **kwargs):
124 | """Normal (non-UTA) account only.
125 |
126 | Returns:
127 | Request results as dictionary.
128 |
129 | Additional information:
130 | https://bybit-exchange.github.io/docs/v5/spot-margin-normal/borrowable-data
131 | """
132 | return self._submit_request(
133 | method="GET",
134 | path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_BORROWABLE_COIN_INFO}",
135 | query=kwargs,
136 | )
137 |
138 | def spot_margin_trade_normal_get_interest_quota(self, **kwargs):
139 | """Normal (non-UTA) account only.
140 |
141 | Required args:
142 | coin (string): Coin name
143 |
144 | Returns:
145 | Request results as dictionary.
146 |
147 | Additional information:
148 | https://bybit-exchange.github.io/docs/v5/spot-margin-normal/interest-quota
149 | """
150 | return self._submit_request(
151 | method="GET",
152 | path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_INTEREST_QUOTA}",
153 | query=kwargs,
154 | auth=True,
155 | )
156 |
157 | def spot_margin_trade_normal_get_loan_account_info(self, **kwargs):
158 | """Normal (non-UTA) account only.
159 |
160 | Returns:
161 | Request results as dictionary.
162 |
163 | Additional information:
164 | https://bybit-exchange.github.io/docs/v5/spot-margin-normal/account-info
165 | """
166 | return self._submit_request(
167 | method="GET",
168 | path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_LOAN_ACCOUNT_INFO}",
169 | query=kwargs,
170 | auth=True,
171 | )
172 |
173 | def spot_margin_trade_normal_borrow(self, **kwargs):
174 | """Normal (non-UTA) account only.
175 |
176 | Required args:
177 | coin (string): Coin name
178 | qty (string): Amount to borrow
179 |
180 | Returns:
181 | Request results as dictionary.
182 |
183 | Additional information:
184 | https://bybit-exchange.github.io/docs/v5/spot-margin-normal/borrow
185 | """
186 | return self._submit_request(
187 | method="POST",
188 | path=f"{self.endpoint}{SpotMarginTrade.NORMAL_BORROW}",
189 | query=kwargs,
190 | auth=True,
191 | )
192 |
193 | def spot_margin_trade_normal_repay(self, **kwargs):
194 | """Normal (non-UTA) account only.
195 |
196 | Required args:
197 | coin (string): Coin name
198 |
199 | Returns:
200 | Request results as dictionary.
201 |
202 | Additional information:
203 | https://bybit-exchange.github.io/docs/v5/spot-margin-normal/repay
204 | """
205 | return self._submit_request(
206 | method="POST",
207 | path=f"{self.endpoint}{SpotMarginTrade.NORMAL_REPAY}",
208 | query=kwargs,
209 | auth=True,
210 | )
211 |
212 | def spot_margin_trade_normal_get_borrow_order_detail(self, **kwargs):
213 | """Normal (non-UTA) account only.
214 |
215 | Returns:
216 | Request results as dictionary.
217 |
218 | Additional information:
219 | https://bybit-exchange.github.io/docs/v5/spot-margin-normal/borrow-order
220 | """
221 | return self._submit_request(
222 | method="GET",
223 | path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_BORROW_ORDER_DETAIL}",
224 | query=kwargs,
225 | auth=True,
226 | )
227 |
228 | def spot_margin_trade_normal_get_repayment_order_detail(self, **kwargs):
229 | """Normal (non-UTA) account only.
230 |
231 | Returns:
232 | Request results as dictionary.
233 |
234 | Additional information:
235 | https://bybit-exchange.github.io/docs/v5/spot-margin-normal/repay-order
236 | """
237 | return self._submit_request(
238 | method="GET",
239 | path=f"{self.endpoint}{SpotMarginTrade.NORMAL_GET_REPAYMENT_ORDER_DETAIL}",
240 | query=kwargs,
241 | auth=True,
242 | )
243 |
244 | def spot_margin_trade_normal_toggle_margin_trade(self, **kwargs):
245 | """Normal (non-UTA) account only.
246 |
247 | Required args:
248 | switch (integer): 1: on, 0: off
249 |
250 | Returns:
251 | Request results as dictionary.
252 |
253 | Additional information:
254 | https://bybit-exchange.github.io/docs/v5/spot-margin-normal/switch-mode
255 | """
256 | return self._submit_request(
257 | method="POST",
258 | path=f"{self.endpoint}{SpotMarginTrade.NORMAL_TOGGLE_MARGIN_TRADE}",
259 | query=kwargs,
260 | auth=True,
261 | )
262 |
--------------------------------------------------------------------------------
/pybit/_v5_spread.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from ._websocket_stream import _V5WebSocketManager
3 | from .spread import Spread
4 |
5 |
6 | WSS_NAME = "Spread Trading"
7 | PUBLIC_WSS = "wss://{SUBDOMAIN}.{DOMAIN}.com/v5/public/spread"
8 |
9 |
10 | class SpreadHTTP(_V5HTTPManager):
11 | def get_instruments_info(self, **kwargs) -> dict:
12 | """
13 | Returns:
14 | Request results as dictionary.
15 |
16 | Additional information:
17 | https://bybit-exchange.github.io/docs/v5/spread/market/instrument
18 | """
19 | request = self._submit_request(
20 | method="GET",
21 | path=f"{self.endpoint}{Spread.GET_INSTRUMENTS_INFO}",
22 | query=kwargs,
23 | )
24 | return request
25 |
26 | def get_orderbook(self, **kwargs) -> dict:
27 | """
28 | Returns:
29 | Request results as dictionary.
30 |
31 | Additional information:
32 | https://bybit-exchange.github.io/docs/v5/spread/market/orderbook
33 | """
34 | return self._submit_request(
35 | method="GET",
36 | path=f"{self.endpoint}{Spread.GET_ORDERBOOK}",
37 | query=kwargs,
38 | )
39 |
40 | def get_tickers(self, **kwargs) -> dict:
41 | """
42 | Returns:
43 | Request results as dictionary.
44 |
45 | Additional information:
46 | https://bybit-exchange.github.io/docs/v5/spread/market/tickers
47 | """
48 | return self._submit_request(
49 | method="GET",
50 | path=f"{self.endpoint}{Spread.GET_TICKERS}",
51 | query=kwargs,
52 | )
53 |
54 | def get_public_trade_history(self, **kwargs) -> dict:
55 | """
56 | Returns:
57 | Request results as dictionary.
58 |
59 | Additional information:
60 | https://bybit-exchange.github.io/docs/v5/spread/market/recent-trade
61 | """
62 | return self._submit_request(
63 | method="GET",
64 | path=f"{self.endpoint}{Spread.GET_PUBLIC_TRADING_HISTORY}",
65 | query=kwargs,
66 | )
67 |
68 | def place_order(self, **kwargs) -> dict:
69 | """
70 | Required args:
71 | category (string): Product type Unified account: spot, linear, optionNormal account: linear, inverse. Please note that category is not involved with business logic
72 | symbol (string): Symbol name
73 | side (string): Buy, Sell
74 | orderType (string): Market, Limit
75 | qty (string): Order quantity
76 | Returns:
77 | Request results as dictionary.
78 |
79 | Additional information:
80 | https://bybit-exchange.github.io/docs/v5/spread/trade/create-order
81 | """
82 | return self._submit_request(
83 | method="POST",
84 | path=f"{self.endpoint}{Spread.PLACE_ORDER}",
85 | query=kwargs,
86 | auth=True,
87 | )
88 |
89 | def amend_order(self, **kwargs) -> dict:
90 | """
91 | Required args:
92 | symbol (string): Spread combination symbol name
93 |
94 | Returns:
95 | Request results as dictionary.
96 |
97 | Additional information:
98 | https://bybit-exchange.github.io/docs/v5/spread/trade/amend-order
99 | """
100 | return self._submit_request(
101 | method="POST",
102 | path=f"{self.endpoint}{Spread.AMEND_ORDER}",
103 | query=kwargs,
104 | auth=True,
105 | )
106 |
107 | def cancel_order(self, **kwargs) -> dict:
108 | """
109 | Returns:
110 | Request results as dictionary.
111 |
112 | Additional information:
113 | https://bybit-exchange.github.io/docs/v5/spread/trade/cancel-order
114 | """
115 | return self._submit_request(
116 | method="POST",
117 | path=f"{self.endpoint}{Spread.CANCEL_ORDER}",
118 | query=kwargs,
119 | auth=True,
120 | )
121 |
122 | def cancel_all_orders(self, **kwargs) -> dict:
123 | """
124 | Returns:
125 | Request results as dictionary.
126 |
127 | Additional information:
128 | https://bybit-exchange.github.io/docs/v5/spread/trade/cancel-all
129 | """
130 | return self._submit_request(
131 | method="POST",
132 | path=f"{self.endpoint}{Spread.CANCEL_ALL_ORDERS}",
133 | query=kwargs,
134 | auth=True,
135 | )
136 |
137 | def get_open_orders(self, **kwargs) -> dict:
138 | """
139 | Returns:
140 | Request results as dictionary.
141 |
142 | Additional information:
143 | https://bybit-exchange.github.io/docs/v5/spread/trade/open-order
144 | """
145 | return self._submit_request(
146 | method="GET",
147 | path=f"{self.endpoint}{Spread.GET_OPEN_ORDERS}",
148 | query=kwargs,
149 | auth=True,
150 | )
151 |
152 | def get_order_history(self, **kwargs) -> dict:
153 | """
154 | Returns:
155 | Request results as dictionary.
156 |
157 | Additional information:
158 | https://bybit-exchange.github.io/docs/v5/spread/trade/order-history
159 | """
160 | return self._submit_request(
161 | method="GET",
162 | path=f"{self.endpoint}{Spread.GET_ORDER_HISTORY}",
163 | query=kwargs,
164 | auth=True,
165 | )
166 |
167 | def get_trade_history(self, **kwargs) -> dict:
168 | """
169 | Returns:
170 | Request results as dictionary.
171 |
172 | Additional information:
173 | https://bybit-exchange.github.io/docs/v5/spread/trade/trade-history
174 | """
175 | return self._submit_request(
176 | method="GET",
177 | path=f"{self.endpoint}{Spread.GET_TRADE_HISTORY}",
178 | query=kwargs,
179 | auth=True,
180 | )
181 |
182 |
183 | class _V5WebSocketSpreadTrading(_V5WebSocketManager):
184 | def __init__(
185 | self,
186 | **kwargs,
187 | ):
188 | super().__init__(WSS_NAME, **kwargs)
189 | self.WS_URL = PUBLIC_WSS
190 | self._connect(self.WS_URL)
191 |
--------------------------------------------------------------------------------
/pybit/_v5_trade.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .trade import Trade
3 |
4 |
5 | class TradeHTTP(_V5HTTPManager):
6 | def place_order(self, **kwargs):
7 | """This method supports to create the order for spot, spot margin, linear perpetual, inverse futures and options.
8 |
9 | Required args:
10 | category (string): Product type Unified account: spot, linear, optionNormal account: linear, inverse. Please note that category is not involved with business logic
11 | symbol (string): Symbol name
12 | side (string): Buy, Sell
13 | orderType (string): Market, Limit
14 | qty (string): Order quantity
15 | Returns:
16 | Request results as dictionary.
17 |
18 | Additional information:
19 | https://bybit-exchange.github.io/docs/v5/order/create-order
20 | """
21 | return self._submit_request(
22 | method="POST",
23 | path=f"{self.endpoint}{Trade.PLACE_ORDER}",
24 | query=kwargs,
25 | auth=True,
26 | )
27 |
28 | def amend_order(self, **kwargs):
29 | """Unified account covers: Linear contract / Options
30 | Normal account covers: USDT perpetual / Inverse perpetual / Inverse futures
31 |
32 | Required args:
33 | category (string): Product type Unified account: spot, linear, optionNormal account: linear, inverse. Please note that category is not involved with business logic
34 | symbol (string): Symbol name
35 |
36 | Returns:
37 | Request results as dictionary.
38 |
39 | Additional information:
40 | https://bybit-exchange.github.io/docs/v5/order/amend-order
41 | """
42 | return self._submit_request(
43 | method="POST",
44 | path=f"{self.endpoint}{Trade.AMEND_ORDER}",
45 | query=kwargs,
46 | auth=True,
47 | )
48 |
49 | def cancel_order(self, **kwargs):
50 | """Unified account covers: Spot / Linear contract / Options
51 | Normal account covers: USDT perpetual / Inverse perpetual / Inverse futures
52 |
53 | Required args:
54 | category (string): Product type Unified account: spot, linear, optionNormal account: linear, inverse. Please note that category is not involved with business logic
55 | symbol (string): Symbol name
56 | orderId (string): Order ID. Either orderId or orderLinkId is required
57 | orderLinkId (string): User customised order ID. Either orderId or orderLinkId is required
58 |
59 | Returns:
60 | Request results as dictionary.
61 |
62 | Additional information:
63 | https://bybit-exchange.github.io/docs/v5/order/cancel-order
64 | """
65 | return self._submit_request(
66 | method="POST",
67 | path=f"{self.endpoint}{Trade.CANCEL_ORDER}",
68 | query=kwargs,
69 | auth=True,
70 | )
71 |
72 | def get_open_orders(self, **kwargs):
73 | """Query unfilled or partially filled orders in real-time. To query older order records, please use the order history interface.
74 |
75 | Required args:
76 | category (string): Product type Unified account: spot, linear, optionNormal account: linear, inverse. Please note that category is not involved with business logic
77 |
78 | Returns:
79 | Request results as dictionary.
80 |
81 | Additional information:
82 | https://bybit-exchange.github.io/docs/v5/order/open-order
83 | """
84 | return self._submit_request(
85 | method="GET",
86 | path=f"{self.endpoint}{Trade.GET_OPEN_ORDERS}",
87 | query=kwargs,
88 | auth=True,
89 | )
90 |
91 | def cancel_all_orders(self, **kwargs):
92 | """Cancel all open orders
93 |
94 | Required args:
95 | category (string): Product type
96 | Unified account: spot, linear, option
97 | Normal account: linear, inverse.
98 |
99 | Please note that category is not involved with business logic. If cancel all by baseCoin, it will cancel all linear & inverse orders
100 |
101 | Returns:
102 | Request results as dictionary.
103 |
104 | Additional information:
105 | https://bybit-exchange.github.io/docs/v5/order/cancel-all
106 | """
107 | return self._submit_request(
108 | method="POST",
109 | path=f"{self.endpoint}{Trade.CANCEL_ALL_ORDERS}",
110 | query=kwargs,
111 | auth=True,
112 | )
113 |
114 | def get_order_history(self, **kwargs):
115 | """Query order history. As order creation/cancellation is asynchronous, the data returned from this endpoint may delay.
116 | If you want to get real-time order information, you could query this endpoint or rely on the websocket stream (recommended).
117 |
118 | Required args:
119 | category (string): Product type
120 | Unified account: spot, linear, option
121 | Normal account: linear, inverse.
122 |
123 | Please note that category is not involved with business logic
124 |
125 | Returns:
126 | Request results as dictionary.
127 |
128 | Additional information:
129 | https://bybit-exchange.github.io/docs/v5/order/order-list
130 | """
131 | return self._submit_request(
132 | method="GET",
133 | path=f"{self.endpoint}{Trade.GET_ORDER_HISTORY}",
134 | query=kwargs,
135 | auth=True,
136 | )
137 |
138 | def place_batch_order(self, **kwargs):
139 | """Covers: Option (Unified Account)
140 |
141 | Required args:
142 | category (string): Product type. option
143 | request (array): Object
144 | > symbol (string): Symbol name
145 | > side (string): Buy, Sell
146 | > orderType (string): Market, Limit
147 | > qty (string): Order quantity
148 |
149 | Returns:
150 | Request results as dictionary.
151 |
152 | Additional information:
153 | https://bybit-exchange.github.io/docs/v5/order/batch-place
154 | """
155 | return self._submit_request(
156 | method="POST",
157 | path=f"{self.endpoint}{Trade.BATCH_PLACE_ORDER}",
158 | query=kwargs,
159 | auth=True,
160 | )
161 |
162 | def amend_batch_order(self, **kwargs):
163 | """Covers: Option (Unified Account)
164 |
165 | Required args:
166 | category (string): Product type. option
167 | request (array): Object
168 | > symbol (string): Symbol name
169 | > orderId (string): Order ID. Either orderId or orderLinkId is required
170 | > orderLinkId (string): User customised order ID. Either orderId or orderLinkId is required
171 |
172 | Returns:
173 | Request results as dictionary.
174 |
175 | Additional information:
176 | https://bybit-exchange.github.io/docs/v5/order/batch-amend
177 | """
178 | return self._submit_request(
179 | method="POST",
180 | path=f"{self.endpoint}{Trade.BATCH_AMEND_ORDER}",
181 | query=kwargs,
182 | auth=True,
183 | )
184 |
185 | def cancel_batch_order(self, **kwargs):
186 | """This endpoint allows you to cancel more than one open order in a single request.
187 |
188 | Required args:
189 | category (string): Product type. option
190 | request (array): Object
191 | > symbol (string): Symbol name
192 |
193 | Returns:
194 | Request results as dictionary.
195 |
196 | Additional information:
197 | https://bybit-exchange.github.io/docs/v5/order/batch-cancel
198 | """
199 | return self._submit_request(
200 | method="POST",
201 | path=f"{self.endpoint}{Trade.BATCH_CANCEL_ORDER}",
202 | query=kwargs,
203 | auth=True,
204 | )
205 |
206 | def get_borrow_quota(self, **kwargs):
207 | """Query the qty and amount of borrowable coins in spot account.
208 |
209 | Required args:
210 | category (string): Product type. spot
211 | symbol (string): Symbol name
212 | side (string): Transaction side. Buy,Sell
213 |
214 | Returns:
215 | Request results as dictionary.
216 |
217 | Additional information:
218 | https://bybit-exchange.github.io/docs/v5/order/spot-borrow-quota
219 | """
220 | return self._submit_request(
221 | method="GET",
222 | path=f"{self.endpoint}{Trade.GET_BORROW_QUOTA}",
223 | query=kwargs,
224 | auth=True,
225 | )
226 |
227 | def set_dcp(self, **kwargs):
228 | """Covers: Option (Unified Account)
229 |
230 | Required args:
231 | timeWindow (integer): Disconnection timing window time. [10, 300], unit: second
232 |
233 | Returns:
234 | Request results as dictionary.
235 |
236 | Additional information:
237 | https://bybit-exchange.github.io/docs/v5/order/dcp
238 | """
239 | return self._submit_request(
240 | method="POST",
241 | path=f"{self.endpoint}{Trade.SET_DCP}",
242 | query=kwargs,
243 | auth=True,
244 | )
245 |
--------------------------------------------------------------------------------
/pybit/_v5_user.py:
--------------------------------------------------------------------------------
1 | from ._http_manager import _V5HTTPManager
2 | from .user import User
3 |
4 |
5 | class UserHTTP(_V5HTTPManager):
6 | def create_sub_uid(self, **kwargs):
7 | """Create a new sub user id. Use master user's api key only.
8 |
9 | Required args:
10 | username (string): Give a username of the new sub user id. 6-16 characters, must include both numbers and letters.cannot be the same as the exist or deleted one.
11 | memberType (integer): 1: normal sub account, 6: custodial sub account
12 |
13 | Returns:
14 | Request results as dictionary.
15 |
16 | Additional information:
17 | https://bybit-exchange.github.io/docs/v5/user/create-subuid
18 | """
19 | return self._submit_request(
20 | method="POST",
21 | path=f"{self.endpoint}{User.CREATE_SUB_UID}",
22 | query=kwargs,
23 | auth=True,
24 | )
25 |
26 | def create_sub_api_key(self, **kwargs):
27 | """To create new API key for those newly created sub UID. Use master user's api key only.
28 |
29 | Required args:
30 | subuid (integer): Sub user Id
31 | readOnly (integer): 0: Read and Write. 1: Read only
32 | permissions (Object): Tick the types of permission. one of below types must be passed, otherwise the error is thrown
33 |
34 | Returns:
35 | Request results as dictionary.
36 |
37 | Additional information:
38 | https://bybit-exchange.github.io/docs/v5/user/create-subuid-apikey
39 | """
40 | return self._submit_request(
41 | method="POST",
42 | path=f"{self.endpoint}{User.CREATE_SUB_API_KEY}",
43 | query=kwargs,
44 | auth=True,
45 | )
46 |
47 | def get_sub_uid_list(self, **kwargs):
48 | """Get all sub uid of master account. Use master user's api key only.
49 |
50 | Returns:
51 | Request results as dictionary.
52 |
53 | Additional information:
54 | https://bybit-exchange.github.io/docs/v5/user/subuid-list
55 | """
56 | return self._submit_request(
57 | method="GET",
58 | path=f"{self.endpoint}{User.GET_SUB_UID_LIST}",
59 | query=kwargs,
60 | auth=True,
61 | )
62 |
63 | def freeze_sub_uid(self, **kwargs):
64 | """Froze sub uid. Use master user's api key only.
65 |
66 | Required args:
67 | subuid (integer): Sub user Id
68 | frozen (integer): 0: unfreeze, 1: freeze
69 |
70 | Returns:
71 | Request results as dictionary.
72 |
73 | Additional information:
74 | https://bybit-exchange.github.io/docs/v5/user/froze-subuid
75 | """
76 | return self._submit_request(
77 | method="POST",
78 | path=f"{self.endpoint}{User.FREEZE_SUB_UID}",
79 | query=kwargs,
80 | auth=True,
81 | )
82 |
83 | def get_api_key_information(self, **kwargs):
84 | """Get the information of the api key. Use the api key pending to be checked to call the endpoint. Both master and sub user's api key are applicable.
85 |
86 | Returns:
87 | Request results as dictionary.
88 |
89 | Additional information:
90 | https://bybit-exchange.github.io/docs/v5/user/apikey-info
91 | """
92 | return self._submit_request(
93 | method="GET",
94 | path=f"{self.endpoint}{User.GET_API_KEY_INFORMATION}",
95 | query=kwargs,
96 | auth=True,
97 | )
98 |
99 | def modify_master_api_key(self, **kwargs):
100 | """Modify the settings of master api key. Use the api key pending to be modified to call the endpoint. Use master user's api key only.
101 |
102 | Returns:
103 | Request results as dictionary.
104 |
105 | Additional information:
106 | https://bybit-exchange.github.io/docs/v5/user/modify-master-apikey
107 | """
108 | return self._submit_request(
109 | method="POST",
110 | path=f"{self.endpoint}{User.MODIFY_MASTER_API_KEY}",
111 | query=kwargs,
112 | auth=True,
113 | )
114 |
115 | def modify_sub_api_key(self, **kwargs):
116 | """Modify the settings of sub api key. Use the api key pending to be modified to call the endpoint. Use sub user's api key only.
117 |
118 | Returns:
119 | Request results as dictionary.
120 |
121 | Additional information:
122 | https://bybit-exchange.github.io/docs/v5/user/modify-sub-apikey
123 | """
124 | return self._submit_request(
125 | method="POST",
126 | path=f"{self.endpoint}{User.MODIFY_SUB_API_KEY}",
127 | query=kwargs,
128 | auth=True,
129 | )
130 |
131 | def delete_master_api_key(self, **kwargs):
132 | """Delete the api key of master account. Use the api key pending to be delete to call the endpoint. Use master user's api key only.
133 |
134 | Returns:
135 | Request results as dictionary.
136 |
137 | Additional information:
138 | https://bybit-exchange.github.io/docs/v5/user/rm-master-apikey
139 | """
140 | return self._submit_request(
141 | method="POST",
142 | path=f"{self.endpoint}{User.DELETE_MASTER_API_KEY}",
143 | query=kwargs,
144 | auth=True,
145 | )
146 |
147 | def delete_sub_api_key(self, **kwargs):
148 | """Delete the api key of sub account. Use the api key pending to be delete to call the endpoint. Use sub user's api key only.
149 |
150 | Returns:
151 | Request results as dictionary.
152 |
153 | Additional information:
154 | https://bybit-exchange.github.io/docs/v5/user/rm-sub-apikey
155 | """
156 | return self._submit_request(
157 | method="POST",
158 | path=f"{self.endpoint}{User.DELETE_SUB_API_KEY}",
159 | query=kwargs,
160 | auth=True,
161 | )
162 |
163 | def delete_sub_uid(self, **kwargs):
164 | """Delete a sub UID. Before deleting the sub UID, please make sure there is no asset. Use master user's api key only.
165 |
166 | Required args:
167 | subMemberId (integer): Sub UID
168 |
169 | Returns:
170 | Request results as dictionary.
171 |
172 | Additional information:
173 | https://bybit-exchange.github.io/docs/v5/user/rm-subuid
174 | """
175 | return self._submit_request(
176 | method="POST",
177 | path=f"{self.endpoint}{User.DELETE_SUB_UID}",
178 | query=kwargs,
179 | auth=True,
180 | )
181 |
182 | def get_all_sub_api_keys(self, **kwargs):
183 | """Query all api keys information of a sub UID.
184 |
185 | Required args:
186 | subMemberId (integer): Sub UID
187 |
188 | Returns:
189 | Request results as dictionary.
190 |
191 | Additional information:
192 | https://bybit-exchange.github.io/docs/v5/user/list-sub-apikeys
193 | """
194 | return self._submit_request(
195 | method="GET",
196 | path=f"{self.endpoint}{User.GET_ALL_SUB_API_KEYS}",
197 | query=kwargs,
198 | auth=True,
199 | )
200 |
201 | def get_affiliate_user_info(self, **kwargs):
202 | """This API is used for affiliate to get their users information
203 |
204 | Required args:
205 | uid (integer): The master account uid of affiliate's client
206 |
207 | Returns:
208 | Request results as dictionary.
209 |
210 | Additional information:
211 | https://bybit-exchange.github.io/docs/v5/user/affiliate-info
212 | """
213 | return self._submit_request(
214 | method="GET",
215 | path=f"{self.endpoint}{User.GET_AFFILIATE_USER_INFO}",
216 | query=kwargs,
217 | auth=True,
218 | )
219 |
220 | def get_uid_wallet_type(self, **kwargs):
221 | """Get available wallet types for the master account or sub account
222 |
223 | Returns:
224 | Request results as dictionary.
225 |
226 | Additional information:
227 | https://bybit-exchange.github.io/docs/v5/user/wallet-type
228 | """
229 | return self._submit_request(
230 | method="GET",
231 | path=f"{self.endpoint}{User.GET_UID_WALLET_TYPE}",
232 | query=kwargs,
233 | auth=True,
234 | )
235 |
--------------------------------------------------------------------------------
/pybit/_websocket_stream.py:
--------------------------------------------------------------------------------
1 | import websocket
2 | import threading
3 | import time
4 | import json
5 | from ._http_manager import generate_signature
6 | import logging
7 | import copy
8 | from uuid import uuid4
9 | from . import _helpers
10 |
11 |
12 | logger = logging.getLogger(__name__)
13 |
14 |
15 | SUBDOMAIN_TESTNET = "stream-testnet"
16 | SUBDOMAIN_MAINNET = "stream"
17 | DEMO_SUBDOMAIN_TESTNET = "stream-demo-testnet"
18 | DEMO_SUBDOMAIN_MAINNET = "stream-demo"
19 | DOMAIN_MAIN = "bybit"
20 | DOMAIN_ALT = "bytick"
21 | TLD_MAIN = "com"
22 |
23 |
24 | class _WebSocketManager:
25 | def __init__(
26 | self,
27 | _callback_function,
28 | ws_name,
29 | testnet,
30 | tld="",
31 | domain="",
32 | demo=False,
33 | rsa_authentication=False,
34 | api_key=None,
35 | api_secret=None,
36 | ping_interval=20,
37 | ping_timeout=10,
38 | retries=10,
39 | restart_on_error=True,
40 | trace_logging=False,
41 | private_auth_expire=1,
42 | ):
43 | self.testnet = testnet
44 | self.domain = domain
45 | self.tld = tld
46 | self.rsa_authentication = rsa_authentication
47 | self.demo = demo
48 | # Set API keys.
49 | self.api_key = api_key
50 | self.api_secret = api_secret
51 |
52 | self.callback = _callback_function
53 | self.ws_name = ws_name
54 | if api_key:
55 | self.ws_name += " (Auth)"
56 |
57 | # Delta time for private auth expiration in seconds
58 | self.private_auth_expire = private_auth_expire
59 |
60 | # Setup the callback directory following the format:
61 | # {
62 | # "topic_name": function
63 | # }
64 | self.callback_directory = {}
65 |
66 | # Record the subscriptions made so that we can resubscribe if the WSS
67 | # connection is broken.
68 | self.subscriptions = []
69 |
70 | # Set ping settings.
71 | self.ping_interval = ping_interval
72 | self.ping_timeout = ping_timeout
73 | self.custom_ping_message = json.dumps({"op": "ping"})
74 | self.retries = retries
75 |
76 | # Other optional data handling settings.
77 | self.handle_error = restart_on_error
78 |
79 | # Enable websocket-client's trace logging for extra debug information
80 | # on the websocket connection, including the raw sent & recv messages
81 | websocket.enableTrace(trace_logging)
82 |
83 | # Set initial state, initialize dictionary and connect.
84 | self._reset()
85 | self.attempting_connection = False
86 |
87 | def _on_open(self):
88 | """
89 | Log WS open.
90 | """
91 | logger.debug(f"WebSocket {self.ws_name} opened.")
92 |
93 | def _on_message(self, message):
94 | """
95 | Parse incoming messages.
96 | """
97 | message = json.loads(message)
98 | if self._is_custom_pong(message):
99 | return
100 | else:
101 | self.callback(message)
102 |
103 | def is_connected(self):
104 | try:
105 | if self.ws.sock.connected:
106 | return True
107 | else:
108 | return False
109 | except AttributeError:
110 | return False
111 |
112 | def _connect(self, url):
113 | """
114 | Open websocket in a thread.
115 | """
116 |
117 | def resubscribe_to_topics():
118 | if not self.subscriptions:
119 | # There are no subscriptions to resubscribe to, probably
120 | # because this is a brand new WSS initialisation so there was
121 | # no previous WSS connection.
122 | return
123 |
124 | for req_id, subscription_message in self.subscriptions.items():
125 | self.ws.send(subscription_message)
126 |
127 | self.attempting_connection = True
128 |
129 | # Set endpoint.
130 | subdomain = SUBDOMAIN_TESTNET if self.testnet else SUBDOMAIN_MAINNET
131 | domain = DOMAIN_MAIN if not self.domain else self.domain
132 | tld = TLD_MAIN if not self.tld else self.tld
133 | if self.demo:
134 | if self.testnet:
135 | subdomain = DEMO_SUBDOMAIN_TESTNET
136 | else:
137 | subdomain = DEMO_SUBDOMAIN_MAINNET
138 | url = url.format(SUBDOMAIN=subdomain, DOMAIN=domain, TLD=tld)
139 | self.endpoint = url
140 |
141 | # Attempt to connect for X seconds.
142 | retries = self.retries
143 | if retries == 0:
144 | infinitely_reconnect = True
145 | else:
146 | infinitely_reconnect = False
147 |
148 | while (
149 | infinitely_reconnect or retries > 0
150 | ) and not self.is_connected():
151 | logger.info(f"WebSocket {self.ws_name} attempting connection...")
152 | self.ws = websocket.WebSocketApp(
153 | url=url,
154 | on_message=lambda ws, msg: self._on_message(msg),
155 | on_close=lambda ws, *args: self._on_close(),
156 | on_open=lambda ws, *args: self._on_open(),
157 | on_error=lambda ws, err: self._on_error(err),
158 | on_pong=lambda ws, *args: self._on_pong(),
159 | )
160 |
161 | # Setup the thread running WebSocketApp.
162 | self.wst = threading.Thread(
163 | target=lambda: self.ws.run_forever(
164 | ping_interval=self.ping_interval,
165 | ping_timeout=self.ping_timeout,
166 | )
167 | )
168 |
169 | # Configure as daemon; start.
170 | self.wst.daemon = True
171 | self.wst.start()
172 |
173 | retries -= 1
174 | while self.wst.is_alive():
175 | if self.ws.sock and self.is_connected():
176 | break
177 |
178 | # If connection was not successful, raise error.
179 | if not infinitely_reconnect and retries <= 0:
180 | self.exit()
181 | raise websocket.WebSocketTimeoutException(
182 | f"WebSocket {self.ws_name} ({self.endpoint}) connection "
183 | f"failed. Too many connection attempts. pybit will no "
184 | f"longer try to reconnect."
185 | )
186 |
187 | logger.info(f"WebSocket {self.ws_name} connected")
188 |
189 | # If given an api_key, authenticate.
190 | if self.api_key and self.api_secret:
191 | self._auth()
192 |
193 | resubscribe_to_topics()
194 | self._send_initial_ping()
195 |
196 | self.attempting_connection = False
197 |
198 | def _auth(self):
199 | """
200 | Prepares authentication signature per Bybit API specifications.
201 | """
202 |
203 | expires = _helpers.generate_timestamp() + (self.private_auth_expire * 1000)
204 |
205 | param_str = f"GET/realtime{expires}"
206 |
207 | signature = generate_signature(
208 | self.rsa_authentication, self.api_secret, param_str
209 | )
210 |
211 | # Authenticate with API.
212 | self.ws.send(
213 | json.dumps(
214 | {"op": "auth", "args": [self.api_key, expires, signature]}
215 | )
216 | )
217 |
218 | def _on_error(self, error):
219 | """
220 | Exit on errors and raise exception, or attempt reconnect.
221 | """
222 | if type(error).__name__ not in [
223 | "WebSocketConnectionClosedException",
224 | "ConnectionResetError",
225 | "WebSocketTimeoutException",
226 | ]:
227 | # Raises errors not related to websocket disconnection.
228 | self.exit()
229 | raise error
230 |
231 | if not self.exited:
232 | logger.error(
233 | f"WebSocket {self.ws_name} ({self.endpoint}) "
234 | f"encountered error: {error}."
235 | )
236 | self.exit()
237 |
238 | # Reconnect.
239 | if self.handle_error and not self.attempting_connection:
240 | self._reset()
241 | self._connect(self.endpoint)
242 |
243 | def _on_close(self):
244 | """
245 | Log WS close.
246 | """
247 | logger.debug(f"WebSocket {self.ws_name} closed.")
248 |
249 | def _on_pong(self):
250 | """
251 | Sends a custom ping upon the receipt of the pong frame.
252 |
253 | The websocket library will automatically send ping frames. However, to
254 | ensure the connection to Bybit stays open, we need to send a custom
255 | ping message separately from this. When we receive the response to the
256 | ping frame, this method is called, and we will send the custom ping as
257 | a normal OPCODE_TEXT message and not an OPCODE_PING.
258 | """
259 | self._send_custom_ping()
260 |
261 | def _send_custom_ping(self):
262 | self.ws.send(self.custom_ping_message)
263 |
264 | def _send_initial_ping(self):
265 | """https://github.com/bybit-exchange/pybit/issues/164"""
266 | timer = threading.Timer(
267 | self.ping_interval, self._send_custom_ping
268 | )
269 | timer.start()
270 |
271 | @staticmethod
272 | def _is_custom_pong(message):
273 | """
274 | Referring to OPCODE_TEXT pongs from Bybit, not OPCODE_PONG.
275 | """
276 | if message.get("ret_msg") == "pong" or message.get("op") == "pong":
277 | return True
278 |
279 | def _reset(self):
280 | """
281 | Set state booleans and initialize dictionary.
282 | """
283 | self.exited = False
284 | self.auth = False
285 | self.data = {}
286 |
287 | def exit(self):
288 | """
289 | Closes the websocket connection.
290 | """
291 |
292 | self.ws.close()
293 | while self.ws.sock:
294 | continue
295 | self.exited = True
296 |
297 |
298 | class _V5WebSocketManager(_WebSocketManager):
299 | def __init__(self, ws_name, **kwargs):
300 | callback_function = (
301 | kwargs.pop("callback_function")
302 | if kwargs.get("callback_function")
303 | else self._handle_incoming_message
304 | )
305 | super().__init__(callback_function, ws_name, **kwargs)
306 |
307 | self.subscriptions = {}
308 |
309 | self.standard_private_topics = [
310 | "position",
311 | "execution",
312 | "order",
313 | "wallet",
314 | "greeks",
315 | "spread.order",
316 | "spread.execution",
317 | ]
318 |
319 | self.other_private_topics = [
320 | "execution.fast"
321 | ]
322 |
323 | def subscribe(
324 | self,
325 | topic: str,
326 | callback,
327 | symbol: (str, list) = False
328 | ):
329 |
330 | def prepare_subscription_args(list_of_symbols):
331 | """
332 | Prepares the topic for subscription by formatting it with the
333 | desired symbols.
334 | """
335 |
336 | if topic in self.standard_private_topics:
337 | # private topics do not support filters
338 | return [topic]
339 |
340 | topics = []
341 | for single_symbol in list_of_symbols:
342 | topics.append(topic.format(symbol=single_symbol))
343 | return topics
344 |
345 | if type(symbol) == str:
346 | symbol = [symbol]
347 |
348 | subscription_args = prepare_subscription_args(symbol)
349 | self._check_callback_directory(subscription_args)
350 |
351 | req_id = str(uuid4())
352 |
353 | subscription_message = json.dumps(
354 | {"op": "subscribe", "req_id": req_id, "args": subscription_args}
355 | )
356 | while not self.is_connected():
357 | # Wait until the connection is open before subscribing.
358 | time.sleep(0.1)
359 | self.ws.send(subscription_message)
360 | self.subscriptions[req_id] = subscription_message
361 | for topic in subscription_args:
362 | self._set_callback(topic, callback)
363 |
364 | def _initialise_local_data(self, topic):
365 | # Create self.data
366 | try:
367 | self.data[topic]
368 | except KeyError:
369 | self.data[topic] = []
370 |
371 | def _process_delta_orderbook(self, message, topic):
372 | self._initialise_local_data(topic)
373 |
374 | # Record the initial snapshot.
375 | if "snapshot" in message["type"]:
376 | self.data[topic] = message["data"]
377 | return
378 |
379 | # Make updates according to delta response.
380 | book_sides = {"b": message["data"]["b"], "a": message["data"]["a"]}
381 | self.data[topic]["u"]=message["data"]["u"]
382 | self.data[topic]["seq"]=message["data"]["seq"]
383 |
384 | for side, entries in book_sides.items():
385 | for entry in entries:
386 | # Delete.
387 | if float(entry[1]) == 0:
388 | index = _helpers.find_index(
389 | self.data[topic][side], entry, 0
390 | )
391 | self.data[topic][side].pop(index)
392 | continue
393 |
394 | # Insert.
395 | price_level_exists = entry[0] in [
396 | level[0] for level in self.data[topic][side]
397 | ]
398 | if not price_level_exists:
399 | self.data[topic][side].append(entry)
400 | continue
401 |
402 | # Update.
403 | qty_changed = entry[1] != next(
404 | level[1]
405 | for level in self.data[topic][side]
406 | if level[0] == entry[0]
407 | )
408 | if price_level_exists and qty_changed:
409 | index = _helpers.find_index(
410 | self.data[topic][side], entry, 0
411 | )
412 | self.data[topic][side][index] = entry
413 | continue
414 |
415 | def _process_delta_ticker(self, message, topic):
416 | self._initialise_local_data(topic)
417 |
418 | # Record the initial snapshot.
419 | if "snapshot" in message["type"]:
420 | self.data[topic] = message["data"]
421 |
422 | # Make updates according to delta response.
423 | elif "delta" in message["type"]:
424 | for key, value in message["data"].items():
425 | self.data[topic][key] = value
426 |
427 | def _process_auth_message(self, message):
428 | # If we get successful futures auth, notify user
429 | if message.get("success") is True:
430 | logger.debug(f"Authorization for {self.ws_name} successful.")
431 | self.auth = True
432 | # If we get unsuccessful auth, notify user.
433 | elif message.get("success") is False or message.get("type") == "error":
434 | raise Exception(
435 | f"Authorization for {self.ws_name} failed. Please check your "
436 | f"API keys and resync your system time. Raw error: {message}"
437 | )
438 |
439 | def _process_subscription_message(self, message):
440 | if message.get("req_id"):
441 | sub = self.subscriptions[message["req_id"]]
442 | else:
443 | # if req_id is not supported, guess that the last subscription
444 | # sent was successful
445 | sub = json.loads(list(self.subscriptions.items())[0][1])["args"][0]
446 |
447 | # If we get successful futures subscription, notify user
448 | if message.get("success") is True:
449 | logger.debug(f"Subscription to {sub} successful.")
450 | # Futures subscription fail
451 | elif message.get("success") is False:
452 | response = message["ret_msg"]
453 | logger.error("Couldn't subscribe to topic." f"Error: {response}.")
454 | self._pop_callback(sub[0])
455 |
456 | def _process_normal_message(self, message):
457 | topic = message["topic"]
458 | if "orderbook" in topic:
459 | self._process_delta_orderbook(message, topic)
460 | callback_data = copy.deepcopy(message)
461 | callback_data["type"] = "snapshot"
462 | callback_data["data"] = self.data[topic]
463 | elif "tickers" in topic:
464 | self._process_delta_ticker(message, topic)
465 | callback_data = copy.deepcopy(message)
466 | callback_data["type"] = "snapshot"
467 | callback_data["data"] = self.data[topic]
468 | else:
469 | callback_data = message
470 | callback_function = self._get_callback(topic)
471 | callback_function(callback_data)
472 |
473 | def _handle_incoming_message(self, message):
474 | def is_auth_message():
475 | if (
476 | message.get("op") == "auth"
477 | or message.get("type") == "AUTH_RESP"
478 | ):
479 | return True
480 | else:
481 | return False
482 |
483 | def is_subscription_message():
484 | if (
485 | message.get("op") == "subscribe"
486 | or message.get("type") == "COMMAND_RESP"
487 | ):
488 | return True
489 | else:
490 | return False
491 |
492 | if is_auth_message():
493 | self._process_auth_message(message)
494 | elif is_subscription_message():
495 | self._process_subscription_message(message)
496 | else:
497 | self._process_normal_message(message)
498 |
499 | def _check_callback_directory(self, topics):
500 | for topic in topics:
501 | if topic in self.callback_directory:
502 | raise Exception(
503 | f"You have already subscribed to this topic: " f"{topic}"
504 | )
505 |
506 | def _set_callback(self, topic, callback_function):
507 | self.callback_directory[topic] = callback_function
508 |
509 | def _get_callback(self, topic):
510 | return self.callback_directory[topic]
511 |
512 | def _pop_callback(self, topic):
513 | self.callback_directory.pop(topic)
514 |
--------------------------------------------------------------------------------
/pybit/_websocket_trading.py:
--------------------------------------------------------------------------------
1 | from dataclasses import dataclass, field
2 | import json
3 | import uuid
4 | import logging
5 | from ._websocket_stream import _WebSocketManager
6 | from . import _helpers
7 |
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 |
12 | WSS_NAME = "WebSocket Trading"
13 | TRADE_WSS = "wss://{SUBDOMAIN}.{DOMAIN}.{TLD}/v5/trade"
14 |
15 |
16 | class _V5TradeWebSocketManager(_WebSocketManager):
17 | def __init__(self, recv_window, referral_id, **kwargs):
18 | super().__init__(self._handle_incoming_message, WSS_NAME, **kwargs)
19 | self.recv_window = recv_window
20 | self.referral_id = referral_id
21 | self._connect(TRADE_WSS)
22 |
23 | def _process_auth_message(self, message):
24 | # If we get successful auth, notify user
25 | if message.get("retCode") == 0:
26 | logger.debug(f"Authorization for {self.ws_name} successful.")
27 | self.auth = True
28 | # If we get unsuccessful auth, notify user.
29 | else:
30 | raise Exception(
31 | f"Authorization for {self.ws_name} failed. Please check your "
32 | f"API keys and resync your system time. Raw error: {message}"
33 | )
34 |
35 | def _process_error_message(self, message):
36 | logger.error(
37 | f"WebSocket request {message['reqId']} hit an error. Enabling "
38 | f"traceLogging to reproduce the issue. Raw error: {message}"
39 | )
40 | self._pop_callback(message["reqId"])
41 |
42 | def _handle_incoming_message(self, message):
43 | def is_auth_message():
44 | if message.get("op") == "auth":
45 | return True
46 | else:
47 | return False
48 |
49 | def is_error_message():
50 | if message.get("retCode") != 0:
51 | return True
52 | else:
53 | return False
54 |
55 | if is_auth_message():
56 | self._process_auth_message(message)
57 | elif is_error_message():
58 | self._process_error_message(message)
59 | else:
60 | callback_function = self._pop_callback(message["reqId"])
61 | callback_function(message)
62 |
63 | def _set_callback(self, topic, callback_function):
64 | self.callback_directory[topic] = callback_function
65 |
66 | def _pop_callback(self, topic):
67 | return self.callback_directory.pop(topic)
68 |
69 | def _send_order_operation(self, operation, callback, request):
70 | request_id = str(uuid.uuid4())
71 |
72 | message = {
73 | "reqId": request_id,
74 | "header": {
75 | "X-BAPI-TIMESTAMP": _helpers.generate_timestamp(),
76 | },
77 | "op": operation,
78 | "args": [
79 | request
80 | ],
81 | }
82 |
83 | if self.recv_window:
84 | message["header"]["X-BAPI-RECV-WINDOW"] = self.recv_window
85 | if self.referral_id:
86 | message["header"]["Referer"] = self.referral_id
87 |
88 | self.ws.send(json.dumps(message))
89 | self._set_callback(request_id, callback)
90 |
--------------------------------------------------------------------------------
/pybit/account.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class Account(str, Enum):
5 | GET_WALLET_BALANCE = "/v5/account/wallet-balance"
6 | GET_TRANSFERABLE_AMOUNT = "/v5/account/withdrawal"
7 | UPGRADE_TO_UNIFIED_ACCOUNT = "/v5/account/upgrade-to-uta"
8 | GET_BORROW_HISTORY = "/v5/account/borrow-history"
9 | REPAY_LIABILITY = "/v5/account/quick-repayment"
10 | GET_COLLATERAL_INFO = "/v5/account/collateral-info"
11 | SET_COLLATERAL_COIN = "/v5/account/set-collateral-switch"
12 | BATCH_SET_COLLATERAL_COIN = "/v5/account/set-collateral-switch-batch"
13 | GET_COIN_GREEKS = "/v5/asset/coin-greeks"
14 | GET_FEE_RATE = "/v5/account/fee-rate"
15 | GET_ACCOUNT_INFO = "/v5/account/info"
16 | GET_TRANSACTION_LOG = "/v5/account/transaction-log"
17 | GET_CONTRACT_TRANSACTION_LOG = "/v5/account/contract-transaction-log"
18 | SET_MARGIN_MODE = "/v5/account/set-margin-mode"
19 | SET_MMP = "/v5/account/mmp-modify"
20 | RESET_MMP = "/v5/account/mmp-reset"
21 | GET_MMP_STATE = "/v5/account/mmp-state"
22 |
23 | def __str__(self) -> str:
24 | return self.value
25 |
--------------------------------------------------------------------------------
/pybit/asset.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class Asset(str, Enum):
5 | GET_COIN_EXCHANGE_RECORDS = "/v5/asset/exchange/order-record"
6 | GET_OPTION_DELIVERY_RECORD = "/v5/asset/delivery-record"
7 | GET_USDC_CONTRACT_SETTLEMENT = "/v5/asset/settlement-record"
8 | GET_SPOT_ASSET_INFO = "/v5/asset/transfer/query-asset-info"
9 | GET_ALL_COINS_BALANCE = "/v5/asset/transfer/query-account-coins-balance"
10 | GET_SINGLE_COIN_BALANCE = "/v5/asset/transfer/query-account-coin-balance"
11 | GET_TRANSFERABLE_COIN = "/v5/asset/transfer/query-transfer-coin-list"
12 | CREATE_INTERNAL_TRANSFER = "/v5/asset/transfer/inter-transfer"
13 | GET_INTERNAL_TRANSFER_RECORDS = (
14 | "/v5/asset/transfer/query-inter-transfer-list"
15 | )
16 | GET_SUB_UID = "/v5/asset/transfer/query-sub-member-list"
17 | ENABLE_UT_FOR_SUB_UID = "/v5/asset/transfer/save-transfer-sub-member"
18 | CREATE_UNIVERSAL_TRANSFER = "/v5/asset/transfer/universal-transfer"
19 | GET_UNIVERSAL_TRANSFER_RECORDS = (
20 | "/v5/asset/transfer/query-universal-transfer-list"
21 | )
22 | GET_ALLOWED_DEPOSIT_COIN_INFO = "/v5/asset/deposit/query-allowed-list"
23 | SET_DEPOSIT_ACCOUNT = "/v5/asset/deposit/deposit-to-account"
24 | GET_DEPOSIT_RECORDS = "/v5/asset/deposit/query-record"
25 | GET_SUB_ACCOUNT_DEPOSIT_RECORDS = (
26 | "/v5/asset/deposit/query-sub-member-record"
27 | )
28 | GET_INTERNAL_DEPOSIT_RECORDS = "/v5/asset/deposit/query-internal-record"
29 | GET_MASTER_DEPOSIT_ADDRESS = "/v5/asset/deposit/query-address"
30 | GET_SUB_DEPOSIT_ADDRESS = "/v5/asset/deposit/query-sub-member-address"
31 | GET_COIN_INFO = "/v5/asset/coin/query-info"
32 | GET_WITHDRAWAL_RECORDS = "/v5/asset/withdraw/query-record"
33 | GET_WITHDRAWABLE_AMOUNT = "/v5/asset/withdraw/withdrawable-amount"
34 | WITHDRAW = "/v5/asset/withdraw/create"
35 | CANCEL_WITHDRAWAL = "/v5/asset/withdraw/cancel"
36 | # Convert
37 | GET_CONVERT_COIN_LIST = "/v5/asset/exchange/query-coin-list"
38 | REQUEST_A_QUOTE = "/v5/asset/exchange/quote-apply"
39 | CONFIRM_A_QUOTE = "/v5/asset/exchange/convert-execute"
40 | GET_CONVERT_STATUS = "/v5/asset/exchange/convert-result-query"
41 | GET_CONVERT_HISTORY = "/v5/asset/exchange/query-convert-history"
42 |
43 | def __str__(self) -> str:
44 | return self.value
45 |
--------------------------------------------------------------------------------
/pybit/broker.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class Broker(str, Enum):
5 | GET_BROKER_EARNINGS = "/v5/broker/earning-record"
6 | GET_EXCHANGE_BROKER_EARNINGS = "/v5/broker/earnings-info"
7 |
8 | def __str__(self) -> str:
9 | return self.value
10 |
--------------------------------------------------------------------------------
/pybit/crypto_loan.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class CryptoLoan(str, Enum):
5 | GET_COLLATERAL_COINS = "/v5/crypto-loan/collateral-data"
6 | GET_BORROWABLE_COINS = "/v5/crypto-loan/loanable-data"
7 | GET_ACCOUNT_BORROWABLE_OR_COLLATERALIZABLE_LIMIT = "/v5/crypto-loan/borrowable-collateralisable-number"
8 | BORROW_CRYPTO_LOAN = "/v5/crypto-loan/borrow"
9 | REPAY_CRYPTO_LOAN = "/v5/crypto-loan/repay"
10 | GET_UNPAID_LOANS = "/v5/crypto-loan/ongoing-orders"
11 | GET_LOAN_REPAYMENT_HISTORY = "/v5/crypto-loan/repayment-history"
12 | GET_COMPLETED_LOAN_ORDER_HISTORY = "/v5/crypto-loan/borrow-history"
13 | GET_MAX_ALLOWED_COLLATERAL_REDUCTION_AMOUNT = "/v5/crypto-loan/max-collateral-amount"
14 | ADJUST_COLLATERAL_AMOUNT = "/v5/crypto-loan/adjust-ltv"
15 | GET_CRYPTO_LOAN_LTV_ADJUSTMENT_HISTORY = "/v5/crypto-loan/adjustment-history"
16 |
17 | def __str__(self) -> str:
18 | return self.value
19 |
--------------------------------------------------------------------------------
/pybit/earn.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class Earn(str, Enum):
5 | GET_EARN_PRODUCT_INFO = "/v5/earn/product"
6 | STAKE_OR_REDEEM = "/v5/earn/place-order"
7 | GET_STAKE_OR_REDEMPTION_HISTORY = "/v5/earn/order"
8 | GET_STAKED_POSITION = "/v5/earn/position"
9 |
10 | def __str__(self) -> str:
11 | return self.value
12 |
--------------------------------------------------------------------------------
/pybit/exceptions.py:
--------------------------------------------------------------------------------
1 | class UnauthorizedExceptionError(Exception):
2 | pass
3 |
4 |
5 | class InvalidChannelTypeError(Exception):
6 | pass
7 |
8 |
9 | class TopicMismatchError(Exception):
10 | pass
11 |
12 |
13 | class FailedRequestError(Exception):
14 | """
15 | Exception raised for failed requests.
16 |
17 | Attributes:
18 | request -- The original request that caused the error.
19 | message -- Explanation of the error.
20 | status_code -- The code number returned.
21 | time -- The time of the error.
22 | resp_headers -- The response headers from API. None, if the request caused an error locally.
23 | """
24 |
25 | def __init__(self, request, message, status_code, time, resp_headers):
26 | self.request = request
27 | self.message = message
28 | self.status_code = status_code
29 | self.time = time
30 | self.resp_headers = resp_headers
31 | super().__init__(
32 | f"{message.capitalize()} (ErrCode: {status_code}) (ErrTime: {time})"
33 | f".\nRequest → {request}."
34 | )
35 |
36 |
37 | class InvalidRequestError(Exception):
38 | """
39 | Exception raised for returned Bybit errors.
40 |
41 | Attributes:
42 | request -- The original request that caused the error.
43 | message -- Explanation of the error.
44 | status_code -- The code number returned.
45 | time -- The time of the error.
46 | resp_headers -- The response headers from API. None, if the request caused an error locally.
47 | """
48 |
49 | def __init__(self, request, message, status_code, time, resp_headers):
50 | self.request = request
51 | self.message = message
52 | self.status_code = status_code
53 | self.time = time
54 | self.resp_headers = resp_headers
55 | super().__init__(
56 | f"{message} (ErrCode: {status_code}) (ErrTime: {time})"
57 | f".\nRequest → {request}."
58 | )
59 |
--------------------------------------------------------------------------------
/pybit/helpers.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 |
4 | def _opposite_side(side):
5 | if side == "Buy":
6 | return "Sell"
7 | else:
8 | return "Buy"
9 |
10 |
11 | class Helpers:
12 | def __init__(self, session):
13 | self.logger = logging.getLogger(__name__)
14 | self.session = session
15 |
16 | def close_position(self, category, symbol) -> list:
17 | """Market close the positions on a certain symbol.
18 |
19 | Required args:
20 | category (string): Product type: linear,inverse
21 | symbol (string): Symbol name
22 |
23 | Returns:
24 | Request results as list.
25 |
26 | Additional information:
27 | """
28 |
29 | positions = self.session.get_positions(category=category, symbol=symbol)
30 | positions = positions["result"]["list"]
31 |
32 | responses = []
33 | for position in positions:
34 | if position["side"] and float(position["size"]) != 0:
35 | response = self.session.place_order(
36 | category=category,
37 | symbol=symbol,
38 | side=_opposite_side(position["side"]),
39 | qty=position["size"],
40 | orderType="Market",
41 | positionIdx=position["positionIdx"],
42 | )
43 | responses.append(response)
44 |
45 | if not responses:
46 | self.logger.error("Tried to close_position; no position detected.")
47 |
48 | return responses
49 |
--------------------------------------------------------------------------------
/pybit/institutional_loan.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class InstitutionalLoan(str, Enum):
5 | GET_PRODUCT_INFO = "/v5/ins-loan/product-infos"
6 | GET_MARGIN_COIN_INFO = "/v5/ins-loan/ensure-tokens-convert"
7 | GET_LOAN_ORDERS = "/v5/ins-loan/loan-order"
8 | GET_REPAYMENT_ORDERS = "/v5/ins-loan/repaid-history"
9 | GET_LTV = "/v5/ins-loan/ltv-convert"
10 | BIND_OR_UNBIND_UID = "/v5/ins-loan/association-uid"
11 |
12 | def __str__(self) -> str:
13 | return self.value
14 |
--------------------------------------------------------------------------------
/pybit/market.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class Market(str, Enum):
5 | GET_SERVER_TIME = "/v5/market/time"
6 | GET_KLINE = "/v5/market/kline"
7 | GET_MARK_PRICE_KLINE = "/v5/market/mark-price-kline"
8 | GET_INDEX_PRICE_KLINE = "/v5/market/index-price-kline"
9 | GET_PREMIUM_INDEX_PRICE_KLINE = "/v5/market/premium-index-price-kline"
10 | GET_INSTRUMENTS_INFO = "/v5/market/instruments-info"
11 | GET_ORDERBOOK = "/v5/market/orderbook"
12 | GET_TICKERS = "/v5/market/tickers"
13 | GET_FUNDING_RATE_HISTORY = "/v5/market/funding/history"
14 | GET_PUBLIC_TRADING_HISTORY = "/v5/market/recent-trade"
15 | GET_OPEN_INTEREST = "/v5/market/open-interest"
16 | GET_HISTORICAL_VOLATILITY = "/v5/market/historical-volatility"
17 | GET_INSURANCE = "/v5/market/insurance"
18 | GET_RISK_LIMIT = "/v5/market/risk-limit"
19 | GET_OPTION_DELIVERY_PRICE = "/v5/market/delivery-price"
20 | GET_LONG_SHORT_RATIO = "/v5/market/account-ratio"
21 |
22 | def __str__(self) -> str:
23 | return self.value
24 |
--------------------------------------------------------------------------------
/pybit/misc.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class Misc(str, Enum):
5 | GET_ANNOUNCEMENT = "/v5/announcements/index"
6 | REQUEST_DEMO_TRADING_FUNDS = "/v5/account/demo-apply-money"
7 |
8 | def __str__(self) -> str:
9 | return self.value
10 |
--------------------------------------------------------------------------------
/pybit/position.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class Position(str, Enum):
5 | GET_POSITIONS = "/v5/position/list"
6 | SET_LEVERAGE = "/v5/position/set-leverage"
7 | SWITCH_MARGIN_MODE = "/v5/position/switch-isolated"
8 | SET_TP_SL_MODE = "/v5/position/set-tpsl-mode"
9 | SWITCH_POSITION_MODE = "/v5/position/switch-mode"
10 | SET_RISK_LIMIT = "/v5/position/set-risk-limit"
11 | SET_TRADING_STOP = "/v5/position/trading-stop"
12 | SET_AUTO_ADD_MARGIN = "/v5/position/set-auto-add-margin"
13 | ADD_MARGIN = "/v5/position/add-margin"
14 | GET_EXECUTIONS = "/v5/execution/list"
15 | GET_CLOSED_PNL = "/v5/position/closed-pnl"
16 |
17 | def __str__(self) -> str:
18 | return self.value
19 |
--------------------------------------------------------------------------------
/pybit/pre_upgrade.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class PreUpgrade(str, Enum):
5 | GET_PRE_UPGRADE_ORDER_HISTORY = "/v5/pre-upgrade/order/history"
6 | GET_PRE_UPGRADE_TRADE_HISTORY = "/v5/pre-upgrade/execution/list"
7 | GET_PRE_UPGRADE_CLOSED_PNL = "/v5/pre-upgrade/position/closed-pnl"
8 | GET_PRE_UPGRADE_TRANSACTION_LOG = "/v5/pre-upgrade/account/transaction-log"
9 | GET_PRE_UPGRADE_OPTION_DELIVERY_RECORD = "/v5/pre-upgrade/asset/delivery-record"
10 | GET_PRE_UPGRADE_USDC_SESSION_SETTLEMENT = "/v5/pre-upgrade/asset/settlement-record"
11 |
12 | def __str__(self) -> str:
13 | return self.value
14 |
--------------------------------------------------------------------------------
/pybit/spot_leverage_token.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class SpotLeverageToken(str, Enum):
5 | GET_LEVERAGED_TOKEN_INFO = "/v5/spot-lever-token/info"
6 | GET_LEVERAGED_TOKEN_MARKET = "/v5/spot-lever-token/reference"
7 | PURCHASE = "/v5/spot-lever-token/purchase"
8 | REDEEM = "/v5/spot-lever-token/redeem"
9 | GET_PURCHASE_REDEMPTION_RECORDS = "/v5/spot-lever-token/order-record"
10 |
11 | def __str__(self) -> str:
12 | return self.value
13 |
--------------------------------------------------------------------------------
/pybit/spot_margin_trade.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class SpotMarginTrade(str, Enum):
5 | # UTA endpoints
6 | TOGGLE_MARGIN_TRADE = "/v5/spot-margin-trade/switch-mode"
7 | SET_LEVERAGE = "/v5/spot-margin-trade/set-leverage"
8 | VIP_MARGIN_DATA = "/v5/spot-margin-trade/data"
9 | STATUS_AND_LEVERAGE = "/v5/spot-margin-trade/state"
10 | HISTORICAL_INTEREST = "/v5/spot-margin-trade/interest-rate-history"
11 | # normal mode (non-UTA) endpoints
12 | NORMAL_GET_VIP_MARGIN_DATA = "/v5/spot-cross-margin-trade/data"
13 | NORMAL_GET_MARGIN_COIN_INFO = "/v5/spot-cross-margin-trade/pledge-token"
14 | NORMAL_GET_BORROWABLE_COIN_INFO = "/v5/spot-cross-margin-trade/borrow-token"
15 | NORMAL_GET_INTEREST_QUOTA = "/v5/spot-cross-margin-trade/loan-info"
16 | NORMAL_GET_LOAN_ACCOUNT_INFO = "/v5/spot-cross-margin-trade/account"
17 | NORMAL_BORROW = "/v5/spot-cross-margin-trade/loan"
18 | NORMAL_REPAY = "/v5/spot-cross-margin-trade/repay"
19 | NORMAL_GET_BORROW_ORDER_DETAIL = "/v5/spot-cross-margin-trade/orders"
20 | NORMAL_GET_REPAYMENT_ORDER_DETAIL = "/v5/spot-cross-margin-trade/repay-history"
21 | NORMAL_TOGGLE_MARGIN_TRADE = "/v5/spot-cross-margin-trade/switch"
22 |
23 | def __str__(self) -> str:
24 | return self.value
25 |
--------------------------------------------------------------------------------
/pybit/spread.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class Spread(str, Enum):
5 | GET_INSTRUMENTS_INFO = "/v5/spread/instrument"
6 | GET_ORDERBOOK = "/v5/spread/orderbook"
7 | GET_TICKERS = "/v5/spread/tickers"
8 | GET_PUBLIC_TRADING_HISTORY = "/v5/spread/recent-trade"
9 | PLACE_ORDER = "/v5/spread/order/create"
10 | AMEND_ORDER = "/v5/spread/order/amend"
11 | CANCEL_ORDER = "/v5/spread/order/cancel"
12 | CANCEL_ALL_ORDERS = "/v5/spread/order/cancel-all"
13 | GET_OPEN_ORDERS = "/v5/spread/order/realtime"
14 | GET_ORDER_HISTORY = "/v5/spread/order/history"
15 | GET_TRADE_HISTORY = "/v5/spread/execution/list"
16 |
17 | def __str__(self) -> str:
18 | return self.value
19 |
--------------------------------------------------------------------------------
/pybit/trade.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class Trade(str, Enum):
5 | PLACE_ORDER = "/v5/order/create"
6 | AMEND_ORDER = "/v5/order/amend"
7 | CANCEL_ORDER = "/v5/order/cancel"
8 | GET_OPEN_ORDERS = "/v5/order/realtime"
9 | CANCEL_ALL_ORDERS = "/v5/order/cancel-all"
10 | GET_ORDER_HISTORY = "/v5/order/history"
11 | BATCH_PLACE_ORDER = "/v5/order/create-batch"
12 | BATCH_AMEND_ORDER = "/v5/order/amend-batch"
13 | BATCH_CANCEL_ORDER = "/v5/order/cancel-batch"
14 | GET_BORROW_QUOTA = "/v5/order/spot-borrow-check"
15 | SET_DCP = "/v5/order/disconnected-cancel-all"
16 |
17 | def __str__(self) -> str:
18 | return self.value
19 |
--------------------------------------------------------------------------------
/pybit/unified_trading.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from dataclasses import dataclass
3 | from pybit.exceptions import (
4 | InvalidChannelTypeError,
5 | TopicMismatchError,
6 | UnauthorizedExceptionError,
7 | )
8 | from ._v5_misc import MiscHTTP
9 | from ._v5_market import MarketHTTP
10 | from ._v5_trade import TradeHTTP
11 | from ._v5_account import AccountHTTP
12 | from ._v5_asset import AssetHTTP
13 | from ._v5_position import PositionHTTP
14 | from ._v5_pre_upgrade import PreUpgradeHTTP
15 | from ._v5_spot_leverage_token import SpotLeverageHTTP
16 | from ._v5_spot_margin_trade import SpotMarginTradeHTTP
17 | from ._v5_user import UserHTTP
18 | from ._v5_broker import BrokerHTTP
19 | from ._v5_institutional_loan import InstitutionalLoanHTTP
20 | from ._v5_crypto_loan import CryptoLoanHTTP
21 | from ._v5_earn import EarnHTTP
22 | from ._websocket_stream import _V5WebSocketManager
23 | from ._websocket_trading import _V5TradeWebSocketManager
24 | from ._v5_spread import (
25 | SpreadHTTP,
26 | _V5WebSocketSpreadTrading,
27 | )
28 |
29 |
30 | logger = logging.getLogger(__name__)
31 |
32 | WSS_NAME = "Unified V5"
33 | PRIVATE_WSS = "wss://{SUBDOMAIN}.{DOMAIN}.com/v5/private"
34 | PUBLIC_WSS = "wss://{SUBDOMAIN}.{DOMAIN}.com/v5/public/{CHANNEL_TYPE}"
35 | AVAILABLE_CHANNEL_TYPES = [
36 | "inverse",
37 | "linear",
38 | "spot",
39 | "option",
40 | "private",
41 | ]
42 |
43 |
44 | @dataclass
45 | class HTTP(
46 | MiscHTTP,
47 | MarketHTTP,
48 | TradeHTTP,
49 | AccountHTTP,
50 | AssetHTTP,
51 | PositionHTTP,
52 | PreUpgradeHTTP,
53 | SpotLeverageHTTP,
54 | SpotMarginTradeHTTP,
55 | UserHTTP,
56 | BrokerHTTP,
57 | InstitutionalLoanHTTP,
58 | CryptoLoanHTTP,
59 | EarnHTTP,
60 | ):
61 | def __init__(self, **args):
62 | super().__init__(**args)
63 |
64 |
65 | class WebSocket(_V5WebSocketManager):
66 | def _validate_public_topic(self):
67 | if "/v5/public" not in self.WS_URL:
68 | raise TopicMismatchError(
69 | "Requested topic does not match channel_type"
70 | )
71 |
72 | def _validate_private_topic(self):
73 | if not self.WS_URL.endswith("/private"):
74 | raise TopicMismatchError(
75 | "Requested topic does not match channel_type"
76 | )
77 |
78 | def __init__(
79 | self,
80 | channel_type: str,
81 | **kwargs,
82 | ):
83 | super().__init__(WSS_NAME, **kwargs)
84 | if channel_type not in AVAILABLE_CHANNEL_TYPES:
85 | raise InvalidChannelTypeError(
86 | f"Channel type is not correct. Available: {AVAILABLE_CHANNEL_TYPES}"
87 | )
88 |
89 | if channel_type == "private":
90 | self.WS_URL = PRIVATE_WSS
91 | else:
92 | self.WS_URL = PUBLIC_WSS.replace("{CHANNEL_TYPE}", channel_type)
93 | # Do not pass keys and attempt authentication on a public connection
94 | self.api_key = None
95 | self.api_secret = None
96 |
97 | if (
98 | self.api_key is None or self.api_secret is None
99 | ) and channel_type == "private":
100 | raise UnauthorizedExceptionError(
101 | "API_KEY or API_SECRET is not set. They both are needed in order to access private topics"
102 | )
103 |
104 | self._connect(self.WS_URL)
105 |
106 | # Private topics
107 |
108 | def position_stream(self, callback):
109 | """Subscribe to the position stream to see changes to your position data in real-time.
110 |
111 | Push frequency: real-time
112 |
113 | Additional information:
114 | https://bybit-exchange.github.io/docs/v5/websocket/private/position
115 | """
116 | self._validate_private_topic()
117 | topic = "position"
118 | self.subscribe(topic, callback)
119 |
120 | def order_stream(self, callback):
121 | """Subscribe to the order stream to see changes to your orders in real-time.
122 |
123 | Push frequency: real-time
124 |
125 | Additional information:
126 | https://bybit-exchange.github.io/docs/v5/websocket/private/order
127 | """
128 | self._validate_private_topic()
129 | topic = "order"
130 | self.subscribe(topic, callback)
131 |
132 | def execution_stream(self, callback):
133 | """Subscribe to the execution stream to see your executions in real-time.
134 |
135 | Push frequency: real-time
136 |
137 | Additional information:
138 | https://bybit-exchange.github.io/docs/v5/websocket/private/execution
139 | """
140 | self._validate_private_topic()
141 | topic = "execution"
142 | self.subscribe(topic, callback)
143 |
144 | def fast_execution_stream(self, callback, categorised_topic=""):
145 | """Fast execution stream significantly reduces data latency compared
146 | original "execution" stream. However, it pushes limited execution type
147 | of trades, and fewer data fields.
148 | Use categorised_topic as a filter for a certain `category`. See docs.
149 |
150 | Push frequency: real-time
151 |
152 | Additional information:
153 | https://bybit-exchange.github.io/docs/v5/websocket/private/fast-execution
154 | """
155 | self._validate_private_topic()
156 | topic = "execution.fast"
157 | if categorised_topic:
158 | topic += "." + categorised_topic
159 | self.subscribe(topic, callback)
160 |
161 | def wallet_stream(self, callback):
162 | """Subscribe to the wallet stream to see changes to your wallet in real-time.
163 |
164 | Push frequency: real-time
165 |
166 | Additional information:
167 | https://bybit-exchange.github.io/docs/v5/websocket/private/wallet
168 | """
169 | self._validate_private_topic()
170 | topic = "wallet"
171 | self.subscribe(topic, callback)
172 |
173 | def greek_stream(self, callback):
174 | """Subscribe to the greeks stream to see changes to your greeks data in real-time. option only.
175 |
176 | Push frequency: real-time
177 |
178 | Additional information:
179 | https://bybit-exchange.github.io/docs/v5/websocket/private/greek
180 | """
181 | self._validate_private_topic()
182 | topic = "greeks"
183 | self.subscribe(topic, callback)
184 |
185 | def spread_order_stream(self, callback):
186 | """Subscribe to the spread trading order stream to see changes to your orders in real-time.
187 |
188 | Push frequency: real-time
189 |
190 | Additional information:
191 | https://bybit-exchange.github.io/docs/v5/spread/websocket/private/order
192 | """
193 | self._validate_private_topic()
194 | topic = "spread.order"
195 | self.subscribe(topic, callback)
196 |
197 | def spread_execution_stream(self, callback):
198 | """Subscribe to the spread trading execution stream to see your executions in real-time.
199 |
200 | Push frequency: real-time
201 |
202 | Additional information:
203 | https://bybit-exchange.github.io/docs/v5/spread/websocket/private/execution
204 | """
205 | self._validate_private_topic()
206 | topic = "spread.execution"
207 | self.subscribe(topic, callback)
208 |
209 | # Public topics
210 |
211 | def orderbook_stream(self, depth: int, symbol: (str, list), callback):
212 | """Subscribe to the orderbook stream. Supports different depths.
213 |
214 | Linear & inverse:
215 | Level 1 data, push frequency: 10ms
216 | Level 50 data, push frequency: 20ms
217 | Level 200 data, push frequency: 100ms
218 | Level 500 data, push frequency: 100ms
219 |
220 | Spot:
221 | Level 1 data, push frequency: 10ms
222 | Level 50 data, push frequency: 20ms
223 |
224 | Option:
225 | Level 25 data, push frequency: 20ms
226 | Level 100 data, push frequency: 100ms
227 |
228 | Required args:
229 | symbol (string/list): Symbol name(s)
230 | depth (int): Orderbook depth
231 |
232 | Additional information:
233 | https://bybit-exchange.github.io/docs/v5/websocket/public/orderbook
234 | """
235 | self._validate_public_topic()
236 | topic = f"orderbook.{depth}." + "{symbol}"
237 | self.subscribe(topic, callback, symbol)
238 |
239 | def trade_stream(self, symbol: (str, list), callback):
240 | """
241 | Subscribe to the recent trades stream.
242 | After subscription, you will be pushed trade messages in real-time.
243 |
244 | Push frequency: real-time
245 |
246 | Required args:
247 | symbol (string/list): Symbol name(s)
248 |
249 | Additional information:
250 | https://bybit-exchange.github.io/docs/v5/websocket/public/trade
251 | """
252 | self._validate_public_topic()
253 | topic = f"publicTrade." + "{symbol}"
254 | self.subscribe(topic, callback, symbol)
255 |
256 | def ticker_stream(self, symbol: (str, list), callback):
257 | """Subscribe to the ticker stream.
258 |
259 | Push frequency: 100ms
260 |
261 | Required args:
262 | symbol (string/list): Symbol name(s)
263 |
264 | Additional information:
265 | https://bybit-exchange.github.io/docs/v5/websocket/public/ticker
266 | """
267 | self._validate_public_topic()
268 | topic = "tickers.{symbol}"
269 | self.subscribe(topic, callback, symbol)
270 |
271 | def kline_stream(self, interval: int, symbol: (str, list), callback):
272 | """Subscribe to the klines stream.
273 |
274 | Push frequency: 1-60s
275 |
276 | Required args:
277 | symbol (string/list): Symbol name(s)
278 | interval (int): Kline interval
279 |
280 | Additional information:
281 | https://bybit-exchange.github.io/docs/v5/websocket/public/kline
282 | """
283 | self._validate_public_topic()
284 | topic = f"kline.{interval}." + "{symbol}"
285 | self.subscribe(topic, callback, symbol)
286 |
287 | def liquidation_stream(self, symbol: (str, list), callback):
288 | """
289 | Pushes at most one order per second per symbol.
290 | As such, this feed does not push all liquidations that occur on Bybit.
291 |
292 | Push frequency: 1s
293 |
294 | Required args:
295 | symbol (string/list): Symbol name(s)
296 |
297 | Additional information:
298 | https://bybit-exchange.github.io/docs/v5/websocket/public/liquidation
299 | """
300 | logger.warning("liquidation_stream() is deprecated. Please use "
301 | "all_liquidation_stream().")
302 | self._validate_public_topic()
303 | topic = "liquidation.{symbol}"
304 | self.subscribe(topic, callback, symbol)
305 |
306 | def all_liquidation_stream(self, symbol: (str, list), callback):
307 | """Subscribe to the liquidation stream, push all liquidations that
308 | occur on Bybit.
309 |
310 | Push frequency: 500ms
311 |
312 | Required args:
313 | symbol (string/list): Symbol name(s)
314 |
315 | Additional information:
316 | https://bybit-exchange.github.io/docs/v5/websocket/public/all-liquidation
317 | """
318 | self._validate_public_topic()
319 | topic = "allLiquidation.{symbol}"
320 | self.subscribe(topic, callback, symbol)
321 |
322 | def lt_kline_stream(self, interval: int, symbol: (str, list), callback):
323 | """Subscribe to the leveraged token kline stream.
324 |
325 | Push frequency: 1-60s
326 |
327 | Required args:
328 | symbol (string/list): Symbol name(s)
329 | interval (int): Leveraged token Kline stream interval
330 |
331 | Additional information:
332 | https://bybit-exchange.github.io/docs/v5/websocket/public/etp-kline
333 | """
334 | self._validate_public_topic()
335 | topic = f"kline_lt.{interval}." + "{symbol}"
336 | self.subscribe(topic, callback, symbol)
337 |
338 | def lt_ticker_stream(self, symbol: (str, list), callback):
339 | """Subscribe to the leveraged token ticker stream.
340 |
341 | Push frequency: 300ms
342 |
343 | Required args:
344 | symbol (string/list): Symbol name(s)
345 |
346 | Additional information:
347 | https://bybit-exchange.github.io/docs/v5/websocket/public/etp-ticker
348 | """
349 | self._validate_public_topic()
350 | topic = "tickers_lt.{symbol}"
351 | self.subscribe(topic, callback, symbol)
352 |
353 | def lt_nav_stream(self, symbol: (str, list), callback):
354 | """Subscribe to the leveraged token nav stream.
355 |
356 | Push frequency: 300ms
357 |
358 | Required args:
359 | symbol (string/list): Symbol name(s)
360 |
361 | Additional information:
362 | https://bybit-exchange.github.io/docs/v5/websocket/public/etp-nav
363 | """
364 | self._validate_public_topic()
365 | topic = "lt.{symbol}"
366 | self.subscribe(topic, callback, symbol)
367 |
368 |
369 | class WebSocketTrading(_V5TradeWebSocketManager):
370 | def __init__(self, recv_window=0, referral_id="", **kwargs):
371 | super().__init__(recv_window, referral_id, **kwargs)
372 |
373 | def place_order(self, callback, **kwargs):
374 | operation = "order.create"
375 | self._send_order_operation(operation, callback, kwargs)
376 |
377 | def amend_order(self, callback, **kwargs):
378 | operation = "order.amend"
379 | self._send_order_operation(operation, callback, kwargs)
380 |
381 | def cancel_order(self, callback, **kwargs):
382 | operation = "order.cancel"
383 | self._send_order_operation(operation, callback, kwargs)
384 |
385 | def place_batch_order(self, callback, **kwargs):
386 | operation = "order.create-batch"
387 | self._send_order_operation(operation, callback, kwargs)
388 |
389 | def amend_batch_order(self, callback, **kwargs):
390 | operation = "order.amend-batch"
391 | self._send_order_operation(operation, callback, kwargs)
392 |
393 | def cancel_batch_order(self, callback, **kwargs):
394 | operation = "order.cancel-batch"
395 | self._send_order_operation(operation, callback, kwargs)
396 |
397 |
398 | class WebsocketSpreadTrading(_V5WebSocketSpreadTrading):
399 | def __init__(self, **kwargs):
400 | super().__init__(**kwargs)
401 |
402 | def orderbook_stream(self, depth: int, symbol: (str, list), callback):
403 | """Subscribe to the orderbook stream. Supports different depths.
404 |
405 | Level 25 data, push frequency: 20ms
406 |
407 | Required args:
408 | symbol (string/list): Symbol name(s)
409 | depth (int): Orderbook depth
410 |
411 | Additional information:
412 | https://bybit-exchange.github.io/docs/v5/spread/websocket/public/orderbook
413 | """
414 | topic = f"orderbook.{depth}." + "{symbol}"
415 | self.subscribe(topic, callback, symbol)
416 |
417 | def trade_stream(self, symbol: (str, list), callback):
418 | """
419 | Subscribe to the recent trades stream.
420 | After subscription, you will be pushed trade messages in real-time.
421 |
422 | Push frequency: real-time
423 |
424 | Required args:
425 | symbol (string/list): Symbol name(s)
426 |
427 | Additional information:
428 | https://bybit-exchange.github.io/docs/v5/spread/websocket/public/trade
429 | """
430 | topic = f"publicTrade." + "{symbol}"
431 | self.subscribe(topic, callback, symbol)
432 |
433 | def ticker_stream(self, symbol: (str, list), callback):
434 | """Subscribe to the ticker stream.
435 |
436 | Push frequency: 100ms
437 |
438 | Required args:
439 | symbol (string/list): Symbol name(s)
440 |
441 | Additional information:
442 | https://bybit-exchange.github.io/docs/v5/spread/websocket/public/ticker
443 | """
444 | topic = "tickers.{symbol}"
445 | self.subscribe(topic, callback, symbol)
446 |
--------------------------------------------------------------------------------
/pybit/user.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class User(str, Enum):
5 | CREATE_SUB_UID = "/v5/user/create-sub-member"
6 | CREATE_SUB_API_KEY = "/v5/user/create-sub-api"
7 | GET_SUB_UID_LIST = "/v5/user/query-sub-members"
8 | FREEZE_SUB_UID = "/v5/user/frozen-sub-member"
9 | GET_API_KEY_INFORMATION = "/v5/user/query-api"
10 | MODIFY_MASTER_API_KEY = "/v5/user/update-api"
11 | MODIFY_SUB_API_KEY = "/v5/user/update-sub-api"
12 | DELETE_MASTER_API_KEY = "/v5/user/delete-api"
13 | DELETE_SUB_API_KEY = "/v5/user/delete-sub-api"
14 | GET_AFFILIATE_USER_INFO = "/v5/user/aff-customer-info"
15 | GET_UID_WALLET_TYPE = "/v5/user/get-member-type"
16 | DELETE_SUB_UID = "/v5/user/del-submember"
17 | GET_ALL_SUB_API_KEYS = "/v5/user/sub-apikeys"
18 |
19 | def __str__(self) -> str:
20 | return self.value
21 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | requests>=2.22.0
2 | websocket-client==1.5.0
3 | pycryptodome==3.20.0
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | license_files = LICENSE
3 |
4 | [options]
5 | zip_safe = False
6 |
7 | [bdist_wheel]
8 | universal=1
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 | from os import path
3 |
4 | here = path.abspath(path.dirname(__file__))
5 |
6 | with open(path.join(here, "README.md"), encoding="utf-8") as f:
7 | long_description = f.read()
8 |
9 | setup(
10 | name='pybit',
11 | version='5.11.0',
12 | description='Python3 Bybit HTTP/WebSocket API Connector',
13 | long_description=long_description,
14 | long_description_content_type="text/markdown",
15 | url="https://github.com/bybit-exchange/pybit",
16 | license="MIT License",
17 | author="Dexter Dickinson",
18 | author_email="dexter.dickinson@bybit.com",
19 | classifiers=[
20 | "Development Status :: 4 - Beta",
21 | "Intended Audience :: Developers",
22 | "Topic :: Software Development :: Libraries :: Python Modules",
23 | "License :: OSI Approved :: MIT License",
24 | "Programming Language :: Python :: 3.9",
25 | "Programming Language :: Python :: 3.10",
26 | ],
27 | keywords="bybit api connector",
28 | packages=["pybit"],
29 | python_requires=">=3.6",
30 | install_requires=[
31 | "requests",
32 | "websocket-client",
33 | "pycryptodome",
34 | ],
35 | )
36 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bybit-exchange/pybit/63cb8922ccae000cae0394c094ada3c07723b371/tests/__init__.py
--------------------------------------------------------------------------------
/tests/test_pybit.py:
--------------------------------------------------------------------------------
1 | import time
2 | import unittest
3 |
4 | from pybit.exceptions import InvalidChannelTypeError, TopicMismatchError
5 | from pybit.unified_trading import HTTP, WebSocket
6 |
7 | # session uses Bybit's mainnet endpoint
8 | session = HTTP()
9 | ws = WebSocket(
10 | channel_type="spot",
11 | testnet=False,
12 | )
13 |
14 |
15 | class HTTPTest(unittest.TestCase):
16 | def test_orderbook(self):
17 | self.assertEqual(
18 | session.get_orderbook(category="spot", symbol="BTCUSDT")["retMsg"],
19 | "OK",
20 | )
21 |
22 | def test_query_kline(self):
23 | self.assertEqual(
24 | (
25 | session.get_kline(
26 | symbol="BTCUSDT",
27 | interval="1",
28 | from_time=int(time.time()) - 60 * 60,
29 | )["retMsg"]
30 | ),
31 | "OK",
32 | )
33 |
34 | def test_symbol_information(self):
35 | self.assertEqual(
36 | session.get_instruments_info(category="spot", symbol="BTCUSDT")[
37 | "retMsg"
38 | ],
39 | "OK",
40 | )
41 |
42 | # We can't really test authenticated endpoints without keys, but we
43 | # can make sure it raises a PermissionError.
44 | def test_place_active_order(self):
45 | with self.assertRaises(PermissionError):
46 | session.place_order(
47 | symbol="BTCUSD",
48 | order_type="Market",
49 | side="Buy",
50 | qty=1,
51 | category="spot",
52 | )
53 |
54 |
55 | class WebSocketTest(unittest.TestCase):
56 | # A very simple test to ensure we're getting something from WS.
57 | def _callback_function(msg):
58 | print(msg)
59 |
60 | def test_websocket(self):
61 | self.assertNotEqual(
62 | ws.orderbook_stream(
63 | depth=1,
64 | symbol="BTCUSDT",
65 | callback=self._callback_function,
66 | ),
67 | [],
68 | )
69 |
70 | def test_invalid_category(self):
71 | with self.assertRaises(InvalidChannelTypeError):
72 | WebSocket(
73 | channel_type="not_exists",
74 | testnet=False,
75 | )
76 |
77 | def test_topic_category_mismatch(self):
78 | with self.assertRaises(TopicMismatchError):
79 | ws = WebSocket(
80 | channel_type="linear",
81 | testnet=False,
82 | )
83 |
84 | ws.order_stream(callback=self._callback_function)
85 |
86 | class PrivateWebSocketTest(unittest.TestCase):
87 | # Connect to private websocket and see if we can auth.
88 | def _callback_function(msg):
89 | print(msg)
90 |
91 | def test_private_websocket_connect(self):
92 | ws_private = WebSocket(
93 | testnet=True,
94 | channel_type="private",
95 | api_key="...",
96 | api_secret="...",
97 | trace_logging=True,
98 | #private_auth_expire=10
99 | )
100 |
101 | ws_private.position_stream(callback=self._callback_function)
102 | #time.sleep(10)
103 |
--------------------------------------------------------------------------------