import pytest from ibkr_quant.rebalance import plan_orders def test_closes_dropped_names_and_sells_before_buys(): orders = plan_orders( target_weights={"AAPL": 0.5, "MSFT": 0.5}, positions={"AAPL": 10, "NVDA": 7}, prices={"AAPL": 100.0, "MSFT": 300.0, "NVDA": 50.0}, investable=10_000, min_trade_value=200, ) assert [(o.symbol, o.delta) for o in orders] == [("NVDA", -7), ("AAPL", 40), ("MSFT", 16)] def test_skips_small_adjustments_but_not_full_exits(): orders = plan_orders( target_weights={"AAPL": 1.0}, positions={"AAPL": 99, "KO": 1}, prices={"AAPL": 100.0, "KO": 60.0}, investable=10_000, min_trade_value=200, ) assert [(o.symbol, o.delta) for o in orders] == [("KO", -1)] def test_refuses_to_plan_without_prices(): with pytest.raises(ValueError, match="NVDA"): plan_orders({"AAPL": 1.0}, {"NVDA": 5}, {"AAPL": 100.0}, 10_000, 0) def test_resize_band_keeps_near_target_holdings(): orders = plan_orders( target_weights={"AAPL": 0.5, "MSFT": 0.5}, positions={"AAPL": 46, "MSFT": 10}, prices={"AAPL": 100.0, "MSFT": 300.0}, investable=10_000, min_trade_value=50, resize_band=0.10, ) # AAPL is 8% under target and stays; MSFT is 40% under and tops up. assert [(o.symbol, o.delta) for o in orders] == [("MSFT", 6)]