3030
3131logger = logging .getLogger (__name__ )
3232
33+
34+ def _ccxt_retry (method ):
35+ """Module-level retry decorator for CCXTStore methods."""
36+
37+ @wraps (method )
38+ def retry_method (self , * args , ** kwargs ):
39+ for i in range (self .retries ):
40+ if self .debug :
41+ logger .debug ("%s - %s - Attempt %d" , datetime .now (), method .__name__ , i )
42+
43+ if self ._rate_limiter :
44+ self ._rate_limiter .acquire ()
45+ else :
46+ time .sleep (self .exchange .rateLimit / 1000 )
47+
48+ try :
49+ result = method (self , * args , ** kwargs )
50+ if self ._rate_limiter and hasattr (self ._rate_limiter , "on_success" ):
51+ self ._rate_limiter .on_success ()
52+ if self ._connection_manager :
53+ self ._connection_manager .mark_success ()
54+ return result
55+ except (NetworkError , ExchangeError ) as e :
56+ if self ._rate_limiter and hasattr (self ._rate_limiter , "on_rate_limit_error" ):
57+ if "rate" in str (e ).lower () or "429" in str (e ):
58+ self ._rate_limiter .on_rate_limit_error ()
59+ if self ._connection_manager :
60+ self ._connection_manager .mark_failure ()
61+ if i == self .retries - 1 :
62+ raise
63+
64+ return retry_method
65+
3366# TimeFrame constants to avoid circular import with backtrader
3467# Values match backtrader.dataseries.TimeFrame
3568_TF_MINUTES = 4
@@ -239,43 +272,7 @@ def get_granularity(self, timeframe, compression):
239272
240273 return granularity
241274
242- @staticmethod
243- def retry (method ):
244- """Decorator for retry with rate limiting."""
245-
246- @wraps (method )
247- def retry_method (self , * args , ** kwargs ):
248- for i in range (self .retries ):
249- if self .debug :
250- logger .debug ("%s - %s - Attempt %d" , datetime .now (), method .__name__ , i )
251-
252- # Use rate limiter if available, otherwise fall back to basic sleep
253- if self ._rate_limiter :
254- self ._rate_limiter .acquire ()
255- else :
256- time .sleep (self .exchange .rateLimit / 1000 )
257-
258- try :
259- result = method (self , * args , ** kwargs )
260- # Mark success for adaptive rate limiter
261- if self ._rate_limiter and hasattr (self ._rate_limiter , "on_success" ):
262- self ._rate_limiter .on_success ()
263- if self ._connection_manager :
264- self ._connection_manager .mark_success ()
265- return result
266- except (NetworkError , ExchangeError ) as e :
267- # Mark failure for adaptive rate limiter
268- if self ._rate_limiter and hasattr (self ._rate_limiter , "on_rate_limit_error" ):
269- if "rate" in str (e ).lower () or "429" in str (e ):
270- self ._rate_limiter .on_rate_limit_error ()
271- if self ._connection_manager :
272- self ._connection_manager .mark_failure ()
273- if i == self .retries - 1 :
274- raise
275-
276- return retry_method
277-
278- @retry
275+ @_ccxt_retry
279276 def get_wallet_balance (self , params = None ):
280277 """Fetch the wallet balance from the exchange.
281278
@@ -290,7 +287,7 @@ def get_wallet_balance(self, params=None):
290287 balance = self .exchange .fetch_balance (params )
291288 return balance
292289
293- @retry
290+ @_ccxt_retry
294291 def get_balance (self ):
295292 """Fetch and update the current balance from the exchange.
296293
@@ -305,7 +302,7 @@ def get_balance(self):
305302 self ._cash = cash if cash else 0
306303 self ._value = value if value else 0
307304
308- @retry
305+ @_ccxt_retry
309306 def getposition (self ):
310307 """Get the current position value.
311308
@@ -314,7 +311,7 @@ def getposition(self):
314311 """
315312 return self ._value
316313
317- @retry
314+ @_ccxt_retry
318315 def create_order (self , symbol , order_type , side , amount , price , params ):
319316 """Create an order on the exchange.
320317
@@ -334,7 +331,7 @@ def create_order(self, symbol, order_type, side, amount, price, params):
334331 symbol = symbol , type = order_type , side = side , amount = amount , price = price , params = params
335332 )
336333
337- @retry
334+ @_ccxt_retry
338335 def cancel_order (self , order_id , symbol ):
339336 """Cancel an existing order on the exchange.
340337
@@ -347,7 +344,7 @@ def cancel_order(self, order_id, symbol):
347344 """
348345 return self .exchange .cancel_order (order_id , symbol )
349346
350- @retry
347+ @_ccxt_retry
351348 def fetch_trades (self , symbol ):
352349 """Fetch recent trades for a symbol from the exchange.
353350
@@ -359,7 +356,7 @@ def fetch_trades(self, symbol):
359356 """
360357 return self .exchange .fetch_trades (symbol )
361358
362- @retry
359+ @_ccxt_retry
363360 def fetch_ohlcv (self , symbol , timeframe , since , limit , params = None ):
364361 """Fetch OHLCV (candlestick) data from the exchange.
365362
@@ -382,7 +379,7 @@ def fetch_ohlcv(self, symbol, timeframe, since, limit, params=None):
382379 symbol , timeframe = timeframe , since = since , limit = limit , params = params
383380 )
384381
385- @retry
382+ @_ccxt_retry
386383 def fetch_order (self , oid , symbol ):
387384 """Fetch details of a specific order from the exchange.
388385
@@ -395,7 +392,7 @@ def fetch_order(self, oid, symbol):
395392 """
396393 return self .exchange .fetch_order (oid , symbol )
397394
398- @retry
395+ @_ccxt_retry
399396 def fetch_open_orders (self ):
400397 """Fetch all open orders from the exchange.
401398
@@ -404,7 +401,7 @@ def fetch_open_orders(self):
404401 """
405402 return self .exchange .fetchOpenOrders ()
406403
407- @retry
404+ @_ccxt_retry
408405 def private_end_point (self , type , endpoint , params ):
409406 """Call any private endpoint on the exchange.
410407
0 commit comments