Help needed for factor analysis and backtesting

I am studying this CGO factor and find it has some merit (ic positive and increasing cumulative returns) .

2
Notebook previews are currently unavailable.
3 responses

Here it is

0
Notebook previews are currently unavailable.

I am a beginner of quantopian so I am not really sure how to interpret the factor analysis result other than reading IC and the cumulative curve.

For some reason, it does not work as expected when I run a backtest (I remove commission and slippage parts). Can you and other spot any wrong-doing in my algo?

1
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
"""
This is a template algorithm on Quantopian for you to adapt and fill in.
"""
import quantopian.algorithm as algo
from quantopian.pipeline import Pipeline,CustomFactor
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import AverageDollarVolume, SimpleBeta
from quantopian.pipeline.data import morningstar
from quantopian.pipeline.data import Fundamentals
import quantopian.optimize as opt
import quantopian.pipeline.factors as Factors
from quantopian.pipeline.classifiers.morningstar import Sector
from sklearn import preprocessing
import numpy as np
from scipy import stats

def preprocess(a):
a = np.nan_to_num(a - np.nanmean(a))
return preprocessing.scale(a)

def make_factors():

class CGO(CustomFactor):
inputs = [USEquityPricing.close, USEquityPricing.volume, morningstar.valuation.shares_outstanding]
window_length=1260

def compute(self, today, assets, out, close, volume, share):
mon = 60
unit = 21
df_price = close
df_hsl = volume/share
copy = 1 - df_hsl
copy[-1,:] = 1.0
copy = copy.prod(axis=0)/copy.cumprod(axis=0)*copy
copy = copy * df_hsl
k = copy.sum(axis=0)
ref_pr = ((df_price*(copy)).sum(axis=0)/k)
if unit > 1:
df_price = df_price[::unit,]
CGO = ((df_price[-1,:] - ref_pr)/df_price[-1,:])

out[:] = preprocess(CGO)

return {

'CGO': CGO,

}

def initialize(context):
#set_slippage(slippage.VolumeShareSlippage(volume_limit=0.1, price_impact=0.1))
context.spy = sid(8554)
#context.exclusion_list = StaticRestrictions([sid(8340), sid(34648), sid(32430)])
context.std = []

algo.schedule_function(
allocate,
date_rule=algo.date_rules.every_day(),
time_rule=algo.time_rules.market_open(hours=0,minutes=5),
)

# Create our dynamic stock selector.
pipe = make_pipeline(context)
algo.attach_pipeline(pipe, 'pipeline')

def make_pipeline(context):
pipe = Pipeline()
beta = SimpleBeta(target=symbol('SPY'),regression_length=120,
allowed_missing_percentage=1.0
)
universe = QTradableStocksUS() & Sector().notnull() & beta.notnull()
dollar_volume = AverageDollarVolume(window_length=5, mask = universe)
high_dollar_volume = dollar_volume.top(500)
universe = universe & high_dollar_volume
universe = universe & mktcap.bottom(200)

factors = make_factors()
combined_alpha = None
for name, f in factors.iteritems():
if combined_alpha == None:
combined_alpha = fac.zscore()
else:
combined_alpha += fac.zscore()

book_value = Fundamentals.tangible_book_value.latest
pipe.set_screen(universe & combined_alpha.notnull())

return pipe

context.output = algo.pipeline_output('pipeline')
context.sector = context.output.sector

def allocate(context, data):

df = context.output
stocks = df.index
st = []
for stock in stocks:
st.append(stock)
alpha = df.loc[st,:].combined_alpha
how_to = {'1': 'alpha', '2':'weights'}

how = how_to['1']

# Constraint Parameters
MAX_GROSS_LEVERAGE = 1.0
MAX_SHORT_POSITION_SIZE = 0.025  # 1.5%
MAX_LONG_POSITION_SIZE = 0.025   # 1.5%
MIN_BETA_EXPOSURE = -0.03
MAX_BETA_EXPOSURE = 0.03
SECTOR_EXPOSURE = 0.10
MAX_TURNOVER = 0.95

constraints = []
constrain_gross_leverage = opt.MaxGrossExposure(MAX_GROSS_LEVERAGE)
constrain_pos_size = opt.PositionConcentration.with_equal_bounds(
-MAX_SHORT_POSITION_SIZE,
MAX_LONG_POSITION_SIZE,
)
beta_neutral = opt.FactorExposure(
context.output[['beta']],#, 'mkt_cap','value']],
min_exposures={'beta': MIN_BETA_EXPOSURE}, #'value': -0.1},
max_exposures={'beta': MAX_BETA_EXPOSURE}, #'value': 0.1},
)

constrain_turnover = opt.MaxTurnover(MAX_TURNOVER)
dollar_neutral = opt.DollarNeutral()#tolerance=0.001)
sector_neutral = opt.NetGroupExposure.with_equal_bounds(
labels=context.sector,
min=-SECTOR_EXPOSURE,
max=SECTOR_EXPOSURE,
)
constrain_sector_style_risk = opt.experimental.RiskModelExposure(

)

constraints = [
constrain_sector_style_risk,
#sector_neutral,
dollar_neutral,
#constrain_turnover,
#beta_neutral,
constrain_pos_size,
constrain_gross_leverage,
]

# Run the optimization. This will calculate new portfolio weights and
# manage moving our portfolio toward the target.
if how == 'alpha':
try:
algo.order_optimal_portfolio(
objective=opt.MaximizeAlpha(alpha),
constraints=constraints
)
except Exception as e:
log.info('error {}'.format(e) )
return
else:

weights = opt.calculate_optimal_portfolio(
objective=opt.TargetWeights(alpha),
constraints=constraints
)

objective = opt.TargetWeights(weights)

algo.order_optimal_portfolio(
objective=objective,
constraints=[],
)

def handle_data(context, data):
"""
Called every minute.
"""
pass
There was a runtime error.

I run the algo on a weekly schedule, it looks better now

1
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
"""
This is a template algorithm on Quantopian for you to adapt and fill in.
"""
import quantopian.algorithm as algo
from quantopian.pipeline import Pipeline,CustomFactor
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import AverageDollarVolume, SimpleBeta
from quantopian.pipeline.data import morningstar
from quantopian.pipeline.data import Fundamentals
import quantopian.optimize as opt
import quantopian.pipeline.factors as Factors
from quantopian.pipeline.classifiers.morningstar import Sector
from sklearn import preprocessing
import numpy as np
from scipy import stats

def preprocess(a):
a = np.nan_to_num(a - np.nanmean(a))
return preprocessing.scale(a)

def make_factors():

class CGO(CustomFactor):
inputs = [USEquityPricing.close, USEquityPricing.volume, morningstar.valuation.shares_outstanding]
window_length=1260

def compute(self, today, assets, out, close, volume, share):
mon = 60
unit = 21
df_price = close
df_hsl = volume/share
copy = 1 - df_hsl
copy[-1,:] = 1.0
copy = copy.prod(axis=0)/copy.cumprod(axis=0)*copy
copy = copy * df_hsl
k = copy.sum(axis=0)
ref_pr = ((df_price*(copy)).sum(axis=0)/k)
if unit > 1:
df_price = df_price[::unit,]
CGO = ((df_price[-1,:] - ref_pr)/df_price[-1,:])

out[:] = preprocess(CGO)

return {

'CGO': CGO,

}

def initialize(context):
#set_slippage(slippage.VolumeShareSlippage(volume_limit=0.1, price_impact=0.1))
context.spy = sid(8554)
#context.exclusion_list = StaticRestrictions([sid(8340), sid(34648), sid(32430)])
context.std = []

algo.schedule_function(
allocate,
date_rule=algo.date_rules.week_start(),
time_rule=algo.time_rules.market_open(hours=0,minutes=5),
)

# Create our dynamic stock selector.
pipe = make_pipeline(context)
algo.attach_pipeline(pipe, 'pipeline')

def make_pipeline(context):
pipe = Pipeline()
beta = SimpleBeta(target=symbol('SPY'),regression_length=120,
allowed_missing_percentage=1.0
)
universe = QTradableStocksUS() & Sector().notnull() & beta.notnull()
dollar_volume = AverageDollarVolume(window_length=5, mask = universe)
high_dollar_volume = dollar_volume.top(500)
universe = universe & high_dollar_volume
universe = universe & mktcap.bottom(200)

factors = make_factors()
combined_alpha = None
for name, f in factors.iteritems():
if combined_alpha == None:
combined_alpha = fac.zscore()
else:
combined_alpha += fac.zscore()

book_value = Fundamentals.tangible_book_value.latest
pipe.set_screen(universe & combined_alpha.notnull())

return pipe

context.output = algo.pipeline_output('pipeline')
context.sector = context.output.sector

def allocate(context, data):

df = context.output
stocks = df.index
st = []
for stock in stocks:
st.append(stock)
alpha = df.loc[st,:].combined_alpha
how_to = {'1': 'alpha', '2':'weights'}

how = how_to['1']

# Constraint Parameters
MAX_GROSS_LEVERAGE = 1.0
MAX_SHORT_POSITION_SIZE = 0.025  # 1.5%
MAX_LONG_POSITION_SIZE = 0.025   # 1.5%
MIN_BETA_EXPOSURE = -0.03
MAX_BETA_EXPOSURE = 0.03
SECTOR_EXPOSURE = 0.10
MAX_TURNOVER = 0.95

constraints = []
constrain_gross_leverage = opt.MaxGrossExposure(MAX_GROSS_LEVERAGE)
constrain_pos_size = opt.PositionConcentration.with_equal_bounds(
-MAX_SHORT_POSITION_SIZE,
MAX_LONG_POSITION_SIZE,
)
beta_neutral = opt.FactorExposure(
context.output[['beta']],#, 'mkt_cap','value']],
min_exposures={'beta': MIN_BETA_EXPOSURE}, #'value': -0.1},
max_exposures={'beta': MAX_BETA_EXPOSURE}, #'value': 0.1},
)

constrain_turnover = opt.MaxTurnover(MAX_TURNOVER)
dollar_neutral = opt.DollarNeutral()#tolerance=0.001)
sector_neutral = opt.NetGroupExposure.with_equal_bounds(
labels=context.sector,
min=-SECTOR_EXPOSURE,
max=SECTOR_EXPOSURE,
)
constrain_sector_style_risk = opt.experimental.RiskModelExposure(

)

constraints = [
constrain_sector_style_risk,
#sector_neutral,
dollar_neutral,
#constrain_turnover,
#beta_neutral,
constrain_pos_size,
constrain_gross_leverage,
]

# Run the optimization. This will calculate new portfolio weights and
# manage moving our portfolio toward the target.
if how == 'alpha':
try:
algo.order_optimal_portfolio(
objective=opt.MaximizeAlpha(alpha),
constraints=constraints
)
except Exception as e:
log.info('error {}'.format(e) )
return
else:

weights = opt.calculate_optimal_portfolio(
objective=opt.TargetWeights(alpha),
constraints=constraints
)

objective = opt.TargetWeights(weights)

algo.order_optimal_portfolio(
objective=objective,
constraints=[],
)

def handle_data(context, data):
"""
Called every minute.
"""
pass
There was a runtime error.