投资范围包括上证50ETF期从5只ETF(SPY、EFA、BND、VNQ、GSG)中选择3只12个月动量表现最佳的ETF,进行等权重配置,持有一个月后重新平衡。和股票。数据来自万得金融数据库。上市不足一年的股票被排除在外。

策略概述

该投资策略从5只ETF中(SPY代表标普500指数,EFA代表国际发达市场股票,BND代表债券,VNQ代表房地产投资信托基金,GSG代表商品)筛选出12个月动量表现最佳的3只ETF。对这3只ETF进行等权重配置,每只ETF占总投资的三分之一。持有一个月后,根据最新的表现数据再次筛选表现最好的3只ETF,并重新平衡投资组合。该策略通过跟随市场趋势,动态调整投资组合,以优化投资回报。

策略合理性

动量投资被学者认为是产生回报的强大因子,持续受到学术界的关注。该策略的基础在于轮换不同资产类别,这些资产对商业周期有不同的敏感性,旨在选择具有最高回报潜力和最低损失风险的资产。Kessler和Scherer在《宏观动量与经济》中指出,轮换策略的成功源于利用可预测的投资机会变化,为投资者提供合理的回报。现如今,投资者可以通过大量的共同基金、ETF和封闭式基金来实施这种策略,其中许多提供低成本或免佣金的交易选择,使得该策略变得越来越可行。

论文来源

Relative Strength Strategies for Investing [点击浏览原文]

<摘要>

本论文旨在提出简单的定量方法,以提高美国股票板块和全球资产类别投资组合的风险调整回报。通过对French-Fama美国股票板块数据进行测试,发现相对强度模型在绝对回报方面有所增加,且具有类似股票的风险。相对强度投资组合在大约70%的年份中表现优于买入持有基准,回报在时间上具有持续性。加入趋势跟踪参数以动态对冲投资组合,降低了波动性和回撤。相对强度模型在全球资产类别投资组合中也进行了测试,结果同样支持其有效性。

回测表现

年化收益率11.5%
波动率11.0%
Beta0.3
夏普比率0.25
索提诺比率0.23
最大回撤39.8%
胜率88%

完整python代码

from AlgoLib import *

class AssetMomentumStrategy(XXX):
    '''
    This class implements a momentum-based trading strategy, inheriting from a base trading algorithm class.
    It selects a subset of assets based on their momentum and rebalances the portfolio monthly, aiming to hold positions in assets demonstrating the strongest momentum.
    '''
    
    def Initialize(self):
        '''
        Initializes the trading strategy by setting the start date, initial cash, the momentum period for calculating rate of change (ROC), 
        the assets to be traded, and setting up the necessary indicators and parameters for the trading algorithm.
        '''
        
        self.SetStartDate(2000, 1, 1)  # Set the start date of the backtest.
        self.SetCash(100000)  # Set the initial capital for the backtest.
        
        self.momentum_data = {}  # Initialize a dictionary to store Rate of Change (ROC) indicators for each asset.
        momentum_period = 252  # Define the momentum period (typically 252 trading days, equivalent to 12 months).
        self.SetWarmUp(momentum_period, Resolution.Daily)  # Set the warm-up period for the algorithm.
        
        self.assets_to_trade = 3  # Define the number of assets to hold positions in simultaneously.
        
        self.asset_symbols = ["SPY", "EFA", "IEF", "VNQ", "GSG"]  # List of asset symbols to consider for trading.
        
        # Loop through each asset symbol, adding them to the algorithm and initializing their ROC indicators.
        for asset in self.asset_symbols:
            self.AddEquity(asset, Resolution.Minute)  # Add the equity data for the asset.
            self.momentum_data[asset] = self.ROC(asset, momentum_period, Resolution.Daily)  # Initialize and store the ROC indicator for the asset.
        
        self.last_rebalance_month = None  # Variable to track the last month the portfolio was rebalanced.

    def OnData(self, data):
        '''
        The event handler for new data points. This function checks for the right conditions to rebalance the portfolio based on asset momentum.
        '''
        
        if self.IsWarmingUp:  # Check if the algorithm is still in the warm-up period.
            return
        
        # Check if the current time is the market open time (9:30 AM).
        if self.Time.hour != 9 or self.Time.minute != 30:
            return
        
        self.Debug(f"Market open at: {self.Time}")  # Debug log for market opening time.
        
        # Check if it's time to rebalance the portfolio (done monthly).
        current_month = self.Time.month
        if current_month == self.last_rebalance_month:  # If already rebalanced this month, do nothing.
            return
        self.last_rebalance_month = current_month  # Update the last rebalance month.
        
        self.Debug("Initiating monthly rebalance...")  # Debug log for rebalancing.
        
        # Filter the assets that are ready for trading based on their ROC being ready.
        ready_assets = {
            symbol: roc for symbol, roc in self.momentum_data.items() 
            if symbol in data and data[symbol] and roc.IsReady
        }
        
        # Sort the assets by their momentum in descending order.
        assets_sorted_by_momentum = sorted(ready_assets.items(), key=lambda item: item[1].Current.Value, reverse=True)
        self.Debug(f"Assets sorted for trading: {len(assets_sorted_by_momentum)} required: {self.assets_to_trade}")
        
        # Select the top assets for trading based on momentum.
        assets_to_long = [asset for asset, _ in assets_sorted_by_momentum[:self.assets_to_trade]]
        
        # Liquidate positions not in the top assets list.
        for asset in self.Portfolio:
            if asset not in assets_to_long and self.Portfolio[asset].Invested:
                self.Liquidate(asset)
        
        self.Debug(f"Assets selected for long positions: {assets_to_long}")  # Debug log for selected assets.
        
        # Allocate equal weight to each selected asset in the portfolio.
        for asset in assets_to_long:
            self.SetHoldings(asset, 1 / len(assets_to_long))

Leave a Reply

Discover more from Quant Buffet

Subscribe now to keep reading and get access to the full archive.

Continue reading