Different long short strategy algo. Additive and filter factors.

Hi,

I'm building an algo that accomplishes:
- Follows a different strategy for longs and shorts.
- Some of the factors are applied as filters, reducing the universe.
- Some of the factors are processed (ranked) and added altogether.
- Is market neutral.
- Uses order_optimal_portfolio and TargetWeights.

I've built the skeleton and implemented some factors just as a first draft (not focused on returns for now, just on coding the strategy):

Short:
- First, filters low profitability stocks.
- Second, filters expensive companies (using the profitability result as a mask).
- Once we have the stocks to trade, weights are defined by adding high volatility and low momentum.

Long:
- This part of the portfolio (50%), in the example just hedges longing the S&P.

Some doubts or things that came to my mind while building it:
- Being profitability the first filter factor, should it be the most emphasized?
- Regarding factor addition, in other discussions I've read that something similar to what I do here does not provide real additive quality but just average between two factors. Which are typical techniques for proper factor addition?
- Any other feedback on the approach is really appreciated.

7
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 quantopian.algorithm as algo
from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import CustomFactor
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.data.morningstar import Fundamentals as ms
import quantopian.optimize as opt
import numpy as np
import pandas as pd
from quantopian.pipeline.filters import  StaticAssets

def initialize(context):
context.FILTER_BASE = 80
context.FILTER_LONG = 1
context.FILTER_SHORT = 30
context.long_weights = pd.Series()
context.short_weights = pd.Series()

algo.attach_pipeline(make_pipeline(context), 'pipeline')
schedule_function(select_stocks_and_set_weights, date_rules.every_day(),
time_rules.market_open(minutes = 60))
time_rules.market_open(minutes = 60))

def make_pipeline(context):

longs = build_long_filter(universe, context)
shorts = build_short_filter(universe, context)

alpha_short = build_short_sort(shorts)
#for this case it doesn't really matter as we just have SPY
alpha_long = build_long_sort(longs)

universe = longs | shorts

pipe = Pipeline(columns={
'long': longs,
'short': shorts,
'alpha_long':alpha_long,
'alpha_short':alpha_short,
},screen=universe)
return pipe

def select_stocks_and_set_weights(context, data):
df = algo.pipeline_output('pipeline')
long_stocks = df.query('long').index
short_stocks = df.query('short').index

alpha_long = df.alpha_long
stock_weight_long = alpha_long/alpha_long.abs().sum()*0.5

alpha_short = df.alpha_short
stock_weight_short = alpha_short/alpha_short.abs().sum()*0.5

context.long_weights = pd.Series(index=long_stocks, data=stock_weight_long)
context.short_weights = pd.Series(index=short_stocks, data=-stock_weight_short)

total_weights = pd.concat([context.long_weights, context.short_weights])
target_weights = opt.TargetWeights(total_weights)
order_optimal_portfolio(
objective = target_weights,
constraints = []
)
record(num_positions=len(context.portfolio.positions))
record(leverage=context.account.leverage)

def build_long_filter(universe, context):
return StaticAssets(symbols('SPY'))

def build_short_filter(universe, context):
factor_1 = ms.roic.latest.rank() #profitability
factor_2 = ms.fcf_yield.latest.rank() #value

return filter_2

def build_short_sort(universe):
return momentum + volatility

def build_long_sort(universe):

class Volatility(CustomFactor):
inputs = [USEquityPricing.close]
window_length = 20
def compute(self, today, assets, out, close):
daily_returns = np.diff(close, axis = 0) / close[0:-1]
out[:] = -np.nanstd(daily_returns, axis=0) * np.sqrt(250)

class Momentum(CustomFactor):
inputs = [USEquityPricing.close]
window_length = 128
def compute(self, today, assets, out, prices):
out[:] = np.nan_to_num((prices[-2] - prices[-128])/prices[-128])
There was a runtime error.