HI guys,
I've got a complicated algo that is set up to work on 5 minute/ 15 minute or 30 minute bars. I've been confounded by Quantopian repeatedly filling my target or breach orders before the original order is filled. Of course that can happen now and then, due to gaps in pricing, but it should not happen in the following documented situation on SPY, which suggests that the data reported by Q on limit orders may be flawed:
for the date 2015/10/15 at 13:43 when close price is registered at 200.14 with minute bar high of 200.14 and low of 199.18
we place following bracket orders:
get_order(context.target_order_id): Order for -4200 shares of (Limit $NaN.N, Open)
amount: -4200
commission: None
created: 2015-10-15 13:43:00+00:00
dt: 2015-10-15 13:43:00+00:00
filled: 0
id: 696e15035e1c40febd97d7f92329b07c
keys: <instancemethod>
limit: 200.24
limit_reached: False
reason: None
sid: Equity(8554, symbol=u'SPY', asset_name=u'SPDR S&P 500 ETF TRUST', exchange=u'NYSE ARCA EXCHANGE', start_date=Timestamp('1993-01-29 00:00:00+0000', tz='UTC'), end_date=Timestamp('2015-10-19 00:00:00+0000', tz='UTC'), first_traded=None)
status: 0
stop: None
stop_reached: False
to_series: <instancemethod>
pNl: NameError: name 'pNl' is not defined
get_datetime().minute % context.interval: 3
get_order(context.entry_order_id): Order for 4200 shares of (Limit $NaN.N, Open)
amount: 4200
commission: None
created: 2015-10-15 13:43:00+00:00
dt: 2015-10-15 13:43:00+00:00
filled: 0
id: d3057efecb164810a59bb61546274363
keys: <instancemethod>
limit: 200.22
limit_reached: False
reason: None
sid: Equity(8554, symbol=u'SPY', asset_name=u'SPDR S&P 500 ETF TRUST', exchange=u'NYSE ARCA EXCHANGE', start_date=Timestamp('1993-01-29 00:00:00+0000', tz='UTC'), end_date=Timestamp('2015-10-19 00:00:00+0000', tz='UTC'), first_traded=None)
status: 0
stop: None
stop_reached: False
to_series: <instancemethod>
in the next minute the context variables show
get_order(context.target_order_id): Order for -4200 shares of (Limit $NaN.N, Filled)
amount: -4200
commission: 25.2
created: 2015-10-15 13:43:00+00:00
dt: 2015-10-15 13:44:00+00:00
filled: -4200
id: 696e15035e1c40febd97d7f92329b07c
keys: <instancemethod>
limit: 200.24
limit_reached: True
reason: None
sid: Equity(8554, symbol=u'SPY', asset_name=u'SPDR S&P 500 ETF TRUST', exchange=u'NYSE ARCA EXCHANGE', start_date=Timestamp('1993-01-29 00:00:00+0000', tz='UTC'), end_date=Timestamp('2015-10-19 00:00:00+0000', tz='UTC'), first_traded=None)
status: 1
stop: None
stop_reached: False
to_series: <instancemethod>
pNl: NameError: name 'pNl' is not defined
get_datetime().minute % context.interval: 4
get_order(context.entry_order_id): Order for 4200 shares of (Limit $NaN.N, Open)
amount: 4200
commission: None
created: 2015-10-15 13:43:00+00:00
dt: 2015-10-15 13:43:00+00:00
filled: 0
id: d3057efecb164810a59bb61546274363
keys: <instancemethod>
limit: 200.22
limit_reached: False
reason: None
sid: Equity(8554, symbol=u'SPY', asset_name=u'SPDR S&P 500 ETF TRUST', exchange=u'NYSE ARCA EXCHANGE', start_date=Timestamp('1993-01-29 00:00:00+0000', tz='UTC'), end_date=Timestamp('2015-10-19 00:00:00+0000', tz='UTC'), first_traded=None)
status: 0
stop: None
stop_reached: False
to_series: <instancemethod>
This is nonsensical: for the target order to have filled, the price must have attained at least 200.24. Yet the entry order is saying it never got filled, although this only required a price of at least 200.22. In theory, that could occur if we had a gap up, with sell order filling before the entry order (a buy). However, there was no gap.
In fact both orders would have been filled, as price opened at 200.18 and went up to 200.34 before closing at 200.33
But that's just the beginning. My algo traps for situations where my closing order fills before my entry order can fill. When it encounters such situations I simply close out all positions, take my lumps, and start the algo afresh. The problem is, no matter what I try, I cannot cancel the original entry orders, so the system incorrectly shows my to be short X number of shares ( my sell order above target price filled but not my buy order below it).
Just in case I am doing something wrong, this is how I track my position:
context.current_position = context.portfolio.positions[context.stock].amount
Here's how I close any open orders:
def closeAnyOpenOrders(context):
orders = get_open_orders(context.stock)
if orders:
for order in orders:
message = 'Canceling order for {amount} shares in {stock}'
message = message.format(amount=order.amount, stock=context.stock)
#log.debug(message)
cancel_order(order)
context.current_position =0
context.bracketOrdersPlaced = False
and here's how I simultaneously place my bracket orders:
def placeBracketOrders(shares,context):
context.bracketOrdersPlaced = False
if context.entryLimit is None or context.target is None or context.stopLimit is None or context.breachpoint is None:
if context.debug2: log.info("ERROR: time: {} MISSING context variables entry:{} target: {} breach: {} stoplimit" . format(context.strtime,context.entryLimit,context.breachpoint,context.stopLimit) )
return context.bracketOrdersPlaced
context.entry_order_id=order_target(context.stock, shares, style=LimitOrder(context.entryLimit))
context.target_order_id=order_target(context.stock, -shares, style=LimitOrder(context.target))
context.breach_order_id=order_target(context.stock, -shares, style=StopLimitOrder(context.stopLimit,context.breachpoint))
if shares >0 :
context.openPosition = 1
direction = 'long'
elif shares<0:
context.openPosition = -1
direction='short'
else:
context.openPosition = 0
if context.entry_order_id is None or context.target_order_id is None or context.breach_order_id is None :
context.bracketOrdersPlaced =False
if context.debug2: log.info("time: {} INCOMPLETE ATTEMPT going {}.limit {} target {} PROTECTION stop {} stoplimit {}" . format(context.strtime,direction,context.entryLimit,context.target,context.breachpoint,context.stopLimit) )
else:
context.bracketOrdersPlaced =True
if context.debug2: log.info("time: {}SUCCESSFUL ATTEMPT going {} limit {} target {} PROTECTION stop {} stoplimit {}" . format(context.strtime,direction,context.entryLimit,context.target,context.breachpoint,context.stopLimit) )
return context.bracketOrdersPlaced
I realize one work around would be to place my original order, and then only place my bracketing (target/breach) orders once I've got
confirmation that the original order filled. But this can take up to 2 minutes for Q to detect, which is far too long in any day trading strategy.
Am I beating a dead horse trying to make Q work for day trading?
Thanks