Bandit scheduler reward: ARR term never fires (trailing space in metric key), and sharpe is derived from it
Checked against main at 6762f84f9bc0.
Both problems are in rdagent/scenarios/qlib/proposal/bandit.py, in extract_metrics_from_experiment. action_selection defaults to "bandit" in QuantBasePropSetting, so this is the default path for rdagent fin_quant.
1. Trailing space in the ARR key, so the term never fires
arr = result.get("1day.excess_return_with_cost.annualized_return ", 0.0)The key qlib actually logs has no trailing space:
qlib/contrib/evaluate.py::risk_analysisreturns{"annualized_return": ..., "information_ratio": ..., "max_drawdown": ...}PortAnaRecordlogs them asf"{_analysis_freq}.{k}", giving1day.excess_return_with_cost.annualized_returnread_exp_res.pywritespd.Series(recorder.list_metrics())toqlib_res.csvQlibFBWorkspace.executereads that CSV withindex_col=0, soexp.resultcarries the index verbatim
The same key without the space is used elsewhere in this repo: IMPORTANT_METRICS in rdagent/scenarios/qlib/developer/feedback.py, and rdagent/log/ui/app.py:58.
Effect: arr is always the 0.0 default, so the ARR term (weight 0.25) contributes nothing.
2. sharpe inherits that zero, and is not a Sharpe ratio
sharpe = arr / -mdd if mdd != 0 else 0.0With arr pinned to 0.0 this term (weight 0.20) is also always 0.0.
Separately from the typo: no volatility enters the expression. It is annualized return over max drawdown, i.e. a Calmar-type ratio, and it is a monotone function of arr, which the reward vector already contains as its own term.
Together the two are 0.45 of EnvController.weights = (0.1, 0.1, 0.05, 0.05, 0.25, 0.15, 0.1, 0.2), so in practice the scheduler is driven by IC / ICIR / Rank IC / Rank ICIR / IR / -MDD only.
3. Why it does not surface
result.get(key, 0.0) defaults silently, and extract_metrics_from_experiment wraps the whole body in except Exception: return Metrics(). A mistyped or missing key is therefore indistinguishable from a genuinely zero metric.
Suggested fix
Happy to send a PR if the direction is agreed:
- share one set of metric-key constants between
bandit.pyandfeedback.py; - treat a missing key as an error, or at least log it, rather than defaulting to
0.0; - decide what the eighth feature should be — a real Sharpe from the qlib metrics, or the return/max-drawdown ratio kept under a name that says so.
Source: microsoft/RD-Agent