Multi-Asset Momentum and Valuation Strategy Using Adjusted Yield Rankings
Log in to collectAcademic paper
Strategy in a nutshell
Develop an investment universe with diverse asset classes, including stocks, bonds, and cash across various regions. Track each class with ETFs or index funds, applying momentum rankings based on price series and valuation rankings using adjusted yield metrics (E/P for stocks, YTM for bonds, with specific adjustments for more accurate yields). Assets are ranked by 12-month and 1-month momentum and valuation, with respective weights of 25% for momentum measures and 50% for valuation. Strategy involves investing in the top quartile of ranked assets and shorting the bottom quartile to optimize portfolio performance.
Economic rationale
Academic research extensively documents the effectiveness of value and momentum strategies, highlighting their potential to significantly boost portfolio profitability. By applying these strategies across a diverse range of asset classes, rather than restricting them to a single class, the robustness and efficacy of an investment strategy can be further enhanced. This cross-asset class application not only broadens the investment horizon but also introduces a layer of diversification that can mitigate risk and stabilize returns, thereby optimizing the overall investment strategy.
Backtest performance
Full Python code
from AlgorithmImports import *
import data_tools
from typing import List, Tuple, Dict
from QuantConnect.Data.Market import TradeBar
from QuantConnect.Indicators import RollingWindow
class ValueMomentumAcrossAssets(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2010, 1, 1)
self.SetCash(100000)
self.assets = [
('SPY', 'SP500_EARNINGS_YIELD_MONTH', data_tools.QuantpediaMonthlyData, 0, True),
('MDY', 'MID_CAP_PE', data_tools.QuantpediaPERatio, 0, True),
('IYR', 'REITS_DIVIDEND_YIELD', data_tools.QuantpediaPERatio, -2, False),
('EWU', 'United Kingdom', None, 0, True),
('EWJ', 'Japan', None, 0, True),
('EEM', 'EMERGING_MARKET_PE', data_tools.QuantpediaPERatio, -1, True),
('LQD', 'ML/AAAEY', data_tools.QuandlAAAYield, -2, False),
('HYG', 'ML/USTRI', data_tools.QuandlHighYield, -6, False),
('CME_TY1', 'US10YT', data_tools.QuantpediaBondYield, -1, False),
('EUREX_FGBL1', 'DE10YT', data_tools.QuantpediaBondYield, -1, False),
('SGX_JB1', 'JP10YT', data_tools.QuantpediaBondYield, -1, False),
('BIL', 'IR3TIB01USM156N', data_tools.QuantpediaMonthlyData, 0, False)
]
self.SetWarmUp(252)
self.leverage = 5
self.quantile = 4
self.data = {}
for symbol, _, data_type, _, _ in self.assets:
if data_type:
self.AddData(data_type, symbol, Resolution.Daily)
else:
self.AddEquity(symbol, Resolution.Daily).SetLeverage(self.leverage)
self.Settings.MinimumOrderMarginPortfolioPercentage = 0.0
self.last_month = -1
def OnData(self, data):
if self.IsWarmingUp:
return
current_month = self.Time.month
if current_month == self.last_month:
return
self.last_month = current_month
performance_1m = {}
performance_12m = {}
valuation = {}
for symbol, yield_symbol, data_type, yield_adj, reverse_flag in self.assets:
if symbol not in self.data:
self.data[symbol] = RollingWindow[float](252)
if symbol in data:
self.data[symbol].Add(data[symbol].Price)
# Calculate performance and valuation
for symbol in self.data:
if self.data[symbol].IsReady:
price_series = [x for x in self.data[symbol]]
performance_1m[symbol] = price_series[0] / price_series[21] - 1
performance_12m[symbol] = price_series[0] / price_series[-1] - 1
# Add your valuation calculation here based on the asset type
# Implement ranking and portfolio construction logic here
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters: OrderFeeParameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))