.
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # BingX API Connector Python
4 | [![PYPI version][pypi-shield]][pypi-url]
5 | [![Python version][python-shield]][python-url]
6 | [![License: GPLv3][license-shield]][license-url]
7 |
8 |
9 |
10 | ## 📌 About The Project
11 |
12 | This is a Python package for bingX API, aims to provide a simple and easy-to-use interface for developers to access bingX API.
13 |
14 | ## 📌 Installation
15 |
16 | ```bash
17 | pip install bingX-connector # install from pypi
18 | pip install -U bingX-connector # upgrade the package to the latest version
19 | ```
20 | > Please always upgrade to the latest version to ensure the latest features and bug fixes
21 |
22 |
23 | ## 📌 Features
24 |
25 | - [x] Standard Contract
26 | - [ ] Standard Contract Web Socket
27 | - [x] Spot
28 | - [ ] Spot Web Socket
29 | - [x] Perpetual v1
30 | - [ ] Perpetual v1 Web Socket
31 | - [x] Perpetual v2
32 | - [ ] Perpetual v2 Web Socket
33 |
34 | ## 📌 Usage
35 |
36 | ### Standard Contract
37 | ```python
38 | from bingX.standard import Standard
39 |
40 | client = Standard(api_key, api_secret)
41 | ```
42 |
43 | ### Spot
44 | ```python
45 | from bingX.spot import Spot
46 |
47 | client = Spot(api_key, api_secret)
48 | ```
49 |
50 | ### Perpetual v1
51 | ```python
52 | from bingX.perpetual.v1 import Perpetual
53 |
54 | client = Perpetual(api_key, api_secret)
55 | ```
56 |
57 | ### Perpetual v2
58 | ```python
59 | from bingX.perpetual.v2 import Perpetual
60 |
61 | client = Perpetual(api_key, api_secret)
62 | ```
63 | > Note that you can not import `Perpetual v1` and `Perpetual v2` at the same time
64 |
65 | > For More Information, please look at the [bingX API document](https://bingx-api.github.io/docs/)
66 |
67 | ## [📌 Report a bug](https://github.com/Ming119/bingX-connector-python/issues)
68 |
69 | - ### Please follow the below guidelines if you would like to report a bug:
70 |
71 | 1. **Use the GitHub issue search** — check if the issue has already been reported.
72 |
73 | 2. **Check if the issue has been fixed** — try to reproduce it using the latest `main` or development branch in the repository.
74 |
75 | 3. **Isolate the problem** — create a [reduced test case](http://css-tricks.com/reduced-test-cases/) and a live example.
76 |
77 |
78 | Example:
79 |
80 | > Short and descriptive example bug report title
81 | >
82 | > A summary of the issue and the browser/OS environment in which it occurs. If
83 | > suitable, include the steps required to reproduce the bug.
84 | >
85 | > 1. This is the first step
86 | > 2. This is the second step
87 | > 3. Further steps, etc.
88 | >
89 | > `` - a link to the reduced test case
90 | >
91 | > Any other information you want to share that is relevant to the issue being
92 | > reported. This might include the lines of code that you have identified as
93 | > causing the bug, and potential solutions (and your opinions on their
94 | > merits).
95 |
96 | ## [📌 Contribute](https://github.com/Ming119/bingX-connector-python/pulls)
97 | - ### Follow this process if you'd like your work considered for inclusion in the project
98 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, and configure the remotes:
99 |
100 | ```bash
101 | # Clone your fork of the repo into the current directory
102 | git clone https://github.com//bingX-connector-python.git
103 | # Navigate to the newly cloned directory
104 | cd bingX-connector-python
105 | # Assign the original repo to a remote called "upstream"
106 | git remote add upstream https://github.com/Ming119/bingX-connector-python
107 | ```
108 |
109 | 2. If you cloned a while ago, get the latest changes from upstream:
110 |
111 | ```bash
112 | git checkout
113 | git pull upstream
114 | ```
115 |
116 | 3. Create a new topic branch (off the main project development branch) to contain your feature, change, or fix:
117 |
118 | ```bash
119 | git checkout -b
120 | ```
121 |
122 | 4. Locally merge (or rebase) the upstream development branch into your topic branch:
123 |
124 | ```bash
125 | git pull [--rebase] upstream
126 | ```
127 |
128 | 5. Push your topic branch up to your fork:
129 |
130 | ```bash
131 | git push origin
132 | ```
133 | 6. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title and description.
134 |
135 | > **IMPORTANT**: By submitting a patch, you agree to allow us to license your work under the same license as that used by `bingX-connector-python`
136 |
137 | [pypi-shield]: https://img.shields.io/pypi/v/bingX-connector
138 | [pypi-url]: https://pypi.org/project/bingX-connector/
139 | [python-shield]: https://img.shields.io/pypi/pyversions/bingX-connector
140 | [python-url]: https://www.python.org/downloads/
141 | [license-shield]: https://img.shields.io/github/license/Ming119/bingX-connector-python
142 | [license-url]: https://www.gnu.org/licenses/gpl-3.0.en.html
--------------------------------------------------------------------------------
/bingX/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.__init__
3 | '''
4 |
5 | from bingX.api import API
6 | from bingX.error import ServerError, ClientError
7 |
--------------------------------------------------------------------------------
/bingX/api.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.api
3 | '''
4 |
5 | import time
6 | import requests
7 | import hmac
8 | import urllib
9 | import base64
10 |
11 | class API(object):
12 | def __init__(self, api_key: str, api_secret: str, base_url: str, api_type: str=None):
13 | self.api_key = api_key
14 | self.api_secret = api_secret
15 | self.base_url = base_url
16 | self.api_type = api_type
17 |
18 | self.headers = {
19 | 'X-BX-APIKEY': self.api_key,
20 | }
21 |
22 | def _handle_params(self, params, path=None, method=None):
23 | params = params or {}
24 | params['timestamp'] = round(time.time() * 1000)
25 | if self.api_type == 'perpetual_v1':
26 | params['apiKey'] = self.api_key
27 | params = '&'.join(f'{k}={params[k]}' for k in sorted(params) if params[k])
28 | else:
29 | params = '&'.join(f'{k}={v}' for k, v in params.items() if v)
30 | params += self._signature(params, path, method)
31 | return params
32 |
33 | def _signature(self, params, path=None, method=None):
34 | if self.api_type != 'perpetual_v1':
35 | sign = hmac.new(self.api_secret.encode(), params.encode(), 'sha256')
36 | return f'&signature={sign.hexdigest()}'
37 | originString = f'{method}{path}{params}'
38 | sign = hmac.new(self.api_secret.encode(), originString.encode(), 'sha256')
39 | return f'&sign={urllib.parse.quote(base64.b64encode(sign.digest()))}'
40 |
41 | def _request(self, method, path, params=None, headers=None):
42 | url = f'{self.base_url}{path}?{self._handle_params(params, path, method)}'
43 | if method == 'GET':
44 | r = requests.get(url, headers=headers or self.headers)
45 | elif method == 'POST':
46 | r = requests.post(url, headers=headers or self.headers)
47 | elif method == 'PUT':
48 | r = requests.put(url, headers=headers or self.headers)
49 | elif method == 'DELETE':
50 | r = requests.delete(url, headers=headers or self.headers)
51 | else:
52 | raise Exception('Invalid method: %s' % method)
53 | return r.json()
54 |
55 | def get(self, path, params=None):
56 | return self._request('GET', path, params=params)
57 |
58 | def post(self, path, params=None):
59 | return self._request('POST', path, params=params)
60 |
61 | def put(self, path, params=None):
62 | return self._request('PUT', path, params=params)
63 |
64 | def delete(self, path, params=None):
65 | return self._request('DELETE', path, params=params)
--------------------------------------------------------------------------------
/bingX/error.py:
--------------------------------------------------------------------------------
1 |
2 | class ServerError(BaseException):
3 | def __init__(self, status_code, error_msg):
4 | self.status_code = status_code
5 | self.error_msg = error_msg
6 |
7 |
8 | class ClientError(BaseException):
9 | def __init__(self, error_code: int, error_msg: str):
10 | self.error_code = error_code
11 | self.error_msg = error_msg
12 |
13 | self.error_msg_map = {
14 | 100001: 'Signature authentication failed',
15 | 100202: 'Insufficient balance',
16 | 100400: 'Invalid parameter',
17 | 100440: 'Order price deviates greatly from the market price',
18 | 100500: 'Internal server error',
19 | 100503: 'Server busy',
20 | }
21 |
22 | def __str__(self):
23 | return f'{self.error_code} - {self.error_msg or self.error_msg_map[self.error_code]}'
24 |
25 |
--------------------------------------------------------------------------------
/bingX/perpetual/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ming119/bingX-connector-python/aae12ee3f585eced644059196eab9bf2c31e3468/bingX/perpetual/__init__.py
--------------------------------------------------------------------------------
/bingX/perpetual/v1/Perpetual.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.perpetual.v1.Perpetual
3 | '''
4 |
5 | from bingX import API
6 |
7 | class Perpetual(API):
8 | def __init__(self,
9 | api_key: str,
10 | api_secret: str,
11 | ) -> object:
12 | super().__init__(
13 | api_key = api_key,
14 | api_secret = api_secret,
15 | base_url = "https://api-swap-rest.bingbon.pro",
16 | api_type = "perpetual_v1"
17 | )
18 |
19 | def server_time(self) -> dict:
20 | ''' Get Server Time
21 | https://bingx-api.github.io/docs/swap/base-info.html#get-server-time
22 | '''
23 | return self.get("/api/v1/common/server/time")
24 |
25 | # ========== MARKET INTERFACE ==========
26 | from bingX.perpetual.v1.market import (
27 | contracts,
28 | latest_price,
29 | market_depth,
30 | latest_trade,
31 | current_funding_rate,
32 | funding_rate_history,
33 | kline_data,
34 | kline_data_history,
35 | open_positions,
36 | ticker,
37 | )
38 |
39 | # ========== ACCOUNT INTERFACE ==========
40 | from bingX.perpetual.v1.account import (
41 | balance,
42 | positions,
43 | )
44 |
45 | # ========== TRADE INTERFACE ==========
46 | from bingX.perpetual.v1.trade import (
47 | place_order,
48 | close_position,
49 | close_all_positions,
50 | cancel_order,
51 | cancel_orders,
52 | cancel_all_orders,
53 | unfilled_order_acquisition,
54 | order_details,
55 | margin_mode,
56 | switch_margin_mode,
57 | leverage,
58 | switch_leverage,
59 | force_orders,
60 | orders_history,
61 | place_a_stop_order,
62 | cancel_stop_order,
63 | stop_orders,
64 | stop_orders_history,
65 | )
66 |
67 | # ========== OTHER INTERFACE ==========
68 | from bingX.perpetual.v1.other import (
69 | generate_listen_key,
70 | extend_listen_key,
71 | delete_listen_key,
72 | )
73 |
--------------------------------------------------------------------------------
/bingX/perpetual/v1/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.perpetual.v1.__init__
3 | '''
4 |
5 | from bingX.perpetual.v1.Perpetual import Perpetual
6 |
--------------------------------------------------------------------------------
/bingX/perpetual/v1/account.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.perpetual.v1.account
3 | '''
4 |
5 | from bingX import ClientError
6 |
7 | def balance(self,
8 | currency: str,
9 | ) -> dict:
10 | ''' Get Perpetual Swap Account Asset Information
11 | POST /api/v1/user/getBalance
12 |
13 | https://bingx-api.github.io/docs/swap/account-api.html#_1-get-perpetual-swap-account-asset-information
14 | '''
15 | res = self.post("/api/v1/user/getBalance", params={
16 | "currency": currency,
17 | })
18 |
19 | if 'code' in res and res['code']:
20 | raise ClientError(res['code'], res['msg'])
21 | return res['data']
22 |
23 | def positions(self,
24 | symbol: str,
25 | ) -> dict:
26 | ''' Perpetual Swap Positions
27 | POST /api/v1/user/getPositions
28 |
29 | https://bingx-api.github.io/docs/swap/account-api.html#_2-perpetual-swap-positions
30 | '''
31 | res = self.post("/api/v1/user/getPositions", params={
32 | "symbol": symbol,
33 | })
34 |
35 | if 'code' in res and res['code']:
36 | raise ClientError(res['code'], res['msg'])
37 | return res['data']
38 |
--------------------------------------------------------------------------------
/bingX/perpetual/v1/market.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.perpetual.v1.market
3 | '''
4 |
5 | from bingX import ClientError
6 |
7 | def contracts(self) -> dict:
8 | ''' Contract Information
9 | GET /api/v1/market/getAllContracts
10 |
11 | https://bingx-api.github.io/docs/swap/market-api.html#_1-contract-information
12 | '''
13 | res = self.get("/api/v1/market/getAllContracts")
14 |
15 | if 'code' in res and res['code']:
16 | raise ClientError(res['code'], res['msg'])
17 | return res['data']
18 |
19 | def latest_price(self,
20 | symbol: str,
21 | ) -> dict:
22 | ''' Get Latest Price of a Trading Pair
23 | GET /api/v1/market/getLatestPrice
24 |
25 | https://bingx-api.github.io/docs/swap/market-api.html#_2-get-latest-price-of-a-trading-pair
26 | '''
27 | res = self.get("/api/v1/market/getLatestPrice", params={
28 | "symbol": symbol,
29 | })
30 |
31 | if 'code' in res and res['code']:
32 | raise ClientError(res['code'], res['msg'])
33 | return res['data']
34 |
35 | def market_depth(self,
36 | symbol: str,
37 | level: int = None,
38 | ) -> dict:
39 | ''' Get Market Depth
40 | GET /api/v1/market/getMarketDepth
41 |
42 | https://bingx-api.github.io/docs/swap/market-api.html#_3-get-market-depth
43 | '''
44 | res = self.get("/api/v1/market/getMarketDepth", params={
45 | "symbol": symbol,
46 | "level": level,
47 | })
48 |
49 | if 'code' in res and res['code']:
50 | raise ClientError(res['code'], res['msg'])
51 | return res['data']
52 |
53 | def latest_trade(self,
54 | symbol: str,
55 | ) -> dict:
56 | ''' The latest Trade of a Trading Pair
57 | GET /api/v1/market/getMarketTrades
58 |
59 | https://bingx-api.github.io/docs/swap/market-api.html#_4-the-latest-trade-of-a-trading-pair
60 | '''
61 | res = self.get("/api/v1/market/getMarketTrades", params={
62 | "symbol": symbol,
63 | })
64 |
65 | if 'code' in res and res['code']:
66 | raise ClientError(res['code'], res['msg'])
67 | return res['data']
68 |
69 | def current_funding_rate(self,
70 | symbol: str,
71 | ) -> dict:
72 | ''' Current Funding Rate
73 | GET /api/v1/market/getLatestFunding
74 |
75 | https://bingx-api.github.io/docs/swap/market-api.html#_5-current-funding-rate
76 | '''
77 | res = self.get("/api/v1/market/getLatestFunding", params={
78 | "symbol": symbol,
79 | })
80 |
81 | if 'code' in res and res['code']:
82 | raise ClientError(res['code'], res['msg'])
83 | return res['data']
84 |
85 | def funding_rate_history(self,
86 | symbol: str,
87 | ) -> dict:
88 | ''' Funding Rate History
89 | GET /api/v1/market/getHistoryFunding
90 |
91 | https://bingx-api.github.io/docs/swap/market-api.html#_6-funding-rate-history
92 | '''
93 | res = self.get("/api/v1/market/getHistoryFunding", params={
94 | "symbol": symbol,
95 | })
96 |
97 | if 'code' in res and res['code']:
98 | raise ClientError(res['code'], res['msg'])
99 | return res['data']
100 |
101 | def kline_data(self,
102 | symbol: str,
103 | klineType: str,
104 | ) -> dict:
105 | ''' Get K-Line Data
106 | GET /api/v1/market/getLatestKline
107 |
108 | https://bingx-api.github.io/docs/swap/market-api.html#_7-get-k-line-data
109 | '''
110 | res = self.get("/api/v1/market/getLatestKline", params={
111 | "symbol": symbol,
112 | "klineType": klineType,
113 | })
114 |
115 | if 'code' in res and res['code']:
116 | raise ClientError(res['code'], res['msg'])
117 | return res['data']
118 |
119 | def kline_data_history(self,
120 | symbol: str,
121 | klineType: str,
122 | startTs: int,
123 | endTs: int,
124 | ) -> dict:
125 | ''' K-Line Data History
126 | GET /api/v1/market/getHistoryKlines
127 |
128 | https://bingx-api.github.io/docs/swap/market-api.html#_8-k-line-data-history
129 | '''
130 | res = self.get("/api/v1/market/getHistoryKlines", params={
131 | "symbol": symbol,
132 | "klineType": klineType,
133 | "startTs": startTs,
134 | "endTs": endTs,
135 | })
136 |
137 | if 'code' in res and res['code']:
138 | raise ClientError(res['code'], res['msg'])
139 | return res['data']
140 |
141 | def open_positions(self,
142 | symbol: str,
143 | ) -> dict:
144 | ''' Get Swap Open Positions
145 | GET /api/v1/market/getOpenPositions
146 |
147 | https://bingx-api.github.io/docs/swap/market-api.html#_9-get-swap-open-positions
148 | '''
149 | res = self.get("/api/v1/market/getOpenPositions", params={
150 | "symbol": symbol,
151 | })
152 |
153 | if 'code' in res and res['code']:
154 | raise ClientError(res['code'], res['msg'])
155 | return res['data']
156 |
157 | def ticker(self,
158 | symbol: str = None,
159 | ) -> dict:
160 | ''' Get Ticker
161 | GET /api/v1/market/getTicker
162 |
163 | https://bingx-api.github.io/docs/swap/market-api.html#_10-get-ticker
164 | '''
165 | res = self.get("/api/v1/market/getTicker", params={
166 | "symbol": symbol,
167 | })
168 |
169 | if 'code' in res and res['code']:
170 | raise ClientError(res['code'], res['msg'])
171 | return res['data']
172 |
--------------------------------------------------------------------------------
/bingX/perpetual/v1/other.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.perpetual.v1.other
3 | '''
4 |
5 | from bingX import ClientError
6 |
7 | def generate_listen_key(self) -> dict:
8 | ''' Generate Listen Key
9 | POST /api/v1/user/auth/userDataStream
10 |
11 | https://bingx-api.github.io/docs/swap/other-interface.html#generate-listen-key
12 | '''
13 | res = self.post("/api/v1/user/auth/userDataStream")
14 |
15 | if 'code' in res and res['code']:
16 | raise ClientError(res['code'], res['msg'])
17 | return res
18 |
19 | def extend_listen_key(self,
20 | listenKey: str,
21 | ) -> dict:
22 | ''' Extend Listen Key
23 | PUT /api/v1/user/auth/userDataStream
24 |
25 | https://bingx-api.github.io/docs/swap/other-interface.html#extend-listen-key-validity-period
26 | '''
27 | res = self.put("/api/v1/user/auth/userDataStream", params={
28 | "listenKey": listenKey,
29 | })
30 |
31 | if 'code' in res and res['code']:
32 | raise ClientError(res['code'], res['msg'])
33 | return res
34 |
35 | def delete_listen_key(self,
36 | listenKey: str,
37 | ) -> dict:
38 | ''' Delete Listen Key
39 | DELETE /api/v1/user/auth/userDataStream
40 |
41 | https://bingx-api.github.io/docs/swap/other-interface.html#delete-listen-key
42 | '''
43 | res = self.delete("/api/v1/user/auth/userDataStream", params={
44 | "listenKey": listenKey,
45 | })
46 |
47 | if 'code' in res and res['code']:
48 | raise ClientError(res['code'], res['msg'])
49 | return res
50 |
--------------------------------------------------------------------------------
/bingX/perpetual/v1/trade.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.perpetual.v1.trade
3 | '''
4 |
5 | from typing import List
6 | from bingX import ClientError
7 |
8 | def place_order(self,
9 | symbol: str,
10 | side: str,
11 | entrustPrice: float,
12 | entrustVolume: float,
13 | tradeType: str,
14 | action: str,
15 | takerProfitPrice: float = None,
16 | stopLossPrice: float = None,
17 | ) -> dict:
18 | ''' Place a New Order
19 | POST /api/v1/user/trade
20 |
21 | https://bingx-api.github.io/docs/swap/trade-api.html#_1-place-a-new-order
22 | '''
23 | res = self.post("/api/v1/user/trade", params={
24 | "symbol": symbol,
25 | "side": side,
26 | "entrustPrice": entrustPrice,
27 | "entrustVolume": entrustVolume,
28 | "tradeType": tradeType,
29 | "action": action,
30 | "takerProfitPrice": takerProfitPrice,
31 | "stopLossPrice": stopLossPrice,
32 | })
33 |
34 | if 'code' in res and res['code']:
35 | raise ClientError(res['code'], res['msg'])
36 | return res['data']
37 |
38 | def close_position(self,
39 | symbol: str,
40 | positionId: str,
41 | ) -> dict:
42 | ''' One-Click Close Position
43 | POST /api/v1/user/oneClickClosePosition
44 |
45 | https://bingx-api.github.io/docs/swap/trade-api.html#_2-one-click-close-position
46 | '''
47 | res = self.post("/api/v1/user/oneClickClosePosition", params={
48 | "symbol": symbol,
49 | "positionId": positionId,
50 | })
51 |
52 | if 'code' in res and res['code']:
53 | raise ClientError(res['code'], res['msg'])
54 | return res['data']
55 |
56 | def close_all_positions(self) -> dict:
57 | ''' One-Click Close All Positions
58 | POST /api/v1/user/oneClickCloseAllPositions
59 |
60 | https://bingx-api.github.io/docs/swap/trade-api.html#_3-one-click-close-all-positions
61 | '''
62 | res = self.post("/api/v1/user/oneClickCloseAllPositions")
63 |
64 | if 'code' in res and res['code']:
65 | raise ClientError(res['code'], res['msg'])
66 | return res['data']
67 |
68 | def cancel_order(self,
69 | symbol: str,
70 | orderId: str,
71 | ) -> dict:
72 | ''' Cancel an Order
73 | https://bingx-api.github.io/docs/swap/trade-api.html#_4-cancel-an-order
74 | '''
75 | res = self.post("/api/v1/user/cancelOrder", params={
76 | "symbol": symbol,
77 | "orderId": orderId,
78 | })
79 |
80 | if 'code' in res and res['code']:
81 | raise ClientError(res['code'], res['msg'])
82 | return res['data']
83 |
84 | def cancel_orders(self,
85 | symbol: str,
86 | orderIds: List[str],
87 | ) -> dict:
88 | ''' Cancel Multiple Orders
89 | POST /api/v1/user/batchCancelOrders
90 |
91 | https://bingx-api.github.io/docs/swap/trade-api.html#_5-cancel-a-batch-of-orders
92 | '''
93 | res = self.post("/api/v1/user/batchCancelOrders", params={
94 | "symbol": symbol,
95 | "oids": orderIds,
96 | })
97 |
98 | if 'code' in res and res['code']:
99 | raise ClientError(res['code'], res['msg'])
100 | return res['data']
101 |
102 | def cancel_all_orders(self) -> dict:
103 | ''' Cancel All Orders
104 | POST /api/v1/user/cancelAll
105 |
106 | https://bingx-api.github.io/docs/swap/trade-api.html#_6-cancel-all-orders
107 | '''
108 | res = self.post("/api/v1/user/cancelAll")
109 |
110 | if 'code' in res and res['code']:
111 | raise ClientError(res['code'], res['msg'])
112 | return res['data']
113 |
114 | def unfilled_order_acquisition(self,
115 | symbol: str,
116 | ) -> dict:
117 | ''' Unfilled Order Acquisition
118 | POST /api/v1/user/pendingOrders
119 |
120 | https://bingx-api.github.io/docs/swap/trade-api.html#_7-unfilled-order-acquisition
121 | '''
122 | res = self.post("/api/v1/user/pendingOrders", params={
123 | "symbol": symbol,
124 | })
125 |
126 | if 'code' in res and res['code']:
127 | raise ClientError(res['code'], res['msg'])
128 | return res['data']
129 |
130 | def order_details(self,
131 | symbol: str,
132 | orderId: str,
133 | ) -> dict:
134 | ''' Query Order Details
135 | POST /api/v1/user/orderInfo
136 |
137 | https://bingx-api.github.io/docs/swap/trade-api.html#_8-query-order-details
138 | '''
139 | res = self.post("/api/v1/user/orderInfo", params={
140 | "symbol": symbol,
141 | "orderId": orderId,
142 | })
143 |
144 | if 'code' in res and res['code']:
145 | raise ClientError(res['code'], res['msg'])
146 | return res['data']
147 |
148 | def margin_mode(self,
149 | symbol: str,
150 | ) -> dict:
151 | ''' Query Margin Mode
152 | POST /api/v1/user/getMarginMode
153 |
154 | https://bingx-api.github.io/docs/swap/trade-api.html#_9-query-margin-mode
155 | '''
156 | res = self.post("/api/v1/user/getMarginMode", params={
157 | "symbol": symbol,
158 | })
159 |
160 | if 'code' in res and res['code']:
161 | raise ClientError(res['code'], res['msg'])
162 | return res['data']
163 |
164 | def switch_margin_mode(self,
165 | symbol: str,
166 | marginMode: str,
167 | ) -> dict:
168 | ''' Switch Margin Mode
169 | POST /api/v1/user/setMarginMode
170 |
171 | https://bingx-api.github.io/docs/swap/trade-api.html#_10-switch-margin-mode
172 | '''
173 | res = self.post("/api/v1/user/setMarginMode", params={
174 | "symbol": symbol,
175 | "marginMode": marginMode,
176 | })
177 |
178 | if 'code' in res and res['code']:
179 | raise ClientError(res['code'], res['msg'])
180 | return res['data']
181 |
182 | def leverage(self,
183 | symbol: str,
184 | ) -> dict:
185 | ''' Query Leverage
186 | POST /api/v1/user/getLeverage
187 |
188 | https://bingx-api.github.io/docs/swap/trade-api.html#_11-query-leverage
189 | '''
190 | res = self.post("/api/v1/user/getLeverage", params={
191 | "symbol": symbol,
192 | })
193 |
194 | if 'code' in res and res['code']:
195 | raise ClientError(res['code'], res['msg'])
196 | return res['data']
197 |
198 | def switch_leverage(self,
199 | symbol: str,
200 | side: str,
201 | leverage: int,
202 | ) -> dict:
203 | ''' Switch Leverage
204 | POST /api/v1/user/setLeverage
205 |
206 | https://bingx-api.github.io/docs/swap/trade-api.html#_12-switch-leverage
207 | '''
208 | res = self.post("/api/v1/user/setLeverage", params={
209 | "symbol": symbol,
210 | "side": side,
211 | "leverage": leverage,
212 | })
213 |
214 | if 'code' in res and res['code']:
215 | raise ClientError(res['code'], res['msg'])
216 | return res
217 |
218 | def force_orders(self,
219 | symbol: str,
220 | autoCloseType: str,
221 | lastOrderId: int,
222 | length: int,
223 | ) -> dict:
224 | ''' User Force Orders
225 | POST /api/v1/user/forceOrders
226 |
227 | https://bingx-api.github.io/docs/swap/trade-api.html#_13-user-s-force-orders
228 | '''
229 | res = self.post("/api/v1/user/forceOrders", params={
230 | "symbol": symbol,
231 | "autoCloseType": autoCloseType,
232 | "lastOrderId": lastOrderId,
233 | "length": length,
234 | })
235 |
236 | if 'code' in res and res['code']:
237 | raise ClientError(res['code'], res['msg'])
238 | return res['data']
239 |
240 | def orders_history(self,
241 | symbol: str,
242 | lastOrderId: int,
243 | length: int,
244 | ) -> dict:
245 | ''' User History Orders
246 | POST /api/v1/user/historyOrders
247 |
248 | https://bingx-api.github.io/docs/swap/trade-api.html#_14-user-s-history-orders
249 | '''
250 | res = self.post("/api/v1/user/historyOrders", params={
251 | "symbol": symbol,
252 | "lastOrderId": lastOrderId,
253 | "length": length,
254 | })
255 |
256 | if 'code' in res and res['code']:
257 | raise ClientError(res['code'], res['msg'])
258 | return res['data']
259 |
260 | def place_a_stop_order(self,
261 | positionId: str,
262 | entrustVolume: float,
263 | orderId: str = None,
264 | stopLossPrice: float = None,
265 | takeProfitPrice: float = None,
266 | ) -> dict:
267 | ''' Place a Stop Order
268 | POST /api/v1/user/stopOrder
269 |
270 | https://bingx-api.github.io/docs/swap/trade-api.html#_15-place-a-stop-order
271 | '''
272 | res = self.post("/api/v1/user/stopOrder", params={
273 | "positionId": positionId,
274 | "orderId": orderId,
275 | "stopLossPrice": stopLossPrice,
276 | "takeProfitPrice": takeProfitPrice,
277 | "entrustVolume": entrustVolume,
278 | })
279 |
280 | if 'code' in res and res['code']:
281 | raise ClientError(res['code'], res['msg'])
282 | return res['data']
283 |
284 | def cancel_stop_order(self,
285 | orderId: str,
286 | ) -> dict:
287 | ''' Cancel a Stop Order
288 | POST /api/v1/user/cancelStopOrder
289 |
290 | https://bingx-api.github.io/docs/swap/trade-api.html#_16-cancel-stop-order
291 | '''
292 | res = self.post("/api/v1/user/cancelStopOrder", params={
293 | "orderId": orderId,
294 | })
295 |
296 | if 'code' in res and res['code']:
297 | raise ClientError(res['code'], res['msg'])
298 | return res['data']
299 |
300 | def stop_orders(self,
301 | symbol: str,
302 | ) -> dict:
303 | ''' Query Stop Orders
304 | POST /api/v1/user/pendingStopOrders
305 |
306 | https://bingx-api.github.io/docs/swap/trade-api.html#_17-query-stop-orders
307 | '''
308 | res = self.post("/api/v1/user/pendingStopOrders", params={
309 | "symbol": symbol,
310 | })
311 |
312 | if 'code' in res and res['code']:
313 | raise ClientError(res['code'], res['msg'])
314 | return res['data']
315 |
316 | def stop_orders_history(self,
317 | symbol: str,
318 | lastOrderId: int,
319 | length: int,
320 | ) -> dict:
321 | ''' Stop Orders History
322 | POST /api/v1/user/historyStopOrders
323 |
324 | https://bingx-api.github.io/docs/swap/trade-api.html#_18-query-history-stop-orders
325 | '''
326 | res = self.post("/api/v1/user/historyStopOrders", params={
327 | "symbol": symbol,
328 | "lastOrderId": lastOrderId,
329 | "length": length,
330 | })
331 |
332 | if 'code' in res and res['code']:
333 | raise ClientError(res['code'], res['msg'])
334 | return res['data']
335 |
--------------------------------------------------------------------------------
/bingX/perpetual/v2/Perpetual.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.perpetual.v2.Perpetual
3 | '''
4 |
5 | from bingX import API
6 |
7 |
8 | class Perpetual(API):
9 | def __init__(self,
10 | api_key: str,
11 | api_secret: str,
12 | ) -> object:
13 | super().__init__(
14 | api_key=api_key,
15 | api_secret=api_secret,
16 | base_url="https://open-api.bingx.com",
17 | )
18 |
19 | def server_time(self) -> dict:
20 | ''' Get Server Time
21 | https://bingx-api.github.io/docs/swapV2/base-info.html#get-server-time
22 | '''
23 | return self.get("/openApi/v2/common/server/time")
24 |
25 | # ========== MARKET INTERFACE ==========
26 | from bingX.perpetual.v2.market import (
27 | contracts,
28 | latest_price,
29 | market_depth,
30 | latest_trade,
31 | current_funding_rate,
32 | funding_rate_history,
33 | kline,
34 | get_open_positions,
35 | ticker,
36 | )
37 |
38 | # ========== ACCOUNT INTERFACE ==========
39 | from bingX.perpetual.v2.account import (
40 | balance,
41 | positions,
42 | income,
43 | )
44 |
45 | # ========== TRADE INTERFACE ==========
46 | from bingX.perpetual.v2.trade import (
47 | trade_order,
48 | bulk_order,
49 | close_all_positions,
50 | cancel_order,
51 | cancel_orders,
52 | cancel_all_orders,
53 | current_orders,
54 | order,
55 | margin_mode,
56 | switch_margin_mode,
57 | leverage,
58 | switch_leverage,
59 | force_orders,
60 | orders_history,
61 | adjust_isolated_margin,
62 | )
63 | # ========== LISTEN KEY ==========
64 | from bingX.perpetual.v2.other import (
65 | generate_listen_key,
66 | extend_listen_key,
67 | delete_listen_key
68 | )
69 |
--------------------------------------------------------------------------------
/bingX/perpetual/v2/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.perpetual.v2.__init__
3 | '''
4 |
5 | from bingX.perpetual.v2.Perpetual import Perpetual
6 |
--------------------------------------------------------------------------------
/bingX/perpetual/v2/account.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.perpetual.v2.account
3 | '''
4 |
5 | from bingX import ClientError
6 |
7 | def balance(self,
8 | recvWindow: int = None,
9 | ) -> dict:
10 | ''' Get Perpetual Swap Account Asset Information
11 | GET /openApi/swap/v2/user/balance
12 |
13 | https://bingx-api.github.io/docs/swapV2/account-api.html#_1-get-perpetual-swap-account-asset-information
14 | '''
15 | res = self.get("/openApi/swap/v2/user/balance", params={
16 | "recvWindow": recvWindow,
17 | })
18 |
19 | if 'code' in res and res['code']:
20 | raise ClientError(res['code'], res['msg'])
21 | return res['data']
22 |
23 | def positions(self,
24 | symbol: str,
25 | recvWindow: int = None,
26 | ) -> dict:
27 | ''' Perpetual Swap Positions
28 | GET /openApi/swap/v2/user/positions
29 |
30 | https://bingx-api.github.io/docs/swap/account-api.html#_2-perpetual-swap-positions
31 | '''
32 | res = self.get("/openApi/swap/v2/user/positions", params={
33 | "symbol": symbol,
34 | "recvWindow": recvWindow,
35 | })
36 |
37 | if 'code' in res and res['code']:
38 | raise ClientError(res['code'], res['msg'])
39 | return res['data']
40 |
41 | def income(self,
42 | symbol: str = None,
43 | incomeType: str = None,
44 | startTime: int = None,
45 | endTime: int = None,
46 | limit: int = None,
47 | recvWindow: int = None,
48 | ) -> dict:
49 | ''' Perpetual Swap Account PnL
50 | GET /openApi/swap/v2/user/income
51 |
52 | https://bingx-api.github.io/docs/swapV2/account-api.html#_3-get-account-profit-and-loss-fund-flow
53 | '''
54 | res = self.get("/openApi/swap/v2/user/income", params={
55 | "symbol": symbol,
56 | "incomeType": incomeType,
57 | "startTime": startTime,
58 | "endTime": endTime,
59 | "limit": limit,
60 | "recvWindow": recvWindow,
61 | })
62 |
63 | if 'code' in res and res['code']:
64 | raise ClientError(res['code'], res['msg'])
65 | return res['data']
--------------------------------------------------------------------------------
/bingX/perpetual/v2/market.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.perpetual.v2.market
3 | '''
4 |
5 | from bingX import ClientError
6 |
7 | def contracts(self) -> dict:
8 | ''' Contract Information
9 | GET /openApi/swap/v2/quote/contracts
10 |
11 | https://bingx-api.github.io/docs/swapV2/market-api.html#_1-contract-information
12 | '''
13 | res = self.get("/openApi/swap/v2/quote/contracts")
14 |
15 | if 'code' in res and res['code']:
16 | raise ClientError(res['code'], res['msg'])
17 | return res['data']
18 |
19 | def latest_price(self,
20 | symbol: str = None,
21 | ) -> dict:
22 | ''' Get Latest Price of a Trading Pair
23 | GET /openApi/swap/v2/quote/price
24 |
25 | https://bingx-api.github.io/docs/swapV2/market-api.html#_2-get-latest-price-of-a-trading-pair
26 | '''
27 | res = self.get("/openApi/swap/v2/quote/price", params={
28 | "symbol": symbol,
29 | })
30 |
31 | if 'code' in res and res['code']:
32 | raise ClientError(res['code'], res['msg'])
33 | return res['data']
34 |
35 | def market_depth(self,
36 | symbol: str,
37 | limit: int = None,
38 | ) -> dict:
39 | ''' Get Market Depth
40 | GET /openApi/swap/v2/quote/depth
41 |
42 | https://bingx-api.github.io/docs/swapV2/market-api.html#_3-get-market-depth
43 | '''
44 | res = self.get("/openApi/swap/v2/quote/depth", params={
45 | "symbol": symbol,
46 | "limit": limit,
47 | })
48 |
49 | if 'code' in res and res['code']:
50 | raise ClientError(res['code'], res['msg'])
51 | return res['data']
52 |
53 | def latest_trade(self,
54 | symbol: str,
55 | limit: int = None,
56 | ) -> dict:
57 | ''' The latest Trade of a Trading Pair
58 | GET /openApi/swap/v2/quote/trades
59 |
60 | https://bingx-api.github.io/docs/swapV2/market-api.html#_4-the-latest-trade-of-a-trading-pair
61 | '''
62 | res = self.get("/openApi/swap/v2/quote/trades", params={
63 | "symbol": symbol,
64 | "limit": limit,
65 | })
66 |
67 | if 'code' in res and res['code']:
68 | raise ClientError(res['code'], res['msg'])
69 | return res['data']
70 |
71 | def current_funding_rate(self,
72 | symbol: str = None,
73 | ) -> dict:
74 | ''' Current Funding Rate
75 | GET /openApi/swap/v2/quote/premiumIndex
76 |
77 | https://bingx-api.github.io/docs/swapV2/market-api.html#_5-current-funding-rate
78 | '''
79 | res = self.get("/openApi/swap/v2/quote/premiumIndex", params={
80 | "symbol": symbol,
81 | })
82 |
83 | if 'code' in res and res['code']:
84 | raise ClientError(res['code'], res['msg'])
85 | return res['data']
86 |
87 | def funding_rate_history(self,
88 | symbol: str,
89 | startTime: int = None,
90 | endTime: int = None,
91 | limit: int = None,
92 | ) -> dict:
93 | ''' Funding Rate History
94 | GET /openApi/swap/v2/quote/fundingRate
95 |
96 | https://bingx-api.github.io/docs/swapV2/market-api.html#_6-funding-rate-history
97 | '''
98 | res = self.get("/openApi/swap/v2/quote/fundingRate", params={
99 | "symbol": symbol,
100 | "startTime": startTime,
101 | "endTime": endTime,
102 | "limit": limit,
103 | })
104 |
105 | if 'code' in res and res['code']:
106 | raise ClientError(res['code'], res['msg'])
107 | return res['data']
108 |
109 | def kline(self,
110 | symbol: str,
111 | interval: str,
112 | startTime: int = None,
113 | endTime: int = None,
114 | limit: int = None,
115 | ) -> dict:
116 | ''' K-Line Data
117 | GET /openApi/swap/v2/quote/klines
118 |
119 | https://bingx-api.github.io/docs/swapV2/market-api.html#_7-k-line-data
120 | '''
121 | res = self.get("/openApi/swap/v2/quote/klines", params={
122 | "symbol": symbol,
123 | "interval": interval,
124 | "startTime": startTime,
125 | "endTime": endTime,
126 | "limit": limit,
127 | })
128 |
129 | if 'code' in res and res['code']:
130 | raise ClientError(res['code'], res['msg'])
131 | return res['data']
132 |
133 | def get_open_positions(self,
134 | symbol: str,
135 | ) -> dict:
136 | ''' Get Swap Open Positions
137 | GET /openApi/swap/v2/quote/openInterest
138 |
139 | https://bingx-api.github.io/docs/swapV2/market-api.html#_8-get-swap-open-positions
140 | '''
141 | res = self.get("/openApi/swap/v2/quote/openInterest", params={
142 | "symbol": symbol,
143 | })
144 |
145 | if 'code' in res and res['code']:
146 | raise ClientError(res['code'], res['msg'])
147 | return res['data']
148 |
149 | def ticker(self,
150 | symbol: str = None,
151 | ) -> dict:
152 | ''' Get Ticker
153 | GET /openApi/swap/v2/quote/ticker
154 |
155 | https://bingx-api.github.io/docs/swapV2/market-api.html#_9-get-ticker
156 | '''
157 | res = self.get("/openApi/swap/v2/quote/ticker", params={
158 | "symbol": symbol,
159 | })
160 |
161 | if 'code' in res and res['code']:
162 | raise ClientError(res['code'], res['msg'])
163 | return res['data']
164 |
--------------------------------------------------------------------------------
/bingX/perpetual/v2/other.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | base_url = "https://open-api.bingx.com"
4 |
5 |
6 | def generate_listen_key(api_key):
7 |
8 | endpoint = "/openApi/user/auth/userDataStream"
9 |
10 | url = base_url + endpoint
11 |
12 | headers = {
13 | "X-BX-APIKEY": api_key
14 | }
15 |
16 | response = requests.post(url, headers=headers)
17 |
18 | if response.status_code == 200:
19 | data = response.json()
20 | listen_key = data.get('listenKey')
21 | return listen_key
22 | else:
23 | return "Failed to get listenKey."
24 |
25 |
26 | def delete_listen_key(listen_key):
27 |
28 | endpoint = "/openApi/user/auth/userDataStream?listenKey=" + listen_key
29 |
30 | url = base_url + endpoint
31 |
32 | response = requests.delete(url)
33 |
34 | if response.status_code == 200:
35 | return "ListenKey extended successfully."
36 | else:
37 | if response.status_code == 204:
38 | return "Not Content"
39 | elif response.status_code == 404:
40 | return "Not Find ListenKey"
41 |
42 |
43 | def extend_listen_key(listen_key):
44 |
45 | endpoint = "/openApi/user/auth/userDataStream?listenKey=" + listen_key
46 |
47 | url = base_url + endpoint
48 |
49 | response = requests.put(url)
50 |
51 | if response.status_code == 200:
52 | return "ListenKey Deleted successfully."
53 | else:
54 | if response.status_code == 204:
55 | return "Not Content"
56 | elif response.status_code == 404:
57 | return "Not Find ListenKey"
58 |
--------------------------------------------------------------------------------
/bingX/perpetual/v2/trade.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.perpetual.v2.trade
3 | '''
4 |
5 | from typing import List
6 | from bingX import ClientError
7 |
8 | def trade_order(self,
9 | symbol: str,
10 | type: str,
11 | side: str,
12 | positionSide: str = None,
13 | price: float = None,
14 | quantity: float = None,
15 | stopPrice: float = None,
16 | recvWindow: int = None,
17 | reduceOnly: bool = None,
18 | ) -> dict:
19 | ''' Place a New Order
20 | POST /openApi/swap/v2/trade/order
21 |
22 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_1-trade-order
23 | '''
24 | res = self.post("/openApi/swap/v2/trade/order", params={
25 | "symbol": symbol,
26 | "type": type,
27 | "side": side,
28 | "positionSide": positionSide,
29 | "price": price,
30 | "quantity": quantity,
31 | "stopPrice": stopPrice,
32 | "recvWindow": recvWindow,
33 | "reduceOnly": reduceOnly
34 | })
35 |
36 | if 'code' in res and res['code']:
37 | raise ClientError(res['code'], res['msg'])
38 | return res['data']
39 |
40 | def trade_order_test(self,
41 | symbol: str,
42 | type: str,
43 | side: str,
44 | positionSide: str = None,
45 | price: float = None,
46 | quantity: float = None,
47 | stopPrice: float = None,
48 | recvWindow: int = None,
49 | reduceOnly: bool = None,
50 | ) -> dict:
51 | ''' Place a New Order
52 | POST /openApi/swap/v2/trade/order/test
53 |
54 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_1-trade-order
55 | '''
56 | res = self.post("/openApi/swap/v2/trade/order/test", params={
57 | "symbol": symbol,
58 | "type": type,
59 | "side": side,
60 | "positionSide": positionSide,
61 | "price": price,
62 | "quantity": quantity,
63 | "stopPrice": stopPrice,
64 | "recvWindow": recvWindow,
65 | "reduceOnly": reduceOnly
66 | })
67 |
68 | if 'code' in res and res['code']:
69 | raise ClientError(res['code'], res['msg'])
70 | return res['data']
71 |
72 | def bulk_order(self,
73 | batchOrders: List,
74 | recvWindow: int = None,
75 | ) -> dict:
76 | ''' Bulk order
77 | POST /openApi/swap/v2/trade/batchOrders
78 |
79 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_2-bulk-order
80 | '''
81 | res = self.post("/openApi/swap/v2/trade/batchOrders", params={
82 | "batchOrders": batchOrders,
83 | "recvWindow": recvWindow,
84 | })
85 |
86 | if 'code' in res and res['code']:
87 | raise ClientError(res['code'], res['msg'])
88 | return res['data']
89 |
90 | def close_all_positions(self,
91 | recvWindow: int = None,
92 | ) -> dict:
93 | ''' One-Click Close All Positions
94 | POST /openApi/swap/v2/trade/closeAllPositions
95 |
96 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_3-one-click-close-all-positions
97 | '''
98 | res = self.post("/openApi/swap/v2/trade/closeAllPositions", params={
99 | "recvWindow": recvWindow,
100 | })
101 |
102 | if 'code' in res and res['code']:
103 | raise ClientError(res['code'], res['msg'])
104 | return res['data']
105 |
106 | def cancel_order(self,
107 | orderId: int,
108 | symbol: str,
109 | recvWindow: int = None,
110 | ) -> dict:
111 | ''' Cancel an Order
112 | DELETE /openApi/swap/v2/trade/order
113 |
114 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_4-cancel-an-order
115 | '''
116 | res = self.delete("/openApi/swap/v2/trade/order", params={
117 | "orderId": orderId,
118 | "symbol": symbol,
119 | "recvWindow": recvWindow,
120 | })
121 |
122 | if 'code' in res and res['code']:
123 | raise ClientError(res['code'], res['msg'])
124 | return res['data']
125 |
126 | def cancel_orders(self,
127 | symbol: str,
128 | orderIdList: List[int],
129 | recvWindow: int = None,
130 | ) -> dict:
131 | ''' Cancel a Batch of Orders
132 | DELETE /openApi/swap/v2/trade/batchOrders
133 |
134 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_5-cancel-a-batch-of-orders
135 | '''
136 | res = self.delete("/openApi/swap/v2/trade/batchOrders", params={
137 | "symbol": symbol,
138 | "orderIdList": orderIdList,
139 | "recvWindow": recvWindow,
140 | })
141 |
142 | if 'code' in res and res['code']:
143 | raise ClientError(res['code'], res['msg'])
144 | return res['data']
145 |
146 | def cancel_all_orders(self,
147 | symbol: str,
148 | recvWindow: int = None,
149 | ) -> dict:
150 | ''' Cancel All Orders
151 | DELETE /openApi/swap/v2/trade/allOpenOrders
152 |
153 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_6-cancel-all-orders
154 | '''
155 | res = self.post("/openApi/swap/v2/trade/allOpenOrders", params={
156 | "symbol": symbol,
157 | "recvWindow": recvWindow,
158 | })
159 |
160 | if 'code' in res and res['code']:
161 | raise ClientError(res['code'], res['msg'])
162 | return res['data']
163 |
164 | def current_orders(self,
165 | symbol: str,
166 | recvWindow: int = None,
167 | ) -> dict:
168 | ''' Query all current pending orders
169 | GET /openApi/swap/v2/trade/openOrders
170 |
171 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_7-query-all-current-pending-orders
172 | '''
173 | res = self.get("/openApi/swap/v2/trade/openOrders", params={
174 | "symbol": symbol,
175 | "recvWindow": recvWindow,
176 | })
177 |
178 | if 'code' in res and res['code']:
179 | raise ClientError(res['code'], res['msg'])
180 | return res['data']
181 |
182 | def order(self,
183 | symbol: str,
184 | orderId: int,
185 | recvWindow: int = None,
186 | ) -> dict:
187 | ''' Query Order
188 | GET /openApi/swap/v2/trade/order
189 |
190 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_8-query-order
191 | '''
192 | res = self.get("/openApi/swap/v2/trade/order", params={
193 | "symbol": symbol,
194 | "orderId": orderId,
195 | "recvWindow": recvWindow,
196 | })
197 |
198 | if 'code' in res and res['code']:
199 | raise ClientError(res['code'], res['msg'])
200 | return res['data']
201 |
202 | def margin_mode(self,
203 | symbol: str,
204 | recvWindow: int = None,
205 | ) -> dict:
206 | ''' Query Margin Mode
207 | GET /openApi/swap/v2/trade/marginType
208 |
209 | recvWindow
210 | '''
211 | res = self.get("/openApi/swap/v2/trade/marginType", params={
212 | "symbol": symbol,
213 | "recvWindow": recvWindow,
214 | })
215 |
216 | if 'code' in res and res['code']:
217 | raise ClientError(res['code'], res['msg'])
218 | return res['data']
219 |
220 | def switch_margin_mode(self,
221 | symbol: str,
222 | marginType: str,
223 | recvWindow: int = None,
224 | ) -> dict:
225 | ''' Switch Margin Mode
226 | POST /openApi/swap/v2/trade/marginType
227 |
228 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_10-switch-margin-mode
229 | '''
230 | res = self.post("/openApi/swap/v2/trade/marginType", params={
231 | "symbol": symbol,
232 | "marginType": marginType,
233 | "recvWindow": recvWindow,
234 | })
235 |
236 | if 'code' in res and res['code']:
237 | raise ClientError(res['code'], res['msg'])
238 | return res # No "data" return?
239 |
240 | def leverage(self,
241 | symbol: str,
242 | recvWindow: int = None,
243 | ) -> dict:
244 | ''' Query Leverage
245 | GET /openApi/swap/v2/trade/leverage
246 |
247 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_11-query-leverage
248 | '''
249 | res = self.get("/openApi/swap/v2/trade/leverage", params={
250 | "symbol": symbol,
251 | "recvWindow": recvWindow,
252 | })
253 |
254 | if 'code' in res and res['code']:
255 | raise ClientError(res['code'], res['msg'])
256 | return res['data']
257 |
258 | def switch_leverage(self,
259 | symbol: str,
260 | side: str,
261 | leverage: int,
262 | recvWindow: int = None,
263 | ) -> dict:
264 | ''' Switch Leverage
265 | POST /openApi/swap/v2/trade/leverage
266 |
267 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_12-switch-leverage
268 | '''
269 | res = self.post("/openApi/swap/v2/trade/leverage", params={
270 | "symbol": symbol,
271 | "side": side,
272 | "leverage": leverage,
273 | "recvWindow": recvWindow,
274 | })
275 |
276 | if 'code' in res and res['code']:
277 | raise ClientError(res['code'], res['msg'])
278 | return res['data']
279 |
280 | def force_orders(self,
281 | symbol: str = None,
282 | autoCloseType: str = None,
283 | startTime: int = None,
284 | endTime: int = None,
285 | limit: int = None,
286 | recvWindow: int = None,
287 | ) -> dict:
288 | ''' User Force Orders
289 | GET /openApi/swap/v2/trade/forceOrders
290 |
291 | https://bingx-api.github.io/docs/swap/trade-api.html#_13-user-s-force-orders
292 | '''
293 | res = self.get("/openApi/swap/v2/trade/forceOrders", params={
294 | "symbol": symbol,
295 | "autoCloseType": autoCloseType,
296 | "startTime": startTime,
297 | "endTime": endTime,
298 | "limit": limit,
299 | "recvWindow": recvWindow,
300 | })
301 |
302 | if 'code' in res and res['code']:
303 | raise ClientError(res['code'], res['msg'])
304 | return res['data']
305 |
306 | def orders_history(self,
307 | symbol: str,
308 | orderId: int = None,
309 | startTime: int = None,
310 | endTime: int = None,
311 | limit: int = 500,
312 | recvWindow: int = None,
313 | ) -> dict:
314 | ''' User History Orders
315 | GET /openApi/swap/v2/trade/allOrders
316 |
317 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_14-user-s-history-orders
318 | '''
319 | res = self.get("/openApi/swap/v2/trade/allOrders", params={
320 | "symbol": symbol,
321 | "orderId": orderId,
322 | "startTime": startTime,
323 | "endTime": endTime,
324 | "limit": limit,
325 | "recvWindow": recvWindow,
326 | })
327 |
328 | if 'code' in res and res['code']:
329 | raise ClientError(res['code'], res['msg'])
330 | return res['data']
331 |
332 | def adjust_isolated_margin(self,
333 | symbol: str,
334 | amount: float,
335 | type: int,
336 | positionSide: str = None,
337 | recvWindow: int = None,
338 | ) -> dict:
339 | ''' Adjust isolated margin
340 | POST /openApi/swap/v2/trade/positionMargin
341 |
342 | https://bingx-api.github.io/docs/swapV2/trade-api.html#_15-adjust-isolated-margin
343 | '''
344 | res = self.get("/openApi/swap/v2/trade/allOrders", params={
345 | "symbol": symbol,
346 | "amount": amount,
347 | "type": type,
348 | "positionSide": positionSide,
349 | "recvWindow": recvWindow,
350 | })
351 |
352 | if 'code' in res and res['code']:
353 | raise ClientError(res['code'], res['msg'])
354 | return res # no "data" return?
--------------------------------------------------------------------------------
/bingX/spot/Spot.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.spot.Spot
3 | '''
4 |
5 | from bingX import API
6 |
7 | class Spot(API):
8 | def __init__(self,
9 | api_key: str,
10 | api_secret: str,
11 | ) -> object:
12 | super().__init__(
13 | api_key = api_key,
14 | api_secret = api_secret,
15 | base_url = 'https://open-api.bingx.com',
16 | )
17 |
18 | # ========== TRADE INTERFACE ==========
19 | from bingX.spot.trade import (
20 | place_order,
21 | cancel_order,
22 | order,
23 | open_orders,
24 | order_history,
25 | assets,
26 | )
27 |
28 | # ========== MARKET INTERFACE ==========
29 | from bingX.spot.market import (
30 | symbols,
31 | trades,
32 | depth,
33 | )
34 |
35 | # ========== USER UNIVERSAL TRANSFER INTERFACE ==========
36 | from bingX.spot.transfer import (
37 | transfer,
38 | transfer_history,
39 | deposit_history,
40 | withdraw_history,
41 | )
42 |
43 | # ========== OTHER INTERFACE ==========
44 | from bingX.spot.other import (
45 | generate_listen_key,
46 | extend_listen_key,
47 | delete_listen_key,
48 | )
49 |
--------------------------------------------------------------------------------
/bingX/spot/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.spot.__init__
3 | '''
4 |
5 | from bingX.spot.Spot import Spot
6 |
--------------------------------------------------------------------------------
/bingX/spot/market.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.spot.market
3 | '''
4 |
5 | from bingX import ClientError
6 |
7 | def symbols(self,
8 | symbol: str = None,
9 | ) -> dict:
10 | ''' Query Symbols
11 | GET /openApi/spot/v1/common/symbols
12 |
13 | https://bingx-api.github.io/docs/spot/market-interface.html#query-symbols
14 | '''
15 | res = self.get('/openApi/spot/v1/common/symbols', params={
16 | 'symbol': symbol,
17 | })
18 |
19 | if 'code' in res and res['code']:
20 | raise ClientError(res['code'], res['msg'])
21 | return res['data']
22 |
23 | def trades(self,
24 | symbol: str,
25 | limit: int = None,
26 | ) -> dict:
27 | ''' Query transaction records
28 | GET /openApi/spot/v1/market/trades
29 |
30 | https://bingx-api.github.io/docs/spot/market-interface.html#query-transaction-records
31 | '''
32 | res = self.get('/openApi/spot/v1/market/trades', params={
33 | 'symbol': symbol,
34 | 'limit': limit,
35 | })
36 |
37 | if 'code' in res and res['code']:
38 | raise ClientError(res['code'], res['msg'])
39 | return res['data']
40 |
41 | def depth(self,
42 | symbol: str,
43 | limit: int = None,
44 | ) -> dict:
45 | ''' Query depth information
46 | GET /openApi/spot/v1/market/depth
47 |
48 | https://bingx-api.github.io/docs/spot/market-interface.html#query-depth-information
49 | '''
50 | res = self.get('/openApi/spot/v1/market/depth', params={
51 | 'symbol': symbol,
52 | 'limit': limit,
53 | })
54 |
55 | if 'code' in res and res['code']:
56 | raise ClientError(res['code'], res['msg'])
57 | return res['data']
58 |
--------------------------------------------------------------------------------
/bingX/spot/other.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.spot.other
3 | '''
4 |
5 | from bingX import ClientError
6 |
7 | def generate_listen_key(self):
8 | ''' Generate Listen Key
9 | POST /openApi/user/auth/userDataStream
10 |
11 | https://bingx-api.github.io/docs/spot/other-interface.html#generate-listen-key
12 | '''
13 | res = self.post('/openApi/user/auth/userDataStream')
14 |
15 | if 'code' in res and res['code']:
16 | raise ClientError(res['code'], res['msg'])
17 | return res
18 |
19 | def extend_listen_key(self,
20 | listenKey: str,
21 | ) -> None:
22 | ''' Extend Listen Key Validity period
23 | PUT /openApi/user/auth/userDataStream
24 |
25 | https://bingx-api.github.io/docs/spot/other-interface.html#extend-listen-key-validity-period
26 | '''
27 | res = self.put('/openApi/user/auth/userDataStream', params={
28 | 'listenKey': listenKey,
29 | })
30 |
31 | if 'code' in res and res['code']:
32 | raise ClientError(res['code'], res['msg'])
33 | return res
34 |
35 | def delete_listen_key(self,
36 | listenKey: str,
37 | ) -> None:
38 | ''' Delete Listen Key
39 | DELETE /openApi/user/auth/userDataStream
40 |
41 | https://bingx-api.github.io/docs/spot/other-interface.html#delete-listen-key
42 | '''
43 | res = self.delete('/openApi/user/auth/userDataStream', params={
44 | 'listenKey': listenKey,
45 | })
46 |
47 | if 'code' in res and res['code']:
48 | raise ClientError(res['code'], res['msg'])
49 | return res
50 |
--------------------------------------------------------------------------------
/bingX/spot/trade.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.spot.trade
3 | '''
4 |
5 | from bingX import ClientError
6 |
7 | def place_order(self,
8 | symbol: str,
9 | side: str,
10 | type: str,
11 | timeInForce: str = None,
12 | quantity: float = None,
13 | quoteOrderQty: float = None,
14 | price: float = None,
15 | recvWindow: int = None,
16 | ) -> dict:
17 | ''' Create an Order
18 | POST /openApi/spot/v1/trade/order
19 |
20 | https://bingx-api.github.io/docs/spot/trade-interface.html#create-an-order
21 | '''
22 | res = self.post('/openApi/spot/v1/trade/order', params={
23 | 'symbol': symbol,
24 | 'side': side,
25 | 'type': type,
26 | 'timeInForce': timeInForce,
27 | 'quantity': quantity,
28 | 'quoteOrderQty': quoteOrderQty,
29 | 'price': price,
30 | 'recvWindow': recvWindow,
31 | })
32 |
33 | if 'code' in res and res['code']:
34 | raise ClientError(res['code'], res['msg'])
35 | return res['data']
36 |
37 | def cancel_order(self,
38 | symbol: str,
39 | orderId: int,
40 | recvWindow: int = None,
41 | ) -> dict:
42 | ''' Cancel an Order
43 | POST /openApi/spot/v1/trade/cancel
44 |
45 | https://bingx-api.github.io/docs/spot/trade-interface.html#cancel-an-order
46 | '''
47 | res = self.post('/openApi/spot/v1/trade/cancel', params={
48 | 'symbol': symbol,
49 | 'orderId': orderId,
50 | 'recvWindow': recvWindow,
51 | })
52 |
53 | if 'code' in res and res['code']:
54 | raise ClientError(res['code'], res['msg'])
55 | return res['data']
56 |
57 | def order(self,
58 | symbol: str,
59 | orderId: int,
60 | recvWindow: int = None,
61 | ) -> dict:
62 | ''' Query Orders
63 | GET /openApi/spot/v1/trade/query
64 |
65 | https://bingx-api.github.io/docs/spot/trade-interface.html#query-orders
66 | '''
67 | res = self.get('/openApi/spot/v1/trade/query', params={
68 | 'symbol': symbol,
69 | 'orderId': orderId,
70 | 'recvWindow': recvWindow,
71 | })
72 |
73 | if 'code' in res and res['code']:
74 | raise ClientError(res['code'], res['msg'])
75 | return res['data']
76 |
77 | def open_orders(self,
78 | symbol: str,
79 | recvWindow: int = None,
80 | ) -> dict:
81 | ''' Query Open Orders
82 | GET /openApi/spot/v1/trade/openOrders
83 |
84 | https://bingx-api.github.io/docs/spot/trade-interface.html#query-open-orders
85 | '''
86 | res = self.get('/openApi/spot/v1/trade/openOrders', params={
87 | 'symbol': symbol,
88 | 'recvWindow': recvWindow,
89 | })
90 |
91 | if 'code' in res and res['code']:
92 | raise ClientError(res['code'], res['msg'])
93 | return res['data']
94 |
95 | def order_history(self,
96 | symbol: str,
97 | orderId: int = None,
98 | startTime: int = None,
99 | endTime: int = None,
100 | pageIndex: int = None,
101 | pageSize: int = None,
102 | recvWindow: int = None,
103 | ) -> dict:
104 | ''' Query Order History
105 | GET /openApi/spot/v1/trade/historyOrders
106 |
107 | https://bingx-api.github.io/docs/spot/trade-interface.html#query-order-history
108 | '''
109 | res = self.get('/openApi/spot/v1/trade/historyOrders', params={
110 | 'symbol': symbol,
111 | 'orderId': orderId,
112 | 'startTime': startTime,
113 | 'endTime': endTime,
114 | 'pageIndex': pageIndex,
115 | 'pageSize': pageSize,
116 | 'recvWindow': recvWindow,
117 | })
118 |
119 | if 'code' in res and res['code']:
120 | raise ClientError(res['code'], res['msg'])
121 | return res['data']
122 |
123 | def assets(self,
124 | recvWindow: int = None,
125 | ) -> dict:
126 | ''' Query Assets
127 | GET /openApi/spot/v1/account/balance
128 |
129 | https://bingx-api.github.io/docs/spot/trade-interface.html#query-assets
130 | '''
131 | res = self.get('/openApi/spot/v1/account/balance', params={
132 | 'recvWindow': recvWindow,
133 | })
134 |
135 | if 'code' in res and res['code']:
136 | raise ClientError(res['code'], res['msg'])
137 | return res['data']
138 |
--------------------------------------------------------------------------------
/bingX/spot/transfer.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.spot.transfer
3 | '''
4 |
5 | from bingX import ClientError
6 |
7 | def transfer(self,
8 | type: str,
9 | asset: str = None,
10 | amount: float = None,
11 | recvWindow: int = None,
12 | ) -> dict:
13 | ''' User Universal Transfer
14 | POST /openApi/api/v3/asset/transfer
15 |
16 | https://bingx-api.github.io/docs/spot/user-interface.html#user-universal-transfer
17 | '''
18 | res = self.post('/openApi/api/v3/asset/transfer', params={
19 | 'type': type,
20 | 'asset': asset,
21 | 'amount': amount,
22 | 'recvWindow': recvWindow,
23 | })
24 |
25 | if 'code' in res and res['code']:
26 | raise ClientError(res['code'], res['msg'])
27 | return res
28 |
29 | def transfer_history(self,
30 | type: str,
31 | startTime: int = None,
32 | endTime: int = None,
33 | current: int = None,
34 | size: int = None,
35 | recvWindow: int = None,
36 | ) -> dict:
37 | ''' Query User Universal Transfer History (USER_DATA)
38 | GET /openApi/api/v3/asset/transfer
39 |
40 | https://bingx-api.github.io/docs/spot/user-interface.html#query-user-universal-transfer-history-user-data
41 | '''
42 | res = self.get('/openApi/api/v3/asset/transfer', params={
43 | 'type': type,
44 | 'startTime': startTime,
45 | 'endTime': endTime,
46 | 'current': current,
47 | 'size': size,
48 | 'recvWindow': recvWindow,
49 | })
50 |
51 | if 'code' in res and res['code']:
52 | raise ClientError(res['code'], res['msg'])
53 | return res
54 |
55 | def deposit_history(self,
56 | coin: str = None,
57 | status: int = None,
58 | startTime: int = None,
59 | endTime: int = None,
60 | offset: int = None,
61 | limit: int = None,
62 | recvWindow: int = None,
63 | ) -> dict:
64 | ''' Deposit History(supporting network)
65 | GET /openApi/api/v3/capital/deposit/hisrec
66 |
67 | https://bingx-api.github.io/docs/spot/user-interface.html#deposit-history-supporting-network
68 | '''
69 | res = self.get('/openApi/api/v3/capital/deposit/hisrec', params={
70 | 'coin': coin,
71 | 'status': status,
72 | 'startTime': startTime,
73 | 'endTime': endTime,
74 | 'offset': offset,
75 | 'limit': limit,
76 | 'recvWindow': recvWindow,
77 | })
78 |
79 | if 'code' in res and res['code']:
80 | raise ClientError(res['code'], res['msg'])
81 | return res
82 |
83 | def withdraw_history(self,
84 | coin: str = None,
85 | withdrawOrderId: str = None,
86 | status: int = None,
87 | startTime: int = None,
88 | endTime: int = None,
89 | offset: int = None,
90 | limit: int = None,
91 | recvWindow: int = None,
92 | ) -> dict:
93 | ''' Withdraw History (supporting network)
94 | GET /openApi/api/v3/capital/withdraw/history
95 |
96 | https://bingx-api.github.io/docs/spot/user-interface.html#withdraw-history-supporting-network
97 | '''
98 | res = self.get('/openApi/api/v3/capital/withdraw/history', params={
99 | 'coin': coin,
100 | 'withdrawOrderId': withdrawOrderId,
101 | 'status': status,
102 | 'startTime': startTime,
103 | 'endTime': endTime,
104 | 'offset': offset,
105 | 'limit': limit,
106 | 'recvWindow': recvWindow,
107 | })
108 |
109 | if 'code' in res and res['code']:
110 | raise ClientError(res['code'], res['msg'])
111 | return res
112 |
--------------------------------------------------------------------------------
/bingX/standard/Standard.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.standard.Standard
3 | '''
4 |
5 | from bingX import API
6 |
7 | class Standard(API):
8 | def __init__(self,
9 | api_key: str,
10 | api_secret: str,
11 | ) -> object:
12 | super().__init__(
13 | api_key = api_key,
14 | api_secret = api_secret,
15 | base_url = "https://open-api.bingx.com",
16 | )
17 |
18 | # ========== STANDARD CONTRACT INTERFACE ==========
19 | from bingX.standard.trade import (
20 | position,
21 | order_history,
22 | balance,
23 | )
24 |
--------------------------------------------------------------------------------
/bingX/standard/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.standard.__init__
3 | '''
4 |
5 | from bingX.standard.Standard import Standard
6 |
--------------------------------------------------------------------------------
/bingX/standard/trade.py:
--------------------------------------------------------------------------------
1 | '''
2 | bingX.standard.trade
3 | '''
4 |
5 | from bingX import ClientError
6 |
7 | def position(self) -> dict:
8 | ''' Position
9 | GET /openApi/contract/v1/allPosition
10 |
11 | https://bingx-api.github.io/docs/standard/contract-interface.html#position
12 | '''
13 | res = self.get("/openApi/contract/v1/allPosition")
14 |
15 | if 'code' in res and res['code']:
16 | raise ClientError(res['code'], res['msg'])
17 | return res['data']
18 |
19 | def order_history(self,
20 | symbol: str,
21 | orderId: int = None,
22 | startTime: int = None,
23 | endTime: int = None,
24 | limit: int = None,
25 | ) -> dict:
26 | ''' Historical order
27 | GET /openApi/contract/v1/allOrders
28 |
29 | https://bingx-api.github.io/docs/standard/contract-interface.html#historical-order
30 | '''
31 | res = self.get("/openApi/contract/v1/allOrders", params={
32 | "symbol": symbol,
33 | "orderId": orderId,
34 | "startTime": startTime,
35 | "endTime": endTime,
36 | "limit": limit,
37 | })
38 |
39 | if 'code' in res and res['code']:
40 | raise ClientError(res['code'], res['msg'])
41 | return res['data']
42 |
43 | def balance(self) -> dict:
44 | ''' Query standard contract balance
45 | GET /openApi/contract/v1/balance
46 |
47 | https://bingx-api.github.io/docs/standard/contract-interface.html#query-standard-contract-balance
48 | '''
49 | res = self.get("/openApi/contract/v1/balance")
50 |
51 | if 'code' in res and res['code']:
52 | raise ClientError(res['code'], res['msg'])
53 | return res['data']
54 |
--------------------------------------------------------------------------------
/bingX/websocket/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ming119/bingX-connector-python/aae12ee3f585eced644059196eab9bf2c31e3468/bingX/websocket/__init__.py
--------------------------------------------------------------------------------
/bingX/websocket/bingx_socket_manager.py:
--------------------------------------------------------------------------------
1 | import gzip
2 | import io
3 |
4 | import logging
5 | import threading
6 | from websocket import (
7 | ABNF,
8 | create_connection,
9 | WebSocketException,
10 | WebSocketConnectionClosedException,
11 | )
12 |
13 |
14 | class BinanceSocketManager(threading.Thread):
15 | def __init__(
16 | self,
17 | stream_url,
18 | on_message=None,
19 | on_open=None,
20 | on_close=None,
21 | on_error=None,
22 | logger=None,
23 | ):
24 | threading.Thread.__init__(self)
25 | if not logger:
26 | logger = logging.getLogger(__name__)
27 | self.logger = logger
28 | self.stream_url = stream_url
29 | self.on_message = on_message
30 | self.on_open = on_open
31 | self.on_close = on_close
32 |
33 | self.on_error = on_error
34 |
35 | self.create_ws_connection()
36 |
37 | def create_ws_connection(self):
38 | self.logger.error(
39 | f"Creating connection with WebSocket Server: {self.stream_url}",
40 | )
41 | self.ws = create_connection(self.stream_url)
42 | self.logger.error(
43 | f"WebSocket connection has been established: {self.stream_url}",
44 | )
45 | self._callback(self.on_open)
46 |
47 | def run(self):
48 | self.read_data()
49 |
50 | def send_message(self, message):
51 | self.logger.error("Sending message to BingX WebSocket Server: %s", message)
52 | self.ws.send(message)
53 |
54 | def read_data(self):
55 | data = ""
56 | while True:
57 | try:
58 | op_code, frame = self.ws.recv_data_frame(True)
59 | except WebSocketException as e:
60 | if isinstance(e, WebSocketConnectionClosedException):
61 | self.logger.error("Lost websocket connection")
62 | print(str(e))
63 | else:
64 | self.logger.error("Websocket exception: {}".format(e))
65 | raise e
66 | except Exception as e:
67 | self.logger.error("Exception in read_data: {}".format(e))
68 | raise e
69 |
70 | if op_code == ABNF.OPCODE_CLOSE:
71 | self.logger.warning(
72 | "CLOSE frame received, closing websocket connection"
73 | )
74 | self._callback(self.on_close)
75 | break
76 | else:
77 | data = frame.data
78 | if op_code == ABNF.OPCODE_TEXT:
79 | data = data.decode("utf-8")
80 |
81 | compressed_data = gzip.GzipFile(fileobj=io.BytesIO(data), mode='rb')
82 | decompressed_data = compressed_data.read()
83 | utf8_data = decompressed_data.decode('utf-8')
84 | if utf8_data == "Ping":
85 | self.ws.send("Pong")
86 | else:
87 | self._callback(self.on_message, utf8_data)
88 |
89 | def close(self):
90 | if not self.ws.connected:
91 | self.logger.warn("Websocket already closed")
92 | else:
93 | self.ws.send_close()
94 | return
95 |
96 | def _callback(self, callback, *args):
97 | if callback:
98 | try:
99 | callback(self, *args)
100 | except Exception as e:
101 | self.logger.error("Error from callback {}: {}".format(callback, e))
102 | if self.on_error:
103 | self.on_error(self, e)
104 |
--------------------------------------------------------------------------------
/bingX/websocket/futures_websocket.py:
--------------------------------------------------------------------------------
1 | from bingX.websocket.websocket_client import BingxWebsocketClient
2 |
3 |
4 | class UMFuturesWebsocketClient(BingxWebsocketClient):
5 | def __init__(
6 | self,
7 | stream_url="wss://open-api-swap.bingx.com/swap-market",
8 | on_message=None,
9 | on_open=None,
10 | on_close=None,
11 | on_error=None,
12 | listen_key=None
13 | ):
14 | if listen_key is not None:
15 | stream_url = stream_url + "?listenKey=" + listen_key
16 | print(stream_url)
17 | super().__init__(
18 | stream_url,
19 | on_message=on_message,
20 | on_open=on_open,
21 | on_close=on_close,
22 | on_error=on_error,
23 | )
24 |
25 | def latest_mark_price(self, symbol: str, id=None, action=None, **kwargs):
26 | """Mark Price Streams
27 |
28 | Push latest mark price changes.
29 |
30 | Subscription Type
31 | dataType is @markPrice, such as BTC-USDT@markPrice.
32 |
33 | https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20to%20latest%20mark%20price%20changes
34 |
35 | """
36 | stream_name = symbol + "@markPrice"
37 |
38 | self.send_message_to_server(stream_name, action=action, id=id)
39 |
40 | def last_price_changes(self, symbol: str, id=None, action=None, **kwargs):
41 |
42 | """
43 | Subscribe to latest price changes
44 |
45 | Push latest price changes.
46 |
47 | Subscription Type
48 | dataType is @lastPrice, such as BTC-USDT@lastPrice.
49 |
50 | symbol:
51 | string
52 | There must be a hyphen/ "-" in the trading pair symbol. eg: BTC-USDT.
53 |
54 | https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20to%20latest%20price%20changes
55 |
56 | """
57 | stream_name = symbol + "@lastPrice"
58 |
59 | self.send_message_to_server(stream_name, action=action, id=id)
60 |
61 | def kline(self, symbol: str, interval: str, id=None, action=None, **kwargs):
62 | """Kline/Candlestick Streams
63 |
64 | Subscribe to market k-line data of one trading pair
65 |
66 | Subscription Type
67 | The dataType is @kline_ E.g. BTC-USDT@kline_1m
68 |
69 | Subscription Example
70 | {"id":"e745cd6d-d0f6-4a70-8d5a-043e4c741b40","reqType": "sub","dataType":"BTC-USDT@kline_1m"}
71 |
72 | For more about return error codes, please see the error code description on the homepage.
73 |
74 |
75 | https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20the%20Latest%20Trade%20Detail
76 |
77 | interval:
78 | m -> minutes; h -> hours; d -> days; w -> weeks; M -> months
79 |
80 | - 1m
81 | - 3m
82 | - 5m
83 | - 15m
84 | - 30m
85 | - 1h
86 | - 2h
87 | - 4h
88 | - 6h
89 | - 8h
90 | - 12h
91 | - 1d
92 | - 3d
93 | - 1w
94 | - 1M
95 | """
96 | stream_name = symbol + "@kline_" + str(interval)
97 | self.send_message_to_server(stream_name, action=action, id=id)
98 |
99 | def user_data(self, listen_key: str, id=None, action=None, **kwargs):
100 | """
101 | Listen to user data by using the provided listenKey
102 | https://bingx-api.github.io/docs/#/en-us/swapV2/socket/account.html#Account%20balance%20and%20position%20update%20push
103 | """
104 | self.send_message_to_server(listen_key, action=action, id=id)
105 |
--------------------------------------------------------------------------------
/bingX/websocket/websocket.py:
--------------------------------------------------------------------------------
1 | import _thread
2 | import json
3 | import random
4 |
5 | import websocket
6 | import gzip
7 | import io
8 |
9 |
10 | class Websocket(object):
11 |
12 | def __init__(self, URL='wss://open-api-swap.bingx.com/swap-market'):
13 | self.sub_link = None
14 | self.url = URL
15 | self.ws = None
16 |
17 | def on_open(self, ws):
18 | subStr = json.dumps(self.sub_link)
19 | ws.send(subStr)
20 | return "Subscribed to :" + subStr
21 |
22 | def on_message(self, ws, message):
23 | compressed_data = gzip.GzipFile(fileobj=io.BytesIO(message), mode='rb')
24 | decompressed_data = compressed_data.read()
25 | utf8_data = decompressed_data.decode('utf-8')
26 | if utf8_data == "Ping":
27 | ws.send("Pong")
28 | return utf8_data
29 |
30 | def on_error(self, ws, error):
31 | return error
32 |
33 | def on_close(self, ws, close_status_code, close_msg):
34 | return 'The connection is closed!'
35 |
36 | def user_data(self, listen_key):
37 | '''
38 | https://bingx-api.github.io/docs/#/en-us/swapV2/socket/account.html#listenKey%20expired%20push
39 | '''
40 | self.url = self.url + '?listenKey=' + listen_key
41 | self.start()
42 |
43 | def kline(self, type, pair, time):
44 | self.sub_link = {"id": ''.join(random.choice(string.ascii_letters) for _ in range(10)), "reqType": type,
45 | "dataType": pair + "@kline_" + time}
46 | self.start()
47 | ''''
48 | Get Price Kline With Websocket
49 | type can be sub or unsub
50 | time must be same as exchange time format like 1m,5m,15m,30m,1h
51 | https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20K-Line%20Data
52 | test.kline("sub","BTC-USDT","1m")
53 | '''
54 |
55 | def last_price(self, type, pair):
56 | self.sub_link = {"id": ''.join(random.choice(string.ascii_letters) for _ in range(10)), "reqType": type,
57 | "dataType": pair + "@lastPrice"}
58 | self.start()
59 |
60 | def start(self):
61 | self.ws = websocket.WebSocketApp(
62 | self.url,
63 | on_open=self.on_open,
64 | on_message=self.on_message,
65 | on_error=self.on_error,
66 | on_close=self.on_close,
67 | )
68 | self.ws.run_forever()
69 |
--------------------------------------------------------------------------------
/bingX/websocket/websocket_client.py:
--------------------------------------------------------------------------------
1 | import json
2 | import logging
3 | import uuid
4 |
5 | from bingX.websocket.bingx_socket_manager import BinanceSocketManager
6 |
7 |
8 | def get_random_id():
9 | return str(uuid.uuid4())
10 |
11 |
12 | class BingxWebsocketClient:
13 | ACTION_SUBSCRIBE = "sub"
14 | ACTION_UNSUBSCRIBE = "unsub"
15 |
16 | def __init__(
17 | self,
18 | stream_url,
19 | on_message=None,
20 | on_open=None,
21 | on_close=None,
22 | on_error=None,
23 | logger=None,
24 | ):
25 | if not logger:
26 | logger = logging.getLogger(__name__)
27 | self.logger = logger
28 | self.socket_manager = self._initialize_socket(
29 | stream_url,
30 | on_message,
31 | on_open,
32 | on_close,
33 | on_error,
34 | logger,
35 | )
36 |
37 | # start the thread
38 | self.socket_manager.start()
39 | self.logger.debug("BingX WebSocket Client started.")
40 |
41 | def _initialize_socket(
42 | self,
43 | stream_url,
44 | on_message,
45 | on_open,
46 | on_close,
47 | on_error,
48 | logger,
49 | ):
50 | return BinanceSocketManager(
51 | stream_url,
52 | on_message=on_message,
53 | on_open=on_open,
54 | on_close=on_close,
55 | on_error=on_error,
56 | logger=logger,
57 | )
58 |
59 | def _single_stream(self, stream):
60 | if isinstance(stream, str):
61 | return True
62 | elif isinstance(stream, list):
63 | return False
64 | else:
65 | raise ValueError("Invalid stream name, expect string or array")
66 |
67 | def send(self, message: dict):
68 | self.socket_manager.send_message(json.dumps(message))
69 |
70 | def send_message_to_server(self, message, action=None, id=None):
71 | if not id:
72 | id = get_random_id()
73 | if action != self.ACTION_UNSUBSCRIBE:
74 | return self.subscribe(message, id=id)
75 | return self.unsubscribe(message, id=id)
76 |
77 | def subscribe(self, stream, id=None):
78 | if not id:
79 | id = get_random_id()
80 | json_msg = json.dumps({"id": id, "reqType": "sub", "dataType": stream})
81 | print(json_msg)
82 | self.socket_manager.send_message(json_msg)
83 |
84 | def unsubscribe(self, stream, id=None):
85 | if not id:
86 | id = get_random_id()
87 | json_msg = json.dumps({"id": id, "reqType": "unsub", "dataType": stream})
88 | self.socket_manager.send_message(json_msg)
89 |
90 | def stop(self, id=None):
91 | self.socket_manager.close()
92 | self.socket_manager.join()
93 |
--------------------------------------------------------------------------------
/example/websocket_example.py:
--------------------------------------------------------------------------------
1 | import gzip
2 | import io
3 |
4 | from bingX.websocket.futures_websocket import UMFuturesWebsocketClient
5 |
6 | def read_websocket_message(_, message):
7 | print(message)
8 |
9 |
10 | future_websocket = UMFuturesWebsocketClient(on_message=read_websocket_message)
11 |
12 | """
13 | Symbol must contain - between coin and pair, interval must be like exchange time frames (1m,5m,15m,30m,1h) id can
14 | be empty and generate random by library action can be sub or unsub default is sub for unsub you must set it to unsub
15 | all messages from websocket will be sent to your function witch here is read_websocket_message
16 |
17 | future_websocket.kline(symbol="BTC-USDT", interval="1m",id="xxxxxxxxxxx",action="sub")
18 | future_websocket.kline(symbol="BTC-USDT", interval="1m",id="xxxxxxxxxxx",action="unsub")
19 |
20 | """
21 |
22 | future_websocket.kline(symbol="BTC-USDT", interval="1m")
23 |
24 | """
25 | for using user_data to get position and order data you don't need to call any function the different is just when you
26 | initialized websocket class that here is UMFuturesWebsocketClient, for using user_data you need to create a listen_key
27 | and sent it to the class.
28 | just follow example
29 | """
30 |
31 | user_data = UMFuturesWebsocketClient(on_message=read_websocket_message,listen_key="x")
32 |
33 | """
34 | with this code you can receive user_data in read_websocket_message and do what ever you want to do.
35 | just remember listen_key need to extend every 1 hour and websocket connection will be destroyed in 24 hours
36 | for creating and expanding listen_key you can use :
37 | from bingX.perpetual.v2 import other
38 |
39 | listen_key = other.generate_listen_key('API_KEY')
40 | other.delete_listen_key('listen_key')
41 | other.extend_listen_key('listen_key')
42 |
43 | """
44 |
45 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | with open("README.md", "r") as fh:
4 | long_description = fh.read()
5 |
6 | NAME = "bingX-connector"
7 | VERSION = "0.0.5"
8 | AUTHOR = "Ming119"
9 | URL = "https://github.com/Ming119/bingX-connector-python"
10 |
11 | setuptools.setup(
12 | name=NAME,
13 | version=VERSION,
14 | author=AUTHOR,
15 | url=URL,
16 | description="A simple connector to BingX Public API",
17 | long_description=long_description,
18 | long_description_content_type="text/markdown",
19 |
20 | packages=setuptools.find_packages(exclude=["test", "docs"]),
21 | package_data={
22 | "perpetual": ["*", "*/*"],
23 | },
24 |
25 | license='GPLv3',
26 |
27 | classifiers=[
28 | "Intended Audience :: Developers",
29 | "Intended Audience :: Financial and Insurance Industry",
30 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
31 | "Programming Language :: Python :: 3",
32 | ],
33 | )
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ming119/bingX-connector-python/aae12ee3f585eced644059196eab9bf2c31e3468/tests/__init__.py
--------------------------------------------------------------------------------
/tests/test_spot.py:
--------------------------------------------------------------------------------
1 | '''
2 | tests/test_spot.py
3 | '''
4 |
5 | import unittest, os
6 | from bingX.spot import Spot
7 |
8 | class TestStandard(unittest.TestCase):
9 |
10 | def setUp(self) -> None:
11 | self.api_key = os.environ['API_KEY']
12 | self.api_secret = os.environ['API_SECRET']
13 | self.spot = Spot(api_key=self.api_key, api_secret=self.api_secret)
14 |
15 | self.listenKey = self.spot.generate_listen_key()['listenKey']
16 |
17 | def tearDown(self) -> None:
18 | del self.spot
19 |
20 | # ========== MARKET ==========
21 | def test_symbols(self):
22 | res = self.spot.symbols()
23 | self.assertEqual(res['code'], 0)
24 |
25 | def test_trades(self):
26 | res = self.spot.trades(symbol='BTC-USDT')
27 | self.assertEqual(res['code'], 0)
28 |
29 | def test_depth(self):
30 | res = self.spot.depth(symbol='BTC-USDT')
31 | self.assertEqual(res['code'], 0)
32 |
33 | # ========== TRADE ==========
34 | def test_balance(self):
35 | res = self.spot.balance()
36 | self.assertEqual(res['code'], 0)
37 |
38 | def test_open_order(self):
39 | res = self.spot.open_orders(symbol='BTC-USDT')
40 | self.assertEqual(res['code'], 0)
41 |
42 | def test_order_history(self):
43 | res = self.spot.order_history(symbol='BTC-USDT')
44 | self.assertEqual(res['code'], 0)
45 |
46 | # ========== OTHER ==========
47 | def test_generate_listen_key(self):
48 | self.assertIsNotNone(self.listenKey)
49 |
50 | def test_extend_listen_key(self):
51 | res = self.spot.extend_listen_key(self.listenKey)
52 | print(res)
53 | self.assertEqual(res['code'], 0)
54 |
55 | def test_delete_listen_key(self):
56 | res = self.spot.delete_listen_key()
57 | self.assertEqual(res['code'], 0)
58 |
59 | if __name__ == '__main__':
60 | unittest.main()
--------------------------------------------------------------------------------
/tests/test_standard.py:
--------------------------------------------------------------------------------
1 | '''
2 |
3 | '''
4 |
5 | import unittest, os
6 | from bingX.standard import Standard
7 |
8 | class TestStandard(unittest.TestCase):
9 |
10 | def setUp(self) -> None:
11 | self.api_key = os.environ['API_KEY']
12 | self.api_secret = os.environ['API_SECRET']
13 | self.standard = Standard(api_key=self.api_key, api_secret=self.api_secret)
14 |
15 | def tearDown(self) -> None:
16 | del self.standard
17 |
18 | def test_position(self):
19 | res = self.standard.position()
20 | self.assertEqual(res['code'], 0)
21 |
22 | def test_order_history(self):
23 | res = self.standard.order_history('BTC-USDT')
24 | self.assertEqual(res['code'], 0)
25 |
26 | if __name__ == '__main__':
27 | unittest.main()
--------------------------------------------------------------------------------