Skip to content

Commit

Permalink
Working indexing of instruments through risk assessor and optimizier.
Browse files Browse the repository at this point in the history
  • Loading branch information
Sinbad-The-Sailor committed Feb 6, 2024
1 parent 782550e commit 0c1c21b
Show file tree
Hide file tree
Showing 18 changed files with 236 additions and 247 deletions.
2 changes: 1 addition & 1 deletion makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ venv:
. venv/bin/activate && exec zsh

env:
@source .env
@source .env;

lint:
@black src/.
Expand Down
33 changes: 28 additions & 5 deletions run.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,50 @@
import pandas as pd

from matplotlib import pyplot
from tests.test_config import TEST_YAHOO_STOCK_UNIVERSE_16
from tests.test_config import TEST_YAHOO_STOCK_UNIVERSE_16, TEST_YAHOO_STOCK_UNIVERSE_8, TEST_YAHOO_STOCK_UNIVERSE_4
from src.abacus.utils.instrument import Instrument
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



# Mock instrument creation...
instruments = []
for ticker in TEST_YAHOO_STOCK_UNIVERSE_16[0:3]:
for id, ticker in enumerate(sorted(TEST_YAHOO_STOCK_UNIVERSE_4)):
file = f"tests/data/{ticker}.csv"
time_series = pd.read_csv(file, index_col='Date')
ins = Instrument(ticker, "Stock", time_series)
ins = Instrument(id, ticker, "Stock", time_series)
instruments.append(ins)

# Simulation ...
simulator = Simulator(instruments)
simulator.calibrate()
simulator.run_simulation(500, 50)
simulator.run_simulation(time_steps=10, number_of_simulations=1000)

# Portfolio creation...
holdings = 500, 200, 100, 125
holdings = dict(zip(instruments[0:4], holdings))
print(holdings)
cash = 100_000
portfolio = Portfolio(holdings, cash)

# Risk assessor creation...
assessor = RiskAssessor(portfolio=portfolio, return_tensor=simulator.return_tensor, time_step=5)
assessor.summary()

for i in range(50):
# Display reasonableness of simulations...
for i in range(25):
y = simulator.price_tensor[0,:,i]
x = [i for i in range(len(y))]
pyplot.plot(x, y)
pyplot.show()

# Create optimizer with different optimization models...
optimizer = SPMaximumUtility(portfolio, simulator.price_tensor)
optimizer.solve()



print("OK!")
2 changes: 2 additions & 0 deletions src/abacus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
import logging
import logging.config



logging.config.fileConfig("src/abacus/logging.cfg")
logger = logging.getLogger(__name__)
Empty file added src/abacus/assessor/__init__.py
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

from scipy.optimize import minimize

from utils.config import EVT_THRESHOLD, GEV_INITIAL_SOLUTION
from utils.portfolio import Portfolio
from src.abacus.config import EVT_THRESHOLD, GEV_INITIAL_SOLUTION
from src.abacus.utils.portfolio import Portfolio



Expand All @@ -25,9 +25,16 @@ def __init__(self, portfolio: Portfolio, return_tensor: torch.Tensor, time_step:
self._return_tensor = return_tensor
self._time_step = time_step
self._calibrated = False

self._check_time_step()

def summary(self):
headers = "VaR", "Extreme VaR", "ES", "Extreme ES"
confidence_99 = self.value_at_risk(0.99), self.extreme_value_at_risk(0.99), self.expected_shortfall(0.99), self.extreme_expected_shortfall(0.99)
confidence_999 = self.value_at_risk(0.999), self.extreme_value_at_risk(0.999), self.expected_shortfall(0.999), self.extreme_expected_shortfall(0.999)
print(*headers)
print(*confidence_99)
print(*confidence_999)

@property
def _evt_threshold(self) -> torch.Tensor:
return torch.quantile(self._portfolio_loss_scenarios, torch.tensor(EVT_THRESHOLD))
Expand All @@ -44,8 +51,7 @@ def _return_matrix(self) -> torch.Tensor:

@property
def _reduced_return_matrix(self) -> torch.Tensor:
instrument_ids = [instrument.id for instrument in self._portfolio.weights]
sorted_ids = torch.tensor(sorted(instrument_ids))
sorted_ids = torch.tensor(self._portfolio.indices)
return torch.index_select(self._return_matrix, dim=0, index=sorted_ids)

@property
Expand All @@ -55,8 +61,8 @@ def _maxmimum_time_step(self):
@property
def _weights(self) -> torch.Tensor:
portfolio_weights = self._portfolio.weights
sorted_keys = sorted(list(portfolio_weights.keys()), key=lambda x: x.id)
weights = torch.empty(len(portfolio_weights))
sorted_keys = sorted(list(portfolio_weights), key=lambda x: x.id)
for i, key in enumerate(sorted_keys):
weights[i] = portfolio_weights[key]
return weights
Expand All @@ -73,7 +79,6 @@ def _portfolio_loss_scenarios(self) -> torch.Tensor:
def _constraints(self) -> list[dict]:
constraints = []
observations = self._excess_portfolio_losses

constraints.append({"type": "ineq", "fun": lambda x: x[1]})

for observation in observations:
Expand Down Expand Up @@ -158,9 +163,6 @@ def extreme_expected_shortfall(self, confidence_level: float) -> float:
threshold = self._evt_threshold
parameters = torch.tensor(self._parameters)
xi, beta = parameters[0], parameters[1]

print(xi, beta)

extreme_es = extreme_var / (1 - xi) + (beta - threshold * xi) /(1 - xi)

return extreme_es.item()
Expand Down
File renamed without changes.
2 changes: 2 additions & 0 deletions src/abacus/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
import pyvinecopulib as pv



# Processes
MAXIMUM_AR_ORDER = 5
INITIAL_VARIANCE_GARCH_OBSERVATIONS = 20
Expand Down
File renamed without changes.
54 changes: 0 additions & 54 deletions src/abacus/legacy/leg_model.py

This file was deleted.

77 changes: 0 additions & 77 deletions src/abacus/legacy/leg_model_factory.py

This file was deleted.

82 changes: 82 additions & 0 deletions src/abacus/optimizer/leg_optimizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
import os
import torch
import numpy as np

from amplpy import AMPL, Environment

from .enums import OptimizationModels
from utils.config import DEFAULT_SOLVER
from utils.portfolio import Portfolio



class Optimizer:

def __init__(self, portfolio: Portfolio, simulation_tensor: torch.Tensor, solver: str = DEFAULT_SOLVER):
self._portfolio = portfolio
self._simulation_tensor = simulation_tensor
self._solver = solver
self._ran = False

def run(self):
self._initiate_ampl_engine()
self._set_ampl_data()
self._solve_optimzation_problem()
print(self._ampl.get_variable("x_buy").get_values())
print(self._ampl.get_variable("x_sell").get_values())
self._ran = True

@property
def solution(self):
self._check_ran()
...

@property
def model(self):
return self._optimization_model

@model.setter
def model(self, other):
self._optimization_model = other


def _initiate_ampl_engine(self):
environment = Environment(os.environ.get("AMPL_PATH"))
self._ampl = AMPL(environment)
self._ampl.option["solver"] = self._solver
self._ampl.read(f"optimization_models/{self._optimization_model.value}")

def _set_ampl_data(self):
if self._optimization_model == OptimizationModels.SP_MAXIMIZE_UTILITY:
assets = self._portfolio.instruments
asset_identifiers = [instrument.identifier for instrument in assets]
instrument_holdings = np.array(list(self._portfolio.holdings.values()))
price_tensor = np.array(self._simulation_tensor[:,-1,:])
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)}

self._ampl.get_set("assets").set_values(asset_identifiers)
self._ampl.param["gamma"] = -24
self._ampl.param["risk_free_rate"] = 0.04
self._ampl.param["dt"] = 1/365
self._ampl.param["number_of_assets"] = number_of_assets
self._ampl.param["number_of_scenarios"] = number_of_scenarios
self._ampl.param["inital_cash"] = self._portfolio._cash
self._ampl.param["inital_holdings"] = instrument_holdings
self._ampl.param["prices"] = price_dict


elif self._optimization_model == OptimizationModels.MPC_MAXIMIZE_UTILITY:
...

def _solve_optimzation_problem(self):
self._ampl.solve()

def _check_ran(self):
if not self._ran:
raise ValueError("Optimizer has not been run.")
Loading

0 comments on commit 0c1c21b

Please sign in to comment.