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?

2
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
"""
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 Q500US
from quantopian.pipeline.factors import SimpleMovingAverage

# import any datasets we need
from quantopian.pipeline.data import Fundamentals
from quantopian.pipeline.data.builtin 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.
"""
context.security = 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.
schedule_function(
rebalance,
date_rules.month_start(),
time_rules.market_open(hours=1),

)
def make_pipeline():
"""
A function to create our dynamic stock selector (pipeline). Documentation
on pipeline can be found here:
https://www.quantopian.com/help#pipeline-title
"""

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

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

pipe = Pipeline(
columns={
'close': yesterday_close,
},
screen=base_universe
)
return pipe

"""
Called every day before market open.
"""
context.output = pipeline_output('pipeline')
context.day_count += 1
log.info(context.daily_message, context.day_count)
#context.pipeline_data = algo.pipeline_output('data_pipe')

# Let's just take the top stocks (in no particular order)
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)
#order(stock,share_amount)
#cash = context.portfolio.cash
#current_positions = context.portfolio.positions[symbol]('SPY').amount
current_positions = 0

if current_positions == 0:
current_positions = 5
number_of_shares = 5
order(context.security, number_of_shares)
elif current_positions !=0:
log.info('Selling shares')

#order_target(context.security, 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(context.security, -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.

Parameters:
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.
"""
log.warn('Got additional parameter, which will be ignored!')
self.params.update(params)

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
"""
self._refresh_amounts(context)

for sec in self.stops.index:
cancel_order(self.stops['id'][sec])
if self._np.isnan(self.stops['price'][sec]):
stop = (1-self.params['pct_init'])*data.current(sec, 'close')
else:
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._import()
self.params = {'pct_init': 0.01, 'pct_trail': 0.03}
self.stops = self._pd.DataFrame(columns=['amount', 'price', 'id'])
self.set_params(**params)

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
else:
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(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: