创建一个包含10-20种货币的投资池,使用OECD购买力平价(PPP)、每月CPI和汇率进行评估。做多三种被低估的货币,做空三种被高估的货币。每季度重新平衡。

策略概述

建立一个包含10-20种货币的投资组合,使用最新的OECD购买力平价(PPP)来初步确定对美元的公允价值。每月基于CPI和汇率变化调整这些值,以确定上个月的公允PPP值。根据PPP计算结果,投资于三种最被低估的货币,并做空三种最被高估的货币。将未使用的现金分配到隔夜利息账户。每月或每季度重新平衡投资组合,以适应不断变化的市场状况。这一策略旨在利用货币估值差异,通过PPP调整来指导投资决策并最大化回报。

策略合理性

Menkhoff、Sarno、Schmeling和Schrimpf探讨了基于实际汇率的货币估值预测能力。他们发现,这些指标对外汇超额收益和跨货币即期汇率变化具有信息价值。这种可预测性主要源于国家间持续的宏观经济差异,表明货币价值很大程度上反映了相对稳定的风险溢价。与传统观点相反,基于简单的货币估值指标进行交易并不一定具有盈利性,因为会回归基本面价值。深入分析表明,精炼的估值方法与原始货币价值概念更加一致,可以预测超额回报和汇率反转。此外,各国不同的消费篮子允许相对价格水平的评估,揭示“便宜”和“昂贵”的国家。价格差异逐渐演变,通过重新平衡包含被低估和高估货币的投资组合,可以通过汇率回归公允价值实现收益。

论文来源

db Currency Returns [点击浏览原文]

<摘要>

“db货币回报”(DBCR)文档详细介绍了DBCR指数的策略和表现,捕捉全球货币市场中的系统性回报,采用三大策略:套息交易,通过交易基于利率差异的货币来利用远期汇率偏差;动量策略,利用货币的长期趋势;估值策略,通过购买被低估的货币和卖出被高估的货币,随着时间推移回归“公允价值”。DBCR指数提供了这些策略的基准曝光,展示了与传统资产类别的低相关性,突出了其分散投资的好处。该指数将这些策略组合为单一的、非自由裁量的指数,具有每日流动性。1989年至2009年的数据展示了每个策略的回报、波动率和夏普比率,凸显了该指数在投资组合中的实用性。DBCR的变体如DBCR+和DBCR Dynamic引入了调整,以通过扩展的货币范围或基于过去表现的动态分配来潜在增强回报。文档强调了DBCR在提供系统性、分散化的货币市场投资曝光中的作用,这基于数十年的学术研究和市场表现数据。

回测表现

年化收益率7.8%
波动率9.3%
Beta0.045
夏普比率-0.18
索提诺比率-0.21
最大回撤30.4%
胜率52%

完整python代码

# Import the algorithm base and dictionary collection from external libraries.
from AlgoLib import algorithm
from System.Collections.Generic import Dictionary

# Define the class PPPBasedCurrencyStrategy which inherits from an unspecified class XXX.
class PPPBasedCurrencyStrategy(XXX):
    def Initialize(self):
        """Initializes the trading algorithm with starting conditions and data subscriptions."""
        
        # Set the start date of the algorithm to January 1st, 2000.
        self.SetStartDate(2000, 1, 1)
        
        # Set the initial cash to $100,000.
        self.SetCash(100000)

        # Define leverage and the number of symbols to trade.
        self.leverage = 3
        self.symbols_to_trade = 3
        
        # Initialize an empty dictionary to store purchasing power parity data.
        self.purchasing_power_parity = {}

        # Define a mapping between future symbols and their corresponding PPP symbols.
        self.future_to_ppp_mapping = {
            "CME_AD1": "AUS_PPP",
            "CME_BP1": "GBR_PPP",
            "CME_CD1": "CAD_PPP",
            "CME_EC1": "DEU_PPP",
            "CME_JY1": "JPN_PPP",
            "CME_NE1": "NZL_PPP",
            "CME_SF1": "CHE_PPP"
        }

        # Subscribe to futures data and purchasing power parity data for each mapped symbol.
        for future_symbol, ppp_symbol in self.future_to_ppp_mapping.items():
            self.AddFuture(future_symbol, Resolution.Daily, self.leverage)
            self.AddData(custom.PurchasingPowerParity, ppp_symbol, Resolution.Daily)
        
        # Set the minimum order margin as a percentage of the portfolio to 0.0, allowing for higher leverage.
        self.Settings.MinimumOrderMarginPortfolioPercentage = 0.0
        
        # Initialize a variable to keep track of the last month processed.
        self.last_month_processed = None
        
    def OnData(self, data):
        """Handles new data events and decides when to rebalance the portfolio."""
        
        # If the current month is the same as the last processed month, do nothing.
        if self.Time.month == self.last_month_processed:
            return
        
        # Update the PPP data with the latest values.
        self.UpdatePPPData(data)
        
        # Update the last processed month to the current month.
        self.last_month_processed = self.Time.month

        # If it's January, rebalance the portfolio.
        if self.Time.month == 1:
            self.RebalancePortfolio(data)

    def AddFuture(self, symbol, resolution, leverage):
        """Adds a future to the data subscription with specified leverage."""
       
        # Add the future data to the algorithm with specified resolution.
        future = self.AddData(custom.QuantpediaFutures, symbol, resolution)
        
        # Set the leverage for the future.
        future.SetLeverage(leverage)
        
        # Return the future object for further use (if needed).
        return future

    def UpdatePPPData(self, data):
        """Updates the purchasing power parity data for each symbol in the mapping."""
        
        # Iterate through each symbol and its corresponding PPP symbol.
        for symbol, ppp_symbol in self.future_to_ppp_mapping.items():
            
            # If the PPP symbol data is available in the new data, update the PPP data dictionary.
            if ppp_symbol in data:
                self.purchasing_power_parity[symbol] = data[ppp_symbol].Value

    def RebalancePortfolio(self, data):
        """Rebalances the portfolio based on the purchasing power parity data."""
        
        # Check if there's enough PPP data to rebalance the portfolio.
        if len(self.purchasing_power_parity) >= self.symbols_to_trade * 2:
            
            # Sort the symbols by their PPP value.
            sorted_symbols = sorted(self.purchasing_power_parity.items(), key=lambda x: x[1])
            
            # Select symbols for long positions (the ones with highest PPP).
            long_symbols = [symbol for symbol, _ in sorted_symbols[-self.symbols_to_trade:]]
            
            # Select symbols for short positions (the ones with lowest PPP).
            short_symbols = [symbol for symbol, _ in sorted_symbols[:self.symbols_to_trade]]

            # Clear the PPP data for the next rebalance.
            self.purchasing_power_parity.clear()
            
            # Execute the trades based on the selected long and short positions.
            self.ExecuteTrades(long_symbols, short_symbols, data)

    def ExecuteTrades(self, long_symbols, short_symbols, data):
        """Executes the trading strategy by setting holdings based on desired long and short positions."""
        
        # Liquidate positions that are no longer in the selected long or short symbols.
        for symbol in self.Portfolio.keys():
            if symbol not in long_symbols + short_symbols and self.Portfolio[symbol].Invested:
                self.Liquidate(symbol)

        # Set holdings for long positions, equally distributing the investment.
        for symbol in long_symbols:
            if symbol in data:
                self.SetHoldings(symbol, 1 / len(long_symbols))
        
        # Set holdings for short positions, equally distributing the investment.
        for symbol in short_symbols:
            if symbol in data:
                self.SetHoldings(symbol, -1 / len(short_symbols))

Leave a Reply

Discover more from Quant Buffet

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

Continue reading