bet105

Optic service

Our door onto OpticOdds. Scores, lists, and ticket grading are running. DraftKings combos are off. We do not take prices from OpticOdds yet. 22 Sep 2026.

Running

Grade a ticket

Inside our network, no login. Open a tunnel, then ask for the ticket. book is bet105 or bracco. live=true asks for the score while the game is still going. save=false returns the grade and does not store it.

kubectl -n drever-feeds port-forward svc/drever-feeds-optic-grade 8000:8000

curl 'http://127.0.0.1:8000/api/grade/bet105/TICKET_ID'
curl 'http://127.0.0.1:8000/api/grade/bet105/TICKET_ID?live=true'
curl 'http://127.0.0.1:8000/api/grade/bracco/TICKET_ID?save=false'

Inside the cluster the host is drever-feeds-optic-grade.drever-feeds.svc.cluster.local:8000. A bad book is 400. No legs is 404. Missing setup is 503. A failure talking to OpticOdds is 502.

/healthzIs the process up.
/api/grade/{book}/{ticket_id}One ticket, leg by leg, folded into one result.
How a grade is built
  • grade_ticketgrade/grader.py Loads every leg, maps each one, asks OpticOdds once per leg, folds a ticket result, and saves it unless told not to.
  • grade_from_coordinatesgrade/grader.py Grades one leg. Turns our market id into the name OpticOdds expects.
  • fold_ticketgrade/grader.py One lost leg loses the ticket. An unmapped leg makes it ungradeable. A pending leg keeps it pending. A refunded leg drops out of a parlay. A half result on a parlay is partial.
  • market_labelgrade/grader.py Our mappings store moneyline. OpticOdds wants Moneyline.
  • resolvegrade/mapping/mapper.py The same mapper the front end uses. Game, market, and selection name.
  • LeagueResolvergrade/mapping/league_resolver.py Sport and league name to an OpticOdds league id.
  • GradeReadClient.gradegrade/read_client.py Leagues, markets, games on the board, games that have started, and the grader.
  • fetch_ticket_legsgrade/store.py Parsed legs from clv.bet_ticker. Unparsed player props come back as unsupported.
  • upsert_leg_gradegrade/store.py Saves each leg and the ticket. Pending can become Won or Lost later.
Running

Scores

A connection stays open to OpticOdds and writes each result as it arrives. The same event updates the match block: score, period, and clock. Injuries are asked for separately and attached to our player ids. There is no public address. These paths are on the pod.

/stream/statusWhich leagues share a connection, and how many events each has written.
/stream/fixturesMapped games, leagues on the live connection, and games we could not place.
/stream/results/{fixture_id}Latest result. The id is the OpticOdds game id.
/stream/results/kibl/{id}Same result, from our fixture id.
/stream/mblock/{fixture_id}Latest match block: score, clock, and game state.
/stream/mblock/kibl/{id}Same match block, from our fixture id.
/stream/injuries/{fixture_id}Injuries for that game, tied to our participant ids.
/stream/injuries/kibl/{id}Same injuries, from our fixture id.

A league is followed from 30 minutes before a mapped game starts until 8 hours after, and also when OpticOdds says a game in that league is live. Sports include soccer, basketball, football, baseball, tennis, hockey, and a long tail: golf, mixed martial arts, boxing, cricket, rugby, volleyball.

What does the work
  • StreamController.runstream/controller.py Splits leagues across connections, at most ten each.
  • ShardPlannerstream/controller.py Keeps a league on the same connection.
  • ResultsWorkerstream/worker.py One live connection for a sport. A drop reconnects and replays from the last event.
  • build_m_blockstream/mblock.py Match block from the same event. Live 6 hours, finished 15 minutes, unplayed 3 hours.
  • ResultStorestream/store.py Writes the result, the match block, and the injuries together.
  • InjuryPoller.runstream/injuries.py Asks for injuries and binds each row to our participant id.
  • FixtureRegistrymapping/registry.py Reads the fixture record kibl-service keeps, including the OpticOdds id.
Running

Reference lists

Downloads OpticOdds’ own lists and stores them as they arrived. Teams and players cover soccer, basketball, football, baseball, and tennis. Leagues and markets cover every sport. A full download once a day, a smaller refresh every 30 minutes.

/readyzReady after the first full download. Until then it answers 503.
/catalog/statusHow many rows each list has, and when it was last pulled.
src:optic:sports
src:optic:leagues
src:optic:markets
src:optic:sportsbooks
src:optic:teams
src:optic:players
What does the work
  • CatalogController.runcatalog/controller.py Full download at startup and once a day. Smaller refresh every 30 minutes.
  • CatalogPullercatalog/pull.py Walks each list page by page. The same market id under several sports is stored once.
  • CatalogStorecatalog/store.py Swaps the new list in one step. Each list expires after 14 days if the pod stops.
Off

DraftKings combos

Zero pods. The publish switch is unset, so nothing is pulled and nothing is sent to the odds wire. When it is on, the site is combos.danchrow.com, only on our network. A game is addressed by DraftKings’ own event id. Football only.

/todayGames whose start falls on today’s date in New York.
/currentEvery game this pod knows, soonest first.
/{source_event_id}Combos for one game. The number is DraftKings’ event id.
/openbook/combosEvery game, in the shape we would publish.
/combos/statusWhether publishing is on, and the counts.
What does the work
  • CombosController.runcombos/controller.py Walks mapped football games, finds the DraftKings event, and keeps the cards. Publishes only when the switch is on.
  • DkClientcombos/dk_client.py The only code that talks to DraftKings.
  • parse_schedule / parse_comboscombos/dk_schedule.py Matches a game by team names and start time, then reads combo cards.
  • OpticComboTranslator.translateopenbook_translate/optic.py A combo card becomes the messages we would publish. The event is the DraftKings event id.
  • OpenBookPublishercombos/openbook.py Writes to ob:odds:draftkings when the switch is on. The switch is off.
Not running

Calls we can make to OpticOdds

Listed in optic/endpoints.py. Scores, the lists, and the grader use them. A public pass-through at /optic/{key} is not running. Supplier address: https://api.opticodds.com/api/v3.

NamePathUsed for
sports, leagues, markets, sportsbooks, teams, playersreferenceThe daily lists.
fixtures/fixturesNeeds sport and league. The grader also asks by start date.
fixtures_active/fixtures/activeFinds which leagues are live.
odds, player_props, futures, parlay_oddspricingNothing in production polls these.
results/fixtures/resultsThe live path is the open connection.
injuries/injuriesThe scores copy polls this.
grader/grader/oddsNeeds the market name, such as Moneyline. The grade copy uses this.

Still open

  • We do not stream OpticOdds prices.
  • Putting the Optic match block on the customer socket is work in the consumer and the socket.
  • Unparsed player-prop legs come back with the ticket and stay ungraded.
  • DraftKings stays off until we choose to run it. Publishing stays off even then.

KIBL still tells us which OpticOdds game is ours (feed source 186). A Pinnacle grade against our own scores is the grading page.