{"record":{"id":"5f6380c213ee0c88","repo":"microsoft/qlib","slug":"risk-analysis-accumulation-mode-mode-is-not-supp","errorCode":null,"errorMessage":"risk_analysis accumulation mode {mode} is not supported. Expected `sum` or `product`.","messagePattern":"risk_analysis accumulation mode (.+?) is not supported\\. Expected `sum` or `product`\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"qlib/contrib/evaluate.py","lineNumber":82,"sourceCode":"\n    if mode == \"sum\":\n        mean = r.mean()\n        std = r.std(ddof=1)\n        annualized_return = mean * N\n        max_drawdown = (r.cumsum() - r.cumsum().cummax()).min()\n    elif mode == \"product\":\n        cumulative_curve = (1 + r).cumprod()\n        # geometric mean (compound annual growth rate)\n        mean = cumulative_curve.iloc[-1] ** (1 / len(r)) - 1\n        # volatility of log returns\n        std = np.log(1 + r).std(ddof=1)\n\n        cumulative_return = cumulative_curve.iloc[-1] - 1\n        annualized_return = (1 + cumulative_return) ** (N / len(r)) - 1\n        # max percentage drawdown from peak cumulative product\n        max_drawdown = (cumulative_curve / cumulative_curve.cummax() - 1).min()\n    else:\n        raise ValueError(f\"risk_analysis accumulation mode {mode} is not supported. Expected `sum` or `product`.\")\n\n    information_ratio = mean / std * np.sqrt(N)\n    data = {\n        \"mean\": mean,\n        \"std\": std,\n        \"annualized_return\": annualized_return,\n        \"information_ratio\": information_ratio,\n        \"max_drawdown\": max_drawdown,\n    }\n    res = pd.Series(data).to_frame(\"risk\")\n    return res\n\n\ndef indicator_analysis(df, method=\"mean\"):\n    \"\"\"analyze statistical time-series indicators of trading\n\n    Parameters\n    ----------","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/contrib/evaluate.py#L64-L100","documentation":"risk_analysis computes cumulative statistics in one of two accumulation modes: 'sum' (simple returns summed) or 'product' (compound growth via (1+r).cumprod()). Any other value of mode falls through to this ValueError, because annualized return and max drawdown formulas differ between the two and no default is assumed.","triggerScenarios":"Calling risk_analysis(r, mode='cumprod') or any mode string other than exactly 'sum' or 'product' (case-sensitive).","commonSituations":"Typo in mode ('Product', 'sums', 'compound'); older code/notebooks using a mode name from a previous qlib API; passing None explicitly.","solutions":["Use mode=\"sum\" for simple (non-compounded) excess returns, which is qlib's typical report format.","Use mode=\"product\" when r is a ratio series that should compound (e.g. (1+r).cumprod() semantics).","Check the exact spelling and casing of the mode string before the call."],"exampleFix":"// before\nres = risk_analysis(r, N=250, mode=\"cumprod\")\n\n// after\nres = risk_analysis(r, N=250, mode=\"product\")","handlingStrategy":"validation","validationCode":"mode = \"sum\" if mode not in (\"sum\", \"product\") else mode\nrisk_analysis(r, N=N, mode=mode)","typeGuard":"def is_valid_mode(mode) -> bool:\n    return mode in (\"sum\", \"product\")","tryCatchPattern":"try:\n    risk_analysis(r, N=N, mode=mode)\nexcept ValueError as e:\n    if \"accumulation mode\" in str(e):\n        raise ValueError(f\"mode must be 'sum' or 'product', got {mode!r}\") from e\n    raise","preventionTips":["Expose only a validated mode parameter in your own API wrappers.","Use 'sum' for qlib excess-return reports; 'product' for compounded ratios.","Spellcheck config keys before passing them through."],"tags":["qlib","evaluate","risk-analysis","validation"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}