Algostream_backtest.Fill_engineOrder matching against historical data.
This is where the order types acquire behaviour. Nothing else in the tree interpreted time_in_force, evaluated a Stop trigger, or exposed only an Iceberg's display slice — the constructors existed in Order.order_type and were pattern-matched nowhere.
Passive fills are simulated with an explicit queue position. When a resting limit order arrives at the venue, its queue_ahead is seeded from Order_book.depth_at_price ~side ~price — the liquidity already resting at or better than our price. Every subsequent tape print at or through that price decrements it, and we only begin filling once it reaches zero.
That model is approximate in a specific, documented way: depth_at_price is cumulative at or better, so it counts better-priced orders that are not literally in our queue. Since those orders do fill ahead of us anyway, the estimate is conservative in the right direction — it makes maker fills harder, not easier. What it cannot capture is order-by-order priority within a level, which would need L3 data the ingestion layer does not capture.
An iceberg that refreshes a slice goes to the back of the queue — queue_ahead is reseeded from current depth. That is real venue behaviour and it is the main reason naive iceberg simulation overstates fill rates.
A pass over one market record proceeds: not-yet-arrived orders are skipped (latency), stops are evaluated, marketable orders cross, then resting orders advance their queue. TIF is applied last, because IOC and FOK are decisions about what to do with whatever did not fill.
module Order = Algostream_domain_orders.Ordermodule Trade = Algostream_domain_trades.Trademodule Execution_quality = Algostream_order_management.Execution_qualitymodule Action = Algostream_strategy.Actionmodule Event = Algostream_strategy.Eventmodule Rng = Algostream_rng.Rngtype maker_fill_model = | Queue_positionthe default; seeds queue_ahead from book depth and drains it on tape prints
| Touch_crossfill when the market trades through our price, ignoring queue. More optimistic, much cheaper, adequate when the strategy is not queue-sensitive
*)| Optimisticfill the moment the touch reaches our price. An upper bound on achievable passive performance; useful for bracketing, dishonest as a headline
*)type config = {slippage : Slippage.model;latency : Latency.t;maker_fill : maker_fill_model;stop_trigger : stop_trigger_ref;allow_partial : bool;when false, a partially fillable order fills fully or not at all
*)}val default_config : configval create : config:config -> cost:Cost_model.t -> rng:Rng.t -> tval admit :
t ->
now_ns:int64 ->
Action.intent ->
order_id:string ->
decision_price:float ->
Order.orderAccept an intent. Returns the Order.order the engine created, stamped with now_ns as its decision time; it becomes eligible to match only after the outbound latency has elapsed.
val on_market :
t ->
now_ns:int64 ->
Data_source.record ->
ctx:Slippage.market_ctx ->
Event.fill list * Event.t listAdvance matching by one market record. Returns the fills that occurred and any order status changes (expiry, IOC/FOK cancellation). Both are in event time.
val cancel : t -> now_ns:int64 -> client_order_id:string -> unitRequest cancellation. Takes effect after the cancel latency, so a cancel can lose a race with a fill — which is exactly what happens on a real venue.
val working_orders : t -> Order.order listval is_working : t -> client_order_id:string -> boolCancel everything outstanding; used at end of run.
val tca :
t ->
client_order_id:string ->
market_vwap:float ->
Execution_quality.report optionPost-trade TCA for a completed order, via the existing Order_management.Execution_quality.analyze. None if the order never existed or never filled.