From 3488c69ff2f523557fb2dfa310ae76fadd2e5b87 Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Fri, 10 Jul 2026 22:53:20 +0200 Subject: [PATCH] feat(exec): execute-multistrat --broker ibkr (whole-share) alongside alpaca Co-Authored-By: Claude Opus 4.8 --- src/fxhnt/cli.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/fxhnt/cli.py b/src/fxhnt/cli.py index 438b2ec..72a603d 100644 --- a/src/fxhnt/cli.py +++ b/src/fxhnt/cli.py @@ -277,6 +277,8 @@ def execute_multistrat( do_execute: bool = typer.Option(False, "--execute", help="place orders (default: dry-run plan only)"), live: bool = typer.Option(False, "--live", help="allow a non-paper account"), max_gross: float = typer.Option(2.0, help="gross budget (risk-parity multistrat can sit modestly above 1)"), + broker_name: str = typer.Option("alpaca", "--broker", + help="execution venue: 'alpaca' (fractional) or 'ibkr' (whole-share)"), ) -> None: """Compute the adaptive multi-strat book's CURRENT target weights and rebalance them on ALPACA — the 5 pure-equity streams (SPY/IEF/GLD/PDBC/DBMF) as fractional shares. BTC-USD is DROPPED from the fund book @@ -284,7 +286,6 @@ def execute_multistrat( no crypto order. Dry-run by default. Same ExecutionService safety gates + fractional sizing as `execute`.""" import datetime as _dt - from fxhnt.adapters.broker.alpaca import AlpacaBroker from fxhnt.adapters.persistence.forward_nav import ForwardNavRepo from fxhnt.application.multistrat_exec_record import plan_and_record from fxhnt.domain.strategies import multistrat @@ -314,8 +315,18 @@ def execute_multistrat( today = _dt.datetime.now(_dt.UTC).strftime("%Y-%m-%d") at = _dt.datetime.now(_dt.UTC).replace(tzinfo=None) - broker = AlpacaBroker(settings.alpaca.api_key, settings.alpaca.api_secret, - settings.alpaca.base_url, settings.alpaca.timeout) + # execution venue: IBKR (whole-share, via ib-gateway) or Alpaca (fractional). Both are the Broker port — + # ExecutionService + plan_and_record are broker-agnostic (fractional handling keys off supports_fractional). + if broker_name == "ibkr": + from fxhnt.adapters.broker.ibkr import IbkrBroker + broker: Any = IbkrBroker(settings.ibkr.host, settings.ibkr.port, + settings.ibkr.client_id, settings.ibkr.timeout) + where = f"IBKR ({settings.ibkr.host}:{settings.ibkr.port})" + else: + from fxhnt.adapters.broker.alpaca import AlpacaBroker + broker = AlpacaBroker(settings.alpaca.api_key, settings.alpaca.api_secret, + settings.alpaca.base_url, settings.alpaca.timeout) + where = f"Alpaca ({settings.alpaca.base_url})" try: with broker as brk: svc = ExecutionService(data, brk, settings) @@ -325,7 +336,7 @@ def execute_multistrat( today=today, at=at, max_gross=max_gross, execute=do_execute) except Exception as e: # connection / runtime typer.echo(f"broker error: {type(e).__name__}: {str(e)[:100]}") - typer.echo(f" reachable? Alpaca ({settings.alpaca.base_url}) — creds set?") + typer.echo(f" reachable? {where} — creds/gateway set?") raise typer.Exit(1) from e typer.echo(f"NLV ${plan.nlv:,.0f} | envelope ${envelope:,.0f} (B2a) | orders {len(plan.orders)}") for n in plan.notes: