From 53e947d391d84340f72ca88bb09f1cf554e3ecc8 Mon Sep 17 00:00:00 2001 From: Axel Nilsson Date: Sun, 18 Feb 2024 16:57:12 +0100 Subject: [PATCH] MPC working with covariance matrix risk aversion and l2 transaction costs. --- run.py | 7 ++- .../mpc_maximize_return.mod | 35 ++++++++++++++ src/abacus/optimizer/optimizer.py | 47 +++++++++++++++++-- src/abacus/utils/enumerations.py | 1 + 4 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 src/abacus/optimizer/optimization_models/mpc_maximize_return.mod diff --git a/run.py b/run.py index 3148d3e..104ee76 100644 --- a/run.py +++ b/run.py @@ -8,7 +8,7 @@ from src.abacus.utils.portfolio import Portfolio from src.abacus.simulator.simulator import Simulator from src.abacus.assessor.risk_assessor import RiskAssessor -from src.abacus.optimizer.optimizer import SPMaximumUtility, MPCMaximumUtility +from src.abacus.optimizer.optimizer import SPMaximumUtility, MPCMaximumUtility, MPCMaximumReturn @@ -60,4 +60,9 @@ # optimizer.solve() +print() +optimizer = MPCMaximumReturn(portfolio, simulator.return_tensor, gamma=10, l1_penalty=0, l2_penalty=1, covariance_matrix=simulator.covariance_matrix) +optimizer.solve() + + print("OK!") diff --git a/src/abacus/optimizer/optimization_models/mpc_maximize_return.mod b/src/abacus/optimizer/optimization_models/mpc_maximize_return.mod new file mode 100644 index 0000000..b532c5d --- /dev/null +++ b/src/abacus/optimizer/optimization_models/mpc_maximize_return.mod @@ -0,0 +1,35 @@ +problem mpcMaximizeReturn; + +set assets; + +param gamma; +param inital_weights {assets}; +param number_of_time_steps > 0 integer; +param returns {1..number_of_time_steps, assets}; +param covariance {assets, assets}; +param l1_penalty {assets}; +param l2_penalty {assets}; + +var weights {0..number_of_time_steps, assets}; + +maximize OBJECTIVE: + sum{t in 1..number_of_time_steps} (sum {a in assets} (returns[t,a] * weights[t,a]) + - gamma * sum{a in assets, b in assets} weights[t, a] * covariance[a , b] * weights [t, b] + - sum{a in assets} ( l1_penalty[a] * abs(weights[t, a] - weights[t-1, a]) + l2_penalty[a] * (weights[t, a] - weights[t-1, a]) **2)) +; + +subject to WEIGHT_CONSTRAINT_SUM {t in 1..number_of_time_steps}: + sum{a in assets} weights[t, a] = 1 +; + +subject to WEGIHT_CONSTRAINT {t in 1..number_of_time_steps, a in assets}: + weights[t, a] <= 1 +; + +subject to WEGIHT_CONSTRAINT_NON_NEGATIVE {t in 1..number_of_time_steps, a in assets}: + weights[t, a] >= 0 +; + +subject to INITIAL_WEIGHTS {a in assets}: + weights[0, a] = inital_weights[a] +; diff --git a/src/abacus/optimizer/optimizer.py b/src/abacus/optimizer/optimizer.py index 7ac843d..66d6221 100644 --- a/src/abacus/optimizer/optimizer.py +++ b/src/abacus/optimizer/optimizer.py @@ -123,12 +123,53 @@ def _set_ampl_data(self): number_of_time_steps = tensor_size[1] return_dict = {(j+1, asset.identifier): expected_return_tensor[asset.id][j] for asset in assets for j in range(number_of_time_steps)} - print(return_dict) - print(asset_identifiers) - print(inital_weights) + self._ampl.get_set("assets").set_values(asset_identifiers) + self._ampl.param["gamma"] = self._gamma + self._ampl.param["number_of_time_steps"] = number_of_time_steps + self._ampl.param["inital_weights"] = inital_weights + self._ampl.param["returns"] = return_dict + + +class MPCMaximumReturn(OptimizationModel): + + _model_specification = OptimizationSpecifications.MPC_MAXIMIZE_RETURN + + def __init__(self, portfolio: Portfolio, simulation_tensor: torch.Tensor, gamma: float, l1_penalty: float, l2_penalty: float, + covariance_matrix: torch.Tensor): + super().__init__(portfolio, simulation_tensor) + self._gamma = gamma + self._l1_penalty = l1_penalty + self._l2_penalty = l2_penalty + self._covariance_matrix = covariance_matrix + + @property + def _return_expectation_tensor(self): + return torch.mean(self._simulation_tensor, dim=2) + + def solve(self): + super().solve() + print(self._ampl.get_variable("weights").get_values()) + print(self._ampl.eval("display OBJECTIVE;")) + + def _set_ampl_data(self): + assets = self._portfolio.instruments + inital_weights = self._portfolio.weights + asset_identifiers = [instrument.identifier for instrument in assets] + inital_weights = dict(zip(asset_identifiers, inital_weights.values())) + expected_return_tensor = np.array(self._return_expectation_tensor) + tensor_size = expected_return_tensor.shape + number_of_time_steps = tensor_size[1] + return_dict = {(j+1, asset.identifier): expected_return_tensor[asset.id][j] for asset in assets for j in range(number_of_time_steps)} + covariance_matrix = np.array(self._covariance_matrix) + + l1_penalty_vector = self._l1_penalty * np.ones(len(assets)) + l2_penalty_vector = self._l2_penalty * np.ones(len(assets)) self._ampl.get_set("assets").set_values(asset_identifiers) self._ampl.param["gamma"] = self._gamma self._ampl.param["number_of_time_steps"] = number_of_time_steps self._ampl.param["inital_weights"] = inital_weights self._ampl.param["returns"] = return_dict + self._ampl.param["covariance"] = covariance_matrix + self._ampl.param["l1_penalty"] = l1_penalty_vector + self._ampl.param["l2_penalty"] = l2_penalty_vector diff --git a/src/abacus/utils/enumerations.py b/src/abacus/utils/enumerations.py index a4bfe2f..ddaa8b7 100644 --- a/src/abacus/utils/enumerations.py +++ b/src/abacus/utils/enumerations.py @@ -5,6 +5,7 @@ class OptimizationSpecifications(Enum): SP_MAXIMIZE_UTILITY = "sp_maximize_utility.mod" MPC_MAXIMIZE_UTILITY = "mpc_maximize_utility.mod" + MPC_MAXIMIZE_RETURN = "mpc_maximize_return.mod" class DataTypes(Enum): LOG_RETURNS = auto(),