Skip to content

fix(longbridge): keep GTC/GTD orders alive on transient Expired status - #1125

Open
FaintGhost wants to merge 1 commit into
TraderAlice:devfrom
FaintGhost:fix/longbridge-gtc-expired-status
Open

fix(longbridge): keep GTC/GTD orders alive on transient Expired status#1125
FaintGhost wants to merge 1 commit into
TraderAlice:devfrom
FaintGhost:fix/longbridge-gtc-expired-status

Conversation

@FaintGhost

Copy link
Copy Markdown

Problem

Longbridge reports US-equity GTC/GTD limit orders as Expired (status 16) between trading sessions — a transient venue state that reverts to New once the market reopens. The broker adapter mapped Expired → Inactive, and the UTA order-sync loop treats any non-Submitted state as terminal (rejected). The order was then dropped from the pending queue and never re-observed.

Live evidence (longbridge-main account): three GTC limits submitted together:

  • ARM GTC limit filled on time and was recorded correctly
  • NVDA GTC limit filled later at its limit price, but UTA had already marked it rejected — the fill was invisible to UTA
  • NET GTC limit stayed open at the broker (still visible in the official Longbridge app), but UTA marked it rejected

So the UTA state machine and the broker disagreed, and the discrepancy was only discoverable by querying the broker directly.

Fix

Disambiguate Expired with the order's timeInForce:

  • Expired + Day (1) → Inactive (a Day order genuinely expires unfilled at close)
  • Expired + GTC (2) / GTD (3) → Submitted (transient between sessions; keep the order in the pending queue so the sync poller keeps observing it until it fills, cancels, or truly terminates)

makeOrderState now accepts and forwards timeInForce; mapOpenOrder supplies it from the broker response.

Verification

  • 73/73 vitest tests pass in LongbridgeBroker.spec.ts, including 4 new cases covering the Expired/TIF matrix
  • No API/UX surface changed; behavior only affects the order-status projection used by sync reconciliation

Longbridge marks US-equity GTC/GTD orders as Expired (status 16) between
trading sessions; the state reverts to New once the market reopens. The
previous mapping treated Expired as Inactive, which the UTA sync loop
interpreted as a terminal rejection — the order was dropped from the
pending queue and never re-observed, even though it was still alive at
the broker (observed live: an NVDA GTC limit filled later and a NET GTC
limit stayed open, both misreported as rejected).

Disambiguate with timeInForce: Day orders (1) really expire at close and
stay Inactive; GTC (2) / GTD (3) Expired maps to Submitted so the sync
poller keeps watching until the order fills, cancels, or truly dies.
@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

@FaintGhost is attempting to deploy a commit to the luokerenx4's Team Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown
Contributor

Thanks for the detailed live evidence here — the GTC failure mode is convincing, especially the order that later filled after UTA had already made the transient Expired status terminal.

Before we reproduce this on a maintainer-owned branch, could you help us pin down a few Longbridge-specific semantics? We do not currently have a Longbridge test account, and this is a trading state-machine boundary where we would rather use venue evidence than infer behavior.

  1. GTC transition evidence. If you still have sanitized observations, could you share the relevant fields from orderDetail across the transition (for example status, timeInForce, expireDate, updatedAt, executedQuantity, and executedPrice) for one order that went Expired → New/Filled? Please omit account identifiers and credentials.
  2. GTD behavior. Have you observed the same between-session Expired → New transition for a GTD order, or is the GTD part inferred from GTC? A GTD order must eventually become genuinely expired on its configured date, so mapping every Expired + GTD to Submitted would appear to keep a truly expired order pending forever unless we also inspect expireDate.
  3. Unknown/missing TIF. The adapter's raw order shape currently requires timeInForce. Is there a real Longbridge response where it is Unknown/missing for an expired order? If not, we are inclined to keep the workaround narrow: only explicitly verified long-lived TIF values should override Expired.
  4. Fill fidelity. In the later-filled NVDA response, did orderDetail provide executedQuantity as well as executedPrice? The current adapter maps the price but not executedQuantity → order.filledQuantity, so fixing the terminal-state error alone may recover the fill status while still leaving UTA's fill/cost-basis record incomplete.
  5. Safe acceptance path. Does Longbridge paper/demo exhibit this same after-hours status transition? If so, a minimal reproduction sequence and expected cleanup would be very helpful. If it only occurs on live accounts, we will treat your sanitized observation as venue evidence and keep our own verification offline/read-only rather than submit real-money test orders.

Our external contribution policy means we will not directly merge a cross-repository trading-surface branch; we use strong external PRs as implementation proposals and reimplement accepted changes on a maintainer-owned branch. Your report and analysis will be credited in CONTRIBUTORS.md if this lands. The core GTC diagnosis looks valuable — the questions above are about choosing a boundary that fixes the observed bug without turning real expirations into permanently live UTA orders.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants