Skip to content

Commit

Permalink
tested difficult edge cases following bug fix from PR #113 (thanks!)
Browse files Browse the repository at this point in the history
  • Loading branch information
enzbus committed Oct 24, 2023
1 parent 4c777e3 commit ddb1605
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 3 deletions.
15 changes: 12 additions & 3 deletions cvxportfolio/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from __future__ import annotations, print_function

import collections
import logging
from typing import Dict

import matplotlib.pyplot as plt
Expand Down Expand Up @@ -63,8 +64,13 @@ def __init__(self, universe, trading_calendar, costs):
self._current_universe = pd.Index(universe)
self._indexer = np.arange(len(universe), dtype=int)

@property
def _current_full_universe(self):
"""Helper property used by _change_universe."""
return self._h.columns

def _change_universe(self, new_universe):
"""Change current universe (columns of dataframes) during backtest."""
"""Change current universe (columns of dataframes) during back-test."""

# print('new universe')
# print(new_universe)
Expand All @@ -83,9 +89,12 @@ def _change_universe(self, new_universe):
joined = new_universe

# otherwise we lose the ordering :(
else: #TODO: write testcase for this
else:
logging.info(
f"{self.__class__.__name__} joining new universe with old")
joined = pd.Index(
sorted(set(self._current_universe[:-1]
# need to join with full, not current!
sorted(set(self._current_full_universe[:-1]
).union(new_universe[:-1])))
joined = joined.append(new_universe[-1:])

Expand Down
32 changes: 32 additions & 0 deletions cvxportfolio/tests/test_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,38 @@ def test_simulator_raises(self):
columns=['X', 'USDOLLAR']),
volumes=pd.DataFrame([[0.]]))

def test_backtest_with_difficult_universe_changes(self):
"""Test back-test with assets that both enter and exit at same time."""
rets = pd.DataFrame(self.returns.iloc[:, -10:], copy=True)
volumes = pd.DataFrame(self.volumes.iloc[:, -9:], copy=True)
prices = pd.DataFrame(self.prices.iloc[:, -9:], copy=True)
rets.iloc[15:25, 1:3] = np.nan
rets.iloc[9:17, 3:5] = np.nan
rets.iloc[8:15, 5:7] = np.nan
rets.iloc[17:29, 7:8] = np.nan
print(rets.iloc[10:20])

modified_market_data = cvx.UserProvidedMarketData(
returns=rets, volumes=volumes, prices=prices,
cash_key='cash',
min_history=pd.Timedelta('0d'))

simulator = cvx.StockMarketSimulator(market_data=modified_market_data)

policy = cvx.SinglePeriodOptimization(
cvx.ReturnsForecast() - 10 * cvx.FullCovariance(),
[cvx.LongOnly(), cvx.LeverageLimit(1)])

bt_result = simulator.backtest(policy, start_time = rets.index[10],
end_time = rets.index[20])

print(bt_result.w)

self.assertTrue(set(bt_result.w.columns) == set(rets.columns))
self.assertTrue(
np.all(bt_result.w.iloc[:-1].isnull() == rets.iloc[
10:20].isnull()))

def test_prepare_data(self):
simulator = MarketSimulator(['ZM', 'META'], base_location=self.datadir)
self.assertTrue(simulator.market_data.returns.shape[1] == 3)
Expand Down

0 comments on commit ddb1605

Please sign in to comment.