| 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