Back to Community
Why does Morningstar shares_outstanding give me a float and negative numbers?

Hi I am trying to create a simple factor that is a combination of a few fundamental values. When I access shares_outstanding from Morningstar, I get values that do not make sense. The values are all floating point numbers, and some are negative. What am I doing wrong?

Please see attached backtest for code, I have tried to access shares_outstanding ~5 different ways and I get the same results.

Clone Algorithm
0
Loading...
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
"""
Sample algo for using in the What Works on Quantopian Research.
This template is based on an example algorithm from Daniel Cascio.
"""

# Import required pipeline methods
from quantopian.pipeline import Pipeline, CustomFactor
from quantopian.algorithm import attach_pipeline, pipeline_output

# Import any built in filters and/or factors
from quantopian.pipeline.domain import US_EQUITIES
from quantopian.pipeline.filters import QTradableStocksUS, Q500US
from quantopian.pipeline.data.factset import RBICSFocus
from quantopian.pipeline.data import EquityPricing, morningstar, Fundamentals

# Import optimize
import quantopian.optimize as opt
import pandas as pd
import numpy as np

def initialize(context):
    # Normally a contest algo uses the default commission and slippage
    # This is unique and only required for this 'mini-contest'
    set_commission(commission.PerShare(cost=0.000, min_trade_cost=0))    
    set_slippage(slippage.FixedSlippage(spread=0))

    attach_pipeline(make_pipeline(context), 'pipeline') 

    # Place orders towards the end of each day
    schedule_function(rebalance, date_rules.every_day(), time_rules.market_close(hours=2))
               
    # Record any custom data at the end of each day    
    schedule_function(record_positions, date_rules.every_day(), time_rules.market_close())      

        
class Sales_to_EV(CustomFactor):     
    inputs = [Fundamentals.shares_outstanding] 
        # inputs = [morningstar.valuation.enterprise_value,
        #       morningstar.valuation_ratios.sales_per_share,
        #       morningstar.valuation.shares_outstanding]
    
    window_length = 1
    def compute(self, today, assets, out, so):
        out[:] = so[-1]
        
def create_factor():
    # Use the QTradableStocksUS universe as a base
    qtu = QTradableStocksUS()
    
    # Create an alpha factor
    # alpha_factor = Sales_to_EV()
    alpha_factor = Fundamentals.shares_outstanding.latest
    
    # Filter out securities with very few estimates or factor is invalid
    screen = qtu & ~alpha_factor.isnull() & alpha_factor.isfinite()
    
    return alpha_factor, screen

def make_pipeline(context):  
    alpha_factor, screen = create_factor()
    
    # Winsorize to remove extreme outliers
    alpha_winsorized = alpha_factor.winsorize(min_percentile=0.01,
                                              max_percentile=0.99,
                                              mask=screen)
    
    # Zscore to get long and short (positive and negative) alphas to use as weights
    alpha_zscore = alpha_winsorized.zscore()
    
    return Pipeline(columns={'alpha_factor': alpha_zscore,}, 
                    screen=screen, domain=US_EQUITIES)

def rebalance(context, data): 
    # Get the alpha factor data from the pipeline output
    output = pipeline_output('pipeline')
    alpha_factor = output.alpha_factor
    print('size of alpha_factor: {}'.format(alpha_factor.shape[0]))
    print(alpha_factor.head())
    
    # Weight securities by their alpha factor
    # Divide by the abs of total weight to create a leverage of 1
    weights = alpha_factor / alpha_factor.abs().sum() 
    
    # Must use TargetWeights as an objective
    order_optimal_portfolio(
        objective=opt.TargetWeights(weights),
        constraints=[],
    )
    
def record_positions(context, data):
    pos = pd.Series()
    for position in context.portfolio.positions.values():
        pos.loc[position.sid] = position.amount
        
    pos /= pos.abs().sum()
    
    # Show quantiles of the daily holdings distribution
    # to show if weights are being squashed to equal weight
    # or whether they have a nice range of sensitivity.
    quantiles = pos.quantile([.05, .25, .5, .75, .95]) * 100
    record(q05=quantiles[.05])
    record(q25=quantiles[.25])
    record(q50=quantiles[.5])
    record(q75=quantiles[.75])
    record(q95=quantiles[.95])
There was a runtime error.
1 response

Never mind, I was look at the result after it was Z-scored. Removing the Z-score shows only positive integers.