robertmartin8 / PyPortfolioOpt

Financial portfolio optimisation in python, including classical efficient frontier, Black-Litterman, Hierarchical Risk Parity
https://pyportfolioopt.readthedocs.io/
MIT License
4.38k stars 940 forks source link

mcaps OrderedDict issues #361

Closed Mikesunyq closed 2 years ago

Mikesunyq commented 3 years ago

Describe the bug I defined a function in the EfficientFrontier class to calculate efficient tracking error. and use functions in BL model" mkt_weights = mcaps / mcaps.sum()" to calculate my benchmark_weights. Hoewver the benchmark_weights assigned to each stock was wrong. After I make my mcaps an OrderedDict by ticker name in the begining, the problem was solved. I'm not sure the benchmark_weight arrangement was right in the BL model so I raise this Issue.

Expected behavior I just want to make sure BL model works well.

Code sample

class EfficientFrontier(base_optimizer.BaseConvexOptimizer):

    def efficient_tracking_error(self,benchmark_weights,target_te,market_neutral=False):
        if not isinstance(target_te, (float, int)) or target_te < 0:
            raise ValueError("target_volatility should be a positive float")
        self._objective = objective_functions.portfolio_return(
            self._w, self.expected_returns
        )
        te = objective_functions.ex_ante_tracking_error(self._w, self.cov_matrix,benchmark_weights= benchmark_weights)
        for obj in self._additional_objectives:
            self._objective += obj
        self._constraints.append(te <= target_te ** 2)
        self._make_weight_sum_constraint(market_neutral)
        return self._solve_cvxpy_opt_problem()
#########################################

def e_te(target_tracking_error):

    mkt_weights = mcaps / mcaps.sum()
    S = risk_models.CovarianceShrinkage(prices).ledoit_wolf()
    mu = expected_returns.capm_return(prices)
    ef = EfficientFrontier(mu, S)
    ef.add_objective(objective_functions.L2_reg, gamma=0.1)
    ef.efficient_tracking_error(benchmark_weights=mkt_weights,target_te=target_tracking_error)
    weights = ef.clean_weights()
    ef.portfolio_performance(verbose=True)
    print(mkt_weights)
    return weights

if I do not make the mcaps an OrderedDict by tickers name in the begining, when I set my target tracking error very small the weight numbers matche but the tickers are different.

Operating system, python version, PyPortfolioOpt version windows 10, python 3.8, PyPortfolioOpt 1.2.6

Additional context wrong outcome looks like this, the first list was mkt_weithts MSFT 0.393155 AMZN 0.343084 NAT 0.000084 BAC 0.061531 DPZ 0.003482 DIS 0.061579 KO 0.044822 MCD 0.032694 COST 0.033486 SBUX 0.026083 dtype: float64 OrderedDict([('AMZN', 0.39427), ('BAC', 0.34214), ('COST', 0.0), ('DIS', 0.06233), ('DPZ', 0.00367), ('KO', 0.05794), ('MCD', 0.0451), ('MSFT', 0.0336), ('NAT', 0.03373), ('SBUX', 0.02722)])

phschiele commented 3 years ago

@Mikesunyq Can you provide a full minimal working example to allow for easier local replication?

robertmartin8 commented 2 years ago

Closing because stale – feel free to reopen if needed