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:
, 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?

Clone Algorithm
Backtest from to with initial capital
Total Returns
Max Drawdown
Benchmark Returns
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
This is a stategy to demonstrate ordering by fixed weights


# import pipeline methods 
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline, CustomFactor

# import the built in filters and factors
from quantopian.pipeline.filters import QTradableStocksUS
from quantopian.pipeline.filters import Q500US
from quantopian.pipeline.factors import SimpleMovingAverage

# import any datasets we need
from import Fundamentals  
from import USEquityPricing

# import optimize
import quantopian.optimize as opt

# import numpy and pandas just in case
import numpy as np
import pandas as pd

def initialize(context):
    Called once at the start of the algorithm.
    """ = symbol('SPY')
    current_positions = context.portfolio.positions[symbol('SPY')].amount
    # Set the constant weight we want
    context.target_leverage = 1.0
    context.target_stock_qty = 50
    context.target_weight = context.target_leverage / context.target_stock_qty
    context.daily_message = "Day {}."
    context.daily_message = 'Current number of shares of SPY: ' + str(current_positions)
    context.day_count = 0

    # Set up our pipeline
    attach_pipeline(make_pipeline(), 'pipeline')
    # # Initializing the StopLoss-Manager       
    #context.SL_Manager = StopLoss_Manager(pct_init=0.000005, pct_trail=0.00003)
    # Managing stop-orders for open positions at market opening.
    #schedule_function(context.SL_Manager.manage_orders, date_rules.every_day(), time_rules.market_close())   
    # order every day, 1 hour after market open.
def make_pipeline():
    A function to create our dynamic stock selector (pipeline). Documentation
    on pipeline can be found here:

    # Base universe set to the QTradableStocksUS
    #base_universe = QTradableStocksUS()
    base_universe = Q500US()

    # Factor of yesterday's close price.
    yesterday_close = USEquityPricing.close.latest

    pipe = Pipeline(
            'close': yesterday_close,
    return pipe

def before_trading_start(context, data):
    Called every day before market open.
    context.output = pipeline_output('pipeline')
    context.day_count += 1, context.day_count)
    #context.pipeline_data = algo.pipeline_output('data_pipe')
    # Let's just take the top stocks (in no particular order)
    #context.my_stocks = context.output.head(context.target_stock_qty).index
    context.my_best_stock_list = context.output.nsmallest(1, 'close').index.tolist()

def rebalance(context, data):
    Use Optimize to place orders all at once
    # Specify our series with the weights we want
    # Need to specify BOTH the securities (as the index) and the weight
    #reactive my_weights = pd.Series(index=context.my_best_stock_list, data=context.target_weight)
    # Create our maximize alpha objective
    #reactive weight_objective = opt.TargetWeights(my_weights) 
    # at the end of rebalance and wherever you create new orders  
    #context.SL_Manager.manage_orders(context, data)  
    # Set the constraints

    # Execute the order_optimal_portfolio method with above objective and constraint
    #reactive order_optimal_portfolio(objective = weight_objective, constraints = [])
    #context.SL_Manager.manage_orders(context, data)
    #cash =
    #current_positions = context.portfolio.positions[symbol]('SPY').amount
    current_positions = 0

    if current_positions == 0:
        current_positions = 5
        number_of_shares = 5
        order(, number_of_shares)'Buying shares')
    elif current_positions !=0:       'Selling shares')
        #order_target(, 0)

def handle_data(context, data):
    Called every minute.
    current_positions = context.portfolio.positions[symbol('SPY')].amount
    if current_positions != 0:
        stockPrice = context.portfolio.positions[symbol('SPY')].cost_basis
        stopPrice = stockPrice - (stockPrice*0.01)
        order(, -1, style=StopOrder(stopPrice))

class StopLoss_Manager:
    Class to manage to stop-orders for any open position or open (non-stop)-order. This will be done for long- and short-positions.
        pct_init (optional),
        pct_trail (optional),
        (a detailed description can be found in the set_params function)
    Example Usage:
        context.SL = StopLoss_Manager(pct_init=0.005, pct_trail=0.03)
        context.SL.manage_orders(context, data)
    def set_params(self, **params):
        Set values of parameters:
        pct_init (optional float between 0 and 1):
            - After opening a new position, this value 
              is the percentage above or below price, 
              where the first stop will be place. 
        pct_trail (optional float between 0 and 1):
            - For any existing position the price of the stop 
              will be trailed by this percentage.
        additionals = set(params.keys()).difference(set(self.params.keys()))
        if len(additionals)>1:
            log.warn('Got additional parameter, which will be ignored!')
            del params[additionals]
    def manage_orders(self, context, data):
        This will:
            - identify any open positions and orders with no stop
            - create new stop levels
            - manage existing stop levels
            - create StopOrders with appropriate price and amount
        for sec in self.stops.index:
            if not data.can_trade(sec):
                if self._np.isnan(self.stops['price'][sec]):
                    stop = (1-self.params['pct_init'])*data.current(sec, 'close')
                    o = self._np.sign(self.stops['amount'][sec])
                    new_stop = (1-o*self.params['pct_trail'])*data.current(sec, 'close')
                    stop = o*max(o*self.stops['price'][sec], o*new_stop)
                self.stops.loc[sec, 'price'] = stop           
                self.stops.loc[sec, 'id'] = order(sec, -self.stops['amount'][sec], style=StopOrder(stop))
    def __init__(self, **params):
        Creatin new StopLoss-Manager object.
        self.params = {'pct_init': 0.01, 'pct_trail': 0.03}
        self.stops = self._pd.DataFrame(columns=['amount', 'price', 'id'])        
    def _refresh_amounts(self, context):
        Identify open positions and orders.
        # Reset position amounts
        self.stops.loc[:, 'amount'] = 0.
        # Get open orders and remember amounts for any order with no defined stop.
        open_orders = get_open_orders()
        new_amounts = []
        for sec in open_orders:
            for order in open_orders[sec]:
                if order.stop is None:
                    new_amounts.append((sec, order.amount))                
        # Get amounts from portfolio positions.
        for sec in context.portfolio.positions:
            new_amounts.append((sec, context.portfolio.positions[sec].amount))
        # Sum amounts up.
        for (sec, amount) in new_amounts:
            if not sec in self.stops.index:
                self.stops.loc[sec, 'amount'] = amount
                self.stops.loc[sec, 'amount'] = +amount
        # Drop securities, with no position/order any more. 
        drop = self.stops['amount'] == 0.
        self.stops.drop(self.stops.index[drop], inplace=True)
    def _import(self):
        Import of needed packages.
        import numpy
        self._np = numpy
        import pandas
        self._pd = pandas
There was a runtime error.
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(      # 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(      # 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.