Overall Statistics
Total Trades
1893
Average Win
0.00%
Average Loss
0.00%
Compounding Annual Return
157.114%
Drawdown
1.700%
Expectancy
8.493
Net Profit
12.651%
Sharpe Ratio
6.693
Probabilistic Sharpe Ratio
95.729%
Loss Rate
5%
Win Rate
95%
Profit-Loss Ratio
9.01
Alpha
1.496
Beta
-0.29
Annual Standard Deviation
0.172
Annual Variance
0.029
Information Ratio
-0.218
Tracking Error
0.237
Treynor Ratio
-3.956
Total Fees
$1917.95
Estimated Strategy Capacity
$54000000.00
from io import StringIO
import pandas as pd
import datetime


class PensiveTanAnguilline(QCAlgorithm):

    ##################################################################
    ### Run the algo once to to store the objectStore              ###    
    ### Run the algo a second time to use it as a custom Benchmark ###
    ##################################################################

    ### START TO CUSTOM BENCHMARK ###
    
    portfolio_ObjectStore_Key_test = "test_v1.0.10"
    string_object = ""
    
    ### END - RELATED TO CUSTOM BENCHMARK ###


    START_DATE = datetime.datetime(2020,11,1)
    END_DATE = datetime.datetime(2020,12,15)

    def Initialize(self):
        self.SetStartDate(self.START_DATE.year, self.START_DATE.month, self.START_DATE.day)  
        self.SetEndDate(self.END_DATE.year, self.END_DATE.month, self.END_DATE.day)
        self.SetCash(2000000)
        
        self.symbols = ['A','AAL','AAP','AAPL','ABBV','ABC','ABMD','ABT','ACN','ADBE','ADI','ADM','ADP','ADSK','AEE','AEP','AES','AFL','AIG','AIZ','AJG','AKAM','ALB','ALGN','ALK','ALL','ALLE','ALXN','AMAT','AMCR','AMD','AME','AMGN','AMP','AMT','AMZN','ANET','ANSS','ANTM','AON','AOS','APA','APD','APH','APTV','ARE','ATO','ATVI','AVB','AVGO','AVY','AWK','AXP','AZO','BA','BAC','BAX','BBY','BDX','BEN','BFb','BIIB','BIO','BK','BKNG','BKR','BLK','BLL','BMY','BR','BRKb','BSX','BWA','BXP','C','CAG','CAH','CARR','CAT','CB','CBOE','CBRE','CCI','CCL','CDNS','CDW','CE','CERN','CF','CFG','CHD','CHRW','CHTR','CI','CINF','CL','CLX','CMA','CMCSA','CME','CMG','CMI','CMS','CNC','CNP','COF','COG','COO','COP','COST','CPB','CPRT','CRM','CSCO','CSX','CTAS','CTLT','CTSH','CTVA','CTXS','CVS','CVX','D','DAL','DD','DE','DFS','DG','DGX','DHI','DHR','DIS','DISCA','DISCK','DISH','DLR','DLTR','DOV','DOW','DPZ','DRE','DRI','DTE','DUK','DVA','DVN','DXC','DXCM','EA','EBAY','ECL','ED','EFX','EIX','EL','EMN','EMR','ENPH','EOG','EQIX','EQR','ES','ESS','ETN','ETR','ETSY','EVRG','EW','EXC','EXPD','EXPE','EXR','F','FANG','FAST','FB','FBHS','FCX','FDX','FE','FFIV','FIS','FISV','FITB','FLIR','FLS','FLT','FMC','FOXA','FRC','FRT','FTNT','FTV','GD','GE','GILD','GIS','GL','GLW','GM','GOOG','GOOGL','GPC','GPN','GPS','GRMN','GS','GWW','HAL','HAS','HBAN','HBI','HCA','HD','HES','HFC','HIG','HII','HLT','HOLX','HON','HPE','HPQ','HRL','HSIC','HST','HSY','HUM','HWM','IBM','ICE','IDXX','IEX','IFF','ILMN','INCY','INFO','INTC','INTU','IP','IPG','IPGP','IQV','IR','IRM','ISRG','IT','ITW','IVZ','JBHT','JCI','JEC','JKHY','JNJ','JNPR','JPM','K','KEY','KEYS','KHC','KIM','KLAC','KMB','KMI','KMX','KO','KR','KSU','L','LB','LDOS','LEG','LEN','LH','LHX','LIN','LKQ','LLY','LMT','LNC','LNT','LOW','LRCX','LUMN','LUV','LVS','LW','LYB','LYV','MA','MAA','MAR','MAS','MCD','MCHP','MCK','MCO','MDLZ','MDT','MET','MGM','MHK','MKC','MKTX','MLM','MMC','MMM','MNST','MO','MOS','MPC','MPWR','MRK','MRO','MS','MSCI','MSFT','MSI','MTB','MTD','MU','MXIM','NCLH','NDAQ','NEE','NEM','NFLX','NI','NKE','NLOK','NLSN','NOC','NOV','NOW','NRG','NSC','NTAP','NTRS','NUE','NVDA','NVR','NWL','NWSA','O','ODFL','OKE','OMC','ORCL','ORLY','OTIS','OXY','PAYC','PAYX','PBCT','PCAR','PEAK','PEG','PEP','PFE','PFG','PG','PGR','PH','PHM','PKG','PKI','PLD','PM','PNC','PNR','PNW','POOL','PPG','PPL','PRGO','PRU','PSA','PSX','PVH','PWR','PXD','PYPL','QCOM','QRVO','RCL','RE','REG','REGN','RF','RHI','RJF','RL','RMD','ROK','ROL','ROP','ROST','RSG','RTX','SBAC','SBUX','SCHW','SEE','SHW','SIVB','SJM','SLB','SLG','SNA','SNPS','SO','SPG','SPGI','SRE','STE','STT','STX','STZ','SWK','SWKS','SYF','SYK','SYY','T','TAP','TDG','TDY','TEL','TER','TFC','TFX','TGT','TJX','TMO','TMUS','TPR','TRMB','TROW','TRV','TSCO','TSLA','TSN','TT','TTWO','TWTR','TXN','TXT','TYL','UA','UAA','UAL','UDR','UHS','ULTA','UNH','UNM','UNP','UPS','URI','USB','V','VAR','VFC','VIAC','VLO','VMC','VNO','VNT','VRSK','VRSN','VRTX','VTR','VTRS','VZ','WAB','WAT','WBA','WDC','WEC','WELL','WFC','WHR','WLTW','WM','WMB','WMT','WRB','WRK','WST','WU','WY','WYNN','XEL','XLNX','XOM','XRAY','XRX','XYL','YUM','ZBH','ZBRA','ZION','ZTS']
        self.allSecurities = []
        for symbol in self.symbols:
            security = self.AddEquity(symbol, Resolution.Daily).Symbol 
            self.allSecurities.append(security)
        
        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol 
        self.tradableSecurities = []
        
        self.SetWarmUp(timedelta(days = 30))
        
        
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Wednesday),
                self.TimeRules.BeforeMarketClose("SPY", 0),
                self.rebalance )
                
        
        self.investedCount = 0

        ### START - RELATED TO CUSTOM BENCHMARK ###

        # ObjectStore for custom Benchmark
        if self.ObjectStore.ContainsKey(self.portfolio_ObjectStore_Key_test):
            values = self.ObjectStore.Read(self.portfolio_ObjectStore_Key_test)
            self.Debug(f'{self.portfolio_ObjectStore_Key_test} key exists in object store.')
            self.Debug("str(values)" + "\n")
            self.Debug(str(values))
            history = pd.read_csv(StringIO(values), header=None, index_col=0, squeeze=True)
            history.index = pd.to_datetime(history.index)

            """
                Here comes the problems
                I tried two ways:
                    1) benchmark = [history.index.values,history.values]
                       self.SetBenchmark(benchmark)
                    
                    2) self.SetBenchmark(self.custom_function) # function definition below
                
                Both doesn't work
                    
            """
            
            # Doesn't work : 
            #benchmark = [history.index.values,history.values]
            #self.SetBenchmark(benchmark)

            # Doesn't work neither:
            #self.SetBenchmark(self.custom_function)
            

        ### END - RELATED TO CUSTOM BENCHMARK ###

    def OnData(self, data):
        
        ### START - RELATED TO CUSTOM BENCHMARK ###

        #self.Debug("self.Time : " + str(self.Time)  + " - FIN" + "\n" )
        # output : self.Time : 2021-02-01 00:00:00 - FIN

        #self.Debug("self.Portfolio.TotalPortfolioValue : " + str(self.Portfolio.TotalPortfolioValue) + " - FIN" + "\n")
        # output : self.Portfolio.TotalPortfolioValue : 2000000.0 - FIN
        
        ### Add only portfolio value since the start_date
        ### We don't store the portfolio value during the warmup period
        if(self.Time >= self.START_DATE):
            self.string_object += ('\n' + str(self.Time) + "," + str(self.Portfolio.TotalPortfolioValue) )
        

        #Save the object at the last day of the algorithm
        if( (self.END_DATE.year == self.Time.year)  and (self.END_DATE.month == self.Time.month)  and (self.END_DATE.day == self.Time.day) ):
            self.Debug(str(self.Time))
            self.ObjectStore.Save(self.portfolio_ObjectStore_Key_test, self.string_object)

        # self.Debug("string_object : " + self.string_object)
        

        ### END - RELATED TO CUSTOM BENCHMARK ###
        
        self.tradableSecurities = []
        for security in self.allSecurities:
            if data.ContainsKey(security.Value):
                self.tradableSecurities.append(security)
            else:
                continue

    def rebalance(self):
        
        '''Include sell statement for removed securities'''
        # Faire if len < 100; return
        if len(self.tradableSecurities) == 0:
            return

        if (len(self.tradableSecurities) < 100):
            self.Debug("Number of tradableSecurities : " + str(len(self.tradableSecurities)))
            self.Debug("We don''t rebalance")        
            return        
                
        for security in self.tradableSecurities:
            if not self.Portfolio[security].Invested:
                self.investedCount += 1
        
        INVESTED_FRACTION = 0.995 # Remove 0.5% for the fees ?
        weight_temp = INVESTED_FRACTION/len(self.tradableSecurities)
        WEIGHT_LIMIT = 0.03

        weight = self.weightLimitation(weight_temp, WEIGHT_LIMIT)
        
        for security in self.tradableSecurities:
            if self.Portfolio[security].Invested:
                self.SetHoldings([PortfolioTarget(security.Value, weight)])
                
        for security in self.tradableSecurities:
            if not self.Portfolio[security].Invested:
                self.SetHoldings([PortfolioTarget(security.Value, weight)])
                
    ### START - RELATED TO CUSTOM BENCHMARK ###
                
    def custom_function(self):
        
        time = self.Time
        date = datetime.datetime(time.year, time.month, time.day)
            
        t = pd.Timestamp(date)
        value = history.loc[t]
            
        return t, value

    ### END - RELATED TO CUSTOM BENCHMARK ###


    def weightLimitation(self, weight_temp,WEIGHT_LIMIT):
        if(weight_temp > WEIGHT_LIMIT):
            return WEIGHT_LIMIT
        else:
            return weight_temp