| 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 (
add_data) has its OWN symbol
(the return value), not the equity symbol; reading alt data with the equity
symbol is the usual cause. Key by add_data(...).symbol;
field names from the QC docs page (Section 4), never getattr.
Skipping a wrong key with a check just turns the crash into 0 trades
(Section 3). symbol in data is right only for a real
fillforward gap on a correct key.
- None / null price:
seed_initial_prices only looks
back 3 days; keep only securities where security.price
is set.
- Insufficient buying power: reduce exposure, then derivative sizing, then raise
free_portfolio_value_percentage; add
liquidate_existing_holdings=True; if a
9:00 schedule still fails, move it to 9, 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, so set_holdings runs with
an empty list -> 0 orders / flat equity.
- Too loose: the filter passes a very large set (e.g. 1000+) into an
equal-weight
set_holdings; LEAN cannot size that many
positions and silently places no orders.
Measure with self.log(...) 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: never
set_runtime_statistic or the Object Store; never
self.log in on_data or an often-firing scheduled
event (it floods); if unavoidable there, gate it to the first N calls with a
counter.
- Schedule / frequency mismatch:
- Daily-resolution data with an intraday TimeRule (e.g.
time_rules.after_market_open) fires before
the daily bar is delivered, so the rebalance reads stale/empty data. With
daily data schedule via self.date_rules... +
self.time_rules.at(8, 0); use an intraday
TimeRule only with minute/hour data.
- Rebalance reads
self._universe.selected on a
different/earlier frequency than the universe updates, so it sees an empty
set. Align both to the same DateRule, or drive the rebalance from
on_securities_changed.
- Universe empty / wrong
Universe.UNCHANGED return (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:
automatic_indicator_warm_up = True
set BEFORE any factory call; feed manual indicators/consolidators in
initialize via 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
getattr guessing.
- 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:
np.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
add_data symbol; 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
self.log; selection not so large set_holdings
silently 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.