Overall Statistics |
Total Trades 136 Average Win 17.59% Average Loss -6.66% Compounding Annual Return 55.332% Drawdown 64.500% Expectancy 0.465 Net Profit 435.413% Sharpe Ratio 1.053 Probabilistic Sharpe Ratio 35.014% Loss Rate 60% Win Rate 40% Profit-Loss Ratio 2.64 Alpha 0.608 Beta -0.397 Annual Standard Deviation 0.527 Annual Variance 0.278 Information Ratio 0.734 Tracking Error 0.577 Treynor Ratio -1.398 Total Fees $845.26 Estimated Strategy Capacity $4600000.00 Lowest Capacity Asset TQQQ UK280CGTCB51 |
class MACDTrendAlgorithm(QCAlgorithm): stopMarketTicket = None stopMarketOrderFillTime = datetime.min stopLoss = 0 state = 0 def Initialize(self): #algorith basics setup self.SetStartDate(2018, 1, 1) self.SetEndDate(2021, 10, 22) self.SetCash(25000) self.stock = self.AddEquity("TQQQ", Resolution.Minute).Symbol # self.stock.SetDataNormalizationMode(DataNormalizationMode.Raw) self.symbol = self.stock self.fill_quantity = 0 self.stopLoss = None #indicator setup self.macd = self.MACD(self.symbol, 6, 35, 6, MovingAverageType.Exponential, Resolution.Daily) self.PlotIndicator("MACD", True, self.macd, self.macd.Signal) self.PlotIndicator("TQQQ", self.macd.Fast, self.macd.Slow) self.SetWarmUp(5*41, Resolution.Daily) #risk-management setup #self.SetRiskManagement(MaximumDrawdownPercentPerSecurity(0.10)) def OnData(self, data): if self.IsWarmingUp or not self.macd.IsReady: return tolerance = 0.0025 holdings = self.Portfolio[self.symbol].Quantity signalDeltaPercent = (self.macd.Current.Value - self.macd.Signal.Current.Value)/self.macd.Fast.Current.Value #if not invested, but previously was invested and stop loss was triggered, wait until signal flips to take position if holdings <= 0 and signalDeltaPercent > tolerance and self.fill_quantity > 0: #need to add OnOrderEvent.FillQuantity logic to check for stop loss trigger self.SetHoldings(self.symbol, 1.0) self.stopMarketTicket = self.StopMarketOrder(self.stock, -self.Portfolio[self.stock].Quantity, 0.9 * self.Securities[self.stock].Close) elif holdings >= 0 and signalDeltaPercent < -tolerance and self.fill_quantity < 0: #need to add OnOrderEvent.FillQuantity logic to check for stop loss trigger self.SetHoldings(self.symbol, -1.0) self.stopMarketTicket = self.StopMarketOrder(self.stock, self.Portfolio[self.stock].Quantity, 0.9 * self.Securities[self.stock].Close) #if not invested, but previously was invested and stop loss wasn't triggered, flip position elif holdings <= 0 and signalDeltaPercent > tolerance: self.Liquidate() self.SetHoldings(self.symbol, 1.0) self.stopMarketTicket = self.StopMarketOrder(self.stock, -self.Portfolio[self.stock].Quantity, 0.9 * self.Securities[self.stock].Close) elif holdings >= 0 and signalDeltaPercent < -tolerance: self.Liquidate() self.SetHoldings(self.symbol, -1.0) self.stopMarketTicket = self.StopMarketOrder(self.stock, self.Portfolio[self.stock].Quantity, 0.9 * self.Securities[self.stock].Close) #trailing stop loss logic if position is long if self.Portfolio[self.stock].Quantity > 0 and self.stopLoss and self.Securities[self.stock].Close > self.stopLoss: self.stopLoss = self.Securities[self.stock].Close updateFields = UpdateOrderFields() updateFields.StopPrice = self.stopLoss * 0.95 self.stopMarketTicket.Update(updateFields) self.Debug("SPY: " + str(self.stopLoss) + " Stop: " + str(updateFields.StopPrice)) #trailing stop loss logic if position is short elif self.Portfolio[self.stock].Quantity < 0 and self.stopLoss and self.Securities[self.stock].Close < self.stopLoss: self.stopLoss = self.Securities[self.stock].Close updateFields = UpdateOrderFields() updateFields.StopPrice = self.stopLoss * 0.95 self.stopMarketTicket.Update(updateFields) self.Debug("SPY: " + str(self.stopLoss) + " Stop: " + str(updateFields.StopPrice)) def OnOrderEvent(self, orderEvent): if orderEvent.Status != OrderStatus.Filled: return if self.stopMarketTicket is not None and self.stopMarketTicket.OrderId == orderEvent.OrderId: self.stopMarketOrderFillTime = self.Time self.fill_quantity = orderEvent.FillQuantity state = 1 else: state = -1