change: rank allocator top_n=5 -> top_pct=0.2 (20% of universe)

This commit is contained in:
Yuxuan Yan
2026-06-07 10:06:49 +08:00
parent edcc4e4b73
commit 7033da131b
4 changed files with 21 additions and 22 deletions
+12 -13
View File
@@ -33,24 +33,17 @@ class ThresholdBuilder:
class RankEqualWeightBuilder:
"""Rank all stocks by signal. Buy top N at equal weight. Sell if drops out.
"""Rank all stocks by signal. Buy top N% at equal weight. Sell if drops out.
Called once per bar with ALL stock signals. Returns per-stock actions.
"""
def __init__(self, top_n: int = 5, min_signal: Optional[float] = None):
def __init__(self, top_n: Optional[int] = None, top_pct: float = 0.2, min_signal: Optional[float] = None):
self.top_n = top_n
self.min_signal = min_signal # optional floor — skip stocks with signal below this
self.top_pct = top_pct
self.min_signal = min_signal
def build(self, signals: dict[str, float]) -> dict[str, PositionAction]:
"""Rank stocks by signal (descending). Top N get 'buy', rest get 'sell' if held.
Args:
signals: {symbol: signal_value} for all stocks on this bar.
Returns:
{symbol: PositionAction}
"""
# Filter by min_signal if set
if self.min_signal is not None:
signals = {s: v for s, v in signals.items() if v >= self.min_signal}
@@ -58,8 +51,14 @@ class RankEqualWeightBuilder:
# Sort by signal descending
ranked = sorted(signals.items(), key=lambda x: x[1], reverse=True)
top_symbols = set(sym for sym, _ in ranked[:self.top_n])
size_pct = 1.0 / self.top_n if self.top_n > 0 else 0.0
# Determine top N: explicit count or percentage of available stocks
if self.top_n is not None:
n = self.top_n
else:
n = max(1, int(len(signals) * self.top_pct))
top_symbols = set(sym for sym, _ in ranked[:n])
size_pct = 1.0 / n if n > 0 else 0.0
actions = {}
for sym in signals: