tickrush 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,73 @@
1
+ name: Build & Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ name: Build wheels (${{ matrix.target }})
13
+ runs-on: ${{ matrix.runner }}
14
+ strategy:
15
+ matrix:
16
+ include:
17
+ - target: x86_64-unknown-linux-gnu
18
+ runner: ubuntu-latest
19
+ manylinux: "2014"
20
+ - target: aarch64-unknown-linux-gnu
21
+ runner: ubuntu-latest
22
+ manylinux: "2014"
23
+ - target: x86_64-apple-darwin
24
+ runner: macos-14
25
+ - target: aarch64-apple-darwin
26
+ runner: macos-14
27
+ - target: x86_64-pc-windows-msvc
28
+ runner: windows-latest
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+
32
+ - uses: PyO3/maturin-action@v1
33
+ with:
34
+ target: ${{ matrix.target }}
35
+ args: --release --out dist --interpreter 3.10 3.11 3.12 3.13
36
+ manylinux: ${{ matrix.manylinux || 'auto' }}
37
+
38
+ - uses: actions/upload-artifact@v4
39
+ with:
40
+ name: wheels-${{ matrix.target }}
41
+ path: dist/*.whl
42
+
43
+ sdist:
44
+ name: Build source distribution
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - uses: actions/checkout@v4
48
+
49
+ - uses: PyO3/maturin-action@v1
50
+ with:
51
+ command: sdist
52
+ args: --out dist
53
+
54
+ - uses: actions/upload-artifact@v4
55
+ with:
56
+ name: wheels-sdist
57
+ path: dist/*.tar.gz
58
+
59
+ publish:
60
+ name: Publish to PyPI
61
+ needs: [build, sdist]
62
+ runs-on: ubuntu-latest
63
+ environment: pypi
64
+ permissions:
65
+ id-token: write
66
+ steps:
67
+ - uses: actions/download-artifact@v4
68
+ with:
69
+ pattern: wheels-*
70
+ merge-multiple: true
71
+ path: dist/
72
+
73
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,18 @@
1
+ # Rust
2
+ target/
3
+
4
+ # Python
5
+ __pycache__/
6
+ *.pyc
7
+ *.egg-info/
8
+ .venv/
9
+
10
+ # IDE
11
+ .idea/
12
+ .vscode/
13
+
14
+ # OS
15
+ .DS_Store
16
+
17
+ # Test cache
18
+ .pytest_cache/
@@ -0,0 +1,283 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "adler2"
7
+ version = "2.0.1"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
10
+
11
+ [[package]]
12
+ name = "autocfg"
13
+ version = "1.5.0"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
16
+
17
+ [[package]]
18
+ name = "cfg-if"
19
+ version = "1.0.4"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
22
+
23
+ [[package]]
24
+ name = "crc32fast"
25
+ version = "1.5.0"
26
+ source = "registry+https://github.com/rust-lang/crates.io-index"
27
+ checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
28
+ dependencies = [
29
+ "cfg-if",
30
+ ]
31
+
32
+ [[package]]
33
+ name = "flate2"
34
+ version = "1.1.9"
35
+ source = "registry+https://github.com/rust-lang/crates.io-index"
36
+ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
37
+ dependencies = [
38
+ "crc32fast",
39
+ "miniz_oxide",
40
+ ]
41
+
42
+ [[package]]
43
+ name = "heck"
44
+ version = "0.5.0"
45
+ source = "registry+https://github.com/rust-lang/crates.io-index"
46
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
47
+
48
+ [[package]]
49
+ name = "indoc"
50
+ version = "2.0.7"
51
+ source = "registry+https://github.com/rust-lang/crates.io-index"
52
+ checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
53
+ dependencies = [
54
+ "rustversion",
55
+ ]
56
+
57
+ [[package]]
58
+ name = "itoa"
59
+ version = "1.0.17"
60
+ source = "registry+https://github.com/rust-lang/crates.io-index"
61
+ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
62
+
63
+ [[package]]
64
+ name = "libc"
65
+ version = "0.2.180"
66
+ source = "registry+https://github.com/rust-lang/crates.io-index"
67
+ checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
68
+
69
+ [[package]]
70
+ name = "memchr"
71
+ version = "2.7.6"
72
+ source = "registry+https://github.com/rust-lang/crates.io-index"
73
+ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
74
+
75
+ [[package]]
76
+ name = "memoffset"
77
+ version = "0.9.1"
78
+ source = "registry+https://github.com/rust-lang/crates.io-index"
79
+ checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
80
+ dependencies = [
81
+ "autocfg",
82
+ ]
83
+
84
+ [[package]]
85
+ name = "miniz_oxide"
86
+ version = "0.8.9"
87
+ source = "registry+https://github.com/rust-lang/crates.io-index"
88
+ checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
89
+ dependencies = [
90
+ "adler2",
91
+ "simd-adler32",
92
+ ]
93
+
94
+ [[package]]
95
+ name = "once_cell"
96
+ version = "1.21.3"
97
+ source = "registry+https://github.com/rust-lang/crates.io-index"
98
+ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
99
+
100
+ [[package]]
101
+ name = "portable-atomic"
102
+ version = "1.13.1"
103
+ source = "registry+https://github.com/rust-lang/crates.io-index"
104
+ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
105
+
106
+ [[package]]
107
+ name = "proc-macro2"
108
+ version = "1.0.106"
109
+ source = "registry+https://github.com/rust-lang/crates.io-index"
110
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
111
+ dependencies = [
112
+ "unicode-ident",
113
+ ]
114
+
115
+ [[package]]
116
+ name = "pyo3"
117
+ version = "0.22.6"
118
+ source = "registry+https://github.com/rust-lang/crates.io-index"
119
+ checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884"
120
+ dependencies = [
121
+ "cfg-if",
122
+ "indoc",
123
+ "libc",
124
+ "memoffset",
125
+ "once_cell",
126
+ "portable-atomic",
127
+ "pyo3-build-config",
128
+ "pyo3-ffi",
129
+ "pyo3-macros",
130
+ "unindent",
131
+ ]
132
+
133
+ [[package]]
134
+ name = "pyo3-build-config"
135
+ version = "0.22.6"
136
+ source = "registry+https://github.com/rust-lang/crates.io-index"
137
+ checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38"
138
+ dependencies = [
139
+ "once_cell",
140
+ "target-lexicon",
141
+ ]
142
+
143
+ [[package]]
144
+ name = "pyo3-ffi"
145
+ version = "0.22.6"
146
+ source = "registry+https://github.com/rust-lang/crates.io-index"
147
+ checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636"
148
+ dependencies = [
149
+ "libc",
150
+ "pyo3-build-config",
151
+ ]
152
+
153
+ [[package]]
154
+ name = "pyo3-macros"
155
+ version = "0.22.6"
156
+ source = "registry+https://github.com/rust-lang/crates.io-index"
157
+ checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453"
158
+ dependencies = [
159
+ "proc-macro2",
160
+ "pyo3-macros-backend",
161
+ "quote",
162
+ "syn",
163
+ ]
164
+
165
+ [[package]]
166
+ name = "pyo3-macros-backend"
167
+ version = "0.22.6"
168
+ source = "registry+https://github.com/rust-lang/crates.io-index"
169
+ checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe"
170
+ dependencies = [
171
+ "heck",
172
+ "proc-macro2",
173
+ "pyo3-build-config",
174
+ "quote",
175
+ "syn",
176
+ ]
177
+
178
+ [[package]]
179
+ name = "quote"
180
+ version = "1.0.44"
181
+ source = "registry+https://github.com/rust-lang/crates.io-index"
182
+ checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
183
+ dependencies = [
184
+ "proc-macro2",
185
+ ]
186
+
187
+ [[package]]
188
+ name = "rustversion"
189
+ version = "1.0.22"
190
+ source = "registry+https://github.com/rust-lang/crates.io-index"
191
+ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
192
+
193
+ [[package]]
194
+ name = "serde"
195
+ version = "1.0.228"
196
+ source = "registry+https://github.com/rust-lang/crates.io-index"
197
+ checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
198
+ dependencies = [
199
+ "serde_core",
200
+ ]
201
+
202
+ [[package]]
203
+ name = "serde_core"
204
+ version = "1.0.228"
205
+ source = "registry+https://github.com/rust-lang/crates.io-index"
206
+ checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
207
+ dependencies = [
208
+ "serde_derive",
209
+ ]
210
+
211
+ [[package]]
212
+ name = "serde_derive"
213
+ version = "1.0.228"
214
+ source = "registry+https://github.com/rust-lang/crates.io-index"
215
+ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
216
+ dependencies = [
217
+ "proc-macro2",
218
+ "quote",
219
+ "syn",
220
+ ]
221
+
222
+ [[package]]
223
+ name = "serde_json"
224
+ version = "1.0.149"
225
+ source = "registry+https://github.com/rust-lang/crates.io-index"
226
+ checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
227
+ dependencies = [
228
+ "itoa",
229
+ "memchr",
230
+ "serde",
231
+ "serde_core",
232
+ "zmij",
233
+ ]
234
+
235
+ [[package]]
236
+ name = "simd-adler32"
237
+ version = "0.3.8"
238
+ source = "registry+https://github.com/rust-lang/crates.io-index"
239
+ checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
240
+
241
+ [[package]]
242
+ name = "syn"
243
+ version = "2.0.114"
244
+ source = "registry+https://github.com/rust-lang/crates.io-index"
245
+ checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
246
+ dependencies = [
247
+ "proc-macro2",
248
+ "quote",
249
+ "unicode-ident",
250
+ ]
251
+
252
+ [[package]]
253
+ name = "target-lexicon"
254
+ version = "0.12.16"
255
+ source = "registry+https://github.com/rust-lang/crates.io-index"
256
+ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
257
+
258
+ [[package]]
259
+ name = "tickrush"
260
+ version = "0.1.0"
261
+ dependencies = [
262
+ "flate2",
263
+ "pyo3",
264
+ "serde_json",
265
+ ]
266
+
267
+ [[package]]
268
+ name = "unicode-ident"
269
+ version = "1.0.22"
270
+ source = "registry+https://github.com/rust-lang/crates.io-index"
271
+ checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
272
+
273
+ [[package]]
274
+ name = "unindent"
275
+ version = "0.2.4"
276
+ source = "registry+https://github.com/rust-lang/crates.io-index"
277
+ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
278
+
279
+ [[package]]
280
+ name = "zmij"
281
+ version = "1.0.19"
282
+ source = "registry+https://github.com/rust-lang/crates.io-index"
283
+ checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445"
@@ -0,0 +1,21 @@
1
+ [package]
2
+ name = "tickrush"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "High-performance Betfair price stream reader"
6
+ license = "MIT"
7
+ readme = "README.md"
8
+
9
+ [lib]
10
+ name = "tickrush"
11
+ crate-type = ["cdylib"]
12
+
13
+ [dependencies]
14
+ pyo3 = { version = "0.22", features = ["extension-module"] }
15
+ serde_json = "1.0"
16
+ flate2 = "1.0"
17
+
18
+ [profile.release]
19
+ lto = true
20
+ codegen-units = 1
21
+ opt-level = 3
tickrush-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Maurice Berk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: tickrush
3
+ Version: 0.1.0
4
+ Classifier: License :: OSI Approved :: MIT License
5
+ Classifier: Programming Language :: Rust
6
+ Classifier: Programming Language :: Python :: 3.10
7
+ Classifier: Programming Language :: Python :: 3.11
8
+ Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ License-File: LICENSE
11
+ Summary: High-performance Betfair price stream reader
12
+ Author-email: Maurice Berk <maurice@mauriceberk.com>
13
+ License: MIT
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
16
+ Project-URL: Repository, https://github.com/mberk/tickrush
17
+
18
+ # tickrush
19
+
20
+ `tickrush` is a performance-first ingestion layer for saved Betfair Exchange price stream data.
21
+ Its sole purpose is to get historical Betfair `MarketBook` updates into `flumine` as fast as possible.
22
+
23
+ ## Installation
24
+
25
+ Initial releases are available from GitHub. A PyPI release will follow shortly.
26
+
27
+ ```
28
+ pip install git+https://github.com/mberk/tickrush.git
29
+ ```
30
+
31
+ Python >=3.10 required.
32
+ A Rust toolchain is required to build the PyO3 extension.
33
+
34
+ ## Usage
35
+
36
+ To use `tickrush` when running a `flumine` simulation simply apply a trivial monkey-patch to `HistoricalStream.create_generator`.
37
+ Here is an illustration using the `flumine` `LowestLayer` example strategy:
38
+
39
+
40
+ ```python
41
+ # Store original create_generator for restoration
42
+ _original_create_generator = HistoricalStream.create_generator
43
+
44
+ # Monkey-patch HistoricalStream to use tickrush
45
+ HistoricalStream.create_generator = tickrush.create_generator
46
+
47
+ client = clients.SimulatedClient()
48
+
49
+ framework = FlumineSimulation(client=client)
50
+
51
+ markets = ["tests/resources/PRO-1.170258213"]
52
+
53
+ strategy = LowestLayer(
54
+ market_filter={"markets": markets},
55
+ max_order_exposure=1000,
56
+ max_selection_exposure=105,
57
+ context={"stake": 2},
58
+ )
59
+ framework.add_strategy(strategy)
60
+
61
+ framework.run()
62
+
63
+ # Restore original
64
+ HistoricalStream.create_generator = _original_create_generator
65
+ ```
66
+
67
+ ## Intent
68
+
69
+ `tickrush` is obsessively focused on minimising the time taken to:
70
+
71
+ - parse archived Betfair stream messages
72
+ - produce `MarketBook` objects consumable by `flumine`
73
+ - access fields on said `MarketBook` objects
74
+
75
+ Everything else is deliberately out of scope.
76
+
77
+ ## Technology
78
+
79
+ - Rust parser exposed via PyO3
80
+ - Low-allocation decoding of Betfair stream messages
81
+ - Efficient runner- and price-level cache reuse
82
+ - Thin Python surface designed to integrate directly with `flumine`
83
+
84
+ ## Roadmap
85
+
86
+ Planned future work includes:
87
+ - A compact proprietary file format for even more efficient parsing
88
+ - Offering a range of stripped down `MarketBook`-like objects to avoid unnecessarily constructing unused fields
89
+ - Switching to a pure C extension to minimise `getter` overhead
90
+
91
+ ## Status
92
+
93
+ This project is under active development and not yet API-stable.
94
+
@@ -0,0 +1,76 @@
1
+ # tickrush
2
+
3
+ `tickrush` is a performance-first ingestion layer for saved Betfair Exchange price stream data.
4
+ Its sole purpose is to get historical Betfair `MarketBook` updates into `flumine` as fast as possible.
5
+
6
+ ## Installation
7
+
8
+ Initial releases are available from GitHub. A PyPI release will follow shortly.
9
+
10
+ ```
11
+ pip install git+https://github.com/mberk/tickrush.git
12
+ ```
13
+
14
+ Python >=3.10 required.
15
+ A Rust toolchain is required to build the PyO3 extension.
16
+
17
+ ## Usage
18
+
19
+ To use `tickrush` when running a `flumine` simulation simply apply a trivial monkey-patch to `HistoricalStream.create_generator`.
20
+ Here is an illustration using the `flumine` `LowestLayer` example strategy:
21
+
22
+
23
+ ```python
24
+ # Store original create_generator for restoration
25
+ _original_create_generator = HistoricalStream.create_generator
26
+
27
+ # Monkey-patch HistoricalStream to use tickrush
28
+ HistoricalStream.create_generator = tickrush.create_generator
29
+
30
+ client = clients.SimulatedClient()
31
+
32
+ framework = FlumineSimulation(client=client)
33
+
34
+ markets = ["tests/resources/PRO-1.170258213"]
35
+
36
+ strategy = LowestLayer(
37
+ market_filter={"markets": markets},
38
+ max_order_exposure=1000,
39
+ max_selection_exposure=105,
40
+ context={"stake": 2},
41
+ )
42
+ framework.add_strategy(strategy)
43
+
44
+ framework.run()
45
+
46
+ # Restore original
47
+ HistoricalStream.create_generator = _original_create_generator
48
+ ```
49
+
50
+ ## Intent
51
+
52
+ `tickrush` is obsessively focused on minimising the time taken to:
53
+
54
+ - parse archived Betfair stream messages
55
+ - produce `MarketBook` objects consumable by `flumine`
56
+ - access fields on said `MarketBook` objects
57
+
58
+ Everything else is deliberately out of scope.
59
+
60
+ ## Technology
61
+
62
+ - Rust parser exposed via PyO3
63
+ - Low-allocation decoding of Betfair stream messages
64
+ - Efficient runner- and price-level cache reuse
65
+ - Thin Python surface designed to integrate directly with `flumine`
66
+
67
+ ## Roadmap
68
+
69
+ Planned future work includes:
70
+ - A compact proprietary file format for even more efficient parsing
71
+ - Offering a range of stripped down `MarketBook`-like objects to avoid unnecessarily constructing unused fields
72
+ - Switching to a pure C extension to minimise `getter` overhead
73
+
74
+ ## Status
75
+
76
+ This project is under active development and not yet API-stable.
@@ -0,0 +1,116 @@
1
+ """
2
+ Benchmark comparing FlumineSimulation baseline (betfairlightweight) vs tickrush.
3
+
4
+ Based on: https://github.com/betcode-org/flumine/blob/master/examples/simulate.py
5
+ """
6
+
7
+ import sys
8
+ import time
9
+ import logging
10
+ from pathlib import Path
11
+
12
+ from pythonjsonlogger import jsonlogger
13
+
14
+ from flumine import FlumineSimulation, clients
15
+ from flumine.streams.historicalstream import HistoricalStream
16
+
17
+ import tickrush
18
+ from lowestlayer import LowestLayer
19
+
20
+
21
+ # Suppress logging to avoid benchmark noise
22
+ logger = logging.getLogger()
23
+ custom_format = "%(asctime) %(levelname) %(message)"
24
+ log_handler = logging.StreamHandler()
25
+ formatter = jsonlogger.JsonFormatter(custom_format)
26
+ formatter.converter = time.gmtime
27
+ log_handler.setFormatter(formatter)
28
+ logger.addHandler(log_handler)
29
+ logger.setLevel(logging.CRITICAL)
30
+
31
+ TEST_FILE = Path(__file__).parent.parent / "tests" / "resources" / "PRO-1.170258213"
32
+
33
+ # Store original create_generator for restoration
34
+ _original_create_generator = HistoricalStream.create_generator
35
+
36
+
37
+ def _tickrush_create_generator(self):
38
+ """Replacement create_generator that uses tickrush."""
39
+ stream_id = self.stream_id
40
+
41
+ def generator():
42
+ for market_book in tickrush.iter_prices_file(str(self.market_filter)):
43
+ market_book.streaming_unique_id = stream_id
44
+ yield [market_book]
45
+
46
+ return generator
47
+
48
+
49
+ def run_simulation(markets, use_tickrush=False):
50
+ """Run a flumine simulation, optionally using tickrush."""
51
+ if use_tickrush:
52
+ HistoricalStream.create_generator = _tickrush_create_generator
53
+ else:
54
+ HistoricalStream.create_generator = _original_create_generator
55
+
56
+ client = clients.SimulatedClient()
57
+ framework = FlumineSimulation(client=client)
58
+
59
+ strategy = LowestLayer(
60
+ market_filter={"markets": markets},
61
+ max_order_exposure=1000,
62
+ max_selection_exposure=105,
63
+ context={"stake": 2},
64
+ )
65
+ framework.add_strategy(strategy)
66
+ framework.run()
67
+
68
+ # Always restore
69
+ HistoricalStream.create_generator = _original_create_generator
70
+ return framework
71
+
72
+
73
+ def get_profit(framework):
74
+ """Get total profit from a simulation run."""
75
+ total = 0.0
76
+ for market in framework.markets:
77
+ total += sum(o.profit for o in market.blotter)
78
+ return total
79
+
80
+
81
+ def main():
82
+ if not TEST_FILE.exists():
83
+ print(f"Test file not found: {TEST_FILE}")
84
+ sys.exit(1)
85
+
86
+ markets = [str(TEST_FILE)]
87
+ print(f"File: {TEST_FILE.name} ({TEST_FILE.stat().st_size / 1024 / 1024:.1f} MB)")
88
+
89
+ # Run baseline
90
+ print("Running baseline (betfairlightweight)...")
91
+ start = time.perf_counter()
92
+ baseline = run_simulation(markets, use_tickrush=False)
93
+ baseline_time = time.perf_counter() - start
94
+ baseline_profit = get_profit(baseline)
95
+ print(f" Time: {baseline_time:.2f}s Profit: {baseline_profit:.2f}")
96
+
97
+ # Run tickrush
98
+ print("Running tickrush...")
99
+ start = time.perf_counter()
100
+ tickrush_fw = run_simulation(markets, use_tickrush=True)
101
+ tickrush_time = time.perf_counter() - start
102
+ tickrush_profit = get_profit(tickrush_fw)
103
+ print(f" Time: {tickrush_time:.2f}s Profit: {tickrush_profit:.2f}")
104
+
105
+ # Results
106
+ speedup = baseline_time / tickrush_time
107
+ profit_match = "MATCH" if abs(baseline_profit - tickrush_profit) < 0.01 else "MISMATCH"
108
+ print()
109
+ print(f"Baseline: {baseline_time:.2f}s")
110
+ print(f"Tickrush: {tickrush_time:.2f}s")
111
+ print(f"Speedup: {speedup:.2f}x")
112
+ print(f"Profit: {profit_match} (baseline={baseline_profit:.2f}, tickrush={tickrush_profit:.2f})")
113
+
114
+
115
+ if __name__ == "__main__":
116
+ main()