Overall Statistics
class ResidualMomentumAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2016, 1, 1)
        self.SetCash(100000)
        
        self.symbol = self.AddData(FF, 'FFFactors', Resolution.Daily).Symbol
    
    def OnData(self, data):
        if data.ContainsKey(self.symbol):
            for factor in ['hml', 'mkt', 'rf', 'smb']:
                self.Plot('FF', factor, data[self.symbol].GetProperty(factor))
                
from dateutil.relativedelta import relativedelta

class FF(PythonData):
    """
    This class is used to stream Fama French data into our algorithm.
    """
    
    def GetSource(self, config, date, isLiveMode):
        """
        Return the URL string source of the file. This will be converted to a stream 
        
        Inputs:
         - config
            Configuration object
         - date
            Date of this source file
         - isLiveMode
            True if we're in live mode; False for backtesting mode
        
        Returns a SubscriptionDataSource - the source location and transport medium for a subscription.
        """
        source = "https://github.com/QuantConnect/Tutorials/raw/feature-data-directory/Data/F-F_Research_Data_Factors.csv"
        return SubscriptionDataSource(source, SubscriptionTransportMedium.RemoteFile)
    
    def Reader(self, config, line, date, isLive):
        """
        Reader converts each line of the data source into BaseData objects. Each data type creates its own 
        factory method, and returns a new instance of the object each time it is called. The returned object 
        is assumed to be time stamped in the config.ExchangeTimeZone.
        
        Inputs:
         - config
            Subscription data config setup object
         - line
            Line of the source document
         - date
            Date of the requested data
         - isLive
            True if we're in live mode; False for backtesting mode
        
        Returns a data point from the Fama French data feed.
        """
        # If first character is not digit, pass
        if not (line.strip() and line[0].isdigit()): 
            return None
    
        try:
            data = line.split(',')

            ff = FF()
            ff.Symbol = config.Symbol
            ff.Time = datetime.strptime(data[0], '%Y%m') + relativedelta(months=1)

            ff.SetProperty("hml", float(data[3]))
            ff.SetProperty("mkt", float(data[1]))
            ff.SetProperty("rf", float(data[4]))
            ff.SetProperty("smb", float(data[2]))
            
            return ff
        except ValueError:
            # Do nothing, possible error in json decoding
            return None