Ah so that's what you're doing. I have similar code but I've broken it down into separate functions instead of trying to do it all at once.
First, have your stop function checking your positions gain/loss every minute using handle_data()
Second, when your stop function detects the stop trigger write an If statement like this with get_open_orders()
Third, when there are no open orders for a stock you write the new market order that closes the position.
def process_stop(context, data):
for stock in context.portfolio.positions:
if not data.can_trade(stock): continue
gain = get_gain(context,stock)
if gain < (-1*context.stoppct):
if get_open_orders(stock):
cancel_open_order(context, stock)
continue
amount = context.portfolio.positions[stock].amount
if not amount == 0:
order(stock,-amount)
log.warn("Sell Order " + str(stock) + " @ " + str(data.current(stock,'price')) + " STOP ORDER.")
def cancel_open_order(context, sec):
oo = get_open_orders(sec)
if len(oo) == 0:
return
for order in oo:
if order.sid == sec:
if context.bug:
log.debug("Cancelling " + str(order.sid.symbol))
cancel_order(order)
def get_gain(context, s):
if s in context.portfolio.positions:
cost = context.portfolio.positions[s].cost_basis
amount = context.portfolio.positions[s].amount
price = context.portfolio.positions[s].last_sale_price
if amount > 0:
gain = price/cost - 1
if amount < 0:
gain = 1 - price/cost
else:
gain = 0
return gain
notice how there is a continue after cancel_open_order(context, stock), it's so the logic skips that one until the next calling of process_stop() in handle_data(). quantopian only refreshes data from robinhood once per minute so if you cancel an order than immediately try and write a new one I'm not sure it would work correctly. By doing this you don't really have to check for amounts you just close the position based on the amount you hold in your portfolio (easy). Canceling the order cancels everything under that stock no need to specify an amount.
If you want to close your position with a limit order it gets trickier because you will always have a new open order that will be getting canceled. In this case you'd want to .append(stock) to a list and check against that list before executing the stop.
get_open_orders(stock) is a quantopian built in function that is very useful when you want to check if any orders exist for one single stock.
.iteritems() is some fancy python code that I don't really understand but it lets you iterate over a dictionary in a more loopable format, to get key/value pairs or something. I didn't write it but it works and you can use breakpoints to understand it fully, or google.