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