Skip to content

Commit c413b44

Browse files
authored
Merge pull request #32 from EtWnn/develop
Release 0.1.2
2 parents 8648977 + 8a28a5f commit c413b44

File tree

6 files changed

+198
-59
lines changed

6 files changed

+198
-59
lines changed

BinanceWatch/BinanceManager.py

Lines changed: 169 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import datetime
22
import math
33
import time
4-
from typing import Optional
4+
from typing import Optional, Dict
55

66
import dateparser
77
from binance.client import Client
8+
from binance.exceptions import BinanceAPIException
89
from tqdm import tqdm
910

1011
from BinanceWatch.storage import tables
12+
from BinanceWatch.utils.LoggerGenerator import LoggerGenerator
1113
from BinanceWatch.utils.time_utils import datetime_to_millistamp
1214
from BinanceWatch.storage.BinanceDataBase import BinanceDataBase
1315

@@ -16,10 +18,24 @@ class BinanceManager:
1618
"""
1719
This class is in charge of filling the database by calling the binance API
1820
"""
21+
API_MAX_RETRY = 3
1922

20-
def __init__(self, api_key: str, api_secret: str):
21-
self.db = BinanceDataBase()
23+
def __init__(self, api_key: str, api_secret: str, account_name: str = 'default'):
24+
"""
25+
initialise the binance manager.
26+
27+
:param api_key: key for the Binance api
28+
:type api_key: str
29+
:param api_secret: secret for the Binance api
30+
:type api_secret: str
31+
:param account_name: if you have several accounts to monitor, you need to give them different names or the
32+
database will collide
33+
:type account_name: str
34+
"""
35+
self.account_name = account_name
36+
self.db = BinanceDataBase(name=f"{self.account_name}_db")
2237
self.client = Client(api_key=api_key, api_secret=api_secret)
38+
self.logger = LoggerGenerator.get_logger(f"BinanceManager_{self.account_name}")
2339

2440
def update_spot(self):
2541
"""
@@ -87,10 +103,14 @@ def update_universal_transfers(self, transfer_filter: Optional[str] = None):
87103
latest_time = self.db.get_last_universal_transfer_time(transfer_type=transfer_type) + 1
88104
current = 1
89105
while True:
90-
universal_transfers = self.client.query_universal_transfer_history(type=transfer_type,
91-
startTime=latest_time,
92-
current=current,
93-
size=100)
106+
client_params = {
107+
'type': transfer_type,
108+
'startTime': latest_time,
109+
'current': current,
110+
'size': 100
111+
}
112+
universal_transfers = self._call_binance_client('query_universal_transfer_history', client_params)
113+
94114
try:
95115
universal_transfers = universal_transfers['rows']
96116
except KeyError:
@@ -134,8 +154,15 @@ def update_cross_margin_interests(self):
134154
'size': 100,
135155
'archived': archived
136156
}
157+
137158
# no built-in method yet in python-binance for margin/interestHistory
138-
interests = self.client._request_margin_api('get', 'margin/interestHistory', signed=True, data=params)
159+
client_params = {
160+
'method': 'get',
161+
'path': 'margin/interestHistory',
162+
'signed': True,
163+
'data': params
164+
}
165+
interests = self._call_binance_client('_request_margin_api', client_params)
139166

140167
for interest in interests['rows']:
141168
self.db.add_margin_interest(margin_type=margin_type,
@@ -164,7 +191,12 @@ def update_cross_margin_repays(self):
164191
:return: None
165192
:rtype: None
166193
"""
167-
symbols_info = self.client._request_margin_api('get', 'margin/allPairs', data={}) # not built-in yet
194+
client_params = {
195+
'method': 'get',
196+
'path': 'margin/allPairs',
197+
'data': {}
198+
}
199+
symbols_info = self._call_binance_client('_request_margin_api', client_params) # not built-in yet
168200
assets = set()
169201
for symbol_info in symbols_info:
170202
assets.add(symbol_info['base'])
@@ -197,12 +229,16 @@ def update_margin_asset_repay(self, asset: str, isolated_symbol=''):
197229
archived = 1000 * time.time() - latest_time > 1000 * 3600 * 24 * 30 * 3
198230
current = 1
199231
while True:
200-
repays = self.client.get_margin_repay_details(asset=asset,
201-
current=current,
202-
startTime=latest_time + 1000,
203-
archived=archived,
204-
isolatedSymbol=isolated_symbol,
205-
size=100)
232+
client_params = {
233+
'asset': asset,
234+
'current':current,
235+
'startTime': latest_time + 1000,
236+
'archived': archived,
237+
'isolatedSymbol': isolated_symbol,
238+
'size': 100
239+
}
240+
repays = self._call_binance_client('get_margin_repay_details', client_params)
241+
206242
for repay in repays['rows']:
207243
if repay['status'] == 'CONFIRMED':
208244
self.db.add_repay(margin_type=margin_type,
@@ -230,7 +266,12 @@ def update_cross_margin_loans(self):
230266
:return: None
231267
:rtype: None
232268
"""
233-
symbols_info = self.client._request_margin_api('get', 'margin/allPairs', data={}) # not built-in yet
269+
client_params = {
270+
'method': 'get',
271+
'path': 'margin/allPairs',
272+
'data': {}
273+
}
274+
symbols_info = self._call_binance_client('_request_margin_api', client_params) # not built-in yet
234275
assets = set()
235276
for symbol_info in symbols_info:
236277
assets.add(symbol_info['base'])
@@ -263,12 +304,16 @@ def update_margin_asset_loans(self, asset: str, isolated_symbol=''):
263304
archived = 1000 * time.time() - latest_time > 1000 * 3600 * 24 * 30 * 3
264305
current = 1
265306
while True:
266-
loans = self.client.get_margin_loan_details(asset=asset,
267-
current=current,
268-
startTime=latest_time + 1000,
269-
archived=archived,
270-
isolatedSymbol=isolated_symbol,
271-
size=100)
307+
client_params = {
308+
'asset': asset,
309+
'current': current,
310+
'startTime': latest_time + 1000,
311+
'archived': archived,
312+
'isolatedSymbol': isolated_symbol,
313+
'size': 100
314+
}
315+
loans = self._call_binance_client('get_margin_loan_details', client_params)
316+
272317
for loan in loans['rows']:
273318
if loan['status'] == 'CONFIRMED':
274319
self.db.add_loan(margin_type=margin_type,
@@ -310,7 +355,13 @@ def update_cross_margin_symbol_trades(self, asset: str, ref_asset: str, limit: i
310355
symbol = asset + ref_asset
311356
last_trade_id = self.db.get_max_trade_id(asset, ref_asset, 'cross_margin')
312357
while True:
313-
new_trades = self.client.get_margin_trades(symbol=symbol, fromId=last_trade_id + 1, limit=limit)
358+
client_params = {
359+
'symbol': symbol,
360+
'fromId': last_trade_id + 1,
361+
'limit': limit
362+
}
363+
new_trades = self._call_binance_client('get_margin_trades', client_params)
364+
314365
for trade in new_trades:
315366
self.db.add_trade(trade_type='cross_margin',
316367
trade_id=int(trade['id']),
@@ -339,7 +390,13 @@ def update_all_cross_margin_trades(self, limit: int = 1000):
339390
:return: None
340391
:rtype: None
341392
"""
342-
symbols_info = self.client._request_margin_api('get', 'margin/allPairs', data={}) # not built-in yet
393+
client_params = {
394+
'method': 'get',
395+
'path': 'margin/allPairs',
396+
'data': {}
397+
}
398+
symbols_info = self._call_binance_client('_request_margin_api', client_params) # not built-in yet
399+
343400
pbar = tqdm(total=len(symbols_info))
344401
for symbol_info in symbols_info:
345402
pbar.set_description(f"fetching {symbol_info['symbol']} cross margin trades")
@@ -367,10 +424,14 @@ def update_lending_redemptions(self):
367424
latest_time = self.db.get_last_lending_redemption_time(lending_type=lending_type) + 1
368425
current = 1
369426
while True:
370-
lending_redemptions = self.client.get_lending_redemption_history(lendingType=lending_type,
371-
startTime=latest_time,
372-
current=current,
373-
size=100)
427+
client_params = {
428+
'lendingType': lending_type,
429+
'startTime': latest_time,
430+
'current': current,
431+
'size': 100
432+
}
433+
lending_redemptions = self._call_binance_client('get_lending_redemption_history', client_params)
434+
374435
for li in lending_redemptions:
375436
if li['status'] == 'PAID':
376437
self.db.add_lending_redemption(redemption_time=li['createTime'],
@@ -405,10 +466,14 @@ def update_lending_purchases(self):
405466
latest_time = self.db.get_last_lending_purchase_time(lending_type=lending_type) + 1
406467
current = 1
407468
while True:
408-
lending_purchases = self.client.get_lending_purchase_history(lendingType=lending_type,
409-
startTime=latest_time,
410-
current=current,
411-
size=100)
469+
client_params = {
470+
'lendingType': lending_type,
471+
'startTime': latest_time,
472+
'current': current,
473+
'size': 100
474+
}
475+
lending_purchases = self._call_binance_client('get_lending_purchase_history', client_params)
476+
412477
for li in lending_purchases:
413478
if li['status'] == 'SUCCESS':
414479
self.db.add_lending_purchase(purchase_id=li['purchaseId'],
@@ -444,10 +509,14 @@ def update_lending_interests(self):
444509
latest_time = self.db.get_last_lending_interest_time(lending_type=lending_type) + 3600 * 1000 # add 1 hour
445510
current = 1
446511
while True:
447-
lending_interests = self.client.get_lending_interest_history(lendingType=lending_type,
448-
startTime=latest_time,
449-
current=current,
450-
size=100)
512+
client_params = {
513+
'lendingType': lending_type,
514+
'startTime': latest_time,
515+
'current': current,
516+
'size': 100
517+
}
518+
lending_interests = self._call_binance_client('get_lending_interest_history', client_params)
519+
451520
for li in lending_interests:
452521
self.db.add_lending_interest(time=li['time'],
453522
lending_type=li['lendingType'],
@@ -477,7 +546,7 @@ def update_spot_dusts(self):
477546
"""
478547
self.db.drop_table(tables.SPOT_DUST_TABLE)
479548

480-
result = self.client.get_dust_log()
549+
result = self._call_binance_client('get_dust_log')
481550
dusts = result['results']
482551
pbar = tqdm(total=dusts['total'])
483552
pbar.set_description("fetching spot dusts")
@@ -517,18 +586,21 @@ def update_spot_dividends(self, day_jump: float = 90, limit: int = 500):
517586
pbar = tqdm(total=math.ceil((now_millistamp - start_time) / delta_jump))
518587
pbar.set_description("fetching spot dividends")
519588
while start_time < now_millistamp:
589+
# the stable working version of client.get_asset_dividend_history is not released yet,
590+
# for now it has a post error, so this protected member is used in the meantime
520591
params = {
521592
'startTime': start_time,
522593
'endTime': start_time + delta_jump,
523594
'limit': limit
524595
}
525-
# the stable working version of client.get_asset_dividend_history is not released yet,
526-
# for now it has a post error, so this protected member is used in the meantime
527-
result = self.client._request_margin_api('get',
528-
'asset/assetDividend',
529-
True,
530-
data=params
531-
)
596+
client_params = {
597+
'method': 'get',
598+
'path': 'asset/assetDividend',
599+
'signed': True,
600+
'data': params
601+
}
602+
result = self._call_binance_client('_request_margin_api', client_params)
603+
532604
dividends = result['rows']
533605
for div in dividends:
534606
self.db.add_dividend(div_id=int(div['tranId']),
@@ -568,7 +640,13 @@ def update_spot_withdraws(self, day_jump: float = 90):
568640
pbar = tqdm(total=math.ceil((now_millistamp - start_time) / delta_jump))
569641
pbar.set_description("fetching spot withdraws")
570642
while start_time < now_millistamp:
571-
result = self.client.get_withdraw_history(startTime=start_time, endTime=start_time + delta_jump, status=6)
643+
client_params = {
644+
'startTime': start_time,
645+
'endTime': start_time + delta_jump,
646+
'status': 6
647+
}
648+
result = self._call_binance_client('get_withdraw_history', client_params)
649+
572650
withdraws = result['withdrawList']
573651
for withdraw in withdraws:
574652
self.db.add_withdraw(withdraw_id=withdraw['id'],
@@ -607,7 +685,13 @@ def update_spot_deposits(self, day_jump: float = 90):
607685
pbar = tqdm(total=math.ceil((now_millistamp - start_time) / delta_jump))
608686
pbar.set_description("fetching spot deposits")
609687
while start_time < now_millistamp:
610-
result = self.client.get_deposit_history(startTime=start_time, endTime=start_time + delta_jump, status=1)
688+
client_params = {
689+
'startTime': start_time,
690+
'endTime': start_time + delta_jump,
691+
'status': 1
692+
}
693+
result = self._call_binance_client('get_deposit_history', client_params)
694+
611695
deposits = result['depositList']
612696
for deposit in deposits:
613697
self.db.add_deposit(tx_id=deposit['txId'],
@@ -643,7 +727,13 @@ def update_spot_symbol_trades(self, asset: str, ref_asset: str, limit: int = 100
643727
symbol = asset + ref_asset
644728
last_trade_id = self.db.get_max_trade_id(asset, ref_asset, 'spot')
645729
while True:
646-
new_trades = self.client.get_my_trades(symbol=symbol, fromId=last_trade_id + 1, limit=limit)
730+
client_params = {
731+
'symbol': symbol,
732+
'fromId': last_trade_id + 1,
733+
'limit': limit
734+
}
735+
new_trades = self._call_binance_client('get_my_trades', client_params)
736+
647737
for trade in new_trades:
648738
self.db.add_trade(trade_type='spot',
649739
trade_id=int(trade['id']),
@@ -681,3 +771,35 @@ def update_all_spot_trades(self, limit: int = 1000):
681771
limit=limit)
682772
pbar.update()
683773
pbar.close()
774+
775+
def _call_binance_client(self, method_name: str, params: Optional[Dict] = None, retry_count: int = 0):
776+
"""
777+
This method is used to handle rate limits: if a rate limits is breached, it will wait the necessary time
778+
to call again the API.
779+
780+
:param method_name: name of the method binance.Client to call
781+
:type method_name: str
782+
:param params: parameters to pass to the above method
783+
:type params: Dict
784+
:param retry_count: internal use only to count the number of retry if rate limits are breached
785+
:type retry_count: int
786+
:return: response of binance.Client method
787+
:rtype: Dict
788+
"""
789+
if params is None:
790+
params = dict()
791+
if retry_count >= BinanceManager.API_MAX_RETRY:
792+
raise RuntimeError(f"The API rate limits has been breached {retry_count} times")
793+
794+
try:
795+
return getattr(self.client, method_name)(**params)
796+
except BinanceAPIException as err:
797+
if err.code == -1003: # API rate Limits
798+
wait_time = float(err.response.headers['Retry-After'])
799+
if err.response.status_code == 418: # ban
800+
self.logger.error(f"API calls resulted in a ban, retry in {wait_time} seconds")
801+
raise err
802+
self.logger.info(f"API calls resulted in a breach of rate limits, will retry after {wait_time} seconds")
803+
time.sleep(wait_time + 1)
804+
return self._call_binance_client(method_name, params, retry_count + 1)
805+
raise err

BinanceWatch/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version__ = "0.1.1"
1+
__version__ = "0.1.2"
22
__author__ = 'EtWnn'

README.rst

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
==============================
2-
Welcome to BinanceWatch v0.1.1
3-
==============================
1+
===================================
2+
Welcome to BinanceWatch v0.1.2
3+
===================================
44

55
Note
66
----
@@ -62,6 +62,14 @@ permissions are needed.
6262
6363
pip install BinanceWatch
6464
65+
If you prefer to install the latest developments use:
66+
67+
.. code:: bash
68+
69+
pip install git+https://github.com/EtWnn/BinanceWatch.git@develop
70+
71+
Use your Binance api keys to initiate the manager:
72+
6573
.. code:: python
6674
6775
from BinanceWatch.BinanceManager import BinanceManager

0 commit comments

Comments
 (0)