QuantConnect Documentation

repository·master·Indexed 42 days ago

https://github.com/quantconnect/documentation

Guides and API references for the LEAN algorithmic trading engine, an open-source C# engine for desktop and cloud environments. Includes documentation on using QuantBook for research, retrieving historical data, and implementing quantitative strategies such as cointegration tests, VECM, and Kalman Filters.

Tokens
381.2K
Snippets
566
Records
742
Agent score
53%

What's inside QuantConnect

  1. How to read Fundamental data paths for Asset Classification

    master

    When accessing Fundamental data points in QuantConnect/LEAN, you must use the exact path from the snapshot f. Paths follow specific rules depending on whether they are single-period or multi-period fields:

    1. Single-period fields: Read the path directly. Example: f.ValuationRatios.PERatio.
    2. Multi-period fields (MultiPeriodField): These paths end in a bracketed period identifier like .[1M 2M 3M 6M 9M 12M]. You must append the corresponding period accessor to the path to retrieve the numeric value.
      • .Value retrieves the most recent reported period.
      • .OneMonth, .TwoMonths, .ThreeMonths, .SixMonths, .NineMonths, and .TwelveMonths correspond to the 1M through 12M tokens.

    Warning: Forgetting the period accessor on a MultiPeriodField will result in the wrapper being treated as a truthy object rather than a number, causing numeric inequalities to fail or return nonsense.

    // Example of a single-period field
    var peRatio = f.ValuationRatios.PERatio;
    
    // Example of a multi-period field (must use the accessor)
    // Path: f.FinancialStatements.IncomeStatement.NetIncome.12M
    var trailingTwelveMonthNetIncome = f.FinancialStatements.IncomeStatement.NetIncome.TwelveMonths;
    
    // Using the most recent period
    var recentNetIncome = f.FinancialStatements.IncomeStatement.NetIncome.Value;
  2. Compute custom or intraday historical IV/Greeks (Route B)

    master

    Use Route B when you need intraday (minute/second) historical values or when you need to customize the computation (e.g., averaging the IV of an ATM call and put pair).

    Core Concepts:

    • Indicators: You must build an indicator (e.g., IV, Delta, Gamma) on a specific contract.
    • Mirroring: To compute custom values like an ATM average, use the 'mirror' option (the paired call/put).
    • Data Requirements: For manual updates, you typically need QUOTE bars for the options and TRADE bars for the underlying.
    • Historical Replay: Use self.indicator_history to request historical data and replay it through the indicator to generate a historical series.
  3. Use non-short-circuit operators for indicator updates

    master

    When updating multiple indicators within a single selection callback, use the non-short-circuit operator (& in Python and C#) instead of the short-circuit operator (and or &&).

    Why? Indicator .update() methods have side effects (they advance the indicator's internal state). If you use a short-circuit operator and the first indicator is not ready, the second indicator's .update() method will never be called, causing the indicators to desynchronize.

    Note: This rule applies only to the update calls. For pure boolean readiness checks (e.g., ind1.is_ready and ind2.is_ready), short-circuiting is correct and preferred.

    # CORRECT: Both indicators advance every bar
    return (ind1.update(data) & ind2.update(data))
    
    # INCORRECT: ind2.update() might be skipped if ind1.update() returns False
    return (ind1.update(data) and ind2.update(data))
  4. How to handle suspected data issues in backtests

    master

    When a backtest 'blows up' (e.g., catastrophic negative returns like -100,000%, PortfolioValueIsNotPositiveAnalysis errors, or equity dropping far below zero), it is often caused by corrupted data rather than position-sizing errors.

    Common data issue patterns to look for:

    • Spike-and-revert: A price jumps orders of magnitude and snaps back within a few bars (e.g., $1 → $10,000 → $1).
    • Flat-line / missing data: A long stretch of no price movement or absent bars (e.g., ~10 days of a dead flat line).

    Important: Do not attempt to fix data issues by adjusting risk parameters like minimum position weight, leverage caps, or disabling margin calls. Instead, confirm the anomaly in the Research Environment and then block the specific asset by its Security ID.

  5. Avoid error-hiding patterns during debugging

    master

    When debugging performance or errors, never introduce patterns that mask the true behavior of the code. These patterns hide the exact failure or the slow path that a profiler is trying to measure.

    Banned Patterns:

    • Python: try/except, hasattr, getattr, setattr, isinstance.
    • C#: try/catch, Reflection.

    If existing code wraps a suspected slow region in one of these patterns, remove it, re-run the backtest, and observe the real behavior before continuing your optimization.

  6. Convert CryptoUniverse data to TradeBar for indicators

    master

    Some indicators (like AverageTrueRange or candle patterns) require OHLC (Open, High, Low, Close) data. The raw data points provided in a CryptoUniverse selection callback do not implement the IBaseDataBar interface required by these indicators.

    To use them, you must manually construct a TradeBar from the CryptoUniverse object's fields (open, high, low, close, volume_in_usd, etc.).

    # Example: Wrapping CryptoUniverse data for an ATR indicator
    def update(self, c):
        return self.atr.update(TradeBar(c.end_time, c.symbol, c.open, c.high, c.low, c.close, c.volume_in_usd))
  7. Automatic vs Manual Technical Indicators

    master

    QuantConnect provides two ways to manage technical indicators: Automatic and Manual. Choosing the right one depends on whether the indicator uses the security's standard data or custom inputs.

    Automatic Indicators

    Use these when the indicator needs the security's standard bars (e.g., Close price). You create them using QCAlgorithm helper methods in initialize. These indicators are automatically registered and updated by the engine.

    Important Rules:

    • The indicator's resolution must be ≥ the security's subscription resolution.
    • Do NOT call register_indicator(...) or .update(...) on an automatic indicator; doing so will cause it to receive data twice and produce incorrect values.
    • Do NOT assign a helper indicator to a variable name that shadows the factory method (e.g., avoid self.roc = self.roc(...)).

    Manual Indicators

    Use these when you need to feed the indicator non-standard data, such as:

    • A custom field (not the default Close).
    • Consolidated bars (e.g., 30-minute bars).
    • Custom data sources or values you compute yourself.

    To use a manual indicator, construct the indicator object directly and then either register it with a specific resolution/consolidator or call .update(time, value) yourself.

    # Automatic: 252-bar rate of change, auto-updated
    roc = self.roc(symbol, 252, Resolution.DAILY)
    
    # Manual: Construct directly and update manually
    roc = RateOfChange(252)
    roc.update(bar.end_time, bar.close)
  8. When to use History() vs Indicators

    master

    Use History() for one-off needs such as seeding a signal at startup, a single lookback, or retrieving a specific recent past value (e.g., yesterday's close).

    Do not use History() for rolling statistics that need to be recomputed on every bar (e.g., moving averages, momentum, or volatility). Instead, register a streaming indicator once and read its current value. Using History() for rolling calculations is slow and prone to errors regarding the 'as-of' bar.

  9. Understand logging quotas and limits

    master

    Logging is quota-bounded and fails silently once the budget is exceeded. Quotas are based on your subscription tier and are measured in two ways: per backtest and per day. Quotas restore on a 24-hour rolling window.

    TierLogs per backtestLogs per day
    Free10 KB3 MB
    Quant Researcher100 KB3 MB
    Team1 MB10 MB
    Trading Firm5 MB50 MB
    Institution∞∞

    Live trading has a separate limit of 100,000 lines per project, retained for up to one year.

  10. Handle JSON arrays with UnfoldingCollection

    master

    If your data source is a JSON file containing an array of records (rather than one record per line), use FileFormat.UnfoldingCollection.

    In your Reader implementation, you must parse the entire array, create a BaseDataCollection containing all objects, and return it. Ensure the objects are sorted by EndTime before returning.

    import json
    
    class MyCustomData(PythonData):
        def get_source(self, config, date, is_live_mode):
            return SubscriptionDataSource(
                "custom-data/custom-news-releases.json",
                SubscriptionTransportMedium.OBJECT_STORE,
                FileFormat.UNFOLDING_COLLECTION
            )
    
        def reader(self, config, line, date, is_live_mode):
            rows = json.loads(line)
            objects = []
            for row in rows:
                data = MyCustomData()
                data.symbol = config.symbol
                data.time = datetime.strptime(row["date"], "%Y-%m-%d")
                data.end_time = data.time + timedelta(days=1)
                data["headline"] = row["headline"]
                data["impact"] = float(row["impact"])
                data.value = data["impact"]
                objects.append(data)
            
            objects.sort(key=lambda x: x.end_time)
            return BaseDataCollection(objects[-1].end_time, config.symbol, objects) if objects else None
  11. Handling Fee, Slippage, and Transaction Cost models

    master

    QuantConnect provides realistic default models for fees, slippage, and fills.

    Rules for implementation:

    • Do not override defaults unless the specification explicitly mandates a specific custom model. Phrases like "account for realistic costs" mean you should use the platform defaults, not implement your own.
    • Never fake or disable transaction costs: Do not use SetFeeModel(new ConstantFeeModel(0)) or similar to emulate paper trading assumptions.
    • Do not mutate cash balances directly: Never manually modify Portfolio.CashBook or cash balances. This corrupts accounting and bypasses the platform's reality models.

    If a specification provides a cost formula (e.g., "X bps per trade"), treat it as a reporting convention rather than a requirement to implement a custom model in the engine.