Overall Statistics
Total Trades
93
Average Win
4.13%
Average Loss
-2.95%
Compounding Annual Return
11.515%
Drawdown
16.400%
Expectancy
0.670
Net Profit
152.572%
Sharpe Ratio
0.901
Probabilistic Sharpe Ratio
32.062%
Loss Rate
30%
Win Rate
70%
Profit-Loss Ratio
1.40
Alpha
0.046
Beta
0.402
Annual Standard Deviation
0.092
Annual Variance
0.008
Information Ratio
-0.069
Tracking Error
0.114
Treynor Ratio
0.206
Total Fees
$241.57
Estimated Strategy Capacity
$1100000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
Portfolio Turnover
2.69%
# region imports
from AlgorithmImports import *
# endregion

class UpgradedRedOrangeSnake(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2015, 1, 1)  # Set Start Date
        self.SetEndDate(2023,7,1)
        self.SetCash(100000)  # Set Strategy Cash
        self.UniverseSettings.Leverage = 2.0   
        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol

        self.apo = self.APO(self.spy,10,20,MovingAverageType.Simple,Resolution.Daily).Signal
        self.SetWarmup(25)
        self.apo.Updated += self.ApoUpdated
        self.apoWin = RollingWindow[IndicatorDataPoint](2)
        self.SetBenchmark("SPY")
        self.lastExitTime = datetime.min

        #Crear promedios y desviaciones estándar para calcular la Z.
        self.signalmean = SimpleMovingAverage(5)
        self.signalstd = StandardDeviation(5)
        self.ZWin = RollingWindow[float](2)

        #Helper Variables para TP y SL
        self.stop_loss_percent = 0.075
    
    def ApoUpdated(self, sender, updated):
        self.apoWin.Add(updated)     

    def OnData(self, data: Slice):
        if not (self.apoWin.IsReady):
            return

        currApo = self.apoWin[0]
        pastApo = self.apoWin[1]

        self.signalmean.Update(self.Time,self.apo.Current.Value)
        self.signalstd.Update(self.Time, self.apo.Current.Value)


        try:
            signal_z = (self.apo.Current.Value-self.signalmean.Current.Value)/(self.signalstd.Current.Value)
            self.ZWin.Add(float(signal_z))
        except:
            signal_z = 0.0

        pastZ = self.ZWin[1]
        currZ = self.ZWin[0]

        if self.apo.IsReady:
            # The current value of self.apo is represented by self.apo.Current.Value
            self.Plot("AbsolutePriceOscillator", "Signal", currApo)
            self.Plot("AbsolutePriceOscillator","Apo Z", currZ)
            self.Plot("AbsolutePriceOscillator","Past Z", pastZ)
        
     
        if not self.Portfolio.Invested and currZ > -1.4 and pastZ < -1.4:
            if (self.Time - self.lastExitTime).days > 10: 
                self.SetHoldings("SPY",0.90)

        if self.Portfolio.Invested and currZ < 1.0 and pastZ > 1.0:
            self.Liquidate(tag="Z Score Liquidation")
            self.lastExitTime = self.Time

        if self.Portfolio.Invested:
            try:
                price = data[self.spy].Close
                if price <= self.Portfolio[self.spy].AveragePrice * (1-self.stop_loss_percent):
                    self.Liquidate(tag="Stop Loss Liquidation")
                    self.lastExitTime = self.Time
            except:
                self.Log("No price data today")