fix(auto-fixer): disable _fix_min_periods for intraday data

The fixer was raising min_periods to match window size, which causes
all-NaN output for intraday factors with 96 bars/day — window=240 means
zero valid bars per day, window=60 means 61% NaN per day. Critics were
consistently flagging this as incorrect for intraday factors. The LLM
now controls its own min_periods.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
TPTBusiness
2026-04-26 18:59:58 +02:00
parent 8aec974702
commit ba4d64b434
2 changed files with 19 additions and 3 deletions
@@ -51,15 +51,17 @@ class FactorAutoFixer:
self.fixes_applied = []
fixed_code = code
# Apply fixes in order - groupby fixes MUST come before min_periods fixes
# Apply fixes in order
# NOTE: _fix_min_periods is intentionally excluded — it increased min_periods to
# match window size, which causes all-NaN output for intraday data with 96 bars/day
# (window=240 > 96 means zero valid bars per day). The LLM sets its own min_periods.
fix_methods = [
self._fix_reset_index_groupby, # First: fix groupby(level=N) after reset_index()
self._fix_groupby_mixed_levels, # Second: fix groupby(level=[int, str])
self._fix_groupby_column_on_multiindex, # Third: fix groupby(['instrument','date']) on MultiIndex
self._fix_chained_groupby, # Fourth: fix groupby(level=N).groupby('date') chain
self._fix_rolling_ddof, # Fifth: remove unsupported ddof kwarg
self._fix_groupby_apply_to_transform, # Fifth: fix groupby patterns
self._fix_min_periods, # Sixth: fix min_periods in rolling calls
self._fix_groupby_apply_to_transform, # Sixth: fix groupby patterns
self._fix_inf_nan_handling, # Seventh: add inf/nan handling
self._fix_data_range_processing, # Eighth: ensure full data range
self._fix_multiindex_groupby, # Ninth: ensure groupby on MultiIndex
+14
View File
@@ -73,6 +73,20 @@ class TestChainedGroupby:
assert '.groupby("date")' not in result
class TestMinPeriodsNotTouched:
def test_small_min_periods_preserved(self, fixer):
# _fix_min_periods is disabled — LLM-set min_periods must not be changed.
# window=60, min_periods=1 should stay as-is (was wrongly raised to 60 before).
result = fixer.fix("df.groupby(level=1)['x'].transform(lambda x: x.rolling(window=60, min_periods=1).mean())")
assert "min_periods=1" in result
def test_large_window_min_periods_preserved(self, fixer):
# window=240 > 96 bars/day: if min_periods were set to 240 the output would be
# all-NaN for intraday data. Verify we leave it untouched.
result = fixer.fix("df['x'] = df.groupby(level=1)['y'].transform(lambda x: x.rolling(240, min_periods=10).std())")
assert "min_periods=10" in result
class TestRollingDdof:
def test_removes_ddof_from_rolling_args(self, fixer):
result = fixer.fix("df.rolling(20, min_periods=1, ddof=1).std()")