Skip to content

Commit

Permalink
Don't access _strategy._backtesting_start when it isn't set
Browse files Browse the repository at this point in the history
Fix Issue #406 which is due to trying to access _backtesting_start when not backtesting.

Also move that big chunk of code into its own method so we can see what's going on.
  • Loading branch information
jimwhite authored May 18, 2024
1 parent c6b2ae4 commit ead0746
Showing 1 changed file with 79 additions and 74 deletions.
153 changes: 79 additions & 74 deletions lumibot/strategies/_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,86 +569,91 @@ def _dump_stats(self):

self._analysis = stats_summary(self._strategy_returns_df, self.risk_free_rate)

# Getting performance for the benchmark asset
if self._backtesting_start is not None and self._backtesting_end is not None:
# Need to adjust the backtesting end date because the data from Yahoo
# is at the start of the day, so the graph cuts short. This may be needed
# for other timeframes as well
backtesting_end_adjusted = self._backtesting_end

# If we are using the polgon data source, then get the benchmark returns from polygon
if type(self.broker.data_source) == PolygonDataBacktesting:
benchmark_asset = self._benchmark_asset
# If the benchmark asset is a string, then convert it to an Asset object
if isinstance(benchmark_asset, str):
benchmark_asset = Asset(benchmark_asset)

timestep = "minute"
# If the strategy sleeptime is in days then use daily data, eg. "1D"
if "D" in str(self._sleeptime):
timestep = "day"

bars = self.broker.data_source.get_historical_prices_between_dates(
benchmark_asset,
timestep,
start_date=self._backtesting_start,
end_date=backtesting_end_adjusted,
quote=self._quote_asset,
)
df = bars.df

# Add returns column
df["return"] = df["close"].pct_change()

# Add the symbol_cumprod column
df["symbol_cumprod"] = (1 + df["return"]).cumprod()

self._benchmark_returns_df = df

# For data sources of type CCXT, benchmark_asset gets bechmark_asset from the CCXT backtest data source.
elif self.broker.data_source.SOURCE.upper() == "CCXT":
benchmark_asset = self._benchmark_asset
# If the benchmark asset is a string, then convert it to an Asset object
if isinstance(benchmark_asset, str):
asset_quote = benchmark_asset.split("/")
if len(asset_quote) == 2:
benchmark_asset = (Asset(symbol=asset_quote[0], asset_type="crypto"),
Asset(symbol=asset_quote[1], asset_type="crypto"))
else:
benchmark_asset = Asset(symbol=benchmark_asset,asset_type="crypto")

timestep = "minute"
# If the strategy sleeptime is in days then use daily data, eg. "1D"
if "D" in str(self._sleeptime):
timestep = "day"

bars = self.broker.data_source.get_historical_prices_between_dates(
benchmark_asset,
timestep,
start_date=self._backtesting_start,
end_date=backtesting_end_adjusted,
quote=self._quote_asset,
)
df = bars.df

# Add the symbol_cumprod column
df["symbol_cumprod"] = (1 + df["return"]).cumprod()

self._benchmark_returns_df = df

# If we are using any other data source, then get the benchmark returns from yahoo
else:
self._benchmark_returns_df = get_symbol_returns(
self._benchmark_asset,
self._backtesting_start,
backtesting_end_adjusted,
)
# Get performance for the benchmark asset
self._dump_benchmark_stats()

for handler in logger.handlers:
if handler.__class__.__name__ == "StreamHandler":
handler.setLevel(current_stream_handler_level)
logger.setLevel(current_level)

def _dump_benchmark_stats(self):
if notself._is_backtesting:
return
if self._backtesting_start is not None and self._backtesting_end is not None:
# Need to adjust the backtesting end date because the data from Yahoo
# is at the start of the day, so the graph cuts short. This may be needed
# for other timeframes as well
backtesting_end_adjusted = self._backtesting_end

# If we are using the polgon data source, then get the benchmark returns from polygon
if type(self.broker.data_source) == PolygonDataBacktesting:
benchmark_asset = self._benchmark_asset
# If the benchmark asset is a string, then convert it to an Asset object
if isinstance(benchmark_asset, str):
benchmark_asset = Asset(benchmark_asset)

timestep = "minute"
# If the strategy sleeptime is in days then use daily data, eg. "1D"
if "D" in str(self._sleeptime):
timestep = "day"

bars = self.broker.data_source.get_historical_prices_between_dates(
benchmark_asset,
timestep,
start_date=self._backtesting_start,
end_date=backtesting_end_adjusted,
quote=self._quote_asset,
)
df = bars.df

# Add returns column
df["return"] = df["close"].pct_change()

# Add the symbol_cumprod column
df["symbol_cumprod"] = (1 + df["return"]).cumprod()

self._benchmark_returns_df = df

# For data sources of type CCXT, benchmark_asset gets bechmark_asset from the CCXT backtest data source.
elif self.broker.data_source.SOURCE.upper() == "CCXT":
benchmark_asset = self._benchmark_asset
# If the benchmark asset is a string, then convert it to an Asset object
if isinstance(benchmark_asset, str):
asset_quote = benchmark_asset.split("/")
if len(asset_quote) == 2:
benchmark_asset = (Asset(symbol=asset_quote[0], asset_type="crypto"),
Asset(symbol=asset_quote[1], asset_type="crypto"))
else:
benchmark_asset = Asset(symbol=benchmark_asset,asset_type="crypto")

timestep = "minute"
# If the strategy sleeptime is in days then use daily data, eg. "1D"
if "D" in str(self._sleeptime):
timestep = "day"

bars = self.broker.data_source.get_historical_prices_between_dates(
benchmark_asset,
timestep,
start_date=self._backtesting_start,
end_date=backtesting_end_adjusted,
quote=self._quote_asset,
)
df = bars.df

# Add the symbol_cumprod column
df["symbol_cumprod"] = (1 + df["return"]).cumprod()

self._benchmark_returns_df = df

# If we are using any other data source, then get the benchmark returns from yahoo
else:
self._benchmark_returns_df = get_symbol_returns(
self._benchmark_asset,
self._backtesting_start,
backtesting_end_adjusted,
)

def plot_returns_vs_benchmark(
self,
plot_file_html="backtest_result.html",
Expand Down

0 comments on commit ead0746

Please sign in to comment.