Skip to content

Commit

Permalink
Updated backtesting with timer.
Browse files Browse the repository at this point in the history
  • Loading branch information
Sinbad-The-Sailor committed Feb 24, 2024
1 parent 23f3715 commit d616352
Show file tree
Hide file tree
Showing 9 changed files with 33 additions and 24 deletions.
2 changes: 2 additions & 0 deletions .improvements
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
1. Model selector based on LOOCV.
2. Add normal copula model (student's t or normal).
32 changes: 20 additions & 12 deletions backtest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
import torch
import numpy as np
import time
import pandas as pd

from tests.test_config import TEST_YAHOO_STOCK_UNIVERSE_16, TEST_YAHOO_STOCK_UNIVERSE_8, TEST_YAHOO_STOCK_UNIVERSE_4
Expand All @@ -10,7 +9,7 @@
from src.abacus.optimizer.optimizer import MPCMaximumReturn



# Backtesting setup.
instrument_specification = {}
inital_weights = {}
wealth = []
Expand All @@ -20,25 +19,34 @@
time_series = pd.read_csv(file, index_col='Date')
instrument_specification[ticker] = time_series
if i < number_of_start_assets: inital_weights[ticker] = 1 / number_of_start_assets

universe = Universe(instrument_specifications=instrument_specification)
portfolio = Portfolio(weights=inital_weights)
simulator = Simulator(universe)
simulator.calibrate()
simulator.run_simulation(time_steps=5, number_of_simulations=100)

# Date range for backtesting.
start_date = "2020-01-02"
end_date = "2020-01-03" # "2023-05-31"
end_date = "2020-01-05" # "2023-05-31"
date_range = pd.date_range(start=start_date, end=end_date, freq='B')
solutions = {}
times = {}

for date in date_range:
t1 = time.time()
universe.date_today = date
simulator = Simulator(universe)
simulator.calibrate()
simulator.run_simulation(time_steps=5, number_of_simulations=25)
optimizer = MPCMaximumReturn(universe, portfolio, simulator.return_tensor, gamma=1, l1_penalty=0, l2_penalty=0.05, covariance_matrix=simulator.covariance_matrix)
simulator.run_simulation(time_steps=10, number_of_simulations=1000)
optimizer = MPCMaximumReturn(universe, portfolio, simulator.return_tensor, gamma=1, l1_penalty=0, l2_penalty=0.05,
covariance_matrix=simulator.covariance_matrix)
optimizer.solve()

print(optimizer.solution)
break
solution = optimizer.solution
times[date] = time.time() - t1
portfolio.weights = solution
solutions[date] = solution

print('\n' * 10)
for date, solution in solutions.items():
print(f"Took {times[date]} seconds.")
for a, w in solution.items():
print(a, w)
print()
4 changes: 2 additions & 2 deletions src/abacus/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Processes
MAXIMUM_AR_ORDER = 5
INITIAL_VARIANCE_GARCH_OBSERVATIONS = 20
INITIAL_GARCH_PARAMETERS = (0.05, 0.90)
INITIAL_GARCH_PARAMETERS = 0.05, 0.90

# Copulas
VINE_COPULA_NUMBER_OF_THREADS = 6
Expand All @@ -24,7 +24,7 @@

# Extreme Value Theory POT Threshold
EVT_THRESHOLD = 0.95
GEV_INITIAL_SOLUTION = (0.1, 0.01)
GEV_INITIAL_SOLUTION = 0.1, 0.01

# Optimizer
DEFAULT_SOLVER = "ipopt"
1 change: 0 additions & 1 deletion src/abacus/models/ar.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
import torch
import numpy as np
import pandas as pd

from torch.distributions import Normal

Expand Down
1 change: 0 additions & 1 deletion src/abacus/models/garch.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
import torch
import numpy as np
import pandas as pd

from scipy.optimize import minimize
from torch.distributions import Normal
Expand Down
10 changes: 5 additions & 5 deletions src/abacus/models/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def calibrate(self):
"""Calibrates the parameters of the model. A non-calibrated model does not
have an AIC/BIC and simulations are not possible.
"""
...
pass

@abstractmethod
def transform_to_uniform(self):
Expand All @@ -31,7 +31,7 @@ def transform_to_uniform(self):
Required for copula calibration.
"""
...
pass

@abstractmethod
def transform_to_true(self, uniform_sample: torch.Tensor) -> torch.Tensor:
Expand All @@ -44,21 +44,21 @@ def transform_to_true(self, uniform_sample: torch.Tensor) -> torch.Tensor:
Returns:
torch.Tensor: Sample of simulated process in chronological order.
"""
...
pass

@property
@abstractmethod
def _log_likelihood(self):
"""
"""
...
pass

@property
@abstractmethod
def _number_of_parameters(self):
"""
"""
...
pass

@property
def aic(self) -> torch.Tensor:
Expand Down
5 changes: 3 additions & 2 deletions src/abacus/optimizer/optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ class OptimizationModel(ABC):

_model_specification: ClassVar[str]

def __init__(self, universe: Universe, portfolio: Portfolio, simulation_tensor: torch.Tensor, solver: str=DEFAULT_SOLVER):
def __init__(self, universe: Universe, portfolio: Portfolio, simulation_tensor: torch.Tensor, solver: str=DEFAULT_SOLVER, verbose: bool=True):
self._portfolio = portfolio
self._simulation_tensor = simulation_tensor
self._universe = universe
self._asset_identifiers = universe.instrument_identifiers
self._solver = solver
self._solved = False
self._ampl = None
self._verbose = verbose

def solve(self):
# TODO: Consider a verbose mode to display command line output.
Expand All @@ -46,7 +47,7 @@ def _initiate_ampl_engine(self):

def _solve_optimzation_problem(self):
self._check_initialization()
self._ampl.solve()
self._ampl.solve(verbose=self._verbose)

def _check_solved(self):
if not self._solved:
Expand Down
1 change: 0 additions & 1 deletion src/abacus/simulator/model_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@


class ModelSelector:
# TODO: Add a selection strategy LOOCV, other information criterion, etc.
# TODO: Add more models here.

STOCK_ADMISSIBLE_MODELS = AR, GARCH
Expand Down
1 change: 1 addition & 0 deletions src/abacus/simulator/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def covariance_matrix(self, data_type: DataTypes=DataTypes.LOG_RETURNS) -> torch
# TODO: Add input for price through enum.
# TODO: Consider making static.
# TODO: Consider check for symmetric and positive semi definiteness.
# TODO: Consider having in universe? Does not actually pertain to simulations itself.
if data_type == DataTypes.LOG_RETURNS:
instrument_data = [instrument.log_returns_tensor for instrument in self._instruments]
elif data_type == DataTypes.ART_RETURNS:
Expand Down

0 comments on commit d616352

Please sign in to comment.