Overall Statistics
Total Trades
16036
Average Win
0.05%
Average Loss
-0.04%
Compounding Annual Return
17.606%
Drawdown
52.800%
Expectancy
0.314
Net Profit
141.085%
Sharpe Ratio
0.711
Probabilistic Sharpe Ratio
18.977%
Loss Rate
46%
Win Rate
54%
Profit-Loss Ratio
1.42
Alpha
0.201
Beta
-0.143
Annual Standard Deviation
0.254
Annual Variance
0.065
Information Ratio
0.12
Tracking Error
0.318
Treynor Ratio
-1.263
Total Fees
$73305.88
Estimated Strategy Capacity
$4800000.00
import pandas as pd
import numpy as np
from io import StringIO

class PensiveTanAnguilline(QCAlgorithm):

    ### Use a new file

    def Initialize(self):
        self.SetStartDate(2015, 9, 1)  # Set Start Date
        self.SetEndDate(2021, 2, 1)
        self.SetCash(2000000)  # Set Strategy Cash
        # self.AddEquity("SPY", Resolution.Minute)
        
        self.SetSecurityInitializer(self.CustomSecurityInitializer)

        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.allSymbols = {}
        self.allSecurities = []

        self.SetBenchmark("SPY")
        for symbol in self.symbols:
            security = self.AddEquity(symbol, Resolution.Daily).Symbol
            # explication
            self.allSymbols[security.Value] = [0, 0] #[position,transaction]
            self.allSecurities.append(security)

        #self.AddRiskMangement(TrailingStopRiskManagementModel(0.05))

        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
        self.tradableSecurities = []
        
        # retirer le '#' pour le warmUp
        self.SetWarmUp(timedelta(days = 30))
        
        # changer le lien
        # old link : https://www.dropbox.com/s/qrta0p6pu2nv9yy/signaux_presentable_wednesday.csv?dl=1
        # new link : https://www.dropbox.com/s/qur4qqfgwvdh4ti/new_factor_signaux_presentable.csv?dl=1
        
        csv = self.Download("https://www.dropbox.com/s/qur4qqfgwvdh4ti/new_factor_signaux_presentable.csv?dl=1")
        self.df = pd.read_csv(StringIO(csv))
        self.externalData = list(self.df.itertuples())
        
        self.table_position = 0
        
        # self.Schedule.On(self.DateRules.EveryDay("SPY"),  
        #          self.TimeRules.At(0, 0),     
        #          self.rebalance)
                 
        self.investedCount = 0
        
        # A RETIRER EVENTUELLEMENT
        # NE jamais faire le rebalancement a 0 minutes de la fermature
        # car l'ordre est exécuté le lendemain au cours d'ouverture
        # donc fort risque de hausse de prix faisant qu'il n'est plus possible d'acheter le titre
        
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Wednesday),
        self.TimeRules.BeforeMarketClose("SPY", 1), # We receive the signal monday 22h swiss
        self.rebalance )
        
        # Assigning securities custom fee models:
        #self.Securities["AAPL"].SetFeeModel(CustomFeeModel())
        for symbol in self.symbols:
            self.Securities[symbol].SetFeeModel(CustomFeeModel())

    def OnData(self, data):
        self.data = data
        # A REMETTRE
        #self.rebalance()
        pass
            
    def rebalance(self):
        '''Include sell statement for removed securities'''
        algYear = self.Time.year
        algMonth = self.Time.month
        algDay = self.Time.day
        
        currentDate = str(algDay) + "/" + str(algMonth) + "/" + str(algYear)
        # displayed based on the resolution (Daily)
        #self.Debug("currentDate : " + currentDate)
        
        i = 0
        
        for row in self.externalData:
            #try: #If there is an error with dates a tuple(row) then continue to the next row
            date = row[1] #Date is the index of the tuple(final colum in the dataframe)
            # DD/MM/YYYY
            date = str(date)
            
                
                
            # erreur ce devrait etre day puis month
            day = date[0:2] 
            month = date[3:5] 
            year = date[6:] 
            
            """
            i = i + 1
            if (i == 3): # vérifier que les mois et jours sont juste
                self.Debug("month : " + month)
                self.Debug("day : " + day)
                self.Debug("year : " + year)
            """

            # retrait du 0 dans jours et mois ex : 01/01/2010 --> 1/1/2010
            if day[0] == '0':
                day = day[1]
            if month[0] == '0':
                month = month[1]
                
            newDate = str(day) + "/" + str(month) + "/" + str(year)

            if newDate == currentDate:
                #self.Debug("Is Current")
                #self.Debug('currentDate : ' + currentDate)
                #self.Debug('newDate : ' + newDate)
                position = row[2]
                transaction = row[3]
                symbol = row[-1]
                #self.Debug('row[2] : ' + str(row[2]))
                #self.Debug('row[3] : ' + str(row[3]))
                #self.Debug('row[-1] : ' + str(row[-1]))
        
                self.allSymbols[row[-1]] = [row[2], row[3]]
        self.trade()
        
        """     # IF THERE IS ENOUGH STOCK IN NEW PORTFOLIO
                self.Debug("We trade")
                #ELSE
                #RETURN
                self.Debug("We do not trade")   """
        
        
    def trade(self):
        buySecurities = []
        investingInto = []
        #positions : [position, transaction]
        # séparer le traitement
        # liquider seulement si on rebalance
        for symbol, positions in self.allSymbols.items():
                
                # ATTENTION il est possible d'ajouter plusieurs fois le même symbol ? 
                # vérifier si limiter à l'instance en cours
                # SI oui risque de LEVIER !!
                # NON IL N'Y A PAS DE DOUBLON
            if positions[0] == 1:
                buySecurities.append(symbol)
                
        for symbol in buySecurities:
            if self.data.ContainsKey(self.allSecurities[self.symbols.index(symbol)]):
                investingInto.append(symbol)
        #self.Debug('investingInto : ' + str(investingInto))
        
        algYear = self.Time.year
        algMonth = self.Time.month
        algDay = self.Time.day
        
        currentDate = str(algDay) + "/" + str(algMonth) + "/" + str(algYear)
        self.Debug(currentDate)
                
        
        # --------------------------------------------------------
        # Don't affect the algo why ?
        MIN_PORTFOLIO_SIZE = 20
        if(len(investingInto) < MIN_PORTFOLIO_SIZE):
            self.Debug("We do not rebalance, because only : " + str(len(investingInto)) + " stocks to invest in")
            """
            self.Debug("Portfolio holdings")
            self.Debug("------------------------------------------------")
            nb_stocks_in_portfolio = 0
            
            algYear = self.Time.year
            algMonth = self.Time.month
            algDay = self.Time.day
        
            currentDate = str(algDay) + "/" + str(algMonth) + "/" + str(algYear)
            
            for kvp in self.Portfolio:
                security_holding = kvp.Value
                
                # Quantity of the security held
                quantity = security_holding.Quantity
                # Quand je fais type quantity, rien ne s'affiche
                # caster la valeur en un INTEGER
                #self.Debug("type(quantity): " + str(type(quantity)) + " - fin")
                # type(security_holding): 2019-12-03 00:00:00
                #self.Debug("int(quantity) : " + str(int(quantity)) + " - fin")
                if(int(quantity) > 0):
                    nb_stocks_in_portfolio = nb_stocks_in_portfolio + 1
                #symbol = security_holding.Symbol.Value

                # Average price of the security holdings 
                #price = security_holding.AveragePrice
            self.Debug("currentDate : " + currentDate + " / ")
            self.Debug("nb_stocks_in_portfolio : " + str(nb_stocks_in_portfolio))"""
            
            return
        else:
            for symbol, positions in self.allSymbols.items():
                #self.Debug('symbol : ' + symbol + ', position : ' + str(positions))
                if positions[0] == 0:
                    self.Liquidate(symbol)
            
            
            # 0.5% cash buffer --> 221 warning
            # 1.0% cash buffer --> 221 warning BIZZARD
            for symbol in investingInto:
                self.SetHoldings([PortfolioTarget(symbol, 0.999/len(investingInto))])   
                self.Debug("We rebalance because : " + str(len(investingInto)) + " stocks to invest in")

        """
        self.Debug("Portfolio holdings")
        self.Debug("------------------------------------------------")

        for kvp in self.Portfolio:
            security_holding = kvp.Value
            #symbol = security_holding.Symbol.Value
            # Quantity of the security held
            quantity = security_holding.Quantity
            self.Debug("quantity : " + str(quantity))
            # Average price of the security holdings 
            #price = security_holding.AveragePrice
        
        
        """
            
        #if(len(buySecurities) < 50):
        #self.Debug("buySecurities : " + str(len(buySecurities)))
        #if(len(investingInto) < 50):
        #self.Debug("investingInto : " + str(len(investingInto)))
        
    def CustomSecurityInitializer(self, security):
        security.SetLeverage(1)
        
class CustomFeeModel:
    def GetOrderFee(self, parameters):
        PERCENTAGE_FEE = 0.0002 # vraie valeur 0.0002
        fee = max(1, parameters.Security.Price
                  * parameters.Order.AbsoluteQuantity
                  * PERCENTAGE_FEE)
        return OrderFee(CashAmount(fee, 'USD'))