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