v2.4.0
A breaking release with a single theme: tracking_error() now computes the tracking
error as the CFA curriculum defines it. The previous default mixed two measures the
curriculum deliberately keeps apart — how far a fund lags its benchmark (tracking
difference) and how unstable that lag is (tracking error) — so the same symbols over
the same period return a different number after this upgrade. See
examples/02 index funds perfomance.ipynb
for the index-fund comparison workflow these methods serve. The release also fixes a
stale Portfolio.assets_weights mapping.
Changed
- Breaking.
tracking_error()now returns the sample standard deviation of the
return differences around their mean (Bessel's correction), annualized by
sqrt(12)— the tracking error as defined by the CFA curriculum (CFA Level II,
2019, V6, eq. 8; CFA Level I, 2025, V9 Portfolio Management, footnote 3). The
previous default was the uncentered root-mean-square of the differences, which
folded the systematic lag behind the benchmark (the tracking difference) into the
result:TE_rms² = (N-1)/N · TE_std² + mean(d)². The same symbols over the same
period therefore return a different number than in 2.3.1 and earlier — usually
lower, since the lag term drops out, though marginally higher for a fund whose lag
is smaller thanTE_std / sqrt(N), where the uncentered formula's division byN
instead ofN-1dominates. Affectshelpers.Index.tracking_error,
AssetList.tracking_errorandPortfolio.tracking_error(#97).
Removed
- Breaking. The
methodparameter ofhelpers.Index.tracking_error,
AssetList.tracking_errorandPortfolio.tracking_error. Tracking error now has a
single definition, somethod="rms"andmethod="std"(both added in 2.2.2) are
gone — passingmethod=raisesTypeError. Code that asked formethod="std"
keeps its values by simply dropping the argument; code that relied on the"rms"
values has to compute them itself, as the mixture of tracking difference and
tracking error that it is.
Fixed
Portfolio.assets_weights(the symbol → weight mapping) was built once in the
constructor and never refreshed by theweightssetter, so after
pf.weights = [...]the public attribute kept reporting the weights the
portfolio was created with. It is now rebuilt wheneverweightsis assigned.requirements.txtcappedarch < 8.0.0andstatsmodels < 0.15.0while
pyproject.tomlhad dropped both upper bounds, so the file forbade the very
versions the release is tested against (arch8.0.0). It mirrors the
pyproject.tomlconstraints again.