Here's an attempt at a liquidity alpha factor (via combination several liquidity alpha factors into one).

Clone Algorithm

48

Loading...

There was an error loading this backtest.

Backtest from
to
with
initial capital

Cumulative performance:

Algorithm
Benchmark

Custom data:

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 |

# https://arxiv.org/pdf/1412.5072.pdf from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor, Returns, SimpleMovingAverage from quantopian.pipeline.data.builtin import USEquityPricing from quantopian.pipeline.data import Fundamentals import quantopian.optimize as opt from sklearn import preprocessing from quantopian.pipeline.filters import QTradableStocksUS from scipy.stats.mstats import winsorize import numpy as np import pandas as pd WIN_LIMIT = 0.05 def preprocess(a): a = a.astype(np.float64) a[np.isinf(a)] = np.nan not_nan_ind = np.argwhere(~np.isnan(a)) if not_nan_ind.size > 0: a_win = winsorize(a[not_nan_ind], limits=[WIN_LIMIT,WIN_LIMIT]) a[not_nan_ind] = a_win else: a = winsorize(a, limits=[WIN_LIMIT,WIN_LIMIT]) a = np.nan_to_num(a - np.nanmean(a)) return preprocessing.scale(a) def normalize(x): r = x - x.mean() return r/r.abs().sum() def make_factors(): class LIX(CustomFactor): inputs = [USEquityPricing.high, USEquityPricing.low, USEquityPricing.close, USEquityPricing.volume] window_length = 21 window_safe = True def compute(self, today, assets, out, high, low, close, volume): dv = close[-1,:]*np.sum(volume, axis=0) r = np.amax(high,axis=0) - np.amin(low,axis=0) out[:] = preprocess(-np.log10(dv/r)) class ILLIQ(CustomFactor): inputs = [USEquityPricing.close, USEquityPricing.volume, Returns(window_length=21)] window_length = 21 window_safe = True def compute(self, today, assets, out, close, volume, ret): ilq = np.absolute(ret)/(close*volume) out[:] = preprocess(-np.sum(ilq,axis=0)) class HH(CustomFactor): inputs = [USEquityPricing.high, USEquityPricing.low, USEquityPricing.volume, Fundamentals.shares_outstanding] window_length = 21 window_safe = True def compute(self, today, assets, out, high, low, volume, n_shares): dv = np.amin(low,axis=0)*np.sum(volume, axis=0) r = np.amax(high,axis=0) - np.amin(low,axis=0) out[:] = preprocess(-n_shares[-1]*r/dv) class share_turnover(CustomFactor): inputs = [USEquityPricing.volume, Fundamentals.shares_outstanding] window_length = 21 window_safe = True def compute(self, today, assets, out, volume, n_shares): v = np.sum(volume, axis=0) out[:] = preprocess(-v/n_shares[-1]) class dollar_turnover(CustomFactor): inputs = [USEquityPricing.close, USEquityPricing.volume, Fundamentals.shares_outstanding] window_length = 21 window_safe = True def compute(self, today, assets, out, close, volume, n_shares): dv = np.sum(close*volume, axis=0)/close[-1,:] out[:] = preprocess(-dv/n_shares[-1]) factors = [ LIX, ILLIQ, HH, share_turnover, dollar_turnover, ] return factors def factor_pipeline(): universe = QTradableStocksUS() factors = make_factors() pipeline_columns = {} for k,f in enumerate(factors): pipeline_columns['alpha_'+str(k)] = SimpleMovingAverage(inputs=[f(mask=universe)], window_length=5) pipe = Pipeline(columns = pipeline_columns, screen = universe) return pipe def initialize(context): attach_pipeline(factor_pipeline(), 'factor_pipeline') # Schedule my rebalance function schedule_function(func=rebalance, date_rule=date_rules.every_day(), time_rule=time_rules.market_close(hours=1), half_days=True) # record my portfolio variables at the end of day schedule_function(func=recording_statements, date_rule=date_rules.every_day(), time_rule=time_rules.market_close(), half_days=True) set_commission(commission.PerShare(cost=0, min_trade_cost=0)) set_slippage(slippage.FixedSlippage(spread=0)) def recording_statements(context, data): record(num_positions=len(context.portfolio.positions)) record(leverage=context.account.leverage) def rebalance(context, data): alpha = pipeline_output('factor_pipeline').sum(axis=1) order_optimal_portfolio(opt.TargetWeights(normalize(alpha)), constraints=[])