diff --git a/main.py b/main.py index 48c2f56..960ac70 100644 --- a/main.py +++ b/main.py @@ -585,6 +585,19 @@ def build_broker_adapters(*, dry_run_only_override: bool | None = None): if RUNTIME_SETTINGS.runtime_target is not None else None ) + + def connect_runtime_ib(host, port, client_id, *, timeout): + # Shadow and dry-run cycles need account and market reads only. Mark + # their Gateway sessions read-only so they cannot trigger an IBKR + # write-access confirmation or acquire trading authority. + return ibkr_connect_ib( + host, + port, + client_id, + timeout=timeout, + readonly=effective_dry_run_only, + ) + return build_runtime_broker_adapters( host_resolver=get_ib_host, refresh_host_fn=refresh_ib_host, @@ -596,7 +609,7 @@ def build_broker_adapters(*, dry_run_only_override: bool | None = None): connect_retry_delay_seconds=IB_CONNECT_RETRY_DELAY_SECONDS, client_id_retry_offset=IB_CLIENT_ID_RETRY_OFFSET, ensure_event_loop_fn=ensure_event_loop, - connect_ib_fn=ibkr_connect_ib, + connect_ib_fn=connect_runtime_ib, fetch_portfolio_snapshot_fn=fetch_market_portfolio_snapshot, fetch_quote_snapshots_fn=fetch_market_quote_snapshots, submit_order_intent_fn=submit_market_order_intent, diff --git a/pyproject.toml b/pyproject.toml index 8ad950d..927e710 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ dependencies = [ "google-cloud-secret-manager", "google-cloud-storage", "yfinance", - "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@aae333fe8b3fe5aeb32e1ff135ab14ea7db32420", + "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@4d716cb1c6b42747fb96be3d398c528b26cd5826", "us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@6776c59d709848abdfb513ef5f1853be029f99b1", "hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@e385485cf1db306ce7efd30bfb9e181139767fa7", ] @@ -64,5 +64,5 @@ include = [ [tool.uv] override-dependencies = [ - "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@aae333fe8b3fe5aeb32e1ff135ab14ea7db32420", + "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@4d716cb1c6b42747fb96be3d398c528b26cd5826", ] diff --git a/qsl.toml b/qsl.toml index 734f330..7a5113f 100644 --- a/qsl.toml +++ b/qsl.toml @@ -5,7 +5,7 @@ upgrade_ring = "ring_d" allow_legacy = false [qsl.requires] -quant_platform_kit = "aae333fe8b3fe5aeb32e1ff135ab14ea7db32420" +quant_platform_kit = "4d716cb1c6b42747fb96be3d398c528b26cd5826" us_equity_strategies = "6776c59d709848abdfb513ef5f1853be029f99b1" hk_equity_strategies = "e385485cf1db306ce7efd30bfb9e181139767fa7" diff --git a/tests/test_event_loop.py b/tests/test_event_loop.py index 1102079..34d9073 100644 --- a/tests/test_event_loop.py +++ b/tests/test_event_loop.py @@ -20,7 +20,7 @@ def worker(): assert not loop.is_closed() -def test_connect_ib_prepares_event_loop_before_connect(strategy_module, monkeypatch): +def test_connect_ib_prepares_readonly_event_loop_before_dry_run_connect(strategy_module, monkeypatch): observed = {} def fake_ibkr_connect(host, port, client_id, **kwargs): @@ -32,7 +32,23 @@ def fake_ibkr_connect(host, port, client_id, **kwargs): with ThreadPoolExecutor(max_workers=1) as executor: executor.submit(strategy_module.connect_ib).result() - assert observed["args"] == ("127.0.0.1", 4001, 1, {"timeout": 60}) + assert observed["args"] == ("127.0.0.1", 4001, 1, {"timeout": 60, "readonly": True}) + + +def test_live_runtime_adapter_keeps_writable_gateway_session(strategy_module_factory, monkeypatch): + module = strategy_module_factory(IBKR_DRY_RUN_ONLY="false") + observed = {} + + def fake_ibkr_connect(host, port, client_id, **kwargs): + observed["args"] = (host, port, client_id, kwargs) + return object() + + monkeypatch.setattr(module, "ibkr_connect_ib", fake_ibkr_connect) + + adapters = module.build_broker_adapters() + adapters.connect_ib_fn("127.0.0.1", 4001, 1, timeout=60) + + assert observed["args"] == ("127.0.0.1", 4001, 1, {"timeout": 60, "readonly": False}) def test_connect_ib_retries_with_offset_client_ids(strategy_module_factory, monkeypatch): diff --git a/uv.lock b/uv.lock index 4dad5cf..ce3dbff 100644 --- a/uv.lock +++ b/uv.lock @@ -17,7 +17,7 @@ resolution-markers = [ ] [manifest] -overrides = [{ name = "quant-platform-kit", git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=aae333fe8b3fe5aeb32e1ff135ab14ea7db32420" }] +overrides = [{ name = "quant-platform-kit", git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=4d716cb1c6b42747fb96be3d398c528b26cd5826" }] [[package]] name = "beautifulsoup4" @@ -791,7 +791,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'test'" }, { name = "pytest-cov", marker = "extra == 'test'" }, { name = "pytz" }, - { name = "quant-platform-kit", git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=aae333fe8b3fe5aeb32e1ff135ab14ea7db32420" }, + { name = "quant-platform-kit", git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=4d716cb1c6b42747fb96be3d398c528b26cd5826" }, { name = "requests" }, { name = "ruff", marker = "extra == 'test'" }, { name = "us-equity-strategies", git = "https://github.com/QuantStrategyLab/UsEquityStrategies.git?rev=6776c59d709848abdfb513ef5f1853be029f99b1" }, @@ -1327,7 +1327,7 @@ wheels = [ [[package]] name = "quant-platform-kit" version = "0.10.0" -source = { git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=aae333fe8b3fe5aeb32e1ff135ab14ea7db32420#aae333fe8b3fe5aeb32e1ff135ab14ea7db32420" } +source = { git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=4d716cb1c6b42747fb96be3d398c528b26cd5826#4d716cb1c6b42747fb96be3d398c528b26cd5826" } [[package]] name = "requests"