diff --git a/rotation/simple_rotation.py b/rotation/simple_rotation.py index 08666e2..8262abc 100644 --- a/rotation/simple_rotation.py +++ b/rotation/simple_rotation.py @@ -530,17 +530,18 @@ class SimpleRotationStrategy: def _compute_greedy_weights(self, holdings: List[str], factors: Dict[str, float]) -> Dict[str, float]: """Compute greedy weights for signal-level holdings. - Greedy Algorithm: - 1. Each index has absorption capacity = min(n_etfs, ceil(1/max_weight)) × max_weight - 2. Iterate through holdings in order (sorted by momentum) - 3. Each index absorbs up to its capacity - 4. Remaining weight flows to next index + 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 Example (select_num=1, max_weight=0.25): - 有色金属(1 ETF): capacity=25%, absorbs 25%, remaining=75% - 原油(3 ETFs): capacity=75%, absorbs 75%, remaining=0% - Total: 100% + For select_num>1, falls back to equal weight (greedy not applicable). + Args: holdings: List of signal codes (sorted by momentum desc) factors: Dict of signal_code -> momentum score @@ -551,6 +552,12 @@ class SimpleRotationStrategy: if not holdings: return {} + # Greedy only for select_num=1 + if self.select_num > 1: + # Fall back to equal weight + w = 1.0 / len(holdings) + return {code: w for code in holdings} + signal_weights = {} remaining_weight = 1.0