EUR/USD synthetic data has \$volume=0 for all rows, causing any VWAP or
volume-weighted factor to produce all-NaN output. Insert a guard after
pd.read_hdf() that replaces zero volume with (\$high - \$low) range proxy
so volume-dependent factors produce meaningful signals.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
LLM sometimes copies the .reset_index(level=N, drop=True) suffix from
groupby().rolling().method() patterns and adds it after .transform(),
but transform() already preserves the original index. The extra
reset_index() drops an index level and causes ValueError: 'cannot reindex
on an axis with duplicate labels' or shape mismatch on assignment.
Detect: any line containing both .transform( and .reset_index(level=..., drop=True)
Fix: strip the .reset_index() suffix from those lines.
Adds 1 new test (test_transform_reset_index_stripped) — total 30 tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. _fix_instrument_column_access: var['instrument'] = EXPR was incorrectly
converted to var.index.get_level_values(1) = EXPR, producing a SyntaxError
('cannot assign to function call'). Added (?!\s*=) negative lookahead to
skip assignment targets.
2. _fix_groupby_column_on_multiindex: groupby(['instrument','date']) on a
reset_index() variable was converted to groupby([var.index.get_level_values...])
but reset_index() produces a plain RangeIndex, not a MultiIndex, causing
AttributeError: 'RangeIndex' has no attribute 'normalize'. Added reset_vars
guard to skip variables produced by reset_index().
Adds 1 new test (test_assignment_target_not_touched) — total 29 tests, all passing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. groupby(level=['instrument','date']) → get_level_values() — string level
names like 'date' don't exist in the (datetime, instrument) MultiIndex;
replaced with get_level_values(0).normalize() + get_level_values(1).
2. groupby(level=['date','instrument']) — symmetric fix for reversed order.
3. groupby(level=['instrument']) → groupby(level=1) — single string level.
4. groupby(level=N)['col'].apply(lambda) → transform(lambda) — apply() on a
grouped Series prepends an extra index level, causing index shape mismatch
when assigned back; transform() preserves the original index.
5. df.loc[instrument] DateParseError fix (instrument_loc_multiindex) — already
committed, adding supporting tests for groupby(level=['instrument','date']).
Adds 5 new tests (TestGroupbyLevelStringNames, TestGroupbyApplyToTransform)
— total 28 tests, all passing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When LLM iterates over instruments via get_level_values('instrument').unique()
and then does df.loc[instrument], pandas tries to parse the instrument string
('EURUSD') as a datetime against level-0 of the (datetime, instrument) index,
raising DateParseError.
Fix: detect loop variables bound to get_level_values(1) or get_level_values('instrument')
and replace DF.loc[loop_var] (read) with DF.xs(loop_var, level=1). Assignment
write-backs are left untouched to avoid complex rewrites.
Adds 4 new tests (TestInstrumentLocMultiindex) — total 23 tests, all passing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
LLM-generated code often accesses df['instrument'] as a column, but
'instrument' is an index level (level 1) in the MultiIndex DataFrame.
Replace with df.index.get_level_values(1) except when the variable
was created via reset_index() (where the column actually exists).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
LLM generates invalid Python by putting keyword args inside lists:
df.groupby([level=1, 'date']) ← SyntaxError
Also fixes the regex for the chained groupby Pattern A/B which had
an unescaped ')' causing re.error that silently reverted the fix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>