_prepare_returns silently converts missing observations to 0.0 returns in every statistic

Author: WatchTree-19Created Sep 12, 2026Updated Sep 12, 2026

_prepare_returns runs data.fillna(0) on its input, so every NaN becomes a flat 0.0 return inside essentially every statistic the library computes. Missing data is common in exactly the places this library is used (unaligned calendars, late-starting funds, delisted names inside a frame), and imputing it as zero-return days dilutes every dispersion-based statistic towards zero with no warning: on a 1,512 point daily series with 40 NaNs inserted, annualised volatility shifts by -1.29%, Sharpe by -1.30%, Sortino by -1.30%, VaR by -1.25% and CVaR by -1.49%, all silently. The shift scales with the share of missing data, so a frame with one late-starting column can have that column's entire pre-inception period counted as flat performance.

Reproduction:

python
import numpy as np, pandas as pd, quantstats as qs
idx = pd.bdate_range("2019-01-01", periods=1552)
r = np.random.default_rng(5).normal(0.0004, 0.011, 1512)
pad = pd.Series(np.insert(r, np.random.default_rng(5).integers(0, 1512, 40), np.nan), index=idx)
clean = pad.dropna()
print(qs.stats.volatility(clean), qs.stats.volatility(pad))  # differ ~1.3%

Suggest either dropna instead of fillna(0), or a documented keyword with a warning when imputation actually fires, so that the behaviour is a choice rather than a surprise. A related small robustness note: stats.max_drawdown raises TypeError on any Series without a DatetimeIndex (it subtracts a Timedelta from index[0]) where the sibling functions accept plain Series.