Back to Posts
Listen to Thread

I had a request for an algo that was dirt-simple. Buy a basket of stocks, hold them, and see how it does. Here it is!

If you want to try different stocks (these were chosen mostly at random) it's very easy. Click "clone algorithm" and edit the

sid()  

When you start typing s-i-d- in the code editor, you'll get an auto-complete window that will help you find the stock you want. Just type the company name and let the auto-complete do the work.

Clone Algorithm
98
Loading...
Backtest from to with initial capital ( data)
Cumulative performance:
Algorithm Benchmark
Custom data:
Week
Month
All
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Information Ratio
--
Benchmark Returns
--
Volatility
--
Max Drawdown
--
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
Information Ratio 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
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.

Hi Dan,
Could you help me write a line of code? I decide to sell stock when its price is larger than 1.1 times buying price. For example, I bought 500 shares of stock at $10 at the initial time, once it reaches $11, I would sell 500 shares. These are some of the codes I learned from your algorithm, but cannot build successfully.

def initialize(context):
# define which stocks you want
context.stocks = (sid(5729), sid(1637), sid(27558), sid(2190), sid(35920), sid(32146), sid(35902))
context.bought = False

def handle_data(context, data):
# buy them all on the first day, then stop
if not context.bought:
for stock in context.stocks:
log.info("buying 500 shares of " + str(stock))
order(stock, 500)
context.bought = True

if context.bought:  
    for stock in context.stocks:  
        if data[context.stocks].price > data[context.stocks].mavg(30):  
            order(stock,-500)  
    context.bought = False  

I've attached a modified version that randomly buys and holds the same securities in Dan's example above, over the same period of time. Perhaps someone can advise how I can handle the Python datetime objects more elegantly. I'd prefer not to specify as separate variables the hour & minute to submit the order.

By the way, I think it is important to distinguish between order submission and order fulfilment (buying/selling). For example, order(stock,500) only submits an order. It will be fulfilled in the next minute of trading for the security (unless the order is submitted on the last minute of the trading day, in which case, the submitted order is dropped...I'm told...never actually tested this).

Clone Algorithm
6
Loading...
Backtest from to with initial capital ( data)
Cumulative performance:
Algorithm Benchmark
Custom data:
Week
Month
All
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Information Ratio
--
Benchmark Returns
--
Volatility
--
Max Drawdown
--
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
Information Ratio 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
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.

Hello Weiyi Chen,

I don't have time right now to work up an example, but I think you should be able to store the purchase prices in a variable (e.g. context.basis) in def initialize(context). In the first iteration of the backtest, store the prices in context.basis, then define logic in your algorithm to sell when they reach a specified limit (e.g. 10% over the purchase price).

@Grant, I think this code does what you want, but uses a datetime.timedelta object. A timedelta represents a duration. You can do addition and subtraction of timedeltas, timedeltas with datetimes, and when you take the difference between datetimes it returns a timedelta. You'll see that I do a datetime difference in the code for this share.

I think I replicated the random behavior you wanted by generating a random duration of up to a week, and then waiting that long between placing orders.

Let me know what you think.

thanks,
fawce

Clone Algorithm
5
Loading...
Backtest from to with initial capital ( data)
Cumulative performance:
Algorithm Benchmark
Custom data:
Week
Month
All
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Information Ratio
--
Benchmark Returns
--
Volatility
--
Max Drawdown
--
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
Information Ratio 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
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.

Thanks Fawce,

I took a first pass at what you wrote, cloned it, and ran it a few times. I'll need to get back to it when I get the chance...seems to do the trick.

Grant

Hello Weiyi,

Here's an algorithm that hopefully does what you want. Just let me know if you can't sort out the flow or if you think that I've botched the logic. For a challenge, see if you can get the algorithm to run with:

context.stocks = [sid(41290),sid(41425),sid(39479),sid(33972),sid(41159)]  

They are thinly traded securities, so you'll need to add some logic to handle this case, since all securities do not have trades for all backtest minutes. Just plug the list of sids into the algorithm and try to run it. You'll get an error message with guidance on how to fix the problem. When I get the chance, I'll take a crack at the problem, too.

Clone Algorithm
8
Loading...
Backtest from to with initial capital ( data)
Cumulative performance:
Algorithm Benchmark
Custom data:
Week
Month
All
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Information Ratio
--
Benchmark Returns
--
Volatility
--
Max Drawdown
--
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
Information Ratio 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
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.

Hey Weiyi (and Dan & Fawce),

Note that in my algorithm above, I capture the estimated basis by saving the security prices at the time of order submission. For the securities selected, the order is fulfilled in the next minute of trading, so one would expect a small error. The actual basis is the price at the time of order fulfilment...I'm not yet sure how to capture that information in the backtester. Maybe somebody can fill me in? It may be an academic point for highly liquid securities, but for thinly traded ones, the price could change dramatically between the time of order submission and order fulfilment. There are other potentially important cases, as well.

Thanks Grant,

It's very kind of you. I learned a lot from your code.

Hi Grant - we do track the cost basis of your purchases. So you can have your code look at that instead if you want. I revised my original algo to buy the stocks, and then log the cost basis thereof.

Clone Algorithm
4
Loading...
Backtest from to with initial capital ( data)
Cumulative performance:
Algorithm Benchmark
Custom data:
Week
Month
All
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Information Ratio
--
Benchmark Returns
--
Volatility
--
Max Drawdown
--
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
Information Ratio 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
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.

Thanks Dan,

When I get the chance, I'll tweak my algorithm above to use the actual cost basis for the sell decision. I'll also see if I can handle the case of thinly traded securities (should be straightforward).

Grant

Hello Weiyi and all,

I've pieced together an algorithm that handles the case of thinly traded securities. It also uses the built-in cost basis computation provided by Quantopian (nice). I have a few more tweaks, so I am posting it here for review before doing a separate post. Let me know what you think. Instead of logging the cost basis at every tic, I plan to modify the code so that it only writes it out when there is a change (buy/sell). Also, I'd like to see if I can capture the datetimes when the orders are actually fulfilled.

I can't get the "Add Backtest" function to work, so I've pasted the code below.

import numpy as np

def initialize(context):

    #context.stocks = [sid(5729), sid(1637), sid(27558), sid(2190), sid(35920), sid(32146), sid(35902)]  
    context.stocks = [sid(41290),sid(41425),sid(39479),sid(33972),sid(41159)]  
    context.m = len(context.stocks)  
    context.basis = np.zeros(context.m)  
    context.bought = False  
    context.sold = [False]*context.m  
    context.sell_limit = 1.1  # relative price limit to submit sell order

def handle_data(context, data):  
    # record share prices at time of order fulfillment (0.0 if not yet fulfilled)  
    for i, stock in enumerate(context.stocks):  
        context.basis[i] = context.portfolio.positions[stock].cost_basis  
    log.info(context.basis)  
    # create numpy array of current security prices (0.0 if no tic)  
    current_prices = np.zeros(context.m) # set prices to 0.0  
    for i, stock in enumerate(context.stocks):  
        if stock in data:  
            current_prices[i] = data[stock].price  
    # submit sell order if relative security price reaches limit  
    if context.bought:  
        for i, stock in enumerate(context.stocks):  
            if context.basis[i] != 0.0 and current_prices[i] != 0.0:  
                relative_price = current_prices[i]/context.basis[i]  
                if relative_price >= context.sell_limit and context.sold[i] == False:  
                    order(stock, -500)  
                    context.sold[i] = True # switch flag if sell order submitted  
                    log.info(context.sold) # output sold flags  
    # submit initial order of basket of securities  
    if not context.bought:  
        for i, stock in enumerate(context.stocks):  
            order(stock, 500)  
            log.info("500 share buy order submitted: " + str(stock))  
        context.bought = True  

Hi Grant, sorry about the "Add Backtest" functionality. It should be fixed now.

thanks,
Jean

Please see attached for the algorithm referenced in my most recent post above. --Grant

Clone Algorithm
8
Loading...
Backtest from to with initial capital ( data)
Cumulative performance:
Algorithm Benchmark
Custom data:
Week
Month
All
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Information Ratio
--
Benchmark Returns
--
Volatility
--
Max Drawdown
--
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
Information Ratio 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
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.
Log in to reply to this thread.
Not a member? Sign up!