9
9
from os .path import basename , exists
10
10
from time import sleep
11
11
from typing import Any , Dict , List , Tuple
12
+ from functools import lru_cache
12
13
13
14
import requests
14
15
import udatetime
@@ -162,7 +163,9 @@ def __init__(
162
163
# price.log service
163
164
self .price_log_service : str = config ["PRICE_LOG_SERVICE_URL" ]
164
165
165
- def extract_order_data (self , order_details , coin ) -> Dict [str , Any ]:
166
+ def extract_order_data (
167
+ self , order_details , coin
168
+ ) -> Tuple [bool , Dict [str , Any ]]:
166
169
"""calculate average price and volume for a buy order"""
167
170
168
171
# Each order will be fullfilled by different traders, and made of
@@ -183,13 +186,20 @@ def extract_order_data(self, order_details, coin) -> Dict[str, Any]:
183
186
184
187
avg : float = total / qty
185
188
186
- volume : float = float (self .calculate_volume_size (coin ))
187
- logging .debug (f"{ coin .symbol } -> volume:{ volume } avgPrice:{ avg } " )
189
+ ok , _volume = self .calculate_volume_size (coin )
190
+ if ok :
191
+ volume : float = float (_volume )
188
192
189
- return {
190
- "avgPrice" : float (avg ),
191
- "volume" : float (volume ),
192
- }
193
+ logging .debug (f"{ coin .symbol } -> volume:{ volume } avgPrice:{ avg } " )
194
+
195
+ return (
196
+ True ,
197
+ {
198
+ "avgPrice" : float (avg ),
199
+ "volume" : float (volume ),
200
+ },
201
+ )
202
+ return (False , {})
193
203
194
204
def run_strategy (self , coin ) -> None :
195
205
"""runs a specific strategy against a coin"""
@@ -329,13 +339,17 @@ def place_sell_order(self, coin):
329
339
orders = self .client .get_all_orders (symbol = coin .symbol , limit = 1 )
330
340
logging .debug (orders )
331
341
# calculate how much we got based on the total lines in our order
332
- coin .price = self .extract_order_data (order_details , coin )[
333
- "avgPrice"
334
- ]
342
+ ok , _value = self .extract_order_data (order_details , coin )
343
+ if not ok :
344
+ return False
345
+
346
+ coin .price = _value ["avgPrice" ]
335
347
# retrieve the total number of units for this coin
336
- coin .volume = self .extract_order_data (order_details , coin )[
337
- "volume"
338
- ]
348
+ ok , _value = self .extract_order_data (order_details , coin )
349
+ if not ok :
350
+ return False
351
+
352
+ coin .volume = _value ["volume" ]
339
353
340
354
# and give this coin a new fresh date based on our recent actions
341
355
coin .date = float (udatetime .now ().timestamp ())
@@ -448,13 +462,15 @@ def place_buy_order(self, coin, volume):
448
462
logging .debug (orders )
449
463
# our order will have been fullfilled by different traders,
450
464
# find out the average price we paid accross all these sales.
451
- coin .bought_at = self .extract_order_data (order_details , coin )[
452
- "avgPrice"
453
- ]
465
+ ok , _value = self .extract_order_data (order_details , coin )
466
+ if not ok :
467
+ return False
468
+ coin .bought_at = float (_value ["avgPrice" ])
454
469
# retrieve the total number of units for this coin
455
- coin .volume = self .extract_order_data (order_details , coin )[
456
- "volume"
457
- ]
470
+ ok , _volume = self .extract_order_data (order_details , coin )
471
+ if not ok :
472
+ return False
473
+ coin .volume = float (_volume ["volume" ])
458
474
with open ("log/binance.place_buy_order.log" , "at" ) as f :
459
475
f .write (f"{ coin .symbol } { coin .date } { self .order_type } " )
460
476
f .write (f"{ bid } { coin .volume } { order_details } \n " )
@@ -477,7 +493,10 @@ def buy_coin(self, coin) -> bool:
477
493
478
494
# calculate how many units of this coin we can afford based on our
479
495
# investment share.
480
- volume = float (self .calculate_volume_size (coin ))
496
+ ok , _volume = self .calculate_volume_size (coin )
497
+ if not ok :
498
+ return False
499
+ volume : float = float (_volume )
481
500
482
501
# we never place binance orders in backtesting mode.
483
502
if self .mode in ["testnet" , "live" ]:
@@ -612,7 +631,8 @@ def sell_coin(self, coin) -> bool:
612
631
)
613
632
return True
614
633
615
- def get_step_size (self , symbol : str ) -> str :
634
+ @lru_cache (1024 )
635
+ def get_step_size (self , symbol : str ) -> Tuple [bool , str ]:
616
636
"""retrieves and caches the decimal step size for a coin in binance"""
617
637
618
638
# each coin in binance uses a number of decimal points, these can vary
@@ -631,29 +651,42 @@ def get_step_size(self, symbol: str) -> str:
631
651
else :
632
652
try :
633
653
info = self .client .get_symbol_info (symbol )
654
+
655
+ if not info :
656
+ return (False , "" )
657
+
658
+ if "filters" not in info :
659
+ return (False , "" )
634
660
except BinanceAPIException as error_msg :
635
661
logging .error (error_msg )
636
- return str (- 1 )
662
+ if "Too much request weight used;" in str (error_msg ):
663
+ sleep (60 )
664
+ return (False , "" )
637
665
638
666
for d in info ["filters" ]:
639
667
if "filterType" in d .keys ():
640
668
if d ["filterType" ] == "LOT_SIZE" :
641
669
step_size = d ["stepSize" ]
642
670
643
- if self .mode == "backtesting" and not exists (f_path ):
644
- with open (f_path , "w" ) as f :
645
- f .write (json .dumps (info ))
671
+ if self .mode == "backtesting" and not exists (f_path ):
672
+ with open (f_path , "w" ) as f :
673
+ f .write (json .dumps (info ))
646
674
647
- with open ("log/binance.step_size.log" , "at" ) as f :
648
- f .write (f"{ symbol } { step_size } \n " )
649
- return step_size
675
+ with open ("log/binance.step_size.log" , "at" ) as f :
676
+ f .write (f"{ symbol } { step_size } \n " )
677
+ return (True , step_size )
678
+ return (False , "" )
650
679
651
- def calculate_volume_size (self , coin ) -> float :
680
+ def calculate_volume_size (self , coin ) -> Tuple [ bool , float ] :
652
681
"""calculates the amount of coin we are to buy"""
653
682
654
683
# calculates the number of units we are about to buy based on the number
655
684
# of decimal points used, the share of the investment and the price
656
- step_size : str = self .get_step_size (coin .symbol )
685
+ ok , _step_size = self .get_step_size (coin .symbol )
686
+ if ok :
687
+ step_size : str = _step_size
688
+ else :
689
+ return (False , 0 )
657
690
658
691
investment : float = percent (self .investment , self .re_invest_percentage )
659
692
@@ -667,7 +700,7 @@ def calculate_volume_size(self, coin) -> float:
667
700
)
668
701
with open ("log/binance.volume.log" , "at" ) as f :
669
702
f .write (f"{ coin .symbol } { step_size } { investment } { volume } \n " )
670
- return volume
703
+ return ( True , volume )
671
704
672
705
@retry (wait = wait_exponential (multiplier = 1 , max = 3 ))
673
706
def get_binance_prices (self ) -> List [Dict [str , str ]]:
0 commit comments