Surebet Analyzer
Salt okunur bir analiz aracı. Altı anonim kaynaktan herkese açık maç öncesi oranları topluyor, hangi kayıtların aynı maç olduğunu buluyor ve bir bütçenin bu kaynaklar arasında nasıl bölüneceğini kuruşuna kadar hesaplıyor.
Ekranlar demo modundan: oranlar sentetik, kaynaklar Kaynak A–F olarak adlandırılıyor.



Kısaca
- Sorun
- Altı kaynak aynı maçları farklı takım adları, lig adları ve başlama saatleriyle listeliyor; oranlar da bir toplamadan diğerine değişiyor. İki oranı karşılaştırmadan önce sistemin bunların aynı maça ait olduğunu bilmesi, ardından bütçeyi tam kuruşlarla, hatasız bölmesi gerekiyor.
- Benim payım
- Baştan sona kendim geliştirdiğim kişisel bir proje: üç akış protokolü için toplayıcılar, ad normalleştirme ve eşleştirme, kuruş dağıtıcısı ve CP-SAT modeli, FastAPI backend'i, React PWA ve Telegram uyarıları.
- Sonuç ve durum
- MIT lisansıyla GitHub'da açık. Her push ve pull request'te CI'da ruff, strict modda mypy, 1.209 backend testi ve PWA'nın Vitest testleri çalışıyor. Açık kodda hiçbir kaynak adresi yok; demo modu uygulamanın tamamını sentetik veriyle çalıştırıyor.
Ekran ekran uygulama
PWA'nın demo modundaki altı ekranı. Oranlar sentetik: çoğu maç normal bir piyasa gibi fiyatlanıyor, birkaçına ise taramanın işaretleyeceği bir şey olsun diye bilerek hatalı fiyat veriliyor.
Ekran 1, toplam 6

Bulunan fırsatlar
Ana ekran: toplam bütçe, spor ve lig filtreleri, ardından taramanın işaretlediği her maç için bir kart. Kart, o bütçe için hesabın bulduğu net farkı ve ev sahibi, beraberlik, deplasman için en iyi oranı kaynağıyla birlikte gösteriyor.

Tüm maçlar
Eşleşen tüm maçlar, ülke ve lige göre gruplu, takım ya da lige göre aranabilir; her kaynağın oranları yan yana. “Yok”, o kaynağın maçı listelemediği anlamına geliyor.

Fırsat ayrıntısı
Açılmış tek bir maç: bütçe için hesaplanan net fark, ardından ev sahibi, beraberlik ve deplasman için seçilen oran; her biri kendi kaynağından, yanında çözücünün ona ayırdığı tutar kuruş hassasiyetiyle.

Kaynak sağlığı
Her kaynak için bir kart; yalnızca Kaynak A–F adıyla ve akış türüyle (WAMP, WebSocket ya da HTTPS) anılıyor. Kart durumunu, son toplamada kaç maç geldiğini, toplamanın ne kadar sürdüğünü ve son başarılı çalışmayı gösteriyor. Devre dışı bir kaynak öyle işaretleniyor ve veri toplamıyor.

Eşleştirme incelemesi
Sistemin kendi başına eşlemediği ligler, her biri gerekçesiyle: metinden türetilmiş bir ad ya da çözülemeyen bir ülke. Operatör ligi onaylıyor, mevcut bir lige bağlıyor ya da yenisini oluşturuyor; sonradan geri de alabiliyor.

Ayarlar
Bütçe gibi kullanıcıya özel varsayılanlar ve herkes için ortak sınırlar: kaynakların ne sıklıkla toplandığı ve bir oranın yok sayılmadan önce ne kadar eskiyebileceği.
Nasıl çalışıyor
Önce akıştan uyarıya giden yol, ardından bir sayıya güvenilip güvenilemeyeceğini belirleyen iki kısma yakından bakış: kayıtları kaynaklar arasında eşleştirmek ve bütçeyi bölmek.
Akıştan uyarıya
Varsayılan olarak 30 saniyede bir, tek bir asyncio döngüsü tüm kaynakları aynı anda topluyor. Her parti SQLite'a anlık kayıt olarak yazılıyor; WAL modu sayesinde toplayıcılar yazarken API okumaya devam ediyor. Kayıtlar maçlara eşleştiriliyor, dağılımlar için taranıyor ve sonuçlar hem PWA'nın arkasındaki REST API'ye hem de kullanıcıya özel Telegram uyarılarına gidiyor.
Veri yolu
- Oran akışlarıWebSocket üzerinden JSON ve WAMP, HTTPS üzerinden JSON
- Toplayıcılarasyncio, 30 sn'de bir, kaynak başına ayrı hata sınırı
- Anlık kayıtlarWAL modunda SQLite, kaynak başına sağlık
- Lig eşlemesağlayıcı kimliklerinden kanonik liglere
- Maç eşleştiricinormalleştirilmiş adlar, başlama ±3 sa
- Dağılım çözücütam sayı kuruş, kapalı form + tek kuruşluk onarım
- PWA + TelegramREST API, kullanıcıya özel uyarı
Tek maç, birçok ad
Kaynaklar bir maçı tanımlayan neredeyse her şeyde ayrışıyor. Biri lig adına sponsoru ekliyor, öteki ülke önekini; biri Fenerbahçe SK yazıyor, öteki Fenerbahce; başlama saatleri de her akışın saat dilimini nasıl işlediğine göre kayıyor. Yanlış bir eşleşme, kaçırılan bir eşleşmeden kötü: iki farklı maçı tek maç sanmak, aslında olmayan bir fiyat farkı gösterir. Bu yüzden eşleştirici bilerek temkinli.
- Önce ligler. Her kaynağın ligi tek bir kanonik lige eşleniyor: sağlayıcı kimliği ya da elle derlenen kayıt şüphe bırakmıyorsa otomatik, bırakıyorsa inceleme kuyruğundan geçerek. Eşlenmemiş bir lig kaynaklar arasında asla eşleşmiyor.
- Adlar karşılaştırılmadan önce normalleştiriliyor: İ ve ı dahil Türkçe harfler sadeleştiriliyor; SK, FC, A.Ş. gibi ekler atılıyor; Türkçe şehir adları eşleniyor, Münih de München de munich oluyor; bilinen takma adlar açılıyor.
- İki kayıt yalnızca aynı lig içinde, başlama saatleri en fazla 3 saat farklıysa ve hem ev sahibi hem deplasman adları uyuşuyorsa eşleşiyor: aynı, biri diğerinin içinde, birinin kelimeleri diğerininkilerin alt kümesi ya da bulanık benzerlik en az 0,84. Başlama saatleri kayabilen bir kaynak için futbolda pencere 24 saate çıkıyor; o zaman adların aynı olması ya da birinin diğerini içermesi gerekiyor.
- Eşleşmiş bir maç her kaynaktan en fazla bir kayıt içeriyor. Kimliği lig, normalleştirilmiş adlar ve başlama tarihinden üretilen bir hash; böylece bir döngüden diğerine değişmiyor.
Kaynak C
- Gelen lig adı
- Trendyol Süper Lig
- Gelen maç adı
- Galatasaray A.Ş. – Fenerbahçe SK
- Karşılaştırılan hâli
galatasaray–fenerbahce
Kaynak B
- Gelen lig adı
- Super Lig
- Gelen maç adı
- Galatasaray – Fenerbahce
- Karşılaştırılan hâli
galatasaray–fenerbahce
Kaynak A
- Gelen lig adı
- Türkiye Süper Lig
- Gelen maç adı
- Galatasaray Istanbul – Fenerbahce SK
- Karşılaştırılan hâli
galatasaray–fenerbahce
- aynı kanonik lig
- başlama farkı en fazla 3 saat
- ev sahibi ve deplasman adları uyuşuyor
Tek maç galatasaray – fenerbahce
Kaynak A, B ve C'nin oranları yan yana
Bütçeyi kuruşuna kadar bölmek
2,90'lık ondalık oran 1 ÷ 2,90, yani yaklaşık %34,48 olasılık ima ediyor. Her sonuç için bir oran alıp ima edilen olasılıkları topluyorsunuz. Normal bir piyasada toplam %100'ü geçiyor; aradaki fark piyasanın marjı. Toplam %100'ün altındaysa her sonuca 1 ÷ oran ile orantılı tutar koymak, hangi sonuç gelirse gelsin aynı tutarın ödenmesini sağlıyor: bütçenin bu toplama bölümü.
Bu, gerçek sayılarla verilen cevap. Gerçek tutarlar tam kuruş, ödeme kuruşa aşağı yuvarlanıyor ve her kaynağın kendi en düşük tutarı, en yüksek tutarı ve bakiyesi var. Eşit bölüşüm, aramanın yalnızca başladığı yer.
Örnek sayılar
Bir dağılım deneyin
Varsayılan oranlar projenin kendi testlerinden. Herhangi birini değiştirin; dağılım tarayıcınızda canlı taramanın başlangıç ve onarım adımlarıyla yeniden hesaplanıyor, yalnızca taramanın birkaç kaynak için yaptığı düzeltme dışarıda kalıyor. Hiçbir oran çekmiyor, hiçbir yere bir şey göndermiyor.
Tutarlar TL cinsinden. Kesin pay, dört ondalıkta kesilmiş gerçek sayı bölüşümü; tutar tam kuruş; ödeme, tutar × oranın kuruşa aşağı yuvarlanmış hâli.
| Ev sahibi (1) | Beraberlik (X) | Deplasman (2) | |
|---|---|---|---|
| Oran | |||
| Olasılık | %34,48 | %27,78 | %32,26 |
| Kesin pay | 364,8251 | 293,8868 | 341,2880 |
| Tutar | 364,82 | 293,89 | 341,29 |
| Kazanırsa ödeme | 1.057,97 | 1.058,00 | 1.057,99 |
| Ödeme − bütçe | +57,97 (en kötü sonuç) | +58,00 | +57,99 |
İma edilen olasılıkların toplamı %94,52, yani %100'ün altında.
Yuvarlama olmasaydı her sonuçta ödeme 1.057,9928 TL olurdu. Tam kuruşla en küçük ödeme 1.057,97 TL.
Hiçbir tek kuruşluk kaydırma, en büyük kalan yöntemiyle kurulan başlangıcı iyileştirmedi.
850 TL'de başlangıç en kötü sonucu olabileceğinden bir kuruş düşük bırakıyor; onarım adımı bir kuruş kaydırarak bunu düzeltiyor.
Canlı taramada
Her döngü, eşleşen her maç için tüm kaynak kombinasyonlarını deniyor. Tarama, kombinasyon başına bir CP-SAT modeli yerine kapalı formu kullanıyor: her sonuca bir kuruş ayrılıyor, kalan 1 ÷ oran ile orantılı paylaştırılıp aşağı yuvarlanıyor, artan kuruşlar en büyük kalanlara veriliyor, ardından en kötü sonucu iyileştirdiği sürece sonuçlar arasında birer kuruş kaydırılıyor. Testleri sonucu, küçük bütçelerde tüm olasılıkları deneyen bir aramayla karşılaştırıyor. Birkaç kaynak için tarama bu payı kaynağa özgü koşullara göre ayrıca düzeltiyor; yukarıdaki hesaplayıcı aynı adımları bu düzeltme olmadan çalıştırıyor.
Kesin modelde
Elle analiz için bir OR-Tools CP-SAT modeli problemi kapalı formun dışarıda bıraktığı her şeyle çözüyor: tam sayı tutarlar, tutar × oran ÷ 10.000 olarak aşağı yuvarlanan ödemeler (tam sayıya ölçeklenmiş oranlar üzerinde bir bölme kısıtı), kaynak başına en düşük ve en yüksek tutar, bakiye ve ödeme tavanları. Önce en kötü sonucu iyileştiriyor, sonra toplam tutarı küçültüyor, sonra ev sahibi, beraberlik ve deplasmanı sırayla sabitliyor; tek iş parçacığı ve sabit tohumla çalıştığı için aynı girdi her zaman aynı dağılımı veriyor. Cevap kabul edilmeden önce bağımsız bir tam sayı kontrolü her sonucu yeniden hesaplıyor.
Hesabın sınırı
- Hesap yalnızca okunduğu andaki oranlar için geçerli; oranlar iki toplama arasında değişiyor.
- Bir kaynak tutarı reddedebilir ya da sınırlayabilir. Araç hiçbir zaman bahis yapmıyor; yalnızca herkese açık oranları okuyor.
- Aynı altyapıyı kullanan iki marka tek operatör sayılıyor; tarama, bütçeyi yalnızca tek operatörün markaları arasında dağıtan kombinasyonları atlıyor.
Bir kaynak sorun çıkardığında
Toplayıcılar tek bir asyncio döngüsünde eşzamanlı çalışıyor, her biri kendi hata sınırı içinde. Zaman aşımı ya da reddedilen bağlantı o kaynağın kaydına yazılıyor, diğerleri devam ediyor. Her kaynağın sağlığı küçük bir durum makinesiyle izleniyor.
- Her oran okunduğu zamanı taşıyor; bayatlama sınırından eski olanlar taramaya alınmıyor.
- Maçlar başlama saatinden 15 dakika sonra kayıtlardan çıkarılıyor; araç yalnızca maç öncesi oranlara bakıyor.
- Uygulamadan yenileme en fazla 10 saniyede bir çalışıyor; arayüz kaynaklara yüklenemiyor.
- Hesaplar yalnızca davetle açılıyor. Parolalar Argon2id ile saklanıyor, her yazma isteği bir CSRF token'ı taşıyor ve girişler hız sınırına tabi.
Erişilemiyor
Hiç veri partisi getirmedi. Adresi tanımlanmamış bir kaynak burada kalıyor.
Erişilemiyor durumundan Sağlıklı durumuna: ilk sağlam parti
Sağlıklı
Son çalışma başarılı; kullanılan veri onun partisi.
Sağlıklı durumundan Bozulmuş durumuna: başarısız çalışma
Bozulmuş durumundan yeniden Sağlıklı durumuna: sonraki sağlam parti
Bozulmuş
Son çalışma başarısız. Son sağlam parti saklanıyor, bu kaynağa bağlı uyarılar bekliyor.
Her fırsat için tek Telegram mesajı
Uyarılar kullanıcıya özel: herkes kendi eşiğini ve takip edeceği ligleri seçiyor. Her fırsat, oranlar değiştikçe düzenlenen tek bir mesajda kalıyor; ardı ardına yeni mesajlar gitmiyor. Kartın parmak izi, kartta görünen yüzdeyi 25 baz puana yuvarlıyor ve sürekli artan oran yaşını dışarıda bırakıyor; böylece küçük oynamalar düzenleme tetiklemiyor, aynı mesajın iki düzenlemesi arasında da en az 60 saniye oluyor. PWA aynı fırsatları gösteriyor, ana ekrana eklenebiliyor ve çevrimdışı olduğunda bunu söylüyor.
- Yeni
- Kullanıcının eşiğinin üstünde ilk kez görüldü; kart gönderiliyor.
- Aktif
- Hâlâ eşiğin üstünde; aynı mesaj düzenleniyor, kartın yüzdesi yeterince değişirse altına bir yanıt düşülüyor.
- İzlemede
- Eşiğin altında ama eşiğin %80'i olan kapanış tabanının üstünde.
- Bayat
- Arkasındaki bir kaynak sağlıklı değil; kart kapanmak yerine bekliyor.
- Kapandı
- 180 saniye boyunca kesintisiz kapanış tabanının altında kaldı ya da maç başladı. 15 dakika içinde geri gelirse aynı mesaj yeniden açılıyor.
Rakamlarla
- 1.209
- pytest'in topladığı backend testi: birim, özellik, entegrasyon ve kabul
- 21
- PWA için Vitest test dosyası
- 6
- kaynak, Kaynak A–F olarak anonimleştirilmiş
- 3
- saat: iki kaydın aynı maç sayılması için başlama penceresi
- 10.000
- oranların çarpıldığı katsayı; böylece her ödeme tam sayı aritmetiği
- 60
- saniye: aynı Telegram mesajının iki düzenlemesi arasındaki en kısa süre
Kararlar ve bedelleri
Tarama için kapalı form, kesin cevap için CP-SAT
Her döngüde her maçın her kaynak kombinasyonu için bir CP-SAT modeli kesin yoldu. Tarama bunun yerine kapalı form bir dağılım ve tek kuruşluk onarım kullanıyor, tüm olasılıkları deneyen bir aramayla test ediliyor. Bedeli: hızlı yol kaynak başına sınırları, bakiyeleri ve ödeme tavanlarını bilmiyor. Bunlar yalnızca elle analizde kullanılan CP-SAT modelinde var.
Uydurmaktansa kaçırmak
Kayıtlar yalnızca eşlenmiş bir lig içinde karşılaştırılıyor; eşlenmemiş bir lig kaynaklar arasında hiç eşleşmiyor. Bazı gerçek maçlar, biri ligi inceleme kuyruğunda eşleyene kadar eşleşmeden kalıyor; ama iki farklı maç asla tek maç sanılmıyor, sanılsaydı aslında olmayan bir fiyat farkı görünürdü.
Her tutar tam sayı
Tutarlar ve ödemeler tam sayı kuruş, yüzdeler baz puan, oranlar en fazla dört ondalıklı Decimal ve bir ödeme her zaman aşağı yuvarlanıyor. Kod, float kullanılsaydı olacağından daha uzun; buna karşılık bir karttaki her sayı kesin olarak yeniden hesaplanabiliyor ve testler sonucu kuruşu kuruşuna kaba kuvvet aramayla karşılaştırabiliyor.
Bu proje hakkında
Surebet Analyzer, veri eşleştirme ve kesin aritmetik üzerine teknik bir çalışma. Yalnızca herkese açık oranları okuyor: hiçbir hesaba giriş yapmıyor, bahis oynamıyor, para hareket ettirmiyor. Kaynaklar Kaynak A–F olarak anonimleştirildi, açık kodda hiçbir sağlayıcı adresi yok ve bu sayfa hiçbir bahis sitesinin adını vermiyor, bağlantısını içermiyor. Buradaki hiçbir şey tavsiye ya da herhangi bir kazanç vaadi değil.

