Skip to content

orders: send the security type in the spelling the gateway uses (ibx#328) - #346

Closed
userFRM wants to merge 1 commit into
deepentropy:mainfrom
userFRM:fix/submit-contract-identity
Closed

orders: send the security type in the spelling the gateway uses (ibx#328)#346
userFRM wants to merge 1 commit into
deepentropy:mainfrom
userFRM:fix/submit-contract-identity

Conversation

@userFRM

@userFRM userFRM commented Jul 29, 2026

Copy link
Copy Markdown

Summary

Order messages put the API-facing security type on tag 167. The gateway answers every execution report with the wire spelling:

35=D  ...|55=SPY|54=1|38=1|40=1|167=STK|100=BEST|15=USD      ← sent
35=8  ...|150=2|39=2|167=CS|55=SPY|38=1|32=1|31=737.53       ← answered

The rest of the client already converts before sending — the market-data subscribe maps it, and SecurityType::to_fix exists for exactly this. The order path was the one place putting the caller's word on the wire.

Closes #328.

Change

order_routing returns the value every caller writes to tag 167 and nothing else — confirmed across all thirty-eight call sites — so it converts there rather than at each one.

It goes through the existing security-type mapping, which means an unrecognised type is now sent empty rather than passed through as itself. That is what the mapping already documents: an instrument the engine could not classify must not masquerade as something the gateway accepts, because that misrouting is silent (#223).

Why now, if it is latent

Order submission is restricted to stocks and the gateway resolves STK for those, so nothing is broken today. It stops being latent the moment another security type can be submitted, and the fix is one line at one place while that is still true.

Verified against a live session — a limit order carrying 167=CS is accepted exactly as before:

35=D  ...|55=SPY|54=1|38=1|40=2|44=100|59=0|167=CS|100=BEST|6210=BEST|15=USD
[status] oid=9951 PreSubmitted

Not addressed here

The other half of #328: a submit carries no ContractID while a replace does, so the two disagree about how a contract is named. Adding it touches every submit arm and deserves its own change — I have left the issue open for that reason rather than closing it on a half fix.

cargo test --lib: 802 pass. The two config::expiry_tests failures are pre-existing and unrelated (timezone data — fixed separately in #336).

Test plan

  • cargo test --offline --lib — 802 passed. The 2 failures are config::expiry_tests::{named_zone_converts_with_dst, instant_round_trips_to_wire}, which fail on the base commit too: the host has no legacy timezone files (fixed separately in config: resolve the legacy timezone names IB states its times in (ibx#335) #336).
  • cargo check --offline --features python — clean.
  • Mutation check: reverting the wire-spelling conversion fails engine::market_state::tests::order_routing_rules by name.

🤖 Generated with Claude Code

…328)

Order messages put the API-facing security type on tag 167. The gateway answers every execution report with the wire spelling — `167=CS` for a stock, against the `167=STK` that went out — and the rest of the client already converts before sending: the market-data subscribe maps it, and `SecurityType::to_fix` exists for the purpose. The order path was the one place that put the caller's word on the wire.

`order_routing` returns the value every caller writes to tag 167 and nothing else, so it converts there rather than at each of the thirty-eight call sites.

The conversion goes through the existing security-type mapping, which means an unrecognised type is now sent empty rather than passed through as itself. That is the behaviour that mapping already documents: an instrument the engine could not classify must not masquerade as something the gateway will accept, because the misrouting is silent (ibx#223).

This is latent today — order submission is restricted to stocks, and the gateway resolves `STK` for those — and it stops being latent as soon as another security type can be submitted. Verified against a live session: a limit order carrying `167=CS` is accepted as before.

The other half of ibx#328 is not addressed here. A submit still carries no ContractID while a replace does, so the two disagree about how a contract is named; adding it touches every submit arm and is worth its own change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@userFRM
userFRM force-pushed the fix/submit-contract-identity branch from 8dbaacb to 61ae1bd Compare July 30, 2026 07:12
@userFRM

userFRM commented Jul 30, 2026

Copy link
Copy Markdown
Author

Updated in 61ae1bd.

Doc comment corrected. The block above order_routing still said the security type defaults to STK, directly above a function this change makes return CS. The commit had added a second doc block beneath the first rather than editing it, so the function carried two descriptions with the older contradicting the code. Merged into one.

One item left as is. The changed test exercises order_routing directly rather than a serialized 35=D, so a future encoder that stopped writing the result to tag 167 would not be caught. True, but that guards against a caller dropping the helper rather than against this change, and the conversion itself is covered: reverting it fails order_routing_rules with ("STK", "BEST") != ("CS", "BEST"). A full order-serialization test is scope this branch does not need.

Verification beyond the diff:

  • All 39 Submit* variants take the result unmodified for tag 167 — 36 inline, three through the shared encoder — and the same helper feeds tracked replacements, so 38 call sites move together.
  • Round-trip per type: missing, STK and CS all land on CS; OPT, FUT, CASH, IND, BOND and WAR keep their spelling; unknown types stay empty as the mapping already documented.
  • The destination rules are unaffected, which was worth confirming rather than assuming: is_cash compares against the converted value, so it would have broken silently had CASH differed between spellings. It does not, and CASH still selects IDEALPRO, non-cash defaults still route BEST, directed exchanges still pass through.

802 pass plus the two config::expiry_tests timezone failures.

@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/submit-contract-identity 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: the submit path identifies the contract differently from every other path — no ContractID, and STK where the gateway says CS

1 participant