Manage every USD stock position except an explicit unmanaged list

rebalance treated only today's S&P 500 members as the strategy's, so a held
name that left the index (or was renamed on Wikipedia) was never sold and
its value shrank the investable amount, unlike quant-trading's engine, which
sells non-members at the next rebalance. Now every USD stock/ETF position
is managed except execution.unmanaged (AAOI); a holding without bars is
priced from the account for its exit. On the paper account the managed set
is unchanged (the 10 current members).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 05:26:06 -07:00
parent a87a0c3b54
commit e2b06f1b1c
4 changed files with 46 additions and 3 deletions

View File

@@ -148,15 +148,16 @@ async def rebalance(cfg: Config, submit: bool) -> None:
async with Broker(cfg.ib) as broker:
state = await broker.account_state()
managed = [h for h in state.holdings if h.sec_type == "STK" and h.currency == "USD" and h.symbol in universe]
managed = [h for h in state.holdings if h.sec_type == "STK" and h.currency == "USD" and h.symbol not in ex.unmanaged]
unmanaged_value = sum(h.market_value_usd for h in state.holdings if h not in managed)
investable = state.nav_usd * (1 - ex.cash_buffer) - unmanaged_value
weights = select_with_buffer(score, {h.symbol for h in managed}, st.top_n, st.buffer)
# Sized at today's open, the latest price in the bars; filled at the close.
# Sized at today's open, the latest price in the bars; filled at the close. A holding
# that left the index has no bars and takes the account's price; its exit size does not use it.
orders = plan_orders(
weights,
{h.symbol: h.quantity for h in managed},
opens.iloc[-1].dropna().to_dict(),
{h.symbol: h.market_price for h in managed} | opens.iloc[-1].dropna().to_dict(),
investable,
max(ex.min_trade_value, ex.min_trade_fraction * state.nav_usd),
ex.resize_band,