| Overall Statistics |
|
Total Trades 445 Average Win 9.55% Average Loss -0.89% Compounding Annual Return 287.538% Drawdown 29.800% Expectancy 1.858 Net Profit 1688.070% Sharpe Ratio 2.337 Loss Rate 76% Win Rate 24% Profit-Loss Ratio 10.75 Alpha -1.089 Beta 153.827 Annual Standard Deviation 0.441 Annual Variance 0.194 Information Ratio 2.306 Tracking Error 0.441 Treynor Ratio 0.007 Total Fees $0.00 |
import numpy as np
import decimal as d
### <summary>
### Basic mean reversion of bitcoin, buy when above sma. Sell when below.
### </summary>
class BTCMeanReversion(QCAlgorithm):
def Initialize(self):
'''Initialise the data and resolution required, as well as the cash and start-end dates for the algorithm'''
self.SetStartDate(2016,01,01) #Set Start Date
self.SetEndDate(2018,02,15) #Set End Date
self.SetCash(100000) #Set Strategy Cash
self.SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash)
self.SetBenchmark("BTCUSD")
self.AddCrypto("BTCUSD", Resolution.Hour)
self.short_sma = self.SMA("BTCUSD", 18, Resolution.Daily)
self.short_ema = self.EMA("BTCUSD", 9, Resolution.Daily)
# 16 * 24
def OnData(self, data):
btcPrice = self.Securities["BTCUSD"].Price
if btcPrice > self.short_sma.Current.Value:
if not self.Portfolio.Invested:
if btcPrice > self.short_ema.Current.Value:
self.SetHoldings("BTCUSD", 1)
elif self.Portfolio.Invested:
if btcPrice < self.short_ema.Current.Value:
self.SetHoldings("BTCUSD", 0)
else:
if self.Portfolio.Invested:
self.SetHoldings("BTCUSD", 0)
# Override SetHoldings to use limit orders (ratio is of totalPortfolioValue.)
def SetHoldings(self, symbol, ratio):
security = self.Securities[symbol]
if not security.IsTradable:
self.Debug("{} is not tradable.".format(symbol))
return # passive fail
ratio = d.Decimal(ratio)
price, quantity = security.Price, security.Holdings.Quantity
# Keep 2% Cash (for the limit order, rounding errors, and safety)
totalPortfolioValue = self.Portfolio.TotalPortfolioValue * d.Decimal(0.98)
# +0.1% Limit Order
# (to make sure it executes quickly and without much loss)
# (if you set the limit large it will act like a market order)
limit = 1.001
desiredQuantity = totalPortfolioValue * ratio / price
orderQuantity = desiredQuantity - quantity
# limit needs to be inverse when selling
limitPrice = price * d.Decimal(limit if orderQuantity >= 0 else 1/limit)
self.Log("Limit Order: {} coins @ ${} per coin".format(orderQuantity, limitPrice))
self.LimitOrder(symbol, orderQuantity, limitPrice)