Back to Community
discuss the sample algorithm

EDIT: This algorithm is obsolete. I recommend that you use the algorithm in the Getting Started tutorial instead.

I encourage you to press the "Clone Algorithm" button below. That will copy the code into your "My Algorithms" page. You can then hack, edit, tweak, modify, and change it as you see fit. A couple easy changes you can try:

  • Change the sid to a different stock
  • Change the vwap to be a different number of days

You can see how the backtest results change with even tiny code updates.

This very basic algorithm comes from our help documentation. In the backtest object below, click the "Source Code" tab to see the code. Read the comments in that code carefully, and you'll get a good idea how to write an algorithm in Quantopian.

Also, you might find the Video Tutorial helpful. It walks you through the same programming concepts. Watch it on YouTube.

P.S. This algorithm was last updated on 11/13/12.

Clone Algorithm
10449
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
    # For this example, we're going to write a simple momentum script.  When the 
    # stock goes up quickly, we're going to buy; when it goes down quickly, we're
    # going to sell.  Hopefully we'll ride the waves.

    # To run an algorithm in Quantopian, you need two functions: initialize and 
    # handle_data.

def initialize(context):
    # This initialize function sets any data or variables that you'll use in
    # your algorithm.  For instance, you'll want to define the security (or 
    # securities) you want to backtest.  You'll also want to define any 
    # parameters or values you're going to use.

    # In our example, we're looking at Apple.  If you re-type this line 
    # yourself, you'll see the auto-complete that is available for the 
    # security ID.
    context.aapl = sid(24)
    
    # In these two lines, we set the maximum and minimum we want our algorithm 
    # to go long or short our security.  You don't have to set limits like this
    # when you write an algorithm, but it's good practice.
    context.max_notional = 1000000.1
    context.min_notional = -1000000.0

def handle_data(context, data):
    # This handle_data function is where the real work is done.  Our data is
    # minute-level tick data, and each minute is called a frame.  This function
    # runs on each frame of the data.
    
    # We've built a handful of useful data transforms for you to use.  In this 
    # line, we're computing the volume-weighted-average-price of the security 
    # defined above, in the context.aapl variable.  For this example, we're 
    # specifying a three-day average.
    vwap = data[context.aapl].vwap(3)
    # We need a variable for the current price of the security to compare to
    # the average.
    price = data[context.aapl].price
       
    # Another powerful built-in feature of the Quantopian backtester is the
    # portfolio object.  The portfolio ojbect tracks your positions, cash,
    # cost basis of specific holdings, and more.  In this line, we calculate
    # how long or short our position is at this minute.   
    notional = context.portfolio.positions[context.aapl].amount * price
       
    # This is the meat of the algorithm, placed in this if statement.  If the
    # price of the security is .5% less than the 3-day volume weighted average
    # price AND we haven't reached our maximum short, then we call the order
    # command and sell 100 shares.  Similarly, if the stock is .5% higher than
    # the 3-day average AND we haven't reached our maximum long, then we call
    # the order command and buy 100 shares.         
    if price < vwap * 0.995 and notional > context.min_notional:
        order(context.aapl,-100)
    elif price > vwap * 1.005 and notional < context.max_notional:
        order(context.aapl,+100)
We have migrated this algorithm to work with a new version of the Quantopian API. The code is different than the original version, but the investment rationale of the algorithm has not changed. We've put everything you need to know here on one page.
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.
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.

23 responses

uvxy

svxy

thanks

I have a small question, if you're up 45% with this, why don't you use this to trade in the real world?

Hi Paul,

try cloning the algorithm and play with the following parameters:

  • use a realistic budget
  • use a realistic commission per trade or share
  • trade a different stock
  • trade for a longer timeframe, especially in a time when the APPL stocks didn't skyrocket :)

You will likely notice, that the algorithm will barely outperform the benchmark.

-Chris

Similar strategy with different parameters for trading SPY(SPDR S&P 500 ETF TRUST).

Clone Algorithm
779
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
def initialize(context):
    # spy = SPDR S&P 500 ETF TRUST
    context.spy = sid(8554)
    context.max = 1000000
    context.min = -100000

def handle_data(context, data):
    # data is minute-level bar

    # mavg(30) = moving average price of 30 days 
    mavg = data[context.spy].mavg(30)
    price = data[context.spy].price

    # account value
    notional = context.portfolio.positions[context.spy].amount * price
    
    # momentum trading with limit order
    if price < mavg * 0.94 and notional > context.min:
       order(context.spy, -100, limit_price = price)
    elif price > mavg * 1.06 and notional < context.max:
       order(context.spy, +100, limit_price = price)
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

if you're up 45% with this, why don't you use this to trade in the
real world?

This algorithm suffers from bias - in retrospect, Apple was a great pick. But there's no way for me to know that will be true going forward.

I will say that the commission is realistic, and it works in different budgets, too. But the "apple-went-up-tons" bias make this a terrible idea as an actual production algorithm.

Hi Dan,

Momentum trading is a good idea. It's one of the great huristics. By using S&P 500 ETF(same with the benchmark) and a 11-year backtest, this heuristic is confirmed again.

Di Chen

My sense is that there is a strong bias in a lot of the algorithms cooked up on Quantopian, with backtests that include the 2008-2009 collapse. My own experience is that it is relatively easy to devise an algorithm that avoids the downturn...the problem is that the algorithm either tracks the market or under-performs it under normal conditions. Di Chen's algorithm gains mostly by avoiding the 2008-2009 market drop, but then appears to track the market...so, it is not obvious that it would be a good choice to use for future trading. --Grant

Hi guys, I'm new to this platform and have been playing around with some simple scripts. Is there a way to apply some sort of a tax rate to the dividends received in the portfolio, since dividends are taxed differently from trading or capital gains?

Dominic,

Dividends aren't directly accessible in back-testing (more information here), however they are accounted for in overall returns (except special dividends)

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.

This version has better performance. What I like is 12.3% max drawdown. But it can't beat the benchmark during 2009 - 2013.

Clone Algorithm
779
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
def initialize(context):
    # spy = SPDR S&P 500 ETF TRUST
    context.spy = sid(8554)
    context.max = 1000000
    context.min = -100000
    
def handle_data(context, data):
    # data is daily bar

    # mavg(60) = moving average price of 60 days
    mavg = data[context.spy].mavg(60)
    price = data[context.spy].price

    # position value
    notional = context.portfolio.positions[context.spy].amount * price
    
    # momentum trading with limit order
    if price > mavg * 1.05 and notional < context.max:
       order(context.spy, +1000, limit_price = price)
    elif price < mavg * 0.95 and notional > context.min:
       order(context.spy, -1000, limit_price = price)
    
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

I don't believe the strategy is behaving as you'd expect, as illustrated by its NAV and the Notional it is trading.

Also note that the min notional was changed to -100,000 vs. -1,000,000.

Clone Algorithm
155
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
def initialize(context):
    # spy = SPDR S&P 500 ETF TRUST
    context.spy = sid(8554)
    context.max = 1000000
    context.min = -100000
    
def handle_data(context, data):
    # data is daily bar

    # mavg(60) = moving average price of 60 days
    mavg = data[context.spy].mavg(60)
    price = data[context.spy].price

    # position value
    nav = context.portfolio.portfolio_value
    notional = context.portfolio.positions[context.spy].amount * price
    record(Notional=notional, NAV=nav)
    
    # momentum trading with limit order
    if price > mavg * 1.05 and notional < context.max:
       order(context.spy, +1000, limit_price = price)
    elif price < mavg * 0.95 and notional > context.min:
       order(context.spy, -1000, limit_price = price)
    
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

Going short is much riskier than going long. So I changed the min notional.

hi all, just a thought, anyone thought of making the number of stocks which are short and long variable and can be calculated by a tracker? say if the tracker spots the trend moving up, it will put more stocks in long rather than short

Ivan, do you mind putting this on a new thread by itself?

I am new to this platform, and am trying to understand code. Why do these samples set context.min to a negative number ?

context.min = -100000

If I understand the code correctly

notional = context.portfolio.positions[context.spy].amount * price

both amount and price cant be negative which implies notional is never negative.

I would expect context.min = 0 in these examples.

Hey Sushanth,
Actually, amount can be negative since we can sell shares by shorting them. So in this case, we are effectively borrowing a security to sell with the expectation that the price will fall. It's sort of a weird concept at first.

Gus

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.

Sushanth,
think like if you are very confident that the stock price of a stock will drop soon. So you'll borrow the stock @ Px= 100.00 USD and you pay a set interest on the borrowing (for simplicity let's assume the borrowing rate is irrelevant). As you borrow the position you immediately sell the same position @ Px=100.00 USD in the market.
Let's assume you were right and the price of the stock do drop let's say @ Px = 50.00 USD.
Then, you buy back the stock at Px=50.00 USD and cover your short position (give back your borrowed position). Effectively you have earned 50.00 USD, assuming interest on your borrowing is irrelevant. On the flip side, stock Px can go up, in which case I'll let you figure out the impact :)

Sometimes have to wonder if the buying "on margin" is on purpose, the first algo spends 70 thousand over budget allocated and the last one goes in the hole nearly half a million within two years.
An example of a few lines you could add to your own code to watch out for that if it isn't your intention.

def initialize(context):  
    [ your code ]

    # make at least higher than gui setting  
    context.cashlow = 9999999999999

def handle_data(context, data):  
    [ your code ]

    cash = context.portfolio.cash  
    if cash < context.cashlow:  
        context.cashlow = cash  
        print cash  
        record(cashlow=cash)

2002-01-03 PRINT 1000000.0  
2002-11-26 PRINT 930191.999916  
2002-11-29 PRINT 836181.999884  
2002-12-02 PRINT 742261.999878  
2002-12-03 PRINT 649681.999869  
2002-12-04 PRINT 557286.999866  
2003-05-16 PRINT 465368.339466  
2003-05-19 PRINT 187748.339386  
2003-05-29 PRINT -3051.66063417  
2003-06-06 PRINT -102302.660638  
2003-06-09 PRINT -200444.660646  
2003-06-30 PRINT -298164.660658  
2003-08-05 PRINT -487601.660678

Wondering why there is such a variability in returns solely based off little changes in starting capital. I ran an elementary code (just equity buy orders) with a million dollars and it produced returns not even remotely close to the thousand dollar starting capital I had in another run. Is it the commission thats making the difference?

"with a million dollars and it produced returns not even remotely close to the thousand dollar starting capital I had in another run"

Here is the reason for that. So I use a run summary quite a bit.

Hello everyone. I am new to this and I try this algorithm. After I saw this algorithm's comments, I came across a problem. Suppose the algorithm's cash limit is 1000000 USD and in the algorithm's code we define context.max_notional = 1000000.1.
the code is:

      if price < vwap * 0.995 and notional > context.min_notional:  
              order(context.aapl,-100)  
          elif price > vwap * 1.005 and notional < context.max_notional:  
              order(context.aapl,+100)

Suppose I have cost my cash to buy shares and in the account only remain 1000 USD. If the code "order(context.aapl,+100) " work, and in order to buy 100 shares of aapl , I have to spend 2000 USD. The account cash's limit is 1000000 USD, after the "order(context.aapl,+100)" work, I spent 1001000 USD actually. I think this is a problem. Maybe I am wrong. Can you help me?