Ejemplo n.º 1
0
def initialize(context):

    universe = QTradableStocksUS()
    sector = Sector()
    momentum = Momentum()
    pipe = Pipeline()
    pipe.add(sector, 'sector')
    
    # ALPHA
    asset_turnover = Fundamentals.assets_turnover.latest.winsorize(.05, .95).zscore()
    ciwc = Fundamentals.change_in_working_capital.latest.winsorize(.05, .95).zscore()

    alpha = (asset_turnover + ciwc).rank().demean()
    pipe.add(alpha, 'alpha')
    
    # BETA
    beta = RollingLinearRegressionOfReturns(target=sid(8554),
                                            returns_length=5,
                                            regression_length=252,
                                            mask=alpha.notnull() & Sector().notnull()
                                            ).beta                    
    pipe.add(beta, 'beta')
    
    # REGISTER PIPELINES
    # ------------------
    attach_pipeline(pipe, 'pipe')
    attach_pipeline(risk_loading_pipeline(), 'risk_loading_pipeline')
    pipe.set_screen(alpha.notnull() & Sector().notnull() & beta.notnull() & universe & (momentum>0))
    
    # SCHEDULE FUNCTIONS
    # ------------------   
    schedule_function(rebalance, 
                      date_rule=date_rules.every_day(), 
                      time_rule=time_rules.market_open(minutes=10))
Ejemplo n.º 2
0
def make_pipeline():
    """
    Create and return our pipeline.
    We break this piece of logic out into its own function to make it easier to
    test and modify in isolation.
    
    """

    # Create our mean reversion factor by taking the negative of a momentum factor
    
    reversion = Reversion()

    # Classify all securities by sector so that we can enforce sector neutrality later
    sector = Sector()

    # Screen out non-desirable securities by defining our universe.
    universe = Q1500US()

    # By applying a mask to the rank computations, we remove any stocks that failed
    # to meet our initial criteria **before** computing ranks.  This means that the
    # stock with rank 10.0 is the 10th-lowest stock that was included in the Q1500US.
    # 標準偏差
    # filter のかけかたを mask で行ってしまう
    factor_rank = reversion.rank(mask=universe).zscore()

    # Build Filters representing the top and bottom 150 stocks by our ranking system.
    # We'll use these as our tradeable universe each day.
    longs = factor_rank.top(NUM_LONG_POSITIONS)
    shorts = factor_rank.bottom(NUM_SHORT_POSITIONS)

    # The final output of our pipeline should only include
    # the top/bottom 300 stocks by our criteria
    long_short_screen = (longs | shorts)

    # Define any risk factors that we will want to neutralize
    # We are chiefly interested in market beta as a risk factor so we define it using
    # Bloomberg's beta calculation
    # Ref: https://www.lib.uwo.ca/business/betasbydatabasebloombergdefinitionofbeta.html
    beta = 0.66 * RollingLinearRegressionOfReturns(
        target=sid(8554),
        returns_length=5,
        regression_length=260,
        mask=long_short_screen
    ).beta + 0.33*1.0

    # Create pipeline
    pipe = Pipeline(
        columns={
            'longs': longs,
            'shorts': shorts,
            'factor_rank': factor_rank,
            'reversion': reversion,
            'sector': sector,
            'market_beta': beta
        },
        screen=long_short_screen
    )
    return pipe
Ejemplo n.º 3
0
def initialize(context):

    # DEFINE UNIVERSE
    # ------------------
    universe = QTradableStocksUS()

    # ALPHA GENERATION
    # ----------------
    # COMPUTE Z SCORES: ASSET TURNOVER AND CHANGE IN WORKING CAPITAL
    # BOTH ARE FUNDAMENTAL VALUE MEASURES

    asset_turnover = Fundamentals.assets_turnover.latest.winsorize(
        .05, .95).zscore()
    ciwc = Fundamentals.change_in_working_capital.latest.winsorize(
        .05, .95).zscore()

    # ALPHA COMBINATION
    # -----------------
    # ASSIGN EVERY ASSET AN ALPHA RANK AND CENTER VALUES AT 0 (DEMEAN).
    alpha = (asset_turnover + 2 * ciwc).rank().demean()

    # BETA DEFINITION
    beta = 0.66 * RollingLinearRegressionOfReturns(
        target=sid(8554),
        returns_length=5,
        regression_length=252,
        mask=alpha.notnull() & Sector().notnull()).beta + 0.33 * 1.0

    # CREATE AND REGISTER PIPELINE
    #-------------------------------
    # COMPUTES COMBINES ALPHA AND SECTOR CODE FOR UNIVERSE OF STOCKS TO BE USED IN OPTIMIZATION
    pipe = Pipeline(
        columns={
            'alpha': alpha,
            'sector': Sector(),
            'beta': beta,
        },
        # RETURN ONLY "NOT NULL" VALUES IN OUR PIPELINE
        screen=alpha.notnull() & Sector().notnull() & beta.notnull()
        & universe,
    )

    # LOAD ALL PIPELINES
    algo.attach_pipeline(pipe, 'pipe')
    algo.attach_pipeline(risk_loading_pipeline(), 'risk_loading_pipeline')

    # SCHEDULE FUNCTIONS
    #--------------------
    algo.schedule_function(
        do_portfolio_construction,
        date_rule=algo.date_rules.week_start(),
        time_rule=algo.time_rules.market_open(minutes=10),
        half_days=False,
    )
Ejemplo n.º 4
0
def make_pipeline():

    # Define universe
    # ===============================================
    base_universe = QTradableStocksUS()
    value = morningstar.valuation_ratios.ev_to_ebitda.latest
    market_cap = morningstar.valuation.market_cap.latest > 2e9
    #Screen Value
    universe1 = value.bottom(2 * (NUM_LONG_POSITIONS + NUM_SHORT_POSITIONS),
                             mask=(QTradableStocksUS() & market_cap))
    universe2 = value.top(2 * (NUM_LONG_POSITIONS + NUM_SHORT_POSITIONS),
                          mask=(QTradableStocksUS() & market_cap))
    #Screen Quality
    #universe3 =
    #universe4 =
    universe = base_universe & (universe1 | universe2)
    sector = Sector(mask=universe)  # sector needed to construct portfolio
    # ===============================================

    factors = make_factors()

    combined_alpha = None
    for name, f in factors.iteritems():
        if combined_alpha == None:
            combined_alpha = f(mask=universe)
        else:
            combined_alpha = combined_alpha + f(mask=universe)

    longs = combined_alpha.top(NUM_LONG_POSITIONS)
    shorts = combined_alpha.bottom(NUM_SHORT_POSITIONS)

    long_short_screen = (longs | shorts)

    beta = 0.66 * RollingLinearRegressionOfReturns(
        target=sid(8554),
        returns_length=5,
        regression_length=260,
        mask=long_short_screen).beta + 0.33 * 1.0

    # Create pipeline
    pipe = Pipeline(columns={
        'combined_alpha': combined_alpha,
        'sector': sector,
        'market_beta': beta
    },
                    screen=long_short_screen)
    return pipe
Ejemplo n.º 5
0
def make_pipeline():

    # Sector
    sector = Sector()

    # Equity Filters
    mkt_cap_filter = morningstar.valuation.market_cap.latest >= 500000000
    price_filter = USEquityPricing.close.latest >= 5
    nan_filter = sentiment_free.sentiment_signal.latest.notnull()

    # Universe
    universe = Q1500US() & price_filter & mkt_cap_filter & nan_filter

    # Rank
    sentiment_signal = sentiment_free.sentiment_signal.latest
    sma_30 = SimpleMovingAverage(inputs=[sentiment_free.sentiment_signal],
                                 window_length=30,
                                 mask=universe)

    combined_rank = (sentiment_signal + sma_30.rank(mask=universe).zscore())

    # Long and Short Positions
    longs = combined_rank.top(NUM_LONG_POSITIONS)
    shorts = combined_rank.bottom(NUM_SHORT_POSITIONS)

    long_short_screen = (longs | shorts)

    # Bloomberg Beta Implementation
    beta = 0.66 * RollingLinearRegressionOfReturns(
        target=sid(8554),
        returns_length=5,
        regression_length=260,
        mask=long_short_screen).beta + 0.33 * 1.0

    pipe = Pipeline()
    pipe.add(longs, 'longs')
    pipe.add(shorts, 'shorts')
    pipe.add(combined_rank, 'combined_rank')
    pipe.add(sentiment_free.sentiment_signal.latest, 'sentiment_signal')
    pipe.add(sma_30, 'sma_30')
    pipe.add(sector, 'sector')
    pipe.add(beta, 'market_beta')
    pipe.set_screen(universe)

    return pipe
Ejemplo n.º 6
0
def make_pipeline():
    
   # Define universe
   # ===============================================    
    base_universe = QTradableStocksUS() 
    value = morningstar.valuation_ratios.ev_to_ebitda.latest
    market_cap = morningstar.valuation.market_cap.latest > 2e9   
    Long_universe = value.bottom(5*(NUM_LONG_POSITIONS), mask = (QTradableStocksUS() & market_cap))
    Short_universe = value.top(5*(NUM_LONG_POSITIONS), mask = (QTradableStocksUS() & market_cap))
    sector_code = morningstar.asset_classification.morningstar_sector_code.latest
    sector_screen = (~sector_code.eq(103) and ~sector_code.eq(104) )
    universe = QTradableStocksUS() & market_cap & sector_screen & (Long_universe | Short_universe)
    sector = Sector(mask=universe)  # sector needed to construct portfolio
    # ===============================================
    factors = make_factors()
    
    combined_alpha = None
    for name, f in factors.iteritems():
        if combined_alpha == None:
            combined_alpha = f(mask=universe)
        else:
            combined_alpha = combined_alpha + f(mask=universe)
    
    longs = combined_alpha.top(NUM_LONG_POSITIONS)
    #longs = (Long_universe & Quality)
    shorts = combined_alpha.bottom(NUM_SHORT_POSITIONS)
    #shorts = (Short_universe & bad_quality)
    long_short_screen = (longs | shorts)
    
    beta = 0.66*RollingLinearRegressionOfReturns(
                    target=sid(8554),
                    returns_length=5,
                    regression_length=260,
                    mask=long_short_screen
                    ).beta + 0.33*1.0

# Create pipeline
    pipe = Pipeline(columns = {
        'combined_alpha':combined_alpha,
        'sector':sector,
        'market_beta':beta
    },
    screen = long_short_screen)
    return pipe
Ejemplo n.º 7
0
def my_pipeline(context):
    pipe = Pipeline()

    revenue_growth = morningstar.operation_ratios.revenue_growth.latest
    dil_eps_growth = morningstar.earnings_ratios.diluted_eps_growth.latest
    net_income_growth = morningstar.operation_ratios.net_income_growth.latest
    operation_income_growth = morningstar.operation_ratios.operation_income_growth.latest

    #Filters
    # Only consider the top 10% of stocks ranked by dollar volume.
    dollar_volume = AverageDollarVolume(window_length=1)
    high_dollar_volume = dollar_volume.percentile_between(90, 100)
    #Traditional growth stocks metrics: >25% growth in all related factors
    rev_filter = revenue_growth > 0.20
    eps_filter = dil_eps_growth > 0.20
    income_filter = net_income_growth > 0.20
    opin_filter = operation_income_growth > 0.20

    # Create a regression factor with SPY.
    regression = RollingLinearRegressionOfReturns(
        target=context.target_asset,
        returns_length=context.returns_length,
        regression_length=context.regression_length,
        mask=high_dollar_volume,
    )
    alpha = regression.alpha
    beta = regression.beta
    correlation = regression.r_value
    low_beta = (beta < context.beta_threshold) & \
               (beta > -context.beta_threshold)
    high_beta = ~low_beta

    pipe.add(alpha, 'alpha')
    pipe.add(beta, 'beta')
    pipe.add(correlation, 'correlation')
    pipe.add(low_beta, 'low_beta')
    pipe.add(high_beta, 'high_beta')
    pipe.set_screen(rev_filter & eps_filter & income_filter & opin_filter
                    & high_dollar_volume)

    return pipe
    pass
def initialize(context):
    # Universe Selection
    # ------------------
    base_universe = QTradableStocksUS()

    # From what remains, each month, take the top UNIVERSE_SIZE stocks by average dollar
    # volume traded.
    monthly_top_volume = (AverageDollarVolume(
        window_length=LIQUIDITY_LOOKBACK_LENGTH).top(
            UNIVERSE_SIZE, mask=base_universe).downsample('week_start'))
    # The final universe is the monthly top volume &-ed with the original base universe.
    # &-ing these is necessary because the top volume universe is calculated at the start
    # of each month, and an asset might fall out of the base universe during that month.
    universe = monthly_top_volume & base_universe

    # Alpha Generation
    # ----------------
    # Compute Z-scores of free cash flow yield and earnings yield.
    # Both of these are fundamental value measures.
    fcf_zscore = Fundamentals.fcf_yield.latest.zscore(mask=universe)
    yield_zscore = Fundamentals.earning_yield.latest.zscore(mask=universe)
    sentiment_zscore = psychsignal.stocktwits.bull_minus_bear.latest.zscore(
        mask=universe)

    # Alpha Combination
    # -----------------
    # Assign every asset a combined rank and center the values at 0.
    # For UNIVERSE_SIZE=500, the range of values should be roughly -250 to 250.
    combined_alpha = (fcf_zscore + yield_zscore +
                      sentiment_zscore).rank().demean()

    beta = 0.66 * RollingLinearRegressionOfReturns(
        target=sid(8554),
        returns_length=5,
        regression_length=260,
        mask=combined_alpha.notnull() & Sector().notnull()).beta + 0.33 * 1.0

    # Schedule Tasks
    # --------------
    # Create and register a pipeline computing our combined alpha and a sector
    # code for every stock in our universe. We'll use these values in our
    # optimization below.
    pipe = Pipeline(
        columns={
            'alpha': combined_alpha,
            'sector': Sector(),
            'sentiment': sentiment_zscore,
            'beta': beta,
        },
        # combined_alpha will be NaN for all stocks not in our universe,
        # but we also want to make sure that we have a sector code for everything
        # we trade.
        screen=combined_alpha.notnull() & Sector().notnull() & beta.notnull(),
    )

    # Multiple pipelines can be used in a single algorithm.
    algo.attach_pipeline(pipe, 'pipe')
    algo.attach_pipeline(risk_loading_pipeline(), 'risk_loading_pipeline')

    # Schedule a function, 'do_portfolio_construction', to run twice a week
    # ten minutes after market open.
    algo.schedule_function(
        do_portfolio_construction,
        date_rule=algo.date_rules.week_start(),
        time_rule=algo.time_rules.market_open(
            minutes=MINUTES_AFTER_OPEN_TO_TRADE),
        half_days=False,
    )
def make_pipeline():
    """
     Defining out trading universe and alpha factor for long and short equities 
    """

    operation_margin = operation_ratios.operation_margin.latest
    revenue_growth = operation_ratios.revenue_growth.latest
    value = (Fundamentals.ebit.latest / Fundamentals.enterprise_value.latest)
    roe = Fundamentals.roe.latest
    momentum = Momentum()
    liquidity = Liquidity()
    free_cash_flows = morningstar.valuation_ratios.fcf_yield.latest
    earnings_yield = morningstar.valuation_ratios.earning_yield.latest

    # Filter for stocks that are announced acquisition target.
    not_announced_acq_target = ~IsAnnouncedAcqTarget()

    mkt_cap_filter = morningstar.valuation.market_cap.latest >= 500000000
    # dividend_filter = Fundamentals.valuation_ratios.dividend_yield >= 0.02

    # Our universe is made up of stocks that have a non-null factors & with market cap over 500 Million $, are not announced
    # acquisition targets, and are in the Q1500US.
    universe = (
        QTradableStocksUS()
        # & not_near_earnings_announcement
        & not_announced_acq_target
        & earnings_yield.notnull()
        & free_cash_flows.notnull()
        & value.notnull()
        & roe.notnull()
        & mkt_cap_filter
        & momentum.notnull())

    # combined_factor = roe*0
    # for factor in factors:
    #     combined_factor += factor.rank(mask=universe)

    # prediction_rank_quantiles = prediction_quality.quantiles(5)

    # longs = prediction_rank_quantiles.eq(4)
    # shorts = prediction_rank_quantiles.eq(0)

    combined_factor = (
        earnings_yield.winsorize(min_percentile=0.05,
                                 max_percentile=0.95).zscore(mask=universe) +
        free_cash_flows.winsorize(min_percentile=0.05,
                                  max_percentile=0.95).zscore(mask=universe) +
        value.winsorize(min_percentile=0.05,
                        max_percentile=0.95).zscore(mask=universe) +
        roe.winsorize(min_percentile=0.05,
                      max_percentile=0.95).zscore(mask=universe) +
        momentum.winsorize(min_percentile=0.05,
                           max_percentile=0.95).zscore(mask=universe))
    combined_factor_quantiles = combined_factor.quantiles(10)

    longs = combined_factor_quantiles.eq(9)
    shorts = combined_factor_quantiles.eq(0)
    # longs = combined_factor.top(2*TOTAL_POSITIONS//3, mask=universe)
    # shorts = combined_factor.bottom(TOTAL_POSITIONS//3, mask=universe)

    # We will take market beta into consideration when placing orders in our algorithm.
    beta = RollingLinearRegressionOfReturns(target=sid(8554),
                                            returns_length=5,
                                            regression_length=260,
                                            mask=(longs | shorts)).beta

    # I calculated the market beta using rolling window regression using Bloomberg's computation.
    # Ref: https://guides.lib.byu.edu/c.php?g=216390&p=1428678
    bb_beta = (0.66 * beta) + (0.33 * 1.0)

    ## create pipeline
    columns = {
        'longs': longs,
        'shorts': shorts,
        'market_beta': bb_beta,
        'sector': Sector(),
        'combined_factor': combined_factor,
    }
    pipe = Pipeline(columns=columns, screen=(longs | shorts))

    return pipe
Ejemplo n.º 10
0
def make_pipeline():
    """
    Create and return our pipeline.

    We break this piece of logic out into its own function to make it easier to
    test and modify in isolation.

    In particular, this function can be copy/pasted into research and run by itself.
    """

    # Create our momentum, value, and quality factors
    momentum = Momentum()
    # By appending .latest to the imported morningstar data, we get builtin Factors
    # so there's no need to define a CustomFactor
    value = Fundamentals.ebit.latest / Fundamentals.enterprise_value.latest
    quality = Fundamentals.roe.latest

    # Classify all securities by sector so that we can enforce sector neutrality later
    sector = Sector()

    # Screen out non-desirable securities by defining our universe.
    # Removes ADRs, OTCs, non-primary shares, LP, etc.
    # Also sets a minimum $500MM market cap filter and $5 price filter
    mkt_cap_filter = Fundamentals.market_cap.latest >= 500000000
    price_filter = USEquityPricing.close.latest >= 5
    universe = Q1500US() & price_filter & mkt_cap_filter

    # Construct a Factor representing the rank of each asset by our momentum,
    # value, and quality metrics. We aggregate them together here using simple
    # addition.
    #
    # By applying a mask to the rank computations, we remove any stocks that failed
    # to meet our initial criteria **before** computing ranks.  This means that the
    # stock with rank 10.0 is the 10th-lowest stock that was included in the Q1500US.
    combined_rank = (momentum.rank(mask=universe).zscore() +
                     value.rank(mask=universe).zscore() +
                     quality.rank(mask=universe).zscore())

    # Build Filters representing the top and bottom 150 stocks by our combined ranking system.
    # We'll use these as our tradeable universe each day.
    longs = combined_rank.top(NUM_LONG_POSITIONS)
    shorts = combined_rank.bottom(NUM_SHORT_POSITIONS)

    # The final output of our pipeline should only include
    # the top/bottom 300 stocks by our criteria
    long_short_screen = (longs | shorts)

    # Define any risk factors that we will want to neutralize
    # We are chiefly interested in market beta as a risk factor so we define it using
    # Bloomberg's beta calculation
    # Ref: https://www.lib.uwo.ca/business/betasbydatabasebloombergdefinitionofbeta.html
    beta = 0.66 * RollingLinearRegressionOfReturns(
        target=sid(8554),
        returns_length=5,
        regression_length=260,
        mask=long_short_screen).beta + 0.33 * 1.0

    # Create pipeline
    pipe = Pipeline(columns={
        'longs': longs,
        'shorts': shorts,
        'combined_rank': combined_rank,
        'quality': quality,
        'value': value,
        'momentum': momentum,
        'sector': sector,
        'market_beta': beta
    },
                    screen=long_short_screen)
    return pipe
def make_pipeline():
    """
    Dynamically apply the custom factors defined below to 
    select candidate stocks from the PreCog universe 
    
    """

    pred_quality_thresh = 0.5

    # Filter for stocks that are not within 2 days of an earnings announcement.
    not_near_earnings_announcement = ~(
        (BusinessDaysUntilNextEarnings() <= 2)
        | (BusinessDaysSincePreviousEarnings() <= 2))

    # Filter for stocks that are announced acquisition target.
    not_announced_acq_target = ~IsAnnouncedAcqTarget()

    # Our universe is made up of stocks that have a non-null sentiment & precog signal that was
    # updated in the last day, are not within 2 days of an earnings announcement, are not announced
    # acquisition targets, and are in the Q1500US.
    universe = (Q1500US()
                & precog.predicted_five_day_log_return.latest.notnull()
                & not_near_earnings_announcement
                & not_announced_acq_target)

    # Prediction quality factor.
    prediction_quality = PredictionQuality(mask=universe)

    # Filter for stocks above the threshold quality.
    quality = prediction_quality > pred_quality_thresh

    latest_prediction = precog.predicted_five_day_log_return.latest

    non_outliers = latest_prediction.percentile_between(1, 99, mask=quality)
    normalized_return = latest_prediction.zscore(mask=non_outliers)

    normalized_prediction_rank = normalized_return.rank()

    prediction_rank_quantiles = normalized_prediction_rank.quantiles(5)

    longs = prediction_rank_quantiles.eq(4)
    shorts = prediction_rank_quantiles.eq(0)

    # We will take market beta into consideration when placing orders in our algorithm.
    beta = RollingLinearRegressionOfReturns(target=sid(8554),
                                            returns_length=5,
                                            regression_length=260,
                                            mask=(longs | shorts)).beta

    # We will actually be using the beta computed using Bloomberg's computation.
    # Ref: https://www.lib.uwo.ca/business/betasbydatabasebloombergdefinitionofbeta.html
    bb_beta = (0.66 * beta) + (0.33 * 1.0)

    ## create pipeline
    columns = {
        'longs': longs,
        'shorts': shorts,
        'market_beta': bb_beta,
        'sector': Sector(),
    }
    pipe = Pipeline(columns=columns, screen=(longs | shorts))

    return pipe
def make_pipeline():
    # Create & return our pipeline (dynamic stock selector). The pipeline is     used to rank stocks based on different factors, including built-in factors, or custom factors. Documentation on pipeline is at:    https://www.quantopian.com/help#pipeline-title
    #Break this piece of logic out into its own function to make it easier to test & modify in isolation. In particular, this function can be  copy / pasted into research and run by itself.

    # specify momentum, quality, value, and any other factors
    # -------------------------------------------------------
    momentum = Momentum()
    # Create and apply a filter representing the top 2000 equities by MarketCap every day
    mkt_cap = MarketCap()
    top_2000 = mkt_cap.top(2000)
    pb = Pricetobook()
    pe = Pricetoearnings()
    roa = Roa()
    roe = Roe()
    roic = Roic()
    volat = Volatility()  # Michelle called this "vol".
    sector = Sector()

    # Define universe of securities
    # -----------------------------
    #universe = Q1500US() & price_filter & mkt_cap_MM_filter & liqMM_filter & volat_GT & volat_LT
    universe = top_2000

    # Combined Rank
    # -------------
    # Construct a Factor representing the rank of each asset by our momentum, quality, value, and any other metrics. Aggregate them together here using simple addition. By applying a mask to the rank computations, remove any stocks that failed to meet our initial criteria **BEFORE** computing ranks.  This means that the stock with rank 10.0 is the 10th-lowest stock that was included in the Q1500US.

    combined_rank = (
        momentum.rank(mask=universe).zscore() +
        #lower is better
        -1.0 * volat.rank(mask=universe).zscore() +
        -1.0 * pb.rank(mask=universe).zscore() +
        -1.0 * pe.rank(mask=universe).zscore() +
        #higher is better
        3.0 * roa.rank(mask=universe).zscore() +
        3.0 * roe.rank(mask=universe).zscore() +
        3.0 * roic.rank(mask=universe).zscore())

    # Build Filters representing the top & bottom stocks by our combined ranking system. Use these as our tradeable universe each day.
    longs = combined_rank.top(NUM_LONG_POSITIONS)
    shorts = combined_rank.bottom(NUM_SHORT_POSITIONS)

    # Final output of pipeline should only include the top/bottom subset of stocks by our criteria
    long_short_screen = (longs | shorts)

    # Define any risk factors that we will want to neutralize. We are chiefly interested in Market Beta as a risk factor. Define it using Bloomberg's beta calculation. Ref: https://www.lib.uwo.ca/business/betasbydatabasebloombergdefinitionofbeta.html
    beta = 0.66 * RollingLinearRegressionOfReturns(
        target=sid(8554),
        returns_length=5,
        regression_length=260,
        mask=long_short_screen).beta + 0.33 * 1.0

    # Create pipeline
    #----------------
    pipe = Pipeline(columns={
        'longs': longs,
        'shorts': shorts,
        'combined_rank': combined_rank,
        'top_2000': top_2000,
        'momentum': momentum,
        'pb': pb,
        'pe': pe,
        'roa': roa,
        'roe': roe,
        'roic': roic,
        'sector': sector,
        'volat': volat,
        'market_beta': beta
    },
                    screen=long_short_screen)
    return pipe