Back to Community
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?

Clone Algorithm
45
Loading...
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
from statsmodels.tsa.stattools import adfuller
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(trade, date_rules.every_day(), time_rules.market_open(minutes=12))
    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))      
 
def before_trading_start(context, data):
    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

def trade(context, data):

    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 = smapi.RLM(returns[:, i], smapi.add_constant(sources)).fit()
        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]):
        model = smapi.RLM(returns[:, i], smapi.add_constant(sources)).fit()
        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]):
        model = smapi.RLM(returns[:, i], smapi.add_constant(sources)).fit()
        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.

Clone Algorithm
45
Loading...
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
from statsmodels.tsa.stattools import adfuller
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(trade, date_rules.every_day(), time_rules.market_open(minutes=12))
    schedule_function(monitor1, date_rules.every_day(), time_rules.market_close(minutes=60))      
     
 
def before_trading_start(context, data):
    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

def trade(context, data):

    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 ;)
thanks for sharing your ideas

Clone Algorithm
26
Loading...
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
from statsmodels.tsa.stattools import adfuller
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
        
    def trade(self, context, data):
        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()]

    schedule_function(trade, date_rules.every_day(), time_rules.market_open(minutes=12))
    monitor1 = lambda context, data: context.portfolios[0].monitor(context, data)
    schedule_function(monitor1, date_rules.every_day(), time_rules.market_close(minutes=60))      
     
def before_trading_start(context, data):
    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

def trade(context, data):
    for p in context.portfolios:
        p.trade(context, data)
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.

Clone Algorithm
60
Loading...
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
from statsmodels.tsa.stattools import adfuller
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
        
    def trade(self, context, data):
        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(trade, date_rules.every_day(), time_rules.market_open(minutes=12))
    schedule_function(monitor, date_rules.every_day(), time_rules.market_close(minutes=180))      
     
def before_trading_start(context, data):
    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

def trade(context, data):
    if context.count > 0:
        return
    
    for p in context.portfolios:
        p.trade(context, data)
        if context.count > 0:
            return

def monitor(context, data):        
    for p in context.portfolios:
        p.monitor(context, data)
There was a runtime error.