修复 load_markets 超时后狂打交易所:失败退避 90 秒,避免反复请求导致封 IP。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -490,6 +490,18 @@ if BINANCE_API_KEY and BINANCE_API_SECRET:
|
||||
exchange.apiKey = BINANCE_API_KEY
|
||||
exchange.secret = BINANCE_API_SECRET
|
||||
MARKETS_LOADED = False
|
||||
_MARKETS_GUARD = None
|
||||
|
||||
|
||||
def _get_markets_guard():
|
||||
global _MARKETS_GUARD
|
||||
if _MARKETS_GUARD is None:
|
||||
from lib.trade.markets_load_guard_lib import MarketsLoadGuard
|
||||
|
||||
_MARKETS_GUARD = MarketsLoadGuard(backoff_sec=90.0, label="binance markets")
|
||||
return _MARKETS_GUARD
|
||||
|
||||
|
||||
ACCOUNT_BALANCE_CACHE = {
|
||||
"updated_at": 0.0,
|
||||
"funding_usdt": None,
|
||||
@@ -3583,10 +3595,16 @@ def calc_trend_manual_breakeven_stop(direction, entry_price, offset_pct=None):
|
||||
|
||||
|
||||
def ensure_markets_loaded(force=False):
|
||||
"""加载 Binance markets;失败后退避,避免超时循环狂打 API."""
|
||||
global MARKETS_LOADED
|
||||
if force or not MARKETS_LOADED:
|
||||
exchange.load_markets(reload=force)
|
||||
guard = _get_markets_guard()
|
||||
guard.loaded = bool(MARKETS_LOADED)
|
||||
try:
|
||||
guard.ensure(lambda: exchange.load_markets(reload=force), force=force)
|
||||
MARKETS_LOADED = True
|
||||
except Exception:
|
||||
MARKETS_LOADED = False
|
||||
raise
|
||||
|
||||
|
||||
def _abort_market_open_after_tpsl_failure(exchange_symbol, direction, order, planned_amount):
|
||||
|
||||
@@ -478,6 +478,18 @@ if GATE_API_KEY and GATE_API_SECRET:
|
||||
exchange.apiKey = GATE_API_KEY
|
||||
exchange.secret = GATE_API_SECRET
|
||||
MARKETS_LOADED = False
|
||||
_MARKETS_GUARD = None
|
||||
|
||||
|
||||
def _get_markets_guard():
|
||||
global _MARKETS_GUARD
|
||||
if _MARKETS_GUARD is None:
|
||||
from lib.trade.markets_load_guard_lib import MarketsLoadGuard
|
||||
|
||||
_MARKETS_GUARD = MarketsLoadGuard(backoff_sec=90.0, label="gate markets")
|
||||
return _MARKETS_GUARD
|
||||
|
||||
|
||||
ACCOUNT_BALANCE_CACHE = {
|
||||
"updated_at": 0.0,
|
||||
"funding_usdt": None,
|
||||
@@ -3376,10 +3388,16 @@ def calc_trend_manual_breakeven_stop(direction, entry_price, offset_pct=None):
|
||||
|
||||
|
||||
def ensure_markets_loaded(force=False):
|
||||
"""加载 Gate markets;失败后退避,避免超时循环狂打 API 导致封 IP."""
|
||||
global MARKETS_LOADED
|
||||
if force or not MARKETS_LOADED:
|
||||
exchange.load_markets(reload=force)
|
||||
guard = _get_markets_guard()
|
||||
guard.loaded = bool(MARKETS_LOADED)
|
||||
try:
|
||||
guard.ensure(lambda: exchange.load_markets(reload=force), force=force)
|
||||
MARKETS_LOADED = True
|
||||
except Exception:
|
||||
MARKETS_LOADED = False
|
||||
raise
|
||||
|
||||
|
||||
def _abort_market_open_after_tpsl_failure(exchange_symbol, direction, order, planned_amount):
|
||||
|
||||
@@ -506,6 +506,18 @@ if OKX_API_KEY and OKX_API_SECRET and OKX_API_PASSPHRASE:
|
||||
exchange_options.password = OKX_API_PASSPHRASE
|
||||
|
||||
MARKETS_LOADED = False
|
||||
_MARKETS_GUARD = None
|
||||
|
||||
|
||||
def _get_markets_guard():
|
||||
global _MARKETS_GUARD
|
||||
if _MARKETS_GUARD is None:
|
||||
from lib.trade.markets_load_guard_lib import MarketsLoadGuard
|
||||
|
||||
_MARKETS_GUARD = MarketsLoadGuard(backoff_sec=90.0, label="okx markets")
|
||||
return _MARKETS_GUARD
|
||||
|
||||
|
||||
ACCOUNT_BALANCE_CACHE = {
|
||||
"updated_at": 0.0,
|
||||
"funding_usdt": None,
|
||||
@@ -2867,10 +2879,16 @@ def build_okx_order_params(direction, reduce_only=False):
|
||||
|
||||
|
||||
def ensure_markets_loaded(force=False):
|
||||
"""加载 OKX markets;失败后退避,避免超时循环狂打 API."""
|
||||
global MARKETS_LOADED
|
||||
if force or not MARKETS_LOADED:
|
||||
exchange.load_markets(reload=force)
|
||||
guard = _get_markets_guard()
|
||||
guard.loaded = bool(MARKETS_LOADED)
|
||||
try:
|
||||
guard.ensure(lambda: exchange.load_markets(reload=force), force=force)
|
||||
MARKETS_LOADED = True
|
||||
except Exception:
|
||||
MARKETS_LOADED = False
|
||||
raise
|
||||
|
||||
|
||||
def _okx_algo_trigger_price_str(exchange_symbol, price):
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
"""交易所 load_markets 失败退避:避免超时后每次调用都狂打 API 导致封 IP."""
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
|
||||
class MarketsLoadGuard:
|
||||
def __init__(self, *, backoff_sec: float = 60.0, label: str = "markets") -> None:
|
||||
self.loaded = False
|
||||
self.backoff_sec = float(backoff_sec)
|
||||
self.label = label
|
||||
self._fail_until = 0.0
|
||||
self._last_error: Optional[str] = None
|
||||
|
||||
def ensure(self, load_fn: Callable[[], Any], *, force: bool = False) -> None:
|
||||
if self.loaded and not force:
|
||||
return
|
||||
now = time.time()
|
||||
if not force and now < self._fail_until:
|
||||
wait = max(0.0, self._fail_until - now)
|
||||
raise RuntimeError(
|
||||
f"{self.label} load backing off {wait:.0f}s after: {self._last_error or 'previous failure'}"
|
||||
)
|
||||
try:
|
||||
load_fn()
|
||||
self.loaded = True
|
||||
self._fail_until = 0.0
|
||||
self._last_error = None
|
||||
except Exception as e:
|
||||
self.loaded = False
|
||||
self._last_error = str(e)
|
||||
self._fail_until = now + self.backoff_sec
|
||||
print(
|
||||
f"[{self.label}] load_markets failed; backoff {self.backoff_sec:.0f}s: {e}",
|
||||
flush=True,
|
||||
)
|
||||
raise
|
||||
@@ -0,0 +1,40 @@
|
||||
"""markets load backoff guard."""
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from lib.trade.markets_load_guard_lib import MarketsLoadGuard
|
||||
|
||||
|
||||
class TestMarketsLoadGuard(unittest.TestCase):
|
||||
def test_success_sets_loaded(self):
|
||||
g = MarketsLoadGuard(backoff_sec=60.0, label="t")
|
||||
calls = {"n": 0}
|
||||
|
||||
def load():
|
||||
calls["n"] += 1
|
||||
|
||||
g.ensure(load)
|
||||
self.assertTrue(g.loaded)
|
||||
g.ensure(load)
|
||||
self.assertEqual(calls["n"], 1)
|
||||
|
||||
def test_failure_backs_off(self):
|
||||
g = MarketsLoadGuard(backoff_sec=60.0, label="t")
|
||||
calls = {"n": 0}
|
||||
|
||||
def load():
|
||||
calls["n"] += 1
|
||||
raise TimeoutError("boom")
|
||||
|
||||
with self.assertRaises(TimeoutError):
|
||||
g.ensure(load)
|
||||
self.assertFalse(g.loaded)
|
||||
self.assertEqual(calls["n"], 1)
|
||||
with self.assertRaises(RuntimeError):
|
||||
g.ensure(load)
|
||||
self.assertEqual(calls["n"], 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user