FutureFillModel fills a resting market order at the midnight extended-hours start on the previous session's quote bar, without stale wait or warning, when the subscription has no extended hours
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
- #9911 de @abhi-byte62 — cerrado sin fusionar
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 48/100
Línea de trabajo
Read Common/Orders/Fills/FutureFillModel.cs, especially MarketFill and the existing StopMarketFill hours check, then trace the stale-price logic in FillModel.cs. Reproduce with a futures subscription where extendedMarketHours is false and an order rests over a session close. Done when the order waits for fresh data or is marked stale, with a regression test covering the reported case.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Expected Behavior
A futures market order that rests across a session close waits for fresh data for its contract before filling, or at least fills with the stale-price warning. In particular it should not fill at a time of day when the subscription cannot receive any data (an extended-hours segment on a subscription with extendedMarketHours: false), on a quote bar from the previous session.
Actual Behavior
FutureFillModel.MarketFill hardcodes IsExchangeOpen(asset, true) (Common/Orders/Fills/FutureFillModel.cs:45), so a resting market order on a future subscribed without extended market hours is filled at the first time step inside an extended-hours segment: for CBOT grains the premarket segment starts at 00:00 Chicago, so the fill lands at midnight exchange time. The data used is the last cached bar, the previous session's 13:17-13:18 quote bar (the regular session closes 13:20 and the subscription delivers nothing after it), and the fill price is that bar's bid/ask close.
No stale-price wait and no stale-price warning apply, because the stale check compares the data end time with the order submission time, not with the fill time (FutureFillModel.cs:57-65, FillModel.cs:304-312, FillModel.cs:1093): the bar ended 2 minutes before the order was placed, so pricesEndTimeUtc + StalePriceTimeSpan < order.Time is false even though at the moment of the fill the bar is 10 h 40 m old (58 h over a weekend).
For a thin deferred contract whose last quote bar of the day carries a wide resting book, the fill is the far side of that stale book (e.g. ask 301.9 against a 273.1 bid, or ask 292.0 against a 270.2 bid in a run over 2010) while the market at the time of the fill is elsewhere (bid 284.9 / ask 285.0 on 2010-05-03 00:00 Chicago for the second example).
FutureFillModel.StopMarketFill (FutureFillModel.cs:124) already gates on GetSubscriptionDataConfigs(asset).IsExtendedMarketHours(); MarketFill does not, which is the inconsistency.
Potential Solution
Either or both:
- In
FutureFillModel.MarketFill, gate on the subscription's setting likeStopMarketFilldoes:IsExchangeOpen(asset, GetSubscriptionDataConfigs(asset).IsExtendedMarketHours()). A regular-hours-only subscription then rests the order until the next regular session, where the first bar is fresh (verified: withextendedMarketHours: truethe same order fills at 19:01 Chicago on the fresh evening bar, see below). - Make the stale check in
InternalMarketFill/FutureFillModel.MarketFillconsider the age of the data at the fill time when the order has been resting (utcTimeof the fill event rather thanorder.Time), so an order that rested across a close waits for fresh data perShouldWaitForFreshDataOnStale, or at least carriesFilledAtStalePrice.
Reproducing the Problem
QuantConnect Cloud, LEAN 2.5.0.0.18184 (also matches master a54e9cc for the lines cited). Backtest c2b01ac9c18f5436ebf3459d405b77f8 (project 37925270): fill Monday 2009-01-05 00:00 Chicago at the Friday 13:17 ask. Backtest 774bc1b258957ee80d4dfd691fd9b780 (project 37925312, same code with extended_market_hours=True): fill Sunday 2009-01-04 19:01 Chicago at the fresh bar's ask.
from AlgorithmImports import *
class FutureRestingMarketOrderFill(QCAlgorithm):
"""Place one market order on a deferred CBOT ZM contract at 14:20 New York (13:20 Chicago, the day-session
close) and log when and on which cached bar it fills."""
def initialize(self) -> None:
self.set_start_date(2009, 1, 2)
self.set_end_date(2009, 1, 6)
self.set_cash(100000)
future = self.add_future("ZM", Resolution.MINUTE, extended_market_hours=False)
future.set_filter(0, 200)
self._target = Symbol.create_future("ZM", Market.CBOT, datetime(2009, 5, 14))
self._sent = False
def on_data(self, slice: Slice) -> None:
if self._sent or self._target not in self.securities:
return
if self.time.hour == 14 and self.time.minute == 20:
sec = self.securities[self._target]
qb = sec.cache.get_data[QuoteBar]()
self.log(f"SUBMIT {self.time} local={sec.local_time} "
f"open_regular={sec.exchange.hours.is_open(sec.local_time, False)} "
f"open_extended={sec.exchange.hours.is_open(sec.local_time, True)} "
f"last_quote_bar={qb.time}->{qb.end_time} bid={qb.bid.close} ask={qb.ask.close} price={sec.price}")
self.market_order(self._target, 1)
self._sent = True
def on_order_event(self, order_event: OrderEvent) -> None:
if order_event.status != OrderStatus.FILLED:
return
sec = self.securities[order_event.symbol]
last = sec.cache.get_data()
qb = sec.cache.get_data[QuoteBar]()
self.log(f"FILL {self.time} local={sec.local_time} fill_price={order_event.fill_price} "
f"message='{order_event.message}' "
f"open_regular={sec.exchange.hours.is_open(sec.local_time, False)} "
f"open_extended={sec.exchange.hours.is_open(sec.local_time, True)} "
f"last_data={type(last).__name__} {last.time}->{last.end_time} fill_forward={last.is_fill_forward} "
f"bid={qb.bid.close} ask={qb.ask.close}")
Output with extended_market_hours=False (algorithm time is New York):
2009-01-02 14:20:00 SUBMIT 2009-01-02 14:20:00 local=2009-01-02 13:20:00 open_regular=False open_extended=False last_quote_bar=2009-01-02 13:17:00->2009-01-02 13:18:00 bid=273.1 ask=301.9 price=287.5
2009-01-05 01:00:00 FILL 2009-01-05 01:00:00 local=2009-01-05 00:00:00 fill_price=301.9 message='' open_regular=False open_extended=True last_data=QuoteBar 2009-01-02 13:17:00->2009-01-02 13:18:00 fill_forward=False bid=273.1 ask=301.9
Output with extended_market_hours=True:
2009-01-02 14:20:00 SUBMIT 2009-01-02 14:20:00 local=2009-01-02 13:20:00 open_regular=False open_extended=False last_quote_bar=2009-01-02 13:17:00->2009-01-02 13:18:00 bid=273.1 ask=301.9 price=287.5
2009-01-04 20:01:00 FILL 2009-01-04 20:01:00 local=2009-01-04 19:01:00 fill_price=299.4 message='' open_regular=False open_extended=True last_data=QuoteBar 2009-01-04 19:00:00->2009-01-04 19:01:00 fill_forward=False bid=298.9 ask=299.4
The same pattern appears on every entry of a two-year probe that places market orders at 14:20 New York on ZL and ZM contracts without extended hours: all of them fill at 01:00 New York (00:00 Chicago) the next trading day on the previous session's last quote bar, none with a stale-price message.
Reported through support conversation 215476421928703.
Checklist
- I have completely filled out this template
- I have confirmed that this issue exists on the current
masterbranch - I have confirmed that this is not a duplicate issue by searching issues
- I have provided detailed steps to reproduce the issue
- Lenguaje dominante
- C#
- Estrellas
- 22.5k
- Forks
- 5.4k
- Merge medio
- 1 d 2 min
- PR fusionados (30 d)
- 71
Preparar el entorno
Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.
- Incluye un Dockerfile o un archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de QuantConnect/Lean
-
PDF report generation can overwrite HTML when the destination extension is not lowercase .htmlAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
QuantConnect/Lean#10082 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
QuantConnect/Lean#10059 ·
Los mantenedores suelen responder en 1 día
-
Library Request: highspyAbiertolibrary-request
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
QuantConnect/Lean#10034 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
QuantConnect/Lean#9965 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
QuantConnect/Lean#9955 ·
Los mantenedores suelen responder en 1 día
Todos los issues de QuantConnect/Lean
Issues similares
-
bug emitter:client:csharp
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
microsoft/typespec#12572 · 1 comentario · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
needs-area-label
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 2 días
-
AWS Lambda: KeyNotFoundException drops HTTP-triggered transactions when allowAllHeaders is disabledAbiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
newrelic/newrelic-dotnet-agent#3949 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
area-networking
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
dotnet/aspnetcore#70545 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 87/100
Los mantenedores suelen responder en 1 día