
The strategy uses Bitcoin’s moving averages (20-day, 1-day, 2-day, 4-day, 10-day) to guide buy or sell decisions. The portfolio is rebalanced daily with 10% actively traded and sub-strategies equally weighted.
ASSET CLASS: cryptos | REGION: Global | FREQUENCY:
Daily | MARKET: cryptos | KEYWORD: Moving Average, Cryptocurrencies
I. STRATEGY IN A NUTSHELL
Trades Bitcoin using multiple moving averages (1-, 2-, 4-, 10-, and 20-day). Buy when price > MA, sell to risk-free when below. Sub-strategies equally weighted, 10% of portfolio actively traded, rebalanced daily.
II. ECONOMIC RATIONALE
Moving averages provide predictive power for Bitcoin prices, reducing drawdowns and enhancing risk-adjusted returns. Combining multiple MA signals improves timing and performance, outperforming macroeconomic predictors.
III. SOURCE PAPER
Bitcoin: Learning and Predictability via Technical Analysis: Evidence from Bitcoin and Stocks with Hard-to- Value Fundamentals [Click to Open PDF]
Detzel, Andrew L., Baylor University – Hankamer School of Business; Liu, Hong, Washington University in St. Louis – Olin Business School; Strauss, Jack, Fudan University – China Institute of Economics and Finance; Zhou, Guofu, University of Denver – Daniels College of Business; Zhu, Yingzi, Washington University in St. Louis – John M. Olin Business School; Tsinghua University – School of Economics & Management
<Abstract>
What predicts returns on assets with “hard-to-value” fundamentals, such as Bitcoin and stocks in new industries? We propose an equilibrium model that shows how rational learning enables return predictability through technical analysis. We document that ratios of prices to their moving averages forecast daily Bitcoin returns in- and out-of sample. Trading strategies based on these ratios generate an economically significant alpha and Sharpe ratio gains relative to a buy-and-hold position. Similar results hold for small-cap, young-firm, and low-analyst-coverage stocks as well as NASDAQ stocks during the dotcom era.


IV. BACKTEST PERFORMANCE
| Annualised Return | 10.03% |
| Volatility | 4.97% |
| Beta | 0.033 |
| Sharpe Ratio | 2.45 |
| Sortino Ratio | 0.571 |
| Maximum Drawdown | -4.01% |
| Win Rate | 51% |
V. FULL PYTHON CODE
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"))
VI. Backtest Performance