fix: greedy模式从所有信号中顺延,而非仅holdings列表
问题:select_num=1时,holdings只有1个元素,ETF池不足时仓位闲置 - 有色金属(1ETF): 仓位25%,75%闲置 - 德国DAX(2ETF): 仓位50%,50%闲置 - 原油(3ETF): 仓位75%,25%闲置 修复:从所有信号中按动量排序顺延 - 有色金属(25%) → 顺延到第2名信号(如原油75%) → 100%满配 回测对比 (select_num=1): - equal: 1053% 累计收益, 1.34 夏普 - greedy(修复前): 730% 累计收益, 1.31 夏普 - greedy(修复后): 1355% 累计收益, 1.69 夏普
This commit is contained in:
@@ -531,9 +531,10 @@ class SimpleRotationStrategy:
|
|||||||
"""Compute greedy weights for signal-level holdings.
|
"""Compute greedy weights for signal-level holdings.
|
||||||
|
|
||||||
Greedy Algorithm (only for select_num=1):
|
Greedy Algorithm (only for select_num=1):
|
||||||
1. The top-1 index has absorption capacity = min(n_etfs, ceil(1/max_weight)) × max_weight
|
1. Get ALL signals sorted by momentum (not just holdings)
|
||||||
2. If capacity < 100%, remaining weight flows to next index by momentum
|
2. The top signal has absorption capacity = min(n_etfs, ceil(1/max_weight)) × max_weight
|
||||||
3. Continue until 100% allocated
|
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):
|
Example (select_num=1, max_weight=0.25):
|
||||||
- 有色金属(1 ETF): capacity=25%, absorbs 25%, remaining=75%
|
- 有色金属(1 ETF): capacity=25%, absorbs 25%, remaining=75%
|
||||||
@@ -558,10 +559,17 @@ class SimpleRotationStrategy:
|
|||||||
w = 1.0 / len(holdings)
|
w = 1.0 / len(holdings)
|
||||||
return {code: w for code in 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 = {}
|
signal_weights = {}
|
||||||
remaining_weight = 1.0
|
remaining_weight = 1.0
|
||||||
|
|
||||||
for signal_code in holdings:
|
for signal_code in all_signals_sorted:
|
||||||
if remaining_weight <= 0:
|
if remaining_weight <= 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -579,6 +587,7 @@ class SimpleRotationStrategy:
|
|||||||
remaining_weight -= absorb
|
remaining_weight -= absorb
|
||||||
|
|
||||||
# Assign weight to this signal
|
# Assign weight to this signal
|
||||||
|
if absorb > 0:
|
||||||
signal_weights[signal_code] = absorb
|
signal_weights[signal_code] = absorb
|
||||||
|
|
||||||
return signal_weights
|
return signal_weights
|
||||||
|
|||||||
Reference in New Issue
Block a user