1
This commit is contained in:
parent
1e94714234
commit
4d5a3cc235
@ -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
@ -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")
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user