Mean reversion experiment

Here is something I tried. Get a bunch of stocks, identify statistical risk factors and create a portfolio such that its exposure to all factors is zero. It should by nature be mean reverting. Unfortunately the opportunity is scant and there are long periods without any trades.

I want to try this on several portfolios (3 at the moment) such as 50/60 portfolios so that there is opportunity every day. My python skills are rusty. Could someone modify this algorithm so that it can scale to 50 or 60 portfolios without duplicating code for each portfolio like I did?

45
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
import math
import numpy as np
import pandas as pd
import scipy as sp
from statsmodels.tsa.vector_ar.var_model import VAR
from sklearn.covariance import OAS, EmpiricalCovariance
import statsmodels.api as smapi
from scipy.optimize import minimize
from sklearn.linear_model import LassoCV

def getweights(params, cov, signal):
cons = []
(m,n) = np.shape(params)

for i in range(0, n):
cons.append({'type': 'ineq', 'fun': lambda x, i=i: np.dot(x.T, params[:, i])})
cons.append({'type': 'ineq', 'fun': lambda x, i=i: -np.dot(x.T, params[:, i])})

x0 = [1.] * m
res = minimize(lambda x: 0., x0,
constraints = cons, method='cobyla',options={'maxiter':2000})
return res.x

def initialize(context):
context.hasPosition1 = False
context.hasPosition2 = False
context.hasPosition3 = False
context.stocks1 = None
context.stocks2 = None
context.stocks3 = None
context.positionStocks1 = None
context.prices1 = None
context.mean1 = 0
context.std1 = 0
context.posSign1 = 0
context.signal1 = 0
context.weights1 = None
context.positionStocks2 = None
context.prices2 = None
context.mean2 = 0
context.std2 = 0
context.posSign2 = 0
context.signal2 = 0
context.weights2 = None
context.positionStocks3 = None
context.prices3 = None
context.mean3 = 0
context.std3 = 0
context.posSign3 = 0
context.signal3 = 0
context.weights3 = None

schedule_function(monitor1, date_rules.every_day(), time_rules.market_close(minutes=60))
schedule_function(monitor2, date_rules.every_day(), time_rules.market_close(minutes=61))
schedule_function(monitor3, date_rules.every_day(), time_rules.market_close(minutes=62))

fundamental_df = get_fundamentals(query(fundamentals.valuation.market_cap)
.filter(fundamentals.asset_classification.morningstar_sector_code == 309)
.filter(fundamentals.valuation.market_cap != None)
.filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
.filter(fundamentals.share_class_reference.security_type == 'ST00000001')
.filter(~fundamentals.share_class_reference.symbol.contains('_WI'))
.filter(fundamentals.share_class_reference.is_primary_share == True)
.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
.order_by(fundamentals.valuation.market_cap.desc())).T
context.stocks1 = fundamental_df[0:25].index

fundamental_df = get_fundamentals(query(fundamentals.valuation.market_cap)
.filter(fundamentals.asset_classification.morningstar_sector_code == 101)
.filter(fundamentals.valuation.market_cap != None)
.filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
.filter(fundamentals.share_class_reference.security_type == 'ST00000001')
.filter(~fundamentals.share_class_reference.symbol.contains('_WI'))
.filter(fundamentals.share_class_reference.is_primary_share == True)
.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
.order_by(fundamentals.valuation.market_cap.desc())).T
context.stocks2 = fundamental_df[0:25].index

fundamental_df = get_fundamentals(query(fundamentals.valuation.market_cap)
.filter(fundamentals.asset_classification.morningstar_sector_code == 102)
.filter(fundamentals.valuation.market_cap != None)
.filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
.filter(fundamentals.share_class_reference.security_type == 'ST00000001')
.filter(~fundamentals.share_class_reference.symbol.contains('_WI'))
.filter(fundamentals.share_class_reference.is_primary_share == True)
.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
.order_by(fundamentals.valuation.market_cap.desc())).T
context.stocks3 = fundamental_df[0:25].index

def handle_data(context,data):
pass

if not context.hasPosition1:
success, W, abspval, prices = compute1(context, data, context.stocks1)
if success:
openPos1(-np.sign(context.signal1), prices, W, abspval, data, context)

if not context.hasPosition2:
success, W, abspval, prices = compute2(context, data, context.stocks2)
if success:
openPos2(-np.sign(context.signal2), prices, W, abspval, data, context)

if not context.hasPosition3:
success, W, abspval, prices = compute3(context, data, context.stocks3)
if success:
openPos3(-np.sign(context.signal3), prices, W, abspval, data, context)

def compute1(context, data, stocks):
prices = data.history(stocks, "price", 200, "1d")
prices = prices.dropna(axis=1)
returns = prices.pct_change().dropna().values
returns = returns * 1000.
cov = OAS().fit(returns).covariance_
e, v = np.linalg.eig(cov)
idx = e.argsort()
comp = v[:, idx[-15:]]

if comp[0, 0] < 0:
comp *= -1

sources = np.dot(returns, comp)
betas = np.zeros((np.shape(returns)[1], np.shape(sources)[1]))

for i in range(0, np.shape(returns)[1]):
betas[i, :] = model.params[1:]

W = getweights(betas, cov, np.asarray([1.] * np.shape(returns)[1]))
context.prices1 = prices.values[0, :]
pvalue = np.dot(prices.values, W / context.prices1)
context.mean1 = np.mean(pvalue)
context.std1 = np.std(pvalue)
context.signal1 = (pvalue[-1] - context.mean1) / context.std1
abspval = np.sum(np.abs(W))

if abs(context.signal1) < 1.75:
return False, None, None, None
return True, W, abspval, prices

def compute2(context, data, stocks):
prices = data.history(stocks, "price", 200, "1d")
prices = prices.dropna(axis=1)
returns = prices.pct_change().dropna().values
returns = returns * 1000.
cov = OAS().fit(returns).covariance_
e, v = np.linalg.eig(cov)
idx = e.argsort()
comp = v[:, idx[-15:]]

if comp[0, 0] < 0:
comp *= -1

sources = np.dot(returns, comp)
betas = np.zeros((np.shape(returns)[1], np.shape(sources)[1]))

for i in range(0, np.shape(returns)[1]):
betas[i, :] = model.params[1:]

W = getweights(betas, cov, np.asarray([1.] * np.shape(returns)[1]))
context.prices2 = prices.values[0, :]
pvalue = np.dot(prices.values, W / context.prices2)
context.mean2 = np.mean(pvalue)
context.std2 = np.std(pvalue)
context.signal2 = (pvalue[-1] - context.mean2) / context.std2
abspval = np.sum(np.abs(W))

if abs(context.signal2) < 1.75:
return False, None, None, None
return True, W, abspval, prices

def compute3(context, data, stocks):
prices = data.history(stocks, "price", 200, "1d")
prices = prices.dropna(axis=1)
returns = prices.pct_change().dropna().values
returns = returns * 1000.
cov = OAS().fit(returns).covariance_
e, v = np.linalg.eig(cov)
idx = e.argsort()
comp = v[:, idx[-15:]]

if comp[0, 0] < 0:
comp *= -1

sources = np.dot(returns, comp)
betas = np.zeros((np.shape(returns)[1], np.shape(sources)[1]))

for i in range(0, np.shape(returns)[1]):
betas[i, :] = model.params[1:]

W = getweights(betas, cov, np.asarray([1.] * np.shape(returns)[1]))
context.prices3 = prices.values[0, :]
pvalue = np.dot(prices.values, W / context.prices3)
context.mean3 = np.mean(pvalue)
context.std3 = np.std(pvalue)
context.signal3 = (pvalue[-1] - context.mean3) / context.std3
abspval = np.sum(np.abs(W))

if abs(context.signal3) < 1.75:
return False, None, None, None
return True, W, abspval, prices

def openPos1(side, prices, W, abspval, data, context):
context.positionStocks1 = []
for i, sid in enumerate(prices):
order_target_value(sid, W[i] / abspval * context.portfolio.portfolio_value * side)
context.positionStocks1.append(sid)
context.days1 = 0
context.posSign1 = side
context.weights1 = W
context.hasPosition1 = True

def monitor1(context, data):
if not context.hasPosition1:
return
context.days1 += 1
prices = data.history(context.positionStocks1, "price", 60, "1d")
prices = prices.dropna(axis=1)
if len(prices.columns) <> len(context.positionStocks1):
closeAll1(context, data, 0)
return
pvalue = np.dot(prices.values, context.weights1 / context.prices1)
signal = (pvalue[-1] - context.mean1) / context.std1

if context.posSign1 > 0 and (signal > 0 or signal < -4):
closeAll1(context, data, signal)
elif context.posSign1 < 0 and (signal < 0 or signal > 4):
closeAll1(context, data, signal)
elif context.days1 > 20:
closeAll1(context, data, signal)

def closeAll1(context, data, signal):
context.positionStocks1 = None
context.mean1 = 0
context.std1 = 0
context.posSign1 = 0
context.weights1 = None
context.hasPosition1 = False
context.prices1 = None
log.info(signal)
for sid in context.portfolio.positions:
order_target(sid, 0)

def openPos2(side, prices, W, abspval, data, context):
context.positionStocks2 = []
for i, sid in enumerate(prices):
order_target_value(sid, W[i] / abspval * context.portfolio.portfolio_value * side)
context.positionStocks2.append(sid)
context.days2 = 0
context.posSign2 = side
context.weights2 = W
context.hasPosition2 = True

def monitor2(context, data):
if not context.hasPosition2:
return
context.days2 += 1
prices = data.history(context.positionStocks2, "price", 60, "1d")
prices = prices.dropna(axis=1)
if len(prices.columns) <> len(context.positionStocks2):
closeAll2(context, data, 0)
return
pvalue = np.dot(prices.values, context.weights2 / context.prices2)
signal = (pvalue[-1] - context.mean2) / context.std2

if context.posSign2 > 0 and (signal > 0 or signal < -4):
closeAll2(context, data, signal)
elif context.posSign2 < 0 and (signal < 0 or signal > 4):
closeAll2(context, data, signal)
elif context.days2 > 20:
closeAll2(context, data, signal)

def closeAll2(context, data, signal):
context.positionStocks2 = None
context.mean2 = 0
context.std2 = 0
context.posSign2 = 0
context.weights2 = None
context.hasPosition2 = False
context.prices2 = None
log.info(signal)
for sid in context.portfolio.positions:
order_target(sid, 0)

def openPos3(side, prices, W, abspval, data, context):
context.positionStocks3 = []
for i, sid in enumerate(prices):
order_target_value(sid, W[i] / abspval * context.portfolio.portfolio_value * side)
context.positionStocks3.append(sid)
context.days3 = 0
context.posSign3 = side
context.weights3 = W
context.hasPosition3 = True

def monitor3(context, data):
if not context.hasPosition3:
return
context.days3 += 1
prices = data.history(context.positionStocks3, "price", 60, "1d")
prices = prices.dropna(axis=1)
if len(prices.columns) <> len(context.positionStocks3):
closeAll3(context, data, 0)
return
pvalue = np.dot(prices.values, context.weights3 / context.prices3)
signal = (pvalue[-1] - context.mean3) / context.std3

if context.posSign3 > 0 and (signal > 0 or signal < -4):
closeAll3(context, data, signal)
elif context.posSign3 < 0 and (signal < 0 or signal > 4):
closeAll3(context, data, signal)
elif context.days3 > 20:
closeAll3(context, data, signal)

def closeAll3(context, data, signal):
context.positionStocks3 = None
context.mean3 = 0
context.std3 = 0
context.posSign3 = 0
context.weights3 = None
context.hasPosition3 = False
context.prices3 = None
log.info(signal)
for sid in context.portfolio.positions:
order_target(sid, 0)                
There was a runtime error.
4 responses

Improved version 2 sharpe.

45
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
import math
import numpy as np
import pandas as pd
import scipy as sp
from statsmodels.tsa.vector_ar.var_model import VAR
from sklearn.covariance import OAS, EmpiricalCovariance
import statsmodels.api as smapi
from scipy.optimize import minimize
from sklearn.linear_model import LassoCV

def getweights(params, cov, signal):
cons = []
(m,n) = np.shape(params)

for i in range(0, n):
cons.append({'type': 'ineq', 'fun': lambda x, i=i: np.dot(x.T, params[:, i])})
cons.append({'type': 'ineq', 'fun': lambda x, i=i: -np.dot(x.T, params[:, i])})

x0 = [1.] * m
res = minimize(lambda x: 0., x0,
constraints = cons, method='cobyla',options={'maxiter':2000})
return res.x

def initialize(context):
context.hasPosition1 = False
context.hasPosition2 = False
context.hasPosition3 = False
context.stocks1 = None
context.stocks2 = None
context.stocks3 = None
context.positionStocks1 = None
context.prices1 = None
context.mean1 = 0
context.std1 = 0
context.posSign1 = 0
context.signal1 = 0
context.weights1 = None
context.positionStocks2 = None
context.prices2 = None
context.mean2 = 0
context.std2 = 0
context.posSign2 = 0
context.signal2 = 0
context.weights2 = None
context.positionStocks3 = None
context.prices3 = None
context.mean3 = 0
context.std3 = 0
context.posSign3 = 0
context.signal3 = 0
context.weights3 = None

schedule_function(monitor1, date_rules.every_day(), time_rules.market_close(minutes=60))

fundamental_df = get_fundamentals(query(fundamentals.valuation.market_cap)
.filter(fundamentals.asset_classification.morningstar_sector_code == 309)
.filter(fundamentals.valuation.market_cap != None)
.filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
.filter(fundamentals.share_class_reference.security_type == 'ST00000001')
.filter(~fundamentals.share_class_reference.symbol.contains('_WI'))
.filter(fundamentals.share_class_reference.is_primary_share == True)
.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
.order_by(fundamentals.valuation.market_cap.desc())).T
context.stocks1 = fundamental_df[0:50].index
context.stocks2 = fundamental_df[25:50].index
context.stocks3 = fundamental_df[50:75].index

def handle_data(context,data):
pass

if not context.hasPosition1:
success, W, abspval, prices = compute1(context, data, context.stocks1)
if success:
openPos1(-np.sign(context.signal1), prices, W, abspval, data, context)

if not context.hasPosition2:
success, W, abspval, prices = compute2(context, data, context.stocks2)
if success:
openPos2(-np.sign(context.signal2), prices, W, abspval, data, context)

if not context.hasPosition3:
success, W, abspval, prices = compute3(context, data, context.stocks3)
if success:
openPos3(-np.sign(context.signal3), prices, W, abspval, data, context)

def compute1(context, data, stocks):
prices = data.history(stocks, "price", 200, "1d")
prices = prices.dropna(axis=1)
returns = prices.pct_change().dropna().values
returns = returns * 1000.
cov = OAS().fit(returns).covariance_
e, v = np.linalg.eig(cov)
idx = e.argsort()
comp = v[:, idx[-15:]]

if comp[0, 0] < 0:
comp *= -1

sources = np.dot(returns, comp)
betas = np.zeros((np.shape(returns)[1], np.shape(sources)[1]))

for i in range(0, np.shape(returns)[1]):
model = LassoCV().fit(sources, returns[:, i])
betas[i, :] = model.coef_

W = getweights(betas, cov, np.asarray([1.] * np.shape(returns)[1]))
context.prices1 = prices.values[0, :]
pvalue = np.dot(prices.values, W / context.prices1)
context.mean1 = np.mean(pvalue)
context.std1 = np.std(pvalue)
context.signal1 = (pvalue[-1] - context.mean1) / context.std1
abspval = np.sum(np.abs(W))

if abs(context.signal1) < .5:
return False, None, None, None
return True, W, abspval, prices

def compute2(context, data, stocks):
prices = data.history(stocks, "price", 200, "1d")
prices = prices.dropna(axis=1)
returns = prices.pct_change().dropna().values
returns = returns * 1000.
cov = OAS().fit(returns).covariance_
e, v = np.linalg.eig(cov)
idx = e.argsort()
comp = v[:, idx[-15:]]

if comp[0, 0] < 0:
comp *= -1

sources = np.dot(returns, comp)
betas = np.zeros((np.shape(returns)[1], np.shape(sources)[1]))

for i in range(0, np.shape(returns)[1]):
model = LassoCV().fit(sources, returns[:, i])
betas[i, :] = model.coef_

W = getweights(betas, cov, np.asarray([1.] * np.shape(returns)[1]))
context.prices2 = prices.values[0, :]
pvalue = np.dot(prices.values, W / context.prices2)
context.mean2 = np.mean(pvalue)
context.std2 = np.std(pvalue)
context.signal2 = (pvalue[-1] - context.mean2) / context.std2
abspval = np.sum(np.abs(W))

if abs(context.signal2) < .5:
return False, None, None, None
return True, W, abspval, prices

def compute3(context, data, stocks):
prices = data.history(stocks, "price", 200, "1d")
prices = prices.dropna(axis=1)
returns = prices.pct_change().dropna().values
returns = returns * 1000.
cov = OAS().fit(returns).covariance_
e, v = np.linalg.eig(cov)
idx = e.argsort()
comp = v[:, idx[-15:]]

if comp[0, 0] < 0:
comp *= -1

sources = np.dot(returns, comp)
betas = np.zeros((np.shape(returns)[1], np.shape(sources)[1]))

for i in range(0, np.shape(returns)[1]):
model = LassoCV().fit(sources, returns[:, i])
betas[i, :] = model.coef_

W = getweights(betas, cov, np.asarray([1.] * np.shape(returns)[1]))
context.prices3 = prices.values[0, :]
pvalue = np.dot(prices.values, W / context.prices3)
context.mean3 = np.mean(pvalue)
context.std3 = np.std(pvalue)
context.signal3 = (pvalue[-1] - context.mean3) / context.std3
abspval = np.sum(np.abs(W))

if abs(context.signal3) < .5:
return False, None, None, None
return True, W, abspval, prices

def openPos1(side, prices, W, abspval, data, context):
context.positionStocks1 = []
for i, sid in enumerate(prices):
order_target_value(sid, W[i] / abspval * context.portfolio.portfolio_value * side)
context.positionStocks1.append(sid)
context.days1 = 0
context.posSign1 = side
context.weights1 = W
context.hasPosition1 = True

def monitor1(context, data):
if not context.hasPosition1:
return
context.days1 += 1
prices = data.history(context.positionStocks1, "price", 60, "1d")
prices = prices.dropna(axis=1)
if len(prices.columns) <> len(context.positionStocks1):
closeAll1(context, data, 0)
return
pvalue = np.dot(prices.values, context.weights1 / context.prices1)
signal = (pvalue[-1] - context.mean1) / context.std1

if context.posSign1 > 0 and (signal > 0 or signal < -4):
closeAll1(context, data, signal)
elif context.posSign1 < 0 and (signal < 0 or signal > 4):
closeAll1(context, data, signal)
elif context.days1 > 20:
closeAll1(context, data, signal)

def closeAll1(context, data, signal):
context.positionStocks1 = None
context.mean1 = 0
context.std1 = 0
context.posSign1 = 0
context.weights1 = None
context.hasPosition1 = False
context.prices1 = None
log.info(signal)
for sid in context.portfolio.positions:
order_target(sid, 0)

def openPos2(side, prices, W, abspval, data, context):
context.positionStocks2 = []
for i, sid in enumerate(prices):
order_target_value(sid, W[i] / abspval * context.portfolio.portfolio_value * side)
context.positionStocks2.append(sid)
context.days2 = 0
context.posSign2 = side
context.weights2 = W
context.hasPosition2 = True

def monitor2(context, data):
if not context.hasPosition2:
return
context.days2 += 1
prices = data.history(context.positionStocks2, "price", 60, "1d")
prices = prices.dropna(axis=1)
if len(prices.columns) <> len(context.positionStocks2):
closeAll2(context, data, 0)
return
pvalue = np.dot(prices.values, context.weights2 / context.prices2)
signal = (pvalue[-1] - context.mean2) / context.std2

if context.posSign2 > 0 and (signal > 0 or signal < -4):
closeAll2(context, data, signal)
elif context.posSign2 < 0 and (signal < 0 or signal > 4):
closeAll2(context, data, signal)
elif context.days2 > 20:
closeAll2(context, data, signal)

def closeAll2(context, data, signal):
context.positionStocks2 = None
context.mean2 = 0
context.std2 = 0
context.posSign2 = 0
context.weights2 = None
context.hasPosition2 = False
context.prices2 = None
log.info(signal)
for sid in context.portfolio.positions:
order_target(sid, 0)

def openPos3(side, prices, W, abspval, data, context):
context.positionStocks3 = []
for i, sid in enumerate(prices):
order_target_value(sid, W[i] / abspval * context.portfolio.portfolio_value * side)
context.positionStocks3.append(sid)
context.days3 = 0
context.posSign3 = side
context.weights3 = W
context.hasPosition3 = True

def monitor3(context, data):
if not context.hasPosition3:
return
context.days3 += 1
prices = data.history(context.positionStocks3, "price", 60, "1d")
prices = prices.dropna(axis=1)
if len(prices.columns) <> len(context.positionStocks3):
closeAll3(context, data, 0)
return
pvalue = np.dot(prices.values, context.weights3 / context.prices3)
signal = (pvalue[-1] - context.mean3) / context.std3

if context.posSign3 > 0 and (signal > 0 or signal < -4):
closeAll3(context, data, signal)
elif context.posSign3 < 0 and (signal < 0 or signal > 4):
closeAll3(context, data, signal)
elif context.days3 > 20:
closeAll3(context, data, signal)

def closeAll3(context, data, signal):
context.positionStocks3 = None
context.mean3 = 0
context.std3 = 0
context.posSign3 = 0
context.weights3 = None
context.hasPosition3 = False
context.prices3 = None
log.info(signal)
for sid in context.portfolio.positions:
order_target(sid, 0)                
There was a runtime error.

Hi Pravin, here is the requested change (and possibly some additional bugs, you can never trust code changes ;)

26
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
import math
import numpy as np
import pandas as pd
import scipy as sp
from statsmodels.tsa.vector_ar.var_model import VAR
from sklearn.covariance import OAS, EmpiricalCovariance
import statsmodels.api as smapi
from scipy.optimize import minimize
from sklearn.linear_model import LassoCV

def getweights(params, cov, signal):
cons = []
(m,n) = np.shape(params)

for i in range(0, n):
cons.append({'type': 'ineq', 'fun': lambda x, i=i: np.dot(x.T, params[:, i])})
cons.append({'type': 'ineq', 'fun': lambda x, i=i: -np.dot(x.T, params[:, i])})

x0 = [1.] * m
res = minimize(lambda x: 0., x0,
constraints = cons, method='cobyla',options={'maxiter':2000})
return res.x

class Porfolio(object) :

def __init__(self):
self.hasPosition = False
self.stocks = None
self.positionStocks = None
self.prices = None
self.mean = 0
self.std = 0
self.posSign = 0
self.signal = 0
self.weights = None

def set_stocks(self, stocks):
self.stocks = stocks

if not self.hasPosition:
success, W, abspval, prices = self.compute(context, data, self.stocks)
if success:
self.openPos(-np.sign(self.signal), prices, W, abspval, data, context)

def compute(self, context, data, stocks):
prices = data.history(stocks, "price", 200, "1d")
prices = prices.dropna(axis=1)
returns = prices.pct_change().dropna().values
returns = returns * 1000.
cov = OAS().fit(returns).covariance_
e, v = np.linalg.eig(cov)
idx = e.argsort()
comp = v[:, idx[-15:]]

if comp[0, 0] < 0:
comp *= -1

sources = np.dot(returns, comp)
betas = np.zeros((np.shape(returns)[1], np.shape(sources)[1]))

for i in range(0, np.shape(returns)[1]):
model = LassoCV().fit(sources, returns[:, i])
betas[i, :] = model.coef_

W = getweights(betas, cov, np.asarray([1.] * np.shape(returns)[1]))
self.prices = prices.values[0, :]
pvalue = np.dot(prices.values, W / self.prices)
self.mean = np.mean(pvalue)
self.std = np.std(pvalue)
self.signal = (pvalue[-1] - self.mean) / self.std
abspval = np.sum(np.abs(W))

if abs(self.signal) < .5:
return False, None, None, None
return True, W, abspval, prices

def openPos(self, side, prices, W, abspval, data, context):
self.positionStocks = []
for i, sid in enumerate(prices):
order_target_value(sid, W[i] / abspval * context.portfolio.portfolio_value * side)
self.positionStocks.append(sid)
self.days = 0
self.posSign = side
self.weights = W
self.hasPosition = True

def monitor(self, context, data):
if not self.hasPosition:
return
self.days += 1
prices = data.history(self.positionStocks, "price", 60, "1d")
prices = prices.dropna(axis=1)
if len(prices.columns) <> len(self.positionStocks):
self.closeAll(context, data, 0)
return
pvalue = np.dot(prices.values, self.weights / self.prices)
signal = (pvalue[-1] - self.mean) / self.std

if self.posSign > 0 and (signal > 0 or signal < -4):
self.closeAll(context, data, signal)
elif self.posSign < 0 and (signal < 0 or signal > 4):
self.closeAll(context, data, signal)
elif self.days > 20:
self.closeAll(context, data, signal)

def closeAll(self, context, data, signal):
self.positionStocks = None
self.mean = 0
self.std = 0
self.posSign = 0
self.weights = None
self.hasPosition = False
self.prices = None
log.info(signal)
for sid in context.portfolio.positions:
order_target(sid, 0)

def initialize(context):

context.portfolios = [Porfolio(), Porfolio(), Porfolio()]

monitor1 = lambda context, data: context.portfolios[0].monitor(context, data)
schedule_function(monitor1, date_rules.every_day(), time_rules.market_close(minutes=60))

fundamental_df = get_fundamentals(query(fundamentals.valuation.market_cap)
.filter(fundamentals.asset_classification.morningstar_sector_code == 309)
.filter(fundamentals.valuation.market_cap != None)
.filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
.filter(fundamentals.share_class_reference.security_type == 'ST00000001')
.filter(~fundamentals.share_class_reference.symbol.contains('_WI'))
.filter(fundamentals.share_class_reference.is_primary_share == True)
.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
.order_by(fundamentals.valuation.market_cap.desc())).T
context.portfolios[0].set_stocks( fundamental_df[0:50].index )
context.portfolios[1].set_stocks( fundamental_df[25:50].index )
context.portfolios[2].set_stocks( fundamental_df[50:75].index )

def handle_data(context,data):
pass

for p in context.portfolios:

There was a runtime error.

Thank you very much Luca. Code is cleaner and easily scalable now. Many thanks again.

10 million 1 portfolio 2 years with Luca's code. It suffers very few losses but opportunity is scant with just one portfolio.

60
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
import math
import numpy as np
import pandas as pd
import scipy as sp
from statsmodels.tsa.vector_ar.var_model import VAR
from sklearn.covariance import OAS, EmpiricalCovariance
import statsmodels.api as smapi
from scipy.optimize import minimize
from sklearn.linear_model import LinearRegression

def getweights(params, cov, signal):
cons = []
(m,n) = np.shape(params)

for i in range(0, n):
cons.append({'type': 'eq', 'fun': lambda x, i=i: np.dot(x.T, params[:, i])})

cons.append({'type': 'eq', 'fun': lambda x: np.sum(x)})

x0 = [1.] * m
res = minimize(lambda x: 1. / np.dot(x.T, np.dot(cov, x)), x0, constraints = cons, method='SLSQP',options={'maxiter':2000})
return res.x

class Porfolio(object) :
hasPosition = False

def __init__(self):
self.stocks = None
self.positionStocks = None
self.prices = None
self.mean = 0
self.std = 0
self.posSign = 0
self.signal = 0
self.weights = None

def set_stocks(self, stocks):
self.stocks = stocks

if not self.hasPosition:
success, W, abspval, prices = self.compute(context, data, self.stocks)
if success:
print self.signal
self.openPos(-np.sign(self.signal), prices, W, abspval, data, context)
context.count += 1

def compute(self, context, data, stocks):
prices = data.history(stocks, "price", 75, "1d")
prices = prices.dropna(axis=1)
returns = prices.pct_change().dropna().values
cov = OAS().fit(returns).covariance_
e, v = np.linalg.eig(cov)
idx = e.argsort()
comp = v[:, idx[-15:]]

if comp[0, 0] < 0:
comp *= -1

sources = np.dot(returns, comp)
betas = np.zeros((np.shape(returns)[1], np.shape(sources)[1]))

for i in range(0, np.shape(returns)[1]):
model = LinearRegression().fit(sources, returns[:, i])
betas[i, :] = model.coef_

W = getweights(betas, cov, np.asarray([1.] * np.shape(returns)[1]))
self.prices = prices.values[0, :]
pvalue = np.dot(prices.values, W / self.prices)
self.mean = np.mean(pvalue)
self.std = np.std(pvalue)
self.signal = (pvalue[-1] - self.mean) / self.std
abspval = np.sum(np.abs(W))

if abs(self.signal) < 2:
return False, None, None, None
return True, W, abspval, prices

def openPos(self, side, prices, W, abspval, data, context):
self.positionStocks = []
for i, sid in enumerate(prices):
order_target_value(sid, W[i] / abspval * context.portfolio.portfolio_value * 2 * side)
self.positionStocks.append(sid)
self.days = 0
self.posSign = side
self.weights = W
self.hasPosition = True

def monitor(self, context, data):
if not self.hasPosition:
return
self.days += 1
prices = data.history(self.positionStocks, "price", 60, "1d")
prices = prices.dropna(axis=1)
if len(prices.columns) <> len(self.positionStocks):
self.closeAll(context, data, 0)
return
pvalue = np.dot(prices.values, self.weights / self.prices)
signal = (pvalue[-1] - self.mean) / self.std

if self.posSign > 0 and (signal > -0 or signal < -4):
self.closeAll(context, data, signal)
elif self.posSign < 0 and (signal < 0 or signal > 4):
self.closeAll(context, data, signal)
elif self.days > 45:
self.closeAll(context, data, signal)

def closeAll(self, context, data, signal):
context.count -= 1
self.positionStocks = None
self.mean = 0
self.std = 0
self.posSign = 0
self.weights = None
self.hasPosition = False
self.prices = None
print signal
for sid in context.portfolio.positions:
order_target(sid, 0)

def initialize(context):

context.portfolios = [Porfolio()]
context.count = 0
schedule_function(monitor, date_rules.every_day(), time_rules.market_close(minutes=180))

fundamental_df = get_fundamentals(query(fundamentals.valuation.market_cap)
.filter(fundamentals.valuation.market_cap != None)
.filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
.filter(fundamentals.share_class_reference.security_type == 'ST00000001')
.filter(~fundamentals.share_class_reference.symbol.contains('_WI'))
.filter(fundamentals.share_class_reference.is_primary_share == True)
.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
.order_by(fundamentals.valuation.market_cap.desc())).T
context.portfolios[0].set_stocks( fundamental_df[0:50].index )

def handle_data(context,data):
pass