“该策略根据最后30分钟的交易情况,交易流动性债券期货。回报为正时建立多头头寸,回报为负时建立空头头寸,并持有头寸直至市场收盘。”

I. 策略概要

该投资范围包括来自北美、欧洲和亚洲/澳大利亚发达市场的16种流动性最强的债券期货。该策略涉及在交易最后30分钟内回报为正的期货中建立多头头寸,回报为负的期货中建立空头头寸。头寸持有至交易日结束。期货合约单独评估并等权重。该策略使用流动性最强的合约,通常是最近交割的合约,并在其交易量超过当前合约时展期到下一个合约。该策略每日重新平衡,更多细节请参见原始论文的附录。

II. 策略合理性

日内交易策略基于以下证据:日内早些时候的大幅价格变动预示着随后的回报,这与日内对冲活动相符。支持这一点的主要原因有四个:1)交易成本使得在价格变动期间部分对冲最优,之后需要额外对冲;2)临近市场收盘时流动性增加,提高了交易执行效率;3)对冲通常在收盘时完成,以避免隔夜风险;4)隔夜持仓会产生更高的成本。此外,不频繁的投资组合再平衡和迟到的知情交易可能有助于动量,因为机构投资者通常在交易日的开始和结束时进行再平衡,从而产生动量。对于股票期货,最大的交易量发生在常规市场交易时间,而对于其他资产类别,交易量在开盘和收盘时飙升,使得这些时期成为理想的交易时机。

III. 来源论文

Low-Risk Effect: Hedging demand and market intraday momentum [点击查看论文]

<摘要>

为对冲空头伽马敞口,需要顺应价格变动方向进行交易,从而产生价格动量。我们通过对1974年至2020年间股票、债券、商品和货币等60多种期货的日内回报进行研究,发现各地都存在强劲的“市场日内动量”。市场收盘前30分钟的回报与当天其余时间(从上次市场收盘到最后30分钟)的回报呈正相关。这种预测能力在经济和统计上都非常显著,并在接下来的几天内反转。我们提供了新的证据,将市场日内动量与期权和杠杆ETF做市商等市场参与者的伽马对冲需求联系起来。

IV. 回测表现

年化回报2.16%
波动率1.33%
β值0
夏普比率1.62
索提诺比率N/A
最大回撤N/A
胜率6%

V. 完整的 Python 代码

from datetime import time
EXPIRY_MIN_DAYS = 90
EXPIRY_LIQUIDATE_DAYS = 5
class IntradayMomentuminFixedIncome(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2008, 1, 1)
        self.SetCash(100000)
        self.symbols = [
                        Futures.Financials.Y30TreasuryBond,
                        Futures.Financials.Y10TreasuryNote,
                        Futures.Financials.Y5TreasuryNote,
                        Futures.Financials.Y2TreasuryNote,
                        ]
                    
        self.close_price = {}
        self.active_future = {}
        
        for symbol in self.symbols:
            future = self.AddFuture(symbol)
            future.SetFilter(EXPIRY_LIQUIDATE_DAYS, EXPIRY_MIN_DAYS)
            self.active_future[symbol] = None
        
        self.Schedule.On(self.DateRules.EveryDay(future.Symbol), self.TimeRules.BeforeMarketClose(future.Symbol, 1), self.Close)
        self.Schedule.On(self.DateRules.EveryDay(future.Symbol), self.TimeRules.BeforeMarketClose(future.Symbol, 30), self.Purchase)
    
    def OnData(self, slice):
        for chain in slice.FutureChains:
            contracts = [contract for contract in chain.Value]
            
            sym = chain.Value.Symbol.Value
            if len(contracts) == 0:
                self.active_future[sym] = None
                continue
            
            contract = sorted(contracts, key=lambda k : k.OpenInterest, reverse=True)[0]
            if sym in self.active_future and self.active_future[sym] is not None and contract.Symbol == self.active_future[sym].Symbol:
                continue
            
            self.active_future[sym] = contract
            self.Securities[contract.Symbol].SetFeeModel(CustomFeeModel(self))
            
    def Purchase(self):
        if len(self.close_price) == 0: return
        
        performance_until_purchase = {}
        for symbol, close_price in self.close_price.items():
            if symbol in self.active_future:
                current_price = (self.active_future[symbol].AskPrice + self.active_future[symbol].BidPrice) / 2 if self.active_future[symbol].LastPrice == 0 else self.active_future[symbol].LastPrice
                sym = self.active_future[symbol].Symbol
                
                if current_price != 0 and close_price != 0:
                    performance_until_purchase[sym] = current_price / close_price - 1
                
        self.close_price.clear()
        
        if len(performance_until_purchase) == 0: return
        
        long = [x[0] for x in performance_until_purchase.items() if x[1] > 0]
        short = [x[0] for x in performance_until_purchase.items() if x[1] < 0]
        
        long_count = len(long)
        short_count = len(short)
        
        for symbol in long:
            self.SetHoldings(symbol, 0.1 / long_count)
        for symbol in short:
            self.SetHoldings(symbol, -0.1 / short_count)
        
    def Close(self):
        self.Liquidate()
        
        for symbol, contract in self.active_future.items():
            if contract:
                self.close_price[symbol] = (contract.AskPrice + contract.BidPrice) / 2 if contract.LastPrice == 0 else contract.LastPrice
# Custom fee model.
class CustomFeeModel(FeeModel):
    def GetOrderFee(self, parameters):
        fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0
        return OrderFee(CashAmount(fee, "USD"))

发表评论

了解 Quant Buffet 的更多信息

立即订阅以继续阅读并访问完整档案。

继续阅读