Back to Community
Free Cash Flow to Enterprise Value - Template Fundamental Algo

After trawling through the stats on the many, many backtests that everyone in the community has developed, we at Quantopian have determined that a new series of template algorithms is warranted. We have the benefit of looking at community activity cross-sectionally and we have seen that there is a lot of strong development work on technical signals (mean reversal and momentum chiefly). Unsurprisingly, there are many fewer algorithms that have tapped into fundamental signals for their sources of predictive power. This algo is a great starting point for anyone looking to incorporate fundamentals-driven signals into their repertoire.

FCF/EV

Free cash flow (FCF) is a measure of how much cash a company has on hand after all expenses are extracted. High FCF indicates that larger amounts of cash are available to the company for reinvestment. By dividing by a company’s enterprise value (EV), we can compute a ratio that shows how cash is generated per unit of the value of a company. In this implementation, we can test the idea that companies with a relatively higher ratio of FCF/EV are likely to outperform companies with relatively lower levels of FCF/EV. Read more here.

As we look to expand the set of algorithms receiving allocations over the next few months we expect to give preference to new ideas that take advantage of a broader range of fundamental factors.

To get started, clone this algorithm, improve it with your own ideas, and submit it to the Quantopian Daily Contest.

N.B. As implemented here, this algo doesn't fully meet all of the criteria for entry in the daily contest so we're leaving that as an "exercise for the reader".

Clone Algorithm
Loading...
Backtest from to with initial capital
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
import numpy as np
import quantopian.algorithm as algo
import quantopian.optimize as opt
from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import CustomFactor
from quantopian.pipeline.filters import QTradableStocksUS
from quantopian.pipeline.data import Fundamentals

ZSCORE_FILTER = 3 # Maximum number of standard deviations to include before counting as outliers
ZERO_FILTER = 0.001 # Minimum weight we allow before dropping security
        

def initialize(context):
    
    algo.attach_pipeline(make_pipeline(), 'alpha_factor_template')

    # Schedule our rebalance function
    algo.schedule_function(func=rebalance,
                           date_rule=algo.date_rules.week_start(),
                           time_rule=algo.time_rules.market_open(),
                           half_days=True)

    # Record our portfolio variables at the end of day
    algo.schedule_function(func=record_vars,
                           date_rule=algo.date_rules.every_day(),
                           time_rule=algo.time_rules.market_close(),
                           half_days=True)


def make_pipeline():
    # Setting up the variables
    alpha_factor = (Fundamentals.fcf_per_share.latest * Fundamentals.shares_outstanding.latest) / \
                    Fundamentals.enterprise_value.latest

    # Standardized logic for each input factor after this point
    alpha_w = alpha_factor.winsorize(min_percentile=0.02,
                                     max_percentile=0.98,
                                     mask=QTradableStocksUS() & alpha_factor.isfinite())

    alpha_z = alpha_w.zscore()
    alpha_weight = alpha_z / 100.0

    outlier_filter = alpha_z.abs() < 3
    zero_filter = alpha_weight.abs() > 0.001

    universe = QTradableStocksUS() & \
               outlier_filter & \
               zero_filter

    pipe = Pipeline(
        columns={
            'alpha_weight': alpha_weight
        },
        screen=universe
    )
    return pipe


def before_trading_start(context, data):
    context.pipeline_data = algo.pipeline_output('alpha_factor_template')


def record_vars(context, data):
    # Plot the number of positions over time.
    algo.record(num_positions=len(context.portfolio.positions))
    algo.record(leverage=context.account.leverage)

    
def rebalance(context, data):
    # Retrieve pipeline output
    pipeline_data = context.pipeline_data
    
    alpha_weight = pipeline_data['alpha_weight']
    alpha_weight_norm = alpha_weight / alpha_weight.abs().sum()


    objective = opt.TargetWeights(alpha_weight_norm)

    # No constraints currently
    constraints = []
    
    algo.order_optimal_portfolio(
        objective=objective,
        constraints=constraints
    )
There was a runtime error.
Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

9 responses

Stragenly enough, I have had an (FCF/EV)-based algo running in the competition for the past few months. It has not done very well, but it (just about) meets the criteria.

One stupid question... Why this strategy is so correlated with SPY? Is there an economical reason of this?

The initial implementation was fairly net long, resulting in a higher beta exposure. I just updated the algo to have more upstream NaN filtering, resulting in a lower Net Dollar Exposure as well as a lower Beta exposure.

Hi Q Team,

Just a bit of feedback: One thing I struggle with when using Morningstar fundamental data is knowing if data from the Income and Cash Flow statements is quarterly (only) or for a full trailing-twelve-month (ttm) year. I believe companies normally report both in their 10Qs, but I don't know how to confirm which one you provide without having to cross validate with another source. The Fundamentals datafield page only has this:

free_cash_flow
Cash Flow Operations minus Capital Expenditures.

It would be great if the Fundamentals datafield page could confirm if the data is quarterly or annual (ttm) for the Income and Cash Flow statement fields. (6 or 9 months wouldn't really make sense to include here, so I believe it's neither of them)

For this one, I'm assuming Fundamentals. free_cash_flow.latest is indeed annual (ttm) as it's working so well. If it was only quarterly, I wouldn't expect it to work nearly as well due to seasonality effect (e.g. ice-cream companies have more free cashflow during summer quarter months than during winter quarter months :)).

Maybe I'm missing something obvious here though? Is there any general guideline as to which fields are quarterly and which are ttm annual? (Balance Sheet items are always 'point-in-time' so not applicable).

EV is not a great indicator of fundamental value. EV increases when a company takes on debt. Debt shouldn't be considered value IMO. EV is useful for Private Equity players who purchase entire companies along with their debt. Public Equity players don't really give a hoot what a company's EV is. The CFA program has glorified EV but I think it's widely misused and misunderstood.

Sorry, I just noticed you're using fcf_per_share rather, but still, how can we tell if this is the last quarter's fcf or annualized (ttm)? The description page only has this:

fcf_per_share
Free Cash Flow / Average Diluted Shares Outstanding

@Jay,

High EV = 'low value'.

Notice that they are dividing by EV. If you're looking for 'bargains' to go long, the lower the EV the better (lots of cash with little debt).

Not sure if this is helpful, but there is a form type field included as part of the fundamentals data. Prior to using the dataset, you can filter it to 10-Q.

Hi @Zak,

Thank you. Yes, that is helpful because I didn't know there was such a field and that you could filter using it.

However, I'm not asking for where the data is coming from (that I normally know) or how often it's updated (that I usually know as well - quarterly in this case, though some fields, like the MS 'scores' I believe are updated monthly, and of course anything with 'price' in it should update daily. Would be nice if this could be more clearly labeled as well though).

What I'm asking for is over what time period is the data representing? For example, does the fcf_per_share (or free_cash_flow) provide the fcf generated during the last 3 months (that the latest 10Q covers) or has it already been 'annualized' to the last trailing-twelve-months (ttm)? I.e. the quarterly fcf reported in the last four 10Qs?

In the 10Qs, what's 'important' and what companies are 'reporting' on is really only the 3 months that the 10Q covers, though sometimes companies also show the last 6 months, 9 months, ttm, and same quarter last year for comparison (I don't know what the (SEC?) exact regulation is and what companies are 'required' to report). So, are we then to 'assume' that any Income Statement or cashflow data fields only covers the last three months? I believe that's the case for some of the fields (free_cash_flow might be only 3 months perhaps?), but definitely not all fields.

Any of the Price / [sales, earnings, fcf] ratios, and any of the yields (excluding book yield) I would assume use ttm rather than only the last 3 months, otherwise they wouldn't really make much sense. Knowing what time-period the data is covering is crucial when trying to create alpha factors (e.g. should I 'annualize' or not? Can I compare last quarters figure to the same quarter a year ago, etc?).

Maybe the period_ending_date field can be used somehow to determine the period covered, but I would think one would then also need a 'period_starting_date' field, which I don't see. Maybe this is all very obvious to everyone else, and I'm the only one struggling with this? sigh :(

PS: Hoping that the FactSet Fundamental dataset will make this more clear.

@ Maxwell Margenot ,

How is your calculation of Free Cash Flow to Enterprise Value (FCF/EV) any different to Fundamentals.cash_return as described in the Fundamentals Help page as follows:

cash_return

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.

Any particular difference we should know about?