Stock Splits applied pending start/end dates

If I call for a small subset of data (1 month) I seem to get non-split prices. If I call for a large amount, then it seems splits are applied?
An equivalent 1min dataset from IBKR produces splits in both scenarios (~$4).

Quoting from the usage guide:

There is a subtle difference in how adjustments are applied in the history database vs the Zipline bundle.

In the history database, all available adjustments are applied to the data at the time of collection, and the data are stored in an adjusted state. In the Zipline bundle, data are stored unadjusted, and adjustments are applied on-the-fly at query time. Moreover, Zipline only applies those adjustments that would have occurred on or before the end date of your query.

Both of these approaches result in a continuous price series that is free of artificial jumps and is suitable for quantitative analysis. However, depending on the date range of your query, the absolute price level may differ based on whether you query the Zipline bundle or the history database. To illustrate with an example, Apple stock underwent a 4-for-1 split on August 31, 2020. The price before the split was around $500, while the price after the split was around $125. If you query the period just before (but not including) the split date, the history database will return a price of around $125 (the split-adjusted price), because the 4-for-1 split will have already been applied to the stored data. In contrast, the Zipline bundle will return a price of around $500, because the 4-for-1 split falls after the query window and thus Zipline does not apply that particular split at query time. For most use cases, this distinction is immaterial. But if your analysis depends on the absolute price level, the Zipline bundle may be preferred because the absolute prices more accurately reflect their historical point-in-time values.

Thank you. With this behavior, does this mean segment="A" will be unreliable as some years there will be splits and others there will not, thus it will append results of some adjusted years and some unadjusted years?

backtest('Code',
         no_cache=True, # make sure strategy starts fresh
         segment="A", # segment to yearly for memory constraints, then append results together
         filepath_or_buffer=DATA_OUTPUT)

I'm not seeing any potential problem. Each segment will run on a continuous price series, fully adjusted as of the segment end date.

I thought so too but I'm seeing this. Ticker TQQQ: FIBBG000QB9Y48 for ref.

You're right, I see now. Running a segmented backtest with a Zipline bundle does indeed cause a problem with the benchmark plot if the benchmark security has had one or more splits. The reason is that Moonshot stores the benchmark prices in the results dataframe. It's fine for Moonshot to concatenate returns and weights across segments, but not prices as that will result in jumps since splits are applied differently to different segments.

The best solution may be for Moonshot to return benchmark returns instead of benchmark prices.

This issue is fixed in QuantRocket 2.10.0. Moonshot now returns benchmark returns instead of benchmark prices. This avoids the problem of price jumps across segmented backtests when the benchmark has undergone splits.

1 Like

Thanks Brian!