I used Wayne's Minimum Variance Portfolio on some selected stocks, and the results looks quite good initially. However, after I ran a dividend re-invested buy&hold strategy on this stock selection, the returns are even better than the returns using Minimum Variance algorithm. Although the algorithm tries to minimize variance, the volatility of the Minimum Variance returns are even larger than the buy&hold strategy. Can anyone help to explain?

Clone Algorithm

60

Loading...

There was an error loading this backtest.

Backtest from
to
with
initial capital

Cumulative performance:

Algorithm
Benchmark

Custom data:

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 |

import pandas as pd import numpy as np import numpy.linalg as la import math def initialize(context): #parameters context.nobs = 252 context.recalibrate = 126 #re-estimate every so often (in days) context.leverage= 1 #setup the identifiers and data storage #context.tickers = ['xlf', 'xle', 'xlu', 'xlk', 'xlb', 'xlp', 'xly','xli', 'xlv'] #context.sids = [ sid(19656), sid(19655), # sid(19660), sid(19658), # sid(19654), sid(19659), # sid(19662), sid(19657), # sid(19661) ] context.sids = [ sid(24), sid(3496), sid(4954), sid(6928), sid(62), sid(23998), sid(22140), sid(3735), sid(6653), sid(368), sid(1900), sid(3766), sid(5061), sid(20940), sid(16841), sid(4799), sid(3951), sid(5692),sid(25010), sid(679), sid(23112), sid(4151), sid(5729), sid(7883), sid(698), sid(2190), sid(5885), sid(700), sid(3149), sid(4283), sid(5923), sid(980), sid(3212), sid(4707), sid(5938), sid(8229), sid(1335), sid(4758), sid(8347), sid(1582), sid(20088), sid(4922), sid(6295)] context.tickers = [int(str(e).split(' ')[0].strip("Security(")) for e in context.sids] context.data = pd.DataFrame({ k : pd.Series() for k in context.tickers } ) context.daysToRecalibration = 0 context.onevec = np.asmatrix(np.ones((len(context.tickers), 1))) def handle_data(context, data): if context.portfolio.starting_cash == context.portfolio.cash: #buy into the benchmark while we build the starting data set order(sid(8554), math.floor(context.portfolio.starting_cash/data[sid(8554)].close_price) ) if len(context.data.index) < context.nobs: #still recording data newRow = pd.DataFrame({k:float(data[e].returns()) for k,e in zip(context.tickers, context.sids) },index=[0]) context.data = context.data.append(newRow, ignore_index = True) else: newRow = pd.DataFrame({k:float(data[e].returns()) for k,e in zip(context.tickers, context.sids) },index=[0]) context.data = context.data.append(newRow, ignore_index = True) context.data = context.data[1:len(context.data.index)] if context.portfolio.positions[sid(8554)].amount != 0: #data gathering time is done, get out of the benchmark order(sid(8554), -1.0*context.portfolio.positions[sid(8554)].amount) #wait a day for the trades to clear before placing the new trades. return if context.daysToRecalibration == 0: context.daysToRecalibration = context.recalibrate #recalibrate log.info('recalibrating...') #calculate the minimum variance portfolio weights; precision = np.asmatrix(la.inv(context.data.cov())) pimv = precision*context.onevec / (context.onevec.T*precision*context.onevec) pimv = { e:pimv[i,0] for i,e in enumerate(context.tickers) } #open all positions: startingCash = (context.portfolio.starting_cash+context.portfolio.pnl)*context.leverage for i, e in enumerate(context.sids): currentPosition = context.portfolio.positions[e].amount newPosition = math.floor(startingCash*pimv[context.tickers[i]]/data[e].price) order(e, newPosition - currentPosition) else: context.daysToRecalibration -= 1 record(c = context.portfolio.positions_value)

This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.