Commit Graph

5 Commits

Author SHA1 Message Date
jgrusewski
bcf58e703d fix(exec): entry-floor threshold on the sizing envelope, not the raw NLV
The IGLN gold-sleeve 'priced but no order' bug. scaled_weights sizes orders to
the paper envelope (envelope*within_weight), but _plan's entry-floor/hysteresis
threshold used entry_floor*nlv (the full $1M account NLV) — so the effective
floor was ~entry_floor*(nlv/envelope) of the envelope (~5%), silently dropping
every sleeve below it. A 3% gold sleeve at a $100k envelope on a $1M account
skipped. FIX: rebalance_weights takes sizing_capital (the envelope); the floor
is entry_floor*min(sizing_capital,nlv). Sizing + recording unchanged. Regression
test: a 3% sleeve survives WITH the envelope basis, drops without it. 2005 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 16:14:06 +02:00
jgrusewski
edf7a659f6 fix(ucits): resolve UCITS contracts by ISIN + size from real IBKR price
execute-multistrat --venue ucits was unrunnable and mis-sized:
- place_order hardcoded Stock(symbol, SMART, USD) so LSE-listed UCITS ETFs
  (CSPX/IBTM/SGLN/ICOM/DBMF) failed IBKR resolution (Error 200);
- apply_ucits_map reused the US ticker's Yahoo price for the UCITS ticker, so
  shares = weight*envelope/price was wrong;
- any single unmapped sleeve refused the whole (PRIIPs-legal) rebalance.

Fix:
- Order carries optional exchange/currency/sec_id_type/sec_id (US path keeps
  SMART/USD defaults, byte-unchanged). IbkrBroker resolves UCITS orders via
  Contract(secIdType=ISIN, secId, currency=USD) + qualifyContracts; an
  unresolved contract logs loudly and is SKIPPED (no crash, no wrong order).
- IbkrBroker.market_price fetches the REAL UCITS USD price via delayed market
  data (reqMarketDataType(3) + reqMktData snapshot); the UCITS route sizes from
  it, not the US Yahoo price. Modeled US weights unchanged.
- route_ucits routes sleeves that map+resolve+price and loudly logs+skips the
  rest; survivors keep their own weight (book NOT rescaled), skipped $ reported.
- UcitsSettings.map now carries verified ISINs (CSPX IE00B5BMR087, IBTM
  IE00B1FZS798, SGLN IE00B4ND3602, ICOM IE00BDFL4P12) and DBMF is mapped to the
  real iMGP DBi Managed Futures UCITS ETF (LU2951555585, USD line) with a
  liquidity caveat comment.

TDD mocks the IB client (no live broker). Full suite: 1986 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 01:56:06 +02:00
jgrusewski
fd9534e978 feat(exec): multistrat book on Alpaca — target-weight bridge + crypto BTC leg
Puts the adaptive multi-strat ETF book on the clean execution port:
- multistrat.target_weights(closes): exposes the final-day per-instrument weights
  (L*tw*volnorm_scale) that book_series applies but only collapses into a return.
  Refactored _volnorm via a shared _volnorm_scale so book_series stays byte-identical.
  Tested: sum(w*R[last]) reconstructs book_series's last booked return exactly.
- ExecutionService.rebalance_weights(weights, prices): a direct pre-computed-weights
  entry beside rebalance(book), sharing one _plan engine (gates + fractional sizing).
  Caller owns data<->broker symbol mapping.
- AlpacaBroker crypto-aware: a slash symbol (BTC/USD) -> time_in_force gtc; equities
  stay day. So the book's BTC-USD leg trades on Alpaca crypto alongside the 5 ETFs.
- CLI fxhnt execute-multistrat (maps BTC-USD -> BTC/USD); the alpaca-rebalancer
  CronJob now runs it (suspended until paper keys).

Full suite green (1832); +5 tests. Alpaca-crypto position-symbol form validated on
first paper run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 00:33:22 +02:00
jgrusewski
4ada050f7e feat(exec): Alpaca broker adapter + fractional execution (commission-free US-equity leg)
Foundation for a 2nd, uncorrelated execution venue alongside the Bybit crypto book:
- AlpacaBroker (httpx REST, Broker port): account_state + place_order, FRACTIONAL
  market+day orders, is_paper from base_url, supports_fractional=True. httpx test
  seam (MockTransport) — no network.
- ExecutionService fractional-aware: sizes float shares when broker.supports_fractional
  (Alpaca) — precise weights at small capital — else whole shares (IBKR, unchanged).
- AlpacaSettings (paper base_url default; live gated by allow_live). CLI
  .
- Suspended CronJob + credentials scaffold (arm after paper keys, like the Bybit
  testnet leg). Runs the survivor book end-to-end.

The multi-strat ETF book on Alpaca is a documented follow-up (its position logic
isn't on the clean port yet). Full suite green (1827); 6 new tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 00:00:06 +02:00
jgrusewski
7fb14c47a0 feat: multi-strategy execution layer (domain netting + Broker port + ExecutionService)
Layer-by-layer: domain/portfolio (Book + StrategyAllocation + compute_target_weights — nets many
sleeves, incl. same market, into per-market targets capped at gross leverage) | ports/broker (Broker
contract + Order/AccountState DTOs) | application/execution (ExecutionService: net → reconcile vs
broker → whole-share orders with entry-floor/hysteresis, behind the hard-won gates: paper-guard,
data-consistency/conservative-NLV, leverage-cap) | config ExecutionSettings. Tested with fake
data+broker (no network/IBKR): netting, leverage cap, buy-from-flat, in-band no-op, live-block,
inconsistent-data-block. 10/10 tests green. IBKR adapter (behind Broker port) is the next step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 13:52:32 +02:00