多因子选股策略在QuantConnect上的实现

概要

QuantConnect平台上多因子选股的一个实现,出处

整体架构流程

前置环境:

  1. VS2022 Community(Python和C#选上)
  2. Anaconda3-2020.11-Windows-x86_64(升级Python到3.8.13,环境变量关联到pythonnet)
  3. Docker Desktop(下载research 16863版本镜像)
  4. VS Code
  5. Github Lean 16863版本(注意修改镜像为research 16863)

数据准备:

  1. 随机生成1000个股行情数据
  2. 配套1000个股生成基本面数据

技术细节

  • 文件夹Lean-16863\Algorithm.Python\FactorInvesting
│  Factor Investing In QuantConnect - Research Notebook & Backtesting Algorithm.mhtml
│  main.py
│  research.ipynb
│  ResearchFactorAnalysis.py
│  ResearchRiskAnalysis.py
│  tree.txt
│  
├─Alpha
│  │  LongShortAlphaCreation.py
│  │  
│  └─__pycache__
│          LongShortAlphaCreation.cpython-311.pyc
│          
├─Helpers
│  │  classSymbolData.py
│  │  HelperFunctions.py
│  │  
│  └─__pycache__
│          classSymbolData.cpython-311.pyc
│          HelperFunctions.cpython-311.pyc
│          
├─Portfolio
│  │  CustomEqualWeightingPortfolioConstruction.py
│  │  
│  └─__pycache__
│          CustomEqualWeightingPortfolioConstruction.cpython-311.pyc
│          
├─Universe
│  │  FactorModelUniverseSelection.py
│  │  
│  └─__pycache__
│          FactorModelUniverseSelection.cpython-311.pyc
│          
└─__pycache__
        main.cpython-311.pyc
  • LongShortAlphaCreation.py
# region imports
from AlgorithmImports import *
# endregion
from clr import AddReference
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Algorithm.Framework")

from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Alphas import AlphaModel, Insight, InsightType, InsightDirection

from Helpers.HelperFunctions import GetFundamentalDataDict, MakeCalculations, GetLongShortLists
from datetime import timedelta, datetime
import pandas as pd
import numpy as np

class LongShortAlphaCreationModel(AlphaModel):

    def __init__(self, maxNumberOfPositions = 10, lookback = 252):
        
        self.maxNumberOfPositions = maxNumberOfPositions
        self.lookback = lookback
        
        self.securities = []
        self.day = 0

    def Update(self, algorithm, data):
        
        insights = [] # list to store the new insights to be created
        
        if algorithm.Time.day != self.day and algorithm.Time.hour > 9:
            for symbol, direction in self.insightsDict.items():
                if data.ContainsKey(symbol) and symbol in algorithm.ActiveSecurities.Keys and algorithm.ActiveSecurities[symbol].Price > 0:
                    insights.append(Insight.Price(symbol, Expiry.EndOfDay, direction))
                    
            self.day = algorithm.Time.day

        return insights
            
    def OnSecuritiesChanged(self, algorithm, changes):
        
        '''
        Description:
            Event fired each time the we add/remove securities from the data feed
        Args:
            algorithm: The algorithm instance that experienced the change in securities
            changes: The security additions and removals from the algorithm
        '''
            
        # check current securities in our self.securities list
        securitiesList = [x.Symbol.Value for x in self.securities]
        algorithm.Log('(Alpha module) securities in self.securities before OnSecuritiesChanged: ' + str(securitiesList))
            
        # add new securities
        addedSecurities = [x for x in changes.AddedSecurities if x not in self.securities]
        for added in addedSecurities:
            self.securities.append(added)
            
        newSecuritiesList = [x.Symbol.Value for x in addedSecurities]
        algorithm.Log('(Alpha module) new securities added to self.securities:'+ str(newSecuritiesList))

        # remove securities
        removedSecurities = [x for x in changes.RemovedSecurities if x in self.securities]
        for removed in removedSecurities:
            self.securities.remove(removed)
                
        removedList = [x.Symbol.Value for x in removedSecurities]
        algorithm.Log('(Alpha module) securities removed from self.securities: ' + str(removedList))
        
        # print the final securities in self.securities for today
        securitiesList = [x.Symbol.Value for x in self.securities]
        algorithm.Log('(Alpha module) final securities in self.securities after OnSecuritiesChanged: ' + str(securitiesList))
        
        # generate dictionary with factors -------------------------------------------------------
        fundamentalDataBySymbolDict = GetFundamentalDataDict(algorithm, self.securities, module = 'alpha')
                    
        # make calculations to create long/short lists -------------------------------------------
        currentSymbols = list(fundamentalDataBySymbolDict.keys())
        calculations = MakeCalculations(algorithm, currentSymbols, self.lookback, Resolution.Daily, fundamentalDataBySymbolDict)
        
        # get long/short lists
        longs, shorts = GetLongShortLists(self, algorithm, calculations)
        finalSymbols = longs + shorts
        
        # update the insightsDict dictionary with long/short signals
        self.insightsDict = {}
        for symbol in finalSymbols:
            if symbol in longs:
                direction = 1
            else:
                direction = -1
                
            self.insightsDict[symbol] = direction
  • classSymbolData.py
# region imports
from AlgorithmImports import *
# endregion
import pandas as pd
import numpy as np
from scipy.stats import skew, kurtosis

class SymbolData:
    
    ''' Perform calculations '''
    
    def __init__(self, symbol):
        self.Symbol = symbol
        
        self.fundamentalDataDict = {}
        
        self.momentum = None
        self.volatility = None
        self.skewness = None
        self.kurt = None
        self.positionVsHL = None
        self.meanOvernightReturns = None
    
    def CalculateFactors(self, history, fundamentalDataBySymbolDict):
        
        self.fundamentalDataDict = fundamentalDataBySymbolDict[self.Symbol]
        self.momentum = self.CalculateMomentum(history)
        self.volatility = self.CalculateVolatility(history)
        #self.skewness = self.CalculateSkewness(history)
        #self.kurt = self.CalculateKurtosis(history)
        #self.distanceVsHL = self.CalculateDistanceVsHL(history)
        #self.meanOvernightReturns = self.CalculateMeanOvernightReturns(history)
    
    def CalculateMomentum(self, history):
        
        closePrices = history.loc[self.Symbol]['close']
        momentum = (closePrices[-1] / closePrices[-252]) - 1
        
        return momentum
        
    def CalculateVolatility(self, history):
        
        closePrices = history.loc[self.Symbol]['close']
        returns = closePrices.pct_change().dropna()
        volatility = np.nanstd(returns, axis = 0)
        
        return volatility
        
    def CalculateSkewness(self, history):
        
        closePrices = history.loc[self.Symbol]['close']
        returns = closePrices.pct_change().dropna()
        skewness = skew(returns)
        
        return skewness
        
    def CalculateKurtosis(self, history):
        
        closePrices = history.loc[self.Symbol]['close']
        returns = closePrices.pct_change().dropna()
        kurt = kurtosis(returns)
        
        return kurt
        
    def CalculateDistanceVsHL(self, history):
        
        closePrices = history.loc[self.Symbol]['close']
        annualHigh = max(closePrices)
        annualLow = min(closePrices)
        distanceVsHL = (closePrices[-1] - annualLow) / (annualHigh - annualLow)
        
        return distanceVsHL
        
    def CalculateMeanOvernightReturns(self, history):
        
        overnnightReturns = (history.loc[self.Symbol]['open'] / history.loc[self.Symbol]['close'].shift(1)) - 1
        meanOvernightReturns = np.nanmean(overnnightReturns, axis = 0)
        return meanOvernightReturns
            
    @property
    def factorsList(self):
        technicalFactors = [self.momentum, self.volatility]
        fundamentalFactors = [float(key) * value for key, value in self.fundamentalDataDict.items()]
        
        if all(v is not None for v in technicalFactors):
            return technicalFactors + fundamentalFactors
        else:
            return None
  • HelperFunctions.py
# region imports
from AlgorithmImports import *
# endregion
import pandas as pd
from scipy.stats import zscore
from Helpers.classSymbolData import SymbolData

def MakeCalculations(algorithm, symbols, lookback, resolution, fundamentalDataBySymbolDict):
    
    '''
    Description:
        Make required calculations using historical data for each symbol
    Args:
        symbols: The symbols to make calculations for
        lookback: Lookback period for historical data
        resolution: Resolution for historical data
        fundamentalDataBySymbolDict: Dictionary of symbols containing factors and the direction of the factor (for sorting)
    Return:
        calculations: Dictionary containing the calculations per symbol
    '''
    
    # store calculations
    calculations = {}

    if len(symbols) > 0:
        # get historical prices for new symbols
        history = GetHistory(algorithm, symbols,
                            lookbackPeriod = lookback,
                            resolution = resolution)
            
        for symbol in symbols:
            # if symbol has no historical data continue the loop
            if (symbol not in history.index
            or len(history.loc[symbol]['close']) < lookback
            or history.loc[symbol].get('close') is None
            or history.loc[symbol].get('close').isna().any()):
                algorithm.Log('no history found for: ' + str(symbol.Value))
                continue

            else:
                # add symbol to calculations
                calculations[symbol] = SymbolData(symbol)
                
                try:
                    calculations[symbol].CalculateFactors(history, fundamentalDataBySymbolDict)
                except Exception as e:
                    algorithm.Log('removing from calculations due to ' + str(e))
                    calculations.pop(symbol)
                    continue
                
    return calculations
    
def GetFundamentalDataDict(algorithm, securitiesData, module = 'universe'):
    
    ''' Create a dictionary of symbols and fundamental factors ready for sorting '''

    fundamentalDataBySymbolDict = {}
    
    # loop through data and get fundamental data
    for x in securitiesData:
        if module == 'alpha':
            if not x.Symbol in algorithm.ActiveSecurities.Keys:
                continue
            fundamental = algorithm.ActiveSecurities[x.Symbol].Fundamentals
        elif module == 'universe':
            fundamental = x
        else:
            raise ValueError('module argument must be either universe or alpha')
            
        # dictionary of symbols containing factors and the direction of the factor (1 for sorting descending and -1 for sorting ascending)
        fundamentalDataBySymbolDict[x.Symbol] = {
                                                    #fundamental.ValuationRatios.BookValuePerShare: 1,
                                                    #fundamental.FinancialStatements.BalanceSheet.TotalEquity.Value: -1,
                                                    #fundamental.OperationRatios.OperationMargin.Value: 1,
                                                    #fundamental.OperationRatios.ROE.Value: 1,
                                                    #fundamental.OperationRatios.TotalAssetsGrowth.Value: 1,
                                                    #fundamental.ValuationRatios.PERatio: 1
                                                }
                                                    
        # check validity of data
        if None in list(fundamentalDataBySymbolDict[x.Symbol].keys()):
            fundamentalDataBySymbolDict.pop(x.Symbol)
                                                    
    return fundamentalDataBySymbolDict
    
def GetLongShortLists(self, algorithm, calculations):
    
    ''' Create lists of long/short stocks '''
            
    # get factors
    factorsDict = { symbol: symbolData.factorsList for symbol, symbolData in calculations.items() if symbolData.factorsList is not None }
    factorsDf = pd.DataFrame.from_dict(factorsDict, orient = 'index')
    
    # normalize factor
    normFactorsDf = factorsDf.apply(zscore)
    normFactorsDf.columns = ['Factor_' + str(x + 1) for x in normFactorsDf.columns]
    
    # combine factors using equal weighting
    #normFactorsDf['combinedFactor'] = normFactorsDf.sum(axis = 1)
    normFactorsDf['combinedFactor'] = normFactorsDf['Factor_1'] * 1 + normFactorsDf['Factor_2'] * 1
        
    # sort descending
    sortedNormFactorsDf = normFactorsDf.sort_values(by = 'combinedFactor', ascending = False) # descending
    
    # create long/short lists
    positionsEachSide = int(self.maxNumberOfPositions / 2)
    longs = list(sortedNormFactorsDf[:positionsEachSide].index)
    shorts = list(sortedNormFactorsDf[-positionsEachSide:].index)
    shorts = [x for x in shorts if x not in longs]
    
    return longs, shorts

def GetHistory(algorithm, symbols, lookbackPeriod, resolution):
    
    ''' Pull historical data in batches '''
    
    total = len(symbols)
    batchsize = 50
    
    if total <= batchsize:
        history = algorithm.History(symbols, lookbackPeriod, resolution)
    else:
        history = algorithm.History(symbols[0:batchsize], lookbackPeriod, resolution)
        for i in range(batchsize, total + 1, batchsize):
            batch = symbols[i:(i + batchsize)]
            historyTemp = algorithm.History(batch, lookbackPeriod, resolution)
            history = pd.concat([history, historyTemp])
            
    return history
    
def UpdateBenchmarkValue(self, algorithm):
        
    ''' Simulate buy and hold the Benchmark '''
    
    if self.initBenchmarkPrice == 0:
        self.initBenchmarkCash = algorithm.Portfolio.Cash
        self.initBenchmarkPrice = algorithm.Benchmark.Evaluate(algorithm.Time)
        self.benchmarkValue = self.initBenchmarkCash
    else:
        currentBenchmarkPrice = algorithm.Benchmark.Evaluate(algorithm.Time)
        self.benchmarkValue = (currentBenchmarkPrice / self.initBenchmarkPrice) * self.initBenchmarkCash
        
def UpdatePlots(self, algorithm):
    
    ''' Update Portfolio Exposure and Drawdown plots '''
    
    # simulate buy and hold the benchmark and plot its daily value --------------
    UpdateBenchmarkValue(self, algorithm)
    algorithm.Plot('Strategy Equity', self.benchmark, self.benchmarkValue)

    # get current portfolio value
    currentTotalPortfolioValue = algorithm.Portfolio.TotalPortfolioValue
    
    # plot the daily total portfolio exposure % --------------------------------
    longHoldings = sum([x.HoldingsValue for x in algorithm.Portfolio.Values if x.IsLong])
    shortHoldings = sum([x.HoldingsValue for x in algorithm.Portfolio.Values if x.IsShort])
    totalHoldings = longHoldings + shortHoldings
    totalPortfolioExposure = (totalHoldings / currentTotalPortfolioValue) * 100
    algorithm.Plot('Chart Total Portfolio Exposure %', 'Daily Portfolio Exposure %', totalPortfolioExposure)
    
    # plot the daily number of longs and shorts --------------------------------
    nLongs = sum(x.IsLong for x in algorithm.Portfolio.Values)
    nShorts = sum(x.IsShort for x in algorithm.Portfolio.Values)
    algorithm.Plot('Chart Number Of Longs/Shorts', 'Daily N Longs', nLongs)
    algorithm.Plot('Chart Number Of Longs/Shorts', 'Daily N Shorts', nShorts)
    
    # plot the drawdown % from the most recent high ---------------------------
    if not self.portfolioValueHighInitialized:
        self.portfolioHigh = currentTotalPortfolioValue # set initial portfolio value
        self.portfolioValueHighInitialized = True
        
    # update trailing high value of the portfolio
    if self.portfolioValueHigh < currentTotalPortfolioValue:
        self.portfolioValueHigh = currentTotalPortfolioValue

    currentDrawdownPercent = ((float(currentTotalPortfolioValue) / float(self.portfolioValueHigh)) - 1.0) * 100
    algorithm.Plot('Chart Drawdown %', 'Drawdown %', currentDrawdownPercent)
  • CustomEqualWeightingPortfolioConstruction.py
# region imports
from AlgorithmImports import *
# endregion
from clr import AddReference
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm.Framework")

from QuantConnect import Resolution, Extensions
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from itertools import groupby
from datetime import datetime, timedelta

class CustomEqualWeightingPortfolioConstructionModel(PortfolioConstructionModel):
    
    '''
    Description:
        Provide a custom implementation of IPortfolioConstructionModel that gives equal weighting to all active securities
    Details:
        - The target percent holdings of each security is 1/N where N is the number of securities with active Up/Down insights
        - For InsightDirection.Up, long targets are returned
        - For InsightDirection.Down, short targets are returned
        - For InsightDirection.Flat, closing position targets are returned
    '''

    def __init__(self, initialAllocationPerSecurity = 0.1, rebalancingFunc = Expiry.EndOfMonth):
        
        '''
        Description:
            Initialize a new instance of CustomEqualWeightingPortfolioConstructionModel
        Args:
            initialAllocationPerSecurity: Portfolio exposure per security (as a % of total equity)
        '''
        
        # portfolio exposure per security (as a % of total equity)
        self.initialAllocationPerSecurity = initialAllocationPerSecurity
        self.rebalancingFunc = rebalancingFunc
        
        self.insightCollection = InsightCollection()
        self.removedSymbols = []
        
        self.nextRebalance = None

    def CreateTargets(self, algorithm, insights):

        '''
        Description:
            Create portfolio targets from the specified insights
        Args:
            algorithm: The algorithm instance
            insights: The insights to create portfolio targets from
        Returns:
            An enumerable of portfolio targets to be sent to the execution model
        '''

        targets = []
            
        if len(insights) == 0:
            return targets
        
        # apply rebalancing logic
        if self.nextRebalance is not None and algorithm.Time < self.nextRebalance and len(self.removedSymbols) == 0:
            return targets
        self.nextRebalance = self.rebalancingFunc(algorithm.Time)
        
        # here we get the new insights and add them to our insight collection
        for insight in insights:
            self.insightCollection.Add(insight)
            
        # create flatten target for each security that was removed from the universe
        if len(self.removedSymbols) > 0:
            universeDeselectionTargets = [ PortfolioTarget(symbol, 0) for symbol in self.removedSymbols ]
            targets.extend(universeDeselectionTargets)
            algorithm.Log('(Portfolio module) liquidating: ' + str([x.Value for x in self.removedSymbols]))
            self.removedSymbols = []

        # get insight that have not expired of each symbol that is still in the universe
        activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime)

        # get the last generated active insight for each symbol
        lastActiveInsights = []
        for symbol, g in groupby(activeInsights, lambda x: x.Symbol):
            lastActiveInsights.append(sorted(g, key = lambda x: x.GeneratedTimeUtc)[-1])
        
        # determine target percent for the given insights
        for insight in lastActiveInsights:
            allocationPercent = self.initialAllocationPerSecurity * insight.Direction
            target = PortfolioTarget.Percent(algorithm, insight.Symbol, allocationPercent)
            targets.append(target)
            
        return targets
        
    def OnSecuritiesChanged(self, algorithm, changes):
        
        '''
        Description:
            Event fired each time the we add/remove securities from the data feed
        Args:
            algorithm: The algorithm instance that experienced the change in securities
            changes: The security additions and removals from the algorithm
        '''
        
        newRemovedSymbols = [x.Symbol for x in changes.RemovedSecurities if x.Symbol not in self.removedSymbols]
        
        # get removed symbol and invalidate them in the insight collection
        self.removedSymbols.extend(newRemovedSymbols)
        self.insightCollection.Clear(self.removedSymbols)
            
        removedList = [x.Value for x in self.removedSymbols]
        algorithm.Log('(Portfolio module) securities removed from Universe: ' + str(removedList))
  • FactorModelUniverseSelection.py
# region imports
from AlgorithmImports import *
# endregion
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Algorithm.Framework")

from QuantConnect.Data.UniverseSelection import *
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel

from Helpers.HelperFunctions import GetFundamentalDataDict, MakeCalculations, GetLongShortLists, UpdatePlots
import pandas as pd
import numpy as np

class FactorModelUniverseSelectionModel(FundamentalUniverseSelectionModel):

    def __init__(self,
                benchmark = 'STK000000',
                nStocks = 500,
                lookback = 252,
                maxNumberOfPositions = 20,
                rebalancingFunc = Expiry.EndOfMonth,
                filterFineData = True,
                universeSettings = None,
                securityInitializer = None):
        
        self.benchmark = benchmark
        
        self.nStocks = nStocks
        self.lookback = lookback
        self.maxNumberOfPositions = maxNumberOfPositions

        self.rebalancingFunc = rebalancingFunc
        self.nextRebalance = None
        
        self.initBenchmarkPrice = 0
        self.portfolioValueHigh = 0 # initialize portfolioValueHigh for drawdown calculation
        self.portfolioValueHighInitialized = False # initialize portfolioValueHighInitialized for drawdown calculation
        
        # super().__init__(filterFineData, universeSettings, securityInitializer)
        super().__init__(filterFineData, universeSettings)

    def SelectCoarse(self, algorithm, coarse):
        
        ''' Perform Universe selection based on price and volume '''
        
        # update plots -----------------------------------------------------------------------------------------------
        UpdatePlots(self, algorithm)
        
        # rebalancing logic -------------------------------------------------------------------------------------------
        if self.nextRebalance is not None and algorithm.Time < self.nextRebalance:
            return Universe.Unchanged
        self.nextRebalance = self.rebalancingFunc(algorithm.Time)
        
        # get new coarse candidates -----------------------------------------------------------------------------------

        # filtered by price and select the top dollar volume stocks
        filteredCoarse = [x for x in coarse if x.HasFundamentalData]
        sortedDollarVolume = sorted(filteredCoarse, key = lambda x: x.DollarVolume, reverse = True)
        coarseSymbols = [x.Symbol for x in sortedDollarVolume][:(self.nStocks * 2)]
        
        return coarseSymbols
        
    def SelectFine(self, algorithm, fine):
        
        ''' Select securities based on fundamental factor modelling '''
        
        sortedMarketCap = sorted(fine, key = lambda x: x.MarketCap, reverse = True)[:self.nStocks]

        # generate dictionary with factors -----------------------------------------------------------------------------
        fundamentalDataBySymbolDict = GetFundamentalDataDict(algorithm, sortedMarketCap, module = 'universe')
                    
        # make calculations to create long/short lists -----------------------------------------------------------------
        fineSymbols = list(fundamentalDataBySymbolDict.keys())
        calculations = MakeCalculations(algorithm, fineSymbols, self.lookback, Resolution.Daily, fundamentalDataBySymbolDict)
        
        # get long/short lists of symbols
        longs, shorts = GetLongShortLists(self, algorithm, calculations)
        finalSymbols = longs + shorts

        return finalSymbols
  • main.py
# region imports
from AlgorithmImports import *
# endregion
### PRODUCT INFORMATION --------------------------------------------------------------------------------
# Copyright Emilio Freire Bauzano
# Use entirely at your own risk.
# This algorithm contains open source code from other sources and no claim is being made to such code.
# Do not remove this copyright notice.
### ----------------------------------------------------------------------------------------------------

from Universe.FactorModelUniverseSelection import FactorModelUniverseSelectionModel
from Alpha.LongShortAlphaCreation import LongShortAlphaCreationModel
from Portfolio.CustomEqualWeightingPortfolioConstruction import CustomEqualWeightingPortfolioConstructionModel

class LongShortEquityFrameworkAlgorithm(QCAlgorithmFramework):
    
    '''
    Trading Logic:
        Long-Short Equity Strategy using factor modelling
    Modules:
        Universe:
            - Final selection based on factor modelling:
                Combination of technical and fundamental factors
            - Long the Top N stocks
            - Short the Bottom N stocks
        Alpha: Creation of Up/Down Insights at the Market Open:
            - Up Insights (to go Long)
            - Down Insights (to go Short)
        Portfolio:
            Equal-Weighting Portfolio with monthly rebalancing
        Execution:
            Immediate Execution with Market Orders
        Risk:
            Null
    '''

    def Initialize(self):
        
        ### user-defined inputs ---------------------------------------------------------------------------

        self.SetStartDate(2018, 1, 1)   # set start date
        self.SetEndDate(2020, 10, 1)    # set end date
        self.SetCash(1000000)           # set strategy cash
        
        # select benchmark ticker
        benchmark = 'STK000000'
        
        # date rule for rebalancing our portfolio by updating long-short positions based on factor values
        rebalancingFunc = Expiry.EndOfMonth
        
        # number of stocks to keep for factor modelling calculations
        nStocks = 100
        
        # number of positions to hold on each side (long/short)
        positionsOnEachSide = 20
        
        # lookback for historical data to calculate factors
        lookback = 252
        
        # select the leverage factor
        leverageFactor = 1
        
        ### --------------------------------------------------------------------------------------------------
        
        # calculate initialAllocationPerSecurity and maxNumberOfPositions
        initialAllocationPerSecurity = (1 / positionsOnEachSide) * leverageFactor
        maxNumberOfPositions = positionsOnEachSide * 2
        
        # set requested data resolution
        self.UniverseSettings.Resolution = Resolution.Hour
        # add leverage to new securities (this does not add leverage to current holdings in the account)
        leverageNeeded = max(1, maxNumberOfPositions * initialAllocationPerSecurity * leverageFactor)
        self.UniverseSettings.Leverage = leverageNeeded + 1
        
        # let's plot the series of daily total portfolio exposure %
        portfolioExposurePlot = Chart('Chart Total Portfolio Exposure %')
        portfolioExposurePlot.AddSeries(Series('Daily Portfolio Exposure %', SeriesType.Line, ''))
        self.AddChart(portfolioExposurePlot)
        
        # let's plot the series of daily number of open longs and shorts
        nLongShortPlot = Chart('Chart Number Of Longs/Shorts')
        nLongShortPlot.AddSeries(Series('Daily N Longs', SeriesType.Line, ''))
        nLongShortPlot.AddSeries(Series('Daily N Shorts', SeriesType.Line, ''))
        self.AddChart(nLongShortPlot)
        
        # let's plot the series of drawdown % from the most recent high
        drawdownPlot = Chart('Chart Drawdown %')
        drawdownPlot.AddSeries(Series('Drawdown %', SeriesType.Line, '%'))
        self.AddChart(drawdownPlot)
        
        # add benchmark
        self.SetBenchmark(benchmark)
        
        # select modules                               
        self.SetUniverseSelection(FactorModelUniverseSelectionModel(benchmark = benchmark,
                                                                    nStocks = nStocks,
                                                                    lookback = lookback,
                                                                    maxNumberOfPositions = maxNumberOfPositions,
                                                                    rebalancingFunc = rebalancingFunc))
        
        self.SetAlpha(LongShortAlphaCreationModel(maxNumberOfPositions = maxNumberOfPositions, lookback = lookback))
        
        self.SetPortfolioConstruction(CustomEqualWeightingPortfolioConstructionModel(initialAllocationPerSecurity = initialAllocationPerSecurity,
                                                                                    rebalancingFunc = rebalancingFunc))
                                                                                    
        self.SetExecution(ImmediateExecutionModel())
        
        self.SetRiskManagement(NullRiskManagementModel())
  • ResearchFactorAnalysis.py
# region imports
from AlgorithmImports import *
# endregion
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
import scipy.stats as stats
import pandas as pd
import numpy as np

import seaborn as sns
sns.set_style('darkgrid')
pd.plotting.register_matplotlib_converters()

from datetime import timedelta

class FactorAnalysis:
    
    def __init__(self, qb, tickers, startDate, endDate, resolution):
        
        # add symbols
        symbols = [qb.AddEquity(ticker, resolution).Symbol for ticker in tickers]
        
        # get historical data at initialization ----------------------------------------------------------
        ohlcvDf = qb.History(symbols, startDate, endDate, resolution)
        # when using daily resolution, QuantConnect uses the date at midnight after the trading day
        # hence skipping Mondays and showing Saturdays. We avoid this by subtracting one day from the index
        ohlcvDf.index = ohlcvDf.index.set_levels(ohlcvDf.index.levels[1] - timedelta(1), level = 'time')
        
        self.ohlcvDf = ohlcvDf.dropna()
        
    def GetFactorsDf(self, fct = None):

        '''
        Description:
            Apply a function to a MultiIndex Dataframe of historical data
            Group on symbol first to get a ohlcv series per symbol, and apply a custom function to it
            in order to get a factor value per symbol and day
        Args:
            fct: Function to calculate the custom factor
        Returns:
            MultiIndex Dataframe (symbol/time indexes) with the factor values
        '''
        
        if fct is None:
            raise ValueError('fct arguments needs to be provided to calculate factors')
        
        # group by symbol to get a timeseries of historical data per symbol and apply CustomFactor function
        factorsDf = self.ohlcvDf.groupby('symbol', group_keys = False).apply(lambda x: fct(x)).dropna()
        factorsDf.columns = ['Factor_' + str(i + 1) for i in range(len(factorsDf.columns))]
        # sort indexes
        factorsDf = factorsDf.sort_index(level = ['symbol', 'time'])

        return factorsDf
    
    def GetStandardizedFactorsDf(self, factorsDf):
        
        '''
        Description:
            Winsorize and standardize factors
        Args:
            factorsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values
        Returns:
            MultiIndex Dataframe (symbol/time indexes) with standardized factor values
        '''
        
        # winsorization
        winsorizedFactorsDf = factorsDf.apply(stats.mstats.winsorize, limits = [0.025, 0.025])
        # zscore standardization
        standardizedFactorsDf = winsorizedFactorsDf.apply(stats.zscore)
        
        return standardizedFactorsDf
    
    def GetCombinedFactorsDf(self, factorsDf, combinedFactorWeightsDict = None):
        
        '''
        Description:
            Create a combined factor as a linear combination of individual factors
        Args:
            factorsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values
            combinedFactorWeightsDict: Dictionary with factor names and weights to calculate a combined factor
        Returns:
            MultiIndex Dataframe (symbol/time indexes) with the individual factors and the combined factor
        '''
        
        # make a deep copy of the DataFrame
        combinedFactorsDf = factorsDf.copy(deep = True)
        
        # calculate a combined factor
        if combinedFactorWeightsDict is None:
            return combinedFactorsDf
        elif not combinedFactorWeightsDict:
            combinedFactorsDf['Combined_Factor'] = combinedFactorsDf.sum(axis = 1)
        else:
            combinedFactorsDf['Combined_Factor'] = sum(combinedFactorsDf[key] * value
                                                       for key, value in combinedFactorWeightsDict.items())
        
        return combinedFactorsDf
    
    def GetFinalFactorsDf(self, fct = None, combinedFactorWeightsDict = None, standardize = True):

        '''
        Description:
            - Apply a function to a MultiIndex Dataframe of historical data
              Group on symbol first to get a ohlcv series per symbol, and apply a custom function to it
                  in order to get a factor value per symbol and day
            - If required, standardize the factors and remove potential outliers
            - If required, add a combined factor as a linear combination of individual factors
        Args:
            fct: Function to calculate the custom factor
            standardize: Boolean to standardize data
            combinedFactorWeightsDict: Dictionary with factor names and weights to calculate a combined factor
        Returns:
            MultiIndex Dataframe (symbol/time indexes) with the factor values
        '''
        
        # get factorsDf
        factorsDf = self.GetFactorsDf(fct)
        
        # standardize
        if standardize:
            factorsDf = self.GetStandardizedFactorsDf(factorsDf)
        
        # add combined factor
        if combinedFactorWeightsDict is not None:
            factorsDf = self.GetCombinedFactorsDf(factorsDf, combinedFactorWeightsDict)

        return factorsDf
    
    def GetPricesDf(self, field = 'close'):
    
        '''
        Description:
            Get a MultiIndex Dataframe of chosen field
        Args:
            field: open, high, low, close or volume
        Returns:
            MultiIndex Dataframe (symbol/time indexes) with the chosen field
        '''
        
        # select only chose field and turn into a dataframe
        pricesDf = self.ohlcvDf[field].to_frame()
        pricesDf.columns = ['price']
        # forward fill nas and after that drop rows with some nas left
        pricesDf = pricesDf.sort_index(level = ['symbol', 'time'])
        pricesDf = pricesDf.groupby('symbol').fillna(method = 'ffill').dropna()

        return pricesDf
    
    def GetFactorsPricesDf(self, factorsDf, field = 'close'):
    
        '''
        Description:
            Get a MultiIndex Dataframe (symbol/time indexes) with all the factors and chosen prices
        Args:
            factorsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values
            field: open, high, low, close or volume
        Returns:
            MultiIndex Dataframe (symbol/time indexes) with all the factors and chosen prices
        '''

        # get the pricesDf
        pricesDf = self.GetPricesDf(field)
        
        # merge factorsDf and pricesDf and fill forward nans by symbol
        factorsPricesDf = pd.merge(factorsDf, pricesDf, how = 'right', left_index = True, right_index = True)
        factorsPricesDf = factorsPricesDf.sort_index(level = ['symbol', 'time'])
        factorsPricesDf = factorsPricesDf.groupby('symbol').fillna(method = 'ffill').dropna()

        return factorsPricesDf
    
    def GetFactorsForwardReturnsDf(self, factorsPricesDf, forwardPeriods = [1, 5, 21]):
    
        '''
        Description:
            Generate a MultiIndex Dataframe (symbol/time indexes) with all previous info plus forward returns
        Args:
            factorsPricesDf:  MultiIndex Dataframe (symbol/time indexes) with all the factors and chosen prices
            forwardPeriods: List of integers defining the different periods for forward returns
        Returns:
            MultiIndex Dataframe (symbol/time indexes) with the factor values and forward returns
        '''

        # make sure 1 day forward returns are calculated even if not provided by user
        if 1 not in forwardPeriods:
            forwardPeriods.append(1)
        
        # calculate forward returns per period
        for period in forwardPeriods:
            factorsPricesDf[str(period) + 'D'] = (factorsPricesDf.groupby('symbol', group_keys = False)
                                                    .apply(lambda x: x['price'].pct_change(period).shift(-period)))
        
        # drop column price
        factorsForwardReturnsDf = factorsPricesDf.dropna().drop('price', axis = 1)

        return factorsForwardReturnsDf
    
    def GetFactorQuantilesForwardReturnsDf(self, factorsDf, field = 'close',
                                            forwardPeriods = [1, 5, 21],
                                            factor = 'Factor_1', q = 5):
        
        '''
        Description:
            Create a MultiIndex Dataframe (symbol/time indexes) with the factor values,
            forward returns and the quantile groups
        Args:
            factorsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values
            field: open, high, low, close or volume
            forwardPeriods: List of integers defining the different periods for forward returns
            factor: Chosen factor to create quantiles for
            q: Number of quantile groups
        Returns:
            MultiIndex Dataframe (symbol/time indexes) with the factor values, forward returns and the quantile groups
        '''
        
        # get factorsForwardReturnsDf
        factorsPricesDf = self.GetFactorsPricesDf(factorsDf, field)
        factorsForwardReturnsDf = self.GetFactorsForwardReturnsDf(factorsPricesDf, forwardPeriods)
        
        # reorder index levels to have time and then symbols so we can then create quantiles per day
        factorsForwardReturnsDf = factorsForwardReturnsDf.reorder_levels(['time', 'symbol'])
        factorsForwardReturnsDf = factorsForwardReturnsDf.sort_index(level = ['time', 'symbol'])
        
        # calculate quintiles given the chosen factor and rename columns
        factorsForwardReturnsDf['Quantile'] = factorsForwardReturnsDf[factor].groupby('time').apply(lambda x: pd.qcut(x, q, labels = False, duplicates = 'drop')).add(1)
        factorsForwardReturnsDf['Quantile'] = 'Group_' + factorsForwardReturnsDf['Quantile'].astype(str)
        
        # remove the other factor columns
        factorCols = [x for x in factorsForwardReturnsDf.columns if 'Factor' not in x or x == factor]
        factorQuantilesForwardReturnsDf = factorsForwardReturnsDf[factorCols]
        
        return factorQuantilesForwardReturnsDf
    
    def GetReturnsByQuantileDf(self, factorQuantilesForwardReturnsDf, forwardPeriod = 1, weighting = 'mean'):
    
        '''
        Description:
            Generate a SingleIndex Dataframe with period forward returns by quantile and time
        Args:
            factorQuantilesForwardReturnsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values,
                                             forward returns and the quantile groups
            forwardPeriod: The period of forward returns
            weighting: The weighting to apply to the returns in each quantile after grouping:
                        - mean: Take the average of all the stock returns within each quantile
                        - factor: Take a factor-weighted return within each quantile
        Returns:
            SingleIndex Dataframe with period forward returns by quantile and time
        '''

        # we drop the symbols and convert to a MultiIndex Dataframe with Quantile and time as indexes and forward returns
        df = factorQuantilesForwardReturnsDf.droplevel(['symbol'])
        df.set_index('Quantile', append = True, inplace = True)
        df = df.reorder_levels(['Quantile', 'time'])
        df = df.sort_index(level = ['Quantile', 'time'])
        
        # get the column name for the factor and period
        factorCol = [x for x in df.columns if 'Factor' in x][0]
        periodCol = [str(forwardPeriod) + 'D'][0]
        
        if weighting == 'mean':
            df = df[[periodCol]]
            # group by Quantile and time and get the mean returns (equal weight across all stocks within each quantiles)
            returnsByQuantileDf = df.groupby(['Quantile', 'time']).mean()
        elif weighting == 'factor':
            relevantCols = [factorCol, periodCol]
            df = df[relevantCols]
            # group by Quantile and time and create a column with weights based on factor values
            df['Factor_Weights'] = (df.groupby(['Quantile', 'time'], group_keys = False)
                                    .apply(lambda x: x[factorCol].abs() / x[factorCol].abs().sum()))
            # group by Quantile and time and calculate the factor weighted average returns
            returnsByQuantileDf = (df.groupby(['Quantile', 'time'], group_keys = False)
                                    .apply(lambda x: (x['Factor_Weights'] * x[periodCol]).sum())).to_frame()

        # unstack to convert to SingleIndex Dataframe
        returnsByQuantileDf = returnsByQuantileDf.unstack(0).fillna(0)
        returnsByQuantileDf.columns = returnsByQuantileDf.columns.droplevel(0)
        returnsByQuantileDf.columns.name = None
        
        # finally keep every nth row to match with the forward period returns
        returnsByQuantileDf = returnsByQuantileDf.iloc[::forwardPeriod, :]

        return returnsByQuantileDf
    
    def GetMeanReturnsByQuantileDf(self, factorQuantilesForwardReturnsDf):
            
        '''
        Description:
            Generate a SingleIndex Dataframe with mean returns by quantile and time
        Args:
            factorQuantilesForwardReturnsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values,
                                             forward returns and the quantile groups
        Returns:
            SingleIndex Dataframe with mean returns by quantile and time
        '''
    
        # remove factor columns, group by quantile and take the average return
        factorCol = [x for x in factorQuantilesForwardReturnsDf.columns if 'Factor' in x]
        quantileMeanReturn = factorQuantilesForwardReturnsDf.drop(factorCol, axis = 1).groupby('Quantile').mean()
        
        return quantileMeanReturn
    
    def GetPortfolioLongShortReturnsDf(self, returnsByQuantileDf, portfolioWeightsDict = None):
        
        '''
        Description:
            Generate a SingleIndex Dataframe with the returns of a Long-Short portfolio
        Args:
            returnsByQuantileDf: SingleIndex Dataframe with period forward returns by quantile and time
            portfolioWeightsDict: Dictionary with quantiles and weights to create a portfolio of returns
        Returns:
            SingleIndex Dataframe with the returns of Long-Short portfolio
        '''
        
        # if no portfolioWeightsDict are provided, create a default one
        # going 100% long top quintile and 100% short bottom quintile
        if portfolioWeightsDict is None:
            quantileGroups = sorted(list(returnsByQuantileDf.columns))
            topQuantile = quantileGroups[-1]
            bottomQuantile = quantileGroups[0]
            portfolioWeightsDict = {topQuantile: 1, bottomQuantile: -1}
        
        # we calculate the weighted average portfolio returns based on given weights for each quintile
        col = list(portfolioWeightsDict.keys())
        portfolioLongShortReturnsDf = returnsByQuantileDf.loc[: , col]
        portfolioLongShortReturnsDf[col[0]] = portfolioLongShortReturnsDf[col[0]] * portfolioWeightsDict[col[0]]
        portfolioLongShortReturnsDf[col[1]] = portfolioLongShortReturnsDf[col[1]] * portfolioWeightsDict[col[1]]
        portfolioLongShortReturnsDf['Strategy'] = portfolioLongShortReturnsDf.sum(axis = 1)
        portfolioLongShortReturnsDf = portfolioLongShortReturnsDf[['Strategy']]

        return portfolioLongShortReturnsDf
    
    def GetCumulativeReturnsDf(self, returnsDf):
        
        '''
        Description:
            Convert a DataFrame of returns into a DataFrame of cumulative returns
        Args:
            returnsDf: SingleIndex Dataframe with returns
        Returns:
            SingleIndex Dataframe with cumulative returns
        '''
        
        cumulativeReturnsDf = returnsDf.add(1).cumprod().add(-1)

        return cumulativeReturnsDf

    # ploting functions -----------------------------------------------------------------------------------------
    
    def PlotFactorsCorrMatrix(self, factorsDf):
        
        '''
        Description:
            Plot the factors correlation matrix
        Args:
            factorsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values
        Returns:
            Plot the factors correlation matrix
        '''

        corrMatrix = round(factorsDf.corr(), 2)
        
        nCol = len(list(factorsDf.columns))
        plt.subplots(figsize = (nCol, nCol))
        sns.heatmap(corrMatrix, annot = True)
        
        plt.show()
        
    def PlotHistograms(self, factorsDf):
        
        '''
        Description:
            Plot the histogram for each factor
        Args:
            factorsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values
        Returns:
            Plot the histogram for each factor
        '''
        
        nCol = len(list(factorsDf.columns))
        factorsDf.hist(figsize = (nCol * 3, nCol * 2), bins = 50)
        
        plt.show()
        
    def PlotBoxPlotQuantilesCount(self, factorQuantilesForwardReturnsDf):
                
        '''
        Description:
            Plot a box plot with the distributions of number of stocks in each quintile.
            The objective is to make sure each quintile has an almost equal number of stocks most of the time
        Args:
            factorQuantilesForwardReturnsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values,
                                             forward returns and the quantile groups
        Returns:
            Plot a box plot with the distributions of number of stocks in each quintile
        '''
        
        factorCol = [x for x in factorQuantilesForwardReturnsDf.columns if 'Factor' in x]
        df = factorQuantilesForwardReturnsDf.groupby(['Quantile', 'time'])[factorCol].count()
        df = df.unstack(0)
        df.columns = df.columns.droplevel(0)
        df.name = None
        
        ax = sns.boxplot(data = df, width = 0.5, palette = "colorblind", orient = 'h')
        ax.set_title('Distribution Of Number Of Assets Within Quintiles')
        
        plt.show()
    
    def PlotMeanReturnsByQuantile(self, factorQuantilesForwardReturnsDf):
        
        '''
        Description:
            Plot the mean return for each quantile group and forward return period
        Args:
            factorQuantilesForwardReturnsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values,
                                             forward returns and the quantile groups
        Returns:
            Plot with the mean return for each quantile group and forward return period
        '''
        
        meanReturnsByQuantileDf = self.GetMeanReturnsByQuantileDf(factorQuantilesForwardReturnsDf)
        # plot
        ax = meanReturnsByQuantileDf.plot(kind = 'bar', figsize = (12, 5))
        ax.set_title('Mean Returns By Quantile Group And Forward Period Return', fontdict = {'fontsize': 15})
        ax.yaxis.set_major_formatter(mtick.PercentFormatter(1.0))
        
        plt.show()    
        
    def PlotCumulativeReturnsByQuantile(self, factorQuantilesForwardReturnsDf,
                                        forwardPeriod = 1, weighting = 'mean'):
        
        '''
        Description:
            Plot cumulative returns per quantile group
        Args:
            factorQuantilesForwardReturnsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values,
                                             forward returns and the quantile groups
            forwardPeriod: The period of forward returns
            weighting: The weighting to apply to the returns in each quantile after grouping:
                        - mean: Take the average of all the stock returns within each quantile
                        - factor: Take a factor-weighted return within each quantile
        Returns:
            Plot with the cumulative returns per quantile group
        '''
        
        # get returns by quantile
        returnsByQuantileDf = self.GetReturnsByQuantileDf(factorQuantilesForwardReturnsDf, forwardPeriod, weighting)
        cumulativeReturnsByQuantileDf = self.GetCumulativeReturnsDf(returnsByQuantileDf)
        
        # take logarithm for better visualization
        cumulativeReturnsByQuantileDf = np.log(1 + cumulativeReturnsByQuantileDf)
        
        # get the relevant columns
        colTop = cumulativeReturnsByQuantileDf.iloc[:, [-1]].columns[0]
        colBottom = cumulativeReturnsByQuantileDf.iloc[:, [0]].columns[0]
        colMiddle = cumulativeReturnsByQuantileDf.drop([colTop, colBottom], axis = 1).columns
        
        # plot
        fig, ax = plt.subplots(figsize = (12, 5))
        ax.plot(cumulativeReturnsByQuantileDf[colBottom], color = 'red', linewidth = 2)
        ax.plot(cumulativeReturnsByQuantileDf[colMiddle], alpha = 0.3)
        ax.plot(cumulativeReturnsByQuantileDf[colTop], color = 'green', linewidth = 2)
        # formatting
        ax.axhline(y = 0, color = 'black', linestyle = '--', linewidth = 0.5)
        ax.set_title('Cumulative Log-Returns By Quantile Group', fontdict = {'fontsize': 15})
        ax.yaxis.set_major_formatter(mtick.PercentFormatter(1.0))
        ax.legend(cumulativeReturnsByQuantileDf.columns, loc = 'best')
        
        plt.show()
        
    def PlotPortfolioLongShortCumulativeReturns(self, factorQuantilesForwardReturnsDf,
                                                forwardPeriod = 1, weighting = 'mean',
                                                portfolioWeightsDict = None):

        '''
        Description:
            Plot cumulative returns for a long-short portfolio
        Args:
            factorQuantilesForwardReturnsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values,
                                             forward returns and the quantile groups
            forwardPeriod: The period of forward returns
            weighting: The weighting to apply to the returns in each quantile after grouping:
                        - mean: Take the average of all the stock returns within each quantile
                        - factor: Take a factor-weighted return within each quantile
        Returns:
            Plot cumulative returns for a long-short portfolio
        '''
        
        # get returns by quantile
        returnsByQuantileDf = self.GetReturnsByQuantileDf(factorQuantilesForwardReturnsDf, forwardPeriod, weighting)
        # calculate returns for a long-short portolio
        portfolioLongShortReturnsDf = self.GetPortfolioLongShortReturnsDf(returnsByQuantileDf, portfolioWeightsDict)
        portfolioLongShortCumulativeReturnsDf = self.GetCumulativeReturnsDf(portfolioLongShortReturnsDf)
        
        # prepare plot
        fig, ax = plt.subplots(figsize = (12, 5))
        
        # plot portfolio
        colPortfolio = portfolioLongShortCumulativeReturnsDf.iloc[:, [0]].columns[0]
        ax.plot(portfolioLongShortCumulativeReturnsDf[colPortfolio], color = 'black', linewidth = 2)
            
        if len(portfolioLongShortCumulativeReturnsDf.columns) > 1:
            colFactors = portfolioLongShortCumulativeReturnsDf.iloc[:, 1:].columns
            # plot factors
            ax.plot(portfolioLongShortCumulativeReturnsDf[colFactors], alpha = 0.3)
            
        # formatting
        ax.axhline(y = 0, color = 'black', linestyle = '--', linewidth = 0.5)
        ax.set_title('Cumulative Returns Long-Short Portfolio', fontdict = {'fontsize': 15})
        ax.yaxis.set_major_formatter(mtick.PercentFormatter(1.0))
        ax.legend(portfolioLongShortCumulativeReturnsDf.columns, loc = 'best')
        
        plt.show()
        
    def PlotIC(self, factorQuantilesForwardReturnsDf):
        
        '''
        Description:
            Plot the Information Coefficient (Spearman Rank Correlation) for different periods along with a moving average
        Args:
            factorQuantilesForwardReturnsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values,
                                             forward returns and the quantile groups
        Returns:
            Plot of the Information Coefficient (Spearman Rank Correlation) for different periods along with a moving average
        '''
        
        # get the forward periods and factor columns
        forwardPeriods = [int(x.split('D', 1)[0]) for x in factorQuantilesForwardReturnsDf.columns if 'D' in x]
        factorCol = [x for x in factorQuantilesForwardReturnsDf.columns if 'Factor' in x]
        
        # iterate over the periods
        for period in forwardPeriods:
            col = str(period) + 'D'
            # calculate the spearman rank coefficient for each day between the factor values and forward returns
            icDf = (factorQuantilesForwardReturnsDf.groupby('time')
                    .apply(lambda x: stats.spearmanr(x[factorCol], x[col])[0]).to_frame().dropna())
            icDf.columns = ['IC']
            # apply a moving average for smoothing
            icDf['21D Moving Average'] = icDf.rolling(21).apply(lambda x: np.mean(x))
            
            # plot
            fig, ax = plt.subplots(figsize = (12, 5))
            ax.plot(icDf['IC'], alpha = 0.5)
            ax.plot(icDf['21D Moving Average'])
            ax.axhline(y = 0, color = 'black', linestyle = '--', linewidth = 0.5)

            mu = icDf['IC'].mean()
            sigma = icDf['IC'].std()
            textstr = '\n'.join((
                        r'$\mu=%.2f$' % (mu, ),
                        r'$\sigma=%.2f$' % (sigma, )))
            props = dict(boxstyle = 'round', facecolor = 'white', alpha = 0.5)
            ax.text(0.05, 0.95, textstr, transform = ax.transAxes, fontsize = 14,
                    verticalalignment = 'top', bbox = props)

            ax.set_title(col + ' Forward Return Information Coefficient (IC)', fontdict = {'fontsize': 15})
            ax.legend(icDf.columns, loc = 'upper right')
            
            plt.show()
            
    # run full factor analysis --------------------------------------------------------------------------------------
    
    def RunFactorAnalysis(self, factorQuantilesForwardReturnsDf, forwardPeriod = 1,
                        weighting = 'mean', portfolioWeightsDict = None, makePlots = True):
                                
        '''
        Description:
            Run all needed functions and generate relevant DataFrames and plots for analysis
        Args:
            factorQuantilesForwardReturnsDf: MultiIndex Dataframe (symbol/time indexes) with the factor values,
                                             forward returns and the quantile groups
            forwardPeriod: The period of forward returns
            weighting: The weighting to apply to the returns in each quantile after grouping:
                        - mean: Take the average of all the stock returns within each quantile
                        - factor: Take a factor-weighted return within each quantile
            portfolioWeightsDict: Dictionary with quantiles and weights to create a portfolio of returns
        Returns:
            Plots for factor analysis
        '''
        
        # plotting
        if makePlots:
            self.PlotMeanReturnsByQuantile(factorQuantilesForwardReturnsDf)
            self.PlotCumulativeReturnsByQuantile(factorQuantilesForwardReturnsDf)
            self.PlotPortfolioLongShortCumulativeReturns(factorQuantilesForwardReturnsDf)
            self.PlotIC(factorQuantilesForwardReturnsDf)
        
        # keep DataFrames
        self.returnsByQuantileDf = self.GetReturnsByQuantileDf(factorQuantilesForwardReturnsDf, forwardPeriod, weighting)
        self.cumulativeReturnsByQuantileDf = self.GetCumulativeReturnsDf(self.returnsByQuantileDf)
        self.portfolioLongShortReturnsDf = self.GetPortfolioLongShortReturnsDf(self.returnsByQuantileDf, portfolioWeightsDict)
        self.portfolioLongShortCumulativeReturnsDf = self.GetCumulativeReturnsDf(self.portfolioLongShortReturnsDf)
  • ResearchRiskAnalysis.py
# region imports
from AlgorithmImports import *
# endregion
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
import statsmodels.api as sm
import pandas as pd
import numpy as np

import seaborn as sns
sns.set_style('darkgrid')
pd.plotting.register_matplotlib_converters()

from statsmodels.regression.rolling import RollingOLS
from io import StringIO

class RiskAnalysis:
    
    def __init__(self, qb):
        
        # get Fama-French and industry factors
        industryFactorsUrl = 'https://www.dropbox.com/s/24bjtztzglo3eyf/12_Industry_Portfolios_Daily.CSV?dl=1'
        ffFiveFactorsUrl = 'https://www.dropbox.com/s/88m1nohi597et20/F-F_Research_Data_5_Factors_2x3_daily.CSV?dl=1'
        self.industryFactorsDf = self.GetExternalFactorsDf(qb, industryFactorsUrl)
        self.ffFiveFactorsDf = self.GetExternalFactorsDf(qb, ffFiveFactorsUrl)
        
    def GetExternalFactorsDf(self, qb, url):
        
        '''
        Description:
            Download a DataFrame with data from external sources
        Args:
            qb: QuantBook
            url: URL for the data source
        Returns:
            SingleIndex Dataframe
        '''
    
        strFile = qb.Download(url)
        df = pd.read_csv(StringIO(strFile), sep = ',')
        df['Date'] = pd.to_datetime(df['Date'], format = '%Y%m%d')
        df.set_index('Date', inplace = True)
        df = df.div(100)
        df.drop('RF', axis = 1, errors = 'ignore', inplace = True)

        return df
    
    def GetCombinedReturnsDf(self, returnsDf, externalFactorsDf = None):
        
        '''
        Description:
            Merge two DataFrames
        Args:
            returnsDf: SingleIndex Dataframe with returns from our strategy
            externalFactorsDf: SingleIndex Dataframe with returns from external factors
        Returns:
            SingleIndex Dataframe with returns
        '''
        
        # if no externalFactorsDf is provided, use the default Fama-French Five Factors
        if externalFactorsDf is None:
            externalFactorsDf = self.ffFiveFactorsDf
        
        # merge returnsDf with externalFactorsDf
        combinedReturnsDf = pd.merge(returnsDf, externalFactorsDf, left_index = True, right_index = True)
        
        return combinedReturnsDf
    
    def GetCumulativeReturnsDf(self, returnsDf):
        
        '''
        Description:
            Convert a DataFrame of returns into a DataFrame of cumulative returns
        Args:
            returnsDf: SingleIndex Dataframe with returns
        Returns:
            SingleIndex Dataframe with cumulative returns
        '''
        
        cumulativeReturnsDf = returnsDf.add(1).cumprod().add(-1)
        
        return cumulativeReturnsDf
        
    def RunRegression(self, returnsDf, dependentColumn = 'Strategy'):
        
        '''
        Description:
            Run Regression using the dependentColumn against the rest of the columns
        Args:
            returnsDf: SingleIndex Dataframe with returns
            dependentColumn: Name for the column to be used as dependent variable
        Returns:
            Summary of the model
        '''
        
        # create variables
        Y = returnsDf[[dependentColumn]]
        X = returnsDf[[x for x in returnsDf.columns if x != dependentColumn]]
        # adding a constant
        X = sm.add_constant(X)

        # fit regression model
        model = sm.OLS(Y, X).fit()
        
        # show summary from the model
        print(model.summary())
        
        return model
        
    def RunRollingRegression(self, returnsDf, dependentColumn = 'Strategy', lookback = 126):
        
        '''
        Description:
            Run Rolling Regression using the dependentColumn against the rest of the columns
        Args:
            returnsDf: SingleIndex Dataframe with returns
            dependentColumn: Name for the column to be used as dependent variable
            lookback: Number of observations for the lookback window
        Returns:
            Rolling Regression Model
        '''
        
        endog = returnsDf[[dependentColumn]]
        exogVariables = [x for x in returnsDf.columns if x != dependentColumn]
        exog = sm.add_constant(returnsDf[exogVariables])
        rollingModel = RollingOLS(endog, exog, window = lookback).fit()
        
        return rollingModel

    # ploting functions -----------------------------------------------------------------------------------------
        
    def PlotCumulativeReturns(self, returnsDf):

        '''
        Description:
            Plot cumulative returns
        Args:
            returnsDf: SingleIndex Dataframe with returns
        Returns:
            Plot cumulative returns
        '''
        
        # calculate cumulative returns
        cumulativeReturnsDf = self.GetCumulativeReturnsDf(returnsDf)
        # take logarithm for better visualization
        cumulativeReturnsDf = np.log(1 + cumulativeReturnsDf)
        
        # prepare plot
        fig, ax = plt.subplots(figsize = (12, 5))
        
        # plot portfolio
        colPortfolio = cumulativeReturnsDf.iloc[:, [0]].columns[0]
        ax.plot(cumulativeReturnsDf[colPortfolio], color = 'black', linewidth = 2)
            
        if len(cumulativeReturnsDf.columns) > 1:
            colFactors = cumulativeReturnsDf.iloc[:, 1:].columns
            # plot factors
            ax.plot(cumulativeReturnsDf[colFactors], alpha = 0.5)
            
        # formatting
        ax.axhline(y = 0, color = 'black', linestyle = '--', linewidth = 0.5)
        ax.set_title('Cumulative Log-Returns', fontdict = {'fontsize': 15})
        ax.yaxis.set_major_formatter(mtick.PercentFormatter(1.0))
        ax.legend(cumulativeReturnsDf.columns, loc = 'best')
        
        plt.show()
        
    def PlotRegressionModel(self, returnsDf, dependentColumn = 'Strategy'):
        
        '''
        Description:
            Run Regression and plot partial regression
        Args:
            returnsDf: SingleIndex Dataframe with returns
            dependentColumn: Name for the column to be used as dependent variable
        Returns:
            Summary of the regression model and partial regression plots
        '''
        
        # run regression
        model = self.RunRegression(returnsDf, dependentColumn)

        # plot partial regression
        exogVariables = [x for x in returnsDf.columns if x != dependentColumn]
        figsize = (10, len(exogVariables) * 2)
        fig = plt.figure(figsize = figsize)
        fig = sm.graphics.plot_partregress_grid(model, fig = fig)
        
        plt.show()
        
    def PlotRollingRegressionCoefficients(self, returnsDf, dependentColumn = 'Strategy', lookback = 126):
        
        '''
        Description:
            Run Rolling Regression and plot the time series of estimated coefficients for each predictor
        Args:
            returnsDf: SingleIndex Dataframe with returns
            dependentColumn: Name for the column to be used as dependent variable
            lookback: Number of observations for the lookback window
        Returns:
            Plot of time series of estimated coefficients for each predictor
        '''
        
        # run rolling regression
        rollingModel = self.RunRollingRegression(returnsDf, dependentColumn, lookback)
        exogVariables = [x for x in returnsDf.columns if x != dependentColumn]
        
        # plot
        figsize = (10, len(exogVariables) * 3)
        fig = rollingModel.plot_recursive_coefficient(variables = exogVariables, figsize = figsize)
        
        plt.show()
        
    def PlotBoxPlotRollingFactorExposure(self, returnsDf, dependentColumn = 'Strategy', lookback = 126):
        
        '''
        Description:
            Run Rolling Regression and make a box plot with the distributions of the estimated coefficients
        Args:
            returnsDf: SingleIndex Dataframe with returns
            dependentColumn: Name for the column to be used as dependent variable
            lookback: Number of observations for the lookback window
        Returns:
            Box plot with distributions of estimated coefficients during the rolling regression
        '''
        
        # run rolling regression
        rollingModel = self.RunRollingRegression(returnsDf, dependentColumn, lookback)
        
        fig, ax = plt.subplots(figsize = (10, 8))
        ax = sns.boxplot(data = rollingModel.params.dropna().drop('const', axis = 1), 
                        width = 0.5,
                        palette = "colorblind",
                        orient = 'h')
        ax.axvline(x = 0, color = 'black', linestyle = '--', linewidth = 0.5)
        ax.set_title('Distribution of Risk Factor Rolling Exposures', fontdict = {'fontsize': 15})
        
        plt.show()
    
    # run full risk analysis --------------------------------------------------------------------------------------
    
    def RunRiskAnalysis(self, returnsDf, externalFactorsDf = None, dependentColumn = 'Strategy', lookback = 126):
        
        # if no externalFactorsDf is provided, use the default Fama-French Five Factors
        if externalFactorsDf is None:
            externalFactorsDf = self.ffFiveFactorsDf
        
        # merge returnsDf with externalFactorsDf
        combinedReturnsDf = pd.merge(returnsDf, externalFactorsDf, left_index = True, right_index = True)
        
        # plot
        self.PlotCumulativeReturns(combinedReturnsDf)
        print('---------------------------------------------------------------------------------------------')
        print('---- Regression Analysis --------------------------------------------------------------------')
        print('---------------------------------------------------------------------------------------------')
        self.PlotRegressionModel(combinedReturnsDf, dependentColumn)
        print('---------------------------------------------------------------------------------------------')
        print('---- Rolling Regression Analysis (Rolling Coefficients) -------------------------------------')
        print('---------------------------------------------------------------------------------------------')
        self.PlotRollingRegressionCoefficients(combinedReturnsDf, dependentColumn, lookback)
        self.PlotBoxPlotRollingFactorExposure(combinedReturnsDf, dependentColumn, lookback)

小结

成功运行。
在这里插入图片描述
补充:

Anaconda | A Faster Solver for Conda: Libmamba

conda 22.11 update: The libmamba solver's experimental flag has been removed. To use the new solver, update conda in your base environment:

conda update -n base conda
To install and set the new solver, run the following commands:

conda install -n base conda-libmamba-solver
conda config --set solver libmamba

-随机因子值的生成

config.json增加"fundamental-data-provider": “QuantConnect.Data.UniverseSelection.CoarseFundamentalDataProviderExt”,

// defines the 'backtesting' environment
 "backtesting": {
   "live-mode": false,
   "setup-handler": "QuantConnect.Lean.Engine.Setup.BacktestingSetupHandler",
   "result-handler": "QuantConnect.Lean.Engine.Results.BacktestingResultHandler",
   "data-feed-handler": "QuantConnect.Lean.Engine.DataFeeds.FileSystemDataFeed",
   "real-time-handler": "QuantConnect.Lean.Engine.RealTime.BacktestingRealTimeHandler",
   "history-provider": [ "QuantConnect.Lean.Engine.HistoricalData.SubscriptionDataReaderHistoryProvider" ],
   "fundamental-data-provider": "QuantConnect.Data.UniverseSelection.CoarseFundamentalDataProviderExt",
   "transaction-handler": "QuantConnect.Lean.Engine.TransactionHandlers.BacktestingTransactionHandler"
 },

复制QuantConnect.Data.UniverseSelection.CoarseFundamentalDataProvider,新建QuantConnect.Data.UniverseSelection.CoarseFundamentalDataProviderExt(实际不必要,使用CoarseFundamentalDataProvider也可以)

/*
 * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
 * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

using System;
using System.IO;
using System.Collections.Generic;
using QuantConnect.Data.Fundamental;
using System.Reflection;
using System.Linq;
using Fasterflect;
using QuantConnect.Interfaces;

namespace QuantConnect.Data.UniverseSelection
{
    /// <summary>
    /// Coarse base fundamental data provider
    /// </summary>
    public class CoarseFundamentalDataProviderExt : BaseFundamentalDataProvider
    {
        private DateTime _date;
        private readonly Dictionary<SecurityIdentifier, CoarseFundamental> _coarseFundamental = new();

        /// <summary>
        /// Will fetch the requested fundamental information for the requested time and symbol
        /// </summary>
        /// <typeparam name="T">The expected data type</typeparam>
        /// <param name="time">The time to request this data for</param>
        /// <param name="securityIdentifier">The security identifier</param>
        /// <param name="name">The name of the fundamental property</param>
        /// <returns>The fundamental information</returns>
        public override T Get<T>(DateTime time, SecurityIdentifier securityIdentifier, FundamentalProperty name)
        {
            var enumName = Enum.GetName(name);
            lock (_coarseFundamental)
            {
                if (time == _date)
                {
                    return GetProperty<T>(securityIdentifier, enumName);
                }
                _date = time;

                var path = Path.Combine(Globals.DataFolder, "equity", "usa", "fundamental", "coarse", $"{time:yyyyMMdd}.csv");
                var fileStream = DataProvider.Fetch(path);
                if (fileStream == null)
                {
                    return GetDefault<T>();
                }

                _coarseFundamental.Clear();
                using (var reader = new StreamReader(fileStream))
                {
                    while (!reader.EndOfStream)
                    {
                        var line = reader.ReadLine();
                        var coarse = Read(line, time);
                        if (coarse != null)
                        {
                            _coarseFundamental[coarse.Symbol.ID] = coarse;
                        }
                    }
                }

                return GetProperty<T>(securityIdentifier, enumName);
            }
        }

        /// <summary>
        /// Reads the given line and returns a CoarseFundamentalSource with the information within it
        /// </summary>
        public static FineFundamentalSource Read(string line, DateTime date)
        {
            try
            {
                var csv = line.Split(',');
                var coarse = new FineFundamentalSource
                {
                    Symbol = new Symbol(SecurityIdentifier.Parse(csv[0]), csv[1]),
                    Time = date,
                    Value = csv[2].ToDecimal(),
                    VolumeSetter = csv[3].ToInt64(),
                    DollarVolumeSetter = (double)csv[4].ToDecimal()
                };

                if (csv.Length > 5)
                {
                    coarse.HasFundamentalDataSetter = csv[5].ConvertInvariant<bool>();
                }

                if (csv.Length > 7)
                {
                    coarse.PriceFactorSetter = csv[6].ToDecimal();
                    coarse.SplitFactorSetter = csv[7].ToDecimal();
                }

                //if (csv.Length > 8)
                //{
                //    Type type = typeof(ValuationRatiosExt);
                //    PropertyInfo[] propertyInfos = type.GetProperties().Where(p => p.PropertyType == typeof(double)).ToArray();
                //    for (int i = 8, j = 0; (i < csv.Length) && (j < propertyInfos.Length); i++, j++)
                //    {
                //        coarse.ValuationRatios.SetPropertyValue(propertyInfos[j].Name, double.Parse(csv[i]));
                //    }
                //}

                return coarse;
            }
            catch (Exception ex)
            {
                var msg = ex.Message;
                return null;
            }
        }

        private dynamic GetProperty<T>(SecurityIdentifier securityIdentifier, string property)
        {
            if (!_coarseFundamental.TryGetValue(securityIdentifier, out var coarse))
            {
                return GetDefault<T>();
            }

            switch (property)
            {
                case nameof(CoarseFundamental.Price):
                    return coarse.Price;
                case nameof(CoarseFundamental.Value):
                    return coarse.Value;
                case nameof(CoarseFundamental.Market):
                    return coarse.Market;
                case nameof(CoarseFundamental.Volume):
                    return coarse.Volume;
                case nameof(CoarseFundamental.PriceFactor):
                    return coarse.PriceFactor;
                case nameof(CoarseFundamental.SplitFactor):
                    return coarse.SplitFactor;
                case nameof(CoarseFundamental.DollarVolume):
                    return coarse.DollarVolume;
                case nameof(CoarseFundamental.HasFundamentalData):
                    //return false;
                    return coarse.HasFundamentalData;
            }

            return GetDefault<T>();
        }

        /// <summary>
        /// Coarse fundamental with setters
        /// </summary>
        public class FineFundamentalSource : FineFundamental
        {
            /// <summary>
            /// Property to set the volume of the Coarse Fundamental
            /// </summary>
            public long VolumeSetter { get; init; }

            /// <summary>
            /// Property to set the dollar volume of the Coarse Fundamental
            /// </summary>
            public double DollarVolumeSetter { get; init; }

            /// <summary>
            /// Property to set the price factor of the Coarse Fundamental
            /// </summary>
            public decimal PriceFactorSetter { get; set; } = 1;

            /// <summary>
            /// Property to set the split factor of the Coarse Fundamental
            /// </summary>
            public decimal SplitFactorSetter { get; set; } = 1;

            /// <summary>
            /// Property to indicate if the Coarse Fundamental has fundamental data
            /// </summary>
            public bool HasFundamentalDataSetter { get; set; }

            /// <summary>
            /// Gets the day's dollar volume for this symbol
            /// </summary>
            public override double DollarVolume => DollarVolumeSetter;

            /// <summary>
            /// Gets the day's total volume
            /// </summary>
            public override long Volume => VolumeSetter;

            /// <summary>
            /// Returns whether the symbol has fundamental data for the given date
            /// </summary>
            public override bool HasFundamentalData => HasFundamentalDataSetter;

            /// <summary>
            /// Gets the price factor for the given date
            /// </summary>
            public override decimal PriceFactor => PriceFactorSetter;

            /// <summary>
            /// Gets the split factor for the given date
            /// </summary>
            public override decimal SplitFactor => SplitFactorSetter;
        }
    }
}

同样复制CoarseFundamental.cs到CoarseFundamentalExt.cs,添加ToRowExt函数,作用是根据ValuationRatios属性添加因子值,其他基本面数据添加因子值可依此参考

/*
 * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
 * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

using System;
using System.Linq;
using System.Globalization;
using QuantConnect.Data.Fundamental;
using System.Reflection;

namespace QuantConnect.Data.UniverseSelection
{
    /// <summary>
    /// Defines summary information about a single symbol for a given date
    /// </summary>
    public class CoarseFundamentalExt : BaseData
    {
        /// <summary>
        /// Gets the market for this symbol
        /// </summary>
        public string Market => Symbol.ID.Market;

        /// <summary>
        /// Gets the day's dollar volume for this symbol
        /// </summary>
        public virtual double DollarVolume { get; }

        /// <summary>
        /// Gets the day's total volume
        /// </summary>
        public virtual long Volume { get; }

        /// <summary>
        /// Returns whether the symbol has fundamental data for the given date
        /// </summary>
        public virtual bool HasFundamentalData { get; }

        /// <summary>
        /// Gets the price factor for the given date
        /// </summary>
        public virtual decimal PriceFactor { get; } = 1;

        /// <summary>
        /// Gets the split factor for the given date
        /// </summary>
        public virtual decimal SplitFactor { get; } = 1;

        /// <summary>
        /// Gets the combined factor used to create adjusted prices from raw prices
        /// </summary>
        public decimal PriceScaleFactor => PriceFactor * SplitFactor;

        /// <summary>
        /// Gets the split and dividend adjusted price
        /// </summary>
        public decimal AdjustedPrice => Price * PriceScaleFactor;

        /// <summary>
        /// The end time of this data.
        /// </summary>
        public override DateTime EndTime
        {
            get { return Time + QuantConnect.Time.OneDay; }
            set { Time = value - QuantConnect.Time.OneDay; }
        }
        
        /// <summary>
        /// Gets the raw price
        /// </summary>
        public override decimal Price => Value; 

        /// <summary>
        /// Initializes a new instance of the <see cref="CoarseFundamental"/> class
        /// </summary>
        public CoarseFundamentalExt()
        {
        }

        /// <summary>
        /// Return the URL string source of the file. This will be converted to a stream
        /// </summary>
        /// <param name="config">Configuration object</param>
        /// <param name="date">Date of this source file</param>
        /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
        /// <returns>String URL of source file.</returns>
        public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
        {
            throw new InvalidOperationException($"Coarse type is obsolete, please use {nameof(Fundamental)}");
        }

        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="config">Subscription data config setup object</param>
        /// <param name="line">Line of the source document</param>
        /// <param name="date">Date of the requested data</param>
        /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
        /// <returns>Instance of the T:BaseData object generated by this line of the CSV</returns>
        public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
        {
            throw new InvalidOperationException($"Coarse type is obsolete, please use {nameof(Fundamental)}");
        }

        /// <summary>
        /// Converts a given fundamental data point into row format
        /// </summary>
        public static string ToRow(CoarseFundamental coarse)
        {
            // sid,symbol,close,volume,dollar volume,has fundamental data,price factor,split factor
            var values = new object[]
            {
                coarse.Symbol.ID,
                coarse.Symbol.Value,
                coarse.Value,
                coarse.Volume,
                coarse.DollarVolume,
                coarse.HasFundamentalData,
                coarse.PriceFactor,
                coarse.SplitFactor
            };

            return string.Join(",", values.Select(s => Convert.ToString(s, CultureInfo.InvariantCulture)));
        }

        /// <summary>
        /// 
        /// </summary>
        public static string ToRowExt(CoarseFundamental coarse)
        {
            // sid,symbol,close,volume,dollar volume,has fundamental data,price factor,split factor
            var values = new object[]
            {
                coarse.Symbol.ID,
                coarse.Symbol.Value,
                coarse.Value,
                coarse.Volume,
                coarse.DollarVolume,
                coarse.HasFundamentalData,
                coarse.PriceFactor,
                coarse.SplitFactor
            };

            Type type = typeof(ValuationRatiosExt);
            PropertyInfo[] propertyInfos = type.GetProperties().Where(p => p.PropertyType == typeof(double)).ToArray();
            int length = propertyInfos.Length;
            object[] fundamentalProperties = new object[length];
            for (int i = 0; i < length; i++)
            {
                fundamentalProperties[i] = new Random().NextDouble();
            }

            var data = new object[values.Length+fundamentalProperties.Length];
            Array.Copy(values, data, values.Length);
            Array.Copy(fundamentalProperties, 0, data, values.Length, fundamentalProperties.Length);

            return string.Join(",", data.Select(s => Convert.ToString(s, CultureInfo.InvariantCulture)));
        }
    }
}

复制ValuationRatios.cs到ValuationRatiosExt.cs,然后两个文件都进行属性修改,由只读变为读写

/*
 * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
 * Lean Algorithmic Trading Engine v2.0. Copyright 2023 QuantConnect Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

using System;
using System.Linq;
using Python.Runtime;
using Newtonsoft.Json;
using System.Collections.Generic;
using QuantConnect.Data.UniverseSelection;

namespace QuantConnect.Data.Fundamental
{
    /// <summary>
    /// Definition of the ValuationRatios class
    /// </summary>
    public class ValuationRatios : FundamentalTimeDependentProperty
    {
        /// <summary>
        /// Dividend per share / Diluted earnings per share
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14000
        /// </remarks>
        [JsonProperty("14000")]
        public double PayoutRatio { get; set; }

        /// <summary>
        /// ROE * (1 - Payout Ratio)
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14001
        /// </remarks>
        [JsonProperty("14001")]
        public double SustainableGrowthRate { get; set; }

        /// <summary>
        /// Refers to the ratio of free cash flow to enterprise value. Morningstar calculates the ratio by using the underlying data reported in the company filings or reports: FCF /Enterprise Value.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14002
        /// </remarks>
        [JsonProperty("14002")]
        public double CashReturn { get; set; }

        /// <summary>
        /// Sales / Average Diluted Shares Outstanding
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14003
        /// </remarks>
        [JsonProperty("14003")]
        public double SalesPerShare { get; set; }

        /// <summary>
        /// Common Shareholder's Equity / Diluted Shares Outstanding
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14004
        /// </remarks>
        [JsonProperty("14004")]
        public double BookValuePerShare { get; set; }

        /// <summary>
        /// Cash Flow from Operations / Average Diluted Shares Outstanding
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14005
        /// </remarks>
        [JsonProperty("14005")]
        public double CFOPerShare { get; set; }

        /// <summary>
        /// Free Cash Flow / Average Diluted Shares Outstanding
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14006
        /// </remarks>
        [JsonProperty("14006")]
        public double FCFPerShare { get; set; }

        /// <summary>
        /// Diluted EPS / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14007
        /// </remarks>
        [JsonProperty("14007")]
        public double EarningYield { get; set; }

        /// <summary>
        /// Adjusted Close Price/ EPS. If the result is negative, zero, &gt;10,000 or &lt;0.001, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14008
        /// </remarks>
        [JsonProperty("14008")]
        public double PERatio { get; set; }

        /// <summary>
        /// SalesPerShare / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14009
        /// </remarks>
        [JsonProperty("14009")]
        public double SalesYield { get; set; }

        /// <summary>
        /// Adjusted close price / Sales Per Share. If the result is negative or zero, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14010
        /// </remarks>
        [JsonProperty("14010")]
        public double PSRatio { get; set; }

        /// <summary>
        /// BookValuePerShare / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14011
        /// </remarks>
        [JsonProperty("14011")]
        public double BookValueYield { get; set; }

        /// <summary>
        /// Adjusted close price / Book Value Per Share. If the result is negative or zero, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14012
        /// </remarks>
        [JsonProperty("14012")]
        public double PBRatio { get; set; }

        /// <summary>
        /// CFOPerShare / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14013
        /// </remarks>
        [JsonProperty("14013")]
        public double CFYield { get; set; }

        /// <summary>
        /// Adjusted close price /Cash Flow Per Share. If the result is negative or zero, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14014
        /// </remarks>
        [JsonProperty("14014")]
        public double PCFRatio { get; set; }

        /// <summary>
        /// FCFPerShare / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14015
        /// </remarks>
        [JsonProperty("14015")]
        public double FCFYield { get; set; }

        /// <summary>
        /// Adjusted close price/ Free Cash Flow Per Share. If the result is negative or zero, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14016
        /// </remarks>
        [JsonProperty("14016")]
        public double FCFRatio { get; set; }

        /// <summary>
        /// Dividends Per Share over the trailing 12 months / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14017
        /// </remarks>
        [JsonProperty("14017")]
        public double TrailingDividendYield { get; set; }

        /// <summary>
        /// (Current Dividend Per Share * Payout Frequency) / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14018
        /// </remarks>
        [JsonProperty("14018")]
        public double ForwardDividendYield { get; set; }

        /// <summary>
        /// Estimated Earnings Per Share / Price Note: a) The "Next" Year's EPS Estimate is used; For instance, if today's actual date is March 1, 2009, the "Current" EPS Estimate for MSFT is June 2009, and the "Next" EPS Estimate for MSFT is June 2010; the latter is used. b) The eps estimated data is sourced from a third party.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14019
        /// </remarks>
        [JsonProperty("14019")]
        public double ForwardEarningYield { get; set; }

        /// <summary>
        /// 1 / ForwardEarningYield If result is negative, then null
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14020
        /// </remarks>
        [JsonProperty("14020")]
        public double ForwardPERatio { get; set; }

        /// <summary>
        /// ForwardPERatio / Long-term Average Earning Growth Rate
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14021
        /// </remarks>
        [JsonProperty("14021")]
        public double PEGRatio { get; set; }

        /// <summary>
        /// The number of years it would take for a company's cumulative earnings to equal the stock's current trading price, assuming that the company continues to increase its annual earnings at the growth rate used to calculate the PEG ratio. [ Log (PG/E + 1) / Log (1 + G) ] - 1 Where P=Price E=Next Fiscal Year's Estimated EPS G=Long-term Average Earning Growth
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14022
        /// </remarks>
        [JsonProperty("14022")]
        public double PEGPayback { get; set; }

        /// <summary>
        /// The company's total book value less the value of any intangible assets dividend by number of shares.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14023
        /// </remarks>
        [JsonProperty("14023")]
        public double TangibleBookValuePerShare { get; set; }

        /// <summary>
        /// The three year average for tangible book value per share.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14024
        /// </remarks>
        [JsonProperty("14024")]
        public double TangibleBVPerShare3YrAvg { get; set; }

        /// <summary>
        /// The five year average for tangible book value per share.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14025
        /// </remarks>
        [JsonProperty("14025")]
        public double TangibleBVPerShare5YrAvg { get; set; }

        /// <summary>
        /// Latest Dividend * Frequency
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14026
        /// </remarks>
        [JsonProperty("14026")]
        public double ForwardDividend { get; set; }

        /// <summary>
        /// (Current Assets - Current Liabilities)/number of shares
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14027
        /// </remarks>
        [JsonProperty("14027")]
        public double WorkingCapitalPerShare { get; set; }

        /// <summary>
        /// The three year average for working capital per share.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14028
        /// </remarks>
        [JsonProperty("14028")]
        public double WorkingCapitalPerShare3YrAvg { get; set; }

        /// <summary>
        /// The five year average for working capital per share.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14029
        /// </remarks>
        [JsonProperty("14029")]
        public double WorkingCapitalPerShare5YrAvg { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of EBITDA generated.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14030
        /// </remarks>
        [JsonProperty("14030")]
        public double EVToEBITDA { get; set; }

        /// <summary>
        /// The net repurchase of shares outstanding over the market capital of the company. It is a measure of shareholder return.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14031
        /// </remarks>
        [JsonProperty("14031")]
        public double BuyBackYield { get; set; }

        /// <summary>
        /// The total yield that shareholders can expect, by summing Dividend Yield and Buyback Yield.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14032
        /// </remarks>
        [JsonProperty("14032")]
        public double TotalYield { get; set; }

        /// <summary>
        /// The five-year average of the company's price-to-earnings ratio.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14033
        /// </remarks>
        [JsonProperty("14033")]
        public double RatioPE5YearAverage { get; set; }

        /// <summary>
        /// Price change this month, expressed as latest price/last month end price.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14034
        /// </remarks>
        [JsonProperty("14034")]
        public double PriceChange1M { get; set; }

        /// <summary>
        /// Adjusted Close Price/ Normalized EPS. Normalized EPS removes onetime and unusual items from net EPS, to provide investors with a more accurate measure of the company's true earnings. If the result is negative, zero, &gt;10,000 or &lt;0.001, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14035
        /// </remarks>
        [JsonProperty("14035")]
        public double NormalizedPERatio { get; set; }

        /// <summary>
        /// Adjusted close price/EBITDA Per Share. If the result is negative or zero, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14036
        /// </remarks>
        [JsonProperty("14036")]
        public double PriceToEBITDA { get; set; }

        /// <summary>
        /// Average of the last 60 monthly observations of trailing dividend yield in the last 5 years.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14037
        /// </remarks>
        [JsonProperty("14037")]
        public double DivYield5Year { get; set; }

        /// <summary>
        /// Estimated EPS/Book Value Per Share
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14038
        /// </remarks>
        [JsonProperty("14038")]
        public double ForwardROE { get; set; }

        /// <summary>
        /// Estimated EPS/Total Assets Per Share
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14039
        /// </remarks>
        [JsonProperty("14039")]
        public double ForwardROA { get; set; }

        /// <summary>
        /// 2 Years Forward Estimated EPS / Adjusted Close Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14040
        /// </remarks>
        [JsonProperty("14040")]
        public double TwoYearsForwardEarningYield { get; set; }

        /// <summary>
        /// Adjusted Close Price/2 Years Forward Estimated EPS
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14041
        /// </remarks>
        [JsonProperty("14041")]
        public double TwoYearsForwardPERatio { get; set; }

        /// <summary>
        /// Indicates the method used to calculate Forward Dividend. There are three options: Annual, Look-back and Manual.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14042
        /// </remarks>
        [JsonProperty("14042")]
        public string ForwardCalculationStyle { get; set; }

        /// <summary>
        /// Used to collect the forward dividend for companies where our formula will not produce the correct value.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14043
        /// </remarks>
        [JsonProperty("14043")]
        public double ActualForwardDividend { get; set; }

        /// <summary>
        /// Indicates the method used to calculate Trailing Dividend. There are two options: Look-back and Manual.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14044
        /// </remarks>
        [JsonProperty("14044")]
        public string TrailingCalculationStyle { get; set; }

        /// <summary>
        /// Used to collect the trailing dividend for companies where our formula will not produce the correct value.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14045
        /// </remarks>
        [JsonProperty("14045")]
        public double ActualTrailingDividend { get; set; }

        /// <summary>
        /// Total Assets / Diluted Shares Outstanding
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14046
        /// </remarks>
        [JsonProperty("14046")]
        public double TotalAssetPerShare { get; set; }

        /// <summary>
        /// The growth rate from the TrailingDividend to the Forward Dividend: {(Forward Dividend/Trailing Dividend) - 1}*100.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14047
        /// </remarks>
        [JsonProperty("14047")]
        public double ExpectedDividendGrowthRate { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of revenue generated.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14048
        /// </remarks>
        [JsonProperty("14048")]
        public double EVToRevenue { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of Pretax Income generated.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14049
        /// </remarks>
        [JsonProperty("14049")]
        public double EVToPreTaxIncome { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of asset value; should be the default EV multiple used in an asset driven business.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14050
        /// </remarks>
        [JsonProperty("14050")]
        public double EVToTotalAssets { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of free cash flow generated.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14051
        /// </remarks>
        [JsonProperty("14051")]
        public double EVToFCF { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of EBIT generated.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14052
        /// </remarks>
        [JsonProperty("14052")]
        public double EVToEBIT { get; set; }

        /// <summary>
        /// Funds from operations per share; populated only for real estate investment trusts (REITs), defined as the sum of net income, gain/loss (realized and unrealized) on investment securities, asset impairment charge, depreciation and amortization and gain/ loss on the sale of business and property plant and equipment, divided by shares outstanding.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14053
        /// </remarks>
        [JsonProperty("14053")]
        public double FFOPerShare { get; set; }

        /// <summary>
        /// The ratio of a stock's price to its cash flow per share.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14054
        /// </remarks>
        [JsonProperty("14054")]
        public double PriceToCashRatio { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of estimated EBITDA.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14055
        /// </remarks>
        [JsonProperty("14055")]
        public double EVToForwardEBITDA { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of estimated revenue.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14056
        /// </remarks>
        [JsonProperty("14056")]
        public double EVToForwardRevenue { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of estimated EBIT.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14057
        /// </remarks>
        [JsonProperty("14057")]
        public double EVToForwardEBIT { get; set; }

        /// <summary>
        /// The one-year growth in the company's EV to EBITDA on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBITDA (earnings minus expenses excluding interest, tax, depreciation, and amortization expenses) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14058
        /// </remarks>
        [JsonProperty("14058")]
        public double EVToEBITDA1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's EV to free cash flow on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by free cash flow (Cash flow from operations - Capital Expenditures) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14059
        /// </remarks>
        [JsonProperty("14059")]
        public double EVToFCF1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's EV to revenue on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Revenue reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14060
        /// </remarks>
        [JsonProperty("14060")]
        public double EVToRevenue1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's EV to total assets on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by total assets reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14061
        /// </remarks>
        [JsonProperty("14061")]
        public double EVToTotalAssets1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's price to free cash flow ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the free cash flow reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14062
        /// </remarks>
        [JsonProperty("14062")]
        public double PFCFRatio1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's price to book ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the book value per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14063
        /// </remarks>
        [JsonProperty("14063")]
        public double PBRatio1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's PE ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14064
        /// </remarks>
        [JsonProperty("14064")]
        public double PERatio1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's price to sales ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the sales per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14065
        /// </remarks>
        [JsonProperty("14065")]
        public double PSRatio1YearGrowth { get; set; }

        /// <summary>
        /// The three-year average for a company's EV to EBIT ratio: EV (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBIT (earnings minus expenses excluding interest and tax expenses) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14066
        /// </remarks>
        [JsonProperty("14066")]
        public double EVToEBIT3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's EV to EBITDA ratio: EV (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBITDA (earnings minus expenses excluding interest, tax, depreciation, and amortization expenses) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14067
        /// </remarks>
        [JsonProperty("14067")]
        public double EVToEBITDA3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's EV to free cash flow ratio: EV (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by free cash flow (Cash Flow from Operations - Capital Expenditures) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14068
        /// </remarks>
        [JsonProperty("14068")]
        public double EVToFCF3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's EV to revenue ratio: EV (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Revenue reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14069
        /// </remarks>
        [JsonProperty("14069")]
        public double EVToRevenue3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's EV to total assets ratio: EV (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Assets reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14070
        /// </remarks>
        [JsonProperty("14070")]
        public double EVToTotalAssets3YrAvg { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's EV to EBIT ratio. Morningstar calculates the growth percentage based on the EV to EBIT ratio ((Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBIT (earnings minus expenses excluding interest and tax expenses) reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14071
        /// </remarks>
        [JsonProperty("14071")]
        public double EVToEBIT3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's EV to EBITDA ratio. Morningstar calculates the growth percentage based on the EV to EBITDA ratio ((Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBITDA (earnings minus expenses excluding interest, tax depreciation and amortization expenses) reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14072
        /// </remarks>
        [JsonProperty("14072")]
        public double EVToEBITDA3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's EV to free cash flow ratio. Morningstar calculates the growth percentage based on the EV to free cash flow ratio ((Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by free cash flow (Cash Flow from Operations - Capital Expenditures) reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14073
        /// </remarks>
        [JsonProperty("14073")]
        public double EVToFCF3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's EV to revenue ratio. Morningstar calculates the growth percentage based on the EV to revenue ratio ((Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Revenue reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14074
        /// </remarks>
        [JsonProperty("14074")]
        public double EVToRevenue3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's EV to total assets ratio. Morningstar calculates the growth percentage based on the EV to total assets ratio ((Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by total assets reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14075
        /// </remarks>
        [JsonProperty("14075")]
        public double EVToTotalAssets3YrAvgChange { get; set; }

        /// <summary>
        /// The three-year average for a company's price to free cash flow ratio (the adjusted close price divided by the free cash flow per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14076
        /// </remarks>
        [JsonProperty("14076")]
        public double PFCFRatio3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's price to book ratio (the adjusted close price divided by the book value per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14077
        /// </remarks>
        [JsonProperty("14077")]
        public double PBRatio3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's price to sales ratio (the adjusted close price divided by the total sales per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14078
        /// </remarks>
        [JsonProperty("14078")]
        public double PSRatio3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's price to cash ratio (the adjusted close price divided by the cash flow per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14079
        /// </remarks>
        [JsonProperty("14079")]
        public double PCashRatio3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's PE ratio (the adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14080
        /// </remarks>
        [JsonProperty("14080")]
        public double PERatio3YrAvg { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's price to free cash flow ratio. Morningstar calculates the growth percentage based on the adjusted close price divided by the free cash flow per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14081
        /// </remarks>
        [JsonProperty("14081")]
        public double PFCFRatio3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's price to book ratio. Morningstar calculates the growth percentage based on the adjusted close price divided by the book value per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14082
        /// </remarks>
        [JsonProperty("14082")]
        public double PBRatio3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's price to sales ratio. Morningstar calculates the growth percentage based on the adjusted close price divided by the total sales per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14083
        /// </remarks>
        [JsonProperty("14083")]
        public double PSRatio3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's PE ratio. Morningstar calculates the growth percentage based on the adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14084
        /// </remarks>
        [JsonProperty("14084")]
        public double PERatio3YrAvgChange { get; set; }

        /// <summary>
        /// The one-year high for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14085
        /// </remarks>
        [JsonProperty("14085")]
        public double PERatio1YearHigh { get; set; }

        /// <summary>
        /// The one-year low for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14086
        /// </remarks>
        [JsonProperty("14086")]
        public double PERatio1YearLow { get; set; }

        /// <summary>
        /// The one-year average for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14087
        /// </remarks>
        [JsonProperty("14087")]
        public double PERatio1YearAverage { get; set; }

        /// <summary>
        /// The five-year high for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14088
        /// </remarks>
        [JsonProperty("14088")]
        public double PERatio5YearHigh { get; set; }

        /// <summary>
        /// The five-year low for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14089
        /// </remarks>
        [JsonProperty("14089")]
        public double PERatio5YearLow { get; set; }

        /// <summary>
        /// The five-year average for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14090
        /// </remarks>
        [JsonProperty("14090")]
        public double PERatio5YearAverage { get; set; }

        /// <summary>
        /// The ten-year high for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14091
        /// </remarks>
        [JsonProperty("14091")]
        public double PERatio10YearHigh { get; set; }

        /// <summary>
        /// The ten-year low for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14092
        /// </remarks>
        [JsonProperty("14092")]
        public double PERatio10YearLow { get; set; }

        /// <summary>
        /// The ten-year average for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14093
        /// </remarks>
        [JsonProperty("14093")]
        public double PERatio10YearAverage { get; set; }

        /// <summary>
        /// The cyclically adjusted PE ratio for a company; adjusted close price divided by earnings per share. If the result is negative, zero, &gt;10,000 or &lt;0.001, then null. Morningstar uses the CPI index for US companies and Indexes from the World Bank for the rest of the global markets.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14094
        /// </remarks>
        [JsonProperty("14094")]
        public double CAPERatio { get; set; }

        /// <summary>
        /// The three-year growth in the company's EV to EBITDA on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBITDA (earnings minus expenses excluding interest, tax, depreciation, and amortization expenses) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14095
        /// </remarks>
        [JsonProperty("14095")]
        public double EVToEBITDA3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's EV to free cash flow on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by free cash flow (Cash flow from operations - Capital Expenditures) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14096
        /// </remarks>
        [JsonProperty("14096")]
        public double EVToFCF3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's EV to revenue on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Revenue reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14097
        /// </remarks>
        [JsonProperty("14097")]
        public double EVToRevenue3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's EV to total assets on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by total assets reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14098
        /// </remarks>
        [JsonProperty("14098")]
        public double EVToTotalAssets3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's price to free cash flow ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the free cash flow reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14099
        /// </remarks>
        [JsonProperty("14099")]
        public double PFCFRatio3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's price to book ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the book value per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14100
        /// </remarks>
        [JsonProperty("14100")]
        public double PBRatio3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's PE ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14101
        /// </remarks>
        [JsonProperty("14101")]
        public double PERatio3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's price to sales ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the sales per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14102
        /// </remarks>
        [JsonProperty("14102")]
        public double PSRatio3YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's EV to EBITDA on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBITDA (earnings minus expenses excluding interest, tax, depreciation, and amortization expenses) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14103
        /// </remarks>
        [JsonProperty("14103")]
        public double EVToEBITDA5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's EV to free cash flow on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by free cash flow (Cash flow from operations - Capital Expenditures) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14104
        /// </remarks>
        [JsonProperty("14104")]
        public double EVToFCF5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's EV to revenue on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Revenue reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14105
        /// </remarks>
        [JsonProperty("14105")]
        public double EVToRevenue5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's EV to total assets on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by total assets reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14106
        /// </remarks>
        [JsonProperty("14106")]
        public double EVToTotalAssets5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's price to free cash flow ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the free cash flow reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14107
        /// </remarks>
        [JsonProperty("14107")]
        public double PFCFRatio5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's price to book ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the book value per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14108
        /// </remarks>
        [JsonProperty("14108")]
        public double PBRatio5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's PE ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14109
        /// </remarks>
        [JsonProperty("14109")]
        public double PERatio5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's price to sales ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the sales per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14110
        /// </remarks>
        [JsonProperty("14110")]
        public double PSRatio5YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's EV to EBITDA on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBITDA (earnings minus expenses excluding interest, tax, depreciation, and amortization expenses) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14111
        /// </remarks>
        [JsonProperty("14111")]
        public double EVToEBITDA10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's EV to free cash flow on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by free cash flow (Cash flow from operations - Capital Expenditures) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14112
        /// </remarks>
        [JsonProperty("14112")]
        public double EVToFCF10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's EV to revenue on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Revenue reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14113
        /// </remarks>
        [JsonProperty("14113")]
        public double EVToRevenue10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's EV to total assets on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by total assets reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14114
        /// </remarks>
        [JsonProperty("14114")]
        public double EVToTotalAssets10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's price to free cash flow ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the free cash flow reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14115
        /// </remarks>
        [JsonProperty("14115")]
        public double PFCFRatio10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's price to book ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the book value per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14116
        /// </remarks>
        [JsonProperty("14116")]
        public double PBRatio10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's PE ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14117
        /// </remarks>
        [JsonProperty("14117")]
        public double PERatio10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's price to sales ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the sales per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14118
        /// </remarks>
        [JsonProperty("14118")]
        public double PSRatio10YearGrowth { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of estimated EBIT in year 2.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14119
        /// </remarks>
        [JsonProperty("14119")]
        public double TwoYrsEVToForwardEBIT { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of estimated EBITDA in year 2.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14120
        /// </remarks>
        [JsonProperty("14120")]
        public double TwoYrsEVToForwardEBITDA { get; set; }

        /// <summary>
        /// EPS Growth Ratio: (Estimated EPS Year 1) / (TTM Normalized diluted EPS
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14121
        /// </remarks>
        [JsonProperty("14121")]
        public double FirstYearEstimatedEPSGrowth { get; set; }

        /// <summary>
        /// EPS Growth Ratio: (Estimated EPS Year 2) / (Estimated EPS Year 1)
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14122
        /// </remarks>
        [JsonProperty("14122")]
        public double SecondYearEstimatedEPSGrowth { get; set; }

        /// <summary>
        /// Normalized ForwardPERatio / Long-term Average Normalized Earnings Growth Rate
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14123
        /// </remarks>
        [JsonProperty("14123")]
        public double NormalizedPEGRatio { get; set; }

        /// <summary>
        /// Creates a new instance for the given time and security
        /// </summary>
        public ValuationRatios(ITimeProvider timeProvider, SecurityIdentifier securityIdentifier)
            : base(timeProvider, securityIdentifier)
        {
        }

        /// <summary>
        /// Clones this instance
        /// </summary>
        public override FundamentalTimeDependentProperty Clone(ITimeProvider timeProvider)
        {
            return new ValuationRatios(timeProvider, _securityIdentifier);
        }
    }
}

/*
 * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
 * Lean Algorithmic Trading Engine v2.0. Copyright 2023 QuantConnect Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

using System;
using System.Linq;
using Python.Runtime;
using Newtonsoft.Json;
using System.Collections.Generic;
using QuantConnect.Data.UniverseSelection;

namespace QuantConnect.Data.Fundamental
{
    /// <summary>
    /// Definition of the ValuationRatios class
    /// </summary>
    public class ValuationRatiosExt
    {
        /// <summary>
        /// Dividend per share / Diluted earnings per share
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14000
        /// </remarks>
        [JsonProperty("14000")]
        public double PayoutRatio { get; set; }

        /// <summary>
        /// ROE * (1 - Payout Ratio)
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14001
        /// </remarks>
        [JsonProperty("14001")]
        public double SustainableGrowthRate { get; set; }

        /// <summary>
        /// Refers to the ratio of free cash flow to enterprise value. Morningstar calculates the ratio by using the underlying data reported in the company filings or reports: FCF /Enterprise Value.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14002
        /// </remarks>
        [JsonProperty("14002")]
        public double CashReturn { get; set; }

        /// <summary>
        /// Sales / Average Diluted Shares Outstanding
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14003
        /// </remarks>
        [JsonProperty("14003")]
        public double SalesPerShare { get; set; }

        /// <summary>
        /// Common Shareholder's Equity / Diluted Shares Outstanding
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14004
        /// </remarks>
        [JsonProperty("14004")]
        public double BookValuePerShare { get; set; }

        /// <summary>
        /// Cash Flow from Operations / Average Diluted Shares Outstanding
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14005
        /// </remarks>
        [JsonProperty("14005")]
        public double CFOPerShare { get; set; }

        /// <summary>
        /// Free Cash Flow / Average Diluted Shares Outstanding
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14006
        /// </remarks>
        [JsonProperty("14006")]
        public double FCFPerShare { get; set; }

        /// <summary>
        /// Diluted EPS / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14007
        /// </remarks>
        [JsonProperty("14007")]
        public double EarningYield { get; set; }

        /// <summary>
        /// Adjusted Close Price/ EPS. If the result is negative, zero, &gt;10,000 or &lt;0.001, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14008
        /// </remarks>
        [JsonProperty("14008")]
        public double PERatio { get; set; }

        /// <summary>
        /// SalesPerShare / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14009
        /// </remarks>
        [JsonProperty("14009")]
        public double SalesYield { get; set; }

        /// <summary>
        /// Adjusted close price / Sales Per Share. If the result is negative or zero, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14010
        /// </remarks>
        [JsonProperty("14010")]
        public double PSRatio { get; set; }

        /// <summary>
        /// BookValuePerShare / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14011
        /// </remarks>
        [JsonProperty("14011")]
        public double BookValueYield { get; set; }

        /// <summary>
        /// Adjusted close price / Book Value Per Share. If the result is negative or zero, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14012
        /// </remarks>
        [JsonProperty("14012")]
        public double PBRatio { get; set; }

        /// <summary>
        /// CFOPerShare / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14013
        /// </remarks>
        [JsonProperty("14013")]
        public double CFYield { get; set; }

        /// <summary>
        /// Adjusted close price /Cash Flow Per Share. If the result is negative or zero, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14014
        /// </remarks>
        [JsonProperty("14014")]
        public double PCFRatio { get; set; }

        /// <summary>
        /// FCFPerShare / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14015
        /// </remarks>
        [JsonProperty("14015")]
        public double FCFYield { get; set; }

        /// <summary>
        /// Adjusted close price/ Free Cash Flow Per Share. If the result is negative or zero, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14016
        /// </remarks>
        [JsonProperty("14016")]
        public double FCFRatio { get; set; }

        /// <summary>
        /// Dividends Per Share over the trailing 12 months / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14017
        /// </remarks>
        [JsonProperty("14017")]
        public double TrailingDividendYield { get; set; }

        /// <summary>
        /// (Current Dividend Per Share * Payout Frequency) / Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14018
        /// </remarks>
        [JsonProperty("14018")]
        public double ForwardDividendYield { get; set; }

        /// <summary>
        /// Estimated Earnings Per Share / Price Note: a) The "Next" Year's EPS Estimate is used; For instance, if today's actual date is March 1, 2009, the "Current" EPS Estimate for MSFT is June 2009, and the "Next" EPS Estimate for MSFT is June 2010; the latter is used. b) The eps estimated data is sourced from a third party.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14019
        /// </remarks>
        [JsonProperty("14019")]
        public double ForwardEarningYield { get; set; }

        /// <summary>
        /// 1 / ForwardEarningYield If result is negative, then null
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14020
        /// </remarks>
        [JsonProperty("14020")]
        public double ForwardPERatio { get; set; }

        /// <summary>
        /// ForwardPERatio / Long-term Average Earning Growth Rate
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14021
        /// </remarks>
        [JsonProperty("14021")]
        public double PEGRatio { get; set; }

        /// <summary>
        /// The number of years it would take for a company's cumulative earnings to equal the stock's current trading price, assuming that the company continues to increase its annual earnings at the growth rate used to calculate the PEG ratio. [ Log (PG/E + 1) / Log (1 + G) ] - 1 Where P=Price E=Next Fiscal Year's Estimated EPS G=Long-term Average Earning Growth
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14022
        /// </remarks>
        [JsonProperty("14022")]
        public double PEGPayback { get; set; }

        /// <summary>
        /// The company's total book value less the value of any intangible assets dividend by number of shares.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14023
        /// </remarks>
        [JsonProperty("14023")]
        public double TangibleBookValuePerShare { get; set; }

        /// <summary>
        /// The three year average for tangible book value per share.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14024
        /// </remarks>
        [JsonProperty("14024")]
        public double TangibleBVPerShare3YrAvg { get; set; }

        /// <summary>
        /// The five year average for tangible book value per share.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14025
        /// </remarks>
        [JsonProperty("14025")]
        public double TangibleBVPerShare5YrAvg { get; set; }

        /// <summary>
        /// Latest Dividend * Frequency
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14026
        /// </remarks>
        [JsonProperty("14026")]
        public double ForwardDividend { get; set; }

        /// <summary>
        /// (Current Assets - Current Liabilities)/number of shares
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14027
        /// </remarks>
        [JsonProperty("14027")]
        public double WorkingCapitalPerShare { get; set; }

        /// <summary>
        /// The three year average for working capital per share.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14028
        /// </remarks>
        [JsonProperty("14028")]
        public double WorkingCapitalPerShare3YrAvg { get; set; }

        /// <summary>
        /// The five year average for working capital per share.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14029
        /// </remarks>
        [JsonProperty("14029")]
        public double WorkingCapitalPerShare5YrAvg { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of EBITDA generated.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14030
        /// </remarks>
        [JsonProperty("14030")]
        public double EVToEBITDA { get; set; }

        /// <summary>
        /// The net repurchase of shares outstanding over the market capital of the company. It is a measure of shareholder return.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14031
        /// </remarks>
        [JsonProperty("14031")]
        public double BuyBackYield { get; set; }

        /// <summary>
        /// The total yield that shareholders can expect, by summing Dividend Yield and Buyback Yield.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14032
        /// </remarks>
        [JsonProperty("14032")]
        public double TotalYield { get; set; }

        /// <summary>
        /// The five-year average of the company's price-to-earnings ratio.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14033
        /// </remarks>
        [JsonProperty("14033")]
        public double RatioPE5YearAverage { get; set; }

        /// <summary>
        /// Price change this month, expressed as latest price/last month end price.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14034
        /// </remarks>
        [JsonProperty("14034")]
        public double PriceChange1M { get; set; }

        /// <summary>
        /// Adjusted Close Price/ Normalized EPS. Normalized EPS removes onetime and unusual items from net EPS, to provide investors with a more accurate measure of the company's true earnings. If the result is negative, zero, &gt;10,000 or &lt;0.001, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14035
        /// </remarks>
        [JsonProperty("14035")]
        public double NormalizedPERatio { get; set; }

        /// <summary>
        /// Adjusted close price/EBITDA Per Share. If the result is negative or zero, then null.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14036
        /// </remarks>
        [JsonProperty("14036")]
        public double PriceToEBITDA { get; set; }

        /// <summary>
        /// Average of the last 60 monthly observations of trailing dividend yield in the last 5 years.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14037
        /// </remarks>
        [JsonProperty("14037")]
        public double DivYield5Year { get; set; }

        /// <summary>
        /// Estimated EPS/Book Value Per Share
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14038
        /// </remarks>
        [JsonProperty("14038")]
        public double ForwardROE { get; set; }

        /// <summary>
        /// Estimated EPS/Total Assets Per Share
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14039
        /// </remarks>
        [JsonProperty("14039")]
        public double ForwardROA { get; set; }

        /// <summary>
        /// 2 Years Forward Estimated EPS / Adjusted Close Price
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14040
        /// </remarks>
        [JsonProperty("14040")]
        public double TwoYearsForwardEarningYield { get; set; }

        /// <summary>
        /// Adjusted Close Price/2 Years Forward Estimated EPS
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14041
        /// </remarks>
        [JsonProperty("14041")]
        public double TwoYearsForwardPERatio { get; set; }

        /// <summary>
        /// Indicates the method used to calculate Forward Dividend. There are three options: Annual, Look-back and Manual.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14042
        /// </remarks>
        [JsonProperty("14042")]
        public string ForwardCalculationStyle { get; set; }

        /// <summary>
        /// Used to collect the forward dividend for companies where our formula will not produce the correct value.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14043
        /// </remarks>
        [JsonProperty("14043")]
        public double ActualForwardDividend { get; set; }

        /// <summary>
        /// Indicates the method used to calculate Trailing Dividend. There are two options: Look-back and Manual.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14044
        /// </remarks>
        [JsonProperty("14044")]
        public string TrailingCalculationStyle { get; set; }

        /// <summary>
        /// Used to collect the trailing dividend for companies where our formula will not produce the correct value.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14045
        /// </remarks>
        [JsonProperty("14045")]
        public double ActualTrailingDividend { get; set; }

        /// <summary>
        /// Total Assets / Diluted Shares Outstanding
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14046
        /// </remarks>
        [JsonProperty("14046")]
        public double TotalAssetPerShare { get; set; }

        /// <summary>
        /// The growth rate from the TrailingDividend to the Forward Dividend: {(Forward Dividend/Trailing Dividend) - 1}*100.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14047
        /// </remarks>
        [JsonProperty("14047")]
        public double ExpectedDividendGrowthRate { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of revenue generated.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14048
        /// </remarks>
        [JsonProperty("14048")]
        public double EVToRevenue { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of Pretax Income generated.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14049
        /// </remarks>
        [JsonProperty("14049")]
        public double EVToPreTaxIncome { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of asset value; should be the default EV multiple used in an asset driven business.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14050
        /// </remarks>
        [JsonProperty("14050")]
        public double EVToTotalAssets { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of free cash flow generated.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14051
        /// </remarks>
        [JsonProperty("14051")]
        public double EVToFCF { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of EBIT generated.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14052
        /// </remarks>
        [JsonProperty("14052")]
        public double EVToEBIT { get; set; }

        /// <summary>
        /// Funds from operations per share; populated only for real estate investment trusts (REITs), defined as the sum of net income, gain/loss (realized and unrealized) on investment securities, asset impairment charge, depreciation and amortization and gain/ loss on the sale of business and property plant and equipment, divided by shares outstanding.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14053
        /// </remarks>
        [JsonProperty("14053")]
        public double FFOPerShare { get; set; }

        /// <summary>
        /// The ratio of a stock's price to its cash flow per share.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14054
        /// </remarks>
        [JsonProperty("14054")]
        public double PriceToCashRatio { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of estimated EBITDA.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14055
        /// </remarks>
        [JsonProperty("14055")]
        public double EVToForwardEBITDA { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of estimated revenue.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14056
        /// </remarks>
        [JsonProperty("14056")]
        public double EVToForwardRevenue { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of estimated EBIT.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14057
        /// </remarks>
        [JsonProperty("14057")]
        public double EVToForwardEBIT { get; set; }

        /// <summary>
        /// The one-year growth in the company's EV to EBITDA on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBITDA (earnings minus expenses excluding interest, tax, depreciation, and amortization expenses) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14058
        /// </remarks>
        [JsonProperty("14058")]
        public double EVToEBITDA1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's EV to free cash flow on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by free cash flow (Cash flow from operations - Capital Expenditures) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14059
        /// </remarks>
        [JsonProperty("14059")]
        public double EVToFCF1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's EV to revenue on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Revenue reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14060
        /// </remarks>
        [JsonProperty("14060")]
        public double EVToRevenue1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's EV to total assets on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by total assets reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14061
        /// </remarks>
        [JsonProperty("14061")]
        public double EVToTotalAssets1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's price to free cash flow ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the free cash flow reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14062
        /// </remarks>
        [JsonProperty("14062")]
        public double PFCFRatio1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's price to book ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the book value per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14063
        /// </remarks>
        [JsonProperty("14063")]
        public double PBRatio1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's PE ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14064
        /// </remarks>
        [JsonProperty("14064")]
        public double PERatio1YearGrowth { get; set; }

        /// <summary>
        /// The one-year growth in the company's price to sales ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the sales per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14065
        /// </remarks>
        [JsonProperty("14065")]
        public double PSRatio1YearGrowth { get; set; }

        /// <summary>
        /// The three-year average for a company's EV to EBIT ratio: EV (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBIT (earnings minus expenses excluding interest and tax expenses) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14066
        /// </remarks>
        [JsonProperty("14066")]
        public double EVToEBIT3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's EV to EBITDA ratio: EV (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBITDA (earnings minus expenses excluding interest, tax, depreciation, and amortization expenses) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14067
        /// </remarks>
        [JsonProperty("14067")]
        public double EVToEBITDA3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's EV to free cash flow ratio: EV (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by free cash flow (Cash Flow from Operations - Capital Expenditures) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14068
        /// </remarks>
        [JsonProperty("14068")]
        public double EVToFCF3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's EV to revenue ratio: EV (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Revenue reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14069
        /// </remarks>
        [JsonProperty("14069")]
        public double EVToRevenue3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's EV to total assets ratio: EV (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Assets reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14070
        /// </remarks>
        [JsonProperty("14070")]
        public double EVToTotalAssets3YrAvg { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's EV to EBIT ratio. Morningstar calculates the growth percentage based on the EV to EBIT ratio ((Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBIT (earnings minus expenses excluding interest and tax expenses) reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14071
        /// </remarks>
        [JsonProperty("14071")]
        public double EVToEBIT3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's EV to EBITDA ratio. Morningstar calculates the growth percentage based on the EV to EBITDA ratio ((Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBITDA (earnings minus expenses excluding interest, tax depreciation and amortization expenses) reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14072
        /// </remarks>
        [JsonProperty("14072")]
        public double EVToEBITDA3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's EV to free cash flow ratio. Morningstar calculates the growth percentage based on the EV to free cash flow ratio ((Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by free cash flow (Cash Flow from Operations - Capital Expenditures) reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14073
        /// </remarks>
        [JsonProperty("14073")]
        public double EVToFCF3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's EV to revenue ratio. Morningstar calculates the growth percentage based on the EV to revenue ratio ((Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Revenue reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14074
        /// </remarks>
        [JsonProperty("14074")]
        public double EVToRevenue3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's EV to total assets ratio. Morningstar calculates the growth percentage based on the EV to total assets ratio ((Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by total assets reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14075
        /// </remarks>
        [JsonProperty("14075")]
        public double EVToTotalAssets3YrAvgChange { get; set; }

        /// <summary>
        /// The three-year average for a company's price to free cash flow ratio (the adjusted close price divided by the free cash flow per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14076
        /// </remarks>
        [JsonProperty("14076")]
        public double PFCFRatio3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's price to book ratio (the adjusted close price divided by the book value per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14077
        /// </remarks>
        [JsonProperty("14077")]
        public double PBRatio3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's price to sales ratio (the adjusted close price divided by the total sales per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14078
        /// </remarks>
        [JsonProperty("14078")]
        public double PSRatio3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's price to cash ratio (the adjusted close price divided by the cash flow per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14079
        /// </remarks>
        [JsonProperty("14079")]
        public double PCashRatio3YrAvg { get; set; }

        /// <summary>
        /// The three-year average for a company's PE ratio (the adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14080
        /// </remarks>
        [JsonProperty("14080")]
        public double PERatio3YrAvg { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's price to free cash flow ratio. Morningstar calculates the growth percentage based on the adjusted close price divided by the free cash flow per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14081
        /// </remarks>
        [JsonProperty("14081")]
        public double PFCFRatio3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's price to book ratio. Morningstar calculates the growth percentage based on the adjusted close price divided by the book value per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14082
        /// </remarks>
        [JsonProperty("14082")]
        public double PBRatio3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's price to sales ratio. Morningstar calculates the growth percentage based on the adjusted close price divided by the total sales per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14083
        /// </remarks>
        [JsonProperty("14083")]
        public double PSRatio3YrAvgChange { get; set; }

        /// <summary>
        /// The growth in the three-year average for a company's PE ratio. Morningstar calculates the growth percentage based on the adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14084
        /// </remarks>
        [JsonProperty("14084")]
        public double PERatio3YrAvgChange { get; set; }

        /// <summary>
        /// The one-year high for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14085
        /// </remarks>
        [JsonProperty("14085")]
        public double PERatio1YearHigh { get; set; }

        /// <summary>
        /// The one-year low for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14086
        /// </remarks>
        [JsonProperty("14086")]
        public double PERatio1YearLow { get; set; }

        /// <summary>
        /// The one-year average for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14087
        /// </remarks>
        [JsonProperty("14087")]
        public double PERatio1YearAverage { get; set; }

        /// <summary>
        /// The five-year high for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14088
        /// </remarks>
        [JsonProperty("14088")]
        public double PERatio5YearHigh { get; set; }

        /// <summary>
        /// The five-year low for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14089
        /// </remarks>
        [JsonProperty("14089")]
        public double PERatio5YearLow { get; set; }

        /// <summary>
        /// The five-year average for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14090
        /// </remarks>
        [JsonProperty("14090")]
        public double PERatio5YearAverage { get; set; }

        /// <summary>
        /// The ten-year high for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14091
        /// </remarks>
        [JsonProperty("14091")]
        public double PERatio10YearHigh { get; set; }

        /// <summary>
        /// The ten-year low for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14092
        /// </remarks>
        [JsonProperty("14092")]
        public double PERatio10YearLow { get; set; }

        /// <summary>
        /// The ten-year average for a company's PE ratio (adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports).
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14093
        /// </remarks>
        [JsonProperty("14093")]
        public double PERatio10YearAverage { get; set; }

        /// <summary>
        /// The cyclically adjusted PE ratio for a company; adjusted close price divided by earnings per share. If the result is negative, zero, &gt;10,000 or &lt;0.001, then null. Morningstar uses the CPI index for US companies and Indexes from the World Bank for the rest of the global markets.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14094
        /// </remarks>
        [JsonProperty("14094")]
        public double CAPERatio { get; set; }

        /// <summary>
        /// The three-year growth in the company's EV to EBITDA on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBITDA (earnings minus expenses excluding interest, tax, depreciation, and amortization expenses) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14095
        /// </remarks>
        [JsonProperty("14095")]
        public double EVToEBITDA3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's EV to free cash flow on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by free cash flow (Cash flow from operations - Capital Expenditures) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14096
        /// </remarks>
        [JsonProperty("14096")]
        public double EVToFCF3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's EV to revenue on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Revenue reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14097
        /// </remarks>
        [JsonProperty("14097")]
        public double EVToRevenue3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's EV to total assets on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by total assets reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14098
        /// </remarks>
        [JsonProperty("14098")]
        public double EVToTotalAssets3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's price to free cash flow ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the free cash flow reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14099
        /// </remarks>
        [JsonProperty("14099")]
        public double PFCFRatio3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's price to book ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the book value per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14100
        /// </remarks>
        [JsonProperty("14100")]
        public double PBRatio3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's PE ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14101
        /// </remarks>
        [JsonProperty("14101")]
        public double PERatio3YearGrowth { get; set; }

        /// <summary>
        /// The three-year growth in the company's price to sales ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the sales per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14102
        /// </remarks>
        [JsonProperty("14102")]
        public double PSRatio3YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's EV to EBITDA on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBITDA (earnings minus expenses excluding interest, tax, depreciation, and amortization expenses) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14103
        /// </remarks>
        [JsonProperty("14103")]
        public double EVToEBITDA5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's EV to free cash flow on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by free cash flow (Cash flow from operations - Capital Expenditures) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14104
        /// </remarks>
        [JsonProperty("14104")]
        public double EVToFCF5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's EV to revenue on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Revenue reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14105
        /// </remarks>
        [JsonProperty("14105")]
        public double EVToRevenue5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's EV to total assets on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by total assets reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14106
        /// </remarks>
        [JsonProperty("14106")]
        public double EVToTotalAssets5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's price to free cash flow ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the free cash flow reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14107
        /// </remarks>
        [JsonProperty("14107")]
        public double PFCFRatio5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's price to book ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the book value per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14108
        /// </remarks>
        [JsonProperty("14108")]
        public double PBRatio5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's PE ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14109
        /// </remarks>
        [JsonProperty("14109")]
        public double PERatio5YearGrowth { get; set; }

        /// <summary>
        /// The five-year growth in the company's price to sales ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the sales per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14110
        /// </remarks>
        [JsonProperty("14110")]
        public double PSRatio5YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's EV to EBITDA on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by EBITDA (earnings minus expenses excluding interest, tax, depreciation, and amortization expenses) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14111
        /// </remarks>
        [JsonProperty("14111")]
        public double EVToEBITDA10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's EV to free cash flow on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by free cash flow (Cash flow from operations - Capital Expenditures) reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14112
        /// </remarks>
        [JsonProperty("14112")]
        public double EVToFCF10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's EV to revenue on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by Total Revenue reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14113
        /// </remarks>
        [JsonProperty("14113")]
        public double EVToRevenue10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's EV to total assets on a percentage basis. Morningstar calculates the growth percentage based on the enterprise value (Market Cap + Preferred stock + Long-Term Debt And Capital Lease + Short Term Debt And Capital Lease + Securities Sold But Not Yet Repurchased - Cash, Cash Equivalent And Market Securities - Securities Purchased with Agreement to Resell - Securities Borrowed) divided by total assets reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14114
        /// </remarks>
        [JsonProperty("14114")]
        public double EVToTotalAssets10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's price to free cash flow ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the free cash flow reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14115
        /// </remarks>
        [JsonProperty("14115")]
        public double PFCFRatio10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's price to book ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the book value per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14116
        /// </remarks>
        [JsonProperty("14116")]
        public double PBRatio10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's PE ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the earnings per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14117
        /// </remarks>
        [JsonProperty("14117")]
        public double PERatio10YearGrowth { get; set; }

        /// <summary>
        /// The ten-year growth in the company's price to sales ratio on a percentage basis. Morningstar calculates the growth percentage based on the adjusted close price divided by the sales per share reported in the Financial Statements within the company filings or reports.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14118
        /// </remarks>
        [JsonProperty("14118")]
        public double PSRatio10YearGrowth { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of estimated EBIT in year 2.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14119
        /// </remarks>
        [JsonProperty("14119")]
        public double TwoYrsEVToForwardEBIT { get; set; }

        /// <summary>
        /// Indicates what is a company being valued per each dollar of estimated EBITDA in year 2.
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14120
        /// </remarks>
        [JsonProperty("14120")]
        public double TwoYrsEVToForwardEBITDA { get; set; }

        /// <summary>
        /// EPS Growth Ratio: (Estimated EPS Year 1) / (TTM Normalized diluted EPS
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14121
        /// </remarks>
        [JsonProperty("14121")]
        public double FirstYearEstimatedEPSGrowth { get; set; }

        /// <summary>
        /// EPS Growth Ratio: (Estimated EPS Year 2) / (Estimated EPS Year 1)
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14122
        /// </remarks>
        [JsonProperty("14122")]
        public double SecondYearEstimatedEPSGrowth { get; set; }

        /// <summary>
        /// Normalized ForwardPERatio / Long-term Average Normalized Earnings Growth Rate
        /// </summary>
        /// <remarks>
        /// Morningstar DataId: 14123
        /// </remarks>
        [JsonProperty("14123")]
        public double NormalizedPEGRatio { get; set; }

        /// <summary>
        /// Creates a new instance for the given time and security
        /// </summary>
        public ValuationRatiosExt()
        {
        }
    }
}

D:\Lean-16863\ToolBox\CoarseUniverseGenerator\CoarseUniverseGeneratorProgram.cs修改

Parallel.ForEach(outputCoarseContent, coarseByDate =>
{
    var filename = $"{coarseByDate.Key.ToString(DateFormat.EightCharacter, CultureInfo.InvariantCulture)}.csv";
    var filePath = Path.Combine(_destinationFolder.FullName, filename);
    Log.Debug($"CoarseUniverseGeneratorProgram.Run(): Saving {filename} with {coarseByDate.Value.Count} entries.");
    /此行作用是写入新因子值
    File.WriteAllLines(filePath, coarseByDate.Value.Select(x => CoarseFundamentalExt.ToRowExt(x)).OrderBy(cr => cr));
    var filesCount = Interlocked.Increment(ref coarseFilesGenerated);
    if (filesCount % 1000 == 0)
    {
        var elapsed = DateTime.UtcNow - startWriting;
        Log.Trace($"CoarseUniverseGeneratorProgram.Run(): Processed {filesCount} in {elapsed:g} at {filesCount / elapsed.TotalSeconds:F2} files/second ");
    }
});

在这里插入图片描述

修改D:\Lean-16863\Common\Data\Fundamental\FundamentalInstanceProvider.cs

/*
 * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
 * Lean Algorithmic Trading Engine v2.0. Copyright 2023 QuantConnect Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Fasterflect;
using QuantConnect.Configuration;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Interfaces;
using QuantConnect.Util;
using static QuantConnect.Data.UniverseSelection.CoarseFundamentalDataProviderExt;

namespace QuantConnect.Data.Fundamental
{
    /// <summary>
    /// Per symbol we will have a fundamental class provider so the instances can be reused
    /// </summary>
    public class FundamentalInstanceProvider
    {
        private static readonly Dictionary<SecurityIdentifier, FundamentalInstanceProvider> _cache = new();

        private readonly FundamentalTimeProvider _timeProvider;
        private readonly FinancialStatements _financialStatements;
        private readonly OperationRatios _operationRatios;
        private readonly SecurityReference _securityReference;
        private readonly CompanyReference _companyReference;
        private readonly CompanyProfile _companyProfile;
        private readonly AssetClassification _assetClassification;
        private ValuationRatios _valuationRatios;
        private readonly EarningRatios _earningRatios;
        private readonly EarningReports _earningReports;

        private DateTime _date;
        private Symbol _symbol;

        public static IDataProvider dataProvider;
        /// <summary>
        /// Get's the fundamental instance provider for the requested symbol
        /// </summary>
        /// <param name="symbol">The requested symbol</param>
        /// <returns>The unique instance provider</returns>
        public static FundamentalInstanceProvider Get(Symbol symbol)
        {
            FundamentalInstanceProvider result = null;
            lock (_cache)
            {
                _cache.TryGetValue(symbol.ID, out result);
            }

            if (result == null)
            {
                // we create the fundamental instance provider without holding the cache lock, this is because it uses the pygil
                // Deadlock case: if the main thread has PyGil and wants to take lock on cache (security.Fundamentals use case) and the data
                // stack thread takes the lock on the cache (creating new fundamentals) and next wants the pygil deadlock!
                result = new FundamentalInstanceProvider(symbol);
                lock (_cache)
                {
                    _cache[symbol.ID] = result;
                }
            }
            return result;
        }

        /// <summary>
        /// Creates a new fundamental instance provider
        /// </summary>
        /// <param name="symbol">The target symbol</param>
        private FundamentalInstanceProvider(Symbol symbol)
        {
            _timeProvider = new();
            _financialStatements = new(_timeProvider, symbol.ID);
            _operationRatios = new(_timeProvider, symbol.ID);
            _securityReference = new(_timeProvider, symbol.ID);
            _companyReference = new(_timeProvider, symbol.ID);
            _companyProfile = new(_timeProvider, symbol.ID);
            _assetClassification = new(_timeProvider, symbol.ID);
            _valuationRatios = new(_timeProvider, symbol.ID);
            _earningRatios = new(_timeProvider, symbol.ID);
            _earningReports = new(_timeProvider, symbol.ID);
        }

        /// <summary>
        /// Returns the ValuationRatios instance
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ValuationRatios GetValuationRatios(DateTime time, Symbol symbol)
        {
            _timeProvider.Time = time;
            if (time == _date)
            {
                return _valuationRatios;
            }
            _date = time;
            var path = Path.Combine(Globals.DataFolder, "equity", "usa", "fundamental", "coarse", $"{time:yyyyMMdd}.csv");
            var fileStream = dataProvider.Fetch(path);
            if (fileStream == null)
            {
                return _valuationRatios;
            }

            //_coarseFundamental.Clear();
            using (var reader = new StreamReader(fileStream))
            {
                while (!reader.EndOfStream)
                {
                    var line = reader.ReadLine();
                    var csv = line.Split(',');

                    if (csv[1] != symbol.Value)
                    {
                        continue;
                    }
                    else
                    {
                        Read(line, time);
                        break;
                    }
                }
            }

            return _valuationRatios;
        }

        private void Read(string line, DateTime time)
        {
            try
            {
                var csv = line.Split(',');

                if (csv.Length > 8)
                {
                    Type type = typeof(ValuationRatios);
                    PropertyInfo[] propertyInfos = type.GetProperties().Where(p => p.PropertyType == typeof(double)).ToArray();
                    for (int i = 8, j = 0; (i < csv.Length) && (j < propertyInfos.Length); i++, j++)
                    {
                        _valuationRatios.SetPropertyValue(propertyInfos[j].Name, double.Parse(csv[i]));
                    }
                    var d = 0;
                }
            }
            catch (Exception ex)
            {
                var msg = ex.Message;
            }
        }

        /// <summary>
        /// Returns the EarningRatios instance
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public EarningRatios GetEarningRatios(DateTime time)
        {
            _timeProvider.Time = time;
            return _earningRatios;
        }

        /// <summary>
        /// Returns the EarningReports instance
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public EarningReports GetEarningReports(DateTime time)
        {
            _timeProvider.Time = time;
            return _earningReports;
        }

        /// <summary>
        /// Returns the OperationRatios instance
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public OperationRatios GetOperationRatios(DateTime time)
        {
            _timeProvider.Time = time;
            return _operationRatios;
        }

        /// <summary>
        /// Returns the FinancialStatements instance
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public FinancialStatements GetFinancialStatements(DateTime time)
        {
            _timeProvider.Time = time;
            return _financialStatements;
        }

        /// <summary>
        /// Returns the SecurityReference instance
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public SecurityReference GetSecurityReference(DateTime time)
        {
            _timeProvider.Time = time;
            return _securityReference;
        }

        /// <summary>
        /// Returns the CompanyReference instance
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public CompanyReference GetCompanyReference(DateTime time)
        {
            _timeProvider.Time = time;
            return _companyReference;
        }

        /// <summary>
        /// Returns the CompanyProfile instance
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public CompanyProfile GetCompanyProfile(DateTime time)
        {
            _timeProvider.Time = time;
            return _companyProfile;
        }

        /// <summary>
        /// Returns the AssetClassification instance
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public AssetClassification GetAssetClassification(DateTime time)
        {
            _timeProvider.Time = time;
            return _assetClassification;
        }

        private class FundamentalTimeProvider : ITimeProvider
        {
            public DateTime Time;

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public DateTime GetUtcNow() => Time;
        }
    }
}

修改D:\Lean-16863\Common\Data\UniverseSelection\FundamentalService.cs传递DataProvider

        /// <summary>
        /// Initializes the service
        /// </summary>
        /// <param name="dataProvider">The data provider instance to use</param>
        /// <param name="liveMode">True if running in live mode</param>
        public static void Initialize(IDataProvider dataProvider, bool liveMode)
        {
        	///传递
            FundamentalInstanceProvider.dataProvider = dataProvider;
            Initialize(dataProvider, Config.Get("fundamental-data-provider", nameof(CoarseFineFundamentalDataProvider)), liveMode);
        }

测试策略CoarseFineFundamentalComboAlgorithm.cs

/*
 * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
 * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;

namespace QuantConnect.Algorithm.CSharp
{
    /// <summary>
    /// Demonstration of how to define a universe filtered by the combination of coarse
    /// fundamental data and fine fundamental data. This lets you do a first pass based on the asset volume; then later
    /// select based on the company fundamentals.
    /// </summary>
    /// <meta name="tag" content="using data" />
    /// <meta name="tag" content="universes" />
    /// <meta name="tag" content="coarse universes" />
    /// <meta name="tag" content="fine universes" />
    public class CoarseFineFundamentalComboAlgorithm : QCAlgorithm
    {
        private const int NumberOfSymbolsCoarse = 5;
        private const int NumberOfSymbolsFine = 2;

        // initialize our changes to nothing
        private SecurityChanges _changes = SecurityChanges.None;

        public override void Initialize()
        {
            UniverseSettings.Resolution = Resolution.Daily;
            SetStartDate(2014, 04, 01);
            SetEndDate(2014, 04, 30);
            SetCash(50000);

            // this add universe method accepts two parameters:
            // - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol>
            // - fine selection function: accepts an IEnumerable<FineFundamental> and returns an IEnumerable<Symbol>
            AddUniverse(CoarseSelectionFunction, FineSelectionFunction);
        }

        // sort the data by daily dollar volume and take the top 'NumberOfSymbolsCoarse'
        public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
        {
            // select only symbols with fundamental data and sort descending by daily dollar volume
            var sortedByDollarVolume = coarse
                .Where(x => x.HasFundamentalData)
                .OrderByDescending(x => x.DollarVolume);

            // take the top entries from our sorted collection
            var top5 = sortedByDollarVolume.Take(NumberOfSymbolsCoarse);
            // we need to return only the symbol objects
            return top5.Select(x => x.Symbol);
        }

        // sort the data by P/E ratio and take the top 'NumberOfSymbolsFine'
        public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
        {
            // sort descending by P/E ratio
            var sortedByPeRatio = fine.OrderByDescending(x => x.ValuationRatios.PERatio);

            // take the top entries from our sorted collection
            var topFine = sortedByPeRatio.Take(NumberOfSymbolsFine);
            foreach (var symbol in topFine)
            {
                Error("--------------------------------------------------");
                Error(symbol.Time.ToString());
                Error(string.Format("symbol={0}, peratio={1}", symbol.Symbol, symbol.ValuationRatios.PERatio));
                Error("--------------------------------------------------");
            }
            // we need to return only the symbol objects
            return topFine.Select(x => x.Symbol);
        }

        //Data Event Handler: New data arrives here.
        public override void OnData(Slice slice)
        {
            // if we have no changes, do nothing
            if (_changes == SecurityChanges.None) return;

            // liquidate removed securities
            foreach (var security in _changes.RemovedSecurities)
            {
                if (security.Invested)
                {
                    Liquidate(security.Symbol);
                    Debug("Liquidated Stock: " + security.Symbol.Value);
                }
            }

            // we want 50% allocation in each security in our universe
            foreach (var security in _changes.AddedSecurities)
            {
                SetHoldings(security.Symbol, 0.5m);
                Debug("Purchased Stock: " + security.Symbol.Value);
            }

            _changes = SecurityChanges.None;
        }

        // this event fires whenever we have changes to our universe
        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            _changes = changes;

            if (changes.AddedSecurities.Count > 0)
            {
                Debug("Securities added: " + string.Join(",", changes.AddedSecurities.Select(x => x.Symbol.Value)));
            }
            if (changes.RemovedSecurities.Count > 0)
            {
                Debug("Securities removed: " + string.Join(",", changes.RemovedSecurities.Select(x => x.Symbol.Value)));
            }
        }
    }
}

可见随机生成的因子值可以被策略读取了,从而行情数据、基本面数据完成全部随机生成,基本可以支持多类型策略
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值