"Winning by not losing."
BetSignal is a full-stack betting intelligence platform — Telegram bot, web app, and automated Twitter monitoring — powered by Claude Sonnet 4.6 for daily analysis and GPT-4.1-nano for real-time on-demand picks. Built on Nassim Taleb's concept of Antifragility: the system gets smarter the more it's used.
External Data Sources
Football-Data.org · The Odds API · SofaScore · SportyBet · Twitter/X
│
▼
Engine Service (core/pipeline.py + engine_service/main.py)
Claude Sonnet 4.6 — primary intelligence (daily pipeline)
GPT-4.1-nano / Gemini 2.0 — on-demand, outside pipeline
│
├── betting.db (SQLite — picks, results, users, cache)
│
├── Telegram Bot (bot.py) ← users on mobile
├── React Web App (webapp/src/) ← users on web
└── Twitter Worker (twitter_worker.py) ← monitors tipsters
Runs once per day. Claude does the heavy lifting here.
Football-Data.org + The Odds API (7 keys) + SofaScore Direct
│
core/pipeline.py
│
For each fixture today:
fragility.analyze_match_full()
→ MatchAnalysis (odds, form, league context)
│
Claude Sonnet 4.6
forecast_match_opus()
→ picks (0-2 per match)
→ probability estimates
→ reasoning per market
│
betting.db
(daily_picks stored)
│
SportyBet catalog lookup
→ top 5 high-confidence picks
→ create_booking_code()
│
betting.db
(daily_booking_code stored)
Key principle: Claude runs once per match per day. Every downstream feature reads from the DB — no repeated AI calls.
Fires only when a match is NOT already in daily picks.
Triggered by:
/analyze CODE → KEEP picks with no DB reasoning
/book [image] → fixture list (teams extracted, no selections)
twitter worker → tipster codes not in our pipeline
SofaScore Direct (last 5 match form)
│
GPT-4.1-nano ← $0.10/$0.40 per 1M tokens
forecast_match_openai()
│
(fails?) → Gemini 2.0 Flash ← free tier fallback
│
→ pick recommendation + reasoning
Read daily picks from DB → format by confidence → return booking code.
SportyBet API: load code → Sportradar probabilities per outcome
→ For each pick: KEEP / WEAK / DROP verdict
→ KEEP + no pipeline data: SofaScore + GPT-4.1-nano on-demand (cap: 5)
→ Refined code: KEEP picks only → new SportyBet booking code
GPT-4.1-nano vision: extract teams + selections from photo
→ If fixture list (no selections visible):
SofaScore form → GPT-4.1-nano forecast → best pick per match
→ find_event_any_league(): fuzzy match in SportyBet catalog
→ create_booking_code() → return code to user
Read today's picks from DB → sort by confidence then probability → top N → booking code.
twitter_worker.py (polls every 15 min via Docker)
│
twikit (cookie auth: auth_token + ct0)
│
Monitor: @Sambetting_tips · @shandave4luv · @isthaths
│
New tweet → extract 6-char booking code
→ analyze_booking_code() → KEEP/WEAK/DROP
→ generate refined code
→ format reply draft
→ notify_admin() → Telegram logs channel
(manual review before posting)
GitHub (main branch)
│ push triggers
▼
GitHub Actions CI/CD
│ builds + deploys to VPS
▼
Docker Compose
┌──────────┬──────────┬──────────┬──────────┬──────────┐
│ engine │ api │ bot │ twitter │ caddy │
│ :9000 │ :8000 │ │ │ :80/:443 │
└──────────┴──────────┴──────────┴──────────┴──────────┘
└── betting.db (shared SQLite volume)
Deploy rule: Push to
main→ CI/CD handles everything. Never SSH to restart manually.
| Model | Where used | Cost /1M tokens | Why |
|---|---|---|---|
| Claude Sonnet 4.6 | Daily pipeline | ~$3 in / $15 out | Best reasoning, runs once/day |
| GPT-4.1-nano | On-demand + vision | $0.10 in / $0.40 out | Fastest/cheapest, real-time |
| Gemini 2.0 Flash | On-demand fallback | Free | Backup if OpenAI fails |
slipcheck/
bot.py Telegram bot — handlers for all commands
twitter_worker.py Twitter monitoring CLI runner
engine_client.py Shared HTTP client (bot + web API → engine)
core/
config.py API keys, league mappings, constants
db.py SQLite — picks, users, cache, resolution
pipeline.py Daily pipeline (Claude forecasts → booking code)
claude_forecast.py Claude/GPT/Gemini forecast layer
fragility.py MatchAnalysis builder (stats + odds)
sportybet.py SportyBet API — event lookup, booking codes
sofascore_direct.py SofaScore scraping (curl_cffi, no API key)
vision.py GPT-4.1-nano image extraction (bet slips)
twitter_bot.py Twitter monitoring + reply formatting
football_data.py Football-Data.org — fixtures, scores, 800+ team mappings
odds.py The Odds API — live odds per league
engine_service/
main.py FastAPI engine — all /picks/* endpoints
api/ FastAPI web backend (auth, payments, user API)
webapp/ React + Vite frontend (user dashboard)
website/ Astro marketing site
tests/ Unit, integration, system tests
docs/ Architecture docs, session logs, plans
betting.db SQLite database (auto-created)
.env API keys (never committed)
| Table | Purpose |
|---|---|
daily_picks |
Claude's picks per match (confidence, probability, reasoning) |
daily_booking_code |
Today's SportyBet code (top 5 picks) |
users |
Accounts, tier, subscription expiry |
telegram_links |
Telegram ID ↔ user account mapping |
tracked_picks |
User's manually tracked bets (pending/won/lost) |
twitter_replies |
Dedup log — processed tweet IDs |
api_cache |
Short-lived cache for SportyBet/SofaScore responses |
| Service | Purpose | Notes |
|---|---|---|
| Football-Data.org | Fixtures + results | 13 leagues, free tier |
| The Odds API | Pre-match odds | 7 keys to spread rate limits |
| SofaScore | Team recent form | curl_cffi Chrome impersonation |
| SportyBet | Event lookup + booking codes | curl_cffi, public endpoints |
| Sportradar | Probability data | Embedded free in SportyBet responses |
| Anthropic | Claude Sonnet 4.6 | Daily pipeline |
| OpenAI | GPT-4.1-nano | On-demand analysis + vision |
| Google Gemini | Gemini 2.0 Flash | Free fallback |
| twikit | Twitter scraping | Cookie auth, rotate every 30-90 days |
| Telegram Bot API | User bot | Free |
| Code | League |
|---|---|
| PL | Premier League |
| PD | La Liga |
| SA | Serie A |
| BL1 | Bundesliga |
| FL1 | Ligue 1 |
| CL | Champions League |
| DED | Eredivisie |
| ELC | Championship |
| PPL | Primeira Liga |
| BSA | Brazil Série A |
- Python 3.10+
- Node.js 25.6.0 (use nvm)
- Docker + Docker Compose (for production)
| Service | Purpose |
|---|---|
| Football-Data.org | Fixtures + scores |
| The Odds API | Live odds |
| Anthropic | Claude forecasts |
| OpenAI | GPT-4.1-nano on-demand + vision |
| Telegram (@BotFather) | Bot token |
git clone https://github.com/nedu-m/betting-analytics.git
cd betting-analytics
# Python
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Node
nvm use
npm install
cd webapp && npm install && cd ..
cd website && npm install && cd ..
# Configure
cp .env.example .env
# Fill in your API keys
# Run all services
npm run dev
# ENGINE :9000 · API :8000 · APP :5173 · SITE :4321
# Tests
venv/bin/python -m pytest tests/unit/ -v| Command | Description |
|---|---|
/picks |
Today's daily slip + SportyBet booking code |
/picks all |
Full breakdown across all confidence levels |
/analyze CODE |
Analyze any SportyBet booking code — verdict per pick + refined code |
/book |
Send a photo of any bet slip → get a SportyBet code |
/book N |
Generate a fresh code with top N picks |
/track |
View pending tracked matches |
/history |
Your prediction record: win rate, ROI, results by market |
/link TOKEN |
Link your BetSignal account to Telegram |
- Via Negativa: We gain more by removing bad bets than by finding "sure things." The system filters out weak picks rather than boosting uncertain ones.
- Tracked Outcomes: Every recommendation is stored and resolved against actual scores. Results feed back into accuracy stats.
- Antifragility: The more the system is used, the more data it accumulates on what works. Bad picks are identified and pruned over time.
Private / Proprietary.