Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Using Stop Loss Orders

I have a very simple algorithm that is supposed to buy 5 shares of SPY at the beginning of the month and then sell if the stop loss is triggered (at 5%). Somehow, my algorithm is selling randomly (at least I don't understand what the logic is). My stop loss is implemented in the handle data function/method (Please note that my attached algorithm also contains the StopLoss manager class from this post:
https://www.quantopian.com/posts/how-to-manage-stop-loss
, but I am not using it because I could not get it to work, if someone knows how to get it to work...I would be happy to use that as well)

Does anyone have an idea how I need to modify the algorithm so that it verifies whether there are any open positions and then sell them if the stop loss is reached?

4 responses

What happens with a trailing stop something like this, untested.

def handle_data(context, data):  
    s   = sid(8554)  
    pos = context.portfolio.positions  
    prc = data.current(s, 'price')  
    stp = 0.01      # threshold ratio relative to 1.0

    if pos[s].amount:  
        if prc < pos[s].cost_basis:  
            order_target(s, 0)     # Outright close below cost basis, perhaps.  
        else:                      # At or above cost basis now.  
            stop_exists = 0  
            new_stop    = 0

            for o in get_open_orders(s):  
                if o.stop:  
                    stop_exists = 1  
                    if prc > o.stop + (o.stop * (1.0 + stp))  
                        cancel_order(o.id)      # cancel previous stop order  
                        new_stop = 1  
                    break   # exit loop, altho there should only ever be one stop order

            if new_stop or not stop_exists:  
                order_target(s, 0, style=StopOrder(prc * (1.0 - stp)))    # New stop

Very nice, I am going to try that out. Thanks so much for the help!!

For a faster backtest, checking stops every some-number-of-minutes and loop in case multiple stocks.

def initialize(context):  
    context.stocks = [sid(8554), sid(39840)]  
    context.stp_threshold = .01   # threshold ratio relative to 1.0

    opens_minute    =  5  
    every_n_minutes = 15

    schedule_function(opens, date_rules.every_day(), time_rules.market_open(minutes=opens_minute))

    # Considering open positions next market open, should probably be checking them before opens()  
    #   especially if opens_minute is far greater like 90 or something.  
    # Note opens_minute position here is non-inclusive, only up to opens_minute - 1  
    #   and that's great in this case, leaving ordering in the opens() minute alone by itself.  
    for m in range(1, opens_minute, every_n_minutes):  
        schedule_function(stoploss, date_rules.every_day(), time_rules.market_open(minutes=m))

    # Checking stops periodically, after opens()  
    for m in range(opens_minute + 1, 390, every_n_minutes):  # start, until, every n minutes  
        schedule_function(stoploss, date_rules.every_day(), time_rules.market_open(minutes=m))

def stoploss(context, data):  
    pos = context.portfolio.positions

    for s in pos:  
        if not data.can_trade(s): continue  # skip any delist

        stop_exists = 0  
        new_stop    = 0  
        prc = data.current(s, 'price')

        for o in get_open_orders(s):  
            if o.stop:  
                stop_exists = 1  
                if prc > o.stop + (o.stop * (1.0 + context.stp_threshold))  
                    cancel_order(o.id)      # cancel previous stop order  
                    new_stop = 1  
                break   # exit loop, should only ever be one stop order

        if new_stop or not stop_exists:  
            order_target(s, 0, style=StopOrder(prc * (1.0 - context.stp_threshold)))    # New stop

def opens(context, data):  
    for s in context.stocks:  
        if not data.can_trade(s): continue

        if s not in context.portfolio.positions:  
            order_target_percent(s, 1.0 / len(context.stocks))  # open  

Very nice, thank you for the suggestion. This will speed up things.