diff --git a/rotation/simple_rotation.py b/rotation/simple_rotation.py index 8262abc..9ae6d4f 100644 --- a/rotation/simple_rotation.py +++ b/rotation/simple_rotation.py @@ -531,9 +531,10 @@ class SimpleRotationStrategy: """Compute greedy weights for signal-level holdings. Greedy Algorithm (only for select_num=1): - 1. The top-1 index has absorption capacity = min(n_etfs, ceil(1/max_weight)) × max_weight - 2. If capacity < 100%, remaining weight flows to next index by momentum - 3. Continue until 100% allocated + 1. Get ALL signals sorted by momentum (not just holdings) + 2. The top signal has absorption capacity = min(n_etfs, ceil(1/max_weight)) × max_weight + 3. If capacity < 100%, remaining weight flows to next signal by momentum + 4. Continue until 100% allocated Example (select_num=1, max_weight=0.25): - 有色金属(1 ETF): capacity=25%, absorbs 25%, remaining=75% @@ -558,10 +559,17 @@ class SimpleRotationStrategy: w = 1.0 / len(holdings) return {code: w for code in holdings} + # Get ALL signals sorted by momentum (for spill-over) + all_signals_sorted = sorted( + self.signal_codes, + key=lambda c: factors.get(c, 0), + reverse=True + ) + signal_weights = {} remaining_weight = 1.0 - for signal_code in holdings: + for signal_code in all_signals_sorted: if remaining_weight <= 0: break @@ -579,7 +587,8 @@ class SimpleRotationStrategy: remaining_weight -= absorb # Assign weight to this signal - signal_weights[signal_code] = absorb + if absorb > 0: + signal_weights[signal_code] = absorb return signal_weights