Quant Buffet放轻松,别过度思虑

加密货币的移动平均策略

登录后收藏

学术论文

Bitcoin: Predictability and Profitability via Technical Analysis

作者Andrew L. Detzel; Hong Liu; Jack Strauss; Guofu Zhou; Yingzi Zhu

机构
  • Baylor University
  • ?Baylor University - Hankamer School of Business
  • Institute of Economics
  • Washington University in St. Louis
  • Fudan University
  • ?Fudan University - China Institute of Economics and Finance
  • ?Washington University in St. Louis - Olin Business School
  • University of Denver
  • ?University of Denver - Daniels College of Business
  • ?Washington University in St. Louis - John M. Olin Business School
  • Tsinghua University
  • ?Tsinghua University - School of Economics & Management

策略概要

投资范围包括比特币,每日价格来自Coindesk.com。该策略使用20天、1天、2天、4天和10天的移动平均线来指导买卖决策。如果比特币在t-1时刻的价格除以移动平均线大于1,投资者买入比特币;如果小于1,投资者卖出并投资于无风险资产。这些子策略等权重,最终投资组合每日重新平衡。只有10%的投资组合进行积极交易。

II. 策略合理性

方差比率测试表明,比特币的价格是可预测的,而不是随机游走。虽然常见的宏观经济变量显示出一些样本内可预测性,但它们缺乏样本外显著性。相比之下,移动平均线比率等技术分析指标在样本内和样本外都显示出有意义的预测能力。结合不同的移动平均线信号可以增强比特币的可预测性。此外,比特币的交易量部分可以用移动平均线信号来解释。值得注意的是,移动平均线指标可以迅速发出退出信号,从而最大限度地减少回撤并提高业绩。在比特币重大危机期间,移动平均线策略有效地减少了回撤,显示了其在风险管理方面的价值。

回测表现

波动率4.97%
夏普比率2.45
索提诺比率0.571
最大回撤-4.01%
胜率51%

完整 Python 代码

from AlgorithmImports import *
class MovingAverageCryptocurrencies(QCAlgorithm):
def Initialize(self):
self.set_start_date(2015, 1, 1)
self.set_cash(100_000)
self.symbol: Symbol = self.add_crypto('BTCUSD', Resolution.DAILY, Market.BITFINEX).symbol
self.securities[self.symbol].set_fee_model(CustomFeeModel())

self.MAs: List[SimpleMovingAverage] = [
    self.SMA(self.symbol, 1, Resolution.Daily),
    self.SMA(self.symbol, 2, Resolution.Daily),
    self.SMA(self.symbol, 4, Resolution.Daily),
    self.SMA(self.symbol, 10, Resolution.Daily),
    self.SMA(self.symbol, 20, Resolution.Daily)
]
    
def OnData(self, slice: Slice) -> None:
if not self.symbol in slice: return
    
price: float = slice[self.symbol].Value
if price == 0: return
long_signal_count: int = 0
for ma in self.MAs:
    if ma.IsReady:
        if price > ma.Current.Value:
            long_signal_count += 1
else:
    self.Liquidate()

w: float = (0.1 / len(self.MAs)) * long_signal_count
self.SetHoldings(self.symbol, w)
        
# Custom fee model
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))