TopkDropoutStrategy ignores get_risk_degree() for position sizing
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 48.7k
- Forks
- 7.7k
- PR merge metrics
- No merged PRs in 30d
Description
🐛 Bug Description
get_risk_degree() is documented as the dynamic market-timing hook ("Dynamically risk_degree will result in Market timing.", qlib/contrib/strategy/signal_strategy.py:69). But TopkDropoutStrategy.generate_trade_decision sizes new buys with the raw attribute self.risk_degree, not the getter:
# qlib/contrib/strategy/signal_strategy.py:266
value = cash * self.risk_degree / len(buy) if len(buy) > 0 else 0
It is the only use of risk_degree in TopkDropoutStrategy, and it bypasses get_risk_degree(). So a subclass that overrides get_risk_degree() to return a time-varying multiplier (the documented way to implement market timing) has zero effect on a TopkDropout book — the override is never called. (By contrast, WeightStrategyBase at line 365 and its subclass EnhancedIndexingStrategy at line 482 do call self.get_risk_degree(...), so the inconsistency is within the same module.)
To Reproduce
- Subclass
TopkDropoutStrategyand overrideget_risk_degree()to return, say,0.0on some dates (a regime gate to cash) andself.risk_degreeotherwise. - Backtest it vs. the un-subclassed
TopkDropoutStrategyon the same signal. - The two produce identical trades/returns — the overridden
get_risk_degree()is never consulted for buy sizing.
Expected Behavior
TopkDropoutStrategy should size buys with self.get_risk_degree(trade_step) (as WeightStrategyBase does), so that overriding get_risk_degree() actually enables market timing as documented:
value = cash * self.get_risk_degree(trade_step) / len(buy) if len(buy) > 0 else 0
Environment
- Qlib version: 0.9.7 (
qlib/contrib/strategy/signal_strategy.py(TopkDropoutStrategy.generate_trade_decision, line ~266) - Python version: 3.12
Additional context
Discovered while building a BTC-trend regime overlay (RegimeGatedTopkStrategy(TopkDropoutStrategy)) that gates gross exposure via get_risk_degree(). The overlay had no effect on backtests until we worked around it by pushing the gated value onto self.risk_degree per step before delegating to super().generate_trade_decision(). A two-line fix in TopkDropoutStrategy (use the getter) would remove the need for the workaround.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read qlib/contrib/strategy/signal_strategy.py, especially TopkDropoutStrategy.generate_trade_decision around line 266 and the get_risk_degree() documentation near line 69. Compare its sizing path with WeightStrategyBase around line 365, then verify that an overridden getter affects buy sizing while the existing strategy behavior remains unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- fintech-quant
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100