Mean-reversion and Trend-Following Based on MIN and MAX in BTC
Log in to collectAcademic paper
Seasonality, Trend-following, and Mean reversion in Bitcoin
Matus Padysak; Radovan Vojtko
- SKComenius University Bratislava
- ?Comenius University - Faculty of Mathematics, Physics and Informatics
- ?Quantpedia.com
Strategy in a nutshell
Trade Bitcoin using 10-day price extremes from Gemini data. Go long when the price equals the 10-day maximum or minimum, holding positions for one day.
Economic rationale
Bitcoin shows strong mean-reversion at local minima and trend-following at local maxima. Trading at these extremes captures high-probability moves while reducing risk, yielding better risk-adjusted returns than passive holding.
Backtest performance
Annualised return98.43%
Volatility47.75%
Beta0.394
Sharpe ratio2.06
Sortino ratio1.046
Maximum drawdown-37.67%
Win rate51%
Full Python code
from AlgorithmImports import *
# endregion
class MeanreversionandTrendFollowingBasedonMINandMAXinBTC(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1)
self.SetCash(100000)
# NOTE Coinbase Pro, CoinAPI, and Bitfinex data is all set in UTC Time. This means that when accessing data from this brokerage, all data will be time stamped in UTC Time.
self.crypto:Crypto = self.AddCrypto("BTCUSD", Resolution.Minute, Market.GDAX)
self.crypto.SetLeverage(10)
self.crypto.SetFeeModel(CustomFeeModel())
self.crypto:Symbol = self.crypto.Symbol
self.period:int = 10
self.daily_prices:RollingWindow = RollingWindow[float](self.period)
self.daily_close_hour:int = 22
def OnData(self, data):
if self.crypto in data and data[self.crypto]:
time:datetime.datetime = self.Time
if time.hour == self.daily_close_hour and time.minute == 0:
price:float = data[self.crypto].Value
self.daily_prices.Add(price)
if self.daily_prices.IsReady:
daily_prices:list[float] = [x for x in self.daily_prices]
daily_max:float = np.max(daily_prices)
daily_min:float = np.min(daily_prices)
# open/rebalance long position
if price == daily_max or price == daily_min:
self.SetHoldings(self.crypto, 1)
else:
# close position
if self.Portfolio[self.crypto].Invested:
self.Liquidate(self.crypto)
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))