该投资策略针对与国家股指挂钩的ETF,选择动量表现最强的前5个国家,以实现最佳投资回报。投资组合每月重新平衡,以保持与动量趋势的匹配。策略旨在利用动量效应,通过动态调整捕捉市场表现的变化。

策略概述

该投资策略针对与国家股指挂钩的ETF。根据投资者的偏好和研究,选择10-12个月动量表现最强的国家可以获得最佳结果。选择动量表现最佳的前5个国家纳入投资组合,并每月重新平衡,以保持与这些动量趋势的匹配。该策略旨在利用动量效应,通过动态调整来捕捉市场表现的变化。

策略合理性

学术共识支持动量异常的有效性,将其持久性归因于行为偏差,如羊群效应、过度反应和确认偏差。Bhorjaj和Swaminathan关于国际股指的研究表明,强劲的动量受到宏观经济新闻反应错误的影响,而不是企业盈利,这支持了行为理论的观点。Andreu、Swinkels和Tjong-A-Tjoe的进一步证据表明,通过交易所交易基金(ETF)利用国家和行业动量,每年可获得约5%的超额回报。他们的研究结果表明,使用ETF进行动量策略是可行的,尤其对那些无法直接交易个股的投资者来说,这为全球市场中动量效应的存在和可利用性提供了验证。

论文来源

Momentum effects in country equity indexes [点击浏览原文]

<摘要>

本文研究了组成MSCI全球指数的70个国家指数,作为全球股票投资机会的代表集,并研究了其中的动量和均值回归效应。我们表明,特别是在短期动量方面,存在持久且显著的效应。持有前11个月表现最佳的4个MSCI国家指数组合并持有一个月的策略,在1970年至2009年的39年期间,持续地比等权重基准每年高出约10%。

回测表现

年化收益率17.7%
波动率N/A
Beta0.91
夏普比率0.12
索提诺比率0.13
最大回撤65.4%
胜率62%

完整python代码

from AlgoLib import *

class GlobalIndexMomentumStrategy(XXX):
    '''
    This strategy implements a global index momentum-based trading strategy.
    It invests in the top-performing country equity indexes based on their
    momentum, which is calculated using the Rate of Change (ROC) indicator
    over a predefined analysis period.
    '''

    def Initialize(self):
        '''
        Initializes the trading algorithm settings, including the start date,
        initial cash, list of index symbols to be tracked, and the setup for
        the Rate of Change (ROC) indicator for each symbol.
        '''
        self.SetStartDate(2000, 1, 1)  # Set the start date of the algorithm.
        self.SetCash(100000)  # Set the initial cash for the portfolio.

        # Initialize performance tracking dictionary.
        self.performance = {}
        
        # Analysis period set for 6 months, approximated by trading days.
        self.analysis_period = 6 * 21
        self.SetWarmUp(self.analysis_period, Resolution.Daily)  # Warm-up period for indicators.
        
        # List of ETFs representing various country equity indexes.
        self.index_symbols = [
            "EWA",  # Australia
            "EWO",  # Austria
            "EWK",  # Belgium
            "EWZ",  # Brazil
            "EWC",  # Canada
            "FXI",  # China
            "EWQ",  # France
            "EWG",  # Germany
            "EWH",  # Hong Kong
            "EWI",  # Italy
            "EWJ",  # Japan
            "EWM",  # Malaysia
            "EWW",  # Mexico
            "EWN",  # Netherlands
            "EWS",  # Singapore
            "EZA",  # South Africa
            "EWY",  # South Korea
            "EWP",  # Spain
            "EWD",  # Sweden
            "EWL",  # Switzerland
            "EWT",  # Taiwan
            "THD",  # Thailand
            "EWU",  # United Kingdom
            "SPY",  # USA
        ]

        self.top_performers_count = 5  # Number of top performers to invest in.

        for symbol in self.index_symbols:
            equity = self.AddEquity(symbol, Resolution.Daily)  # Add equity for tracking.
            equity.SetFeeModel(StandardFeeModel())  # Set the fee model.
            equity.SetLeverage(5)  # Set leverage for each equity.
            
            # Initialize and track the Rate of Change (ROC) indicator for each symbol.
            self.performance[symbol] = self.ROC(symbol, self.analysis_period, Resolution.Daily)
            
        self.last_month_processed = -1  # Track the last processed month.
    
    def OnData(self, data: Slice):
        '''
        This method is called at the start of each trading day. It checks for
        the top-performing indexes based on momentum and adjusts the portfolio
        accordingly by liquidating positions not in the top performers and
        investing in the new top performers.
        '''
        
        if self.IsWarmingUp:
            return  # Do nothing if the algorithm is still warming up.

        # Execute trades at the start of the trading day after warm-up.
        if self.Time.hour == 9 and self.Time.minute == 30:
            if self.Time.month == self.last_month_processed:
                return  # Do nothing if we have already processed this month.
            
            self.last_month_processed = self.Time.month  # Update the last processed month.
            
            # Sort symbols by their momentum in descending order.
            momentum_sorted = sorted([x for x in self.performance.items() if x[1].IsReady and x[0] in data], key=lambda x: x[1].Current.Value, reverse=True)
            to_invest = []

            # Select the top performers to invest in.
            if len(momentum_sorted) >= self.top_performers_count:
                to_invest = [x[0] for x in momentum_sorted[:self.top_performers_count]]
                
            # Adjust portfolio based on the selected top performers.
            for symbol in self.Portfolio:
                if symbol not in to_invest:
                    self.Liquidate(symbol)  # Liquidate positions not in the top performers.
            
            for symbol in to_invest:
                self.SetHoldings(symbol, 1 / len(to_invest))  # Invest in the top performers evenly.
            
class StandardFeeModel(FeeModel):
    '''
    This class represents a customized fee model where the order fee is calculated
    as a percentage of the transaction amount, with a custom fee rate specified.
    '''

    def GetOrderFee(self, parameters: OrderFeeParameters):
        '''
        Calculates the fee for an order based on the custom fee rate.
        
        :param parameters: Contains details about the order for which the fee is calculated.
        :return: The calculated fee for the order.
        '''
        
        fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.0001  # Calculate the fee at the custom rate.
        return OrderFee(CashAmount(fee, "USD"))  # Return the calculated fee.

Leave a Reply

Discover more from Quant Buffet

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

Continue reading