name: debug-algorithm description: > Diagnoses QuantConnect Python (.py) and C# (.cs) algorithm failures: runtime exceptions and zero-trade backtests. Invoked by other agents the moment an algorithm throws a runtime error or completes a backtest with 0 orders. Walks an ordered checklist to the root cause without hiding it. Trigger phrases: "runtime error", "stack trace", "0 orders", "no trades", "flat equity curve", "unknown property", "AttributeError", "KeyNotFoundException", "wasn't found in the DataDictionary", "insufficient buying power", "indicator not ready", "debug the algorithm".
/debug-algorithm - QuantConnect Cross-Language Debugging
Invoked on a runtime error or a 0-order backtest. Work top to bottom. Find the
real cause; never hide it. Same rules for main.py and main.cs.
0. Never add error-hiding patterns
Never introduce pytry/exceptcstry/catch or
pyhasattr/getattr/setattr/isinstancecsreflection. They hide the exact
failure. If existing code wraps the failing region in one, remove it, re-run,
read the real exception, then continue. Only add an if check to skip a known,
expected case (such as a header line) by testing that exact condition. Adding a
check just to make a crash or 0 trades go away is not a fix; fix the cause.
1. Shorten the backtest, then find the failure type
Record the original pyset_start_date/set_end_datecsSetStartDate/SetEndDate
and universe, then shrink both: the smallest window that still reproduces the
failure (runtime error: a span around the error date; 0 trades: 3 to 6 months),
and the universe to a small fixed symbol list or tight filter. Only after the
fix gives non-zero orders with no runtime error, restore the original dates and
universe and run ONE final full-period backtest to confirm.
Match the failure type:
- Runtime exception -> Section 2.
- 0 orders / flat equity -> Section 3.
2. Runtime exception
Read the actual message and line; never guess.
- Unknown property or method / AttributeError / "no attribute" -> Section 4.
- KeyNotFound / "wasn't found in the DataDictionary": find WHY the key is
absent first. Custom/alt data (
AddData) has its OWN symbol (the return value), not the equity symbol; reading alt data with the equity symbol is the usual cause. Key byAddData(...).Symbol; field names from the QC docs page (Section 4), neverreflection. Skipping a wrong key with a check just turns the crash into 0 trades (Section 3).ContainsKeyis right only for a real fillforward gap on a correct key. - None / null price:
SeedInitialPricesonly looks back 3 days; keep only securities wheresecurity.Priceis set. - Insufficient buying power: reduce exposure, then derivative sizing, then raise
FreePortfolioValuePercentage; addliquidateExistingHoldings: true; if a 9:00 schedule still fails, move it to9, 31(sizes on intraday price).
3. 0 trades / 0 orders
Stop at the first that applies. NEVER add or tighten a check here.
- Selection too strict OR too loose (the usual flat equity / silent-fail
cause).
- Too strict: a long chain of
if (...) continue;checks drops every candidate, soSetHoldingsruns with an empty list -> 0 orders / flat equity. - Too loose: the filter passes a very large set (e.g. 1000+) into an
equal-weight
SetHoldings; LEAN cannot size that many positions and silently places no orders. Measure withLog(...)in the rebalance/filter: log the count passing each check and the final selected count. Find the one check responsible, then loosen or tighten just it. Common cause: data an earlier bug left empty (e.g. a signal dict from the Section 2 wrong-symbol bug), or thresholds too strict to ever happen together. Logging rules: neverSetRuntimeStatisticor the Object Store; neverLoginOnDataor an often-firing scheduled event (it floods); if unavoidable there, gate it to the first N calls with a counter.
- Too strict: a long chain of
- Schedule / frequency mismatch:
- Daily-resolution data with an intraday TimeRule (e.g.
TimeRules.AfterMarketOpen) fires before the daily bar is delivered, so the rebalance reads stale/empty data. With daily data schedule viaDateRules...+TimeRules.At(8, 0); use an intraday TimeRule only with minute/hour data. - Rebalance reads
Universe.Selectedon a different/earlier frequency than the universe updates, so it sees an empty set. Align both to the same DateRule, or drive the rebalance fromOnSecuritiesChanged.
- Daily-resolution data with an intraday TimeRule (e.g.
- Universe empty / wrong
Universe.UNCHANGEDreturn (only a filter that just stores symbols and selects nothing returns it; a real selection returns a list). - Securities dropped for missing price (see Section 2).
- Indicators never ready:
AutomaticIndicatorWarmUp = trueset BEFORE any factory call; feed manual indicators/consolidators inInitializevia a history loop; warm-up length fits the rebalance frequency (weekly 14d, monthly or slower 45d). Futures/continuous contracts need this.
4. Unknown property or method
Do not find a property or method by pyhasattr/getattrcsreflection. Find
the real property/field name on the dataset's or type's QC docs page, then use
it directly.
Quick rules:
- Alt-data fields (headline, body, sentiment, etc.) come from that dataset's
page, never from
reflectionguessing. - Comparing indicators: use
indicator.Current.Value. Direct object comparison only works when both are the same indicator type; different types (or an indicator vs a sub-indicator) need the explicit.current.value. Previous value:indicator.Previous.Value, not.window[0]. - Fundamental data missing:
double.IsNaN(value), never!= 0. - Yesterday's daily OHLC:
security.Session(set.size; index[1]is yesterday). Change one access at a time, then re-run.
5. Checklist
- No banned pattern (Section 0).
- Original dates + universe recorded before shrinking.
- Fix targets the real exception line, not a guess.
- Alt/custom data keyed by the
AddDatasymbol; presence checks only for real fillforward gaps. - Unknown properties/fields taken from the QC docs page, not guessed.
- No check added/tightened to mask 0 trades; cause found via
Log; selection not so largeSetHoldingssilently no-ops (Section 3). - Universe and rebalance same frequency; schedule matches data (daily not on an intraday TimeRule, DateRule symbol subscribed); indicators warmed; no buying-power rejection.
- Original period + universe restored; ONE final backtest confirms non-zero orders and no error.