def make_pipeline():

    base_universe = QTradableStocksUS()

    gross_margin = morningstar.operation_ratios.gross_margin.latest

    roa = morningstar.operation_ratios.roa.latest

    factor_to_analyze = gross_margin.zscore() + roa.zscore()

    sector = Sector()

    return Pipeline(columns={
        'factor to analyze': factor_to_analyze,
        'sector': sector
    },
                    screen=base_universe & sector.notnull()
                    & factor_to_analyze.notnull())
def initialize(context):
    # To set a custom benchmark the following function can be called:
    # set_benchmark(symbol('IWV'))
    # Otherwise the default benchmark will be used (SPY).

    # Commission is set to be $0.005 per share and $1 per trade.
    set_commission(
        us_equities=commission.PerShare(cost=0.005, min_trade_cost=1))

    # Exchange code of a firm.
    exchange = mstar.share_class_reference.exchange_id.latest

    # A filter rule is created that returns True only for
    # the stocks from the exchanges listed.
    my_exchanges = exchange.element_of(['NYSE', 'NYS', 'NAS', 'ASE'])

    # Market capitalisation, sector code and momentum of a firm.
    market_cap = MarketCap()
    sector = Sector()
    umd = Momentum()

    # Defining total_equity, operating_income and interest_expense as
    # corresponding values in the latest income statement and balance sheet.
    total_equity = mstar.balance_sheet.total_equity.latest
    operating_income = mstar.income_statement.operating_income.latest
    interest_expense = mstar.income_statement.interest_expense.latest

    # The trading universe is defined as QTradableStocksUS that falls into
    # my_exchanges and has data for umd, total_equity, operating_income,
    # interest_expense, market_cap and sector.
    universe_exchange = QTradableStocksUS() & umd.notnull(
    ) & my_exchanges & total_equity.notnull() & market_cap.notnull(
    ) & sector.notnull() & operating_income.notnull(
    ) & interest_expense.notnull()

    # Small and large market cap groups specified as percentile.
    small = (MarketCap(mask=universe_exchange).percentile_between(0, 50))
    large = (MarketCap(mask=universe_exchange).percentile_between(50, 100))

    # Create a filter that returns True for the assets in the universe
    # that belong to the given sector(s).
    sec = mstar.asset_classification.morningstar_sector_code.latest
    my_sec = sec.element_of([101])

    # Here the universe redefined as universe_exchange that belongs
    # to the sector(s) in 'my_sec' and falls into either
    # small or large market cap group as defined above.
    # my_sec should be uncommented in case if a speficic sector is wanted.
    '''
    Here are the sector codes that might be used:
    
     -1: 'Misc',  
    101: 'Basic Materials',  
    102: 'Consumer Cyclical',  
    103: 'Financial Services',  
    104: 'Real Estate',  
    205: 'Consumer Defensive',  
    206: 'Healthcare',  
    207: 'Utilities',  
    308: 'Communication Services',  
    309: 'Energy',  
    310: 'Industrials',  
    311: 'Technology' , 
    '''
    universe = universe_exchange & small  #& my_sec

    # Book to market is defined as total_equity divided by the market_cap.
    # The value is normalised and ranked in an ascending order.
    bm = total_equity / market_cap
    bm_weights = bm.rank(ascending=True, mask=universe)

    # Operating profitability ratio is defined as operating_income subtracted
    # interest_expense divided by the total_equity.
    # The value is normalised and ranked in an ascending order.
    op = (operating_income - interest_expense) / total_equity
    op_weights = op.rank(ascending=True, mask=universe)

    # Price momentum values are ranked and normalised in an ascending order.
    umd_weights = umd.rank(ascending=True, mask=universe)

    # A class JoinFactors is defined that is used to combine the normalised
    # scores of the factors defined above.
    class JoinFactors(CustomFactor):
        #inputs = [factor1, factor2, ...] There can be multiple inputs.
        window_length = 1

        def compute(self, today, assets, out, *inputs):
            array = np.concatenate(inputs, axis=0)
            out[:] = np.nansum(array, axis=0)
            out[np.all(np.isnan(array), axis=0)] = np.nan

    # window_safe declares that scores of the factors are robust to
    # pricing adjustments from splits or dividends. In other words,
    # the value that will be the same no matter what day you are
    # looking back from. This is a required step in order to
    # use them as the input to JoinFactors.
    bm_weights.window_safe = True
    op_weights.window_safe = True
    umd_weights.window_safe = True

    # The weights of the combined factor. 1, 2, 3 or more factors can be used.
    final_weights = JoinFactors(inputs=[bm_weights, op_weights, umd_weights],
                                mask=universe)
    universe = final_weights.notnan()

    # The Pipeline object filled with the data defined above is returned.
    pipe = Pipeline(
        columns={
            'bm_weights': bm_weights,
            'op_weights': op_weights,
            'umd_weights': umd_weights,
            'alpha': final_weights,
            'exchange': exchange,
            'market_cap': market_cap,
            'sector': sector,
        },
        # Screen out all the data points outside the trading universe.
        screen=universe)

    # The function attach_pipeline is called
    # to load the data in defined in the pipeline.
    algo.attach_pipeline(pipe, 'pipe')

    # Schedule a function, 'do_portfolio_construction', to run once a month
    # ten minutes after market is open.
    algo.schedule_function(
        do_portfolio_construction,
        date_rule=algo.date_rules.month_start(),
        time_rule=algo.time_rules.market_open(minutes=MINUTES_AFTER_MARKET),
        half_days=False,
    )