from __future__ import annotations from decimal import Decimal from typing import Any from llm_trade_bot.bybit_gateway import BybitGateway from llm_trade_bot.config import BybitConfig, ScannerConfig class FakeBybitHTTP: def __init__(self, wallet: dict[str, Any] | None = None) -> None: self.wallet = wallet self.open_order_requests: list[dict[str, Any]] = [] def get_wallet_balance(self, **parameters: Any) -> dict[str, Any]: assert parameters == {"accountType": "UNIFIED"} return self.wallet or {"retCode": 0, "result": {"list": []}} def get_positions(self, **parameters: Any) -> dict[str, Any]: return {"retCode": 0, "result": {"list": []}} def get_open_orders(self, **parameters: Any) -> dict[str, Any]: self.open_order_requests.append(parameters) return {"retCode": 0, "result": {"list": []}} async def test_open_orders_scopes_linear_requests_by_settle_coin() -> None: gateway = BybitGateway(BybitConfig("key", "secret", "USDC"), ScannerConfig()) http = FakeBybitHTTP() gateway._http = http # type: ignore[assignment] await gateway.open_orders() assert http.open_order_requests == [ {"category": "spot", "limit": 50}, {"category": "linear", "limit": 50, "settleCoin": "USDC"}, ] async def test_account_snapshot_uses_quote_coin_balance_when_account_available_is_empty() -> None: wallet = { "retCode": 0, "result": { "list": [ { "totalEquity": "100000", "totalAvailableBalance": "", "totalMarginBalance": "100000", "coin": [ { "coin": "USDT", "walletBalance": "50000", "availableToWithdraw": "", "totalPositionIM": "100", "totalOrderIM": "200", "locked": "300", "bonus": "400", }, {"coin": "USDC", "walletBalance": "50000", "availableToWithdraw": ""}, ], } ] }, } gateway = BybitGateway(BybitConfig("key", "secret", "USDC"), ScannerConfig(quote_coin="USDT")) gateway._http = FakeBybitHTTP(wallet) # type: ignore[assignment] spot = await gateway.account_snapshot("spot") linear = await gateway.account_snapshot("linear") assert spot.available_balance == Decimal("49000") assert linear.available_balance == Decimal("50000")