Overall Statistics
Total Trades
7
Average Win
3.98%
Average Loss
0%
Compounding Annual Return
204731.948%
Drawdown
5.000%
Expectancy
0
Net Profit
95.296%
Sharpe Ratio
3.537
Loss Rate
0%
Win Rate
100%
Profit-Loss Ratio
0
Alpha
9.086
Beta
-212.474
Annual Standard Deviation
1.765
Annual Variance
3.115
Information Ratio
3.53
Tracking Error
1.765
Treynor Ratio
-0.029
Total Fees
$83.27
# Allan Lin - EMA (Py) v0.01
# (Note: This was heavily copied from public)
# (Spend a ton of time working on testing and graphing)

import clr
from clr import AddReference

clr.AddReference("System")
clr.AddReference("QuantConnect.Algorithm")
clr.AddReference("QuantConnect.Indicators")
clr.AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Data import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *

import decimal as d
import numpy as np

class CryptoWarmupMovingAverageCross(QCAlgorithm):
    
    def Initialize(self):
        
        # If Backtesting (Use this data)
        self.SetStartDate(2017, 11, 1) # Set Start Date
        self.SetEndDate(2017, 12, 2) # Set End Date
        self.SetCash(10000) # Set Strategy Cash

        # Default Values
        fast_period = 12
        slow_period = 26
        resolutionTime = Resolution.Hour

        # Set brokerage we are using: GDAX for cryptos
        self.SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash)

        # Avoid Fees? 
        # Ref: https://www.quantconnect.com/forum/discussion/3040/fees-on-limit-orders-on-gdax
        DefaultOrderProperties = GDAXOrderProperties()
        DefaultOrderProperties.PostOnly = True

        # Set crypto to BTC at 1 hour resolution
        self.AddCrypto("BTCUSD", resolutionTime)

        # create a fast exponential moving average at daily resolution
        self.fast = self.EMA("BTCUSD", fast_period, resolutionTime)

        # create a slow exponential moving average at daily resolution
        self.slow = self.EMA("BTCUSD", slow_period, resolutionTime)

        # "slow_period + 1" because rolling window waits for one to fall off the back to be considered ready
        # History method returns a dict with a pandas.DataFrame
        history = self.History(["BTCUSD"], slow_period + 1)
        
        # prints out the tail of the dataframe
        self.Log(str(history.loc["BTCUSD"].tail()))
        self.Log(str(history.loc["BTCUSD"].head()))

        # Populate warmup data
        for index, row in history.loc["BTCUSD"].iterrows():
            self.fast.Update(index, row["close"])
            self.slow.Update(index, row["close"])

        # Log warmup status
        self.Log("FAST {0} READY. Samples: {1}".format("IS" if self.fast.IsReady else "IS NOT", self.fast.Samples))
        self.Log("SLOW {0} READY. Samples: {1}".format("IS" if self.slow.IsReady else "IS NOT", self.slow.Samples))

        # Adding the EMA Plot
        emaPlot = Chart("EMA")
        emaPlot.AddSeries(Series("SLOW", SeriesType.Line, 0))
        emaPlot.AddSeries(Series("FAST", SeriesType.Line, 0))
        emaPlot.AddSeries(Series("CROSS", SeriesType.Scatter, 0))
        emaPlot.AddSeries(Series("PRICE", SeriesType.Line, 0))
        self.AddChart(emaPlot)
         
        usdPlot = Chart("USD")
        usdPlot.AddSeries(Series("BUY", SeriesType.Scatter, 0))
        usdPlot.AddSeries(Series("SELL", SeriesType.Scatter, 0))
        usdPlot.AddSeries(Series("CASH", SeriesType.Line, 0))
        self.AddChart(usdPlot)

        self.previous = None

    def OnData(self, data):

        # wait for our slow ema to fully initialize
        if not self.slow.IsReady:
            return

        # only once per Hour (temporary remove)
        if self.previous is not None and self.Time == self.previous:
            return

        # define a small tolerance on our checks to avoid bouncing
        tolerance = 0.00015

        # Determine number of BTC held
        cash = self.Portfolio.Cash
        price = self.Securities["BTCUSD"].Price
        holdings = self.Portfolio["BTCUSD"].Quantity

        # Log stats
        #self.Log("{0} Holding {1} BTCUSD @ {2}".format(self.Time, holdings, holdings * price))

        # Plot Points
        self.Plot("EMA", "SLOW", self.slow.Current.Value)
        self.Plot("EMA", "FAST", self.fast.Current.Value)
        self.Plot("USD", "PRICE", self.Securities["BTCUSD"].Price)
        self.Plot("USD", "CASH", cash)

        # we only want to go long if we're currently short or flat
        if holdings <= 0.0001:
            # if the fast is greater than the slow, we'll go long
            if self.fast.Current.Value > self.slow.Current.Value * d.Decimal(1 + tolerance):
                self.Log("BUY  >> {0}".format(price))
                self.Plot("USD", "BUY", price)
                self.Plot("EMA", "CROSS", price)
                
                # Market Buys = Fees.
                #self.SetHoldings("BTCUSD", 1)
                 
                # Trying to Cut Cost (by doing a limit order)
                canBuyNumCoins = float(cash) / float(price)
                if (canBuyNumCoins > 0.0001):
                    self.LimitOrder("BTCUSD", canBuyNumCoins, price, "buy")
    
        # we only want to liquidate if we're currently long
        # if the fast is less than the slow we'll liquidate our long
        if holdings > 0.0001 and self.fast.Current.Value < self.slow.Current.Value:
            self.Log("SELL >> {0}".format(price))
            self.Plot("USD", "SELL", price)
            self.Plot("EMA", "CROSS", price)
            
            # We won't use market sells (for same reason).
            self.SetHoldings("BTCUSD", 0)
            #self.Sell("BTCUSD", holdings)
            #self.Order("BTCUSD", holdings, OrderType.StopMarket)
            #self.StopLimitOrder("BTCUSD", holdings, price, "sell")

        self.previous = self.Time