fix: rank-based allocator + momentum signal (reversal->momentum flip)
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user