Skip to content

ccp: report the time-in-force that was submitted, not a plausible default (ibx#307) - #356

Closed
userFRM wants to merge 1 commit into
deepentropy:mainfrom
userFRM:fix/tif-fallback-unreachable
Closed

ccp: report the time-in-force that was submitted, not a plausible default (ibx#307)#356
userFRM wants to merge 1 commit into
deepentropy:mainfrom
userFRM:fix/tif-fallback-unreachable

Conversation

@userFRM

@userFRM userFRM commented Jul 30, 2026

Copy link
Copy Markdown

Problem

The wire match on tag 59 ended in _ => "DAY", so every path produced a non-empty string and the fallback that knows what this client actually submitted could never run.

An unrecognised code and an absent tag alike were reported as DAY. That is an ordinary value, so a caller reconciling its own orders got a plausible answer that disagreed with what it sent, and nothing said so.

What this changes

An absent tag leaves the string empty, which is what makes the fallback to the tracked order reachable.

A stated but unmapped code is reported as stated. The gateway is authoritative when it says anything, and a code this does not name is still better seen than replaced by an unrelated local value. That mirrors the order-type sibling directly above it.

A recovery record with no tag 59 is recorded as unstated, not guessed. The order was not placed by this session, so there is nothing to recover it from. Either guess becomes a real instruction on the next replace — tag 59 on a 35=G sets what the gateway is holding — and an invented DAY would expire an order that is resting until cancelled. A replace of an order with no stated time-in-force omits tag 59 and leaves the resting value in force.

decode_tif already maps anything it does not recognise to the empty string, so an unstated time-in-force reports as unstated rather than as an ordinary value.

Tests

  • an_unknown_time_in_force_falls_back_to_the_one_that_was_submitted — absence uses the submitted value; a stated code is reported as stated.
  • a_recovered_order_without_a_time_in_force_states_none — the recovered order reports unstated, and a replace of it carries no tag 59.

Both fail by name against a compiling reversion of the production change they cover.

Closes #307.

Test plan

  • Mutation: restoring the recovery insert's b'0' fallback fails a_recovered_order_without_a_time_in_force_states_none by name.
  • Mutation: removing the tag-59 removal on replace fails the same test — the mutant frame carries 59= with the raw sentinel, which is worse than the value it replaced.
  • an_unknown_time_in_force_falls_back_to_the_one_that_was_submitted covers both directions: absence uses the submitted value, a stated code is reported as stated.
  • 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).

…ault

The wire match on tag 59 ended in `_ => "DAY"`, so every path produced a non-empty string and the fallback that knows what this client actually submitted could never run. An unrecognised code and an absent tag alike were reported as DAY — an ordinary value, so a caller reconciling its own orders got a plausible answer that disagreed with what it sent, with nothing to say so.

An absent tag now leaves the string empty, which is what makes the fallback reachable, and a stated but unmapped code is reported as stated: the gateway is authoritative when it says anything, and a code this does not name is still better seen than replaced by an unrelated local value. That mirrors the order-type sibling directly above it.

A recovery record with no tag 59 states no time-in-force, and the order was not placed by this session, so there is nothing to recover it from. It is recorded as unstated rather than guessed. Either guess is restated as a real instruction on the next replace — tag 59 on a `35=G` sets what the gateway is holding — and an invented DAY would expire an order that is resting until cancelled. A replace of an order with no stated time-in-force omits tag 59 and leaves the resting value in force.

`decode_tif` already maps anything it does not recognise to the empty string, so an unstated time-in-force reports as unstated rather than as an ordinary value.

Closes deepentropy#307.
@userFRM
userFRM force-pushed the fix/tif-fallback-unreachable branch from 92afc50 to bde8652 Compare July 30, 2026 15:41
userFRM added a commit to userFRM/ibx that referenced this pull request Aug 3, 2026
… the side guard, and drop tag 59 after the fields are built
@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 added a commit to userFRM/ibx that referenced this pull request Aug 30, 2026
…d TIF with the side guard, and drop tag 59 after the fields are built
@userFRM
userFRM deleted the fix/tif-fallback-unreachable 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.

ccp: an unrecognised or absent time-in-force is reported as DAY, and the tracked value is never consulted

1 participant