Back to Community
Pipeline Implementation

I had posted earlier today about using an Excel spreadsheet to execute my screened stocks using Quantopian. The screener parameters follow below. Is this feasible to implement with Pipeline? I've been trying to get it to work, but am not familiar enough with Python or using the correct syntax to get it 'just right'

MktCapM is >= US$ 300 million (basis year 2000) adjusted yearly
Rank all stocks according to Price 1-Day ago / TTM Free Cash Flow Per Share (lower is better)
Rank all stocks according to ( TTM Cash from Operations – TTM Net Income) / latest Filing Market Cap (higher is better)
Rank all stocks according to TTM Cash from Financing Activities / latest Filing Market Cap (lower is better)
Rank all stocks according to Latest FY Return on Invested Capital (higher is better)
Rank all stocks according to 26 week Excess Total return (higher is better)
Take the 200 stocks with the smallest sum of these ranks added up.

3 responses

Attached below is a backtest that sort of follows the general theory of the screener, however, this does not accurately reflect my trades or performance on the live market.

Clone Algorithm
5
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
"""
Trading Strategy: Let Morningstar do the legwork

    Buy all the companies trading above an N day moving average
    with growth and profitability grades from Morningstar in the A-B range, 
    put any remaining capital in long term bonds. 
    Rebalance monthly
    
"""
from quantopian.pipeline import Pipeline
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.data import morningstar
from quantopian.pipeline.factors import SimpleMovingAverage
import math
import pandas as pd
import numpy as np


def initialize(context):
    set_slippage(slippage.FixedSlippage(spread=0.10))
    set_commission(commission.PerShare(cost=0.01, min_trade_cost=1.0))
    context.num_stocks = 20
    context.leverage = 1.0
    
    fundamental_pipe = make_fundamental_pipeline()
    attach_pipeline(fundamental_pipe, 'fundamental_pipe')
    
    context.bonds = symbols('TLT')
    schedule_function(func=buy_stocks, 
                      date_rule=date_rules.month_start())
    schedule_function(func=buy_bonds, 
                      date_rule=date_rules.month_start(1))
    schedule_function(func=record_leverage,
                      date_rule=date_rules.every_day())
    
def record_leverage(context, data):
    record(leverage=context.account.leverage)
    
    
def buy_bonds(context, data):
    P = context.portfolio    
    market_value = sum(data.current(i, 'price') * abs(P.positions[i].amount) for i in context.portfolio.positions)
    leverage = market_value / max(1, P.portfolio_value)
    gap = max(context.leverage - leverage, 0.0)
    weight = gap / len(context.bonds)
    record(bond_pct=gap*100)
    for bnd in context.bonds:
        if not math.isnan(weight):
            order_target_percent(bnd, weight)
        
    
def buy_stocks(context, data):
    fundies = context.stocks['price'].index.tolist()
    count = 0    
    for stock in fundies:
        if data.can_trade(stock):
            order_target_percent(stock, context.leverage / context.num_stocks)
            count = count + 1
            if count >= context.num_stocks:
                break

    # Exit Securities
    for stock in context.portfolio.positions:
        if stock not in fundies and \
           stock not in context.bonds and\
           data.can_trade(stock):
            order_target_percent(stock, 0)


def before_trading_start(context, data): 
    """
      Called before the start of each trading day. 
      It updates our universe with the
      securities and values found from our fundamental pipeline.
    """
    pipe_out = pipeline_output('fundamental_pipe').sort_values(by='ev_to_ebitda',ascending=True)
    context.stocks = pipe_out.iloc[0:context.num_stocks]

def make_fundamental_pipeline():

    # Factors we are tracking
    latest_close = USEquityPricing.close.latest
    ev_to_ebitda = morningstar.valuation_ratios.ev_to_ebitda.latest
    cash_flow_per_share = morningstar.valuation_ratios.cfo_per_share.latest
    cash_from_operations = morningstar.cash_flow_statement.operating_cash_flow.latest
    net_income = morningstar.cash_flow_statement.net_income.latest
    market_cap = morningstar.valuation.market_cap
    financing_act = morningstar.cash_flow_statement.cash_flow_from_continuing_financing_activities.latest
    roic = morningstar.operation_ratios.roic.latest

    ev_to_ebitda_filter = (ev_to_ebitda > 0) & (ev_to_ebitda < 10)
    cash_filter = (latest_close / cash_flow_per_share).percentile_between(0, 25)
    ops_to_cap_filter = ((cash_from_operations - net_income) / market_cap).percentile_between(75, 100)
    financing_act_filter = (financing_act / market_cap).percentile_between(0, 25)
    roic_filter = roic.percentile_between(75, 100)
   
    mask = ev_to_ebitda_filter & cash_filter & ops_to_cap_filter & financing_act_filter & roic_filter
   
    return Pipeline(columns={'price':latest_close, 'ev_to_ebitda':ev_to_ebitda}, screen=mask)








There was a runtime error.

After thinking about it for a while, is it possible to give the stocks a score using something along the lines of "context.score", tally that up, and then take the top stocks out of that using the pipeline parameters?

Maybe take a look at this post for a simple way to combine ranks and get a 'context.score' as you noted https://www.quantopian.com/posts/how-to-combine-factors-in-alphalens .

Also, Trailing Twelve Month (TTM) fundamental data isn't directly available in the Morningstar dataset. The Morningstar data returns whatever was reported on the latest quarterly filing. This may or may not impact your algorithm.