fix: rank-based allocator + momentum signal (reversal->momentum flip)

This commit is contained in:
Yuxuan Yan
2026-06-07 09:50:08 +08:00
parent da01312292
commit bd0605072f
7 changed files with 117 additions and 44 deletions
+37 -11
View File
@@ -4,23 +4,49 @@ import pandas as pd
class AlphaStrategy(bt.Strategy):
"""Trade each feed based on its precomputed ``signal`` line.
"""Trade feeds based on precomputed ``signal`` line.
The strategy delegates the buy/sell/hold decision to a portfolio builder
(e.g. ``ThresholdBuilder``) and sizes entries with ``order_target_percent``.
Supports two builder modes:
- ThresholdBuilder: per-stock threshold (passed ``(signal_value, in_position)``)
- RankEqualWeightBuilder: cross-sectional ranking (passed ``{symbol: signal}`` dict)
"""
def __init__(self, builder):
self.builder = builder
def next(self):
# Collect all signals
signals: dict[str, float] = {}
for data in self.datas:
sig = data.signal[0]
if pd.isna(sig):
continue
in_position = bool(self.getposition(data).size)
action = self.builder.build(float(sig), in_position)
if action.action == "buy":
self.order_target_percent(data=data, target=action.size_pct)
elif action.action == "sell":
self.close(data=data)
if not pd.isna(sig):
signals[data._name] = float(sig)
if not signals:
return
# Detect builder type: if RankEqualWeightBuilder, use cross-sectional mode
from portfolio.builder import RankEqualWeightBuilder
if isinstance(self.builder, RankEqualWeightBuilder):
actions = self.builder.build(signals)
for data in self.datas:
name = data._name
if name not in actions:
continue
action = actions[name]
if action.action == "buy":
self.order_target_percent(data=data, target=action.size_pct)
elif action.action == "sell":
self.close(data=data)
else:
# Legacy per-stock ThresholdBuilder
for data in self.datas:
name = data._name
if name not in signals:
continue
in_position = bool(self.getposition(data).size)
action = self.builder.build(signals[name], in_position)
if action.action == "buy":
self.order_target_percent(data=data, target=action.size_pct)
elif action.action == "sell":
self.close(data=data)