This commit is contained in:
aaron 2026-05-30 22:58:04 +08:00
parent 1e94714234
commit 4d5a3cc235
4 changed files with 139 additions and 35 deletions

View File

@ -97,6 +97,55 @@ def _compact_order(item: dict) -> dict:
} }
def _normalize_symbol(symbol: str) -> str:
value = str(symbol or "").strip().upper()
if value and "/" not in value and value.endswith("USDT"):
value = value[:-4] + "/USDT"
return value
def _order_history_symbols(account: dict, overview: dict) -> list[str]:
"""Build the smallest safe symbol set for Binance order-history queries."""
risk = account.get("risk_config") if isinstance(account.get("risk_config"), dict) else {}
symbols: list[str] = []
for raw in risk.get("allowed_symbols") or []:
symbol = _normalize_symbol(raw)
if symbol:
symbols.append(symbol)
for row in (overview.get("positions") or []) + (overview.get("open_orders") or []):
symbol = _normalize_symbol(row.get("symbol"))
if symbol:
symbols.append(symbol)
for row in overview.get("intent_history") or []:
symbol = _normalize_symbol(row.get("symbol"))
if symbol:
symbols.append(symbol)
result = []
seen = set()
for symbol in symbols:
key = symbol.upper()
if key not in seen:
seen.add(key)
result.append(symbol)
return result[:20]
def _fetch_order_history_by_symbol(client, symbols: list[str], limit: int) -> tuple[list[dict], list[str]]:
orders = []
errors = []
if not symbols:
return orders, errors
per_symbol_limit = max(1, min(int(limit or 30), 50))
for symbol in symbols:
try:
orders.extend(_compact_order(o) for o in client.fetch_orders(symbol, limit=per_symbol_limit))
except Exception as exc:
errors.append(f"订单历史读取失败 {symbol}{exc}")
orders.sort(key=lambda x: str(x.get("timestamp") or ""), reverse=True)
return orders[: max(1, int(limit or 30))], errors
def _account_risk_view(account: dict) -> dict: def _account_risk_view(account: dict) -> dict:
risk = account.get("risk_config") if isinstance(account.get("risk_config"), dict) else {} risk = account.get("risk_config") if isinstance(account.get("risk_config"), dict) else {}
allowed = [str(x).strip().upper() for x in risk.get("allowed_symbols", []) if str(x).strip()] allowed = [str(x).strip().upper() for x in risk.get("allowed_symbols", []) if str(x).strip()]
@ -176,9 +225,9 @@ def get_live_account_overview(account_id: int, *, history_limit: int = 30, refre
overview["open_orders"] = [_compact_order(o) for o in client.fetch_open_orders(None)] overview["open_orders"] = [_compact_order(o) for o in client.fetch_open_orders(None)]
except Exception as exc: except Exception as exc:
overview["errors"].append(f"挂单读取失败:{exc}") overview["errors"].append(f"挂单读取失败:{exc}")
try: symbols = _order_history_symbols(account, overview)
overview["order_history"] = [_compact_order(o) for o in client.fetch_orders(None, limit=history_limit)] order_history, order_history_errors = _fetch_order_history_by_symbol(client, symbols, history_limit)
except Exception as exc: overview["order_history"] = order_history
overview["errors"].append(f"订单历史读取失败:{exc}") overview["errors"].extend(order_history_errors)
overview["exchange_cache"] = {"cached": False, "loaded": True, "requires_refresh": False} overview["exchange_cache"] = {"cached": False, "loaded": True, "requires_refresh": False}
return _cache_overview(account_id, overview) return _cache_overview(account_id, overview)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -161,6 +161,7 @@ def test_live_account_overview_refresh_compacts_position_value_side_and_leverage
return [] return []
def fetch_orders(self, symbol=None, limit=30): def fetch_orders(self, symbol=None, limit=30):
assert symbol == "BTC/USDT"
return [] return []
overview = get_live_account_overview(account["id"], refresh=True, client_factory=lambda account: Client()) overview = get_live_account_overview(account["id"], refresh=True, client_factory=lambda account: Client())
@ -173,6 +174,53 @@ def test_live_account_overview_refresh_compacts_position_value_side_and_leverage
assert pos["leverage_source"] == "account_config" assert pos["leverage_source"] == "account_config"
def test_live_account_overview_fetches_order_history_per_symbol():
account = upsert_live_account(
account_code="binance_overview_orders_by_symbol",
status="enabled",
risk_config={"max_order_margin_usdt": 10, "max_symbol_leverage": 2, "allowed_symbols": ["BTC/USDT", "ETHUSDT"]},
)
class Client:
def __init__(self):
self.order_symbols = []
def load_markets(self):
return {}
def fetch_balance(self):
return {"total": {"USDT": 1000}, "free": {"USDT": 1000}, "used": {"USDT": 0}}
def fetch_positions(self, symbols=None):
return []
def fetch_open_orders(self, symbol=None):
return []
def fetch_orders(self, symbol=None, limit=30):
if symbol is None:
raise AssertionError("fetch_orders must be called with a symbol for binanceusdm")
self.order_symbols.append(symbol)
return [{
"id": f"{symbol}-1",
"symbol": symbol,
"type": "limit",
"side": "buy",
"status": "closed",
"price": 1,
"amount": 2,
"filled": 2,
"datetime": "2026-05-30T08:00:00",
}]
client = Client()
overview = get_live_account_overview(account["id"], refresh=True, client_factory=lambda account: client)
assert client.order_symbols == ["BTC/USDT", "ETH/USDT"]
assert len(overview["order_history"]) == 2
assert overview["errors"] == []
def test_live_account_can_be_deleted_without_deleting_history_contract(): def test_live_account_can_be_deleted_without_deleting_history_contract():
account = upsert_live_account(account_code="binance_delete_me", status="disabled") account = upsert_live_account(account_code="binance_delete_me", status="disabled")