Both findings are backed by closed-form identities, so the expected values are checkable independently of my code.
1. kelly_criterion returns the growth-optimal fraction multiplied by the average loss. On a two-outcome series with p = 0.60, win +2%, loss -1%, the closed-form Kelly fraction is 39.9985 and the function returns 0.399985 - exactly 1/l smaller. The function is also scale-invariant, returning the same 0.03554685 for a series scaled by 1.0, 0.5 and 0.1, where true Kelly moves 2.86, 5.72 and 28.62. Issue #535 frames this as discrete versus continuous Kelly, which I think is not quite the sharpest statement of it.
2. The risk-free rate is selected by caller name. _prepare_returns inspects inspect.stack(), with the effect that cagr(r, rf=0.00) and cagr(r, rf=0.50) both return 0.2163540607973844. The separate if rf > 0 guard also discards negative rates everywhere. That the intent differs is visible inside reports.py, which passes rf at line 1314 and 0.0 at lines 1550-1563; at present all five paths return the same number.
Two further items worth a look: aggregate_returns silently no-ops on its documented 'M', 'Q' and 'Y' arguments, so best(aggregate='Y') returns the best single day, and raises AttributeError on 'W' since .week was removed in pandas 2.0; and information_ratio is not annualized while sharpe is, a gap of exactly sqrt(252).
For balance, I looked at issue #514 (arithmetic versus geometric active return) and think it should be declined - arithmetic active return is the Grinold-Kahn convention and matches your docstring. Issue #518 did not reproduce for me, and #493 looks like correct period statistics rather than a defect.
I have a patch: repro goes 26 failing to 33/33, and your suite stays at 125 passing with nothing weakened. Glad to open a PR.
Both findings are backed by closed-form identities, so the expected values are checkable independently of my code.
1.
kelly_criterionreturns the growth-optimal fraction multiplied by the average loss. On a two-outcome series with p = 0.60, win +2%, loss -1%, the closed-form Kelly fraction is 39.9985 and the function returns 0.399985 - exactly1/lsmaller. The function is also scale-invariant, returning the same 0.03554685 for a series scaled by 1.0, 0.5 and 0.1, where true Kelly moves 2.86, 5.72 and 28.62. Issue #535 frames this as discrete versus continuous Kelly, which I think is not quite the sharpest statement of it.2. The risk-free rate is selected by caller name.
_prepare_returnsinspectsinspect.stack(), with the effect thatcagr(r, rf=0.00)andcagr(r, rf=0.50)both return0.2163540607973844. The separateif rf > 0guard also discards negative rates everywhere. That the intent differs is visible insidereports.py, which passesrfat line 1314 and0.0at lines 1550-1563; at present all five paths return the same number.Two further items worth a look:
aggregate_returnssilently no-ops on its documented'M','Q'and'Y'arguments, sobest(aggregate='Y')returns the best single day, and raisesAttributeErroron'W'since.weekwas removed in pandas 2.0; andinformation_ratiois not annualized whilesharpeis, a gap of exactlysqrt(252).For balance, I looked at issue #514 (arithmetic versus geometric active return) and think it should be declined - arithmetic active return is the Grinold-Kahn convention and matches your docstring. Issue #518 did not reproduce for me, and #493 looks like correct period statistics rather than a defect.
I have a patch: repro goes 26 failing to 33/33, and your suite stays at 125 passing with nothing weakened. Glad to open a PR.