Overall Statistics
Total Trades
385
Average Win
7.21%
Average Loss
-0.89%
Compounding Annual Return
471.983%
Drawdown
23.600%
Expectancy
1.602
Net Profit
974.912%
Sharpe Ratio
2.665
Loss Rate
71%
Win Rate
29%
Profit-Loss Ratio
8.08
Alpha
2.227
Beta
-65.462
Annual Standard Deviation
0.497
Annual Variance
0.247
Information Ratio
2.637
Tracking Error
0.497
Treynor Ratio
-0.02
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,10,07)  #Set Start Date
        self.SetEndDate(2018,02,15)    #Set End Date
        self.SetCash(100000)           #Set Strategy Cash

        self.SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash)
        self.AddCrypto("BTCUSD", Resolution.Hour)
        
        self.short_sma = self.SMA("BTCUSD", 15, Resolution.Daily)
        self.short_ema = self.EMA("BTCUSD", 5, Resolution.Daily)

    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)