TickDots replays a stream of order events (new, modify, cancel) through a market-by-order book with price-time priority matching. The same input always produces the same fills, the same final book, and the same statistics, on any machine: prices and quantities are integers (ticks and lots), event ordering is explicit, and nothing in the core reads a clock or an unseeded random source.
It is a research and verification tool, not a trading system. There is no exchange connectivity, no live feed, no networking, and no order routing.
- What it does
- Build
- Quick start
- CLI reference
- Input format
- Determinism
- Tests
- Layout
- Limitations
- Version
- License
- Parses one documented CSV event format into normalized events, with one-based line diagnostics for malformed records.
- Maintains a market-by-order book: every order is tracked individually, with price-time priority, partial fills, and cancel/replace modify semantics.
- Replays events in strict mode (first invalid event fails the replay) or lenient mode (invalid events are skipped and counted). CSV parse errors are never skipped.
- Reports execution statistics (fill count, total/buy/sell quantity, min/max price, and an exact integer VWAP kept as a 128-bit numerator over the traded quantity), queue position for a designated order, and derived market-state views (best bid/ask, per-level aggregates).
- Runs sessions under an explicit configuration (instrument, tick size, lot size, matching rule, replay mode, optional PRNG seed, optional latency bounds) that is serialized canonically and identified by a deterministic hash, printed with every successful replay.
- Ships a CLI that replays a CSV file and prints a fixed summary.
Correctness is checked against an independent, scan-based reference model: a generated 6,000-event session is run through both, and the fills are compared field by field after every event, along with the resting order count, best bid/ask, and the execution statistics. The suite also has deterministic property tests and boundary tests at the int64 limits.
Requirements: CMake 3.22 or newer, a C++20 compiler (GCC or Clang), and Git. Building the tests fetches GoogleTest with CMake FetchContent on first configure, so the first configure needs network access. The library and the CLI have no third-party dependencies.
git clone https://github.com/SachuJames/TickDots.git
cd tickdots
cmake --preset default
cmake --build --preset default -j4This produces build/tickdots (the CLI) and build/tickdots_tests.
Other presets: release (optimized build in build-release/) and asan
(Debug with AddressSanitizer and UndefinedBehaviorSanitizer in
build-asan/).
To install just the CLI executable from the release build (headers and libraries are not installed; there is no stable library installation contract yet):
cmake --install build-release --prefix /usr/localThe repository includes a five-event example session in
examples/sample_session.csv: an ask rests, a smaller bid rests, a second
bid crosses the ask for a partial fill, the ask is reduced, and the first
bid is cancelled.
./build/tickdots --input examples/sample_session.csv --instrument AAPLOutput:
TickDots v0.2.0
Input: examples/sample_session.csv
Instrument: AAPL
Replay mode: strict
Events processed: 5
Events skipped: 0
Fills: 1
Resting orders: 1
Best bid: none
Best ask: 105
Config hash: 9ebef60ff75d9b97
Replay status: success
One order is left resting (the ask at 105, reduced to 4 lots), so there is no best bid. The config hash identifies the exact session configuration that produced this run; the same command on any machine prints the same summary.
tickdots --input PATH --instrument SYMBOL [--mode strict|lenient]
[--tick-size VALUE] [--lot-size VALUE] [--seed VALUE]
[--latency-min-ns VALUE] [--latency-max-ns VALUE]
tickdots --help
tickdots --version
| Option | Required | Value |
|---|---|---|
--input PATH |
yes | CSV event file to replay. |
--instrument SYMBOL |
yes | Session instrument, e.g. AAPL. Non-empty, no =, newline, or carriage return. |
--mode |
no | strict (default) or lenient. |
--tick-size VALUE |
no | Tick size as a decimal string, e.g. 0.01. Validated by the session configuration rules. |
--lot-size VALUE |
no | Lot size as a decimal string, e.g. 1. Validated the same way. |
--seed VALUE |
no | PRNG seed, an unsigned 64-bit decimal integer (0 to 18446744073709551615). Stored in the session configuration and covered by its hash. Seeds the latency model when latency bounds are given. |
--latency-min-ns VALUE |
no | Minimum simulated latency in nanoseconds. Needs --latency-max-ns and --seed. |
--latency-max-ns VALUE |
no | Maximum simulated latency in nanoseconds. Needs --latency-min-ns and --seed. |
--help, --version |
no | Print usage or the version and exit 0. Invalid arguments are still errors when combined with these. |
Every option may appear at most once. A successful replay prints the 12-line summary to stdout and nothing to stderr. Failures print one diagnostic to stderr and no summary.
With both latency bounds and a seed, the replay also draws one simulated latency sample per fill from the seeded model and prints them after the summary:
$ ./build/tickdots --input examples/sample_session.csv --instrument AAPL --seed 42 --latency-min-ns 1000 --latency-max-ns 1999
...
Latency model: uniform-latency v1
Latency seed: 42
Latency min (ns): 1000
Latency max (ns): 1999
Latency samples: 1
Latency sample 0 (ns): 1413The same seed, bounds, and input always produce the same samples. The values are simulated for study, not measurements of any real system, and enabling the model does not change the matching results above it.
Exit codes:
| Code | Meaning |
|---|---|
| 0 | Success, --help, or --version. |
| 2 | Invalid arguments: unknown or duplicate option, missing or empty value, missing required option, malformed seed. |
| 3 | The input file could not be opened, or reading it failed. |
| 4 | CSV parsing failed; the diagnostic names the category and the one-based line. |
| 5 | Invalid session configuration, or the replay itself failed (in strict mode the diagnostic names the error and the event position). |
| 6 | An unexpected internal exception. |
The benchmark suite lives in bench/ and builds as
tickdots_bench. It measures deterministic workloads (insertion,
cancellation, modification, matching, a mixed generated session,
the parse and replay pipeline, and latency-observer overhead)
with warm-ups and repeated measurements.
$ cmake --preset release
$ cmake --build --preset release
$ ./build-release/tickdots_bench # full suite, text output
$ ./build-release/tickdots_bench --format csv # one CSV row per benchmark
$ ./build-release/tickdots_bench --workload mixed --size 50000 --repetitions 5Use a Release build for meaningful numbers; a Debug build measures a different program. Results depend on the machine and its load, so compare runs taken on the same machine in the same build configuration, and treat any single run as an observation, not a guarantee. The methodology is documented in ARCHITECTURE.md section 28. Benchmarks measure speed only; correctness is covered by the test suite.
The first line must be exactly this header. Whitespace at the ends of the line, and a trailing carriage return, are trimmed before the comparison; the fields of each record are trimmed the same way.
timestamp,event_type,order_id,side,price,quantity
Each record has six fields:
timestamp: event time in nanoseconds, a non-negative integer.event_type:N(new order),M(modify),C(cancel).order_id: positive integer identifying the order.side:B(bid) orA(ask).price: price in ticks. Required for new orders; empty on a modify means unchanged, empty on a cancel means unused.quantity: quantity in lots. Required for new orders; on a modify,0or empty means unchanged. Reducing the quantity keeps queue priority; changing the price or increasing the quantity is a cancel/replace and the order rejoins at the back of its new level.
Timestamps must never decrease across the file; events that share a timestamp are ordered by their position in the file. A record that breaks the format stops parsing with an error; there is no partial-success parse.
The full contract, including validation rules, matching semantics, the configuration format and its canonical hash, and the aggregate-arithmetic domain, is in SPEC.md. Design notes are in ARCHITECTURE.md.
- Prices and quantities are
int64ticks and lots end to end; no floating point is used in the core. - Events are ordered by
(timestamp, sequence); sequences are assigned densely in input order. - Replaying the same file under the same configuration produces identical output, including the configuration hash.
- Aggregate quantities (total traded lots, per-level totals, quantity
ahead of an order) are exact while their sum fits in
int64; see SPEC.md section 2.4 for the supported domain.
ctest --preset default --output-on-failure
# a small number of stress tests are gated behind an environment flag:
TICKDOTS_EXTENDED=1 ./build/tickdots_testsThe suite covers the event model, book, matching, analytics, CSV parser, session configuration and hashing, replay, the CLI (run as a real subprocess), and the reference-model differentials described above. Formatting is checked with clang-format and static analysis with clang-tidy. CI runs both, plus GCC and Clang Debug and Release builds and the sanitizer build, on every push and pull request.
include/tickdots/ public headers (core, event, book, matching,
replay, analytics, market_data, model)
src/ implementation, plus main.cpp (the CLI)
tests/ GoogleTest suites and the property-test support code
examples/ sample_session.csv, the quick-start session
SPEC.md normative behavior contract
ARCHITECTURE.md design notes and decision records
BENCHMARKING.md benchmarking policy: what a performance claim
has to include before it counts as a result
- One instrument per session; no multi-instrument book.
- Price-time priority is the only matching rule.
- No persistence, no networking, no live data, no concurrency.
- Sessions are bounded by the aggregate domain in SPEC.md section 2.4.
Current version: 0.2.0 (see the project() declaration in
CMakeLists.txt; tickdots --version prints the same value). Changes
are recorded in CHANGELOG.md.
Apache License 2.0. See LICENSE.
