This tutorial will:
  • Give an overview of the Quantopian Contest and how it works (lesson 1).
  • Walk through each of the criteria that are required for contest algorithms (lessons 2-10).
  • Provide guidance on how to write or modify an algorithm to satisfy the contest criteria (lessons 2-10).
  • Show you how to test whether or not your algorithm meets the contest criteria (lesson 11).
This tutorial will not:
  • Teach you how to research and implement a strategy on Quantopian (for this, you should reference the Getting Started tutorial).
Contest Overview
The Quantopian Contest is a competition that awards daily cash prizes to the top quantitative trading algorithms written on Quantopian. The contest is designed to evaluate cross-sectional, long-short equity strategies. As such, contest algorithms are required to have certain structural properties. This tutorial will give a brief overview of how the contest works, and then walk through each of the required structural properties, offering guidance on how to meet these requirements.
Criteria
In order to participate in the contest, algorithms need to meet a special set of criteria. These criteria select for cross-sectional, long-short equity strategies and are aligned with our allocation process. Algorithms are required to have the following properties in order to participate in the contest:
  • Positive returns.
  • Leverage between 0.8x-1.1x.
  • Low position concentration.
  • Low beta-to-SPY.
  • Mid-range turnover.
  • Long and short positions.
  • Trades liquid stocks.
  • Low exposure to sector risk.
  • Low exposure to style risk.
  • Place orders with Optimize API.
See the official rules of the contest for all the details on the required criteria. Lesson 11 of this tutorial has a notebook that you can use to see if your backtest passes all of the contest criteria.
The contest criteria are checked over a 2 year backtest and are re-checked every day that your algorithm remains in the contest. If your algorithm fails one or more of the criteria at any point, it will be withdrawn from the contest. If your algorithm is withdrawn, you can modify it such that it meets all the criteria and resubmit it.
Each criterion will be discussed and explained in more detail later in this tutorial. Each lesson focuses on one of the requirements and offers suggestions on what you can do to write an algorithm that meets each criterion.
Submitting an Algorithm
To enter the contest, you need to write an algorithm, run a full backtest, and then click ‘Enter Contest’. After entering the contest, your algorithm will be backtested from 2 years ago up to the most recent trading day. The contest criteria will be tested over this 2 year period. If your algorithm passes all the criteria it will be scored and ranked after the next trading day, and appear on the leaderboard. If your algorithm fails any of the criteria, you will receive an email telling you that your entry was withdrawn along with the specific criteria that failed.
Scoring & Ranking
Contest algorithms that meet all the structural criteria are ranked after every trading day based on a score. The score of an algorithm is based on its out-of-sample returns and volatility. Algorithms are rewarded a high score for achieving high returns and low volatility.
Each day after an algorithm has been submitted, the daily return of the algorithm is divided by the trailing 63-trading-day volatility to compute a volatility adjusted daily return (VADR). The score of the algorithm is the sum of the algorithm’s VADRs since the algorithm was submitted to the contest.
At the end of each trading day, participants are ranked by their highest scoring submission. The top 10 participants each day are awarded a cash prize.
In this lesson, we overviewed the contest, we learned a bit about the structural criteria for contest algorithms, and we learned how algorithms are scored. The remaining lessons in this tutorial walk through the contest criteria and help you to write an algorithm that is eligible for the contest.
Order with Optimize API
What Is the Requirement?
Your algorithm is required to place all of its orders with the order_optimal_portfolio function. This is the core function of the Optimize API and the best way to move a portfolio from one state to another on Quantopian. Other ordering methods in the Quantopian API are not allowed in the contest.
Why Is It Required?
The order_optimal_portfolio function is the only ordering function supported in our internal live trading infrastructure. Everyone who receives an allocation must update that algorithm to use the Optimize API. We're steering the community to use the Optimize API at the very beginning of the process rather than stubbing it in at the end. This will make the simulations an even better predictor of the as-traded performance.
How Can You Satisfy This Requirement?
Lesson 7 of the Getting Started tutorial teaches you how to use order_optimal_portfolio to place orders. Reading through that lesson is the best way to learn the basics of order_optimal_portfolio. To meet this requirement, you need to use order_optimal_portfolio to move your portfolio from one state to another. Here are a few example algorithms that use order_optimal_portfolio:
For a more detailed overview of order_optimal_portfolio and the Optimize API, check out this research tutorial notebook or the help documentation.
Liquid Universe
What Is the Requirement?
Your algorithm must only invest in stocks in the QTradableStocksUS universe (QTU, for short).
Why Is It Required?
Your strategy's universe is its foundation. You want a universe to be as large as possible, but contain only assets that match the platform capabilities. For Quantopian, that means assets for which we can find data reliably, that fit our risk management, that we can use in a multi-algorithm portfolio, and that we can trade at a reasonable transaction cost. The QTradableStocksUS is the largest universe that satisfies these requirements on Quantopian. In addition, all of the constituents of the QTradableStocksUS are covered by the risk model.
How Can You Satisfy This Requirement?
Use the QTradableStocksUS pipeline filter as your base universe. Often, this can be done by simply setting screen=QTradableStocksUS in your pipeline (if you are unfamiliar with Pipeline, check out the Pipeline tutorial). If you compute your asset weights/expected alpha outside of pipeline, you might need to check whether or not the stocks you want to order are in the QTU before you pass them to order_optimal_portfolio. There is some leniency built into the contest requirement - contest algorithms are technically required to have at least 95% of their capital invested in stocks in the QTU, so you have some time to adjust your portfolio if a stock held by your algorithm falls out of the QTU.
Here is an example of a basic pipeline that provides the QTradableStocksUS as a screen:
from quantopian.pipeline import Pipeline
from quantopian.pipeline.experimental import QTradableStocksUS

def make_pipeline():

    return Pipeline(
        columns={
            # Your pipeline columns go here.
        },
        screen=QTradableStocksUS()
    )
Note that there are multiple ways to use a pipeline filter like the QTradableStocksUS. Sometimes its appropriate to supply it as a screen and sometimes you may need to supply it as a mask to another pipeline term. To learn more about filters and pipeline, check out the Pipeline tutorial.
Here are some example algorithms that trade in the QTradableStocksUS:
Position Concentration
What Is the Requirement?
Your algorithm cannot invest more than 5% of its capital base in any one asset.
Why Is It Required?
Investing too much in any one asset exposes an algorithm to position risk. If a big chunk of your capital is invested in an asset, and the price of that asset changes significantly, your the value of your portfolio will also have a significant change. The risk of a large drawdown can be mitigated by making several smaller investments in a larger number of assets.
How Can You Satisfy This Requirement?
Include a PositionConcentration constraint when you call order_optimal_portfolio. For example, you can define a position concentration constraint of 1% like this:
import quantopian.algorithm as algo
import quantopian.optimize as opt

MAX_SHORT_POSITION_SIZE = 0.01  # 1%
MAX_LONG_POSITION_SIZE = 0.01   # 1%

# Define the position concentration constraint.
constrain_pos_size = opt.PositionConcentration.with_equal_bounds(
    -MAX_SHORT_POSITION_SIZE,
    MAX_LONG_POSITION_SIZE,
)

# Supply the constraint to order_optimal_portfolio.
algo.order_optimal_portfolio(
    objective=my_objective, #Fill in with your objective function.
    constraints=[
        constrain_pos_size,
    ],
)
Even though the position concentration limit is 5%, it is a good idea to set a lower limit in your algorithm. Price movements can cause your algorithm to temporarily hold a position that exceeds the limits that you specify. By leaving a cushion, you greatly reduce the odds of exceeding the 5% contest limit (and your position risk!).
Here are some other examples that use a PositionConcentration constraint:
Long/Short
What Is the Requirement?
Your algorithm cannot have more than 10% net dollar exposure. This means that the long and short books of a contest algorithm can not be more than 10% different (measured as 10% of the total capital base). If your algorithm has 100% of its capital invested, neither the long investments nor the short investments must exceed 55% of that investment.
Why Is It Required?
Cross-sectional, long-short equity strategies are designed to bet on the expected value of assets relative to other assets in the universe. If stock A is expected to go up 10% and stock B is expected to go up 5%, a cross-sectional strategy might open a long position in stock A and a short position in stock B, betting on the fact that the price of stock A will increase more than the price of stock B. As a result, cross-sectional, long-short equity strategies should not be biased in either the long or short direction. Since the contest is selecting for these types of strategies, the long/short balance is required.
To learn more about cross-sectional, long-short equity strategies, check out this lecture .
How Can You Satisfy This Requirement?
Include a DollarNeutral constraint when you call order_optimal_portfolio. Here is an example of what a DollarNeutral constraint might look like:
import quantopian.algorithm as algo
import quantopian.optimize as opt

# Define the dollar neutral constraint.
dollar_neutral = opt.DollarNeutral()

# Supply the constraint to order_optimal_portfolio.
algo.order_optimal_portfolio(
    objective=my_objective, #Fill in with your objective function.
    constraints=[
        dollar_neutral,
    ],
)
Even with the DollarNeutral constraint, your long and short portfolios might vary in size a bit. This is caused by the price fluctuations of the portfolio you hold. For example, if all of your long positions go up in value while your shorts stay at the same price, your long book will be greater in value. As long as you have low position concentration (your investment is spread across many assets), and you rebalance frequently (once every 2 weeks or more frequently), the DollarNeutral constraint should keep you under the 10% net exposure limit of the contest.
Here are some other examples that use a DollarNeutral constraint:
Turnover
What Is the Requirement?
Your algorithm must have a mean daily turnover between 5%-65%, measured over a 63-trading-day rolling window. Turnover ranges from 0-200% (200% means you completely moved your capital from one set of assets to another). This means that positions in a contest algorithm are held for an average of ~3-40 days.
Why Is It Required?
The lower bound of 5% exists because algorithms that place trades (“bets”) less frequently need more time for their performance to be evaluated. The more frequently an algorithm trades, the sooner an algorithm accumulates a larger number of out-of-sample trades. The lower bound on turnover requires algorithms to make active decisions.
Currently, algorithms that hold positions for less than ~2-3 days on average are not supported by the risk model. The risk model can't accurately assess algorithms with relatively large volume of intraday trading. Also, high turnover leads to higher transaction costs so it is generally a good idea to avoid high turnover.
How Can You Satisfy This Requirement?
There are two components of an algorithm that determine its turnover: the volatility of your alpha signal and the trade frequency.
You should determine the volatility of your alpha signal during the research phase of your work using Alphalens. For the contest, you should target a factor with a ~3-40 day prediction horizon .
Once you have a signal with the appropriate prediction horizon, you can further control your turnover with your algorithm’s rebalance schedule. Contest algorithms should use schedule_function to trade at a daily or weekly frequency. The exact trade frequency of your algorithm should depend on your anticipated holding period. You should experiment with different trading frequencies and analyze the turnover of your backtests with Pyfolio. As an example, you can schedule a function my_rebalance to run daily with the following code:
def initialize(context):
    # Schedule our reblance function to run every day, 45 minutes after
    # market open.
    schedule_function(
        func=my_rebalance,  
        date_rule=date_rules.every_day(),  
        time_rule=time_rules.market_open(hours=0,minutes=45)
    )

def my_rebalance(context, data):
    # Ordering/rebalance logic goes here.
Here are some examples that meet the turnover constraint:
Leverage
What Is the Requirement?
Your algorithm must maintain between 0.8x-1.1x gross leverage, checked at the end of each day. This means that your algorithm needs to have between between 80% to 110% of your notional capital invested at all times. For example, with a $10M capital base, having $5M invested in long positions and $5M invested in short positions would come out to 1x leverage (100% invested) and would satisfy the requirement for the contest.
Why Is It Required?
The upper bound on leverage is in place as a normalization procedure so that contest algorithms are evaluated on the same scale of invested capital. The lower bound is in place so that contest algorithms always stay invested in US equities instead of holding cash for periods of time. This is consistent with our allocation process.
In addition, leverage is checked at the end of the day meaning an algorithm has to hold its positions overnight. This is required because the current version of the Quantopian risk model is designed to measure risk exposures on positions that are held for at least one market close to market close period.
How Can You Satisfy This Requirement?
To control the maximum leverage of your algorithm, you should provide a MaxGrossExposure constraint when you call order_optimal_portfolio. Here is an example that calls order_optimal_portfolio with a MaxGrossExposure constraint to keep the portfolio at 1x leverage:
import quantopian.algorithm as algo
import quantopian.optimize as opt

MAX_GROSS_LEVERAGE = 1.0  # 1x

# Define the max leverage constraint.
constrain_gross_leverage = opt.MaxGrossExposure(MAX_GROSS_LEVERAGE)

# Supply the constraint to order_optimal_portfolio.
algo.order_optimal_portfolio(
    objective=my_objective, #Fill in with your objective function.
    constraints=[
        constrain_gross_leverage,
    ],
)
Even with the MaxGrossExposure constraint, your algorithm’s portfolio might exceed 1x leverage. There are a few reasons for this. The context maximum of 1.1x leverage is designed to give a buffer so that algorithms can invest 1x without being disqualified for exceeding 1x on occasion. The leverage fluctuations can be further mitigated by holding a large number of assets.
To meet the minimum leverage requirement, you have to provide an objective function with a large set of stocks to order_optimal_portfolio. Unlike the upper limit on leverage, the lower limit can’t be controlled with a constraint. Passing an objective function to order_optimal_portfolio that covers a large set of assets (100 or more in most cases) will give the Optimize API an opportunity to find a portfolio that meets all of the supplied constraints while investing all of its capital.
Here are some example that meet the leverage criteria:
Beta-to-SPY
What Is the Requirement?
Your algorithm must not be correlated to the market. Specifically, your algorithm must have an absolute beta-to-SPY below 0.3.
Why Is It Required?
An algorithm needs to have low exposure to market risk in order for it to be eligible for allocation consideration. If your algorithm is correlated with the rest of the stock market, the algorithm isn't providing any quality different from buying a market index. As such, the contest requires algorithms to be market neutral.
How Can You Satisfy This Requirement?
To write a market neutral strategy, you need to make sure your alpha signal is not correlated to the market. By definition, alpha factors should move independently of the market as a whole. If your alpha signal is market neutral, your algorithm should just be able to use a MaximizeAlpha objective with a vector of weights decided by your alpha signal.
Another way to control the beta-to-SPY of an algorithm is by supplying a FactorExposure constraint to order_optimal_portfolio using historical per-asset beta-to-SPY. However, this approach operates on an important assumption that combining the historical beta-to-SPY of each asset in our portfolio is a good predictor of the beta-to-SPY of our portfolio return tomorrow. This isn't necessarily true of all algorithms, so it might not work well for particular strategies. Here is an example of a FactorExposure using historical per-asset beta-to-SPY values:
import quantopian.algorithm as algo
import quantopian.optimize as opt

from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import SimpleBeta

def initialize(context):
    # Define the beta-to-SPY factor in Pipeline.
    beta = SimpleBeta(
                    target=sid(8554),
                    regression_length=260,
                    )

    pipe = Pipeline(
        columns={
            'beta': beta,
        },
        screen=beta.notnull(),
    )
    algo.attach_pipeline(pipe, 'pipe')

def before_trading_start(context, data):
    # Get the pipeline data every day.
    context.pipeline_data = algo.pipeline_output('pipe')

def do_portfolio_construction(context, data):
    pipeline_data = context.pipeline_data

    # Define the beta constraint to be +/- 0.05 beta-to-SPY based 
    # on historical per-asset beta.
    beta_neutral = opt.FactorExposure(
        pipeline_data[['beta']],
        min_exposures={'beta': -0.05},
        max_exposures={'beta': 0.05},
    )

    # Supply the constraint to order_optimal_portfolio.
    algo.order_optimal_portfolio(
        objective=my_objective, #Fill in with your objective function.
        constraints=[
            beta_neutral,
        ],
    )
Here are some beta-neutral examples:
Risk Model Exposure
What Is the Requirement?
Your algorithm must be less than 20% exposed to each sector as defined in the Quantopian risk model. There are 11 sectors in the risk model:
  • Basic Materials
  • Technology
  • Real Estate
  • Energy
  • Communication Services
  • Health Care
  • Financial Services
  • Consumer Cyclical
  • Industrials
  • Consumer Defensive
  • Utilities
Your algorithm must also be less than 40% exposed to each style factor in the Quantopian risk model. There are 5 style factors in the risk model:
  • Size
  • Value
  • Momentum
  • Short-Term Reversal
  • Volatility
Exposure to risk factors in the Quantopian risk model is measured as the mean net exposure over a 63-trading-day rolling window. Each day, the rolling mean net exposure to each sector must be between -20% and 20%, while the rolling mean net exposure to each style factor must be between -40% and 40%. If your algorithm is overexposed to any of the sector or style factors, it won’t be eligible to run in the contest.
Why Is It Required?
The definition of a risk model codifies a particular view of the market. The Quantopian Risk Model is designed to identify the particular risk exposures that are desired by Quantopian’s investor clients. One of the goals of the contest is to incentivize the construction of algorithms that could be considered for an allocation. Keeping low exposure to the factors in the risk model aligns your algorithm with Quantopian's allocation process.
How Can You Satisfy This Requirement?
There are two ways to limit your exposure to the terms in the Quantopian Risk Model. The first way is to construct an alpha factor that is independent of any of the sector or style factors. This is typically done in the research step when analyzing the properties of your alpha factor.
The other way to limit your exposure to the risk factors in the Quantopian Risk Model is by supplying constraints to order_optimal_portfolio. You can use the risk_loading_pipeline to get historical risk factor exposures for each asset. Then you can constrain your portfolio based on these historical exposures by supplying a RiskModelExposure constraint to order_optimal_portfolio.
Here’s an example algorithm that constrains the portfolio to the default exposure limits (18% for each sector, 36% for each style):
import quantopian.algorithm as algo
import quantopian.optimize as opt

from quantopian.pipeline.experimental import risk_loading_pipeline  

def initialize(context):
    # Attach the risk loading pipeline to our algorithm.
    algo.attach_pipeline(risk_loading_pipeline(), 'risk_loading_pipeline')

def before_trading_start(context, data):
    # Get the risk loading data every day.
    context.risk_loading_pipeline = pipeline_output('risk_loading_pipeline')

def place_orders(context, data):  
    # Constrain our risk exposures. We're using version 0 of the default bounds
    # which constrain our portfolio to 18% exposure to each sector and 36% to
    # each style factor.
    constrain_sector_style_risk = opt.experimental.RiskModelExposure(  
        risk_model_loadings=context.risk_loading_pipeline,  
        version=0,
    )

    # Supply the constraint to order_optimal_portfolio.
    algo.order_optimal_portfolio(  
        objective=my_objective,  #Fill in with your objective function.
        constraints=[constrain_sector_style_risk],  
    )
The default exposures are constrained to 18% for each sector, and 36% for each style. These constraints are below the respective limits of 20% and 40% because we are using historical exposures to constrain our portfolio for the next day. Tomorrow’s exposures can vary from their past exposures, so the buffer leaves you with a bit of room to stay under the contest limits.
Some of the risk exposures are more stable than others. For example, the per-asset sector exposures don’t tend to change as much over time as some of the style factors. The short-term reversal risk factor tends to change the most since it is computed over a 14 day trailing window. If you are having trouble controlling your exposure to this factor, you can try overriding the default constraint of 36% historical exposure. To do this, you can modify the constraint you pass to order_optimal_portfolio to look like this:
constrain_sector_style_risk = opt.experimental.RiskModelExposure(  
    risk_model_loadings=context.risk_loading_pipeline,  
    version=0,  
    min_momentum=-0.1,  
    max_momentum=0.1,  
)
Finally, you can set the version of your RiskModelExposure to be a specific version (version=0 above), or you can set it to version=opt.Newest so that your algorithm always uses the newest version of the defaults. For the contest, you should pick a specific version to use since you don’t know how your algorithm will be affected by a change to the default.
Here are some example that meet the risk exposure criteria:
Positive Returns
What Is the Requirement?
Your algorithm must have positive returns. The returns are checked on a daily basis starting on the day that you submit to the contest. The returns are measured from 2 years before you submit to the contest, up to the most recent trading day. The returns of the algorithm need to be above 0 in order for an algorithm to stay active in the contest.
Why Is It Required?
Quantopian is looking to provide allocations to algorithms that consistently make money.
How Can You Satisfy This Requirement?
The best way to write a strategy that makes money is to have an alpha factor that holds up out-of-sample. If you’re having a hard time finding such a factor, you should try looking through the Community Forums or the Lecture Series for ideas, or researching some of the alternative data that’s available on Quantopian. After that, follow the process outlined in the Getting Started tutorial to turn your idea into an algorithm using Alphalens and Pyfolio along the way.
Testing If Your Algorithm Meets the Criteria
Before submitting your algorithm to the contest, you can test whether or not it meets the criteria. To do this, run a full backtest from 2 years ago up to the most recent trading day. When your backtest has finished, copy the backtest ID from the URL. The URL has the following structure:
https://www.quantopian.com/algorithms/ALGORITHM_ID/BACKTEST_ID
Then, clone this notebook and copy your backtest ID into the get_backtest function in the first cell. Finally, run all the cells in the notebook and look for the report at the bottom. It should look something like this:
Contest report card
If all of the checks report PASS, then you can submit your algorithm to the contest.
Happy coding!
NEXT

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.