Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Trailing stop

Hi everyone,

I was wondering how I could implement a trailing stop in my algorithm. Have had a look in trading guards in the API reference but there doesn't seem to be any trailing stop code examples.

A trailing stop:
"A sell trailing stop order sets the stop price at a fixed amount below the market price with an attached "trailing" amount. As the market price rises, the stop price rises by the trail amount, but if the stock price falls, the stop loss price doesn't change, and a market order is submitted when the stop price is hit."

So a 15% trailing stop should mean that if the stock declines by 15% or more, the trailing stop will be triggered, thereby capping your loss. It takes the emotion out of the investments.

I appreciate any suggestions. Thanks!

6 responses

There are some examples of stop loss functions in https://www.quantopian.com/posts/trailing-stop-loss . Have a look and see if they are any good for your purposes.

Some good examples. Thank you James.

Just beware that those stop loss examples do not use actual stop orders, so that you will not be stopped out intra-minute. Unfortunately setting and resetting actual stop orders to running positions gets quite tedious.

hi, yes, resetting the stop order can be tedious. I am trying to implement a trailing stop and having a problem as well.

I am only buying 2 securities and setting a trailing stop that is in effect until 3:15pm. But the stop does not trail, it is only set once and they reiterated each minute, this just causes me to sell at each minute.

Would anyone be willing to look over my code and see where I am going wrong? I have some decent logs. I am starting to think there is a problem returning the object or something.
code below:

import talib
import datetime
import time
import pytz
from pytz import timezone
import brokers.ib
from brokers.ib import IBExchange

def initialize(context):
log.info(str(get_datetime("US/Eastern")) + ": initializing algorithm")
context.stock = sid(8554) # SPY
context.ETFs = sid(42471),sid(42472) # UWTI and DWTI
# Add other 3x ETF's later
set_benchmark(context.stock) # comparing against SPY
context.stop_price = 0
context.stop_pct = 0.99 # sets 1% trailing stop-loss
log.error

schedule_function(buy, date_rules.every_day(), time_rules.market_open(minutes=30),  
half_days=True)  
# I want to schedule to buy ETFs everyday 30min after the open at market price  

schedule_function(sell, date_rules.every_day(), time_rules.market_close(minutes=45),  
half_days=True)  
# I want to schedule to sell the ETFs everyday, if no stop triggered, 45min before close 

def handle_data(context, data):
set_trailing_stop(context, data)
for sec in context.ETFs:
if data[sec].price < context.stop_price:
log.info(str(get_datetime("US/Eastern")) + ": selling security [" + str(sec.symbol) + "] | current price: " + str(data[sec].price) + " < stop price: " + str(context.stop_price))
order_target(sec, 0)
context.stop_price = 0

record(UWTI_price=data[sid(42471)].price,  
       DWTI_price=data[sid(42472)].price,  
       stop=context.stop_price)  

def buy(context, data):
log.info(str(get_datetime("US/Eastern")) + ": placing order for $5000 of UWTI and DWTI")
order_value(sid(42471), 5000, style=MarketOrder(exchange=IBExchange.IEX))
order_value(sid(42472), 5000, style=MarketOrder(exchange=IBExchange.IEX)) # order value orders a $5000 value of stock, roughly, it rounds down. In this case, about $5000 of UWTI and DWTI are bought.

def sell(context, data):
log.info(str(get_datetime("US/Eastern")) + ": selling current holdings of UWTI and DWTI")
order_target(sid(42471), 0, style=MarketOrder(exchange=IBExchange.IEX))
order_target(sid(42472), 0, style=MarketOrder(exchange=IBExchange.IEX)) # order target uses a target number of shares. In this case, sell in order to have 0 shares left, closes the positions in UWTI and DWTI

def set_trailing_stop(context, data):
for sec in context.ETFs:
if context.portfolio.positions[sec].amount:
price = data[sec].price
context.stop_price = max(context.stop_price, context.stop_pct * price)

Some code to try for trailing trades. These are geared to be able to easily replace order_target_percent() in existing code to try them out.

def handle_data(context, data):  
    for stock in data:  
        [ some conditions ]  
            #order_target_percent(stock, .5)  
            TrailingBuy(stock, .5, context, data)  
        else:  
            #order_target_percent(stock, 0)  
            TrailingSell(stock, 0, context, data)

def TrailingBuy(sec, percent, c, data):  # Trailing Stop for Buy on dn-then-up movement  
    b_stop_ratio = 1.06   # Ratio above low trigger to buy.  
    if sec not in data: return  
    if 'books' not in c: c.books = {}  
    log_it = 0  
    sym    = sec.symbol  
    if sym not in c.books:  
        c.books[sym] = {}  
    bsym   = c.books[sym]  
    price  = data[sec].price  
    keyy   = price   # Could act on moving average for example, or other  
    b_stop = b_stop_ratio * keyy  
    if 'trailingBuy' not in bsym:  
        bsym['trailingBuy'] = b_stop        # The marker line  
        return

    if keyy > bsym['trailingBuy']:  
        order_target_percent(sec, percent)  # Buy

    elif b_stop < bsym['trailingBuy']: # If lower  
        if log_it:  
            log.info('{} Lower trailingBuy from {} to {}'.format(  
                sym, bsym['trailingBuy'], b_stop))  
        bsym['trailingBuy'] = b_stop   # Adjust trailingBuy lower with dn movement.

def TrailingSell(sec, percent, c, data):  # Trailing Stop for Sell on up-then-dn movement  
    s_stop_ratio = .94    # Ratio below high trigger to sell.  
    if sec not in data: return  
    if 'books' not in c: c.books = {}  
    log_it = 0  
    sym    = sec.symbol  
    if sym not in c.books:  
        c.books[sym] = {}  
    bsym   = c.books[sym]  
    price  = data[sec].price  
    keyy   = price   # Could act on moving average for example, or other  
    s_stop = s_stop_ratio * keyy  
    if 'trailingSell' not in bsym:  
        bsym['trailingSell'] = s_stop        # The marker line  
        return

    if keyy < bsym['trailingSell']:  
        order_target_percent(sec, percent)   # Sell

    elif s_stop > bsym['trailingSell']: # If lower  
        if log_it:  
            log.info('{} Increase trailingSell from {} to {}'.format(  
                sym, bsym['trailingSell'], s_stop))  
        bsym['trailingSell'] = s_stop   # Adjust trailingSell higher with up movement.

Chris, can you format your code please? It would help to read if indentation was in place. Please use the editor buttons to mark selected text as code (or insert three ` on separate lines before and after the code)