Overall Statistics
Total Trades
163
Average Win
0.73%
Average Loss
-0.62%
Compounding Annual Return
32.810%
Drawdown
11.400%
Expectancy
0.761
Net Profit
79.428%
Sharpe Ratio
1.517
Probabilistic Sharpe Ratio
73.538%
Loss Rate
20%
Win Rate
80%
Profit-Loss Ratio
1.19
Alpha
0
Beta
0
Annual Standard Deviation
0.152
Annual Variance
0.023
Information Ratio
1.517
Tracking Error
0.152
Treynor Ratio
0
Total Fees
$163.00
Estimated Strategy Capacity
$100000000.00
Lowest Capacity Asset
TSM R735QTJ8XC9X
from AlgorithmImports import *


class MaximumDrawdownPercentPerSecurity(RiskManagementModel):
    '''Provides an implementation of IRiskManagementModel that limits the drawdown per holding to the specified percentage'''
    

    def __init__(self, maximumDrawdownPercent = 0.15):
        
        
    #    '''Initializes a new instance of the MaximumDrawdownPercentPerSecurity class
    #   Args:
    #      maximumDrawdownPercent: The maximum percentage drawdown allowed for any single security holding'''
        self.maximumDrawdownPercent = -abs(maximumDrawdownPercent)

    def ManageRisk(self, algorithm, targets):
        '''Manages the algorithm's risk at each time step
        Args:
            algorithm: The algorithm instance
            targets: The current portfolio targets to be assessed for risk'''
        targets = []
        for kvp in algorithm.Securities:
            security = kvp.Value

            if not security.Invested:
                continue

            pnl = security.Holdings.UnrealizedProfitPercent
            if pnl < self.maximumDrawdownPercent:
                # liquidate
                targets.append(PortfolioTarget(security.Symbol, 0))

        return targets

from AlgorithmImports import *
from MaximumDrawdownPercentPerSecurity import MaximumDrawdownPercentPerSecurity



class WellDressedSkyBlueSardine(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020,1,1)
        #self.SetEndDate(2022,1,1)
        self.SetCash(25000)
        self.rebalanceTime = self.Time 
        #self.rebalanceTime = datetime.min
        self.activeStocks = set()
        #self.AddEquity("SPY", Resolution.Daily)
        self.AddRiskManagement(MaximumDrawdownPercentPerSecurity())
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash)
        self.VixNegative = True
        
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseFilter, self.FineFilter)
        
        
        
        
        #VIX block
        self.previous = 0.0
        self.lastTrade = datetime(1, 1, 1)
        self.cboeVix = self.AddData(CBOE, "VIX").Symbol
        
        
        
        
      
        self.portfolioTargets = []
        
        #price_plot = Chart('My Plot') #name of plot => what chart object
        #price_plot.AddSeries(Series('Portfolio', SeriesType.Candle, 0)) #one tab with daily prices => what series connected to
        #price_plot.AddSeries(Series('Monthly Open Price', SeriesType.Candle,1)) # the second series it is connected to
        #self.AddChart(price_plot) #two with monthly prices
        
        
        
        
        #Parameters
        
        timedelta = self.GetParameter("timedelta")
        FinalSharesAmount = self.GetParameter("FinalSharesAmount")
        MarketCap = self.GetParameter("MarketCap")
        
        
        self.PRAM_timedelta = 6 #if timedelta is None else int(timedelta)
        self.PRAM_FinalSharesAmount = 10# if sortedByPE is None else int(FinalSharesAmount)
        self.PRAM_MarketCap = 100000000 #if MarketCap is None else int(MarketCap)
        

    def CoarseFilter(self, coarse):
        # Rebalancing monthly
        if self.Time <= self.rebalanceTime:
            return self.Universe.Unchanged
        
        self.rebalanceTime = timedelta(self.PRAM_timedelta)+ self.Time
        sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True)
        return [x.Symbol for x in sortedByDollarVolume if x.Price > 10
                                                and x.HasFundamentalData][:100]

    def FineFilter(self, fine):
        
        sortedByPE = sorted(fine, key=lambda x: x.MarketCap, reverse=True)
        return [x.Symbol for x in sortedByPE if x.MarketCap > self.PRAM_MarketCap][:self.PRAM_FinalSharesAmount]
        
    
    def OnSecuritiesChanged(self, changes):
        # close positions in removed securities
        if self.VixNegative is False:
            return
        for x in changes.RemovedSecurities:
            self.Liquidate(x.Symbol)
            self.activeStocks.remove(x.Symbol)
            self.lastTrade = self.Time
        
        # can't open positions here since data might not be added correctly yet
        for x in changes.AddedSecurities:
            self.activeStocks.add(x.Symbol)
            self.lastTrade = self.Time

        # adjust targets if universe has changed
        if self.SetRiskManagement is True:
           self.SetHoldings(-1)
           self.lastTrade = self.Time
        else:
            self.portfolioTargets = [PortfolioTarget(symbol, 1/self.PRAM_FinalSharesAmount)#len(self.activeStocks)) 
                            for symbol in self.activeStocks]
            self.lastTrade = self.Time
                            
                            

    def OnData(self, data):
        
        
      #  if self.Portfolio.Invested:
      #      self.Plot('My Plot','My Portfolio',self.activeStocks.Open)
      #      if self.Time.day == 1: #if its the first of the month ( monthly forst day)
      #       self.Plot('My Plot','SPY',self.Securities['SPY'].Open)
      
      
        if not data.ContainsKey(self.cboeVix):
            return
        
        vix = data.Get(CBOE, self.cboeVix)
        current = vix.Close
        
        if self.previous != 0:
            # Calculate the percentage, and if it is greater than 10%
            percentageChange = ((current - self.previous) / self.previous)-1
            
            if percentageChange < 0.01:
                self.VixNegative is True
                self.lastTrade = self.Time
            
            
            if percentageChange > 0.1:
                self.Liquidate()
                self.VixNegative is False
                self.lastTrade = self.Time
            
        
        
        if self.portfolioTargets == []:
            return
        
        for symbol in self.activeStocks:
            if symbol not in data:
                return
       
        self.SetHoldings(self.portfolioTargets)
        
        self.portfolioTargets = []
        
        self.previous = vix.Close
        
    def OnOrderEvent(self, orderEvent):
        if orderEvent.Status == OrderStatus.Filled:
            self.Debug(f'Processed Order: {orderEvent.Symbol}, Quantity: {orderEvent.FillQuantity}' )