Skip to content

orders: route adaptive, algo and what-if through the shared encoder (ibx#318) - #374

Closed
userFRM wants to merge 2 commits into
deepentropy:mainfrom
userFRM:fix/algo-orders-bypass-attrs
Closed

orders: route adaptive, algo and what-if through the shared encoder (ibx#318)#374
userFRM wants to merge 2 commits into
deepentropy:mainfrom
userFRM:fix/algo-orders-bypass-attrs

Conversation

@userFRM

@userFRM userFRM commented Jul 30, 2026

Copy link
Copy Markdown

Stacked on #246 — review the tip commit only.

Problem

build_order_request computes the extended-attribute block partway down, and three order types returned before reaching it. Their request variants carried no attribute block at all, so Order::attrs() was never consulted on those paths and their encoders emitted none of it.

Setting outsideRth on an adaptive, algo or what-if order was accepted by the API and silently ignored — the order went out regular-hours-only with nothing reporting the difference. The same early return bypassed the parent link and the OCA group, so an adaptive or algo order used as a bracket child was submitted unlinked and outside its group, which is what the attribute block exists to prevent. The time-in-force was hard-coded to DAY on all three.

What this changes

Patching the missing tags into each dedicated encoder would leave the rest of the block — hidden, display size, minimum quantity, good-after, GTD expiry, discretionary amount, sweep-to-fill, all-or-none, trigger method, cash quantity, conditions — still dropped on those paths, and would put the emission in four places.

These route instead, the way the adjustable stop does in #246: Adaptive, Algo and WhatIf are OrderKind variants carried by SubmitEx, and the three standalone encoder arms are gone.

Their own tags keep their values and their position — after tag 204 and the attribute block, where the encoders this replaces put them: 18=e and the adaptive priority parameter, the algo strategy with 849 and its parameter pairs, and the what-if flag. A what-if is still tracked under its marker so the response is recognised as a preview.

Breaking change to the low-level Rust API

Pre-1.0, and the same shape #246 already applied to the adjustable stop:

  • OrderRequest::SubmitAdaptive, SubmitAlgo and SubmitWhatIf are removed — use SubmitEx { kind: OrderKind::Adaptive | Algo | WhatIf, tif, attrs }.
  • Context::submit_adaptive, submit_algo and submit_what_if take a time-in-force and an OrderAttrs, as submit_adjustable_stop does.
  • OrderKind is no longer Copy, because the algo parameters it now carries own their strings.

The Client/EClient and Python surfaces are unchanged.

Tests

  • the_algo_order_types_carry_the_attributes_the_caller_set — at the API boundary, where the drop happened: all three types, outside-RTH, parent link, OCA group and a non-DAY tif.
  • adaptive_wire_carries_the_attributes_and_keeps_its_algo_tags
  • algo_wire_carries_the_attributes_and_keeps_its_algo_tags
  • what_if_wire_carries_the_attributes_and_keeps_its_preview_flag

The three wire tests assert on the bytes, because the enum-level tests passed throughout the period the children shipped naked. Each fails by name against a compiling reversion of the production change it covers.

Closes #318.

Test plan

  • Mutation: dropping the caller's attributes and tif on each of the three routing arms fails the_algo_order_types_carry_the_attributes_the_caller_set by name.
  • Mutation: dropping the what-if flag fails what_if_wire_carries_the_attributes_and_keeps_its_preview_flag.
  • Mutation: dropping 18=e fails adaptive_wire_carries_the_attributes_and_keeps_its_algo_tags.
  • The three wire tests assert on the bytes, because the enum-level tests passed throughout the period the children shipped naked.
  • Wire output verified byte-identical to the standalone encoders — including field order, body length and checksum — for adaptive, what-if and all six algo strategies with default attributes.
  • cargo check --offline clean on --lib, --lib --features python, --bins, --examples, and each integration target individually.
  • tests/ib_paper_compat compared against a clean checkout of the base commit — identical sorted diagnostic sets.
  • cargo test --offline --lib — only the two known config::expiry_tests failures, which fail on the base commit for missing legacy tzdata (fixed separately in config: resolve the legacy timezone names IB states its times in (ibx#335) #336).

userFRM and others added 2 commits July 30, 2026 08:53
`build_order_request` returned early into a standalone `SubmitAdjustableStop` request, which bypassed the extended-attributes path every other order type goes through. The dedicated encoder arm emitted neither tag 6107 nor 583 and hard-coded 59=0, so an adjustable stop used as a bracket child shipped unlinked from its parent, outside its OCA group and DAY. The bypass dropped the rest of `OrderAttrs` with them: outside-RTH, hidden, display size, trigger method, conditions and GTD expiry were all silently discarded on this path.

The adjustable stop is now an `OrderKind::AdjustableStop` carried by `SubmitEx`, so it encodes through `send_order_ex` like every other kind and picks up the shared attribute block. The wire layout is unchanged: 40=3 and 99 sit with the other order-type tags, and the 6257/6261/6258/6259 group plus the conditional 6262 and 6260/6269 are appended after 204 and the attribute block, which is where the encoder being replaced put them. Tag order should not carry meaning, but this path had a shipped layout and there was no reason to move it as a side effect. `Context::submit_adjustable_stop` takes `tif` and `attrs` to match the other extended submitters.

`Connection::for_test()` is new test-only plumbing: it hands back the peer socket so a test can assert on the bytes an encoder actually writes. Two regression tests use it: one pins 6107, 583 and 59 for a bracket child, the other pins the conditional 6262/6260/6269, the absence of 6107/583 when no parent or OCA is set, and the relative order of the whole group. The enum-level tests passed unchanged for the whole time the child was shipping naked, which is why these assert on the wire instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`build_order_request` computes the extended-attribute block partway down, and three order types returned before reaching it. Their request variants carried no attribute block at all, so `Order::attrs()` was never consulted on those paths and their encoders emitted none of it.

Setting `outsideRth` on an adaptive, algo or what-if order was accepted by the API and silently ignored — the order went out regular-hours-only with nothing reporting the difference. The same early return bypassed the parent link and the OCA group, so an adaptive or algo order used as a bracket child was submitted unlinked and outside its group, which is what the attribute block exists to prevent. The tif was hard-coded to DAY on all three.

Patching the missing tags into each dedicated encoder would leave the rest of the block — hidden, display size, min quantity, good-after, GTD expiry, discretionary amount, sweep-to-fill, all-or-none, trigger method, cash quantity, conditions — still dropped on those paths, and would put the emission in four places. These route instead, the way the adjustable stop does: `Adaptive`, `Algo` and `WhatIf` are now `OrderKind` variants carried by `SubmitEx`, and the three standalone encoder arms are gone.

Their own tags keep their values and their position — after tag 204 and the attribute block, where the encoders this replaces put them: 18=e and the adaptive priority parameter, the algo strategy with 849 and its parameter pairs, and the what-if flag. A what-if is still tracked under its marker so the response is recognised as a preview.

`OrderKind` is no longer `Copy`, because the algo parameters it now carries own their strings.

`Context::submit_adaptive`, `submit_algo` and `submit_what_if` take a tif and an attribute block, as `submit_adjustable_stop` does.

Closes deepentropy#318.
@userFRM

userFRM commented Aug 26, 2026

Copy link
Copy Markdown
Author

Closing this. It's in #409 along with the rest of the fork, which is easier to take in one piece than sixty separate branches.

@userFRM userFRM closed this Aug 26, 2026
@userFRM
userFRM deleted the fix/algo-orders-bypass-attrs branch August 30, 2026 12:45
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.

orders: adaptive, algo, what-if and adjustable-stop orders silently drop outsideRth, parentId and the OCA group

1 participant