Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions bt/backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ def __init__(

self.stats = {}
self._original_prices = None
self._stat_prices = None
self._weights = None
self._sweights = None
self.has_run = False
Expand Down Expand Up @@ -353,8 +354,19 @@ def run(self):
if self.progress_bar:
bar.stop()

self.stats = self.strategy.prices.calc_perf_stats()
self._original_prices = self.strategy.prices
self._stat_prices = self._compute_stat_prices()
self.stats = self._stat_prices.calc_perf_stats()

def _compute_stat_prices(self):
prices = self.strategy.prices
transactions = self.strategy.get_transactions()

if not transactions.empty:
first_transaction_date = transactions.index.get_level_values(0)[0]
return prices.loc[first_transaction_date:]

return prices

@property
def weights(self):
Expand Down Expand Up @@ -469,7 +481,7 @@ class Result(ffn.GroupStats):
"""

def __init__(self, *backtests):
tmp = [pd.DataFrame({x.name: x.strategy.prices}) for x in backtests]
tmp = [pd.DataFrame({x.name: x._stat_prices if x._stat_prices is not None else x.strategy.prices}) for x in backtests]
super().__init__(*tmp)
self.backtest_list = backtests
self.backtests = {x.name: x for x in backtests}
Expand Down Expand Up @@ -687,7 +699,12 @@ def __init__(self, normalizing_value, *backtests):
raise ValueError(f"Cannot apply RenormalizedFixedIncomeResult because backtest {backtest.name} is not on a fixed income strategy")
if not isinstance(normalizing_value, dict):
normalizing_value = {x.name: normalizing_value for x in backtests}
tmp = [pd.DataFrame({x.name: self._price(x.strategy, normalizing_value[x.name])}) for x in backtests]
tmp = []
for backtest in backtests:
prices = self._price(backtest.strategy, normalizing_value[backtest.name])
if backtest._stat_prices is not None:
prices = prices.reindex(backtest._stat_prices.index)
tmp.append(pd.DataFrame({backtest.name: prices}))
super(Result, self).__init__(*tmp)
self.backtest_list = backtests
self.backtests = {x.name: x for x in backtests}
Expand Down
22 changes: 22 additions & 0 deletions tests/test_backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,28 @@ def test_nested_strategy_backtest_handles_initial_paper_trade_value():
assert result.prices["root"].iloc[0] == 100


def test_run_after_date_stats_start_on_first_transaction():
dates = pd.date_range("2000-01-01", "2002-12-31", freq=pd.tseries.offsets.BDay())
prices = pd.DataFrame(index=dates, data={"a": 100.0})
prices.loc[dates[260]:, "a"] = np.linspace(100, 150, len(dates[260:]))

strategy = bt.Strategy(
"delayed",
[
bt.algos.RunAfterDate("2001-01-01"),
bt.algos.SelectAll(),
bt.algos.WeighEqually(),
bt.algos.Rebalance(),
],
)

result = bt.run(bt.Backtest(strategy, prices, progress_bar=False))

first_transaction_date = result.get_transactions().index.get_level_values(0)[0]
assert result.stats["delayed"].start == first_transaction_date
assert result.prices.index[0] == first_transaction_date


def test_30_min_data():
names = ["foo"]
dates = pd.date_range(start="2017-01-01", end="2017-12-31", freq="30min")
Expand Down
Loading