Hello Grant, et al:
An algorithm that is simple, trades weekly, and achieves Sharpe > 2, deserves some consideration.
In playing with Grant's algo I noticed some odd behavior and took some effort to investigate.
My backtest file contains additional comments.
1. The algorithm is novel in how returns are calculated from minute data over a five day span. This overcomes a problem with the use of daily data. For robust covariance you want to use a number of periods that is at least 10x the number of equities. Four equities implies 40 days of data. Such a long window is a problem when trying to characterize an erratic equity like VIX or a commodity.
2. Total return for selected equities (RSP, EDV, TLT, XIV) and eps (0.05) is superb over the period 1 Dec 2010 to 4 Feb 2016
3. Total return is very sensitive to eps
288% @ eps = 0.049
362% @ eps = 0.050
219% @ eps = 0.051
4. Total return changes drastically with substitution of similar equities
(SPY for RSP) 235% vs 362%
(TLO for TLT) 197% vs 362%
5. The total return is very sensitive to the number of days of look back (separate backtest showed more than 3-to-1 variation in Sharpe ratio for lookback periods of 3 to 20 days). Similarly the result was very sensitive to start/end dates.
6. Something is wrong for such sensitivity in the result.
Compliance with the inequality constraint
For Grant's version of the algo and the case of eps=0.05 the inequality constraint is only met 49 out of 1303 days
This means that the algo is coasting the prior solution most of the time and is not operating as intended.
As you might expect is such a case the result is very sensitive to parameter or equity changes.
If the "success" logic is corrected the actual return is 101%, which is better than SPY, but with much higher volatility and drawdown
Why is this happening?
The ret_norm values are typically smaller than 0.05 and are often negative.
This means that on most days no set of positive weights summing to 1.0 can be found that will satisfy the constraint: dot(weights,ret_norm) > eps
But the algo checks for res.success
Yes, it does.
I don't understand the scipy SLSQP implementation well enough to say why res.success is True when res.status is not 0
Grant appears to be calling/invoking (what is the python term?) per the documentation
The following res.status errors are commonly seen with large eps values:
4 : Inequality constraints incompatible
8 : Positive directional derivative for linesearch
9 : Iteration limit exceeded (this always appears on the first 4 days)
So should I use a smaller eps value?
Generally yes, but not a fixed value
In this particular algo the set of equities is small and there is no other mechanism to assure that ret_norm values above some threshold, or are even positive.
Given this you face poor trade: reducing eps allows the algorithm to function as intended, but degrades performance as eps is the return threshold
Some dynamic method for setting eps is needed for this algorithm.
How to dynamically select eps
I welcome those of you with finance and math backgrounds to provide a more elegant solution, Here's a simple approach.
Assume that Equity 1 has the highest of the four ret_norm values (ret_norm_max). If the weights are set so that all are zero except for Equity 1, then the portfolio's ret_norm = ret_norm_max. Since the number of equities is small it is possible that Equity 1 is the only one with ret_norm>0. This condition frustrates optimization. A smaller eps value than ret_norm_max will improve the likelihood of optimization. As noted above a too small value will rob performance.
Here are some results for eps = 85%, 90%, 95%, and 100% of ret_norm_max
For each case the SLSQP optimizer succeeds in 1299 of 1303 days. It consistently fails to on the first 4 days.
return = 189% @ 100%
return = 239% @ 95%
return = 243% @ 90%
return = 232% @ 85%
While the result is not as spectacular as it first appeared the algorithm is now functioning as I expected and is more robust to parameter and equity changes. Now more investigation can be done (eps selection, equity selection, look back window, ...)