| Overall Statistics |
|
Total Orders 363 Average Win 0.48% Average Loss -0.64% Compounding Annual Return 4.945% Drawdown 17.000% Expectancy 0.191 Start Equity 1000000 End Equity 1275893.95 Net Profit 27.589% Sharpe Ratio -0.06 Sortino Ratio -0.07 Probabilistic Sharpe Ratio 0.195% Loss Rate 32% Win Rate 68% Profit-Loss Ratio 0.75 Alpha -0.027 Beta 0.407 Annual Standard Deviation 0.096 Annual Variance 0.009 Information Ratio -0.504 Tracking Error 0.114 Treynor Ratio -0.014 Total Fees $3234.49 Estimated Strategy Capacity $4200000.00 Lowest Capacity Asset RWR S47K41INKZMT Portfolio Turnover 2.24% Drawdown Recovery 700 |
BRIEF = """
IMPLEMENTATION BRIEF v2 - Giordano (2019), "Antifragile Asset Allocation Model" (NAAIM 2019), with undefined pieces taken from
Giordano (2018), "Ranked Asset Allocation Model" (Dow Award 2018) ONLY where the 2019 paper is silent (client instruction).
STRATEGY
Monthly ETF rotation combining two sub-models that share one ranking algorithm:
- Sector Rotation Model (SRM): 11 US sector ETFs, pick the best 5 by rank score.
- Black Swan Hedging Model (BSHM): 7 "crisis" ETFs, pick the best 3 by rank score.
AAAM = SRM top-5 with positive absolute momentum at 20% each; every 20% slot whose sector ETF has non-positive momentum is handed to
the BSHM allocation (equal split across its 3 slots; a BSHM pick with non-positive momentum is replaced by SHY = Cash). If all 5
sectors have non-positive momentum the BSHM allocation gets 100%. Long-only, fully invested, no leverage.
UNIVERSE (fixed lists, 2019 Tables 2 and 5)
SRM: XLY, XLV, XLU, XLP, XLK, XLI, XLF, XLE, XLB, VOX, RWR
BSHM: FXF (Swiss Franc), FXY (Japanese Yen), GLD, IEF, SH, TLT, SHY ; Cash asset = SHY
Dataset: QuantConnect US Equity daily bars (adjusted). All 18 ETFs trade from 2007-02-13.
SIGNALS (daily EOD data; "classification is done on a monthly basis, taking the last value of the month"; n = assets in the model)
(M) Absolute Momentum: 4-month ROC = close(t) / close(t - 84 trading days) - 1.
(V) Volatility Model (2018 paper, Sec. III + V): RiskMetrics EWMA daily variance, lambda = 0.94, on daily log returns
(sigma2_t = 0.94*sigma2_{t-1} + 0.06*r_{t-1}^2), volatility = sqrt(sigma2), then the "10-day smoothed variant" = 10-day simple
moving average of that daily volatility. Lower = better.
(C) Average Relative Correlation: mean of the pairwise Pearson correlations of the asset's daily returns with each other asset of the
same model (10 others for SRM, 6 for BSHM) over the trailing 84 trading days. Lower = better.
(T) ATR Trend/Breakout System (2018 paper, Sec. IV), daily bars, Wilder ATR(42):
Upper Band = ATR(42) + Highest Close of 63 periods; Lower Band = ATR(42) + Highest Low of 105 periods (as printed, client decision;
bands from bars up to t-1). Signal: if a day's HIGH > Upper Band the model goes Long the following session, T = +2; if a day's LOW <
Lower Band the model goes Neutral/Short the following session, T = -2. The state persists until the opposite signal; if both
conditions fire on the same day the Neutral/Short signal prevails. T = -2 before the first signal (no long position yet).
Score (2019 eq. 1, per model): TRANK = wM*Rank(M) + wV*Rank(V) + wC*Rank(C) - wT*T + M/n
Rank(M): 1 = highest momentum; Rank(V): 1 = lowest volatility; Rank(C): 1 = lowest average correlation.
Weights: wM = 1.0, wV = 0.5, wC = 0.5 (Keller & van Putten FAA weights; neither paper discloses values), wT = 1.0 (the 2018 formula
has "-T" with no weight).
LOWEST TRANK = best (2018: "Only the 5 ETFs with the lowest Total Rank will be taken"). SRM keeps the 5 lowest, BSHM the 3 lowest.
PORTFOLIO CONSTRUCTION
k = number of SRM top-5 with M > 0. Each such sector ETF gets 20%. Remaining weight R = 1 - 0.20*k goes to the BSHM: each of its
top-3 gets R/3 if its M > 0, otherwise that R/3 goes to SHY. Weights of the same ETF are summed (SHY may be a pick and cash).
REBALANCE TIMING
Ranking computed from the last trading session of each month; target weights ordered before the open of the first trading session of
the following month (market orders, filled at the open). Held for one month. Platform default fees kept (paper is gross of fees).
ESTIMATION SCHEME
No parameter fitting. All indicators (ROC 84, EWMA variance, SMA 10, ATR 42, Max 63 / Max 105 of closes / lows, 84-day return
windows) run daily and are read once a month. No train/validation/test split (single out-of-sample run, as in the paper).
SAMPLE (client decision)
Backtest start 2021-09-01, no end date. All lookbacks (ROC 84, ATR 42 + bands 105, EWMA seeded ~1 year back, T state) are seeded
from history / warm-up before that date, so the first ranking uses the last session of August 2021 and the first allocation is
ordered on the first session of September 2021. Starting cash 1,000,000 (smallest weight 6.67% -> 66,700 per order).
DECISIONS
1. Factor weights wM, wV, wC are not given in either paper. Chosen: wM=1, wV=0.5, wC=0.5 (FAA weights of Keller & van Putten 2012,
cited by both papers). wT = 1 because the 2018 formula subtracts T with no weight. Rejected: equal weights.
2. Rank direction / selection direction. The 2018 paper contradicts itself: its prose says the best asset gets the HIGHEST rank
number (rank 11 = highest M, lowest V, lowest C), but its selection rule says "the 5 ETFs with the LOWEST Total Rank", and its
"-T" term (T = +2 when Long) only makes sense if a LOWER total is better. Chosen: rank 1 = best in each factor and the LOWEST
TRANK is selected (consistent with the explicit selection rule, the -T term and the FAA convention). Rejected: rank 11 = best with
the HIGHEST total selected (would make -T penalise uptrending assets, contrary to the stated purpose of T). Under the chosen reading
the literal "+M/n" tie-break favours lower momentum; it is implemented literally as printed (both papers print +M/n or +M/x).
3. Volatility Model = "edited GARCH": the 2018 paper defines it as RiskMetrics daily variance with lambda 0.94 (Zangari 1996) and a
10-day smoothed variant used for ranking. Chosen: EWMA(0.94) of squared daily log returns, sqrt, 10-day SMA. The 2018 paper also
says the model "uses OHLC daily data" but gives no OHLC formula; close-to-close returns are used (RiskMetrics definition).
Rejected: GARCH(1,1) MLE, plain stdev, range-based (Garman-Klass) estimators.
4. Momentum window: "4 months" read as 84 trading days on daily closes (Metastock daily ROC). Rejected: 4 month-end closes.
5. Correlation: 84 trading days of daily simple returns, average of pairwise correlations with the other assets of the SAME model.
Rejected: correlation vs. the whole 18-ETF set or vs. an equity index.
6. Lower Band = Highest Low(105) + ATR(42), as printed (client decision; the 2018 paper confirms the sum is intentional).
7. T values and mechanics from the 2018 paper: Long = +2, Neutral/Short = -2, state changes the session after the signal day, signals
on the day's HIGH vs Upper Band and LOW vs Lower Band. Not stated: precedence when both fire on the same day (chosen: Neutral/Short
prevails, the exit rule dominates; rejected: Long prevails) and the initial state (chosen: -2, i.e. not Long until a breakout;
rejected: 0).
8. Bands use bars up to t-1. With bars including t, "low(t) < Highest Low(105) + ATR" would be true on every bar (tautology), so the
prior-bar convention is the only one under which the printed rule can produce a Long state.
9. Ties in ranks: average rank (scipy rankdata). An asset lacking data is excluded (does not occur with fixed lists after warm-up).
10. Execution: ranking from the month's last session; market orders before the open of the next session (paper: "the Ranking Model in
the last session of the current month determines the allocation of the following month"). Rejected: trading at that close.
11. Cash asset SHY is also one of the 7 BSHM candidates (2019 paper; the 2018 paper excluded Cash from ranking, 2019 wins).
12. Dates (client decision): start 2021-09-01, no end date; lookbacks seeded from history before that date. No proxy back-fill.
13. FXF/FXY tickers appear swapped against their names in 2019 Tables 5 and 8; both ETFs are in the model, so nothing changes.
14. Fees: platform default brokerage fees kept (paper is gross of fees). No slippage model added.
15. n in M/n = number of ranked assets of the model (11 for SRM, 7 for BSHM), following the 2019 definition (n = number of assets).
16. EWMA initialisation: seeded with the sample variance of the first 84 returns of a ~1-year history before the start; after ~250
days the seed's weight is below 1e-6, so it does not affect the ranking at 2021-08-31.
CLIENT CORRECTION (approved brief): RiskMetrics lambda = 0.943 (not 0.94) everywhere above:
sigma2_t = 0.943*sigma2_{t-1} + 0.057*r_{t-1}^2. Brief v2 approved 2026-09-17 with this correction; no further review needed.
REQUESTS
NONE
"""
from AlgorithmImports import *
import numpy as np
from scipy.stats import rankdata
class EwmaVolatility(PythonIndicator):
"""RiskMetrics EWMA volatility of daily log returns (lambda), 10-day SMA smoothed.
Seeded with the sample variance of the first seed_len returns."""
def __init__(self, name: str, lam: float, seed_len: int, smooth: int):
self.name = name
self.time = datetime.min
self.value = 0.0
self._lam = lam
self._seed_len = seed_len
self._seed = []
self._prev_close = None
self._var = None
self._sma = SimpleMovingAverage(smooth)
def update(self, input: TradeBar) -> bool:
close = float(input.close)
self.time = input.end_time
if self._prev_close is not None and self._prev_close > 0 and close > 0:
r = np.log(close / self._prev_close)
if self._var is None:
self._seed.append(r)
if len(self._seed) >= self._seed_len:
self._var = float(np.var(self._seed))
else:
self._var = self._lam * self._var + (1.0 - self._lam) * r * r
if self._var is not None:
self._sma.update(input.end_time, float(np.sqrt(self._var)))
self.value = self._sma.current.value
self._prev_close = close
return self._sma.is_ready
@property
def is_ready(self) -> bool:
return self._sma.is_ready
class AssetData:
"""All daily indicator state of one ETF; fed one TradeBar at a time (same path for seeding and live)."""
LONG = 2
SHORT = -2
def __init__(self, symbol: Symbol, lam: float):
self.symbol = symbol
self.roc = RateOfChange(84) # (M) 4-month momentum
self.vol = EwmaVolatility(f"{symbol}_vol", lam, 84, 10) # (V)
self.ret1 = RateOfChange(1) # daily returns for (C)
self.ret1.window.size = 84
self.atr = AverageTrueRange(42, MovingAverageType.WILDERS)
self.max_close = Maximum(63)
self.min_low = Minimum(105)
self.t_value = AssetData.SHORT # T applicable to the last processed session
self._t_next = AssetData.SHORT # T applicable to the following session
self.bars = 0
def update(self, bar: TradeBar) -> None:
# bands from bars up to t-1 (indicator values before this bar is applied)
if self.atr.is_ready and self.max_close.is_ready and self.min_low.is_ready:
upper = self.atr.current.value + self.max_close.current.value
lower = self.atr.current.value + self.min_low.current.value
self.t_value = self._t_next
if bar.low < lower:
self._t_next = AssetData.SHORT
elif bar.high > upper:
self._t_next = AssetData.LONG
t = bar.end_time
self.roc.update(t, bar.close)
self.ret1.update(t, bar.close)
self.vol.update(bar)
self.atr.update(bar)
self.max_close.update(t, bar.close)
self.min_low.update(t, bar.low)
self.bars += 1
@property
def is_ready(self) -> bool:
return (self.roc.is_ready and self.vol.is_ready and self.ret1.is_ready
and self.ret1.window.count >= 84 and self.atr.is_ready)
def returns(self) -> List[float]:
return [self.ret1[i].value for i in range(84)]
class AntifragileAssetAllocation(QCAlgorithm):
SRM = ["XLY", "XLV", "XLU", "XLP", "XLK", "XLI", "XLF", "XLE", "XLB", "VOX", "RWR"]
BSHM = ["FXF", "FXY", "GLD", "IEF", "SH", "TLT", "SHY"]
CASH = "SHY"
W_M, W_V, W_C, W_T = 1.0, 0.5, 0.5, 1.0
LAMBDA = 0.943
SEED_BARS = 500
def initialize(self) -> None:
self.set_start_date(2021, 9, 1)
self.set_cash(1_000_000)
self.settings.seed_initial_prices = True
self._srm = []
self._bshm = []
for ticker in self.SRM:
sec = self.add_equity(ticker, Resolution.DAILY)
sec.aaam = AssetData(sec.symbol, self.LAMBDA)
self._srm.append(sec)
for ticker in self.BSHM:
sec = self.add_equity(ticker, Resolution.DAILY)
sec.aaam = AssetData(sec.symbol, self.LAMBDA)
self._bshm.append(sec)
self._cash = self.securities[self.CASH].symbol
self._seed()
self.schedule.on(self.date_rules.month_start("XLY"), self.time_rules.at(8, 0), self._rebalance)
def _seed(self) -> None:
symbols = [s.symbol for s in self._srm + self._bshm]
history = self.history[TradeBar](symbols, self.SEED_BARS, Resolution.DAILY)
for bars in history:
for symbol, bar in bars.items():
self.securities[symbol].aaam.update(bar)
def on_data(self, data: Slice) -> None:
for symbol, bar in data.bars.items():
self.securities[symbol].aaam.update(bar)
def _rank_model(self, secs: List[Security]) -> Dict[Symbol, Any]:
"""Paper eq. (1): TRANK = wM*Rank(M) + wV*Rank(V) + wC*Rank(C) - wT*T + M/n, rank 1 = best.
Returns {symbol: (trank, M)} for the assets with complete data."""
ready = [s for s in secs if s.aaam.is_ready]
n = len(ready)
if n < 2:
return {}
m = np.array([s.aaam.roc.current.value for s in ready])
v = np.array([s.aaam.vol.value for s in ready])
t = np.array([s.aaam.t_value for s in ready], dtype=float)
rets = np.array([s.aaam.returns() for s in ready])
corr = np.corrcoef(rets)
c = (corr.sum(axis=1) - 1.0) / (n - 1)
rank_m = rankdata(-m)
rank_v = rankdata(v)
rank_c = rankdata(c)
trank = self.W_M * rank_m + self.W_V * rank_v + self.W_C * rank_c - self.W_T * t + m / n
return {s.symbol: (float(trank[i]), float(m[i])) for i, s in enumerate(ready)}
def _rebalance(self) -> None:
srm = self._rank_model(self._srm)
bshm = self._rank_model(self._bshm)
if len(srm) < 5 or len(bshm) < 3:
return
weights = {}
# SRM: best 5 (lowest TRANK); 20% each if M > 0, otherwise the slot goes to the BSHM
srm_top = sorted(srm.items(), key=lambda kv: kv[1][0])[:5]
residual = 1.0
for symbol, (trank, m) in srm_top:
if m > 0:
weights[symbol] = weights.get(symbol, 0.0) + 0.20
residual -= 0.20
# BSHM: best 3 share the residual equally; a pick with M <= 0 is replaced by Cash (SHY)
if residual > 1e-9:
bshm_top = sorted(bshm.items(), key=lambda kv: kv[1][0])[:3]
slot = residual / 3.0
for symbol, (trank, m) in bshm_top:
target = symbol if m > 0 else self._cash
weights[target] = weights.get(target, 0.0) + slot
targets = [PortfolioTarget(symbol, w) for symbol, w in weights.items()]
self.set_holdings(targets, liquidate_existing_holdings=True)
NOTES = """
PAPER: Giordano, G. (2019) "Antifragile Asset Allocation Model", NAAIM Wagner award submission. 19 pages.
p1-3: Abstract + Intro. Combines an active quantitative investment model with a tail-risk hedging strategy
to create an "antifragile" portfolio. Black Swan concept (Taleb 2007). Figure 2: DJIA with a "Black Swan Indicator"
showing market corrections, monthly data Feb 1915 - Feb 2019. Riskiest asset classes identified via volatility.
No formulas yet.
p4-6: Model = Sector Rotation Model (SRM) + Black Swan Hedging Model (BSHM) = Antifragile Asset Allocation Model (AAAM).
Influences: Keller & van Putten (Flexible Asset Allocation, FAA), Faber, Wilder (ATR), Engle/Bollerslev (GARCH), Pring.
Backtest results shown as monthly performance Jun 2004 - Feb 2019.
SECTOR ROTATION MODEL (p6): 11 sector ETFs of S&P500 (Table 2):
1 XLY Consumer Discretionary; 2 Health Care (table typo says XLY -> XLV); 3 XLU Utilities; 4 XLP Staples; 5 XLK Tech;
6 XLI Industrials; 7 XLF Financials; 8 XLE Energy; 9 XLB Materials; 10 VOX Vanguard Communication Services; 11 RWR SPDR DJ REIT.
Each MONTH ranks the 11 ETFs on factors:
(M) Absolute Momentum: 4-month momentum (ROC rate of change).
(V) Volatility model: "edited version of GARCH model" (details later).
(C) Average Relative Correlations: 4-month average correlation across the ETFs (diversification).
(T) ATR Trend/Breakout system: ATR bands on DAILY timeframe.
Upper Band = 42-period ATR + Highest Close of 63 periods.
Lower Band = 42-period ATR + Highest Low of 105 periods (sic; as printed - likely "Highest Low of 105 periods minus ATR"? check later pages)
p7: Ranking formula (1):
TRANK = (wM*Rank(M) + wV*Rank(V) + wC*Rank(C) - wT*T) + M/n
Rank(M) = rank 1..11 by Absolute Momentum; Rank(V) = rank 1..11 by Volatility Model; Rank(C) = rank 1..11 by Avg Relative Correlation;
T = ATR Trend/Breakout System (value, not rank; enters with minus sign times wT);
wM, wV, wC, wT = % weights; n = number of assets (11). M/n = momentum divided by n (tie-break term).
NUMERIC WEIGHT VALUES NOT GIVEN on p7 (check later pages / FAA paper: Keller & van Putten use wR=1, wV=0.5, wC=0.5).
Rank direction not stated on p7 (FAA convention: rank 1 = best, i.e. highest momentum, lowest vol, lowest corr; lowest total = best).
Best 5 ETFs by TRank selected, EQUALLY WEIGHTED.
Figure 5: SRM vs SPY, monthly Aug 2003 - Feb 2019. Table 3 / Fig 6: SRM returns & allocation Aug 2003 - Feb 2019.
p8: SRM beats S&P500 but not immune to crashes -> needs hedge.
p8-9: Section IV Black Swan Hedging Strategy: tail-risk background. Fig 7: AQR 1-yr OTM puts growth 1996-2012.
Mentions a permanent tail-risk strategy buying monthly 5% OTM S&P500 puts with 90% in 10-yr US Govt bonds (illustrative, chart follows).
p10: Table 4 (Faber/GFD tail risk vs S&P 1986-2012): static tail risk lowers DD but worsens Sharpe.
BLACK SWAN HEDGING MODEL (BSHM) p10-11: 7 ETFs representing asset classes that benefit from corrections of different nature (Table 5, p10):
1 Swiss Franc ETF (labelled FXY in table - ticker swap; Swiss Franc = FXF); 2 Japanese Yen ETF (labelled FXF; Yen = FXY);
3 GLD SPDR Gold; 4 IEF iShares 7-10Y Treasury; 5 SH ProShares Short S&P500; 6 TLT iShares 20+Y Treasury; 7 SHY iShares 1-3Y Treasury.
(SHY is BOTH a ranked candidate among the 7 and the Cash replacement asset.)
Best 3 of the 7 ETFs selected using the SAME ranking system as SRM (TRank formula (1)).
For each of the 3: if Absolute Momentum M > 0 -> included; else its weight replaced with CASH = iShares 1-3 Year Treasury Bond ETF (SHY).
If all top 3 have negative M -> 100% Cash (SHY). (Implied: each of the 3 gets 1/3 weight, equal weight.)
Fig 8/Table 6/7/Fig 9: BSHM results Aug 2003 - Feb 2019.
PROGRESS CHECKPOINT: pages 1-9 read; p10-11 text read but Table 5 (7 BSHM ETF list) missing -> needs OCR re-run of p10 and p11 alone;
p12 partially read (hidden tail); pages 13-19 NOT yet read. PDF converter was returning 'Error connecting to QuantConnect Cloud API'.
Data availability (research notebook cell 0): XL* sectors from 1998-12-22, RWR 2001-04-27, VOX 2004-09-29, SHY/TLT/IEF 2002-07-26,
GLD 2004-11-18, TIP 2003-12-05, DBC 2006-02-03, SH 2006-06-21, GDX 2006-05-22, UUP 2007-02-20, FXY 2007-02-13, VXX 2018-01-18.
p11: Table 6 BSHM monthly returns start Aug 2003 although FXY/FXF (2007), SH (2006), GLD (Nov 2004) ETFs did not exist -> author
must have back-filled with index/proxy data (not stated). Table 7 BSHM stats (not to be copied). Fig 9 BSHM allocation.
V. ANTIFRAGILITY (p12, verbatim logic): AAAM = union of SRM and BSHM. SRM selects best 5 sector ETFs. For each of the 5, if M > 0 -> 20% weight
in the Antifragile Portfolio. If all top-5 have negative M -> BSHM allocation gets 100%. Unassigned weighting replaced with BSHM allocation.
p13: Table 8 AAAM weights as of 02/19/2019: XLU 20%, RWR 20%, "Swiss Franc ETF (FXY label)" 20%, IEF 20%, SHY 20%, all others 0.
=> 2 sector ETFs with M>0 at 20% each; remaining 60% went to the BSHM's 3 picks at 20% each (=60%/3). Confirms BSHM allocation is
equal-weight over its 3 slots (negative-M slot -> SHY), scaled to the unassigned weight. Fig 10 AAAM vs SPY Aug 2003-Feb 2019. Table 9 AAAM monthly returns.
p14: VI. APPLICATION AND EMPIRICAL TESTS: Data = end-of-day from Yahoo! Finance; interpolations with consistent historical series where
necessary for temporal homogeneity (i.e., proxy back-fill before ETF inception). Indicators (M, V, C, T) programmed on Metastock;
ranking in Excel. USD portfolio of ETFs. Daily and monthly returns used. Simulation Aug 2003 - Feb 2019. No transaction costs.
TIMING: "The current allocation of the Portfolio is determined by the Ranking Model of the previous month. The Ranking Model in the
last session of the current month determines the allocation of the following month." -> compute ranking at last trading day of month
(close), hold from the start of next month through its end.
Benchmark: Salient Risk Parity Index (10% vol target). Fig 11 / Table 10 comparison Jan 2004-Feb 2019.
p15: Table 10 stats (not copied). p16: Fig 12 AAAM allocation over time. VII Conclusion: factors Momentum, Correlations, Volatility, Trend
determine profitability, diversification, risk, directionality. p17 References: Keller & van Putten 2012 FAA (SSRN); Keller & Butler 2014 EAA;
Faber 2007; Wilder 1978; Bollerslev 1986/87; Engle 1982.
p18-19: Endnotes + figure/table lists. Figure list has NO Figure 4 (numbering skips 3->5). ALL 19 PAGES COVERED.
GAPS THE PAPER LEAVES OPEN (for DECISIONS/REQUESTS):
- numeric factor weights wM, wV, wC, wT never given (FAA convention: wR=1, wV=0.5, wC=0.5; wT unknown).
- rank direction (1=best or 11=best) never stated; FAA convention: rank 1 = best, lowest score selected.
- Volatility model "edited GARCH" unspecified (lookback, spec). arch 8.0.0 is available in the QC environment.
- T system output values/state machine unspecified; Lower Band printed as "+ Highest Low of 105" (probable sign typo).
- momentum definition "4 months ROC": ROC over ~84 trading days (4*21) or 4 calendar months of month-end closes.
- correlation: 4-month average pairwise correlation of DAILY returns (paper: daily and monthly returns used).
- data back-fill before ETF inception (Yahoo + interpolation); platform: start when all 18 ETFs exist (UUP n/a; FXF/FXY Feb 2007).
- FXF/FXY tickers swapped in Tables 5 and 8 (name vs ticker). Use both anyway (both are in the 7).
DATA AVAILABILITY (notebook cells 0-1): FXF 2006-06-26, FXY 2007-02-13 (latest), SH 2006-06-21, GLD 2004-11-18, IEF/TLT/SHY 2002-07-26,
VOX 2004-09-29, RWR 2001-04-27, XL* 1998-12-22. With 252-day GARCH window first ranking 2008-02-29 -> backtest start 2008-02-15.
=== 2018 PAPER: Giordano, "Ranked Asset Allocation Model", Charles H. Dow Award 2018 (24 pages) ===
p1-3: bio + abstract only. Author student at Modena, analyst at MRM Milan, CFTe. Abstract: passive-portfolio as base for active
non-discretionary quant strategy that outperforms. No method details yet.
p4-6: Intro on passive vs active; base = 7Twelve Portfolio (Israelsen), 12 ETFs from 7 asset classes; selection/weighting by revised
FAA (Keller & van Putten) with new factors + proprietary indicators. Influences: Engle/Bollerslev (GARCH), Maillard/Roncalli/Teiletche
(risk parity), Welles Wilder (ATR breakout/trend). Figures 2-4: VIX charts.
PILLARS (p6, verbatim-ish):
- (M) Absolute Momentum: "4 months momentum on daily returns".
- Rank(M): rank assets according to the monthly Absolute Momentum values in ASCENDING order.
- (V) Volatility Model: "edited version of GARCH Model".
- Rank(V): rank assets according to the monthly Volatility Model values in DESCENDING order.
(=> presumably higher rank number = better: highest M gets highest rank, lowest V gets highest rank; check next pages)
p7-9: (C) Average Relative Correlation Momentum: "4 months relative average correlations on assets' daily returns".
Rank(C): rank according to monthly Average Relative Correlation values in DESCENDING order.
(T) ATR Trend/Breakout System: ATR Bands on daily timeframe. Upper Band = 42 periods ATR + Highest Close of 63 periods.
Lower Band = 42 periods ATR + Highest Low of 105 periods. (same wording as 2019 paper; client says keep printed sign.)
(R) Ranking Model: formula in its paragraph. Backtest Jul 2004 - Nov 2017, monthly.
7Twelve portfolio (Israelsen 2008): 12 ETFs VV, IJH, IJR, EFA, EEM, RWR, DBC, VAW, AGG, TIP, IGOV, SHY at 8.33% each (Table 1).
p10-12 (KEY): III. VOLATILITY MODEL: "modified version of GARCH"; "optimizes the GARCH model using the RiskMetrics database of
J.P. Morgan, through daily variance estimations (lambda=0.94, footnote 3)". Uses OHLC daily data. Fig 9: "Volatility Model and Smoothed
Volatility Model". Ranking section: "A 10-day smoothed variant will be used. The algorithm is calculated on daily OHLC data."
=> V = RiskMetrics EWMA variance (lambda 0.94) of daily returns, 10-day smoothed (simple MA of the vol over 10 days).
IV. ATR TREND/BREAKOUT SYSTEM: "The model varies in the session following the one in which the signal occurred: if a given day's HIGH
is higher than the Upper Band, the following day the model will go Long (=2); on the opposite, if a given day's LOW is lower than the
Lower Band, the following day the model will go Neutral/Short (=-2)." Lower Band: "consisting of market sessions highs, is summed up
and not subtracted from volatility (42 periods ATR)" -> Lower = HighestLow(105) + ATR(42) confirmed intentional; higher vol -> more
responsive. T is a contribution factor, not an entry/exit rule. T values: +2 (Long), -2 (Neutral/Short); persistent state.
V. RANKING MODEL components: (M) 4 months ROC on daily returns; (V) Volatility Model 10-day smoothed on daily OHLC; (C) 4 months
average correlation across the ETFs on daily returns (Varadi); (T) ATR system.
"Although the algorithms application is daily, classification is done on a monthly basis, taking the last value of the month."
Each asset EXCEPT Cash (SHY) is ranked 1 to 11 (11 non-cash ETFs). "ETFs are ranked 1..11 according to monthly Absolute Momentum
values in ascending order. This means that, greater the momentum of an asset is, greater are the profitability and the rank."
=> Rank(M): highest M -> rank 11 (best = HIGHEST rank number).
p13-15: Rank(V): ranked 1..11 by monthly Volatility Model values in descending order: "the lower the volatility, the higher its
ranking" (lowest V -> rank 11). Rank(C): 1..11 by monthly Average Relative Correlation in descending order: "the lower the average
correlation, the higher its ranking" (lowest C -> rank 11). => In ALL three, rank 11 = best; HIGHEST Total Rank = best.
Figures 11-16: factor values and ranks Jan 2008 - Feb 2009 (no numbers extracted). Total Rank computed from A(M), V, C ranks.
p16-18 (KEY): TOTAL RANK = (wM*Rank(M) + wV*Rank(V) + wC*Rank(C) - T) + M/x [NOTE: -T with NO weight wT in the 2018 formula]
Rank(M/V/C) = ranking 1..11; T = ATR Trend/Breakout System (values +2 / -2 per p10-12); wM, wV, wC = "% weight assigned" (values NOT
disclosed); x = "value assigned to the Absolute Momentum to avoid equal ranks" (tie-break; 2019 paper uses n=11).
SELECTION: "Only the 5 ETFs with the LOWEST Total Rank will be taken in consideration for the upcoming allocation." Each with positive
M included, else its weight replaced with Cash (SHY); all 5 negative -> 100% Cash. (=> 20% slots).
!! INTERNAL CONTRADICTION: p12-13 say higher rank number = better (rank 11 = highest M / lowest V / lowest C) but selection takes the
LOWEST Total Rank; and -T (T=+2 Long) lowers the total for uptrending assets, consistent with LOWEST = best. The -T term and the
explicit selection rule are consistent with each other; only the rank-direction prose contradicts them.
VI. APPLICATION: Yahoo EOD, interpolations in RStudio for homogeneity; indicators in Metastock, ranking in Excel. Daily and monthly
returns used. Jul 2004 - Nov 2017, no costs. "The Ranking Model in the last session of the current month determines the allocation of
the following month." Compared to Salient Risk Parity Index, 7Twelve, SPY. Table 2 (11/28/2017): VV, IJH, EFA, DBC, VAW 20% each.
RAAM stats: 638.55% total, Sharpe 1.94, ann. std 8.5%.
p19-21: Figures 20-21 allocations over time; Conclusion; References (Bollerslev 86/87, Engle 82, Faber 07, Israelsen, Keller & van
Putten 2012 FAA, Wilder 1978, Pring, Lim, Lee, Hubbard). No new method details.
p22-24: Endnotes: 3 = Zangari, RiskMetrics Technical Document 1996 pp75-100 (EWMA lambda 0.94 daily variance); 4 = Lim 2015 Handbook
of Technical Analysis pp125-171 (volatility bands); 5 = Salient risk parity index. Lists of tables/figures. ALL 24 PAGES COVERED.
(older summary follows) AAAM = SRM + BSHM.
SRM selects best 5 sector ETFs. Each of the 5 with positive M gets 20% weight. Unassigned weight (20% per sector ETF with M<=0)
is replaced with the BSHM allocation. If all 5 have negative M -> BSHM allocation gets 100%.
"""