Skip to content
View byteforge9190's full-sized avatar
  • Madrid, España

Block or report byteforge9190

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
byteforge9190/README.md

whoami

{
  "role":      "Odds data engineer — aggregation, normalisation, distribution",
  "obsession": "Getting the same market from 40 books to agree on what it is",
  "builds": [
    "bookmaker adapters that survive layout changes and rate limits",
    "event + market + selection matching across incompatible taxonomies",
    "devig / fair-value pricing off sharp reference books",
    "arbitrage, middle and +EV scanners that fire before the line moves",
    "WebSocket fanout that pushes deltas, not snapshots"
  ],
  "cares_about": ["p99 latency", "data lineage", "not getting blocked", "responsible gambling"],
  "status": "open to consulting on odds feeds & pricing infrastructure"
}

The hard part of betting data was never the scraping. It's that Man Utd vs Man City is MANU-MANC at one book, Manchester United FC – Manchester City FC at the next, and an opaque numeric ID at a third — and you have three seconds to decide they are the same event before the price goes stale. That is the problem I have spent my career on.


🏗️ The pipeline I keep rebuilding (and keep making faster)

flowchart LR
    subgraph SRC["🔌 Sources"]
        A1["Official APIs<br/>Pinnacle · Betfair · The Odds API"]
        A2["Licensed feeds<br/>Sportradar · LSports · OpticOdds"]
        A3["Headless collectors<br/>rotating proxies · TLS fingerprints"]
    end

    subgraph ING["⚡ Ingest"]
        B1["Adapter per book<br/>schema + rate-limit aware"]
        B2["Dedup and delta filter"]
    end

    subgraph RES["🧩 Resolution"]
        C1["Entity matcher<br/>alias tables + fuzzy + embeddings"]
        C2["Market and selection mapper"]
        C3["Odds normaliser<br/>US · frac · HK · MY · IDN → decimal"]
    end

    subgraph PRC["📐 Pricing"]
        D1["Overround strip<br/>multiplicative · power · Shin"]
        D2["Fair line + CLV tracker"]
        D3["Arb / middle / +EV scanner"]
    end

    subgraph OUT["📡 Distribution"]
        E1["WebSocket deltas"]
        E2["REST snapshots + history"]
        E3["Alerting → Telegram / webhook"]
    end

    A1 --> B1
    A2 --> B1
    A3 --> B1
    B1 --> B2 --> C1 --> C2 --> C3 --> D1 --> D2 --> D3
    D3 --> E1
    D3 --> E2
    D3 --> E3

    KAF[("Kafka / Redpanda")] -.-> B2
    KAF -.-> C1
    CH[("ClickHouse<br/>tick history")] -.-> D2
    RD[("Redis<br/>hot book state")] -.-> E1

    classDef src fill:#CFFAFE,stroke:#0E7490,color:#083344
    classDef ing fill:#D1FAE5,stroke:#047857,color:#022C22
    classDef res fill:#EDE9FE,stroke:#6D28D9,color:#2E1065
    classDef prc fill:#FEF3C7,stroke:#B45309,color:#451A03
    classDef out fill:#FFE4E6,stroke:#BE123C,color:#4C0519
    classDef store fill:#E2E8F0,stroke:#475569,color:#0F172A

    class A1,A2,A3 src
    class B1,B2 ing
    class C1,C2,C3 res
    class D1,D2,D3 prc
    class E1,E2,E3 out
    class KAF,CH,RD store
Loading

🧮 The bit everyone gets wrong: devig before you compare

Two books showing 2.05 / 1.85 and 2.10 / 1.80 are not offering the same opinion — they are carrying different margin. Compare fair prices, not posted prices.

// oddsx/fair.go — strip the overround, then look for a real edge.
package oddsx

import "math"

type Quote struct {
	Book  string
	Price []float64 // decimal, one per outcome
}

// impliedSum is the total implied probability once every price is raised to k.
func impliedSum(price []float64, k float64) (s float64) {
	for _, p := range price {
		s += math.Pow(1/p, k)
	}
	return s
}

// Devig removes the bookmaker margin so the outcomes sum to 1.0.
// Power method: solve for k where Σ (1/pᵢ)^k == 1. Closer to reality than
// naive proportional scaling, which systematically over-taxes longshots.
func Devig(price []float64) []float64 {
	lo, hi := 0.5, 1.5
	for i := 0; i < 60; i++ { // bisection — 60 rounds is float64-exact
		if k := (lo + hi) / 2; impliedSum(price, k) > 1 {
			lo = k
		} else {
			hi = k
		}
	}
	k := (lo + hi) / 2
	fair := make([]float64, len(price))
	for i, p := range price {
		fair[i] = math.Pow(1/p, k)
	}
	return fair
}

// Arb returns ROI (percent) and which book to take each leg at. Positive == surebet.
func Arb(quotes []Quote) (roi float64, legs []string) {
	best := make([]float64, len(quotes[0].Price))
	legs = make([]string, len(best))
	for _, q := range quotes {
		for i, p := range q.Price {
			if p > best[i] {
				best[i], legs[i] = p, q.Book
			}
		}
	}
	inv := 0.0
	for _, p := range best {
		inv += 1 / p
	}
	return (1/inv - 1) * 100, legs
}

Why it matters: an arb computed on posted odds surfaces hundreds of fake edges a day. One computed against a devigged sharp reference surfaces the handful that are really there — and tells you which side is wrong.


🛠️ Stack

Collection
Streaming
Storage
Modelling
Delivery

📚 Domain toolkit

Odds formats & math — the conversions and corrections that have to be exact
Concept What I do with it
Decimal · American · Fractional · Hong Kong · Malay · Indonesian One canonical decimal representation, lossless round-trip, rational-form fractions
Overround / vig Multiplicative, additive, power and Shin devigging, chosen per market shape
Fair value & no-vig line Sharp-book reference pricing (Pinnacle, exchange) as the truth signal
Closing Line Value Per-bet CLV tracking — the only honest measure of whether a model is real
Expected value & Kelly Fractional Kelly staking with correlation-aware exposure caps
Arbitrage · middles · scalps Stake splitting, rounding to book limits, execution-risk scoring
Line movement Steam detection, limit-weighted moves, market width as a confidence signal
Feeds & integrations — what I have wired up
  • Exchanges: Betfair Exchange API (Stream + REST), Smarkets, Matchbook — ladder depth, not just top-of-book
  • Sharp books: Pinnacle and reference-grade pricing, with limit movement treated as a signal in its own right
  • Retail books: the long tail of regional operators, each with its own taxonomy and none of them with a spec
  • Licensed providers: Sportradar, Genius Sports, LSports, OpticOdds, The Odds API, BetsAPI
  • Sports: football, basketball, tennis, baseball, hockey, MMA, esports — including player props and alternate lines
  • Realities: rate limits, geo-fencing, TLS/JA3 fingerprinting, Cloudflare, and schema drift on a Tuesday with no changelog
Matching & normalisation — the unglamorous 80%
  • Event matching: alias dictionaries → normalised tokens → fuzzy scoring → embedding fallback → human review queue for the last 0.5%
  • Market mapping: one internal market taxonomy; every book maps into it, never the other way round
  • Selection mapping: handicaps and totals keyed by line value, so -2.5 at one book never silently pairs with -3.0 at another
  • Player props: name disambiguation across roster feeds, injury-driven market suspension
  • Time alignment: kickoff drift, postponement and the in-play clock — a "live" price on a suspended market is worse than no price at all
Running a feed in production
  • Deltas over snapshots — bandwidth drops by roughly 90% and clients stay in sync
  • Per-book health scoring: staleness, error rate, suspension ratio, silent-drift detection
  • Backfill and replay from ClickHouse, so a model backtests on exactly what the feed saw
  • Circuit breakers per adapter — one dead book never takes the pipeline down with it
  • Data lineage on every price: which book, which fetch, which parser version

📦 Featured work

Project What it is Stack
odds-mesh Multi-book aggregation service — 40+ adapters, unified taxonomy, WebSocket delta stream Go · Kafka · Redis · ClickHouse
devigger Overround removal (multiplicative / power / Shin) and fair-line computation Rust + Python bindings
matchbook-ai Event and selection resolution across bookmakers: alias tables, fuzzy scoring, embedding fallback Python · Polars · pgvector
surebet-radar Arbitrage, middle and +EV scanner with execution-risk scoring and Telegram alerting Go · NATS · TypeScript
clv-tracker Closing-line-value analytics — the scoreboard that says whether a model actually works Python · DuckDB · Next.js

Some client work lives in private repos. Happy to walk through the architecture and the trade-offs on a call.


📊 GitHub

Panels below are rendered nightly by metrics.yml and committed into this repo — no third-party widget host to go down on me. Fitting, for someone who builds feeds.

GitHub metrics — languages, activity, habits, achievements Commit streak

🧊 A year of commits, in isometric

3D contribution calendar

🐍 Watch the contributions get eaten

Contribution snake

🤝 Reach me

Email Telegram LinkedIn Discord X Website



Good conversations to have with me:
"our feed goes stale during in-play" · "we cannot match events across books" · "our arb alerts are 95% false positives" · "we need tick history we can actually backtest on"


🔞 I build tooling for licensed operators, traders and researchers. Gamble responsibly — BeGambleAware · Gambling Therapy

footer

Popular repositories Loading

  1. casino-game-sportsbook-sports-betting casino-game-sportsbook-sports-betting Public

    casino game casino game casino game casino game casino game casino slot game casino slot game casino slot game casino slot game casino slot game sportsbook sportsbook sportsbook sportsbook sportsbo…

    PHP 68 142

  2. byteforge9190 byteforge9190 Public

    Hello, This is Full Stack Dev !

  3. angular-unit-testing angular-unit-testing Public

    This projects contains the sample unit test cases in angular application, implemented using Jasmine and Karma.

    TypeScript

  4. angular-sample-project angular-sample-project Public

    Angular Sample Project

    TypeScript

  5. sports-betting-arbitrage sports-betting-arbitrage Public

    Web scraping using selenium in sports betting websites (Novibet, Stoiximan). Calculate arbitrage opportunities for football, basketball and tennis.

    Jupyter Notebook

  6. authorization-server-example authorization-server-example Public

    Sample projects using spring boot authorizationserver

    Java