microsoft/qlib · error · ValueError
at least one of `N` and `freq` should exist
Error message
at least one of `N` and `freq` should exist
What it means
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.
Source
Thrown at qlib/contrib/evaluate.py:59
analysis frequency used for calculating the scaler, at least one of `N` and `freq` should exist
mode: Literal["sum", "product"]
the method by which returns are accumulated:
- "sum": Arithmetic accumulation (linear returns).
- "product": Geometric accumulation (compounded returns).
"""
def cal_risk_analysis_scaler(freq):
_count, _freq = Freq.parse(freq)
_freq_scaler = {
Freq.NORM_FREQ_MINUTE: 240 * 238,
Freq.NORM_FREQ_DAY: 238,
Freq.NORM_FREQ_WEEK: 50,
Freq.NORM_FREQ_MONTH: 12,
}
return _freq_scaler[_freq] / _count
if N is None and freq is None:
raise ValueError("at least one of `N` and `freq` should exist")
if N is not None and freq is not None:
warnings.warn("risk_analysis freq will be ignored")
if N is None:
N = cal_risk_analysis_scaler(freq)
if mode == "sum":
mean = r.mean()
std = r.std(ddof=1)
annualized_return = mean * N
max_drawdown = (r.cumsum() - r.cumsum().cummax()).min()
elif mode == "product":
cumulative_curve = (1 + r).cumprod()
# geometric mean (compound annual growth rate)
mean = cumulative_curve.iloc[-1] ** (1 / len(r)) - 1
# volatility of log returns
std = np.log(1 + r).std(ddof=1)
cumulative_return = cumulative_curve.iloc[-1] - 1View on GitHub (pinned to 79633dd950)
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.
Example fix
// before res = risk_analysis(report.normal_return) // after res = risk_analysis(report.normal_return, N=250) # or: risk_analysis(report.normal_return, freq="day")
Defensive patterns
Strategy: validation
Validate before calling
if N is None and freq is None:
N = 250 # daily data default
risk_analysis(r, N=N) Type guard
def has_annualization(N, freq) -> bool:
return N is not None or freq is not None Try / catch
try:
risk_analysis(r)
except ValueError as e:
if "at least one of" in str(e):
risk_analysis(r, N=250)
else:
raise Prevention
- 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.
When it happens
Trigger: Calling qlib.contrib.evaluate.risk_analysis(r) with both N=None and freq=None (their defaults), e.g. risk_analysis(report.normal_return).
Common situations: 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.
Related errors
- risk_analysis accumulation mode {mode} is not supported. Exp
- method {method} is not supported!
- This type of input {rtype} is not supported
- Can't find the BASE_CONFIG file: {base_config_path}
- Invalid Qlib configuration (note: the global config has alre
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/9420914405c8579a.
Report an issue: GitHub.