Hi, in the last weeks I played a while with the MVO Algorithm presented by JCL on his blog and the "Momentum and Markowitz: A Golden Combination" paper (I admit not yet fully metabolized). I have tried to assemble a script that trade the MVO algorithm in a similar way than the new z8. The idea was to compare different settings of the DAYS and CAP parameters (see the code below) with the usual Zorro cockpit (AR PF SR UI R2).
Z8 was a perfect fit I had a properly coded system ready to be compared.
This is the code:
Code:
//////////////////////////////////////////////////////
// Mean Variance Optimization ////////////////////////
//////////////////////////////////////////////////////
#define DAYS 252 // 1 year
//#define DAYS 6*22 // 6 Months
//#define DAYS 4*22 // 4 Months
//#define DAYS 2*22 // 2 Months
#define WEIGHTCAP .25 // Cap 0.15 - 0.5 Range
#define NN 50 // max number of assets
#define LEVERAGE 4 // 1:4 leverage
//////////////////////////////////////////////////////
function run()
{
BarPeriod = 1440;
LookBack = DAYS;
NumYears = 7;
set(PRELOAD); // allow extremely long lookback period
set(LOGFILE);
Verbose = 0;
// AssetList = "ETF2016-OK.csv";
AssetList = "AssetsZ8.csv";
string Names[NN];
string Symbols[NN]; // Store the ISIN Code
vars Returns[NN];
var Means[NN];
var Covariances[NN][NN];
var Weights[NN];
static int OldLots[NN];
var TotalCapital = slider(1,25000,10000,50000,"Capital","Total capital to distribute");
var VFactor = slider(2, 10 ,0, 100,"Risk","Variance factor");
int N = 0;
while(Names[N] = loop(Assets))
{
asset(Names[N]);
if((is(INITRUN) && (strstr(Names[N], "#")== NULL))) {
assetHistory(Names[N],FROM_YAHOO);
Symbols[N] = Symbol; // Store the isin code for quick referenze
} else if(strstr(Names[N], "#")== NULL) Returns[N] = series((priceClose(0)-priceClose(1))/priceClose(1));
else Returns[N] = series(0);
if(N++ >= NN) break;
}
int i,j;
if(tdm() == 1 ){
for(i=0; i<N; i++) {
Means[i] = Moment(Returns[i],LookBack,1);
for(j=0; j<N; j++)
Covariances[N*i+j] = Covariance(Returns[i],Returns[j],LookBack);
}
var BestVariance = markowitz(Covariances, Means, N, WEIGHTCAP);
var MinVariance = markowitzReturn(0,0);
markowitzReturn(Weights,MinVariance+VFactor/100.*(BestVariance-MinVariance));
// change the portfolio composition according to new weights
for(i=0; i<N; i++)
if (strstr(Names[i], "#")== NULL){
asset(Names[i]);
MarginCost = priceClose()/LEVERAGE;
int NewLots = TotalCapital*Weights[i]/MarginCost;
if(NewLots > OldLots[i])
enterLong(NewLots-OldLots[i]);
else if(NewLots < OldLots[i]) exitLong(0,0,OldLots[i]-NewLots);
// printf("\n%s - %s: OldLots: %d NewLots: %d %.0f$",Names[i],Symbols[i], OldLots[i], NewLots );
OldLots[i] = NewLots;
}
}
}
Considered that JCL plays in another league, I am satisfied with the following results obtained with the same z8 assets list.
Trades 460 Win 75.0% Avg +386.4p Bars 184 AR 22% PF 4.94 SR 1.45 UI 5% R2 0.97
Anyway the code is not polished and has some issues:
Starting the script without any history (delete the .bar file from the history dir) I have to press the test button twice before get the results. I am doing something wrong, but I do not understand what.
The number of trades are high compared with Z8. Is a mistake in re-balancing the portfolio?
the z8 equity line is calculated once per month (as it should be), in my script is plotted once for day. Mistake or just a Z8 better way to plot the equity line?
I think the number of trades is related to the TotalCapital variable. Try setting it to 1000 and the number will be similar to Z8 backtests with default settings.
Two additional notes: - maybe it would be more proper to use longer time period - to include (for example) crash of 2008 or 2002 downturn. Without it the results might be too optimistic. - could be worth to simulate the choosing of the assets year by year. In the past some of the current assets did not exist so if you traded this strategy, you would probably use different ones.
yes, that was only one testing setting regarding the longer period, that is a key point. On my to do list is to develop a script to test an etf and decide if it has enough data from a quality and quantitative point of view. ETF in Italy does not have such a long data history.
Another point is the trading costs. I am trying to simulate fixed cost for each transaction. Still ea lot to do...
I am located in iTaly and I have seen that the MVO script trade at 4 pm (local time). Is someone aware how to ask the system to trade at 9.00 am (local time))
I have read the manual regarding timezone, but I am a bit confused.
////////////////////////////////////////////////////// // Mean Variance Optimization //////////////////////// //////////////////////////////////////////////////////
//#define DAYS 252 // 1 year 252 //#define DAYS 231 // 11 Months //#define DAYS 210 // 10 Months #define DAYS 189 // 9 Months //#define DAYS 168 // 8 Months //#define DAYS 147 // 7 Months //#define DAYS 126 // 6 Months
#define NN 300 // max number of assets #define WEIGHTCAP 0.25 // Cap 0.15 - 0.5 Range #define LEVERAGE 4 // 1-20 leverage //#define MAXETF 7 //////////////////////////////////////////////////////
string Names[NN]; string Symbols[NN]; // Store the ISIN Code vars Returns[NN]; var Means[NN]; var Covariances[NN][NN]; var Weights[NN]; int OldLots[NN]; var ThePrice[NN]; int N = 0;
vars Returns[NN]; var TotalCapital = slider(1,20000,1000,50000,"Capital","Total capital to distribute"); var VFactor = slider(2, 0 ,0, 100,"Risk","Variance factor");
if(is(INITRUN)) { while(Names[N]=loop(Assets)) { if(strstr(Names[N], "#")== NULL) { assetHistory(Names[N], FROM_YAHOO); asset(Names[N]); Symbols[N] = Symbol; // Store the isin code for quick referenze // printf("\n Names, Asset, Symbol %d: %s - %s - %s", N, Names[N], Asset, Symbol); if(N++ >= NN) break; } } }
for(i=N-MAXETF; i<N; i++) // sum up the 4 highest weights TotalWeight += Weights[idx[i]]; for(i=0; i<N; i++) { if(idx[i] < N-MAXETF) Weights[i] = 0; else // adjust weights so that their sum is still 1 Weights[i] /= TotalWeight; } #endif
// change the portfolio composition according to new weights // var TheCapital = TotalCapital + WinTotal; printf ( "\n\n --- %s ---",datetime()); for(i=0; i<N; i++) { asset(Names[i]); MarginCost = priceClose()/LEVERAGE;
// RollShort = RollLong =0; // Spread = FIXEDCOM;
int NewLots = TotalCapital * Weights[i] / MarginCost;
I have been working on an implementation of MVO and the Markowitz portfolio theory similarly to MatPed's work here. My goal is to backtest an actual implementation including transaction costs.
There is a curious problem. I wrote a function current_position() to get the position in an Asset. Theoretically, open_position and oldLots[i] should be the same. However, as the check at the end of run() shows, this is not always the case.
Possible reasons:
1. current_position doesn't do what it is supposed to do. Should I use open_trades? Check if TradeIsOpen? Can someone confirm this function does what it is supposed to do? Is there a better way?
2. Zorro sells the positions from time to time. Why? According to which rule?
This would mean that the calls enterLong(newLot - oldLots[i]) and exitLong(0, 0, oldLots[i] - newLot) would need to be changed. According to my reading of the manual, they are correct (all defaults are 0).
(I am actually using a different asset list csv file, but this shouldn't matter at all.)
Currently, the backtesting doesn't work correctly, as Zorro reduces the positions and then the portfolio is not balanced according to the weights[]. Any help would be appreciated. I think this code is very close, but I guess I am missing some enterLong or exitLong magic.
Cheers
PS: I'd be happy to post improved versions if we can figure out the problem and if people are interested.
PPS: It seems as if the indentation gets messed up.
const var LEVERAGE = 4.0; const bool verbose = FALSE;
function current_position(string asset_name) { int sum = 0; for (current_trades) { if (TRUE) { // (TradeIsOpen) { //(0 == strcmp(Asset, asset_name)) { sum += TradeLots;}} return sum;}
function run() { set(PRELOAD); set(LOGFILE); set(PLOTNOW);
static int N = 0; static string names[NN]; static int oldLots[NN]; vars returns[NN]; // series var means[NN]; var covariances[NN][NN]; var weights[NN];
var totalCapital = slider(1, 10000, 1000, 50000, "Capital", "Total capital to distribute"); var vFactor = slider(2, 100, 0, 100, "Variance", "Variance factor, 0 = minimal variance, 100 = best variance"); var weightConstraintFactor = slider(3, 50, 10, 90, "Max. w.", "maximum weight = (this value)/100");
if (is(INITRUN)) { while (asset(loop(Assets))) { names[N] = Asset; printf("\nDownloading asset %d %s", N, names[N]); assetHistory(Asset, FROM_YAHOO); printf("\nAsset %d: name %s", N, names[N]); oldLots[N] = 0;
if (++N >= NN) { break;}}
printf("\n%d assets", N);}
int i; for (i=0; i<N; i++) { asset(names[i]); returns[i] = series((priceClose(0)-priceClose(1)) / priceClose(1)); // printf("\nAsset %d %s: return %f", i, names[i], (returns[i])[0]); }
if (1 == tdm() && !is(LOOKBACK)) { int i, j; for (i=0; i<N; i++) { means[i] = Moment(returns[i], LookBack, 1); for (j=0; j<N; j++) { covariances[i*N + j] = Covariance(returns[i], returns[j], LookBack);}}
var bestVariance = markowitz(covariances, means, N, weightConstraintFactor/100.0); var minVariance = markowitzReturn(0, 0);
I compered the performance of Z8 vs MatPed's provided script (DAYS = 6 months) and I found the following figures: 1) Z8 capital=1000 AR=26% 2) Z8 capital=7000 AR=24% 3) MVO capital=1000 AR=17% 4) MVO capital=7000 AR=11% I don't understand why so big differences between 3) and 4)?
MVO compiling.......... Assets........................ Test: MVO portfolio 2011..2016 Account AssetsZ8.csv Monte Carlo Analysis... Median AR 17% Profit 935$ MI 14$ DD 71$ Capital 1012$ Trades 221 Win 70.6% Avg +140.7p Bars 56 AR 17% PF 4.45 SR 1.53 UI 2% R2 0.52
Test: MVO portfolio 2011..2016 Account AssetsZ8.csv Monte Carlo Analysis... Median AR 11% Profit 4593$ MI 69$ DD 559$ Capital 7492$ Trades 337 Win 67.1% Avg +61.0p Bars 45 AR 11% PF 3.11 SR 1.10 UI 3% R2 0.84
Z8 (oP group) .. Z8.5: PH M 1000 H 5 W 2 V 2 24 assets loaded Assets........................ Test: Z8 portfolio 2011..2016 Account AssetsZ8 (NFA) Monte Carlo Analysis... Median AR 28% Profit 1863$ MI 26$ DD 150$ Capital 1180$ Trades 368 Win 73.6% Avg +181.0p Bars 69 AR 26% PF 4.13 SR 1.49 UI 4% R2 0.95 Chart... ok
Z8 (oP group) .. Z8.5: PH M 1000 H 5 W 2 V 2 24 assets loaded Assets........................ Test: Z8 portfolio 2011..2016 Account AssetsZ8 (NFA) Monte Carlo Analysis... Median AR 26% Profit 13305$ MI 183$ DD 1544$ Capital 9233$ Trades 561 Win 70.1% Avg +147.7p Bars 61 AR 24% PF 3.44 SR 1.42 UI 4% R2 0.93 Chart... ok