diff --git a/backtest.py b/backtest.py index 52dff06..693751c 100644 --- a/backtest.py +++ b/backtest.py @@ -1,7 +1,11 @@ # -*- coding: utf-8 -*- import time + +import numpy as np import pandas as pd +from matplotlib import pyplot as plt + from tests.test_config import TEST_YAHOO_STOCK_UNIVERSE_16, TEST_YAHOO_STOCK_UNIVERSE_8, TEST_YAHOO_STOCK_UNIVERSE_4 from src.abacus.utils.portfolio import Portfolio from src.abacus.utils.universe import Universe @@ -12,7 +16,8 @@ # Backtesting setup. instrument_specification = {} inital_weights = {} -wealth = [] +wealth = [100] +wealth_n = [100] number_of_start_assets = 5 for i, ticker in enumerate(sorted(TEST_YAHOO_STOCK_UNIVERSE_8)): file = f"tests/data/{ticker}.csv" @@ -24,7 +29,7 @@ # Date range for backtesting. start_date = "2020-01-02" -end_date = "2020-01-05" # "2023-05-31" +end_date = "2020-05-02" # "2023-05-31" date_range = pd.date_range(start=start_date, end=end_date, freq='B') solutions = {"2020-01-01": inital_weights} times = {} @@ -34,8 +39,8 @@ universe.date_today = date simulator = Simulator(universe) simulator.calibrate() - simulator.run_simulation(time_steps=10, number_of_simulations=1_000) - optimizer = MPCMaximumReturn(universe, portfolio, simulator.return_tensor, gamma=2, l1_penalty=0, l2_penalty=0.15, + simulator.run_simulation(time_steps=5, number_of_simulations=1000) + optimizer = MPCMaximumReturn(universe, portfolio, simulator.return_tensor, gamma=2, l1_penalty=0, l2_penalty=0.02, covariance_matrix=simulator.covariance_matrix) optimizer.solve() solution = optimizer.solution @@ -43,6 +48,23 @@ portfolio.weights = solution solutions[date] = solution + universe_returns = universe.todays_returns + portfolio_weights = np.array(list(solution.values())) + equal_weights = (1/8) * np.ones(8) + portfolio_return = np.dot(universe_returns, portfolio_weights) + equal_return = np.dot(universe_returns, equal_weights) + wealth.append(wealth[-1] * (1 + portfolio_return)) + wealth_n.append(wealth_n[-1] * (1 + equal_return)) + +dates = ["2024-01-01"] +for date in date_range: + dates.append(str(date)[0:10]) + +np.savetxt('data.csv', np.array(wealth), delimiter=',') + +plt.plot(dates, wealth) +plt.plot(dates, wealth_n) +plt.show() print('\n' * 10) for date, solution in solutions.items(): diff --git a/src/abacus/legacy/leg_main.py b/src/abacus/legacy/leg_main.py index 7269823..c558f7e 100644 --- a/src/abacus/legacy/leg_main.py +++ b/src/abacus/legacy/leg_main.py @@ -8,9 +8,9 @@ from utils.stock_factory import StockFactory from utils.data_handler import YahooDataHandler from utils.portfolio import Portfolio -from utils.risk_assessor import RiskAssessor +from abacus.assessor.risk_assessor import RiskAssessor from simulator.simulator import Simulator -from optimizer.optimizer import Optimizer +from abacus.optimizer.leg_optimizer import Optimizer from optimizer.enums import OptimizationModels diff --git a/src/abacus/optimizer/leg_optimizer.py b/src/abacus/optimizer/leg_optimizer.py index 7ed976e..87d70ca 100644 --- a/src/abacus/optimizer/leg_optimizer.py +++ b/src/abacus/optimizer/leg_optimizer.py @@ -56,10 +56,7 @@ def _set_ampl_data(self): tensor_size = price_tensor.shape number_of_assets = tensor_size[0] number_of_scenarios = tensor_size[1] - - price_dict = {(j+1, asset.identifier): price_tensor[asset.id][j] for asset in assets - for j in range(number_of_scenarios)} - + price_dict = {(j+1, asset.identifier): price_tensor[asset.id][j] for asset in assets for j in range(number_of_scenarios)} self._ampl.get_set("assets").set_values(asset_identifiers) self._ampl.param["gamma"] = -24 self._ampl.param["risk_free_rate"] = 0.04 diff --git a/src/abacus/utils/instrument.py b/src/abacus/utils/instrument.py index 53b7684..e58498a 100644 --- a/src/abacus/utils/instrument.py +++ b/src/abacus/utils/instrument.py @@ -26,7 +26,7 @@ def log_returns(self) -> pd.DataFrame: @property def art_returns(self) -> pd.DataFrame: - return self.price_history.pct_change()[1:] + return np.array(self.price_history.pct_change())[1:] @property def log_returns_tensor(self) -> torch.Tensor: diff --git a/src/abacus/utils/universe.py b/src/abacus/utils/universe.py index 5d03de2..8bb64f2 100644 --- a/src/abacus/utils/universe.py +++ b/src/abacus/utils/universe.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import numpy as np from pandas import DataFrame from datetime import date @@ -38,6 +39,10 @@ def instruments(self) -> list[Instrument]: built_instruments.append(ins) return built_instruments + @property + def todays_returns(self): + return np.array([instrument.art_returns[-1] for instrument in self.instruments]).flatten() + def has_updated_cache(self) -> bool: has_cache = self._instruments is not None has_last_build = self._instrument_build_date is not None