Back to Community
Trading strategy - experiment with covariance and cross sectional mean

A simple algorithm based on cross sectional mean of a bunch of stocks and their covariance. It works great from 2003 until July 2008 but after that fails to work. I think it can be improved by using a different bunch of stocks and also possibly changing the hedge security. Please keep me posted if you manage to find a better composition or trick to get it working.

Clone Algorithm
550
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 numpy as np
import statsmodels.api as smapi
import math
import pandas as pd
from scipy import stats

def initialize(context):
    set_symbol_lookup_date('2015-08-01')
    context.XLE = sid(8554)
    context.stocks = symbols('XOM','CVX','SLB','KMI','EOG','WMB','OXY','VLO','PSX','COP','APC','PXD','TSO','HAL','BHI','MPC','SE','COG','APA','DVN','NBL','NOV','HES','CAM','FTI','MRO','EQT','XEC','RRC','CPGX','SWN','OKE','MUR','HP','NFX','CHK','CNX','RIG','ESV','DO') 

    schedule_function(myfunc,date_rule=date_rules.every_day(),time_rule=time_rules.market_close(minutes=30))
    
def handle_data(context, data):
    record(l=context.account.leverage)
    pass

def myfunc(context, data):
    prices = history(90, "1d", "price")
    prices = prices.dropna(axis=1)
    prices = prices.drop([context.XLE], axis=1)
    ret = prices.pct_change(5).dropna().values
    ret = np.log1p(ret)
    xle = np.median(prices.pct_change(5).dropna().values, axis=1)
    xle = np.log1p(xle)
    
    scores = []
    i = 0
    for sid in prices:
        excess = ret[:,i] - xle
        cov = np.cov(np.diff(ret[:,i]), np.diff(excess))[0,1]
        scores.append(cov)
        i += 1
    
    netscore = np.sum(np.abs(scores))
    
    i = 0
    wsum = 0
    for sid in prices:
        val = scores[i] * 140000 / netscore
        order_target_value(sid,  val)
        wsum += val
        i += 1      
    order_target_value(context.XLE, -wsum) 
There was a runtime error.
3 responses

Could you please write down the equations (formulas) that you are using to get the score for a given security?

Tim,

I am taking weekly log returns (WSR) and then finding the cross sectional mean return (MR) for each day. Then excess returns are defined as the return on top of the MR.

Excess return (ER) = WSR - MR
Weight = covariance(delta(WSR), delta(ER))

where delta is the first difference.

Hope that helps.

Best regards,
Pravin

While trying to understand what this algorithm was doing, I added some comments and changed some of the variable names, etc. I'm attaching my result of this - I shouldn't have changed anything about what this algo actually does. Thought others might find this useful..

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 numpy as np
import statsmodels.api as smapi
import math
import pandas as pd
from scipy import stats

#initialize runs one time at the beginning of the run 
def initialize(context):
    set_symbol_lookup_date('2015-08-01')
#this is SPY
    context.XLE = sid(8554)
    context.stocks = symbols('XOM','CVX','SLB','KMI','EOG','WMB','OXY','VLO','PSX','COP','APC','PXD','TSO','HAL','BHI','MPC','SE','COG','APA','DVN','NBL','NOV','HES','CAM','FTI','MRO','EQT','XEC','RRC','CPGX','SWN','OKE','MUR','HP','NFX','CHK','CNX','RIG','ESV','DO') 
#   schedule market_close_func to run every day 30 minutes before market close
    schedule_function(market_close_func,date_rule=date_rules.every_day(),time_rule=time_rules.market_close(minutes=30))
    
#handle_data runs whenever data changes
def handle_data(context, data):
    record(l=context.account.leverage)
    pass

#we have scheduled this to run daily before close
def market_close_func(context, data):
    #get 90 days worth of daily prices for our stocks
    prices = history(90, "1d", "price")
    prices = prices.dropna(axis=1)
    prices = prices.drop([context.XLE], axis=1)
    #calculate the percent change (return) over the last 5 periods (days)
    pctchange = prices.pct_change(5).dropna().values
    #calculate the natural logarithm of the 5 day return
    ret = np.log1p(pctchange)
    
    #calculate the median return across all stocks for the day
    xle = np.median(pctchange, axis=1)
    #calculate the log of the median return (cross sectional median return)
    xle = np.log1p(xle)
    
    scores = []
    i = 0
    for thisstock_sid in prices:
        #calculate excess return as this stocks return above the median return
        excess = ret[:,i] - xle
        #calculate the daily change 
        dchange = np.diff(ret[:,i])
        #calculate the daily excess change
        echange =np.diff(excess)
        #covariance of the 5 day return and the excess for daily change
        cov = np.cov(dchange, echange)[0,1]
        scores.append(cov)
        i += 1
    
    netscore = np.sum(np.abs(scores))
    
    max_dollars_invested = 140000
    i = 0
    #wsum is the total dollars invested
    wsum = 0
    for thisstock_sid in prices:
        thisstock_investment_ratio = scores[i] / netscore 
        thisstock_dollar_target = max_dollars_invested * thisstock_investment_ratio
        #adjust the number of shares purchased for thisstock to match the dollar target
        order_target_value(thisstock_sid,  thisstock_dollar_target)
        wsum += thisstock_dollar_target
        i += 1      
    #hedge by purchasing the opposite amount of SPY 
    order_target_value(context.XLE, -wsum) 
There was a runtime error.