racinglines 1.0.0__py3-none-any.whl

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.
Files changed (349) hide show
  1. racinglines/__init__.py +10 -0
  2. racinglines/__main__.py +5 -0
  3. racinglines/_bundled/exchanges/og.toml +188 -0
  4. racinglines/_bundled/markets/kinds.toml +567 -0
  5. racinglines/_bundled/migrations/env.py +38 -0
  6. racinglines/_bundled/migrations/script.py.mako +24 -0
  7. racinglines/_bundled/migrations/versions/20260926_0e47bb766d0b_house_markets_and_bets.py +68 -0
  8. racinglines/_bundled/migrations/versions/20260926_1983462a0695_initial_schema.py +258 -0
  9. racinglines/_bundled/migrations/versions/20260926_3f94d44a125e_market_trades_and_book_snapshots.py +57 -0
  10. racinglines/_bundled/migrations/versions/20260926_50a176ca2955_market_price_history.py +33 -0
  11. racinglines/_bundled/migrations/versions/20260926_66d4b92ba307_market_links_and_orders.py +79 -0
  12. racinglines/_bundled/migrations/versions/20260926_81022900a936_jobs.py +45 -0
  13. racinglines/_bundled/migrations/versions/20260926_8c46493d7cda_house_market_uniqueness_per_mirrored_.py +30 -0
  14. racinglines/_bundled/migrations/versions/20260926_8d64d59a2940_users_roles_activity_log.py +80 -0
  15. racinglines/_bundled/migrations/versions/20260926_d84cd5aa7ce4_polymarket_alignment.py +83 -0
  16. racinglines/_bundled/migrations/versions/20260926_f3e017071b76_laps_track_profiles_round_extra.py +64 -0
  17. racinglines/_bundled/migrations/versions/20260927_5b1e0c7d2a41_user_prefs.py +24 -0
  18. racinglines/_bundled/migrations/versions/20260927_9c4d2e8f1a63_market_first_seen.py +23 -0
  19. racinglines/_bundled/migrations/versions/20260927_c7a1f4e2b9d0_strategy_signals.py +79 -0
  20. racinglines/_bundled/migrations/versions/20260927_d2b8e5a1c3f7_replay_taker_account.py +35 -0
  21. racinglines/_bundled/migrations/versions/20260927_e4c1a9b7d205_paper_position_venue.py +23 -0
  22. racinglines/_bundled/migrations/versions/20260929_a3f5c8d1e7b2_live_events.py +40 -0
  23. racinglines/_bundled/migrations/versions/20260929_b7d2e4f1a9c3_data_changes.py +33 -0
  24. racinglines/_bundled/migrations/versions/20260929_c8e3f6a2d4b1_market_disagreements.py +55 -0
  25. racinglines/_bundled/migrations/versions/20261002_d9f2a7e5c1b3_user_email_prefs.py +40 -0
  26. racinglines/_bundled/migrations/versions/20261002_d9f4b1c7e5a2_user_mcp_tokens.py +24 -0
  27. racinglines/_bundled/migrations/versions/20261003_f8a2c4e7b5d1_fastf1_snapshots.py +59 -0
  28. racinglines/_bundled/migrations/versions/20261005_a7e3c1f9b4d2_kalshi_candlestick_bid_ask.py +34 -0
  29. racinglines/_bundled/sports/f1/calibration.toml +241 -0
  30. racinglines/_bundled/sports/f1/fastest_lap.toml +72 -0
  31. racinglines/_bundled/sports/f1/grid_penalties.toml +24 -0
  32. racinglines/_bundled/sports/f1.toml +228 -0
  33. racinglines/_bundled/sports/mtb_dh.toml +161 -0
  34. racinglines/_bundled/sports/points/uci_dhi_wc.toml +57 -0
  35. racinglines/attribution.py +137 -0
  36. racinglines/books/__init__.py +3 -0
  37. racinglines/books/ledger.py +138 -0
  38. racinglines/books/picks.py +132 -0
  39. racinglines/books/render.css +85 -0
  40. racinglines/books/render.py +650 -0
  41. racinglines/books/schema.py +394 -0
  42. racinglines/books/slips.py +561 -0
  43. racinglines/cli/__init__.py +120 -0
  44. racinglines/cli/backtest.py +197 -0
  45. racinglines/cli/book.py +432 -0
  46. racinglines/cli/check.py +44 -0
  47. racinglines/cli/config.py +57 -0
  48. racinglines/cli/db.py +204 -0
  49. racinglines/cli/f1.py +1302 -0
  50. racinglines/cli/live.py +555 -0
  51. racinglines/cli/markets.py +337 -0
  52. racinglines/cli/mtb_dh.py +502 -0
  53. racinglines/cli/replay_cmd.py +405 -0
  54. racinglines/cli/rich_view.py +130 -0
  55. racinglines/cli/source.py +274 -0
  56. racinglines/cli/tty.py +115 -0
  57. racinglines/cli/users.py +141 -0
  58. racinglines/cli/verbs.py +217 -0
  59. racinglines/cli/web.py +34 -0
  60. racinglines/core/__init__.py +1 -0
  61. racinglines/core/calibration.py +60 -0
  62. racinglines/core/stages.py +54 -0
  63. racinglines/core/stats.py +34 -0
  64. racinglines/core/walk_forward.py +103 -0
  65. racinglines/db/__init__.py +0 -0
  66. racinglines/db/changes.py +51 -0
  67. racinglines/db/config.py +33 -0
  68. racinglines/db/ingest.py +194 -0
  69. racinglines/db/mirror.py +433 -0
  70. racinglines/db/models.py +654 -0
  71. racinglines/db/pull.py +336 -0
  72. racinglines/db/queries.py +183 -0
  73. racinglines/db/reads.py +335 -0
  74. racinglines/db/records.py +61 -0
  75. racinglines/db/registry.py +41 -0
  76. racinglines/db/schedule.py +142 -0
  77. racinglines/db/snapshot.py +71 -0
  78. racinglines/demo/__init__.py +0 -0
  79. racinglines/demo/mk_demo/__init__.py +2 -0
  80. racinglines/demo/mk_demo/generate.py +513 -0
  81. racinglines/demo/packs/kart/book.toml +147 -0
  82. racinglines/demo/packs/kart/files/live/f1/demo-2026-09.toml +35 -0
  83. racinglines/demo/packs/kart/pack.toml +72 -0
  84. racinglines/demo/packs/kart/world.toml +336 -0
  85. racinglines/demo/store.py +279 -0
  86. racinglines/exchanges.py +78 -0
  87. racinglines/fantasy/__init__.py +8 -0
  88. racinglines/fantasy/exchange.py +653 -0
  89. racinglines/fantasy/follow.py +287 -0
  90. racinglines/fantasy/ledger.py +313 -0
  91. racinglines/fantasy/sizing.py +110 -0
  92. racinglines/frames/__init__.py +55 -0
  93. racinglines/frames/_util.py +34 -0
  94. racinglines/frames/f1.py +123 -0
  95. racinglines/frames/mtb_dh.py +156 -0
  96. racinglines/frames/schema.py +255 -0
  97. racinglines/markets/__init__.py +0 -0
  98. racinglines/markets/alerts.py +219 -0
  99. racinglines/markets/combos.py +260 -0
  100. racinglines/markets/crowd.py +249 -0
  101. racinglines/markets/disagree.py +442 -0
  102. racinglines/markets/exchange_driver.py +733 -0
  103. racinglines/markets/identity.py +54 -0
  104. racinglines/markets/kalshi/__init__.py +0 -0
  105. racinglines/markets/kalshi/client.py +113 -0
  106. racinglines/markets/kalshi/sync.py +565 -0
  107. racinglines/markets/kalshi/trade.py +110 -0
  108. racinglines/markets/kinds.py +312 -0
  109. racinglines/markets/payoffs.py +502 -0
  110. racinglines/markets/polymarket/__init__.py +0 -0
  111. racinglines/markets/polymarket/links.py +107 -0
  112. racinglines/markets/polymarket/sync.py +611 -0
  113. racinglines/markets/polymarket/trade.py +240 -0
  114. racinglines/markets/private_book.py +438 -0
  115. racinglines/markets/probalytics.py +390 -0
  116. racinglines/markets/quoting.py +76 -0
  117. racinglines/markets/settle_check.py +136 -0
  118. racinglines/markets/settlement_rules.py +184 -0
  119. racinglines/markets/store.py +407 -0
  120. racinglines/markets/strategies/__init__.py +0 -0
  121. racinglines/markets/strategies/buy_everything.py +107 -0
  122. racinglines/markets/strategies/maker_replay.py +691 -0
  123. racinglines/markets/strategies/replay_json.py +51 -0
  124. racinglines/markets/strategies/season.py +162 -0
  125. racinglines/markets/strategies/sizing.py +30 -0
  126. racinglines/markets/strategies/taker_weekend.py +312 -0
  127. racinglines/markets/venue_replay.py +454 -0
  128. racinglines/markets/venues.py +654 -0
  129. racinglines/models/__init__.py +0 -0
  130. racinglines/models/calibration.py +173 -0
  131. racinglines/models/combo_job.py +212 -0
  132. racinglines/models/dnf_check.py +123 -0
  133. racinglines/models/evaluate.py +267 -0
  134. racinglines/models/inputs/__init__.py +107 -0
  135. racinglines/models/inputs/grid.py +138 -0
  136. racinglines/models/inputs/laps.py +864 -0
  137. racinglines/models/inputs/laps_core.py +82 -0
  138. racinglines/models/inputs/laps_pricing.py +730 -0
  139. racinglines/models/inputs/practice.py +246 -0
  140. racinglines/models/inputs/results.py +454 -0
  141. racinglines/models/inputs/run_times/__init__.py +6 -0
  142. racinglines/models/inputs/run_times/core.py +107 -0
  143. racinglines/models/inputs/run_times/model.py +296 -0
  144. racinglines/models/inputs/run_times/points.py +183 -0
  145. racinglines/models/inputs/run_times/season.py +271 -0
  146. racinglines/models/inputs/run_times/settings.py +61 -0
  147. racinglines/models/inrace.py +46 -0
  148. racinglines/models/model_global.py +70 -0
  149. racinglines/models/outcomes.py +224 -0
  150. racinglines/models/position_sim/__init__.py +27 -0
  151. racinglines/models/props.py +394 -0
  152. racinglines/models/race_model.py +95 -0
  153. racinglines/models/sprint_check.py +114 -0
  154. racinglines/models/timed_runs/__init__.py +15 -0
  155. racinglines/models/variants.py +84 -0
  156. racinglines/paths.py +62 -0
  157. racinglines/pipelines/__init__.py +0 -0
  158. racinglines/pipelines/clv.py +470 -0
  159. racinglines/pipelines/coverage.py +199 -0
  160. racinglines/pipelines/demo_history.py +91 -0
  161. racinglines/pipelines/live.py +341 -0
  162. racinglines/pipelines/live_dh.py +983 -0
  163. racinglines/pipelines/live_f1.py +897 -0
  164. racinglines/pipelines/live_report.py +280 -0
  165. racinglines/pipelines/model_vs_market.py +128 -0
  166. racinglines/pipelines/position_replay.py +636 -0
  167. racinglines/pipelines/profiles.py +418 -0
  168. racinglines/pipelines/reconcile.py +483 -0
  169. racinglines/pipelines/scorecard.py +302 -0
  170. racinglines/pipelines/search.py +580 -0
  171. racinglines/pipelines/search_plan.py +108 -0
  172. racinglines/pipelines/search_report.py +404 -0
  173. racinglines/pipelines/season_checkpoints.py +174 -0
  174. racinglines/pipelines/season_replay.py +362 -0
  175. racinglines/pipelines/season_strategy.py +369 -0
  176. racinglines/pipelines/season_sweep.py +498 -0
  177. racinglines/pipelines/signals.py +933 -0
  178. racinglines/pipelines/sport_forecast.py +201 -0
  179. racinglines/pipelines/sport_paper.py +302 -0
  180. racinglines/pipelines/sport_signals.py +331 -0
  181. racinglines/pipelines/story.py +345 -0
  182. racinglines/pipelines/sweep_settings.py +379 -0
  183. racinglines/pipelines/weekend_sweep.py +680 -0
  184. racinglines/progress.py +110 -0
  185. racinglines/reporting/__init__.py +1 -0
  186. racinglines/reporting/markdown_html.py +55 -0
  187. racinglines/reporting/metrics.py +19 -0
  188. racinglines/reporting/search_analyze.py +231 -0
  189. racinglines/reporting/search_noise.py +59 -0
  190. racinglines/reporting/search_plot.py +157 -0
  191. racinglines/sources/__init__.py +0 -0
  192. racinglines/sources/chronorace/__init__.py +0 -0
  193. racinglines/sources/chronorace/download.py +488 -0
  194. racinglines/sources/chronorace/ingest.py +131 -0
  195. racinglines/sources/chronorace/parse.py +736 -0
  196. racinglines/sources/chronorace/pdf.py +198 -0
  197. racinglines/sources/fastf1/__init__.py +0 -0
  198. racinglines/sources/fastf1/fetch.py +219 -0
  199. racinglines/sources/fastf1/ingest.py +350 -0
  200. racinglines/sources/http.py +108 -0
  201. racinglines/sports.py +233 -0
  202. racinglines/testing/__init__.py +0 -0
  203. racinglines/testing/checks.py +273 -0
  204. racinglines/testing/synthetic.py +265 -0
  205. racinglines/weather/__init__.py +21 -0
  206. racinglines/weather/open_meteo.py +269 -0
  207. racinglines/weather/schema.py +173 -0
  208. racinglines/weather/venues.toml +278 -0
  209. racinglines/weather/wet.py +274 -0
  210. racinglines/weather/wunderground.py +230 -0
  211. racinglines/web/__init__.py +1 -0
  212. racinglines/web/accounts.py +170 -0
  213. racinglines/web/admin.py +508 -0
  214. racinglines/web/api.py +80 -0
  215. racinglines/web/app.py +912 -0
  216. racinglines/web/audience.py +47 -0
  217. racinglines/web/board.py +342 -0
  218. racinglines/web/book_routes.py +318 -0
  219. racinglines/web/demo.py +83 -0
  220. racinglines/web/diag.py +314 -0
  221. racinglines/web/diag_routes.py +89 -0
  222. racinglines/web/edge.py +453 -0
  223. racinglines/web/exchange_routes.py +207 -0
  224. racinglines/web/f1_live.py +191 -0
  225. racinglines/web/f1_ws.py +283 -0
  226. racinglines/web/fantasy_routes.py +211 -0
  227. racinglines/web/formats.py +87 -0
  228. racinglines/web/info_routes.py +154 -0
  229. racinglines/web/jobs.py +517 -0
  230. racinglines/web/layout.py +52 -0
  231. racinglines/web/legacy.py +38 -0
  232. racinglines/web/mybets.py +28 -0
  233. racinglines/web/order_routes.py +186 -0
  234. racinglines/web/prefs.py +45 -0
  235. racinglines/web/ranges.py +43 -0
  236. racinglines/web/roles.py +192 -0
  237. racinglines/web/sport_status.py +227 -0
  238. racinglines/web/static/app.js +700 -0
  239. racinglines/web/static/brand/logo.svg +8 -0
  240. racinglines/web/static/f1_stream.js +58 -0
  241. racinglines/web/static/htmx.min.js +1 -0
  242. racinglines/web/static/replay.js +115 -0
  243. racinglines/web/static/replay.wasm +0 -0
  244. racinglines/web/static/style.css +643 -0
  245. racinglines/web/strategy_pick.py +208 -0
  246. racinglines/web/templates/_admin_nav.html +9 -0
  247. racinglines/web/templates/_board_recent.html +9 -0
  248. racinglines/web/templates/_board_sport.html +66 -0
  249. racinglines/web/templates/_lab_nav.html +8 -0
  250. racinglines/web/templates/_macros.html +77 -0
  251. racinglines/web/templates/_recorders.html +11 -0
  252. racinglines/web/templates/_site_notice.html +28 -0
  253. racinglines/web/templates/_sport_status.html +14 -0
  254. racinglines/web/templates/_storage_status.html +60 -0
  255. racinglines/web/templates/admin.html +19 -0
  256. racinglines/web/templates/admin_activity.html +17 -0
  257. racinglines/web/templates/admin_db.html +11 -0
  258. racinglines/web/templates/admin_mcp.html +25 -0
  259. racinglines/web/templates/admin_monitors.html +25 -0
  260. racinglines/web/templates/admin_row.html +17 -0
  261. racinglines/web/templates/admin_sql.html +19 -0
  262. racinglines/web/templates/admin_table.html +17 -0
  263. racinglines/web/templates/admin_user.html +29 -0
  264. racinglines/web/templates/admin_users.html +47 -0
  265. racinglines/web/templates/athlete.html +11 -0
  266. racinglines/web/templates/base.html +37 -0
  267. racinglines/web/templates/base_public.html +8 -0
  268. racinglines/web/templates/bet.html +43 -0
  269. racinglines/web/templates/board.html +61 -0
  270. racinglines/web/templates/book.html +32 -0
  271. racinglines/web/templates/diag.html +123 -0
  272. racinglines/web/templates/error.html +7 -0
  273. racinglines/web/templates/event.html +15 -0
  274. racinglines/web/templates/exchange.html +44 -0
  275. racinglines/web/templates/fantasy_trade.html +53 -0
  276. racinglines/web/templates/forgot.html +26 -0
  277. racinglines/web/templates/history.html +90 -0
  278. racinglines/web/templates/house.html +51 -0
  279. racinglines/web/templates/house_market.html +47 -0
  280. racinglines/web/templates/house_sheet.html +15 -0
  281. racinglines/web/templates/lab.html +26 -0
  282. racinglines/web/templates/lab_backtests.html +9 -0
  283. racinglines/web/templates/lab_diagnostics.html +12 -0
  284. racinglines/web/templates/lab_edge.html +134 -0
  285. racinglines/web/templates/lab_jobs.html +12 -0
  286. racinglines/web/templates/lab_models.html +9 -0
  287. racinglines/web/templates/lab_run.html +39 -0
  288. racinglines/web/templates/lab_runs.html +6 -0
  289. racinglines/web/templates/lab_scenarios.html +11 -0
  290. racinglines/web/templates/lab_sport_sweep_form.html +25 -0
  291. racinglines/web/templates/lab_sweep_form.html +40 -0
  292. racinglines/web/templates/live.html +52 -0
  293. racinglines/web/templates/live_f1.html +97 -0
  294. racinglines/web/templates/live_f1_stream.html +690 -0
  295. racinglines/web/templates/live_mtb_dh.html +95 -0
  296. racinglines/web/templates/live_partial.html +1 -0
  297. racinglines/web/templates/live_picks.html +17 -0
  298. racinglines/web/templates/market.html +31 -0
  299. racinglines/web/templates/market_lookup.html +37 -0
  300. racinglines/web/templates/markets.html +18 -0
  301. racinglines/web/templates/mcp_authorize.html +21 -0
  302. racinglines/web/templates/order_preview.html +21 -0
  303. racinglines/web/templates/orders.html +20 -0
  304. racinglines/web/templates/positions.html +144 -0
  305. racinglines/web/templates/positions_backtests.html +26 -0
  306. racinglines/web/templates/race.html +119 -0
  307. racinglines/web/templates/racinglines101.html +389 -0
  308. racinglines/web/templates/run.html +17 -0
  309. racinglines/web/templates/settings.html +111 -0
  310. racinglines/web/templates/signin.html +19 -0
  311. racinglines/web/templates/signup.html +31 -0
  312. racinglines/web/templates/strategy.html +137 -0
  313. racinglines/web/templates/strategy_pick.html +51 -0
  314. racinglines/web/templates/strategy_pick_alpha.html +56 -0
  315. racinglines/web/templates/tapes.html +25 -0
  316. racinglines/web/templates/term.html +51 -0
  317. racinglines/web/term/demos.toml +16 -0
  318. racinglines/web/term/f1-singapore-10.ansi +21 -0
  319. racinglines/web/term/f1-singapore-10.html +23 -0
  320. racinglines/web/term/f1-singapore-2.ansi +7 -0
  321. racinglines/web/term/f1-singapore-2.html +9 -0
  322. racinglines/web/term/f1-singapore-4.ansi +21 -0
  323. racinglines/web/term/f1-singapore-4.html +23 -0
  324. racinglines/web/term/f1-singapore-6.ansi +21 -0
  325. racinglines/web/term/f1-singapore-6.html +23 -0
  326. racinglines/web/term/f1-singapore-8.ansi +21 -0
  327. racinglines/web/term/f1-singapore-8.html +23 -0
  328. racinglines/web/term/f1-singapore.json +48 -0
  329. racinglines/web/term/f1-singapore.toml +84 -0
  330. racinglines/web/term/quickstart.json +37 -0
  331. racinglines/web/term/step-1.html +0 -0
  332. racinglines/web/term/step-2.ansi +37 -0
  333. racinglines/web/term/step-2.html +39 -0
  334. racinglines/web/term/step-3.ansi +52 -0
  335. racinglines/web/term/step-3.html +54 -0
  336. racinglines/web/term/step-4.ansi +60 -0
  337. racinglines/web/term/step-4.html +62 -0
  338. racinglines/web/term/step-5.ansi +146 -0
  339. racinglines/web/term/step-5.html +148 -0
  340. racinglines/web/term_routes.py +72 -0
  341. racinglines/web/users.py +130 -0
  342. racinglines/web/views.py +1117 -0
  343. racinglines/web/viz.py +183 -0
  344. racinglines-1.0.0.dist-info/METADATA +273 -0
  345. racinglines-1.0.0.dist-info/RECORD +349 -0
  346. racinglines-1.0.0.dist-info/WHEEL +5 -0
  347. racinglines-1.0.0.dist-info/entry_points.txt +2 -0
  348. racinglines-1.0.0.dist-info/licenses/LICENSE +21 -0
  349. racinglines-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,10 @@
1
+ """racinglines: fair prices and market making for race sports. See `racinglines -h`."""
2
+
3
+ import sys
4
+
5
+ if sys.version_info < (3, 11):
6
+ raise SystemExit(
7
+ f"racinglines needs Python 3.11+ (this is {sys.version.split()[0]} at {sys.executable}).\n"
8
+ "Set up the project environment once:\n"
9
+ " python3.14 -m venv .venv && source .venv/bin/activate\n"
10
+ " pip install -r requirements.txt && pip install -e .")
@@ -0,0 +1,5 @@
1
+ import sys
2
+
3
+ from racinglines.cli import main
4
+
5
+ sys.exit(main() or 0)
@@ -0,0 +1,188 @@
1
+ # OG.com (Crypto.com's CFTC prediction market): an exchange defined as data. Read by racinglines/exchanges.py; the one
2
+ # generic driver (racinglines/markets/exchange_driver.py) does the sync, tape, history and books from this file, so
3
+ # adding a venue with a plain JSON API is a new file here, not new code. Read-only: only public market data, no key.
4
+ # Built from real responses captured 2026-09-29 (tests/fixtures/market/og_*.json, described in og_README.txt).
5
+ # Off by default: the app shows the venue only with RACINGLINES_OG_VENUE=1. Nothing here sends an order.
6
+
7
+ [exchange]
8
+ code = "og"
9
+ name = "OG.com"
10
+ kind = "exchange"
11
+ switch = "RACINGLINES_OG_VENUE" # env var; the venue appears in the app only when it is 1 / true / yes
12
+ url = "https://og.com/" # no per-event page pattern known: the venue links to the front page
13
+ # What the feed offers historically, shown under the Markets board's "Exchange data" counts.
14
+ coverage = "OG.com: the API serves only about the last month of trades and minute prices, so history starts when we began pulling (2026-09-29); no book history (one snapshot per books run)."
15
+ # The taker fee per contract, in dollars, from 2026 reviews of the exchange (UNVERIFIED against its fee schedule).
16
+ # Used only by the fair-price report; change it here when the schedule is confirmed.
17
+ taker_fee_per_contract = 0.02
18
+
19
+ [api]
20
+ host = "https://api.crypto.com"
21
+ host_env = "OG_API_HOST" # override, e.g. the UAT sandbox https://uat-api.3ona.co
22
+ prefix = "/fcm/v1/public"
23
+ timeout = 20
24
+ max_per_second = 20 # the exchange allows 100 per method per IP; stay well under it
25
+ ok_path = "code" # every response is {"code": 0, "result": {...}}
26
+ ok_value = 0
27
+ error_path = "message"
28
+
29
+ [endpoints.events]
30
+ path = "/get-events"
31
+ data = "result.data"
32
+ next = "result.next_cursor" # paged with cursor=<next>; the last page has none
33
+ cursor_param = "cursor"
34
+ params = { since = 0, limit = 1000 }
35
+
36
+ [endpoints.instruments]
37
+ path = "/get-instruments"
38
+ data = "result.data"
39
+ next = "result.next_cursor"
40
+ cursor_param = "cursor"
41
+ params = { limit = 1000 }
42
+ batch_param = "event_symbols" # comma-separated event symbols, at most `batch_size` per call
43
+ # The live API caps this at 10: 11 or more event symbols return 400 {"code": 40004, "message": "Invalid event_symbol"}
44
+ # whatever the symbols are (checked against api.crypto.com on 2026-09-29; the fixture request used 2). get-tickers took 20.
45
+ batch_size = 10
46
+ limits = { max_batch = 10 } # checked when the schema loads (racinglines/exchanges.py)
47
+
48
+ [endpoints.tickers]
49
+ path = "/get-tickers"
50
+ data = "result.data"
51
+ batch_param = "instrument_name" # comma-separated instrument symbols
52
+ batch_size = 50
53
+
54
+ [endpoints.book]
55
+ path = "/get-book"
56
+ data = "result.data"
57
+ params = { depth = 50 }
58
+ instrument_param = "instrument_name"
59
+
60
+ [endpoints.trades]
61
+ path = "/get-trades"
62
+ data = "result.data"
63
+ instrument_param = "instrument_name"
64
+ start_param = "start_ts" # nanoseconds
65
+ start_unit = "ns"
66
+ params = { count = 150 } # the server's maximum: 200 and up is "Invalid count" (checked 2026-09-29)
67
+ limits = { max_count = 150 }
68
+ window_days = 30 # the docs say 7; the server accepted 30 and 60, and returned nothing at 100
69
+
70
+ [endpoints.history]
71
+ path = "/get-ticker-histories"
72
+ data = "result.data"
73
+ instrument_param = "instrument_names"
74
+ start_param = "start_ts" # milliseconds
75
+ end_param = "end_ts"
76
+ start_unit = "ms"
77
+ params = { count = 1000 }
78
+ window_days = 31 # start to end at most 31 days, 1,000 one-minute rows a call
79
+ limits = { max_window_days = 31 }
80
+
81
+ [endpoints.settlements]
82
+ # Who won. get-instruments, get-events and get-tickers drop an instrument once it settles (a settled event returns
83
+ # []), so the listing never says the outcome; this feed does. It holds every instrument the exchange settles (mostly
84
+ # FX and crypto, about 20,000 rows per 12 hours), unfiltered: an instrument_type filter is refused together with
85
+ # since / cursor. exchange_driver.settle scans it forward from `since` until every closed, unresolved link of the
86
+ # sport is found or the feed ends (probed from the owner's Mac 2026-10-06; the docs site is JS-only and unreadable).
87
+ path = "/get-expired-settlement-price"
88
+ data = "result.data"
89
+ next = "result.next_cursor" # paged with cursor=<next>, like get-events; the last page has none
90
+ cursor_param = "cursor"
91
+ start_param = "since" # nanoseconds; the feed goes back to about 2026-07-08
92
+ start_unit = "ns"
93
+ params = { limit = 1000 } # 1,000 rows a call worked; about 20 calls cover 12 hours
94
+ max_pages = 500 # our own cap per pass (500,000 rows, about 12 days); the next pass resumes
95
+
96
+ # Where each thing lives in a response: dotted paths into the JSON ("a.b" = obj["a"]["b"]). `unit` says how a time
97
+ # is written: ms / ns since the epoch, or iso.
98
+ [fields.event]
99
+ id = "symbol"
100
+ name = "name"
101
+ sports_grouping = "event_details.metaData.SPORTS_GROUPING"
102
+
103
+ [fields.instrument]
104
+ id = "symbol"
105
+ event = "underlying_symbol"
106
+ subject = "event_details.metaData.PARTICIPANT" # the driver, team or player
107
+ contract = "event_details.metaData.PREDICT_CONTRACT_TYPE" # the market's name, e.g. "F1 Drivers' Champion 2026"
108
+ event_title = "event_details.metaData.NAME"
109
+ title = "display_name"
110
+ tick = "price_tick_size"
111
+ tradable = "tradable"
112
+ expiry = "expiry_timestamp_ms"
113
+ expiry_unit = "ms"
114
+
115
+ [fields.ticker]
116
+ instrument = "i"
117
+ bid = "b"
118
+ ask = "k"
119
+ last = "a"
120
+ volume = "v"
121
+
122
+ [fields.book]
123
+ instrument = "instrument_name"
124
+ bids = "data.0.bids" # levels [price, quantity], strings
125
+ asks = "data.0.asks"
126
+ ts = "data.0.t"
127
+ ts_unit = "ms"
128
+
129
+ [fields.trade]
130
+ id = "d"
131
+ ts = "t"
132
+ ts_unit = "ms"
133
+ price = "p"
134
+ size = "q"
135
+ side = "s" # "buy" / "sell", the taker's side of the YES contract
136
+
137
+ [fields.history]
138
+ instrument = "i"
139
+ ts = "t"
140
+ ts_unit = "ms"
141
+ bid = "b"
142
+ ask = "k"
143
+ last = "a"
144
+
145
+ [fields.settlement]
146
+ # One row per settled instrument: {"i": symbol, "x": expiry ms, "v": "1.00" | "0.00" | "0.50", "t": settled at ms}.
147
+ # `value` is what one YES contract paid: 1 = YES, 0 = NO, anything between (0.50 seen) is a void, which leaves
148
+ # resolved_yes empty. Sports contracts settle when decided, not at the nominal expiry (the Sepang race winners
149
+ # expire 2026-11-30; 21 of 22 settled 2026-10-04 11:36 UTC), so `ts` is the settle time and `expiry` only a note.
150
+ instrument = "i" # equals market_links.token_id (and market_slug)
151
+ value = "v"
152
+ ts = "t"
153
+ ts_unit = "ms"
154
+ expiry = "x"
155
+ expiry_unit = "ms"
156
+
157
+ [prices]
158
+ unit = "dollars" # 0 to 1, decimal strings; a contract pays $1
159
+
160
+ # What the exchange lists of each of our sports: how to find its events and how a market is named. A sport listed
161
+ # here without `modeled = true` is tape-only (every link unmodeled, recorded like Kalshi's NASCAR). Its rules map a
162
+ # market's contract name (regex, case-insensitive, first match wins) to one of our prediction kinds and to what its
163
+ # `subject` field names (driver, team). The sport's own schema (sports/<code>.toml) says which competition it is.
164
+ [sports.f1]
165
+ event_prefixes = ["F1-"] # season futures carry no SPORTS_GROUPING, so events are found by symbol
166
+ modeled = true
167
+ [[sports.f1.rules]]
168
+ contract = "constructors'? champion"
169
+ kind = "constructors_champion"
170
+ subject = "team"
171
+ [[sports.f1.rules]]
172
+ contract = "drivers'? champion"
173
+ kind = "champion"
174
+ subject = "driver"
175
+ [[sports.f1.rules]]
176
+ contract = "^race winner$" # a Grand Prix's winner (event "Bahrain Grand Prix 2026"): linked to that race
177
+ kind = "race_win"
178
+ subject = "driver"
179
+
180
+ [sports.nascar]
181
+ event_prefixes = ["NSCAR-"] # 17 Cup Champion contracts; tape only
182
+ modeled = false
183
+
184
+ [sports.sailgp]
185
+ # 13 Championship Winner contracts plus per-event Overall Winner events (tests/fixtures/market/og_events_sailgp.json,
186
+ # symbols SAIL-000NN-26); tape only
187
+ event_prefixes = ["SAIL-"]
188
+ modeled = false