QuantConnect/Lean · error · AssertionError

Unexpected columns in SPY tick history

Error message

Unexpected columns in SPY tick history

What it means

Regression assertion that the SPY tick history dataframe has exactly the expected column set: askprice, asksize, bidprice, bidsize, exchange, lastprice, quantity. It uses np.array_equal against the sorted columns, so both the names and the exact set must match. The assertion fires if Lean's PandasData column layout for Tick data changed or columns were added/renamed/missing.

Source

Thrown at Algorithm.Python/PandasDataFrameFromMultipleTickTypeTickHistoryRegressionAlgorithm.py:42

        self.set_end_date(2013, 10, 8)

        spy = self.add_equity("SPY", Resolution.MINUTE).symbol

        subscriptions = [x for x in self.subscription_manager.subscriptions if x.symbol == spy]
        if len(subscriptions) != 2:
            raise AssertionError(f"Expected 2 subscriptions, but found {len(subscriptions)}")

        history = pd.DataFrame()
        try:
            history = self.history(Tick, spy, timedelta(days=1), Resolution.TICK)
        except Exception as e:
            raise AssertionError(f"History call failed: {e}")

        if history.shape[0] == 0:
            raise AssertionError("SPY tick history is empty")

        if not np.array_equal(history.columns.to_numpy(), ['askprice', 'asksize', 'bidprice', 'bidsize', 'exchange', 'lastprice', 'quantity']):
            raise AssertionError("Unexpected columns in SPY tick history")

        self.quit()

View on GitHub (pinned to d2c3659f87)

Solutions

  1. Log the actual columns (list(history.columns)) to see which name differs.
  2. If a column was renamed/added on purpose in a Lean change, update the expected array to match the new contract.
  3. If the frame shape is wrong (e.g. Series), ensure the request returns a DataFrame — request a single symbol but keep the multi-index path, or inspect dtype.
  4. Align the pandas version with what the dataframe builder expects.

Example fix

# before
if not np.array_equal(history.columns.to_numpy(), ['askprice','asksize','bidprice','bidsize','exchange','lastprice','quantity']):
    raise AssertionError("Unexpected columns in SPY tick history")

# after (report the diff so the mismatch is obvious)
expected = ['askprice','asksize','bidprice','bidsize','exchange','lastprice','quantity']
actual = sorted(history.columns.astype(str).tolist())
if sorted(expected) != actual:
    raise AssertionError(f"Unexpected columns in SPY tick history: {actual}")
Defensive patterns

Strategy: validation

Validate before calling

# Compare as sorted sets and report the diff
import numpy as np
expected = ['askprice','asksize','bidprice','bidsize','exchange','lastprice','quantity']
actual = sorted(map(str, history.columns))
if sorted(expected) != actual:
    self.debug(f"Column mismatch. expected={expected} actual={actual}")

Type guard

def columns_match(history, expected):
    """True when the frame columns equal the expected set (order-insensitive)."""
    return set(map(str, history.columns)) == set(expected)

Prevention

When it happens

Trigger: history.columns.to_numpy() does not equal the expected 7-name array. This happens after a change to the Tick property/column mapping in PandasData (e.g. a column renamed, a new field added like 'suspectedfalsy', or a column dropped), or when the returned frame is a different shape (e.g. transposed, or a Series instead of a DataFrame).

Common situations: Lean contributors hit this after modifying PandasData.GetFrame / the Tick data column generation, or after a refactor of the BaseData-to-pandas column mapping. Users hit it after upgrading Lean if the tick column contract changed, or when the history call unexpectedly returns a different dtype/layout (e.g. due to a pandas version difference).

Related errors


AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13). Data as JSON: /api/errors/4a33d6fafdbcfc4e. Report an issue: GitHub.