该策略投资于比特币(BTC)和短期国债(如ETF BIL),通过比特币期货的净空头头寸构建对冲因子。利用线性回归预测加密货币回报率,风险规避投资者在BTC和无风险短期国债之间进行资产配置。每周计算BTC的最优配置权重,并根据预测回报和波动率进行重新平衡。

策略概述

投资领域包括比特币(BTC)和无风险的短期国债(如ETF BIL)。对冲因子𝑏𝑡𝑐𝑁𝑆𝑡的构建方式如公式(3)所示,即通过将比特币期货的净空头头寸除以所有商业交易者的多头和空头头寸总和。

接下来,作者建议使用线性回归(公式(7)),其中加密货币回报率作为因变量,由独立变量(预测因子)𝑏𝑡𝑐𝑁𝑆𝑡(对冲因子)预测。

风险规避的投资者(相对风险规避系数为3)在风险加密货币和无风险短期国债之间进行资产配置。在每周结束时,投资者对BTC的最优资产配置𝑤𝑡计算方式是,将预测回归中的超额回报率𝐶𝑟𝑒𝑡𝑡+1除以3(投资者的风险规避系数),再除以超额回报率的预测方差𝜎_𝑡+1^2。波动率预测是使用过去8周的回报数据生成的移动窗口。投资组合每周重新平衡,权重根据最后一个公式计算(1-𝑤𝑡部分投资于现金)。

策略合理性

相关文献要求在资产定价模型中同时考虑风险厌恶变化和经济不确定性。然而,Dunbar和Owusu-Amoako(2022)表明,期货市场中代理人的时变定位对预测加密货币回报更有用,尤其是因为风险规避的代理人购买保险。研究结果清楚地表明,对冲因子𝑏𝑡𝑐_𝑁𝑆相对于加密货币不确定性指标和标准的商业周期风险因子(如波动指数VIX或芝加哥联储的国家金融状况指数)具有更强的预测能力。

论文来源

Predictability of Crypto Returns: A Habit-Based Explanation of the Risk Premium [点击浏览原文]

<摘要>

我们评估了期货市场参与者的对冲决策在通过风险溢价渠道影响风险厌恶的基础上预测加密货币回报变化的能力。研究表明,对冲因子对风险厌恶和金融市场不确定性指标有显著影响。值得注意的是,对冲因子对风险厌恶的影响相较于不确定性指标更为显著。样本外证据还表明,对冲因子具有显著的回报预测能力。此外,我们的研究表明,在“正常”时期,对冲因子带来了适度但显著的收益,而在严重宏观经济压力时期,对冲因子带来了显著且出色的收益。

回测表现

年化收益率4.68%
波动率N/A
Beta0.124
夏普比率N/A
索提诺比率0.48
最大回撤N/A
胜率44%

完整python代码

from AlgorithmImports import *
import numpy as np
from data_tools import CommitmentsOfTraders, CryptoCotPair
from typing import Tuple, List
import statsmodels.api as sm
#endregion

class HedgingFactorinCryptocurrencies(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2015, 1, 1)
        self.SetCash(1000000)

        self.leverage:int = 5

        # subscribe crypto and COT data
        data:Crypto = self.AddCrypto('BTCUSD', Resolution.Minute, Market.Bitfinex)
        data.SetLeverage(self.leverage)
        crypto_symbol:Symbol = data.Symbol

        cot_symbol:Symbol = self.AddData(CommitmentsOfTraders, 'QBT', Resolution.Daily).Symbol

        data:Equity = self.AddEquity('BIL', Resolution.Minute)
        data.SetLeverage(self.leverage)
        self.t_bill:Symbol = data.Symbol

        self.risk_aversion:float = 3.
        self.allocation_limits:List[float] = [-0.5, 1.5]
        
        # weekly hedging factor data
        hedging_factor_w_period:int = 8
        price_w_period:int = hedging_factor_w_period + 1
        self.SetWarmup(max(price_w_period, hedging_factor_w_period) * 7, Resolution.Daily)

        max_missing_crypto_days:int = 5
        max_missing_cot_days:int = 7

        self.crypto_cot_pair:CryptoCotPair = CryptoCotPair(crypto_symbol,               \
                                                            cot_symbol,                 \
                                                            price_w_period,             \
                                                            hedging_factor_w_period,    \
                                                            max_missing_crypto_days,    \
                                                            max_missing_cot_days
                                                            )

    def OnData(self, data:Slice) -> None:
        # COT data is present in the algo
        if self.crypto_cot_pair._cot_symbol in data and data[self.crypto_cot_pair._cot_symbol] and \
            self.crypto_cot_pair._crypto_symbol in data and data[self.crypto_cot_pair._crypto_symbol]:

            # update weekly COT data
            cot_data = data[self.crypto_cot_pair._cot_symbol]
            comm_hedgers_interest:float = cot_data.GetProperty("COMMERCIAL_HEDGER_LONG") + cot_data.GetProperty("COMMERCIAL_HEDGER_SHORT")
            if comm_hedgers_interest != 0:
                hedging_factor_value:float = cot_data.GetProperty("COMMERCIAL_HEDGER_SHORT") / comm_hedgers_interest

                price:float = data[self.crypto_cot_pair._crypto_symbol].Value

                self.crypto_cot_pair.update_data(price, hedging_factor_value)

                if self.crypto_cot_pair.is_ready():
                    x:Tuple[np.ndarray, np.ndarray] = self.crypto_cot_pair.get_regression_data()
                    model = self.multiple_linear_regression(x[1][:-1], x[0][1:])
                    forecast_return:float = model.predict([1, x[1][-1]])[0]
                    forecast_variance:float = np.std(x[0]) ** 2 # * np.sqrt(52)
                    w_t:float = (1. / self.risk_aversion) * (forecast_return / forecast_variance)
                    w_t = min(max(w_t, self.allocation_limits[0]), self.allocation_limits[1])
                    t_bill_w:float = 1. - w_t

                    self.SetHoldings(self.crypto_cot_pair._crypto_symbol, w_t)
                    self.SetHoldings(self.t_bill, t_bill_w)
        else:
            # COT data is still comming in
            if not self.crypto_cot_pair.cot_updated(self):
                self.crypto_cot_pair.reset_data()
                self.Liquidate()
    
    def multiple_linear_regression(self, x:np.ndarray, y:np.ndarray):
        x = np.array(x).T
        x = sm.add_constant(x)
        result = sm.OLS(endog=y, exog=x).fit()
        return result

Leave a Reply

Discover more from Quant Buffet

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

Continue reading