Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
712 changes: 712 additions & 0 deletions .dev/bench-report

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions .dev/rs-lint
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@ if [ "$1" = "fix" ] ; then
fi


# without this the script exits with clippy's status alone, so a formatting
# failure in --check mode passes CI silently
set -e

cargo fmt ${FMT_ARG}
cargo clippy ${CLIPPY_ARG}
12 changes: 12 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,15 @@ jobs:

- name: Test
run: make rs-test

- name: Benchmarks
# --test runs each benchmark once instead of measuring it: enough to
# catch a bench that stopped compiling, fast enough for every PR
run: cargo bench --bench arithmetic -- --test

- name: Docs
# warnings are errors here, so a broken intra-doc link fails the build
# rather than shipping a dead link to docs.rs
env:
RUSTDOCFLAGS: -Dwarnings
run: make docs
47 changes: 47 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: pages

# Publishes the benchmark page to GitHub Pages from the committed snapshot in
# docs/bench-data.json. The numbers are measured on a known machine and
# committed deliberately — a shared CI runner varies by more than the
# differences being measured, so nothing here runs `cargo bench`.

on:
push:
branches: [main]
paths:
- docs/bench-data.json
- .dev/bench-report
- .github/workflows/pages.yml
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

# a queued deploy waits for the running one rather than cancelling it, so the
# published page always reflects the last commit rather than the last start
concurrency:
group: pages
cancel-in-progress: false

jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v5

- name: Build the benchmark page
run: .dev/bench-report html --out site/index.html

- uses: actions/configure-pages@v5

- uses: actions/upload-pages-artifact@v3
with:
path: site

- id: deployment
uses: actions/deploy-pages@v4
20 changes: 14 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
name: release

# Triggered by `v*` tags (push one with `make release`). Checks the tag matches
# the version in Cargo.toml, runs lint and tests, publishes the crate to
# crates.io, then extracts the matching section from docs/release-notes.md and
# publishes it as the GitHub Release body. Re-runs idempotently update an
# existing release rather than failing.
# the version in Cargo.toml, runs lint, tests and the doc build, and extracts
# the matching section from docs/release-notes.md - every step that can fail
# runs before the crates.io publish, which cannot be taken back for a version.
# It then publishes the crate and the extracted notes as the GitHub Release
# body. Re-runs idempotently update an existing release rather than failing.

on:
push:
Expand Down Expand Up @@ -55,8 +56,12 @@ jobs:
- name: tests
run: make rs-test

- name: publish to crates.io
run: make rs-publish
- name: build docs
# a rustdoc warning is a broken page on docs.rs, which cannot be
# rebuilt for a version once it is published
env:
RUSTDOCFLAGS: -Dwarnings
run: make docs

- name: Extract release notes
run: |
Expand All @@ -73,6 +78,9 @@ jobs:
echo "--- extracted notes for ${TAG} ---"
cat /tmp/notes.md

- name: publish to crates.io
run: make rs-publish

- name: Publish GitHub Release
run: |
TAG="${GITHUB_REF_NAME}"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
__pycache__

target/
site/
17 changes: 15 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Goal

Troy provides superfast numeric primitives for High Frequency Trading, where every nanosecond counts. Correctness and performance are the top priorities: the crate is a fixed-scale decimal type for fast, exact arithmetic, written in safe Rust (`unsafe` is forbidden).
Troy provides superfast primitives and data structures for High Frequency Trading, where every nanosecond counts. Correctness and performance are the top priorities. The crate is written in safe Rust (`unsafe` is forbidden) and holds two things: `Dec`, a fixed-scale decimal for fast exact arithmetic, and `OrderBook`, a level 2 book built on it.

## Build & Test

Expand All @@ -29,9 +29,22 @@ make rs-test # test with all features
## Benchmarks

```bash
cargo bench # run the criterion benchmarks
make bench # run the criterion benchmarks
make bench-save # run them and record docs/bench-data.json
make bench-page # render that snapshot into site/index.html
make bench-report # open criterion's own report (violin, PDF, sweeps)
```

criterion's report lives in `target/criterion` and is ~30 MB of SVG, so it
stays a local tool. The published page carries the curated summary and the
digit sweeps, both rendered from the snapshot.

`docs/bench-data.json` is a committed snapshot measured on a known machine,
carrying the CPU, rustc version and commit it came from. `.dev/bench-report`
reads criterion's JSON and renders both the terminal table and the page that
`.github/workflows/pages.yml` publishes to GitHub Pages. CI never measures:
a shared runner varies by more than the differences the page reports.

## Alternatives Crates

This crates is design to be fast and efficient for high-frequency trading scenarios, where performance is critical.
Expand Down
76 changes: 75 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 15 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,50 @@ name = "troy"
version = "0.1.0"
edition = "2024"
rust-version = "1.87"
description = "A fixed-scale decimal type for fast, exact arithmetic"
description = "Superfast primitives and data structures for high frequency trading"
repository = "https://github.com/quantmind/troy"
license = "MIT"
readme = "readme.md"
keywords = ["decimal", "fixed-point", "numeric", "finance"]
keywords = ["hft", "trading", "orderbook", "decimal", "low-latency"]
categories = ["mathematics", "data-structures"]
publish = true

[lib]
name = "troy"
path = "src/mod.rs"

# docs.rs builds one page per published version; build it with serde and
# rust_decimal on so the optional conversions are documented
[package.metadata.docs.rs]
all-features = true

[lints.rust]
unsafe_code = "forbid"
missing_debug_implementations = "warn"
missing_docs = "warn"

[lints.clippy]
all = { level = "warn", priority = -1 }

[dependencies]
rand = { version = "0.10.2", optional = true }
rand_distr = { version = "0.6.0", optional = true }
rust_decimal = { version = "1.43", optional = true }
serde = { version = "1.0", optional = true }

[dev-dependencies]
criterion = "0.8"
# also optional dependencies of the `testing` feature; repeated here so the
# crate's own tests can use the harness without enabling it
rand = "0.10.2"
rand_distr = "0.6.0"
fastnum = "0.7.5"
rust_decimal = "1.43"
serde_json = "1.0"

[features]
default = []
testing = ["dep:rand", "dep:rand_distr"]
rust_decimal = ["dep:rust_decimal"]
serde = ["dep:serde"]

Expand Down
20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,26 @@ docs: ## build the rust documentation
docs-open: ## build the rust documentation and open it in the browser
@cargo doc --all-features --no-deps --open

.PHONY: bench
bench: ## run the criterion benchmarks
@cargo bench --bench arithmetic

.PHONY: bench-save
bench-save: ## run the benchmarks and commit the numbers to docs/bench-data.json
@.dev/bench-report save --run

.PHONY: bench-table
bench-table: ## print the benchmark snapshot as a table
@.dev/bench-report table

.PHONY: bench-report
bench-report: ## open criterion's own report: violin, PDF and per-parameter charts
@python3 -c "import webbrowser; webbrowser.open('file://$(PWD)/target/criterion/report/index.html')"

.PHONY: bench-page
bench-page: ## build the benchmark page into site/index.html
@.dev/bench-report html

.PHONY: rs-publish
rs-publish: ## publish the crate to crates.io
@cargo publish --token $(CARGO_REGISTRY_TOKEN)
Expand Down
Loading