Sell-gap backtest INTERNAL SERVER ERROR

Problem statement: The first cell of the Sell-gap part5 notebook not working

How to reproduce:

  1. Clone Sell-gap repo
from quantrocket.codeload import clone
clone("sell-gap")
  1. Run part1 ~ part 4 notebooks
  2. Open part5 notebook and run the first cell
from quantrocket.zipline import backtest
backtest(
    "sell-gap", 
    start_date="2015-01-03", 
    end_date="2020-08-18", 
    capital_base=2e4,
    filepath_or_buffer="sell_gap_backtest_results.csv", 
    progress="M")

Expected behavior: No errors

Actual behavior: See error message below


HTTPError: ('500 Server Error: INTERNAL SERVER ERROR for url: http://houston/zipline/backtests/sell-gap?capital_base=20000.0&start_date=2015-01-03&end_date=2020-08-18', {'status': 'error', 'msg': "Timestamp('2021-10-08 00:00:00+0000', tz='UTC')"})

Comments: I’m not sure where did this 2021-10-08 come from in the error message.

Full error logs:




quantrocket_zipline_1|# Copyright 2020 QuantRocket LLC - All Rights Reserved
quantrocket_zipline_1|#
quantrocket_zipline_1|# Licensed under the Apache License, Version 2.0 (the "License");
quantrocket_zipline_1|# you may not use this file except in compliance with the License.
quantrocket_zipline_1|# You may obtain a copy of the License at
quantrocket_zipline_1|#
quantrocket_zipline_1|#     http://www.apache.org/licenses/LICENSE-2.0
quantrocket_zipline_1|#
quantrocket_zipline_1|# Unless required by applicable law or agreed to in writing, software
quantrocket_zipline_1|# distributed under the License is distributed on an "AS IS" BASIS,
quantrocket_zipline_1|# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
quantrocket_zipline_1|# See the License for the specific language governing permissions and
quantrocket_zipline_1|# limitations under the License.
quantrocket_zipline_1|
quantrocket_zipline_1|import zipline.api as algo
quantrocket_zipline_1|from zipline.pipeline import Pipeline
quantrocket_zipline_1|from zipline.pipeline.factors import AverageDollarVolume, SimpleMovingAverage, ExponentialWeightedMovingStdDev
quantrocket_zipline_1|from zipline.pipeline.data.equity_pricing import EquityPricing
quantrocket_zipline_1|from zipline.pipeline.data.master import SecuritiesMaster
quantrocket_zipline_1|from zipline.finance.execution import MarketOrder, LimitOrder
quantrocket_zipline_1|from zipline.finance.order import ORDER_STATUS
quantrocket_zipline_1|from zipline.finance import slippage, commission
quantrocket_zipline_1|from quantrocket.realtime import collect_market_data
quantrocket_zipline_1|from codeload.sell_gap.pipeline import make_pipeline
quantrocket_zipline_1|
quantrocket_zipline_1|def initialize(context):
quantrocket_zipline_1|    """
quantrocket_zipline_1|    Called once at the start of a backtest, and once per day at
quantrocket_zipline_1|    the start of live trading.
quantrocket_zipline_1|    """
quantrocket_zipline_1|    # Attach the pipeline to the algo
quantrocket_zipline_1|    algo.attach_pipeline(make_pipeline(), 'pipeline')
quantrocket_zipline_1|
quantrocket_zipline_1|    # Set SPY as benchmark
quantrocket_zipline_1|    algo.set_benchmark(algo.sid("FIBBG000BDTBL9"))
quantrocket_zipline_1|
quantrocket_zipline_1|    # identify down gaps immediately after the opening
quantrocket_zipline_1|    algo.schedule_function(
quantrocket_zipline_1|        find_down_gaps,
quantrocket_zipline_1|        algo.date_rules.every_day(),
quantrocket_zipline_1|        algo.time_rules.market_open(minutes=1),
quantrocket_zipline_1|    )
quantrocket_zipline_1|
quantrocket_zipline_1|    # at 9:40, short stocks that gapped down
quantrocket_zipline_1|    algo.schedule_function(
quantrocket_zipline_1|        short_down_gaps,
quantrocket_zipline_1|        algo.date_rules.every_day(),
quantrocket_zipline_1|        algo.time_rules.market_open(minutes=10),
quantrocket_zipline_1|    )
quantrocket_zipline_1|
quantrocket_zipline_1|    # close positions 5 minutes before the close
quantrocket_zipline_1|    algo.schedule_function(
quantrocket_zipline_1|        close_positions,
quantrocket_zipline_1|        algo.date_rules.every_day(),
quantrocket_zipline_1|        algo.time_rules.market_close(minutes=5),
quantrocket_zipline_1|    )
quantrocket_zipline_1|
quantrocket_zipline_1|    # Set commissions and slippage
quantrocket_zipline_1|    algo.set_commission(
quantrocket_zipline_1|        commission.PerShare(cost=0.0))
quantrocket_zipline_1|    algo.set_slippage(
quantrocket_zipline_1|        slippage.FixedBasisPointsSlippage(
quantrocket_zipline_1|            basis_points=3.0))
quantrocket_zipline_1|
quantrocket_zipline_1|def before_trading_start(context, data):
quantrocket_zipline_1|    """
quantrocket_zipline_1|    Called every day before market open. Gathers today's pipeline
quantrocket_zipline_1|    output and initiates real-time data collection (in live trading).
quantrocket_zipline_1|    """
quantrocket_zipline_1|    context.candidates = algo.pipeline_output('pipeline')
quantrocket_zipline_1|    context.assets_to_short = []
quantrocket_zipline_1|    context.target_value_per_position = -50e3
quantrocket_zipline_1|
quantrocket_zipline_1|    # Start real-time data collection if we are in live trading
quantrocket_zipline_1|    if algo.get_environment("arena") == "trade":
quantrocket_zipline_1|
quantrocket_zipline_1|        # start real-time tick data collection for our candidates...
quantrocket_zipline_1|        sids = [asset.real_sid for asset in context.candidates.index]
quantrocket_zipline_1|
quantrocket_zipline_1|        if sids:
quantrocket_zipline_1|
quantrocket_zipline_1|            # collect the trade/volume data
quantrocket_zipline_1|            collect_market_data(
quantrocket_zipline_1|                "us-stk-tick",
quantrocket_zipline_1|                sids=sids,
quantrocket_zipline_1|                until="09:32:00 America/New_York")
quantrocket_zipline_1|
quantrocket_zipline_1|            # ...and point Zipline to the derived aggregate db
quantrocket_zipline_1|            algo.set_realtime_db(
quantrocket_zipline_1|                "us-stk-tick-1min",
quantrocket_zipline_1|                fields={
quantrocket_zipline_1|                    "close": "LastPriceClose",
quantrocket_zipline_1|                    "open": "LastPriceOpen",
quantrocket_zipline_1|                    "high": "LastPriceHigh",
quantrocket_zipline_1|                    "low": "LastPriceLow",
quantrocket_zipline_1|                    "volume": "VolumeClose"}) # for IBKR real-time data, use VolumeClose
quantrocket_zipline_1|
quantrocket_zipline_1|def find_down_gaps(context, data):
quantrocket_zipline_1|    """
quantrocket_zipline_1|    Identify stocks that gapped down below their moving average.
quantrocket_zipline_1|    """
quantrocket_zipline_1|
quantrocket_zipline_1|    if len(context.candidates) == 0:
quantrocket_zipline_1|        return
quantrocket_zipline_1|
quantrocket_zipline_1|    today_opens = data.current(context.candidates.index, 'open')
quantrocket_zipline_1|    prior_lows = context.candidates["prior_low"]
quantrocket_zipline_1|    stds = context.candidates["std"]
quantrocket_zipline_1|
quantrocket_zipline_1|    # find stocks that opened sufficiently below the prior day's low...
quantrocket_zipline_1|    gapped_down = today_opens < (prior_lows - stds)
quantrocket_zipline_1|
quantrocket_zipline_1|    # ...and are now below their moving averages
quantrocket_zipline_1|    are_below_mavg = (today_opens < context.candidates["mavg"])
quantrocket_zipline_1|
quantrocket_zipline_1|    assets_to_short = context.candidates[
quantrocket_zipline_1|        gapped_down
quantrocket_zipline_1|        & are_below_mavg
quantrocket_zipline_1|        ]
quantrocket_zipline_1|
quantrocket_zipline_1|    # Limit to the top 10 by std
quantrocket_zipline_1|    assets_to_short = assets_to_short.sort_values(
quantrocket_zipline_1|        "std", ascending=False).iloc[:10].index
quantrocket_zipline_1|
quantrocket_zipline_1|    context.assets_to_short = assets_to_short
quantrocket_zipline_1|
quantrocket_zipline_1|def short_down_gaps(context, data):
quantrocket_zipline_1|    """
quantrocket_zipline_1|    Short the stocks that gapped down.
quantrocket_zipline_1|    """
quantrocket_zipline_1|    for asset in context.assets_to_short:
quantrocket_zipline_1|
quantrocket_zipline_1|        # Sell with market order
quantrocket_zipline_1|        algo.order_value(
quantrocket_zipline_1|            asset,
quantrocket_zipline_1|            context.target_value_per_position,
quantrocket_zipline_1|            style=MarketOrder(exchange="SMART") # for IBKR, specify exchange (e.g. exchange="SMART")
quantrocket_zipline_1|        )
quantrocket_zipline_1|
quantrocket_zipline_1|def close_positions(context, data):
quantrocket_zipline_1|    """
quantrocket_zipline_1|    Closes all positions.
quantrocket_zipline_1|    """
quantrocket_zipline_1|    for asset, position in context.portfolio.positions.items():
quantrocket_zipline_1|        algo.order(
quantrocket_zipline_1|            asset,
quantrocket_zipline_1|            -position.amount,
quantrocket_zipline_1|            style=MarketOrder(exchange="SMART")
quantrocket_zipline_1|        )
quantrocket_zipline_1|Traceback (most recent call last):
quantrocket_zipline_1|  File "/opt/conda/lib/python3.6/site-packages/trading_calendars/utils/memoize.py", line 47, in __get__
quantrocket_zipline_1|    return self._cache[instance]
quantrocket_zipline_1|  File "/opt/conda/lib/python3.6/weakref.py", line 394, in __getitem__
quantrocket_zipline_1|    return self.data[ref(key)]
quantrocket_zipline_1|KeyError: <weakref at 0x7fe099255b38; to 'BcolzMinuteBarReader' at 0x7fe09952eef0>
quantrocket_zipline_1|
quantrocket_zipline_1|During handling of the above exception, another exception occurred:
quantrocket_zipline_1|
quantrocket_zipline_1|Traceback (most recent call last):
quantrocket_zipline_1|  File "pandas/_libs/index.pyx", line 449, in pandas._libs.index.DatetimeEngine.get_loc
quantrocket_zipline_1|  File "pandas/_libs/hashtable_class_helper.pxi", line 811, in pandas._libs.hashtable.Int64HashTable.get_item
quantrocket_zipline_1|  File "pandas/_libs/hashtable_class_helper.pxi", line 817, in pandas._libs.hashtable.Int64HashTable.get_item
quantrocket_zipline_1|KeyError: 1633651200000000000
quantrocket_zipline_1|
quantrocket_zipline_1|During handling of the above exception, another exception occurred:
quantrocket_zipline_1|
quantrocket_zipline_1|Traceback (most recent call last):
quantrocket_zipline_1|  File "sym://qrocket_app_py", line 738, in post
quantrocket_houston_1|172.18.0.12 - - [11/Oct/2020:02:56:29 +0000] "POST /zipline/backtests/sell-gap?data_frequency=minute&capital_base=20000.0&bundle=usstock-1min&start_date=2015-01-03&end_date=2020-08-18&progress=M HTTP/1.1" 500 78 "-" "-"
quantrocket_zipline_1|  File "sym://qrocket_qrzipline_backtest_py", line 110, in backtest_algo
quantrocket_zipline_1|  File "/opt/conda/lib/python3.6/site-packages/zipline/data/data_portal.py", line 192, in __init__
quantrocket_zipline_1|    for reader in [equity_minute_reader, future_minute_reader]
quantrocket_zipline_1|  File "/opt/conda/lib/python3.6/site-packages/zipline/data/data_portal.py", line 193, in <listcomp>
quantrocket_zipline_1|    if reader is not None
quantrocket_zipline_1|  File "/opt/conda/lib/python3.6/site-packages/trading_calendars/utils/memoize.py", line 49, in __get__
quantrocket_zipline_1|    self._cache[instance] = val = self._get(instance)
quantrocket_zipline_1|  File "/opt/conda/lib/python3.6/site-packages/zipline/data/minute_bars.py", line 973, in last_available_dt
quantrocket_zipline_1|    _, close = self.calendar.open_and_close_for_session(self._end_session)
quantrocket_zipline_1|  File "/opt/conda/lib/python3.6/site-packages/trading_calendars/trading_calendar.py", line 768, in open_and_close_for_session
quantrocket_zipline_1|    sched.at[session_label, 'market_open'].tz_localize(UTC),
quantrocket_zipline_1|  File "/opt/conda/lib/python3.6/site-packages/pandas/core/indexing.py", line 1869, in __getitem__
quantrocket_zipline_1|    return self.obj._get_value(*key, takeable=self._takeable)
quantrocket_zipline_1|  File "/opt/conda/lib/python3.6/site-packages/pandas/core/frame.py", line 1985, in _get_value
quantrocket_zipline_1|    return engine.get_value(series._values, index)
quantrocket_zipline_1|  File "pandas/_libs/index.pyx", line 83, in pandas._libs.index.IndexEngine.get_value
quantrocket_zipline_1|  File "pandas/_libs/index.pyx", line 91, in pandas._libs.index.IndexEngine.get_value
quantrocket_zipline_1|  File "pandas/_libs/index.pyx", line 451, in pandas._libs.index.DatetimeEngine.get_loc
quantrocket_zipline_1|KeyError: Timestamp('2021-10-08 00:00:00+0000', tz='UTC')
quantrocket_zipline_1|
quantrocket_zipline_1|[pid: 174|app: 0|req: 54/54] 172.18.0.12 () {34 vars in 651 bytes} [Sun Oct 11 02:56:29 2020] POST /zipline/backtests/sell-gap?data_frequency=minute&capital_base=20000.0&bundle=usstock-1min&start_date=2015-01-03&end_date=2020-08-18&progress=M => generated 78 bytes in 119 msecs (HTTP/1.1 500) 2 headers in 90 bytes (1 switches on core 1)

The origin of that date is that Zipline extends the trading calendar one year into the future. I think the issue might be that it’s trying to use a cached calendar that has an earlier end date. Can you try restarting Zipline and then try to run the backtest again?

docker-compose restart zipline

Thanks for quick turn around, Brian! Restarting the zipline fixed the issue.

This issue is fixed in version 2.3.0.