Skip to content

fix: Calmar ratio sign for losing portfolios + DCA cutoff logic#1

Open
marcm0de wants to merge 1 commit into
Acelogic:masterfrom
marcm0de:fix/calmar-ratio-dca-cutoff
Open

fix: Calmar ratio sign for losing portfolios + DCA cutoff logic#1
marcm0de wants to merge 1 commit into
Acelogic:masterfrom
marcm0de:fix/calmar-ratio-dca-cutoff

Conversation

@marcm0de

Copy link
Copy Markdown

Summary

Two logic bug fixes in the backtesting core.

1. Calmar Ratio reports positive values for losing portfolios

File: app/core/calculations/stats.py, line 275

Problem: The formula abs(cagr / mdd) always produces a positive result. When a portfolio has negative CAGR (lost money) and negative MDD, the division yields a positive number, and abs() keeps it positive. This makes a losing portfolio look like it has a decent risk-adjusted return.

Example:

  • CAGR = -5%, MDD = -30%
  • Before: abs(-5 / -30) = 0.167 ← incorrectly positive
  • After: -5 / abs(-30) = -0.167 ← correctly negative

Fix: Changed to cagr / abs(mdd) so the Calmar ratio preserves the sign of CAGR.

2. DCA silently killed from day 1 when dca_in_retirement=False

File: app/core/shadow_backtest.py, line 597

Problem: The DCA cutoff logic uses draw_start_date or retirement_date. Since draw_start_date is always set (the orchestrator defaults it to the backtest start date), Python's or short-circuits and always picks draw_start_date. When a user disables DCA in retirement, their DCA contributions are stopped from the very first day of the simulation — $0 invested over the entire backtest.

Fix: Changed to use retirement_date directly, which is the actual intended cutoff for DCA contributions when dca_in_retirement=False.

1. Calmar Ratio (stats.py):
   abs(cagr / mdd) incorrectly reports positive Calmar for losing
   portfolios (negative CAGR / negative MDD = positive). Changed to
   cagr / abs(mdd) so the sign correctly reflects portfolio direction.

2. DCA Cutoff (shadow_backtest.py):
   When dca_in_retirement=False, the cutoff used draw_start_date
   (which defaults to backtest start) via Python's 'or' short-circuit,
   silently killing all DCA from day one. Fixed to use retirement_date
   directly, which is the actual intended cutoff for DCA contributions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant