Correlation of volume spike prior to price spike

Study showed that big change in trading volume often precedes price change, reflecting information leak. One might be able to detect this by find the correlation between price change and volume turnover. Here is my attempt to do that.

There are two issues need to be addressed:

1. The algo runs slowly because it has to do column-wise subset correlation. Any suggestion to speed it up is welcome

2. The equity curve is trending down monotonically which looks like the factor has some alpha. Is there any way to transform the factor so that the equity curve is trending up?

4
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, Returns, SimpleMovingAverage,Latest
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 math
import numpy as np
import pandas as pd
from scipy import stats

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

def preprocess2(a):
a = a.astype(np.float64)
a[np.isinf(a)] = np.nan
a = np.nan_to_num(a - np.nanmean(a))
a = winsorize(a, limits=[WIN_LIMIT,WIN_LIMIT])
return preprocessing.scale(a)

def leaky(p,q):
FRup = -0.42*p[p>0].corr(q[p>0])
FRdn = 1.42*p[p<=0].corr(q[p<=0])
return -(FRup+FRdn)

def make_factors():

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

def compute(self, today, assets, out, close, vol, share):
n = 8
sft = 1
pr = pd.DataFrame(close, columns=assets).dropna(how='all')
hsl = pd.DataFrame(vol/share, columns=assets).dropna(how='all')
pr_ret = pr.pct_change(sft)
pr_ret21 = pr_ret.rolling(n).mean()
hsl_ret = hsl.pct_change(sft)
hsl_ret21 = hsl_ret.rolling(n).mean()
pr_ret = (pr_ret-pr_ret21).iloc[-n:,:]
hsl_ret = (hsl_ret-hsl_ret21).shift(sft).iloc[-n:,:]
FR = list(map(lambda x: leaky(pr_ret[x], hsl_ret[x]), assets))
out[:] = preprocess(FR)

return {
'Leak' : (Leak,1),
}

def initialize(context):
context.spy = sid(8554)
context.std = []

algo.schedule_function(
allocate,
date_rule=algo.date_rules.week_start(),
time_rule=algo.time_rules.market_close(minutes=30),
)

pipe = make_pipeline(context)
algo.attach_pipeline(pipe, 'pipeline')

def make_pipeline(context):
class Window_Safe(CustomFactor):
window_length = 1
window_safe = True
def compute(self, today, assets, out, value):
out[:] = value

pipe = Pipeline()
my_etfs = StaticAssets(symbols('SPY'))
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(800)
universe = universe & high_dollar_volume

factors = make_factors()
combined_alpha = None
for name, (f,w)  in factors.items():
if name == 'CPT':
else:
if combined_alpha == None:
combined_alpha = fac
else:
combined_alpha += fac

pipe.set_screen((universe & combined_alpha.notnull())|my_etfs)

return pipe

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

def allocate(context, data):

df = context.output
alpha = df.combined_alpha
stocks = alpha.index

st = []
for i in range(len(stocks)):
st.append(stocks[i])
alpha = alpha[st]

how_to = {'1': 'alpha', '2':'weights'}
how = how_to['1']

# Constraint Parameters
MAX_GROSS_LEVERAGE = 1.0
MAX_SHORT_POSITION_SIZE = 0.03
MAX_LONG_POSITION_SIZE = 0.03
MIN_BETA_EXPOSURE = -0.04
MAX_BETA_EXPOSURE = 0.04
SECTOR_EXPOSURE = 0.05
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']],
min_exposures={'beta': MIN_BETA_EXPOSURE},
max_exposures={'beta': MAX_BETA_EXPOSURE},
)

constrain_turnover = opt.MaxTurnover(MAX_TURNOVER)
dollar_neutral = opt.DollarNeutral(tolerance=0.0000001)

constrain_sector_style_risk = opt.experimental.RiskModelExposure(
min_volatility=-0.3,
max_volatility=0.3,
min_momentum=-0.3,
max_momentum=0.3,
min_short_term_reversal=-0.3,
max_short_term_reversal=0.3,

)

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 = alpha.fillna(0)
weights = weights - weights.mean()
weights = weights/weights.abs().sum()
weights = opt.calculate_optimal_portfolio(
objective=opt.TargetWeights(weights),
constraints=[
dollar_neutral,
constrain_pos_size,],
)
algo.order_optimal_portfolio(
objective=opt.TargetWeights(weights),
constraints=[dollar_neutral,
constrain_pos_size,],
)

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