{"id":21816965,"url":"https://github.com/yukunj/limitorderbook","last_synced_at":"2026-05-17T15:31:48.708Z","repository":{"id":260943933,"uuid":"879003729","full_name":"YukunJ/LimitOrderBook","owner":"YukunJ","description":"Step-by-step implementation and improvement of the limit order book contest.","archived":false,"fork":false,"pushed_at":"2025-01-13T12:06:44.000Z","size":355,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-23T06:44:53.858Z","etag":null,"topics":["c","cpp","finance","high-frequency-trading","order-book","trading-algorithms"],"latest_commit_sha":null,"homepage":"http://www.quantcup.org/","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/YukunJ.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-10-26T17:39:43.000Z","updated_at":"2025-01-13T12:06:47.000Z","dependencies_parsed_at":"2025-07-23T06:34:27.236Z","dependency_job_id":"24268dfc-d68c-4f28-a5d0-e37b5a018906","html_url":"https://github.com/YukunJ/LimitOrderBook","commit_stats":null,"previous_names":["yukunj/limitorderbook"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/YukunJ/LimitOrderBook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YukunJ%2FLimitOrderBook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YukunJ%2FLimitOrderBook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YukunJ%2FLimitOrderBook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YukunJ%2FLimitOrderBook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/YukunJ","download_url":"https://codeload.github.com/YukunJ/LimitOrderBook/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YukunJ%2FLimitOrderBook/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279015312,"owners_count":26085684,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","cpp","finance","high-frequency-trading","order-book","trading-algorithms"],"created_at":"2024-11-27T15:37:51.255Z","updated_at":"2025-10-13T13:32:43.836Z","avatar_url":"https://github.com/YukunJ.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LimitOrderBook\n\nImplementation of the QuantCup (http://www.quantcup.org/) limit order book contest in vanilia C. We gradually improve its performance via profiling.\n\n## Benchmark\n\nWe take the winning champion solution (by voyager) as the benchmark.\n\n## Iterations\n\nThe first best-effort version I implemented features the following:\n\n* each `price_level_t` has a singly-linked list of `resting_order_t` respect the price-time priority\n* statically allocated all the price level and order entry to avoid dynamic memory allocation\n* best bid price and best ask price helps to determine the side. so `side` could be omitted from the struct\n* cancel is `O(1)` by marking the order size to 0\n\n```\nAcross 10 scoring:\nmy implementation:        mean 5687.13 std 7872.69 score 6779.91\nbenchmark implementation: mean 2777.48 std 807.75  score 1792.62\n```\n\nThe benchmark implementation is twice as fast as this first version of implementation. Further profiling to be made and optimization to be made.\n\n### Profiling 1\n\nThere are only 2 actions in the LimitOrderBook: `limit` or `cancel`. We need to understand which one is the bottleneck between my implementation and the benchmark implementation.\n\n```bash\nMy engine\nlimit() called 35624000 times, average 0.067 us\ncancel() called 35576000 times, average 0.025 us\n\nWinning engine\nlimit() called 35624000 times, average 0.042 us\ncancel() called 35576000 times, average 0.026 us\n```\n\nSo looks like it's the placing a `limit` order that becomes the bottleneck. Among many other metrics I took measurement of, one caught my attention in particular:\n\n```bash\nMy engine\nAmong 35624000 walking through the orderbook to match, average steps 15.585\n\nWinning engine\nAmong 35624000 walking through the orderbook to match, average steps 0.956\n```\n\nMy implementation and the benchmark implementation are more or less the same algorithm and data structure, why in matching an incoming order my implementation would have to walk so many steps into the order list?\n\nA closer look into the benchmark implementation revails that I forgot to \"clear out\" the exhausted order entries from the order list, accumulating a long of zombie order entries of size 0 in the resting order book.\n\naka, this singe line from benchmark implementation is what my implementation is missing.\n\n```c\n    # after finish matching incoming order against resting order book\n    ppEntry-\u003elistHead = bookEntry;\n```\n\nAfter this optimization, my implementation's performance gets closer to the benchmark performance, about 5% inferior to it. So there might be some other places of details that we need to profile and iterate improvement.\n\n```bash\nAcross 10 scoring:\nmy implementation:        mean 2914.82 std 803.91  score 1859.37\nbenchmark implementation: mean 2754.70 std 857.01  score 1805.85\n```\n\n### Profiling 2\n\nSo we continue our optimization. One intriguing point the benchmark implementation mentioned is that \"The memory layout of struct orderBookEntry has been optimized\". In particular, despite the given macro `#define STRINGLEN 5`, the benchmark defines the trader and symbol char buffer of size 4. My first thought: \"Why? Does this really matter?\". \n\nLet dive deep into this and look at the generated assembly code for the part of interests.\n\n```asm\n# engine.c:49:   STR_COPY(o_slot-\u003etrader, order.trader);\n\tmovq\t-16(%rbp), %rax\t# o_slot, tmp109\n\taddq\t$16, %rax\t#, _5\n\tmovl\t21(%rbp), %edx\t# MEM \u003cunsigned char[5]\u003e [(char * {ref-all})\u0026order + 5B], tmp110\n\tmovl\t%edx, (%rax)\t# tmp110, MEM \u003cunsigned char[5]\u003e [(char * {ref-all})_5]\n\tmovzbl\t25(%rbp), %edx\t# MEM \u003cunsigned char[5]\u003e [(char * {ref-all})\u0026order + 5B], tmp111\n\tmovb\t%dl, 4(%rax)\t# tmp111, MEM \u003cunsigned char[5]\u003e [(char * {ref-all})_5]\n```\n\nIt takes 4 steps to copy the 5 bytes from `order.trader` to `o_slot-\u003etrader`.\n\n1. copy first 4 bytes from `order.trader` into 4-byte-long register `%edx`\n2. copy `%edx` to first 4 bytes of `o_slot-\u003etrader`\n3. copy the 5th byte from `order.trader` into the 4-byte-long register `%edx` and zero extend\n4. copy this last byte from lower byte of `%edx` into `o_slot-\u003etrader`'s 5th byte.\n\nNow we can see, the 1 extra byte copying doubles the entire operation cycles required. We could safely discard the null terminator at the fifth poistion and manage the string copy ourselves.\n\nwe set the `t_execution exe` to be a static variable to avoid allocating it on the stack everytime we enter the `execution()` function and ensure it's zero initialized. and then allocate and only copy the first 4 bytes of `trader` and `symbol`, ignoring the null terminator.\n\nwe end up with the new assembly code as follows:\n\n```asm\n# engine.c:50:   STR_COPY(o_slot-\u003etrader, order.trader);\n\tmovq\t-16(%rbp), %rax\t# o_slot, tmp110\n\tleaq\t16(%rax), %rdx\t#, _5\n\tmovl\t21(%rbp), %eax\t# MEM \u003cunsigned int\u003e [(char * {ref-all})\u0026order + 5B], _26\n\tmovl\t%eax, (%rdx)\t# _26, MEM \u003cunsigned int\u003e [(char * {ref-all})_5]\n```\n\nAs we expected, we effectively reduce the 4 steps to 2 steps. With this optimzation, we slightly improve the score from 5% inferior to 4% inferior:\n\n```bash\nAcross 10 scoring:\nmy implementation:        mean 3132.51 std 842.94  score 1987.73\nbenchmark implementation: mean 2984.59 std 887.512  score 1936.05\n```\n\nThere is probably more to be profiled and optimized.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyukunj%2Flimitorderbook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyukunj%2Flimitorderbook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyukunj%2Flimitorderbook/lists"}