Back to Community
new contest entry trial

Looking for feedback on suitability for new contest.

Clone Algorithm
49
Loading...
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
from quantopian.algorithm import attach_pipeline, pipeline_output, order_optimal_portfolio
from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import Latest, SimpleBeta, AnnualizedVolatility
from quantopian.pipeline.data import Fundamentals
from quantopian.pipeline.data.builtin import USEquityPricing
import quantopian.optimize as opt
from sklearn import preprocessing
from quantopian.pipeline.experimental import QTradableStocksUS, risk_loading_pipeline
from scipy.stats.mstats import winsorize
import numpy as np
import pandas as pd

# Algo parameters
NUM_TOTAL_POSITIONS = 300
N_LEADING = 10 # days
N_TRAILING = 30 # days
EPSILON = 1.0
WIN_LIMIT = 0

# Optimize API parameters
MAX_GROSS_EXPOSURE = 1.0
MAX_POSITION_SIZE = 0.05 # absolute value
MIN_BETA_EXPOSURE = -0.3
MAX_BETA_EXPOSURE = -0.3

def initialize(context):
    
    # set_commission(commission.PerShare(cost=0, min_trade_cost=0))
    # set_slippage(slippage.FixedSlippage(spread=0))
    set_slippage(slippage.FixedBasisPointsSlippage())
    
    schedule_function(record_out, date_rules.every_day(), time_rules.market_close())
    schedule_function(get_weights, date_rules.every_day(), time_rules.market_open(minutes=60))
    schedule_function(rebalance, date_rules.every_day(), time_rules.market_open(minutes=60))
     
    attach_pipeline(make_pipeline(context), 'my_pipe')
    attach_pipeline(risk_loading_pipeline(), 'risk_loading_pipeline')
    
def make_pipeline(context):
    
    universe = (
        AnnualizedVolatility(mask=QTradableStocksUS())
        .percentile_between(80, 95))
    
    beta = SimpleBeta(target=sid(8554),
                      regression_length=260,
                     )
    
    pipe = Pipeline(columns = {
        'beta':beta,
    },
    screen = universe)
    
    return pipe
    
def before_trading_start(context,data):
    
    context.pipeline_data = pipeline_output('my_pipe')
    context.stocks = list(pipeline_output('my_pipe').index.values)
    context.risk_loading_pipeline = pipeline_output('risk_loading_pipeline')
  
def record_out(context, data):
            
    num_secs = 0
    
    for stock in context.portfolio.positions.keys():
        if context.portfolio.positions[stock].amount > 0:
            num_secs += 1
            
    record(num_secs = num_secs)
    record(leverage = context.account.leverage)

def get_weights(context,data):
    
    prices = data.history(context.stocks, 'price', 390*N_TRAILING, '1m').dropna(axis=1)
    context.stocks = list(prices.columns.values)
    prices = prices.ewm(ignore_na=False,min_periods=0,adjust=True,com=78).mean()
      
    m = len(context.stocks)
    
    b_t = np.zeros(m)
    
    for i, stock in enumerate(context.stocks):
        b_t[i] = abs(context.portfolio.positions[stock].amount*data.current(stock,'price'))
    
    denom = np.sum(np.absolute(b_t))
    
    # test for divide-by-zero case
    if denom > 0:
        b_t = b_t/denom
    else:
        b_t = 1.0*np.ones(m)/m
      
    a = np.zeros(m)
    b = np.zeros(m)
        
    for n in range(5*N_LEADING,5*N_TRAILING+1):
        p = prices.tail(n*78).as_matrix(context.stocks)
        p_mean = np.mean(p,axis=0)
        
        p_rel = p_mean/p[-1,:]
        p_rel[p_rel<1] = 0
        a += preprocess(get_opt(p_rel,np.sign(p_rel)*b_t))
        
        p_rel = p[-1,:]/p_mean
        p_rel[p_rel<1] = 0
        b += preprocess(get_opt(p_rel,np.sign(p_rel)*b_t))
        
    a = preprocess(a)
    a[a<0] = 0
    b = preprocess(b)
    b[b<0] = 0
                
    context.weights = pd.Series(preprocess(a-b),index=context.stocks)
    
    context.weights = context.weights - context.weights.mean()
    context.weights = context.weights/context.weights.abs().sum()
    
    close_list = {}
    for stock in context.portfolio.positions.keys():
        if stock not in context.stocks:
            close_list[stock] = 0
    
    context.weights = context.weights.append(pd.Series(close_list))
    
def get_opt(x_tilde,b_t):

    x_bar = x_tilde.mean()

    # Calculate terms for lambda (lam)
    dot_prod = np.dot(b_t, x_tilde)
    num = EPSILON - dot_prod
    denom = (np.linalg.norm((x_tilde-x_bar)))**2

    # test for divide-by-zero case
    if denom == 0.0:
        lam = 0 # no portolio update
    else:     
        lam = max(0, num/denom)
                
    b = b_t + lam*(x_tilde-x_bar)

    b_norm = simplex_projection(b)
    
    return b_norm*np.dot(b_norm,x_tilde)

def rebalance(context, data):
    
    pipeline_data = context.pipeline_data
    
    objective = opt.TargetWeights(context.weights)
    
    constraints = []
    constraints.append(opt.MaxGrossExposure(MAX_GROSS_EXPOSURE))   
    constraints.append(opt.DollarNeutral())
    
    beta_neutral = opt.FactorExposure(
        loadings=pipeline_data[['beta']],
        min_exposures={'beta':MIN_BETA_EXPOSURE},
        max_exposures={'beta':MAX_BETA_EXPOSURE}
        )
    constraints.append(beta_neutral)
    
    constraints.append(
        opt.PositionConcentration.with_equal_bounds(
            min=-MAX_POSITION_SIZE,
            max=MAX_POSITION_SIZE
        ))
    
    risk_model_exposure = opt.experimental.RiskModelExposure(
        context.risk_loading_pipeline,
        version=opt.Newest,
    )
    constraints.append(risk_model_exposure)
    
    order_optimal_portfolio(
        objective=objective,
        constraints=constraints,
        )
         
def simplex_projection(v, b=1):
    """Projection vectors to the simplex domain

Implemented according to the paper: Efficient projections onto the
l1-ball for learning in high dimensions, John Duchi, et al. ICML 2008.
Implementation Time: 2011 June 17 by [email protected] AT pmail.ntu.edu.sg
Optimization Problem: min_{w}\| w - v \|_{2}^{2}
s.t. sum_{i=1}^{m}=z, w_{i}\geq 0

Input: A vector v \in R^{m}, and a scalar z > 0 (default=1)
Output: Projection vector w

:Example:
>>> proj = simplex_projection([.4 ,.3, -.4, .5])
>>> print proj
array([ 0.33333333, 0.23333333, 0. , 0.43333333])
>>> print proj.sum()
1.0

Original matlab implementation: John Duchi ([email protected])
Python-port: Copyright 2012 by Thomas Wiecki ([email protected]).
"""

    v = np.asarray(v)
    p = len(v)

    # Sort v into u in descending order
    v = (v > 0) * v
    u = np.sort(v)[::-1]
    sv = np.cumsum(u)

    rho = np.where(u > (sv - b) / np.arange(1, p+1))[0][-1]
    theta = np.max([0, (sv[rho] - b) / (rho+1)])
    w = (v - theta)
    w[w<0] = 0
    return w

def preprocess(a):
    
    a = np.nan_to_num(a - np.nanmean(a))
    a = winsorize(a,limits=(WIN_LIMIT,WIN_LIMIT))
    a = a/np.sum(np.absolute(a))
    
    return preprocessing.scale(a)
There was a runtime error.
23 responses

Hi Grant,

I'd recommend looking at lesson 11 of the Contest tutorial which will direct you to a notebook for evaluating a backtest using the new contest criteria. In another thread, you were asking for details on some of the criteria. The notebook has a prototype of the code for computing the criteria, so I suggest you look there to find out more about how the criteria are checked.

Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

Well, almost passed. Failed on max position concentration (which makes sense, since I'd set the constraint at 5%).


Checking positions concentration limit...
FAIL: Max position concentration of 6.11% > 5.0%.

Checking leverage limits...
PASS: Leverage range of 0.95x-1.04x is between 0.8x-1.1x.

Checking turnover limits...
PASS: Mean turnover range of 26.11%-35.49% is between 5.0%-65.0%.

Checking net exposure limit...
PASS: Net exposure (absolute value) of 7.09% <= 10.0%.

Checking beta-to-SPY limit...
PASS: Absolute beta with max of 0.18 satisfies all constraints.

Checking sector exposure limits...
PASS: All sector exposures were between +/-0.20.

Checking style exposure limits...
PASS: All style exposures were between +/-0.40.

Checking investment in tradable universe...
PASS: Investment in QTradableStocksUS is >= 90.0% at all times.

Checking that algorithm has positive returns...
PASS: Cumulative returns of 0.18 is positive.

Results:
8/9 tests passed.

Score computed between 2018-01-18 and 2018-01-19.
Cumulative Score: 0.105952

Loading notebook preview...

Karl -

Thanks - I've been meaning to try that Alphalens thingy. I could convert the algo to daily data and then write a pipeline factor, which perhaps would make applying Alphalens to it easier? I'd prefer to run Alphalens on the factor based on minute data, but I've never seen a working example.

One small thing to note is that projecting onto the l1 ball tends to give sparse weights. I can imagine the Quantopian black-box optimizer having a tug-of-war with this OLMAR style of optimization.

Another thing to consider is reversion to the median rather than the mean, as estimating the median is more robust to outliers than estimating the mean.

Thanks Karl & HS,

The optimization can also be done with CVXPY, which is more flexible than the OLMAR formulation (although the latter appears to be more efficient). In fact, I think the Optimize API can be used with the target weights objective, which would allow applying other constraints, in addition to the (forecast profit >= 1.0) one.

Regarding the median vs. mean, it is a simple matter of changing to np.median. I've tried this in the past and should probably revisit it.

Hi Grant,

I am no longer motivated to participate in this community forum, but I thought I've give one last post to make you aware, if you aren't already, that the QTradableStocksUS have been moved from experimental to filters. I found out about this last night while playing with one of my algos that I was entering for Contest 38 and got a deprecated warning about this change. I guess this is Q's way to make an announcement of changes via deprecation warnings. This is nothing out of the extraordinary but it changes the final list of stocks under the QTradableStocksUS universe. The effect on my algo was negative as some of what Optimize API chose as my tradeable stocks under the experimental mode is no longer available or were changed by Optimize API under the new filters mode.

So I thought I'd try it with your above algo as yours is an interesting case. Your algo has managed to achieved zero beta by making the beta constraint an equality constraint, both min and max beta exposure are set to -0.3. This is actually going against the grain of how it should function, if Optimize API was working properly. The min beta exposure should be a negative number and the max beta exposure should be a positive number (i.e, -+ 0.05). Nevertheless, you found a way to zero beta whether intentionally, by accident or whatever reasons. The effect of this new change in QTradableStocksUS for your above algo is actually positive and I attached backtest below. Keep at it, Grant, I think you have a good algo here and a few more tweaks and you're good to go, Cheers!

PS- I advice everyone to rerun their backtests done under the experimental QTradableStocksUS , results may change.

Clone Algorithm
26
Loading...
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
from quantopian.algorithm import attach_pipeline, pipeline_output, order_optimal_portfolio
from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import Latest, SimpleBeta, AnnualizedVolatility
from quantopian.pipeline.data import Fundamentals
from quantopian.pipeline.data.builtin import USEquityPricing
import quantopian.optimize as opt
from sklearn import preprocessing
#from quantopian.pipeline.experimental import QTradableStocksUS, risk_loading_pipeline
from quantopian.pipeline.experimental import risk_loading_pipeline
from quantopian.pipeline.filters import QTradableStocksUS
from scipy.stats.mstats import winsorize
import numpy as np
import pandas as pd

# Algo parameters
NUM_TOTAL_POSITIONS = 300
N_LEADING = 10 # days
N_TRAILING = 30 # days
EPSILON = 1.0
WIN_LIMIT = 0

# Optimize API parameters
MAX_GROSS_EXPOSURE = 1.0
MAX_POSITION_SIZE = 0.05 # absolute value
MIN_BETA_EXPOSURE = -0.3
MAX_BETA_EXPOSURE = -0.3

def initialize(context):
    
    # set_commission(commission.PerShare(cost=0, min_trade_cost=0))
    # set_slippage(slippage.FixedSlippage(spread=0))
    set_slippage(slippage.FixedBasisPointsSlippage())
    
    schedule_function(record_out, date_rules.every_day(), time_rules.market_close())
    schedule_function(get_weights, date_rules.every_day(), time_rules.market_open(minutes=60))
    schedule_function(rebalance, date_rules.every_day(), time_rules.market_open(minutes=60))
     
    attach_pipeline(make_pipeline(context), 'my_pipe')
    attach_pipeline(risk_loading_pipeline(), 'risk_loading_pipeline')
    
def make_pipeline(context):
    
    universe = (
        AnnualizedVolatility(mask=QTradableStocksUS())
        .percentile_between(80, 95))
    
    beta = SimpleBeta(target=sid(8554),
                      regression_length=260,
                     )
    
    pipe = Pipeline(columns = {
        'beta':beta,
    },
    screen = universe & beta.notnull())
    
    return pipe
    
def before_trading_start(context,data):
    
    context.pipeline_data = pipeline_output('my_pipe')
    context.stocks = list(pipeline_output('my_pipe').index.values)
    context.risk_loading_pipeline = pipeline_output('risk_loading_pipeline')
  
def record_out(context, data):
            
    num_secs = 0
    
    for stock in context.portfolio.positions.keys():
        if context.portfolio.positions[stock].amount > 0:
            num_secs += 1
            
    record(num_secs = num_secs)
    record(leverage = context.account.leverage)

def get_weights(context,data):
    
    prices = data.history(context.stocks, 'price', 390*N_TRAILING, '1m').dropna(axis=1)
    context.stocks = list(prices.columns.values)
    prices = prices.ewm(ignore_na=False,min_periods=0,adjust=True,com=78).mean()
      
    m = len(context.stocks)
    
    b_t = np.zeros(m)
    
    for i, stock in enumerate(context.stocks):
        b_t[i] = abs(context.portfolio.positions[stock].amount*data.current(stock,'price'))
    
    denom = np.sum(np.absolute(b_t))
    
    # test for divide-by-zero case
    if denom > 0:
        b_t = b_t/denom
    else:
        b_t = 1.0*np.ones(m)/m
      
    a = np.zeros(m)
    b = np.zeros(m)
        
    for n in range(5*N_LEADING,5*N_TRAILING+1):
        p = prices.tail(n*78).as_matrix(context.stocks)
        p_mean = np.mean(p,axis=0)
        
        p_rel = p_mean/p[-1,:]
        p_rel[p_rel<1] = 0
        a += preprocess(get_opt(p_rel,np.sign(p_rel)*b_t))
        
        p_rel = p[-1,:]/p_mean
        p_rel[p_rel<1] = 0
        b += preprocess(get_opt(p_rel,np.sign(p_rel)*b_t))
        
    a = preprocess(a)
    a[a<0] = 0
    b = preprocess(b)
    b[b<0] = 0
                
    context.weights = pd.Series(preprocess(a-b),index=context.stocks)
    
    context.weights = context.weights - context.weights.mean()
    context.weights = context.weights/context.weights.abs().sum()
    
    close_list = {}
    for stock in context.portfolio.positions.keys():
        if stock not in context.stocks:
            close_list[stock] = 0
    
    context.weights = context.weights.append(pd.Series(close_list))
    
def get_opt(x_tilde,b_t):

    x_bar = x_tilde.mean()

    # Calculate terms for lambda (lam)
    dot_prod = np.dot(b_t, x_tilde)
    num = EPSILON - dot_prod
    denom = (np.linalg.norm((x_tilde-x_bar)))**2

    # test for divide-by-zero case
    if denom == 0.0:
        lam = 0 # no portolio update
    else:     
        lam = max(0, num/denom)
                
    b = b_t + lam*(x_tilde-x_bar)

    b_norm = simplex_projection(b)
    
    return b_norm*np.dot(b_norm,x_tilde)

def rebalance(context, data):
    
    pipeline_data = context.pipeline_data
    
    objective = opt.TargetWeights(context.weights)
    
    constraints = []
    constraints.append(opt.MaxGrossExposure(MAX_GROSS_EXPOSURE))   
    constraints.append(opt.DollarNeutral())
    
    beta_neutral = opt.FactorExposure(
        loadings=pipeline_data[['beta']],
        min_exposures={'beta':MIN_BETA_EXPOSURE},
        max_exposures={'beta':MAX_BETA_EXPOSURE}
        )
    constraints.append(beta_neutral)
    
    constraints.append(
        opt.PositionConcentration.with_equal_bounds(
            min=-MAX_POSITION_SIZE,
            max=MAX_POSITION_SIZE
        ))
    
    risk_model_exposure = opt.experimental.RiskModelExposure(
        context.risk_loading_pipeline,
        version=opt.Newest,
    )
    constraints.append(risk_model_exposure)
    
    order_optimal_portfolio(
        objective=objective,
        constraints=constraints,
        )
         
def simplex_projection(v, b=1):
    """Projection vectors to the simplex domain

Implemented according to the paper: Efficient projections onto the
l1-ball for learning in high dimensions, John Duchi, et al. ICML 2008.
Implementation Time: 2011 June 17 by [email protected] AT pmail.ntu.edu.sg
Optimization Problem: min_{w}\| w - v \|_{2}^{2}
s.t. sum_{i=1}^{m}=z, w_{i}\geq 0

Input: A vector v \in R^{m}, and a scalar z > 0 (default=1)
Output: Projection vector w

:Example:
>>> proj = simplex_projection([.4 ,.3, -.4, .5])
>>> print proj
array([ 0.33333333, 0.23333333, 0. , 0.43333333])
>>> print proj.sum()
1.0

Original matlab implementation: John Duchi ([email protected])
Python-port: Copyright 2012 by Thomas Wiecki ([email protected]).
"""

    v = np.asarray(v)
    p = len(v)

    # Sort v into u in descending order
    v = (v > 0) * v
    u = np.sort(v)[::-1]
    sv = np.cumsum(u)

    rho = np.where(u > (sv - b) / np.arange(1, p+1))[0][-1]
    theta = np.max([0, (sv[rho] - b) / (rho+1)])
    w = (v - theta)
    w[w<0] = 0
    return w

def preprocess(a):
    
    a = np.nan_to_num(a - np.nanmean(a))
    a = winsorize(a,limits=(WIN_LIMIT,WIN_LIMIT))
    a = a/np.sum(np.absolute(a))
    
    return preprocessing.scale(a)
There was a runtime error.

Thanks James. Take care, Grant

You're welcome, Grant. One last thing, I just added the max turnover constraint to your algo about 10 minutes ago and got this error below. Seems like their coding team is busy doing modifications as we speak and I believe is based on my findings, could be wrong though. But at least they should give credit where credit is due. They probably don't want to admit the flaws of the Optimize API that I pointed out, such a shame and very unprofessional if this is what is happening. Could be wrong again though but why all of the sudden, the Infeasible Constraints Error start to magically work? Take care, my friend.

InfeasibleConstraints: The attempted optimization failed because no portfolio could be found that
satisfied all required constraints.
The following special portfolios were spot checked and found to be in violation
of at least one constraint:
Target Portfolio (as provided to TargetWeights):
Would violate FactorExposure(['beta']) because:
Exposure to 'beta' (0.100325202617) would be greater than max exposure (-0.3).
Would violate MaxTurnover(0.35) because:
Portfolio turnover (-2.91433543964e-16) would be greater than max turnover (0.35).
Would violate RiskModelExposure() because:
Exposure to 'momentum' (0.38133512463) would be greater than max exposure (0.36).
Exposure to 'short_term_reversal' (0.502836067118) would be greater than max exposure (0.36).
Current Portfolio (at the time of the optimization):
Would violate FactorExposure(['beta']) because:
Exposure to 'beta' (0.0) would be greater than max exposure (-0.3).
Empty Portfolio (no positions):
Would violate FactorExposure(['beta']) because:
Exposure to 'beta' (0.0) would be greater than max exposure (-0.3).
There was a runtime error on line 182.

Quantopian has always been a work-in-progress (perpetually in "beta"). I will give them credit, though, for continuously charging down the field toward the goal line. Managing changes vis-a-vis their user base is not their strong suit, however. Hiccups are the norm.

Yeah right, Grant. I have started threads that point out these flaws and have met nothing but strong resistance from the Q team and other community members, probably dismissing me as a "crazie" from another planet. What they don't know is this is what I do for a living, trading systems development and financial consulting. Yes, its a perpetual beta but listen, learn and acknowledge coz it's the right thing to do.

James -

Given your profession, you could take it as a "homework problem" to devise a solution using the many tools on Quantopian. The only limitation, potentially, would be if the solution requires some fancy high-performance computing, but you could still get a sense for how to crack the nut. My read is that Quantopian has a long prioritized list of problems to work on; a robust forward-looking "beta nullifier" is probably not at the top of the list (and they may have some sense that it is not a trivial problem, and so wouldn't want to devote their rock star technical talent to study it). Having hyper-focus as a start-up is a good thing, I would think.

Grant,

So here's my story. I ran into Quantopian years ago when it was still on its inception stage. At that time, I thought the idea of crowd sourcing talent was a brilliant idea but since nothing substantial was happening yet, I forgot about it. Few years later, I bumped into Delaney at the New York Trading Show, had a short chat about what I do and how it can fit into Q model. He referred me to Justin Lent via email and had some email correspondence. Cut long story short, since I specialize in AI flavored market timing models it doesn't fit Q's market neutral model, so nothing come out of it. Fast forward to September 2017, I stumbled upon Q's website again and was quite suprised at how it's grown and gotten investor funding and what intrigued me was the contest. So I joined Contest 34 in October with a non AI algo and something the fits its framework just for the heck of it. Didn't think much of it until my entry started making it to the top 10. So out of curiosity, I wanted to find out the performance of the algos in front of me and to my suprise, I see algos churning returns of 1-2% with low volatilities and drawdowns on top of the leaderboard. Someone with my background would easily spot that there is something wrong with the scoring system. So I dug into it and true enough the error in the calculation of Sharpe and Sortino ratios are the culprits and took it to the community forum for anyone who would care to listen. Upon further digging of the system, I found more flaws and started two threads to highlight them. Needless to say, I am quite disappointed and unmotivated to continue given the reception I got from the Q team. Now a couple months of this journey is a total waste of my time on something I normally get paid for to do.

James -

Thanks for the background. As far as Quantopian goes, it is not clear who is running the show there, after the departure of Jonathan Larkin. One would think they are looking for a new Chief Investment officer with industry experience, who would also provide leadership on simple things like applying the industry-standard Sharpe ratio, and other things you've touched on. In terms of the of the architecture of the system they are implementing, I gather that Jonathan was instrumental; it is finally all coming together.

You might have better luck contacting Q directly. I'd suggest reaching out via e-mail to Dan Dunn--to me, he's always been responsive and fair-minded. Maybe he'd provide some background via a private channel, versus this open forum.

@Grant, took @James' last algo above. Made no change except for the start and end dates.

A way to estimate if a strategy might do well going forward is to feed it its past. It should be expected that it could do well there too.

The attached algo is for the two years prior to @James' test. Will let you draw your own conclusions.

Clone Algorithm
2
Loading...
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
from quantopian.algorithm import attach_pipeline, pipeline_output, order_optimal_portfolio
from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import Latest, SimpleBeta, AnnualizedVolatility
from quantopian.pipeline.data import Fundamentals
from quantopian.pipeline.data.builtin import USEquityPricing
import quantopian.optimize as opt
from sklearn import preprocessing
#from quantopian.pipeline.experimental import QTradableStocksUS, risk_loading_pipeline
from quantopian.pipeline.experimental import risk_loading_pipeline
from quantopian.pipeline.filters import QTradableStocksUS
from scipy.stats.mstats import winsorize
import numpy as np
import pandas as pd

# Algo parameters
NUM_TOTAL_POSITIONS = 300
N_LEADING = 10 # days
N_TRAILING = 30 # days
EPSILON = 1.0
WIN_LIMIT = 0

# Optimize API parameters
MAX_GROSS_EXPOSURE = 1.0
MAX_POSITION_SIZE = 0.05 # absolute value
MIN_BETA_EXPOSURE = -0.3
MAX_BETA_EXPOSURE = -0.3

def initialize(context):
    
    # set_commission(commission.PerShare(cost=0, min_trade_cost=0))
    # set_slippage(slippage.FixedSlippage(spread=0))
    set_slippage(slippage.FixedBasisPointsSlippage())
    
    schedule_function(record_out, date_rules.every_day(), time_rules.market_close())
    schedule_function(get_weights, date_rules.every_day(), time_rules.market_open(minutes=60))
    schedule_function(rebalance, date_rules.every_day(), time_rules.market_open(minutes=60))
     
    attach_pipeline(make_pipeline(context), 'my_pipe')
    attach_pipeline(risk_loading_pipeline(), 'risk_loading_pipeline')
    
def make_pipeline(context):
    
    universe = (
        AnnualizedVolatility(mask=QTradableStocksUS())
        .percentile_between(80, 95))
    
    beta = SimpleBeta(target=sid(8554),
                      regression_length=260,
                     )
    
    pipe = Pipeline(columns = {
        'beta':beta,
    },
    screen = universe & beta.notnull())
    
    return pipe
    
def before_trading_start(context,data):
    
    context.pipeline_data = pipeline_output('my_pipe')
    context.stocks = list(pipeline_output('my_pipe').index.values)
    context.risk_loading_pipeline = pipeline_output('risk_loading_pipeline')
  
def record_out(context, data):
            
    num_secs = 0
    
    for stock in context.portfolio.positions.keys():
        if context.portfolio.positions[stock].amount > 0:
            num_secs += 1
            
    record(num_secs = num_secs)
    record(leverage = context.account.leverage)

def get_weights(context,data):
    
    prices = data.history(context.stocks, 'price', 390*N_TRAILING, '1m').dropna(axis=1)
    context.stocks = list(prices.columns.values)
    prices = prices.ewm(ignore_na=False,min_periods=0,adjust=True,com=78).mean()
      
    m = len(context.stocks)
    
    b_t = np.zeros(m)
    
    for i, stock in enumerate(context.stocks):
        b_t[i] = abs(context.portfolio.positions[stock].amount*data.current(stock,'price'))
    
    denom = np.sum(np.absolute(b_t))
    
    # test for divide-by-zero case
    if denom > 0:
        b_t = b_t/denom
    else:
        b_t = 1.0*np.ones(m)/m
      
    a = np.zeros(m)
    b = np.zeros(m)
        
    for n in range(5*N_LEADING,5*N_TRAILING+1):
        p = prices.tail(n*78).as_matrix(context.stocks)
        p_mean = np.mean(p,axis=0)
        
        p_rel = p_mean/p[-1,:]
        p_rel[p_rel<1] = 0
        a += preprocess(get_opt(p_rel,np.sign(p_rel)*b_t))
        
        p_rel = p[-1,:]/p_mean
        p_rel[p_rel<1] = 0
        b += preprocess(get_opt(p_rel,np.sign(p_rel)*b_t))
        
    a = preprocess(a)
    a[a<0] = 0
    b = preprocess(b)
    b[b<0] = 0
                
    context.weights = pd.Series(preprocess(a-b),index=context.stocks)
    
    context.weights = context.weights - context.weights.mean()
    context.weights = context.weights/context.weights.abs().sum()
    
    close_list = {}
    for stock in context.portfolio.positions.keys():
        if stock not in context.stocks:
            close_list[stock] = 0
    
    context.weights = context.weights.append(pd.Series(close_list))
    
def get_opt(x_tilde,b_t):

    x_bar = x_tilde.mean()

    # Calculate terms for lambda (lam)
    dot_prod = np.dot(b_t, x_tilde)
    num = EPSILON - dot_prod
    denom = (np.linalg.norm((x_tilde-x_bar)))**2

    # test for divide-by-zero case
    if denom == 0.0:
        lam = 0 # no portolio update
    else:     
        lam = max(0, num/denom)
                
    b = b_t + lam*(x_tilde-x_bar)

    b_norm = simplex_projection(b)
    
    return b_norm*np.dot(b_norm,x_tilde)

def rebalance(context, data):
    
    pipeline_data = context.pipeline_data
    
    objective = opt.TargetWeights(context.weights)
    
    constraints = []
    constraints.append(opt.MaxGrossExposure(MAX_GROSS_EXPOSURE))   
    constraints.append(opt.DollarNeutral())
    
    beta_neutral = opt.FactorExposure(
        loadings=pipeline_data[['beta']],
        min_exposures={'beta':MIN_BETA_EXPOSURE},
        max_exposures={'beta':MAX_BETA_EXPOSURE}
        )
    constraints.append(beta_neutral)
    
    constraints.append(
        opt.PositionConcentration.with_equal_bounds(
            min=-MAX_POSITION_SIZE,
            max=MAX_POSITION_SIZE
        ))
    
    risk_model_exposure = opt.experimental.RiskModelExposure(
        context.risk_loading_pipeline,
        version=opt.Newest,
    )
    constraints.append(risk_model_exposure)
    
    order_optimal_portfolio(
        objective=objective,
        constraints=constraints,
        )
         
def simplex_projection(v, b=1):
    """Projection vectors to the simplex domain

Implemented according to the paper: Efficient projections onto the
l1-ball for learning in high dimensions, John Duchi, et al. ICML 2008.
Implementation Time: 2011 June 17 by [email protected] AT pmail.ntu.edu.sg
Optimization Problem: min_{w}\| w - v \|_{2}^{2}
s.t. sum_{i=1}^{m}=z, w_{i}\geq 0

Input: A vector v \in R^{m}, and a scalar z > 0 (default=1)
Output: Projection vector w

:Example:
>>> proj = simplex_projection([.4 ,.3, -.4, .5])
>>> print proj
array([ 0.33333333, 0.23333333, 0. , 0.43333333])
>>> print proj.sum()
1.0

Original matlab implementation: John Duchi ([email protected])
Python-port: Copyright 2012 by Thomas Wiecki ([email protected]).
"""

    v = np.asarray(v)
    p = len(v)

    # Sort v into u in descending order
    v = (v > 0) * v
    u = np.sort(v)[::-1]
    sv = np.cumsum(u)

    rho = np.where(u > (sv - b) / np.arange(1, p+1))[0][-1]
    theta = np.max([0, (sv[rho] - b) / (rho+1)])
    w = (v - theta)
    w[w<0] = 0
    return w

def preprocess(a):
    
    a = np.nan_to_num(a - np.nanmean(a))
    a = winsorize(a,limits=(WIN_LIMIT,WIN_LIMIT))
    a = a/np.sum(np.absolute(a))
    
    return preprocessing.scale(a)
There was a runtime error.

@Grant,

You're spot on regarding the leadership that Jonathan Larkin, an industry veteran, provided. Without guidance from an industry veteran, all you have are a bunch of Phds with very good math skills that read hundred of books about trading and skilled programmers that badly needs direction. Don't know much about CEO Fawcett's background but I could imagine his focus now is on capital raising at this stage, investor relations and the formation of his hedge fund. If they want to interview me for CIO position, they know where to reach me :) I have very good ideas on how to take Q to the next level, the AI space where companies like Google, Amazon, Microsoft and other tech companies are heavily invested. This is the new frontier.

Yes, I've communicated with Dan Dunn via private email before, really nice level headed guy. But I know he is reading this, so he knows how to reach out to me.

@ Guy -

Yes, that's pretty ugly. Still a work in progress. I definitely agree with the idea of running longer backtests. Part of the reason I post algos is so I can get criticism and not be deluded. Usually, folks make suggestions for improvements, as well. I don't subscribe to the idea that I have some "secret sauce"--it's a big 24/7/365 global industry. I'm sure I'm on a well-worn path. Not really any downside for me to share.

@ James -

The ML/AI path for Q is murky at this point. I gather that they can't afford to offer it up to the world for free, but I also have to think that they must be planning to do something in this domain. I've asked, but they tend to keep their plans close to the chest.

@James : I also noticed the changes to QTradableStocksUS yesterday, and it was a bit negative for my algo too. I guess it's an unexpected good test for overfitting... ;) I know it was tagged as experimental, but I agree that it would have been nice to have some kind of update about it in the forums. Contest 38 uses QTradableStocksUS too, so I wonder if all the backtests will have to be re-run.

Another thing I noticed is that all my live (non-contest) algos have been updated with the latest commission/slippage models (in the form of big dips at the beginning of the week).

@Charles, et al,
Indeed, we have made improvements to the QTradableStocksUS and it has migrated from experimental. A more detailed announcement will come on Monday as we've been deploying each of the relevant parts associated to the change.

Sorry for any confusion this has caused!

Thanks
Josh

Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

@James Villa -- well, they are changing the contest format after a month or so, so lets hope they've taken some criticism to heart. The Q platform has advanced by leaps and bounds in just the past two years. It's absolutely staggering.

I agree the current contest scoring is crap. Lots of algos at the top of the leaderboards have terrible backtests, which is a big red flag. At the very least they should have been taking the worst score between paper trade and backtest as the final score... so an entry with 4.0 sharpe paper trade and 0.4 sharpe backtest should be treated as 0.4 cumulative. In addition, I don't know why they don't eliminate/penalize algos that don't outperform interest rates. Seems like that should be obvious. Low annual returns combined with leverage (which I understand is used by fund investors, up to 5x) seems like a sure-fire way to lose money.

Finally, I don't understand why they don't launch more than one fund. That would allow for different criteria/strategies. So in addition to their current risk-neutral hedge fund-style paradigm, they could have another fund that's optimized for total return with more relaxed hedging criteria, etc. There's a market for different kinds of product -- as evidenced by the wide variety of funds on the market with huge AUMs -- and Q are pretty much already just sitting on the product (wide variety algos+coders). Or they could launch an ETF even. Look at the recent success of AIEQ. Or launch a CEF, which would be a more manageable option. There's a lot they could do to enthuse the community -- #1 of which would be to let more of the community have successes. (I guess I can answer my own question -- that given a difficult puzzle and a very difficult puzzle, they anticipate algo coders will opt to focus on the former.)

I wonder if Q are implementing AI on their side of the order optimizer.

Hi Josh -

Thanks for the note. I guess it is to be expected that anything with the term experimental is its description should be expected to change without notice--fair enough. Having the experimental sandbox would seem to be a sound engineering practice. Users are basically knowingly signing up to be beta testers, although given that we have this new-fangled thing called the Internet, they could be notified of pending changes ahead of time very easily.

On a broader note, I see that you are a VP and "Product Manager at Quantopian. I focus on improving the platform." (per the About page and your personal page, respectively). The idea of a Quantopian "product" is kinda murky. Your "product" is the 1337 Street Fund, as I understand (although from the perspective of those who have equity in Quantopian, the "product" may be the overall valuation of the enterprise--if they were to sell it, how much would they get). In any case, it sounds like your role is to improve the "product" from a user perspective. If so, my read is that you are missing opportunities. Part of the problem, it seems, is not having a systematic way of capturing inputs and then prioritizing and managing them. For example, I think it would be very easy to deploy a Quantopian bug & feature request tracker on Github. As a specific case, Thomas W. graciously picked up the topic of the applicability of the risk-free rate on Quantopian, let everyone vent their spleens, and then apparently dropped it, without really any explanation. One way to deal with this sort of thing would be to create a feature request in Github (perhaps with a priority ranking and associated justification). Then you don't end up with folks screaming at the top of their lungs feeling like they are being ignored (although in the case of the misleading Quantopian "Sharpe Ratio" the fix would seem to be so simple, it is baffling to understand why it would not be applied pronto...but as I mentioned, if a reasonable justification is given for the priority level, then calm rationality should prevail).

By the way, will the code for generating the QTradableStocksUS be made available? Presumably, it is just a bunch of Pipeline code that uses daily OHLCV bars and Morningstar data and could be put out on Github (or perhaps within the research platform, code for certain modules could be written out as one can do in MATLAB for many functions). Or are there proprietary elements that you don't want to share?

@Charles Piche,

Since we both experienced a negative impact on our algo's performance due to this transition of QTU from experimental to API release, I'd like to ask you if you are also experiencing your algo hanging or stuck in a particular date? This I believe is a direct result of removing stocks in the M&A target announcement list. More on this here: QTU

@Viridian Hawk,

I share the same sentiments as you do but I think Q believes that they are doing or implementing things correctly already and for some reason are indifferent to some of the user feedbacks, oh well . I also highly doubt if Q is implementing AI on their side of the order optimizer.

Meh. This has been the Q M.O. for a long time. I've had extensive conversations with the Q powers-that-be, via e-mail, and it is-what-it-is. My read is they don't have a defined process for managing changes that properly incorporates user evaluation and feedback. Until this is in place, and championed by management, we'll continue to see this sort of thing. In this case, my guess is that they rushed it to find the problems before the official start of the next contest. I had a contest algo crash first-thing Monday morning, which undoubtedly was due to the change (and I saw the impact this past weekend on backtests). No biggy...and I appreciate that they are trying to stick to a timeline with limited resources. Planning a time period for user evaluation and comment would have been nice, though.

@James No hanging here.