Skip to content

Commit e09bb46

Browse files
authored
v3.3.0 release (#69)
1 parent d30576a commit e09bb46

File tree

10 files changed

+90
-23
lines changed

10 files changed

+90
-23
lines changed

.github/workflows/pythonpackage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
runs-on: ubuntu-latest
1616
strategy:
1717
matrix:
18-
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
18+
python-version: ["3.7", "3.8", "3.9", "3.10"]
1919

2020
steps:
2121
- uses: actions/checkout@v2

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 3.3.0 - 2023-01-11
4+
5+
### Add
6+
- RSA Key support
7+
8+
### Remove
9+
- Python 3.6 Support Deprecated
10+
311
## 3.2.0 - 2022-08-29
412

513
### Add
@@ -32,4 +40,4 @@
3240

3341
## 3.0.0 - 2022-05-26
3442

35-
- Refactor the connector
43+
- Refactor the connector

README.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Binance Futures Public API Connector Python
2-
[![Python 3.6](https://img.shields.io/badge/python-3.6+-blue.svg)](https://www.python.org/downloads/release/python-360/)
2+
[![Python version](https://img.shields.io/pypi/pyversions/binance-futures-connector)](https://www.python.org/downloads/)
33
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
44

55
This is a lightweight library that works as a connector to [Binance Futures public API](https://binance-docs.github.io/apidocs/futures/en/)
@@ -52,6 +52,25 @@ print(response)
5252
```
5353
Please find `examples` folder to check for more endpoints.
5454

55+
## Authentication
56+
Binance supports HMAC and RSA API authentication.
57+
58+
```python
59+
# HMAC Authentication
60+
client = Client(api_key, api_secret)
61+
print(client.account())
62+
63+
# RSA Authentication
64+
key = ""
65+
with open("/Users/john/private_key.pem", "r") as f: # Location of private key file
66+
private_key = f.read()
67+
private_key_passphrase = "" # Optional: only used for encrypted RSA key
68+
69+
client = Client(key=key, private_key=private_key, private_key_passphrase=private_key_passphrase)
70+
print(client.account())
71+
```
72+
Please see `examples/um_futures/trade/get_account.py` or `examples/cm_futures/trade/get_account.py` for more details.
73+
5574
### Base URL
5675

5776
For USDT-M Futures, if `base_url` is not provided, it defaults to `fapi.binance.com`.<br/>
@@ -151,7 +170,7 @@ There are 2 types of error returned from the library:
151170
- `status_code` - HTTP status code
152171
- `error_code` - Server's error code, e.g. `-1102`
153172
- `error_message` - Server's error message, e.g. `Unknown order sent.`
154-
- `header` - Full response header.
173+
- `header` - Full response header.
155174
- `binance.error.ServerError`
156175
- This is thrown when server returns `5XX`, it's an issue from server side.
157176

@@ -193,4 +212,4 @@ Once connected, the websocket server sends a ping frame every 3 minutes and requ
193212
a 10 minutes period. This package handles the pong responses automatically.
194213

195214
## License
196-
MIT
215+
MIT

binance/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "3.2.0"
1+
__version__ = "3.3.0"

binance/api.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
import hmac
21
import json
32
import logging
4-
import hashlib
53
from json import JSONDecodeError
6-
74
import requests
85
from .__version__ import __version__
96
from binance.error import ClientError, ServerError
107
from binance.lib.utils import get_timestamp
118
from binance.lib.utils import cleanNoneValue
129
from binance.lib.utils import encoded_string
1310
from binance.lib.utils import check_required_parameter
11+
from binance.lib.authentication import hmac_hashing, rsa_signature
1412

1513

1614
class API(object):
@@ -33,13 +31,17 @@ def __init__(
3331
proxies=None,
3432
show_limit_usage=False,
3533
show_header=False,
34+
private_key=None,
35+
private_key_passphrase=None,
3636
):
3737
self.key = key
3838
self.secret = secret
3939
self.timeout = timeout
4040
self.show_limit_usage = False
4141
self.show_header = False
4242
self.proxies = None
43+
self.private_key = private_key
44+
self.private_key_pass = private_key_passphrase
4345
self.session = requests.Session()
4446
self.session.headers.update(
4547
{
@@ -77,8 +79,7 @@ def sign_request(self, http_method, url_path, payload=None, special=False):
7779
payload = {}
7880
payload["timestamp"] = get_timestamp()
7981
query_string = self._prepare_params(payload, special)
80-
signature = self._get_sign(query_string)
81-
payload["signature"] = signature
82+
payload["signature"] = self._get_sign(query_string)
8283
return self.send_request(http_method, url_path, payload, special)
8384

8485
def limited_encoded_sign_request(self, http_method, url_path, payload=None):
@@ -94,8 +95,9 @@ def limited_encoded_sign_request(self, http_method, url_path, payload=None):
9495
payload = {}
9596
payload["timestamp"] = get_timestamp()
9697
query_string = self._prepare_params(payload)
97-
signature = self._get_sign(query_string)
98-
url_path = url_path + "?" + query_string + "&signature=" + signature
98+
url_path = (
99+
url_path + "?" + query_string + "&signature=" + self._get_sign(query_string)
100+
)
99101
return self.send_request(http_method, url_path)
100102

101103
def send_request(self, http_method, url_path, payload=None, special=False):
@@ -145,9 +147,10 @@ def send_request(self, http_method, url_path, payload=None, special=False):
145147
def _prepare_params(self, params, special=False):
146148
return encoded_string(cleanNoneValue(params), special)
147149

148-
def _get_sign(self, data):
149-
m = hmac.new(self.secret.encode("utf-8"), data.encode("utf-8"), hashlib.sha256)
150-
return m.hexdigest()
150+
def _get_sign(self, payload):
151+
if self.private_key:
152+
return rsa_signature(self.private_key, payload, self.private_key_pass)
153+
return hmac_hashing(self.secret, payload)
151154

152155
def _dispatch_request(self, http_method):
153156
return {

binance/lib/authentication.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import hmac
2+
import hashlib
3+
from base64 import b64encode
4+
from Crypto.PublicKey import RSA
5+
from Crypto.Hash import SHA256
6+
from Crypto.Signature import pkcs1_15
7+
8+
9+
def hmac_hashing(secret, payload):
10+
m = hmac.new(secret.encode("utf-8"), payload.encode("utf-8"), hashlib.sha256)
11+
return m.hexdigest()
12+
13+
14+
def rsa_signature(private_key, payload, private_key_pass=None):
15+
private_key = RSA.import_key(private_key, passphrase=private_key_pass)
16+
h = SHA256.new(payload.encode("utf-8"))
17+
signature = pkcs1_15.new(private_key).sign(h)
18+
return b64encode(signature)

examples/cm_futures/trade/get_account.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,22 @@
66

77
config_logging(logging, logging.DEBUG)
88

9+
# HMAC authentication with API key and secret
910
key = ""
1011
secret = ""
1112

12-
client = Client(key, secret)
13+
hmac_client = Client(key=key, secret=secret)
14+
logging.info(hmac_client.account(recvWindow=6000))
15+
16+
# RSA authentication with RSA key
17+
key = ""
18+
with open("/Users/john/private_key.pem", "r") as f:
19+
private_key = f.read()
20+
21+
rsa_client = Client(key=key, private_key=private_key)
1322

1423
try:
15-
response = client.account(recvWindow=6000)
24+
response = rsa_client.account(recvWindow=6000)
1625
logging.info(response)
1726
except ClientError as error:
1827
logging.error(

examples/um_futures/trade/get_account.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,22 @@
66

77
config_logging(logging, logging.DEBUG)
88

9+
# HMAC authentication with API key and secret
910
key = ""
1011
secret = ""
1112

12-
um_futures_client = UMFutures(key=key, secret=secret)
13+
hmac_client = UMFutures(key=key, secret=secret)
14+
logging.info(hmac_client.account(recvWindow=6000))
15+
16+
# RSA authentication with RSA key
17+
key = ""
18+
with open("/Users/john/private_key.pem", "r") as f:
19+
private_key = f.read()
20+
21+
rsa_client = UMFutures(key=key, private_key=private_key)
1322

1423
try:
15-
response = um_futures_client.account(recvWindow=6000)
24+
response = rsa_client.account(recvWindow=6000)
1625
logging.info(response)
1726
except ClientError as error:
1827
logging.error(

requirements/common.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ autobahn>=21.2.1
22
Twisted>=22.2.0
33
requests>=2.25.1
44
pyOpenSSL>=19.0.0
5-
service-identity>=21.1.0
5+
service-identity>=21.1.0
6+
pycryptodome>=3.15.0

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@
4141
"Intended Audience :: Developers",
4242
"Intended Audience :: Financial and Insurance Industry",
4343
"License :: OSI Approved :: MIT License",
44-
"Programming Language :: Python :: 3.6",
4544
"Programming Language :: Python :: 3.7",
4645
"Programming Language :: Python :: 3.8",
4746
"Programming Language :: Python :: 3.9",
47+
"Programming Language :: Python :: 3.10",
4848
],
49-
python_requires=">=3.6",
49+
python_requires=">=3.7",
5050
)

0 commit comments

Comments
 (0)