├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── benches └── order_benchmark.rs ├── main.rs └── src ├── lib.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.svg 3 | perf.* 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "atty" 7 | version = "0.2.14" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 10 | dependencies = [ 11 | "hermit-abi", 12 | "libc", 13 | "winapi", 14 | ] 15 | 16 | [[package]] 17 | name = "autocfg" 18 | version = "1.0.1" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 21 | 22 | [[package]] 23 | name = "bitflags" 24 | version = "1.3.2" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 27 | 28 | [[package]] 29 | name = "bstr" 30 | version = "0.2.17" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" 33 | dependencies = [ 34 | "lazy_static", 35 | "memchr", 36 | "regex-automata", 37 | "serde", 38 | ] 39 | 40 | [[package]] 41 | name = "bumpalo" 42 | version = "3.7.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" 45 | 46 | [[package]] 47 | name = "cast" 48 | version = "0.2.7" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" 51 | dependencies = [ 52 | "rustc_version", 53 | ] 54 | 55 | [[package]] 56 | name = "cfg-if" 57 | version = "1.0.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 60 | 61 | [[package]] 62 | name = "clap" 63 | version = "2.33.3" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 66 | dependencies = [ 67 | "bitflags", 68 | "textwrap", 69 | "unicode-width", 70 | ] 71 | 72 | [[package]] 73 | name = "criterion" 74 | version = "0.3.5" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" 77 | dependencies = [ 78 | "atty", 79 | "cast", 80 | "clap", 81 | "criterion-plot", 82 | "csv", 83 | "itertools", 84 | "lazy_static", 85 | "num-traits", 86 | "oorandom", 87 | "plotters", 88 | "rayon", 89 | "regex", 90 | "serde", 91 | "serde_cbor", 92 | "serde_derive", 93 | "serde_json", 94 | "tinytemplate", 95 | "walkdir", 96 | ] 97 | 98 | [[package]] 99 | name = "criterion-plot" 100 | version = "0.4.4" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" 103 | dependencies = [ 104 | "cast", 105 | "itertools", 106 | ] 107 | 108 | [[package]] 109 | name = "crossbeam-channel" 110 | version = "0.5.1" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" 113 | dependencies = [ 114 | "cfg-if", 115 | "crossbeam-utils", 116 | ] 117 | 118 | [[package]] 119 | name = "crossbeam-deque" 120 | version = "0.8.1" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" 123 | dependencies = [ 124 | "cfg-if", 125 | "crossbeam-epoch", 126 | "crossbeam-utils", 127 | ] 128 | 129 | [[package]] 130 | name = "crossbeam-epoch" 131 | version = "0.9.5" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" 134 | dependencies = [ 135 | "cfg-if", 136 | "crossbeam-utils", 137 | "lazy_static", 138 | "memoffset", 139 | "scopeguard", 140 | ] 141 | 142 | [[package]] 143 | name = "crossbeam-utils" 144 | version = "0.8.5" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" 147 | dependencies = [ 148 | "cfg-if", 149 | "lazy_static", 150 | ] 151 | 152 | [[package]] 153 | name = "csv" 154 | version = "1.1.6" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" 157 | dependencies = [ 158 | "bstr", 159 | "csv-core", 160 | "itoa", 161 | "ryu", 162 | "serde", 163 | ] 164 | 165 | [[package]] 166 | name = "csv-core" 167 | version = "0.1.10" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" 170 | dependencies = [ 171 | "memchr", 172 | ] 173 | 174 | [[package]] 175 | name = "either" 176 | version = "1.6.1" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 179 | 180 | [[package]] 181 | name = "getrandom" 182 | version = "0.2.3" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 185 | dependencies = [ 186 | "cfg-if", 187 | "libc", 188 | "wasi", 189 | ] 190 | 191 | [[package]] 192 | name = "half" 193 | version = "1.7.1" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" 196 | 197 | [[package]] 198 | name = "hermit-abi" 199 | version = "0.1.19" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 202 | dependencies = [ 203 | "libc", 204 | ] 205 | 206 | [[package]] 207 | name = "itertools" 208 | version = "0.10.1" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" 211 | dependencies = [ 212 | "either", 213 | ] 214 | 215 | [[package]] 216 | name = "itoa" 217 | version = "0.4.8" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 220 | 221 | [[package]] 222 | name = "js-sys" 223 | version = "0.3.55" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" 226 | dependencies = [ 227 | "wasm-bindgen", 228 | ] 229 | 230 | [[package]] 231 | name = "lazy_static" 232 | version = "1.4.0" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 235 | 236 | [[package]] 237 | name = "libc" 238 | version = "0.2.103" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" 241 | 242 | [[package]] 243 | name = "log" 244 | version = "0.4.14" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 247 | dependencies = [ 248 | "cfg-if", 249 | ] 250 | 251 | [[package]] 252 | name = "memchr" 253 | version = "2.4.1" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 256 | 257 | [[package]] 258 | name = "memoffset" 259 | version = "0.6.4" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" 262 | dependencies = [ 263 | "autocfg", 264 | ] 265 | 266 | [[package]] 267 | name = "num-traits" 268 | version = "0.2.14" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 271 | dependencies = [ 272 | "autocfg", 273 | ] 274 | 275 | [[package]] 276 | name = "num_cpus" 277 | version = "1.13.0" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 280 | dependencies = [ 281 | "hermit-abi", 282 | "libc", 283 | ] 284 | 285 | [[package]] 286 | name = "oorandom" 287 | version = "11.1.3" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" 290 | 291 | [[package]] 292 | name = "orderbook" 293 | version = "0.1.0" 294 | dependencies = [ 295 | "criterion", 296 | "rand", 297 | ] 298 | 299 | [[package]] 300 | name = "plotters" 301 | version = "0.3.1" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" 304 | dependencies = [ 305 | "num-traits", 306 | "plotters-backend", 307 | "plotters-svg", 308 | "wasm-bindgen", 309 | "web-sys", 310 | ] 311 | 312 | [[package]] 313 | name = "plotters-backend" 314 | version = "0.3.2" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" 317 | 318 | [[package]] 319 | name = "plotters-svg" 320 | version = "0.3.1" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" 323 | dependencies = [ 324 | "plotters-backend", 325 | ] 326 | 327 | [[package]] 328 | name = "ppv-lite86" 329 | version = "0.2.10" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 332 | 333 | [[package]] 334 | name = "proc-macro2" 335 | version = "1.0.29" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" 338 | dependencies = [ 339 | "unicode-xid", 340 | ] 341 | 342 | [[package]] 343 | name = "quote" 344 | version = "1.0.9" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 347 | dependencies = [ 348 | "proc-macro2", 349 | ] 350 | 351 | [[package]] 352 | name = "rand" 353 | version = "0.8.4" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 356 | dependencies = [ 357 | "libc", 358 | "rand_chacha", 359 | "rand_core", 360 | "rand_hc", 361 | ] 362 | 363 | [[package]] 364 | name = "rand_chacha" 365 | version = "0.3.1" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 368 | dependencies = [ 369 | "ppv-lite86", 370 | "rand_core", 371 | ] 372 | 373 | [[package]] 374 | name = "rand_core" 375 | version = "0.6.3" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 378 | dependencies = [ 379 | "getrandom", 380 | ] 381 | 382 | [[package]] 383 | name = "rand_hc" 384 | version = "0.3.1" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 387 | dependencies = [ 388 | "rand_core", 389 | ] 390 | 391 | [[package]] 392 | name = "rayon" 393 | version = "1.5.1" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" 396 | dependencies = [ 397 | "autocfg", 398 | "crossbeam-deque", 399 | "either", 400 | "rayon-core", 401 | ] 402 | 403 | [[package]] 404 | name = "rayon-core" 405 | version = "1.9.1" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" 408 | dependencies = [ 409 | "crossbeam-channel", 410 | "crossbeam-deque", 411 | "crossbeam-utils", 412 | "lazy_static", 413 | "num_cpus", 414 | ] 415 | 416 | [[package]] 417 | name = "regex" 418 | version = "1.5.4" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 421 | dependencies = [ 422 | "regex-syntax", 423 | ] 424 | 425 | [[package]] 426 | name = "regex-automata" 427 | version = "0.1.10" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 430 | 431 | [[package]] 432 | name = "regex-syntax" 433 | version = "0.6.25" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 436 | 437 | [[package]] 438 | name = "rustc_version" 439 | version = "0.4.0" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 442 | dependencies = [ 443 | "semver", 444 | ] 445 | 446 | [[package]] 447 | name = "ryu" 448 | version = "1.0.5" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 451 | 452 | [[package]] 453 | name = "same-file" 454 | version = "1.0.6" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 457 | dependencies = [ 458 | "winapi-util", 459 | ] 460 | 461 | [[package]] 462 | name = "scopeguard" 463 | version = "1.1.0" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 466 | 467 | [[package]] 468 | name = "semver" 469 | version = "1.0.4" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" 472 | 473 | [[package]] 474 | name = "serde" 475 | version = "1.0.130" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 478 | 479 | [[package]] 480 | name = "serde_cbor" 481 | version = "0.11.2" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" 484 | dependencies = [ 485 | "half", 486 | "serde", 487 | ] 488 | 489 | [[package]] 490 | name = "serde_derive" 491 | version = "1.0.130" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" 494 | dependencies = [ 495 | "proc-macro2", 496 | "quote", 497 | "syn", 498 | ] 499 | 500 | [[package]] 501 | name = "serde_json" 502 | version = "1.0.68" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" 505 | dependencies = [ 506 | "itoa", 507 | "ryu", 508 | "serde", 509 | ] 510 | 511 | [[package]] 512 | name = "syn" 513 | version = "1.0.77" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0" 516 | dependencies = [ 517 | "proc-macro2", 518 | "quote", 519 | "unicode-xid", 520 | ] 521 | 522 | [[package]] 523 | name = "textwrap" 524 | version = "0.11.0" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 527 | dependencies = [ 528 | "unicode-width", 529 | ] 530 | 531 | [[package]] 532 | name = "tinytemplate" 533 | version = "1.2.1" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 536 | dependencies = [ 537 | "serde", 538 | "serde_json", 539 | ] 540 | 541 | [[package]] 542 | name = "unicode-width" 543 | version = "0.1.9" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 546 | 547 | [[package]] 548 | name = "unicode-xid" 549 | version = "0.2.2" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 552 | 553 | [[package]] 554 | name = "walkdir" 555 | version = "2.3.2" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" 558 | dependencies = [ 559 | "same-file", 560 | "winapi", 561 | "winapi-util", 562 | ] 563 | 564 | [[package]] 565 | name = "wasi" 566 | version = "0.10.2+wasi-snapshot-preview1" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 569 | 570 | [[package]] 571 | name = "wasm-bindgen" 572 | version = "0.2.78" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" 575 | dependencies = [ 576 | "cfg-if", 577 | "wasm-bindgen-macro", 578 | ] 579 | 580 | [[package]] 581 | name = "wasm-bindgen-backend" 582 | version = "0.2.78" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" 585 | dependencies = [ 586 | "bumpalo", 587 | "lazy_static", 588 | "log", 589 | "proc-macro2", 590 | "quote", 591 | "syn", 592 | "wasm-bindgen-shared", 593 | ] 594 | 595 | [[package]] 596 | name = "wasm-bindgen-macro" 597 | version = "0.2.78" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" 600 | dependencies = [ 601 | "quote", 602 | "wasm-bindgen-macro-support", 603 | ] 604 | 605 | [[package]] 606 | name = "wasm-bindgen-macro-support" 607 | version = "0.2.78" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" 610 | dependencies = [ 611 | "proc-macro2", 612 | "quote", 613 | "syn", 614 | "wasm-bindgen-backend", 615 | "wasm-bindgen-shared", 616 | ] 617 | 618 | [[package]] 619 | name = "wasm-bindgen-shared" 620 | version = "0.2.78" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" 623 | 624 | [[package]] 625 | name = "web-sys" 626 | version = "0.3.55" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" 629 | dependencies = [ 630 | "js-sys", 631 | "wasm-bindgen", 632 | ] 633 | 634 | [[package]] 635 | name = "winapi" 636 | version = "0.3.9" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 639 | dependencies = [ 640 | "winapi-i686-pc-windows-gnu", 641 | "winapi-x86_64-pc-windows-gnu", 642 | ] 643 | 644 | [[package]] 645 | name = "winapi-i686-pc-windows-gnu" 646 | version = "0.4.0" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 649 | 650 | [[package]] 651 | name = "winapi-util" 652 | version = "0.1.5" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 655 | dependencies = [ 656 | "winapi", 657 | ] 658 | 659 | [[package]] 660 | name = "winapi-x86_64-pc-windows-gnu" 661 | version = "0.4.0" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 664 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "orderbook" 3 | version = "0.1.0" 4 | edition = "2018" 5 | [lib] 6 | name = "orderbooklib" 7 | path = "src/lib.rs" 8 | 9 | [[bin]] 10 | name = "orderbook-bin" 11 | path = "src/main.rs" 12 | 13 | [profile.release] 14 | lto = true 15 | [profile.bench] 16 | debug = true 17 | 18 | [dependencies] 19 | rand = "0.8.4" 20 | 21 | [dev-dependencies] 22 | criterion = "0.3.5" 23 | 24 | [[bench]] 25 | name = "order_benchmark" 26 | harness = false 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Orderbook. 2 | Limit Orderbook written in Rust. 3 | Only supports limit orders. Price time priority. 4 | In an orderbook with 1 million orders, can match a single order in about 10 microseconds. 5 | 6 | For more info, look at my blog post here: 7 | [Porting a Python Limit Orderbook to Rust](https://sanket.tech/posts/rustbook) 8 | 9 | -------------------------------------------------------------------------------- /benches/order_benchmark.rs: -------------------------------------------------------------------------------- 1 | use core::time::Duration; 2 | use criterion::{criterion_group, criterion_main, Criterion, SamplingMode}; 3 | use orderbooklib::{OrderBook, Side}; 4 | use rand::Rng; 5 | use rand_distr::{Distribution, Normal}; 6 | 7 | fn initialize_orderbook( 8 | num_orders: i32, 9 | rng: &mut rand::prelude::ThreadRng, 10 | normal: Normal, 11 | ) -> OrderBook { 12 | let mut ob = OrderBook::new("Random".to_string()); 13 | for _ in 0..num_orders { 14 | if rng.gen_bool(0.5) { 15 | ob.add_limit_order(Side::Bid, normal.sample(rng) as u64, rng.gen_range(1..=500)); 16 | } else { 17 | ob.add_limit_order(Side::Ask, normal.sample(rng) as u64, rng.gen_range(1..=500)); 18 | } 19 | } 20 | ob 21 | } 22 | 23 | fn match_orders(ob: &mut OrderBook, rng: &mut rand::prelude::ThreadRng, normal: Normal) { 24 | for _ in 0..10000 { 25 | let _fr = ob.add_limit_order(Side::Ask, normal.sample(rng) as u64, rng.gen_range(1..=500)); 26 | } 27 | //ob.get_bbo(); 28 | } 29 | pub fn criterion_benchmark(c: &mut Criterion) { 30 | let mut rng: rand::prelude::ThreadRng = rand::thread_rng(); 31 | let normal = Normal::new(5000.0, 500.0).unwrap(); 32 | let mut ob = initialize_orderbook(100_000, &mut rng, normal); 33 | let mut group = c.benchmark_group("order-benchmark"); 34 | group.sample_size(10); 35 | group.measurement_time(Duration::new(20, 0)); 36 | group.bench_function("Match 100k orders", |b| { 37 | b.iter(|| initialize_orderbook(1_000_00, &mut rng, normal)) 38 | }); 39 | /* 40 | group.bench_function("match 10000 orders on orderbook with 100k orders", |b| { 41 | b.iter(|| match_orders(&mut ob, &mut rng, normal)) 42 | }); 43 | */ 44 | group.finish(); 45 | } 46 | 47 | criterion_group!(benches, criterion_benchmark); 48 | criterion_main!(benches); 49 | -------------------------------------------------------------------------------- /main.rs: -------------------------------------------------------------------------------- 1 | use orderbooklib::{dbgp, OrderBook, OrderStatus, Side}; 2 | use rand::Rng; 3 | fn main() { 4 | println!("Creating new Orderbook"); 5 | let mut ob = OrderBook::new("BTC".to_string()); 6 | let mut rng = rand::thread_rng(); 7 | for _ in 1..100000 { 8 | ob.add_limit_order(Side::Bid, rng.gen_range(1..5000), rng.gen_range(1..=500)); 9 | } 10 | //dbgp!("{:#?}", ob); 11 | println!("Done adding orders, Starting to fill"); 12 | 13 | for _ in 1..10 { 14 | for _ in 1..10000 { 15 | let fr = ob.add_limit_order(Side::Ask, rng.gen_range(1..5000), rng.gen_range(1..=500)); 16 | // if matches! {fr.status, OrderStatus::Filled} { 17 | // dbgp!("{:#?}, avg_fill_price {}", fr, fr.avg_fill_price()); 18 | // } 19 | } 20 | 21 | } 22 | println!("Done!"); 23 | ob.get_bbo(); 24 | } 25 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use rand::Rng; 2 | use std::collections::{BTreeMap, HashMap, VecDeque}; 3 | 4 | // Change BTreeMap to Vec? 5 | // Tests 6 | // README/Blog 7 | 8 | // Removed from Open source version 9 | // Price qty serialize 10 | // Deserialize Binance/FTX api endpoints. 11 | // Log list of trades done. 12 | #[macro_export] 13 | macro_rules! dbgp { 14 | ($($arg:tt)*) => (#[cfg(debug_assertions)] println!($($arg)*)); 15 | } 16 | #[derive(Debug)] 17 | pub enum Side { 18 | Bid, 19 | Ask, 20 | } 21 | 22 | #[derive(Debug)] 23 | pub enum OrderStatus { 24 | Uninitialized, 25 | Created, 26 | Filled, 27 | PartiallyFilled, 28 | } 29 | #[derive(Debug)] 30 | pub struct FillResult { 31 | // Orders filled (qty, price) 32 | pub filled_orders: Vec<(u64, u64)>, 33 | pub remaining_qty: u64, 34 | pub status: OrderStatus, 35 | } 36 | 37 | impl FillResult { 38 | fn new() -> Self { 39 | FillResult { 40 | filled_orders: Vec::new(), 41 | remaining_qty: u64::MAX, 42 | status: OrderStatus::Uninitialized, 43 | } 44 | } 45 | 46 | pub fn avg_fill_price(&self) -> f32 { 47 | let mut total_price_paid = 0; 48 | let mut total_qty = 0; 49 | for (q, p) in &self.filled_orders { 50 | total_price_paid += p * q; 51 | total_qty += q; 52 | } 53 | return total_price_paid as f32 / total_qty as f32; 54 | } 55 | } 56 | 57 | #[derive(Debug)] 58 | pub struct Order { 59 | pub order_id: u64, 60 | pub qty: u64, 61 | } 62 | #[derive(Debug)] 63 | struct HalfBook { 64 | s: Side, 65 | price_map: BTreeMap, 66 | price_levels: Vec>, 67 | } 68 | 69 | impl HalfBook { 70 | pub fn new(s: Side) -> Self { 71 | HalfBook { 72 | s, 73 | price_map: BTreeMap::new(), 74 | price_levels: Vec::with_capacity(50_000), 75 | } 76 | } 77 | 78 | pub fn get_total_qty(&self, price: u64) -> u64 { 79 | self.price_levels[self.price_map[&price]] 80 | .iter() 81 | .map(|s| s.qty) 82 | .sum() 83 | } 84 | } 85 | 86 | // TODO: Make bid and offer price Option types 87 | #[derive(Debug)] 88 | pub struct OrderBook { 89 | symbol: String, 90 | best_bid_price: u64, 91 | best_offer_price: u64, 92 | bid_book: HalfBook, 93 | ask_book: HalfBook, 94 | // For fast cancels Order id -> (Side, Price_level) 95 | order_loc: HashMap, 96 | } 97 | 98 | impl OrderBook { 99 | pub fn new(symbol: String) -> Self { 100 | OrderBook { 101 | symbol, 102 | best_bid_price: u64::MIN, 103 | best_offer_price: u64::MAX, 104 | bid_book: HalfBook::new(Side::Bid), 105 | ask_book: HalfBook::new(Side::Ask), 106 | order_loc: HashMap::with_capacity(50_000), 107 | } 108 | } 109 | 110 | pub fn cancel_order(&mut self, order_id: u64) -> Result<&str, &str> { 111 | if let Some((side, price_level)) = self.order_loc.get(&order_id) { 112 | let currdeque = match side { 113 | Side::Bid => self.bid_book.price_levels.get_mut(*price_level).unwrap(), 114 | Side::Ask => self.ask_book.price_levels.get_mut(*price_level).unwrap(), 115 | }; 116 | currdeque.retain(|x| x.order_id != order_id); 117 | self.order_loc.remove(&order_id); 118 | Ok("Successfully cancelled order") 119 | } else { 120 | Err("No such order id") 121 | } 122 | } 123 | 124 | fn create_new_limit_order(&mut self, s: Side, price: u64, qty: u64) -> u64 { 125 | let mut rng = rand::thread_rng(); 126 | let order_id: u64 = rng.gen(); 127 | let book = match s { 128 | Side::Ask => &mut self.ask_book, 129 | Side::Bid => &mut self.bid_book, 130 | }; 131 | let order = Order { order_id, qty }; 132 | 133 | if let Some(val) = book.price_map.get(&price) { 134 | book.price_levels[*val].push_back(order); 135 | self.order_loc.insert(order_id, (s, *val)); 136 | } else { 137 | let new_loc = book.price_levels.len(); 138 | book.price_map.insert(price, new_loc); 139 | let mut vec_deq = VecDeque::new(); 140 | vec_deq.push_back(order); 141 | book.price_levels.push(vec_deq); 142 | self.order_loc.insert(order_id, (s, new_loc)); 143 | } 144 | order_id 145 | } 146 | 147 | fn update_bbo(&mut self) { 148 | for (p, u) in self.bid_book.price_map.iter().rev() { 149 | if !self.bid_book.price_levels[*u].is_empty() { 150 | self.best_bid_price = *p; 151 | break; 152 | } 153 | } 154 | for (p, u) in self.ask_book.price_map.iter() { 155 | if !self.ask_book.price_levels[*u].is_empty() { 156 | self.best_offer_price = *p; 157 | break; 158 | } 159 | } 160 | } 161 | 162 | pub fn add_limit_order(&mut self, s: Side, price: u64, order_qty: u64) -> FillResult { 163 | fn match_at_price_level( 164 | price_level: &mut VecDeque, 165 | incoming_order_qty: &mut u64, 166 | order_loc: &mut HashMap, 167 | ) -> u64 { 168 | let mut done_qty = 0; 169 | for o in price_level.iter_mut() { 170 | if o.qty <= *incoming_order_qty { 171 | *incoming_order_qty -= o.qty; 172 | done_qty += o.qty; 173 | o.qty = 0; 174 | order_loc.remove(&o.order_id); 175 | } else { 176 | o.qty -= *incoming_order_qty; 177 | done_qty += *incoming_order_qty; 178 | *incoming_order_qty = 0; 179 | } 180 | } 181 | price_level.retain(|x| x.qty != 0); 182 | done_qty 183 | } 184 | 185 | let mut remaining_order_qty = order_qty; 186 | dbgp!( 187 | "Got order with qty {}, at price {}", 188 | remaining_order_qty, 189 | price 190 | ); 191 | let mut fill_result = FillResult::new(); 192 | match s { 193 | Side::Bid => { 194 | let askbook = &mut self.ask_book; 195 | let price_map = &mut askbook.price_map; 196 | let price_levels = &mut askbook.price_levels; 197 | let mut price_map_iter = price_map.iter(); 198 | 199 | if let Some((mut x, _)) = price_map_iter.next() { 200 | while price >= *x { 201 | let curr_level = price_map[x]; 202 | let matched_qty = match_at_price_level( 203 | &mut price_levels[curr_level], 204 | &mut remaining_order_qty, 205 | &mut self.order_loc, 206 | ); 207 | if matched_qty != 0 { 208 | dbgp!("Matched {} qty at level {}", matched_qty, x); 209 | fill_result.filled_orders.push((matched_qty, *x)); 210 | } 211 | if let Some((a, _)) = price_map_iter.next() { 212 | x = a; 213 | } else { 214 | break; 215 | } 216 | } 217 | } 218 | } 219 | Side::Ask => { 220 | let bidbook = &mut self.bid_book; 221 | let price_map = &mut bidbook.price_map; 222 | let price_levels = &mut bidbook.price_levels; 223 | let mut price_map_iter = price_map.iter(); 224 | 225 | if let Some((mut x, _)) = price_map_iter.next_back() { 226 | while price <= *x { 227 | let curr_level = price_map[x]; 228 | let matched_qty = match_at_price_level( 229 | &mut price_levels[curr_level], 230 | &mut remaining_order_qty, 231 | &mut self.order_loc, 232 | ); 233 | if matched_qty != 0 { 234 | dbgp!("Matched {} qty at level {}", matched_qty, x); 235 | fill_result.filled_orders.push((matched_qty, *x)); 236 | } 237 | if let Some((a, _)) = price_map_iter.next_back() { 238 | x = a; 239 | } else { 240 | break; 241 | } 242 | } 243 | } 244 | } 245 | } 246 | fill_result.remaining_qty = remaining_order_qty; 247 | if remaining_order_qty != 0 { 248 | dbgp!( 249 | "Still remaining qty {} at price level {}", 250 | remaining_order_qty, 251 | price 252 | ); 253 | if remaining_order_qty == order_qty { 254 | fill_result.status = OrderStatus::Created; 255 | } else { 256 | fill_result.status = OrderStatus::PartiallyFilled; 257 | } 258 | self.create_new_limit_order(s, price, remaining_order_qty); 259 | } else { 260 | fill_result.status = OrderStatus::Filled; 261 | } 262 | self.update_bbo(); 263 | 264 | fill_result 265 | } 266 | 267 | pub fn get_bbo(&self) { 268 | let total_bid_qty = self.bid_book.get_total_qty(self.best_bid_price); 269 | let total_ask_qty = self.ask_book.get_total_qty(self.best_offer_price); 270 | 271 | println!("Best bid {}, qty {}", self.best_bid_price, total_bid_qty); 272 | println!("Best ask {}, qty {}", self.best_offer_price, total_ask_qty); 273 | println!( 274 | "Spread is {:.6},", 275 | ((self.best_offer_price - self.best_bid_price) as f64 / self.best_offer_price as f64) 276 | as f32 277 | ); 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use orderbooklib::{dbgp, OrderBook, OrderStatus, Side}; 2 | use rand::Rng; 3 | fn main() { 4 | println!("Creating new Orderbook"); 5 | let mut ob = OrderBook::new("BTC".to_string()); 6 | let mut rng = rand::thread_rng(); 7 | for _ in 1..100000 { 8 | ob.add_limit_order(Side::Bid, rng.gen_range(1..5000), rng.gen_range(1..=500)); 9 | } 10 | //dbgp!("{:#?}", ob); 11 | println!("Done adding orders, Starting to fill"); 12 | 13 | for _ in 1..10 { 14 | for _ in 1..10000 { 15 | let fr = ob.add_limit_order(Side::Ask, rng.gen_range(1..5000), rng.gen_range(1..=500)); 16 | // if matches! {fr.status, OrderStatus::Filled} { 17 | // dbgp!("{:#?}, avg_fill_price {}", fr, fr.avg_fill_price()); 18 | // } 19 | } 20 | 21 | } 22 | println!("Done!"); 23 | ob.get_bbo(); 24 | } 25 | --------------------------------------------------------------------------------