-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #28 from khalil-research/portfolio
Add Portfolio dataset
- Loading branch information
Showing
9 changed files
with
192 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
|
||
package: | ||
name: pyepo | ||
version: 0.3.7 | ||
version: 0.3.8 | ||
|
||
source: | ||
path: ./ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#!/usr/bin/env python | ||
# coding: utf-8 | ||
""" | ||
Synthetic data for portfolio | ||
""" | ||
|
||
import numpy as np | ||
|
||
|
||
def genData(num_data, num_features, num_assets, deg=1, noise_level=1, seed=135): | ||
""" | ||
A function to generate synthetic data and features for travelling salesman | ||
Args: | ||
num_data (int): number of data points | ||
num_features (int): dimension of features | ||
num_assets (int): number of assets | ||
deg (int): data polynomial degree | ||
noise_level (float): level of data random noise | ||
seed (int): random seed | ||
Returns: | ||
tuple: data features (np.ndarray), costs (np.ndarray) | ||
""" | ||
# positive integer parameter | ||
if type(deg) is not int: | ||
raise ValueError("deg = {} should be int.".format(deg)) | ||
if deg <= 0: | ||
raise ValueError("deg = {} should be positive.".format(deg)) | ||
# set seed | ||
rnd = np.random.RandomState(seed) | ||
# number of data points | ||
n = num_data | ||
# dimension of features | ||
p = num_features | ||
# number of assets | ||
m = num_assets | ||
# random matrix parameter B | ||
B = rnd.binomial(1, 0.5, (m, p)) | ||
# random matrix parameter L | ||
L = rnd.uniform(-2.5e-3*noise_level, 2.5e-3*noise_level, (num_assets, num_features)) | ||
# feature vectors | ||
x = rnd.normal(0, 1, (n, p)) | ||
# value of items | ||
r = np.zeros((n, m)) | ||
for i in range(n): | ||
# mean return of assets | ||
r[i] = (0.05 * np.dot(B, x[i].reshape(p, 1)).T / np.sqrt(p) + \ | ||
0.1 ** (1 / deg)) ** deg | ||
# random noise | ||
f = rnd.randn(num_features) | ||
eps = rnd.randn(num_assets) | ||
r[i] += L @ f + 0.01 * noise_level * eps | ||
# covariance matrix of the returns | ||
cov = L @ L.T + (1e-2 * noise_level) * np.eye(num_assets) | ||
return cov, x, r |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
#!/usr/bin/env python | ||
# coding: utf-8 | ||
""" | ||
Portfolio problem | ||
""" | ||
|
||
import numpy as np | ||
import gurobipy as gp | ||
from gurobipy import GRB | ||
|
||
from pyepo.model.grb.grbmodel import optGrbModel | ||
|
||
|
||
class portfolioModel(optGrbModel): | ||
""" | ||
This class is optimization model for portfolio problem | ||
Attributes: | ||
_model (GurobiPy model): Gurobi model | ||
num_assets (int): number of assets | ||
cov (numpy.ndarray): covariance matrix of the returns | ||
risk_level (float): risk level | ||
""" | ||
|
||
def __init__(self, num_assets, covariance, gamma=2.25): | ||
""" | ||
Args: | ||
num_assets (int): number of assets | ||
covariance (numpy.ndarray): covariance matrix of the returns | ||
gamma (float): risk level parameter | ||
""" | ||
self.num_assets = num_assets | ||
self.cov = covariance | ||
self.risk_level = self._getRiskLevel(gamma) | ||
super().__init__() | ||
|
||
def _getRiskLevel(self, gamma): | ||
""" | ||
A method to calculate risk level | ||
Returns: | ||
float: risk level | ||
""" | ||
risk_level = gamma * np.mean(self.cov) | ||
return risk_level | ||
|
||
def _getModel(self): | ||
""" | ||
A method to build Gurobi model | ||
Returns: | ||
tuple: optimization model and variables | ||
""" | ||
# ceate a model | ||
m = gp.Model("portfolio") | ||
# varibles | ||
x = m.addMVar(self.num_assets, ub=1, name="x") | ||
# sense | ||
m.modelSense = GRB.MAXIMIZE | ||
# constraints | ||
m.addConstr(x.sum() == 1, "budget") | ||
m.addConstr(x.T @ self.cov @ x <= self.risk_level, "risk_limit") | ||
return m, x | ||
|
||
|
||
if __name__ == "__main__": | ||
|
||
import random | ||
from pyepo.data.portfolio import genData | ||
# random seed | ||
random.seed(42) | ||
# set random cost for test | ||
cov, _, revenue = genData(num_data=100, num_features=4, num_assets=50, deg=2) | ||
|
||
# solve model | ||
optmodel = portfolioModel(num_assets=50, covariance=cov) # init model | ||
optmodel = optmodel.copy() | ||
optmodel.setObj(revenue[0]) # set objective function | ||
sol, obj = optmodel.solve() # solve | ||
# print res | ||
print('Obj: {}'.format(obj)) | ||
for i in range(50): | ||
if sol[i] > 1e-3: | ||
print("Asset {}: {:.2f}%".format(i, 100*sol[i])) | ||
|
||
# add constraint | ||
optmodel = optmodel.addConstr([1]*50, 30) | ||
optmodel.setObj(revenue[0]) # set objective function | ||
sol, obj = optmodel.solve() # solve | ||
# print res | ||
print('Obj: {}'.format(obj)) | ||
for i in range(50): | ||
if sol[i] > 1e-3: | ||
print("Asset {}: {:.2f}%".format(i, 100*sol[i])) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters