{"record":{"id":"9420914405c8579a","repo":"microsoft/qlib","slug":"at-least-one-of-n-and-freq-should-exist","errorCode":null,"errorMessage":"at least one of `N` and `freq` should exist","messagePattern":"at least one of `N` and `freq` should exist","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"qlib/contrib/evaluate.py","lineNumber":59,"sourceCode":"        analysis frequency used for calculating the scaler, at least one of `N` and `freq` should exist\n    mode: Literal[\"sum\", \"product\"]\n        the method by which returns are accumulated:\n        - \"sum\": Arithmetic accumulation (linear returns).\n        - \"product\": Geometric accumulation (compounded returns).\n    \"\"\"\n\n    def cal_risk_analysis_scaler(freq):\n        _count, _freq = Freq.parse(freq)\n        _freq_scaler = {\n            Freq.NORM_FREQ_MINUTE: 240 * 238,\n            Freq.NORM_FREQ_DAY: 238,\n            Freq.NORM_FREQ_WEEK: 50,\n            Freq.NORM_FREQ_MONTH: 12,\n        }\n        return _freq_scaler[_freq] / _count\n\n    if N is None and freq is None:\n        raise ValueError(\"at least one of `N` and `freq` should exist\")\n    if N is not None and freq is not None:\n        warnings.warn(\"risk_analysis freq will be ignored\")\n    if N is None:\n        N = cal_risk_analysis_scaler(freq)\n\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","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/contrib/evaluate.py#L41-L77","documentation":"risk_analysis requires an annualization factor: either an explicit number of periods per year (N) or a data frequency (freq) from which it derives N via Freq.parse and a per-frequency scaler table. Passing neither leaves no way to annualize mean/std into annualized_return and information_ratio, so it raises this ValueError.","triggerScenarios":"Calling qlib.contrib.evaluate.risk_analysis(r) with both N=None and freq=None (their defaults), e.g. risk_analysis(report.normal_return).","commonSituations":"Upgrading from older qlib versions where freq had a different default or N was inferred; passing report analysis output without specifying report_freq; passing freq in an unrecognized form that ends up None.","solutions":["Pass N directly for full control: risk_analysis(r, N=250) for daily returns.","Or pass freq: risk_analysis(r, freq='day') — minute/week/month are also mapped to annualization scalars.","If both are given, remove one — freq is silently ignored with a warning when N is present."],"exampleFix":"// before\nres = risk_analysis(report.normal_return)\n\n// after\nres = risk_analysis(report.normal_return, N=250)\n# or: risk_analysis(report.normal_return, freq=\"day\")","handlingStrategy":"validation","validationCode":"if N is None and freq is None:\n    N = 250  # daily data default\nrisk_analysis(r, N=N)","typeGuard":"def has_annualization(N, freq) -> bool:\n    return N is not None or freq is not None","tryCatchPattern":"try:\n    risk_analysis(r)\nexcept ValueError as e:\n    if \"at least one of\" in str(e):\n        risk_analysis(r, N=250)\n    else:\n        raise","preventionTips":["Always pass N explicitly in production reports (freq-derived scalers can change between versions).","Wrap risk_analysis with a default-N helper in your codebase.","Read warnings: 'freq will be ignored' means you passed both."],"tags":["qlib","evaluate","risk-analysis","config"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}