Overall Statistics |
Total Trades 41 Average Win 46.76% Average Loss -5.46% Compounding Annual Return 276.127% Drawdown 39.500% Expectancy 3.307 Net Profit 1657.579% Sharpe Ratio 2.057 Loss Rate 55% Win Rate 45% Profit-Loss Ratio 8.57 Alpha 0.507 Beta 0.448 Annual Standard Deviation 0.504 Annual Variance 0.254 Information Ratio -0.265 Tracking Error 0.547 Treynor Ratio 2.315 Total Fees $0.00 |
import clr clr.AddReference("System") clr.AddReference("QuantConnect.Algorithm") clr.AddReference("QuantConnect.Indicators") clr.AddReference("QuantConnect.Common") from System import * from QuantConnect import * from QuantConnect.Algorithm import * from QuantConnect.Indicators import * import decimal as d class MovingAverageAlgorithm(QCAlgorithm): def __init__(self): # Market parameters self.previousTime = None self.previousPrice = None self.previousTrigger = None self.buyTicket = None self.sellTicket = None self.stopTicket = None self.symbol = "BTCEUR" self.base = "BTC" self.quote = "EUR" self.market = Market.GDAX self.brokerage = BrokerageName.GDAX self.resolution = Resolution.Daily self.conversionRate = 1.23 # Strategy parameters self.length_fast_ema = 5 self.length_slow_ema = 14 self.initial_stop_percent_delta = 0.1 self.follow_factor = 1 def Initialize(self): '''Initialise algorithm with data, resolution, cash and start-end dates''' self.SetStartDate(2016,1,1) # Set Start Date self.SetEndDate(2018,2,28) # Set End Date self.Portfolio.SetCash(0) # Set USD to 0 since we only have EUR in our account self.Portfolio.SetCash("EUR", 1000, self.conversionRate) # Set EUR strategy cash with static EURUSD conversion rate self.AddCrypto(self.symbol, self.resolution, self.market) # symbol to trade self.SetBrokerageModel(self.brokerage, AccountType.Cash) # crypto brokerage # Indicators self.ema_fast = self.EMA(self.symbol, self.length_fast_ema, self.resolution) # fast moving average self.ema_slow = self.EMA(self.symbol, self.length_slow_ema, self.resolution) # slow moving average # Benchmark is buy & hold of the traded security self.SetBenchmark(self.symbol) # Note - use single quotation marks: ' instead of double " # Chart - Master Container for the Chart: coinPlot = Chart('Strategy Equity') # On the Trade Plotter Chart we want 3 series: trades and price: coinPlot.AddSeries(Series('Benchmark', SeriesType.Line, 0)) coinPlot.AddSeries(Series('Fast EMA', SeriesType.Line, 0)) coinPlot.AddSeries(Series('Slow EMA', SeriesType.Line, 0)) def OnData(self, data): '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. Arguments: data: Slice object keyed by symbol containing the security data ''' # Notice in this method: # 1. We never need to 'update' our indicators with the data, the engine takes care of this for us # 2. We can use indicators directly in math expressions # 3. We can easily plot many indicators at the same time # Wait for indicators to fully initialize if not self.ema_slow.IsReady: return # Allow one trade maximum per day if self.previousTime is not None and self.previousTime.date() == self.Time.date(): return # Retrieve quote asset. CashBook is a dictionary of currencies (including crypto assets) availableQuote = self.Portfolio.CashBook[self.quote].Amount # Retrieve current holdings holdings = self.Portfolio.CashBook[self.base].Amount # Retrieve current price currentPrice = self.Securities[self.symbol].Close ## UPDATE TRAILING STOP LIMIT ORDER ## # Increase trailing stop limit if it exist and if necessary: if self.stopTicket is not None: if currentPrice > self.previousPrice: ratio = currentPrice / self.previousPrice adjustedRatio = (ratio + self.follow_factor) / (1 + self.follow_factor) trigger = decimal.Decimal(self.previousTrigger * adjustedRatio) updateOrderFields = UpdateOrderFields() updateOrderFields.StopPrice = trigger updateOrderFields.LimitPrice = trigger * 0.99 self.stopTicket.Update(updateOrderFields) self.previousTrigger = trigger # Define a small tolerance to avoid bouncing when comparing indicators: tolerance = 0.00015 ## BUY ORDER ## # 1. Go long if we're currently short or flat # Note that we cannot short crypto assets at the moment with GDAX API # 2. If fast ema is greater than slow ema, then go long # 3. Wait for price confirmation: price must exceed a recent high if holdings <= 0: if self.ema_fast.Current.Value > self.ema_slow.Current.Value * d.Decimal(1 + tolerance): limitPrice = currentPrice * d.Decimal(1+0.001) # use all cash on long, 0.999 is here to avoid buying power issues quantity = (availableQuote / limitPrice) * d.Decimal(0.999) lot = self.Securities[self.symbol].SymbolProperties.LotSize roundedQuantity = round(quantity/lot-d.Decimal(0.5))*lot # -0.5 is present to round down the quantity self.buyTicket = self.LimitOrder(self.symbol, roundedQuantity, round(limitPrice, 2)) ## SELL ORDER ## # 1. Liquidate if we're currently long # 2. If fast ema is less than slow ema then liquidate the long if holdings > 0: if self.ema_fast.Current.Value < self.ema_slow.Current.Value: limitPrice = currentPrice * d.Decimal(1-0.001) self.sellTicket = self.LimitOrder(self.symbol, -holdings, round(limitPrice, 2)) # sell entire position # Store in memory the time and the price of the last execution self.previousTime = self.Time self.previousPrice = currentPrice # Plot indicators and benchma self.Plot('Strategy Equity', 'Benchmark', self.Securities[self.symbol].Price) self.Plot('Strategy Equity', 'Fast EMA', self.ema_fast.Current.Value) self.Plot('Strategy Equity', 'Slow EMA', self.ema_slow.Current.Value) def OnOrderEvent(self, event): # Handle filling of buy & sell orders: # Determine if order is the buy or the sell or the stop order = self.Transactions.GetOrderById(event.OrderId) self.Log("{0}: {1}: {2}".format(self.Time, order.Type, event)) ## BUY ORDER FILLED ## if event.OrderId == self.buyTicket.OrderId: self.Debug("Buy ticket event detected") # If buy order is filled, create stop loss if self.buyTicket.Status == OrderStatus.Filled: self.Debug("Buy order filled") quantity = self.buyTicket.Quantity # limit price is set below the trigger to maximise the chances of catching a price decrease trigger = (1 - self.initial_stop_percent_delta) * self.buyTicket.AverageFillPrice limit = trigger * 0.99 self.stopTicket = self.StopLimitOrder(self.symbol, -quantity, stopPrice=trigger, limitPrice=limit) self.previousTrigger = trigger self.buyTicket = None ## SELL ORDER FILLED ## elif event.OrderId == self.sellTicket.OrderId: self.Debug("Sell ticket event detected") # If sell order is filled, cancel stop loss if self.sellTicket.Status == OrderStatus.Filled: self.Debug("Sell order filled") self.stopTicket.Cancel() ## STOP ORDER FILLED ## elif event.OrderId == self.stopTicket.OrderId: self.Debug("Stop ticket event detected") # If stop order is filled, cancel the sell order, if any: if self.stopTicket.Status == OrderStatus.Filled: self.Debug("Stop order filled") self.stopTicket = None if self.sellTicket is not None: self.sellTicket.Cancel() self.sellTicket = None