From bd5a5e0fd55947bbb15f9766ef82b51a0c57f61d Mon Sep 17 00:00:00 2001 From: TPTBusiness Date: Sun, 26 Apr 2026 08:53:40 +0200 Subject: [PATCH] fix(auto-fixer): remove ddof from rolling() args, not only from std()/var() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LLM generates x.rolling(window=N, ddof=1).std() where ddof is passed to rolling() instead of std() — pandas raises TypeError on any ddof in rolling(). Fix both forms: rolling(..., ddof=N) and rolling(...).std(ddof=N). Co-Authored-By: Claude Sonnet 4.6 --- .../coder/factor_coder/auto_fixer.py | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/rdagent/components/coder/factor_coder/auto_fixer.py b/rdagent/components/coder/factor_coder/auto_fixer.py index 12656a09..5acb8997 100644 --- a/rdagent/components/coder/factor_coder/auto_fixer.py +++ b/rdagent/components/coder/factor_coder/auto_fixer.py @@ -153,16 +153,25 @@ class FactorAutoFixer: def _fix_rolling_ddof(self, code: str) -> str: """ - Fix: pandas rolling().std(ddof=N) is not supported — ddof is ignored or - raises TypeError depending on pandas version. Remove the ddof kwarg. + Fix: pandas rolling() does not accept a ddof kwarg — raises TypeError. + Remove ddof from both rolling(..., ddof=N) and rolling(...).std(ddof=N). """ fixed_code = code - pattern = r'(\.rolling\([^)]+\)\.\w+\([^)]*),\s*ddof\s*=\s*\d+([^)]*\))' - if re.search(pattern, fixed_code): - fixed_code = re.sub(pattern, r'\1\2', fixed_code) - self.fixes_applied.append("rolling_ddof: removed unsupported ddof kwarg") - # Also handle ddof as only arg: .std(ddof=1) → .std() - fixed_code = re.sub(r'\.(std|var)\(ddof\s*=\s*\d+\)', r'.\1()', fixed_code) + + # Form 1: ddof inside rolling() — .rolling(window=N, min_periods=M, ddof=K) + def _strip_ddof_from_rolling(m): + inner = re.sub(r',?\s*ddof\s*=\s*\d+', '', m.group(1)) + inner = inner.strip(', ') + self.fixes_applied.append("rolling_ddof: removed ddof from rolling()") + return f'.rolling({inner})' + + fixed_code = re.sub(r'\.rolling\(([^)]*ddof\s*=\s*\d+[^)]*)\)', _strip_ddof_from_rolling, fixed_code) + + # Form 2: ddof inside .std() / .var() — .std(ddof=N) + if re.search(r'\.(std|var)\([^)]*ddof\s*=\s*\d+', fixed_code): + fixed_code = re.sub(r'\.(std|var)\([^)]*ddof\s*=\s*\d+[^)]*\)', r'.\1()', fixed_code) + self.fixes_applied.append("rolling_ddof: removed ddof from std()/var()") + return fixed_code def _fix_min_periods(self, code: str) -> str: