Quant Buffet放轻松,别过度思虑

多资产市场广度动量策略

登录后收藏

学术论文

Protective Asset Allocation (PAA): A Simple Momentum-Based Alternative for Term Deposits

作者保护性资产配置(PAA);一种基于简单动量的定期存款替代方案 [点击查看论文]

机构
  • NLVrije Universiteit Amsterdam
  • ?VU University Amsterdam
  • ?TrendXplorer

策略概要

该策略涉及12种跨不同资产类别的ETF:美国股票(SPY、QQQ、IWM)、全球股票(VGK、EWJ)、新兴市场(EEM)、另类资产(GSG、GLD、IYR)和债券(HYG、LQD、TLT、IEF)。资产基于12个月的动量指标(MOM > 0)进行选择。投资者使用保护因子2计算债券比例(BF)。选择动量为正的前6名资产作为风险投资组合。风险资产的比例计算为(1-BF)/BF,投资组合中的资产等权重。

II. 策略合理性

该策略在熊市期间提供比传统双重动量策略更高的保护,因为它倾向于将更高比例的资金转移到低风险资产中。因此,即使PAA策略在原始回报方面表现不如传统双重动量策略,它也往往能实现更好的风险特征,即显著更低的跌幅和标准差。

回测表现

波动率7.9%
夏普比率1
索提诺比率0.28
最大回撤-8.8%
胜率66%

完整 Python 代码

from AlgorithmImports import *
from collections import deque
class MultiAssetMarketBreadthMomentum(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2008, 1, 1)
self.SetCash(100_000)

self.symbols: List[str] = [
    'SPY', 'QQQ', 'IWM', 'VGK', 'EWJ', 'EEM', 'GSG', 'GLD', 'IYR', 'HYG', 'LQD', 'TLT'
]
self.safe_bond: str = 'IEF'

period: int = 12 * 21
self.data: Dict[str, deque] = {}
self.sma: Dict[str, SimpleMovingAverage]  = {}

for symbol in self.symbols:
    self.AddEquity(symbol, Resolution.Daily)
    self.data[symbol] = deque(maxlen = period)
    self.sma[symbol] = self.SMA(symbol, period, Resolution.Daily)
        
    history: DataFrame = self.History(self.Symbol(symbol), period, Resolution.Daily)
    if not history.empty:
        closes: Series = history.loc[symbol].close
        for time, close in closes.items():
            self.sma[symbol].Update(time, close)

self.AddEquity(self.safe_bond, Resolution.Daily)
self.last_month: int = -1

def OnData(self, slice: Slice) -> None:
if self.last_month == self.Time.month:
    return
self.last_month = self.Time.month

mom: Dict[str, float] = {}
for symbol in self.symbols:
    symbol_obj: Symbol = self.Symbol(symbol)
    # SMA data is ready.
    if self.sma[symbol].IsReady:
        if symbol_obj in slice.Bars:
            price: float = slice.Bars[symbol_obj].Value
            if price != 0:
                mom[symbol] = price / self.sma[symbol].Current.Value - 1
    else:
        return  # Wait for every asset.
        
if len(mom) != 0:
    # Bond fraction calc.
    good_assets: List = sorted([x[0] for x in mom.items() if x[1] > 0], key = lambda x: x[1], reverse = True)[:6]
    N: int = len(self.symbols)
    n: int = len(good_assets)
    a: int = 2
    n1: float = a*N/4
    BF: float = (N-n)/(N-n1)
    
    bond_share: float = (1-BF)/BF
    # Trade execution.
    self.Liquidate()
    
    # Risky part.
    # "leverage" ratio in case there's two parts of portfolio - risky as well as safe part.
    ratio: float = 0.5 if bond_share != 0 else 1
    
    for symbol in good_assets:
        self.SetHoldings(symbol, ratio * (1/n))
    
    # Bond part.
    self.SetHoldings(self.safe_bond, ratio * bond_share)