|
| 1 | +from types import SimpleNamespace |
| 2 | + |
| 3 | +import pytest |
| 4 | + |
| 5 | +from application.runtime_broker_adapters import build_runtime_broker_adapters |
| 6 | + |
| 7 | + |
| 8 | +def _build_adapters(*, account_ids=("U1234567",)): |
| 9 | + return build_runtime_broker_adapters( |
| 10 | + host_resolver=lambda: "127.0.0.1", |
| 11 | + ib_port=4001, |
| 12 | + ib_client_id=11, |
| 13 | + connect_timeout_seconds=60, |
| 14 | + connect_attempts=1, |
| 15 | + connect_retry_delay_seconds=0, |
| 16 | + client_id_retry_offset=100, |
| 17 | + ensure_event_loop_fn=lambda: None, |
| 18 | + connect_ib_fn=lambda *_args, **_kwargs: SimpleNamespace(managedAccounts=lambda: ["U1234567"]), |
| 19 | + fetch_portfolio_snapshot_fn=lambda *_args, **_kwargs: None, |
| 20 | + fetch_quote_snapshots_fn=lambda *_args, **_kwargs: None, |
| 21 | + submit_order_intent_fn=lambda *_args, **_kwargs: None, |
| 22 | + application_get_market_prices_fn=lambda *_args, **_kwargs: None, |
| 23 | + application_check_order_submitted_fn=lambda *_args, **_kwargs: None, |
| 24 | + application_execute_rebalance_fn=lambda *_args, **_kwargs: None, |
| 25 | + execute_paper_liquidation_fn=lambda *_args, **_kwargs: None, |
| 26 | + translator=lambda key, **_kwargs: key, |
| 27 | + strategy_profile="global_etf_rotation", |
| 28 | + account_group="live-slot-a", |
| 29 | + service_name="interactive-brokers-live-slot-a-service", |
| 30 | + account_ids=account_ids, |
| 31 | + dry_run_only=False, |
| 32 | + cash_reserve_ratio=0.0, |
| 33 | + cash_reserve_floor_usd=0.0, |
| 34 | + rebalance_threshold_ratio=0.02, |
| 35 | + limit_buy_premium=1.005, |
| 36 | + quantity_step=1.0, |
| 37 | + min_order_notional=50.0, |
| 38 | + safe_haven_cash_substitute_threshold_usd=750.0, |
| 39 | + sell_settle_delay_sec=0.0, |
| 40 | + separator="---", |
| 41 | + strategy_display_name="Test Strategy", |
| 42 | + sleep_fn=lambda _seconds: None, |
| 43 | + printer=lambda *_args, **_kwargs: None, |
| 44 | + ) |
| 45 | + |
| 46 | + |
| 47 | +def test_connect_ib_accepts_configured_managed_account(): |
| 48 | + adapters = _build_adapters(account_ids=("U1234567",)) |
| 49 | + |
| 50 | + ib = adapters.connect_ib() |
| 51 | + |
| 52 | + assert ib.managedAccounts() == ["U1234567"] |
| 53 | + |
| 54 | + |
| 55 | +def test_connect_ib_rejects_configured_account_not_visible_to_gateway_username(): |
| 56 | + observed = {"disconnects": 0} |
| 57 | + |
| 58 | + class FakeIB: |
| 59 | + def managedAccounts(self): |
| 60 | + return ["U7654321"] |
| 61 | + |
| 62 | + def disconnect(self): |
| 63 | + observed["disconnects"] += 1 |
| 64 | + |
| 65 | + adapters = _build_adapters(account_ids=("U1234567",)) |
| 66 | + adapters = adapters.__class__( |
| 67 | + **{ |
| 68 | + **adapters.__dict__, |
| 69 | + "connect_ib_fn": lambda *_args, **_kwargs: FakeIB(), |
| 70 | + } |
| 71 | + ) |
| 72 | + |
| 73 | + with pytest.raises(RuntimeError, match="Configured IBKR account_ids are not available"): |
| 74 | + adapters.connect_ib() |
| 75 | + |
| 76 | + assert observed["disconnects"] == 1 |
| 77 | + |
0 commit comments