Portfolio optimization

Can you share the notebook so that we can reproduce the issue?

multistep mean_var.zip (731.2 KB)
Here is my work with your help.

It depends on the module talib which is not included. Could you please send the .ipynb notebook and all files it depends on in order for us to just run it and reproduce the issue?

Note that the .html you sent is failing with a pandas error (ValueError: DataFrame constructor not properly called!), not with termination of the underlying interpreter.

multistep mean_var.zip (731.2 KB)
I wanted to use for i, slice_df in enumerate(stocks_in_sample): for sigma calculation.I changed your code with respect to my case.
I want to write AMPL for multistep mean variance
:max E(x_t)-risk_aversion*variance(x_t) ,I want to use min max standardization and subject to


where u is weights updated each time.

Do you want to solve a single period model multiple times with different sigmas and updating the weights u?

Yes,exactly.I worked all day for this today

In that case you don’t need time in the model, and you can load just regular sigma matrices.

The following optimizes the same minimum variance model at every iteration but for a different data range:

from pypfopt import expected_returns, risk_models
from datetime import datetime, timedelta
from amplpy import AMPL
import yfinance as yf
import numpy as np
import pandas as pd
import yfinance as yf

tickers = [
    "HD", "MCD", "NKE", "KO", "PG", "SYY", "WMT", # Consumer Staples
    "CVX", "XOM", # Energy
    "AXP", "JPM", # Financials
    "JNJ", "MRK", "PFE", "WBA", # Health Care
    "BA", "CAT", "MMM", # Industrials
]
end_date = datetime.now().date()
start_date = end_date - timedelta(days=365)
ohlc = yf.download(tickers, start=start_date, end=end_date)
prices = ohlc["Adj Close"].dropna(how="all")

print(len(prices))
tau = 240
for t in range(tau, len(prices)):
    in_sample_data = prices[t - tau : t]
    ampl = AMPL()
    ampl.eval(
        r"""
        set A ordered;
        param Sigma{A, A};
        param lb default 0;
        param ub default 1;
        param u{A};
        var w{A} >= lb <= ub;
        minimize portfolio_variance:
            sum {i in A, j in A} w[i] * Sigma[i, j] * w[j];
        s.t. portfolio_weights:
            sum {i in A} w[i] = 1;
        """
    )
    ampl.set["A"] = tickers
    ampl.param["Sigma"] = risk_models.risk_matrix(in_sample_data, method="sample_cov")
    ampl.param["u"] = {t: 0 for t in tickers} # just a placeholder, u needs to be calculated
    ampl.option["solver"] = "gurobi"
    ampl.solve()
    print("optimal variance:", ampl.get_value("portfolio_variance"))
    ampl.get_data("w").to_pandas().plot.barh()

You can run it at: Google Colab

The missing part is updating the weights u, and using the parameter u in the model.

1 Like

Yes,thank you for your answer,we deal with time as data after AMPL model structure since my for t in range(tau, len(prices)): loop? I thought,I need time parameter in sigma equation

1 Like

You would need t in the model if you were optimizing a single multi-period model with all data at once (e.g., if u was something that had to be optimized globally for the entire model, and not weights that are calculated in some other way). Since you will be updating u manually, then you just need to solve the same single-period model with different data like in that example.

You define param u{A}; not use in equations, then ampl.param[“u”] = {t: 0 for t in tickers},what is the effect of u,can you explain

I add returnn function than calculate min variance corresponding return

Create a function to calculate returns

def calculate_returns(assets):
return np.mean(assets,axis=0)
stocks_in_sample = data[stocks][t - tau : t]
mu = calculate_returns(stocks_in_sample)
ampl = AMPL()
ampl.eval(
r"“”
set A ordered;
param Sigma{A, A};
param lb default 0;
param ub default 1;
param mu{A};
param u{A};
var w{A} >= lb <= ub;
minimize portfolio_variance:
sum {i in A, j in A} w[i] * Sigma[i, j] * w[j];
s.t. portfolio_weights:
sum {i in A} w[i] = 1;
“”"
)
ampl.set[“A”] = stocks
ampl.param[“Sigma”] = risk_models.risk_matrix(stocks_in_sample, method=“sample_cov”)
ampl.param[“u”] = {t: 0 for t in stocks} # just a placeholder, u needs to be calculated
ampl.param[“mu”]=mu
ampl.option[“solver”] = “gurobi”
ampl.solve()
print(“optimal variance:”, ampl.get_value(“portfolio_variance”))
#Var_min=ampl.get_value(“sum {i in A, j in A} w[i] * Sigma[i, j] * w[j]”)
#R_min = ampl.get_value(“sum {i in A, j in A} w[i] * Sigma[i, j] * w[j]”)
print(“min variance:”, ampl.get_value(“sum {i in A, j in A} w[i] * Sigma[i, j] * w[j]”))
print(“min return:”, ampl.get_value(“sum {i in A} mu[i] * w[i]”))

ampl.get_data("w").to_pandas().plot.barh()

Parameter u is there just to load the data. You need to replace {t: 0 for t in tickers} by whatever you use to calculate it, and you also need to use u in the model.

I believe you may want u to be in the objective, but I am not sure what is the expression you want to optimize that would use it. This is why I just showed how to load the data into it. Without knowing the full model, I can’t help with that part.


I want big for loop and load my datas but with iteration than I want variance minimization as in the picture objective is variance.I write ampl.param[“u”] = {t: 0 for t in stocks} stocks or tickers.Then code works,actually

Is u supposed to be expected returns? If that is the case what you may want to optimize would be the following:

set A ordered;
param Sigma{A, A};
param lb default 0;
param ub default 1;
param risk_aversion;
param u{A};
var w{A} >= lb <= ub;
maximize objective:
    sum{i in A} u[i] * w[i] - risk_aversion  * sum {i in A, j in A} w[i] * Sigma[i, j] * w[j];
s.t. portfolio_weights:
    sum {i in A} w[i] = 1;

where u would be the expected returns, and risk_aversion would control the weight that would be given to the variance.

In the objective, portfolio variance is expressed as sum {i in A, j in A} w[i] * Sigma[i, j] * w[j], and expected returns as sum{i in A} u[i] * w[i].

Later,I am tryin to solve that objective,thank you for your help,I am asking u in your solution:ampl.param[“u”] = {t: 0 for t in tickers} # just a placeholder, u needs to be calculated

I defined expected returns as mu

I think u is just iteration,helps me for iteration

You wrote the AMPL book MAD portfolio optimization part ,in that solution you upload data and also define t in the AMPL modelling part.As far as I understand,since you don t use for loop in MAD,than use t in modelling part,it is also multiperiod.Are these two solution same?

That is just to show where you would load the data for u. You had mentioned the following:

You need to assign to param["u"] something with the values for it. {t: 0 for t in tickers} is assigning 0 to every u[i]. Don’t you have something for calculating u?

The model in MAD portfolio optimization seems different form what you have. There are expected returns for various periods, and the objective is to optimize the mean absolute deviation in daily returns.

If you are looking for something like that but taking Sigma in consideration as well, then t is part of the model. In that case you would be solving a single model, not iteratively solving single-period models.

In the picture,u is weight,but you define u and w. For expected returns as mu,I define
def calculate_returns(assets):
return np.mean(assets,axis=0)
stocks_in_sample = data[stocks][t - tau : t]
mu = calculate_returns(stocks_in_sample)
than
ampl.param[“u”] = {t: 0 for t in stocks} # just a placeholder, u needs to be calculated
ampl.param[“mu”]=mu,
Here expected return=mu is calculated for each new stocks, if u is replaced as expected return calculation how? Then u is not used in the AMPL modelling part after just calculation of
print(“min return:”, ampl.get_value(“sum {i in A} mu[i] * w[i]”))