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)