Back to Community
long-only minimum variance portfolio using scipy.optimize.minimize

Here's an example of a long-only minimum variance portfolio using scipy.optimize.minimize. Securities are:

    context.stocks = [ sid(19662),  # XLY Consumer Discrectionary SPDR Fund  
                       sid(19656),  # XLF Financial SPDR Fund  
                       sid(19658),  # XLK Technology SPDR Fund  
                       sid(19655),  # XLE Energy SPDR Fund  
                       sid(19661),  # XLV Health Care SPRD Fund  
                       sid(19657),  # XLI Industrial SPDR Fund  
                       sid(19659),  # XLP Consumer Staples SPDR Fund  
                       sid(19654),  # XLB Materials SPDR Fund  
                       sid(19660),  # XLU Utilities SPRD Fund  
                       sid(33652)]  # BND Vanguard Total Bond Market ETF  

Grant

Clone Algorithm
275
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 537f77fcb329dc070e4b15b7
There was a runtime error.
13 responses

Hey Grant,

I cloned your trade and tried it on levered ETFs. I need to find some with a longer price history or maybe try using levered equity ETFs with unlevered bond ETFs to get something that includes the financial crisis.

Thanks for sharing.

JW

Clone Algorithm
46
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 544ac0ce0561060958425ac1
There was a runtime error.

J.,

Glad you revived it! Have fun.

Grant

Very cool, I added a leverage parameter and made some small tweaks.

Clone Algorithm
82
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 5454f2e8236ce708fe9e2590
There was a runtime error.

Note that the minimum variance computation is a kind of trivial example, since a closed-form solution exists (e.g. http://faculty.washington.edu/ezivot/econ424/portfolioTheoryMatrix.pdf). Folks might consider problems that actually require an optimizer. My understanding is that some of the Python routines will accept inequality constraints, which might be of interest. --Grant

The code I posted is no longer running. I get the error:

Something went wrong. Sorry for the inconvenience. Try using the built-in debugger to analyze your code. If you would like help, send us an email.
ValueError: shapes (10,20,1) and (1,20,10) not aligned: 1 (dim 2) != 20 (dim 1)
There was a runtime error on line 48.

I suspect it is related to the recent update of numpy, etc. Please advise how to fix the code.

Thanks,

Grant

You got the daily returns (10,20) matrix ret that is passed into the variance function by scipy.optimize.minimize as (10,20,1) matrix arg.
I don't know why it does it, but here is a fix using numpy.squeeze:

Change like 64 for

p = np.squeeze(np.asarray(args))  

Here's the fixed version (thanks Alexandre), in case someone ends up cloning it. Odd that low-level numpy would be changed? Kinda disturbing...

Clone Algorithm
571
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 55159de32d70fd3ca7824dd9
We have migrated this algorithm to work with a new version of the Quantopian API. The code is different than the original version, but the investment rationale of the algorithm has not changed. We've put everything you need to know here on one page.
There was a runtime error.

You're welcome, Grant.
When I read your code, I saw that long-only meant that after optimization, you set short positions to zero and invest in bonds instead.
I think that if the optimizer found a minimum variance taking shorts into account and you take those shorts out, you're get a new variance. Would that be a minimum? So wouldn't be better to change the constraint from sum of weights equals to 1 to something that checks both sum of weights equal to 1 and every weight greater or equal to zero?

Well, go for it! See what you can come up with. Worst case, you might learn some Python (or maybe you're an expert), and it is cheaper than Lumosity. My hunch is that if you search, you'll find some literature on the topic...I just don't know. --Grant

Maybe I'm misunderstanding how the minimizer works, but it seems to be disregarding the bounds in this line:
bnds = ((0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1))

Shouldn't those bounds preclude shorts?

I've seen some discussion elsewhere about the SLSQP method disregarding bounds.

Might not be working properly, but I have gotten the bounds to be respected in other algos. If you sort it out, please post here. --Grant

@Grant, editing line 64 of the code will get the algo to compile again after the platform upgrade.

Changing it from,

    p = np.asarray(args)  

to,

p = np.squeeze(np.asarray(args))  
Clone Algorithm
49
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 55301f68da1f9240700a0ee5
There was a runtime error.
Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

Errors....

ValueError: shapes (9,20,1) and (1,20,9) not aligned: 1 (dim 2) != 20 (dim 1)
------> There was a runtime error on line 48.