Quant Buffet放轻松,别过度思虑

过滤后的短期反转

登录后收藏

学术论文

Filtered Market Statistics and Technical Trading Rules

作者过滤后的市场统计数据和技术交易规则 [点击查看论文]

机构
  • MUFlex (Mauritius)
  • ?Flexible Plan Investments, Ltd

策略概要

该策略基于连续的每日收益或损失(“运行”)交易标准普尔500指数工具(交易所交易基金(ETF)、差价合约(CFD)或期货)。在连续两天下跌(包括当天)后,投资者在收盘时做多,在连续两天上涨后,投资者转为空头。动态过滤器通过排除绝对回报低于标准普尔500指数(SPX)每日回报的60天滚动标准差的20%的交易日来消除噪音。此过滤器每日更新,以优化信号计算并提高交易准确性。头寸根据过滤后的“运行”动态调整,有效捕捉短期市场趋势。

II. 策略合理性

回测表现

波动率18.6%
夏普比率0.31
索提诺比率-0.03
胜率49%

完整 Python 代码

import numpy as np
from AlgorithmImports import *
class FilteredShortTermReversal(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2000, 1, 1)
self.SetCash(100000)
self.symbol: Symbol = self.AddEquity("SPY", Resolution.Minute).Symbol
# Setup consolidator.
self.spy_onsolidator = TradeBarConsolidator(timedelta(days=1))
self.spy_onsolidator.DataConsolidated += self.DailyData
self.SubscriptionManager.AddConsolidator(self.symbol, self.spy_onsolidator)
# SPY closes.
self.period: int = 61
self.data: RollingWindow = RollingWindow[float](self.period)
# Warmup.
history: DataFrame = self.History(self.symbol, self.period, Resolution.Daily)
if not history.empty:
    closes = history.loc[self.symbol].close
    for time, close in closes.items():
        self.data.Add(close)

self.run_days: RollingWindow = RollingWindow[float](2)

# On daily data.
def DailyData(self, sender, consolidated) -> None:
self.data.Add(consolidated.Close)

if self.data.IsReady:
    closes: np.ndarray = np.array([x for x in self.data])
    return_data: np.ndarray = closes[:-1] / closes[1:] - 1
    ret: float = return_data[0]
    
    if ret >= 0:
        self.run_days.Add(1)
    else:
        self.run_days.Add(0)
    
    if len(return_data) == self.period - 1:
        if self.run_days.IsReady:
            mean: float = np.mean(return_data)
            ret_std: float = np.std(return_data)
            
            run_days: List[float] = [x for x in self.run_days]
            
            # Positive run
            if sum(run_days) == 2:
                if ret >= mean + ret_std:
                    if self.Portfolio[self.symbol].IsLong:
                        self.Liquidate()
                    self.SetHoldings(self.symbol, -1)
                else:
                    self.Liquidate()
            # Negative run
            elif sum(run_days) == 0: 
                if ret <= mean - ret_std:
                    if self.Portfolio[self.symbol].IsShort:
                        self.Liquidate()
                    self.SetHoldings(self.symbol, 1)
                else: 
                    self.Liquidate()
            else:
                self.Liquidate()