Back to Community
PC1 acceleration

Acceleration of factor vs. returns.

Clone Algorithm
64
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 cvxpy as cvx
import math
import numpy as np
import pandas as pd
import scipy as sp
import statsmodels.api as smapi
from sklearn.covariance import OAS
from sklearn.decomposition import PCA
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline.filters.morningstar import Q500US
from quantopian.pipeline.classifiers.morningstar import Sector
from quantopian.pipeline.factors.morningstar import MarketCap
from quantopian.pipeline.data import morningstar as mstar

def make_pipeline():
    price_filter = USEquityPricing.close.latest >= 25
    pipe = Pipeline(screen=Q500US() & price_filter)
    return pipe
    
def initialize(context):
    context.stocks = None
    context.counter = 90
    schedule_function(trade, 
                      date_rules.week_start(), 
                      time_rules.market_open(minutes=20))
    
    schedule_function(close, date_rules.week_start(), time_rules.market_open(minutes=21))
    
        
    schedule_function(update_chart, 
                      date_rules.every_day(), 
                      time_rules.market_open(minutes=21))
    
    attach_pipeline(make_pipeline(), "Q500")
    
def handle_data(context, data):
    pass

def close(context, data):
    for sid in context.portfolio.positions:
        if sid not in context.stocks:
            if data.can_trade(sid):
                order_target(sid, 0)
                
   
def before_trading_start(context, data):
    if context.counter < 90:
        context.counter += 1
        return
    context.counter = 0
    context.stocks = pipeline_output("Q500").index

def trade(context, data):
    prices = data.history(context.stocks, "price", 200, "1d").resample('W').last()
    prices = prices.dropna(axis=1)
    ret = prices.pct_change().dropna().values
    factors = PCA(1).fit_transform(ret)
    model = smapi.OLS(ret, factors).fit()
    betas = model.params.T[:, 0:]
    scores = np.zeros(ret.shape[1])
    
    X = np.diff(factors.ravel())
    for i in range(0, ret.shape[1]):
        Y = np.diff(ret[:, i])
        model = smapi.OLS(Y, smapi.add_constant(X)).fit()
        scores[i] = model.params[0] / model.ssr
    idx = scores.argsort()
    scores[idx[50:-50]] = 0.
    weights = getW(OAS().fit(ret).covariance_, betas, scores)
    den = np.sum(np.abs(weights))
    
    if den == 0:
        den = 1
        
    weights /= den
                   
    for i, sid in enumerate(prices.columns.values):
        val = weights[i] * context.portfolio.portfolio_value
        order_target_value(sid, -val)

def getW(cov, betas, signal):
    (m, n) = betas.shape
    x = cvx.Variable(m)
    objective = cvx.Maximize(signal.T * x - cvx.quad_form(x, cov))
    
    constraints = [sum(x) == 0, sum(cvx.abs(x)) <= 1]
    
    for i in range(0, n):
        constraints.append(betas[:, i].T * x == 0)
    
    for i in range(0, m):
        constraints.append(x[i] <= 0.04)
        constraints.append(-x[i] <= 0.04)
        
    prob = cvx.Problem(objective, constraints)
    prob.solve(solver=cvx.CVXOPT)
    
    if prob.status <> 'optimal':
        print prob.status
    return np.asarray(x.value).flatten()

def update_chart(context,data):
    record(leverage = context.account.leverage)

    longs = shorts = 0
    long_lever = 0
    short_lever = 0
    
    for position in context.portfolio.positions.itervalues():        
        if position.amount > 0:
            longs += 1
            
            long_lever += position.amount*data.current(position.sid, 'price')
        if position.amount < 0:
            shorts += 1
            short_lever += -position.amount*data.current(position.sid, 'price')
            
    long_lever /= context.portfolio.portfolio_value
    short_lever /= context.portfolio.portfolio_value
    if 'weights' in context:
        record(target_lev=context.weights.abs().sum())
    record(long_lever=long_lever, short_lever=short_lever)
There was a runtime error.
3 responses

Nice algo, Pravin. First time I saw the use of principal components with that perspective. Have you thought about using more than one? The algo does not have a good performance in 2014 for example, do you have any idea why? What is your thought for further development?

@Roberto, I haven't tried using more than one PC factor. This was just an experiment and I think has to be clubbed with other signals to get meaningful results.

@Pravin , can you explain a bit about what you are doing in the algo?