個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Open source SaaS starter for predictive horse racing analytics, value bet detection, bankroll simulation, and explainable AI recommendations.
• Next.js App Router
• React
• TypeScript
• Tailwind CSS
• Lucide React
• Neon Postgres compatible data layer
The technical stack is based on open source packages. The product can still be commercialized through subscriptions, API access, and B2B licensing.檢視完整 README ↗Open source SaaS starter for predictive horse racing analytics, value bet detection, bankroll simulation, and explainable AI recommendations.
The technical stack is based on open source packages. The product can still be commercialized through subscriptions, API access, and B2B licensing.
npm install
cp .env.example .env.local # optional: every variable is documented there, all commented out
npm run dev
No variable is required locally: without DATABASE_URL the app runs in demo mode on fictitious races. .env.example lists every environment variable read by src/ and scripts/; uncomment only the ones you need (an empty value is not the same as an absent one).
Tests (Node's built-in node:test run through tsx, no framework — they cover the pure helpers of scripts/lib/ and src/lib/: profiles, market reading, strategies, live cadence, payouts, rate limiting):
npm test
Coverage of src/lib/ (Node's experimental built-in coverage, no extra dependency):
npm run test:coverage
Target: 80 % of lines on src/lib/. It is not enforced in CI yet (about 66 % today); only files imported by at least one test appear in the report, so an untested module lowers nothing until it gets its first test.
End-to-end tests (Playwright, Chromium only, in e2e/ — separate from npm test). They build and start the production server on port 3100 in demo mode (or reuse one already running there), walk the main flows (programme → race → table tabs, /pronostics, content pages, push-alerts toggle) and run an axe-core accessibility audit that fails on serious or critical violations:
npx playwright install chromium # once
npm run test:e2e
CI runs them in a separate e2e job after the main verify job and uploads the HTML report when they fail.
Server errors are reported without any third-party service: src/instrumentation.ts (onRequestError) writes one JSON line per error ("kind":"request-error", route, method, path without query string, digest, truncated message) to the runtime logs.
The app is ready for Neon Postgres through DATABASE_URL.
db/schema.sqldb/seed-demo.sqlsrc/lib/race-repository.tsdocs/DATA_RETENTION_POLICY.mdIf DATABASE_URL is missing, the app falls back to the built-in mock dataset so development and Vercel previews keep working.
Database stats:
npm run db:stats
Experimental PMU import:
npm run data:import:pmu -- --date 03052026 --max-races 10
Full PMU programme import for a day:
npm run data:import:pmu -- --date 03052026
Auto-learning from official arrivals:
npm run model:learn
Imports follow exactly the PMU point-of-sale programme (pmu.fr/point-de-vente, API specialisation=OFFLINE), foreign meetings included.
This connector uses the publicly reachable PMU JSON programme endpoint with a clear user agent, no bot evasion, and a short delay between race participant requests. For commercial scale, validate usage rights or replace it with an authorised PMU partner feed.
The full PMU programme import runs from GitHub Actions, not from a local machine:
04:30 UTC: morning import for J-1/J/J+117:30 UTC: evening refresh and result catch-up, followed by model learning and jockey/trainer statisticsThe mid-day import was removed to avoid re-reading the same three-day window. After the evening import (or a manual run), the workflow stores post-race feedback, selects the best active scoring profile by segment (DEFAULT, QUARTE_PLUS, QUINTE_PLUS) and applies it only to races without official results. Model learning and jockey/trainer statistics now run once daily instead of after both scheduled imports.
Required GitHub secret:
DATABASE_URLManual trigger:
gh workflow run import_pmu.yml -f date=03052026
The market is the best predictor we have, but only close to the start (37.6 % of winners found with the starting price versus 31.0 % with odds 6-12 h old, see scripts/evaluate-freshness.mjs). GitHub does not guarantee scheduled-run times (8 real runs out of 60 planned between 30/09 and 02/10/2026), so each live_refresh.yml run executes a 5.5-hour loop (scripts/live-refresh.ts) and then dispatches the next run itself; the cron (every 2 h) only restarts the chain if it breaks. Outside race hours the loop sleeps without querying the database. Each race starting within 90 minutes is refreshed through src/lib/live/refresh-race.ts — odds, pool shares, speed figures, declared non-runners — at most every 15 min beyond H-60, every 10 min until H-15, and every 2 min in the last quarter of an hour. On an open race page, the browser checks for updates every 5 min in the last ten minutes; a manual refresh remains available. History is written sparingly (odds_snapshots, pool_snapshots) and the prediction displayed at H-60, H-15 and just before the off is frozen in prediction_snapshots. It never creates races. Main pages and public data APIs use a 5-minute cache; live race data can still invalidate a race page as soon as it changes.
npm run data:refresh:live -- --once # one pass, locally
npm run data:refresh:live -- --minutes 30 # 30-minute loop
A weekly compact_storage.yml (Sunday 02:00 UTC) runs npm run db:compact -- --apply; it shares a concurrency group with the import so the two never overlap.
Two independent opinions are compared on every runner:
devig in src/lib/probability.ts);src/lib/fundamental/, a conditional logit per discipline that never sees the odds (form, earnings, age, sex, jockey and trainer strike rates, trot handicap distance, draw). Trained by npx tsx scripts/train-fundamental.ts on races before the cutoff stored in model.json, measured on the following months.The displayed probability blends both (MODEL_WEIGHT = 0.10). src/lib/profiles.ts is the single source for runner profiles (Base, Caché, Value, Favori, Outsider, Tocard, À éviter) and the race reading (lisible / ouverte / piège); the course page, the dashboard, the frozen predictions and the backtest all read it. Every rule and threshold is published on /methode.
Proof:
npx tsx scripts/backtest.ts --dry-run # odds known 15 min before the off, official PMU payouts, 90 % bootstrap
npx tsx scripts/backtest.ts --dry-run --dump f.json # every evaluated runner, to explore thresholds
node scripts/backfill-payouts.mjs --from 2026-04-01 # official payouts for past races
node scripts/check-freshness.mjs # did a race start without fresh odds today?
track_record.yml completes missing payouts daily, but recalculates the report from the full historical dataset only on Sundays (or when manually triggered) to reduce Neon data transfer. /track-record publishes it, negative results included, with the live tracking of frozen predictions (prediction_snapshots, stage H-2). freshness_check.yml fails — and GitHub emails — when more than 10 % of the day's races started without odds observed in the 20 minutes before the off.
Public database-backed API responses and main pages are cached for five minutes, and the full-history backtest runs weekly rather than nightly. This substantially reduces repeat reads, but a limit below 5 GB/month cannot be guaranteed from code alone: actual traffic, crawlers, history size and Neon usage must be checked in the Neon dashboard after deployment. The weekly backtest still reads the full history once per run; run it manually less often if that single run consumes too much of the monthly allowance.
GET /api/predictionsGET /api/racesGET /api/race-analysisGET /api/bet-recommendationsGET /api/post-race-analysisGET /api/value-betsPOST /api/simulatePOST /api/simulate-betGET /api/model-cardSee docs/INSTITUTIONAL_SYSTEM_DESIGN.md for the full institutional-grade system design: data engine, AI engine, scoring, database structure, API/MCP, compliance, and roadmap.
research/ holds the former Python tree (agents, pace, portfolio, drift models, promote_challenger.py). It is not deployed, not maintained and not wired to the site; see research/README.md before running any of it — never against production.
KAYZEN TURF AI is a decision-support product, not a guarantee of profit. The application must keep clear risk disclaimers, responsible gaming messages, and transparent AI explanations.