# Order lifecycle tracker

Our gateway sends orders to an exchange and receives execution reports
back. Implement `OrderTracker`, which keeps every order's state and
quantities correct, including when our requests and the exchange's
reports cross each other on the wire. The names follow FIX, simplified;
no FIX knowledge is needed beyond this page.

## Ids

Every request carries a new `clOrdId`. A cancel or replace also names the
order's **current id** in `origClOrdId`: the order's first id, or the id of
its last confirmed replace.

An id is used as soon as the request method returns `true`, even if the
exchange later rejects that request, and it can never be used again. Every
id an order has used identifies that order, both in reports and in `find`.

## Requests we send

Each returns `false` and changes nothing if the request must not be sent.

| Request | Allowed when | Effect |
|---|---|---|
| `newOrder` | id never used; quantity > 0; price > 0 | PendingNew |
| `cancelRequest` | new id never used; `origClOrdId` is the current id; order is New or PartiallyFilled | PendingCancel |
| `replaceRequest` | same as cancel; price > 0; new quantity > filled quantity | PendingReplace |

Only one request can be pending at a time. While a replace is pending the
order keeps its old quantity and price.

## Reports we receive

| Report | Valid in | Result |
|---|---|---|
| New | PendingNew | New |
| Rejected | PendingNew | Rejected |
| Fill | New, PartiallyFilled, PendingCancel, PendingReplace | see below |
| Cancelled | same four states | Cancelled (requested by us or not) |
| Expired | same four states | Expired |
| Replaced | PendingReplace | new quantity and price apply; the request id becomes the current id; see below for the resulting state |
| CancelRejected | PendingCancel, PendingReplace | the request is dropped; PartiallyFilled if anything is filled, otherwise New |

**Fill:** `lastQty` must be positive and at most quantity − filled (the old
quantity while a replace is pending). It adds to `filled` and to
`filledNotional` (lastQty × lastPriceTicks). When filled reaches the
quantity the order is Filled, even if a request was pending. Otherwise a
New order becomes PartiallyFilled and a pending order stays pending.

**Replaced:** fills may have arrived while the replace was pending. If
filled is at or above the new quantity, the order is Filled: `quantity`
takes the new value, `filled` keeps its real value (it may be larger than
`quantity`) and `remaining` is 0. Otherwise the order is PartiallyFilled
if anything is filled, or New.

**A CancelRejected for an order that is already finished** is the
exchange answering a request that arrived too late: ignore it.

## Bad and repeated reports

- A report whose `execId` was already received is a duplicate: ignore it.
- Otherwise, record a `Violation` and leave the order unchanged if:
  - no order uses the `clOrdId` (UnknownOrder),
  - the report is not valid in the order's state (InvalidForState),
  - a valid fill has a bad `lastQty` (InvalidQuantity).
- Keep processing after a violation.

## Queries

`find` returns the order for any of its ids. `remaining` is
quantity − filled, and 0 once the order is finished (Filled, Cancelled,
Rejected, Expired).

## Assumptions

In Fill reports, `lastPriceTicks` is always positive. For valid operations,
all arithmetic, including `lastQty × lastPriceTicks` and the accumulated
`filledNotional`, fits in `std::int64_t`, so no overflow handling is
required. The quantity rules above still apply and must be checked.

You may add private members and helpers. Do not change the public
interface, the types in the header or the tests. Passing all unit tests
is required.
