Compare commits
60 Commits
cron
..
15c6313246
| Author | SHA1 | Date | |
|---|---|---|---|
| 15c6313246 | |||
| 1b420a425e | |||
| 55e62d8fe5 | |||
| 21e05148c8 | |||
| e001ce9ab5 | |||
| 9481ad7094 | |||
| 1d4aa36602 | |||
| 5574a3c59d | |||
| 94c7a4481a | |||
| 17ace9bd12 | |||
| 2b87669f41 | |||
| 2507678bc0 | |||
| 2b8dce665f | |||
| b6d64b59bf | |||
| f8599bdb9a | |||
| 4dcc4ced50 | |||
| 70fdc066c7 | |||
| f3362f266c | |||
| 8ce8fa5b94 | |||
| c525b12dfd | |||
| 497b5d8d3b | |||
| 4f7090e2d9 | |||
| 5b5f83c8cf | |||
| bfddcaca7d | |||
| 56d560af08 | |||
| 4bc51cfa99 | |||
| fdb8a5d0f0 | |||
| 22596e69f2 | |||
| f32badbd8f | |||
| 5645b38f20 | |||
| 244d8f5366 | |||
| 9bb8f39bca | |||
| 7a1cf14e2f | |||
| 62c797d299 | |||
| 34cc4a6cbb | |||
| 27e96da31d | |||
| 145a8b336b | |||
| 7a8960edb8 | |||
| 691c52f610 | |||
| bc461429f6 | |||
| a338d02244 | |||
| 1623432039 | |||
| 4c7930e9d2 | |||
| ec463cb927 | |||
| eab95c4e5c | |||
| 9027cc9900 | |||
| 3875f2a512 | |||
| 300dceeb4b | |||
| ad01976fb9 | |||
| 6880eb92f5 | |||
| 9e2edd590c | |||
| b5c2edf346 | |||
| bf7473c1e7 | |||
| 1f26a5bf2f | |||
| fb53fdf1df | |||
| 634204acf0 | |||
| df428ed1e8 | |||
| 2ccd6831eb | |||
| 1346924387 | |||
| e4c74025e5 |
@@ -11,13 +11,27 @@ jobs:
|
||||
- name: Kodu Cek
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Docker Build
|
||||
- name: Docker Build (Backend)
|
||||
run: docker build -t iddaai-be:latest .
|
||||
|
||||
- name: Eski Konteyneri Sil
|
||||
run: docker rm -f iddaai-be || true
|
||||
- name: Docker Build (AI Engine)
|
||||
run: docker build -t iddaai-ai-engine:latest ./ai-engine
|
||||
|
||||
- name: Yeni Versiyonu Baslat
|
||||
- name: Eski Konteynerleri Sil
|
||||
run: |
|
||||
docker rm -f iddaai-be || true
|
||||
docker rm -f iddaai-ai-engine || true
|
||||
|
||||
- name: AI Engine'i Baslat
|
||||
run: |
|
||||
docker run -d \
|
||||
--name iddaai-ai-engine \
|
||||
--restart unless-stopped \
|
||||
--network iddaai_iddaai-network \
|
||||
-e DATABASE_URL='${{ secrets.DATABASE_URL }}' \
|
||||
iddaai-ai-engine:latest
|
||||
|
||||
- name: Backend'i Baslat
|
||||
run: |
|
||||
docker run -d \
|
||||
--name iddaai-be \
|
||||
@@ -25,11 +39,21 @@ jobs:
|
||||
--network iddaai_iddaai-network \
|
||||
-p 127.0.0.1:1810:3005 \
|
||||
-e NODE_ENV=production \
|
||||
-e DATABASE_URL='postgresql://iddaai_user:IddaA1_S4crET!@iddaai-postgres:5432/iddaai_db?schema=public' \
|
||||
-e REDIS_HOST='iddaai-redis' \
|
||||
-e REDIS_PORT='6379' \
|
||||
-e REDIS_PASSWORD='IddaA1_Redis_Pass!' \
|
||||
-e DATABASE_URL='${{ secrets.DATABASE_URL }}' \
|
||||
-e REDIS_HOST='${{ secrets.REDIS_HOST }}' \
|
||||
-e REDIS_PORT='${{ secrets.REDIS_PORT }}' \
|
||||
-e REDIS_PASSWORD='${{ secrets.REDIS_PASSWORD }}' \
|
||||
-e AI_ENGINE_URL='http://iddaai-ai-engine:8000' \
|
||||
-e JWT_SECRET='b7V8jM2wP1L5mQxs2RdfFkAsLpI2oG!w' \
|
||||
-e JWT_SECRET='${{ secrets.JWT_SECRET }}' \
|
||||
-e JWT_ACCESS_EXPIRATION='1d' \
|
||||
iddaai-be:latest /bin/sh -c "npx prisma migrate deploy && node dist/src/main.js"
|
||||
|
||||
- name: Saglik Kontrolu
|
||||
run: |
|
||||
sleep 10
|
||||
echo "=== AI Engine logs ==="
|
||||
docker logs --tail 30 iddaai-ai-engine || true
|
||||
echo "=== Backend logs ==="
|
||||
docker logs --tail 30 iddaai-be || true
|
||||
echo "=== AI Engine health ==="
|
||||
docker exec iddaai-ai-engine python -c "import urllib.request; print(urllib.request.urlopen('http://127.0.0.1:8000/health').read().decode())" || echo "AI engine health check failed"
|
||||
+9
-3
@@ -21,7 +21,10 @@ venv/
|
||||
env/
|
||||
|
||||
# Database / Docker Volumes
|
||||
data/
|
||||
/data/
|
||||
ai-engine/data/**/*.csv
|
||||
ai-engine/data/v26_shadow/
|
||||
ai-engine/data/__pycache__/
|
||||
postgres-data/
|
||||
redis-data/
|
||||
|
||||
@@ -42,7 +45,10 @@ uploads/
|
||||
public/uploads/
|
||||
|
||||
# Large Datasets and ML Models
|
||||
ai-engine/models/
|
||||
models/
|
||||
ai-engine/models/*
|
||||
!ai-engine/models/*.py
|
||||
!ai-engine/models/v25/
|
||||
models/*
|
||||
!models/*.py
|
||||
colab_export/
|
||||
|
||||
|
||||
+3
-3
@@ -16,7 +16,7 @@ RUN npm ci
|
||||
COPY . .
|
||||
|
||||
# Generate Prisma client
|
||||
RUN npx prisma generate
|
||||
RUN DATABASE_URL="postgresql://dummy:dummy@localhost/dummy" npx prisma generate
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
@@ -38,7 +38,7 @@ RUN apk add --no-cache --virtual .build-deps python3 make g++ cairo-dev pango-de
|
||||
|
||||
# Copy Prisma schema and generate client
|
||||
COPY prisma ./prisma
|
||||
RUN npx prisma generate
|
||||
RUN DATABASE_URL="postgresql://dummy:dummy@localhost/dummy" npx prisma generate
|
||||
|
||||
# Copy built application
|
||||
COPY --from=builder /app/dist ./dist
|
||||
@@ -47,7 +47,7 @@ COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/src/i18n ./dist/i18n
|
||||
|
||||
# Copy league filter config files (critical: without these, feeder stores ALL matches)
|
||||
COPY top_leagues.json basketball_top_leagues.json ./
|
||||
COPY qualified_leagues.json top_leagues.json basketball_top_leagues.json ./
|
||||
|
||||
# Set environment
|
||||
ENV NODE_ENV=production
|
||||
|
||||
@@ -0,0 +1,874 @@
|
||||
{
|
||||
"meta":{"test_sets":["test"],"test_metrics":[{"best_value":"Min","name":"Logloss"}],"learn_metrics":[{"best_value":"Min","name":"Logloss"}],"launch_mode":"Train","parameters":"","iteration_count":2000,"learn_sets":["learn"],"name":"experiment"},
|
||||
"iterations":[
|
||||
{"learn":[0.692389481],"iteration":0,"passed_time":0.04679785798,"remaining_time":93.54891809,"test":[0.6924099937]},
|
||||
{"learn":[0.6916338586],"iteration":1,"passed_time":0.08350330552,"remaining_time":83.41980222,"test":[0.6916660956]},
|
||||
{"learn":[0.6910159214],"iteration":2,"passed_time":0.132821758,"remaining_time":88.41501689,"test":[0.691108145]},
|
||||
{"learn":[0.6903417151],"iteration":3,"passed_time":0.162826233,"remaining_time":81.25029026,"test":[0.6904585078]},
|
||||
{"learn":[0.6896961461],"iteration":4,"passed_time":0.1969265393,"remaining_time":78.57368918,"test":[0.689812816]},
|
||||
{"learn":[0.6890979366],"iteration":5,"passed_time":0.2309352918,"remaining_time":76.74749531,"test":[0.689192261]},
|
||||
{"learn":[0.6884946167],"iteration":6,"passed_time":0.2693987513,"remaining_time":76.70167304,"test":[0.6886032715]},
|
||||
{"learn":[0.6879503686],"iteration":7,"passed_time":0.3199759681,"remaining_time":79.67401607,"test":[0.6880706742]},
|
||||
{"learn":[0.6874528094],"iteration":8,"passed_time":0.3645802206,"remaining_time":80.65324659,"test":[0.6876192378]},
|
||||
{"learn":[0.6869036785],"iteration":9,"passed_time":0.4116507506,"remaining_time":81.91849936,"test":[0.6870868859]},
|
||||
{"learn":[0.6863761921],"iteration":10,"passed_time":0.4562469316,"remaining_time":82.49774064,"test":[0.6865493528]},
|
||||
{"learn":[0.6859038678],"iteration":11,"passed_time":0.491541699,"remaining_time":81.43207481,"test":[0.686105086]},
|
||||
{"learn":[0.685410175],"iteration":12,"passed_time":0.5221556769,"remaining_time":79.80948692,"test":[0.6856345086]},
|
||||
{"learn":[0.6849483392],"iteration":13,"passed_time":0.5553110353,"remaining_time":78.77483686,"test":[0.6852027185]},
|
||||
{"learn":[0.6845417792],"iteration":14,"passed_time":0.5952927147,"remaining_time":78.77706925,"test":[0.6848238481]},
|
||||
{"learn":[0.6841038875],"iteration":15,"passed_time":0.6300274185,"remaining_time":78.12339989,"test":[0.6844045699]},
|
||||
{"learn":[0.6836957422],"iteration":16,"passed_time":0.662600544,"remaining_time":77.29040464,"test":[0.6840077621]},
|
||||
{"learn":[0.6832947461],"iteration":17,"passed_time":0.7004221698,"remaining_time":77.12426337,"test":[0.6836197496]},
|
||||
{"learn":[0.6829014105],"iteration":18,"passed_time":0.7300844347,"remaining_time":76.12090869,"test":[0.6832475033]},
|
||||
{"learn":[0.6825264546],"iteration":19,"passed_time":0.7641559459,"remaining_time":75.65143865,"test":[0.6829012069]},
|
||||
{"learn":[0.6822106577],"iteration":20,"passed_time":0.8040792063,"remaining_time":75.77489282,"test":[0.6825880966]},
|
||||
{"learn":[0.6818649349],"iteration":21,"passed_time":0.8356039756,"remaining_time":75.12839381,"test":[0.6822424968]},
|
||||
{"learn":[0.6815467855],"iteration":22,"passed_time":0.8861440327,"remaining_time":76.16985881,"test":[0.6819180513]},
|
||||
{"learn":[0.6812293319],"iteration":23,"passed_time":0.920219319,"remaining_time":75.76472393,"test":[0.6816384467]},
|
||||
{"learn":[0.6808837443],"iteration":24,"passed_time":0.960164738,"remaining_time":75.8530143,"test":[0.6813262593]},
|
||||
{"learn":[0.6805816494],"iteration":25,"passed_time":0.9895547925,"remaining_time":75.13004463,"test":[0.6810353411]},
|
||||
{"learn":[0.6803209634],"iteration":26,"passed_time":1.025550161,"remaining_time":74.94112844,"test":[0.6808138172]},
|
||||
{"learn":[0.6800350862],"iteration":27,"passed_time":1.060852064,"remaining_time":74.71429535,"test":[0.6805550049]},
|
||||
{"learn":[0.6797703947],"iteration":28,"passed_time":1.10467538,"remaining_time":75.07983357,"test":[0.680347991]},
|
||||
{"learn":[0.6794926675],"iteration":29,"passed_time":1.141766834,"remaining_time":74.97602208,"test":[0.680089679]},
|
||||
{"learn":[0.6792251865],"iteration":30,"passed_time":1.180421588,"remaining_time":74.9758099,"test":[0.6798451919]},
|
||||
{"learn":[0.6789670166],"iteration":31,"passed_time":1.213674604,"remaining_time":74.64098814,"test":[0.6796090443]},
|
||||
{"learn":[0.678722402],"iteration":32,"passed_time":1.245848393,"remaining_time":74.26011482,"test":[0.6793890865]},
|
||||
{"learn":[0.678476935],"iteration":33,"passed_time":1.287262512,"remaining_time":74.43406171,"test":[0.6791683772]},
|
||||
{"learn":[0.6782297335],"iteration":34,"passed_time":1.327473991,"remaining_time":74.52818262,"test":[0.6789766369]},
|
||||
{"learn":[0.6780226701],"iteration":35,"passed_time":1.3760549,"remaining_time":75.07143955,"test":[0.6787930242]},
|
||||
{"learn":[0.6778291026],"iteration":36,"passed_time":1.427620019,"remaining_time":75.74102965,"test":[0.6786087714]},
|
||||
{"learn":[0.6776045324],"iteration":37,"passed_time":1.468182407,"remaining_time":75.80457587,"test":[0.6784161299]},
|
||||
{"learn":[0.6773969079],"iteration":38,"passed_time":1.508647379,"remaining_time":75.85788487,"test":[0.6782227897]},
|
||||
{"learn":[0.6771819602],"iteration":39,"passed_time":1.549435187,"remaining_time":75.92232419,"test":[0.6780242369]},
|
||||
{"learn":[0.6769816736],"iteration":40,"passed_time":1.586036608,"remaining_time":75.78160282,"test":[0.6778499631]},
|
||||
{"learn":[0.6767984027],"iteration":41,"passed_time":1.621458864,"remaining_time":75.59086802,"test":[0.6776975784]},
|
||||
{"learn":[0.6766201184],"iteration":42,"passed_time":1.663424818,"remaining_time":75.70517136,"test":[0.6775231674]},
|
||||
{"learn":[0.6764394377],"iteration":43,"passed_time":1.70110089,"remaining_time":75.62166686,"test":[0.6773582124]},
|
||||
{"learn":[0.6762698797],"iteration":44,"passed_time":1.739954496,"remaining_time":75.59135644,"test":[0.6772234666]},
|
||||
{"learn":[0.6760974263],"iteration":45,"passed_time":1.776461223,"remaining_time":75.46098325,"test":[0.6770659843]},
|
||||
{"learn":[0.6759245179],"iteration":46,"passed_time":1.819761638,"remaining_time":75.61690381,"test":[0.6769049529]},
|
||||
{"learn":[0.6757673909],"iteration":47,"passed_time":1.869479807,"remaining_time":76.02551217,"test":[0.6767664194]},
|
||||
{"learn":[0.6756172628],"iteration":48,"passed_time":1.916010121,"remaining_time":76.28848462,"test":[0.6766584917]},
|
||||
{"learn":[0.675474531],"iteration":49,"passed_time":1.953635244,"remaining_time":76.19177452,"test":[0.6765507257]},
|
||||
{"learn":[0.6753286933],"iteration":50,"passed_time":1.993876686,"remaining_time":76.19736591,"test":[0.6764489911]},
|
||||
{"learn":[0.6751900513],"iteration":51,"passed_time":2.038943041,"remaining_time":76.38194316,"test":[0.6763947956]},
|
||||
{"learn":[0.6750574835],"iteration":52,"passed_time":2.080276765,"remaining_time":76.42073325,"test":[0.6762778712]},
|
||||
{"learn":[0.6749329567],"iteration":53,"passed_time":2.158576742,"remaining_time":77.78871001,"test":[0.6761865366]},
|
||||
{"learn":[0.6748033265],"iteration":54,"passed_time":2.220619687,"remaining_time":78.52918711,"test":[0.6760679685]},
|
||||
{"learn":[0.6746797823],"iteration":55,"passed_time":2.286959228,"remaining_time":79.39015604,"test":[0.6759774874]},
|
||||
{"learn":[0.674535525],"iteration":56,"passed_time":2.328472096,"remaining_time":79.3723032,"test":[0.6758500622]},
|
||||
{"learn":[0.6744256514],"iteration":57,"passed_time":2.367031568,"remaining_time":79.25474665,"test":[0.6757625065]},
|
||||
{"learn":[0.674310819],"iteration":58,"passed_time":2.409161286,"remaining_time":79.25732298,"test":[0.6756876412]},
|
||||
{"learn":[0.6741967947],"iteration":59,"passed_time":2.444825903,"remaining_time":79.04937087,"test":[0.6756151069]},
|
||||
{"learn":[0.6740879654],"iteration":60,"passed_time":2.48484996,"remaining_time":78.98564055,"test":[0.6755303655]},
|
||||
{"learn":[0.6739772476],"iteration":61,"passed_time":2.521603395,"remaining_time":78.8204416,"test":[0.6754565036]},
|
||||
{"learn":[0.67388281],"iteration":62,"passed_time":2.554102332,"remaining_time":78.5285114,"test":[0.6753738983]},
|
||||
{"learn":[0.6737789726],"iteration":63,"passed_time":2.593937938,"remaining_time":78.46662263,"test":[0.6752897299]},
|
||||
{"learn":[0.6736812332],"iteration":64,"passed_time":2.623889155,"remaining_time":78.11116175,"test":[0.6752115539]},
|
||||
{"learn":[0.6735930009],"iteration":65,"passed_time":2.660795108,"remaining_time":77.96935967,"test":[0.6751595431]},
|
||||
{"learn":[0.6734947116],"iteration":66,"passed_time":2.695822592,"remaining_time":77.77649358,"test":[0.6750764658]},
|
||||
{"learn":[0.6733961481],"iteration":67,"passed_time":2.725876686,"remaining_time":77.44696703,"test":[0.6750179194]},
|
||||
{"learn":[0.6732990195],"iteration":68,"passed_time":2.761848366,"remaining_time":77.29172746,"test":[0.6749408803]},
|
||||
{"learn":[0.6732133575],"iteration":69,"passed_time":2.791847449,"remaining_time":76.97522253,"test":[0.6748795802]},
|
||||
{"learn":[0.673111539],"iteration":70,"passed_time":2.824541003,"remaining_time":76.73999429,"test":[0.674790372]},
|
||||
{"learn":[0.6730080451],"iteration":71,"passed_time":2.861023716,"remaining_time":76.61185729,"test":[0.6747239773]},
|
||||
{"learn":[0.6729157861],"iteration":72,"passed_time":2.897136588,"remaining_time":76.47646857,"test":[0.6746701254]},
|
||||
{"learn":[0.6728347949],"iteration":73,"passed_time":2.935718661,"remaining_time":76.40802894,"test":[0.6746120937]},
|
||||
{"learn":[0.6727640693],"iteration":74,"passed_time":3.040023476,"remaining_time":78.02726921,"test":[0.6745550085]},
|
||||
{"learn":[0.6726808811],"iteration":75,"passed_time":3.097341794,"remaining_time":78.41165279,"test":[0.6744855074]},
|
||||
{"learn":[0.6726029645],"iteration":76,"passed_time":3.152948955,"remaining_time":78.74182909,"test":[0.6744264172]},
|
||||
{"learn":[0.6725356026],"iteration":77,"passed_time":3.216126808,"remaining_time":79.24866314,"test":[0.674381715]},
|
||||
{"learn":[0.6724606887],"iteration":78,"passed_time":3.256861302,"remaining_time":79.19532355,"test":[0.6743331681]},
|
||||
{"learn":[0.6723849561],"iteration":79,"passed_time":3.305679851,"remaining_time":79.33631641,"test":[0.67428564]},
|
||||
{"learn":[0.6723050519],"iteration":80,"passed_time":3.348083566,"remaining_time":79.32064647,"test":[0.6742202413]},
|
||||
{"learn":[0.6722508802],"iteration":81,"passed_time":3.38129387,"remaining_time":79.08928832,"test":[0.6741620971]},
|
||||
{"learn":[0.6721773904],"iteration":82,"passed_time":3.41660066,"remaining_time":78.91112609,"test":[0.6741109453]},
|
||||
{"learn":[0.6721007598],"iteration":83,"passed_time":3.48099347,"remaining_time":79.39980344,"test":[0.6740556003]},
|
||||
{"learn":[0.6720353564],"iteration":84,"passed_time":3.535359896,"remaining_time":79.64957884,"test":[0.6740146772]},
|
||||
{"learn":[0.6719790902],"iteration":85,"passed_time":3.581806996,"remaining_time":79.71603012,"test":[0.673983295]},
|
||||
{"learn":[0.6719140024],"iteration":86,"passed_time":3.612293661,"remaining_time":79.42893993,"test":[0.6739595301]},
|
||||
{"learn":[0.6718573633],"iteration":87,"passed_time":3.644530261,"remaining_time":79.18570293,"test":[0.6739336659]},
|
||||
{"learn":[0.671795602],"iteration":88,"passed_time":3.67809653,"remaining_time":78.97575809,"test":[0.673890361]},
|
||||
{"learn":[0.6717369134],"iteration":89,"passed_time":3.712417516,"remaining_time":78.78574951,"test":[0.673863586]},
|
||||
{"learn":[0.6716711079],"iteration":90,"passed_time":3.743502971,"remaining_time":78.53128759,"test":[0.6738190616]},
|
||||
{"learn":[0.6716070843],"iteration":91,"passed_time":3.775351679,"remaining_time":78.2975109,"test":[0.6737799295]},
|
||||
{"learn":[0.6715517232],"iteration":92,"passed_time":3.806186247,"remaining_time":78.04728142,"test":[0.6737364374]},
|
||||
{"learn":[0.6714957378],"iteration":93,"passed_time":3.83798807,"remaining_time":77.82133257,"test":[0.6737093719]},
|
||||
{"learn":[0.6714364567],"iteration":94,"passed_time":3.871278973,"remaining_time":77.62933099,"test":[0.6736630475]},
|
||||
{"learn":[0.6713881758],"iteration":95,"passed_time":3.913531039,"remaining_time":77.6183656,"test":[0.67364367]},
|
||||
{"learn":[0.6713336502],"iteration":96,"passed_time":3.945433866,"remaining_time":77.40371802,"test":[0.6735998081]},
|
||||
{"learn":[0.6712700267],"iteration":97,"passed_time":3.989716281,"remaining_time":77.43306496,"test":[0.6735526984]},
|
||||
{"learn":[0.6712154424],"iteration":98,"passed_time":4.020621946,"remaining_time":77.20406384,"test":[0.6735012924]},
|
||||
{"learn":[0.6711600413],"iteration":99,"passed_time":4.053732144,"remaining_time":77.02091074,"test":[0.6734818024]},
|
||||
{"learn":[0.6711060533],"iteration":100,"passed_time":4.084124711,"remaining_time":76.78963194,"test":[0.6734379341]},
|
||||
{"learn":[0.6710494943],"iteration":101,"passed_time":4.116434744,"remaining_time":76.59797199,"test":[0.6734059869]},
|
||||
{"learn":[0.6709936897],"iteration":102,"passed_time":4.148330356,"remaining_time":76.40177365,"test":[0.6733740852]},
|
||||
{"learn":[0.6709472183],"iteration":103,"passed_time":4.176511193,"remaining_time":76.14101176,"test":[0.6733330971]},
|
||||
{"learn":[0.6708914508],"iteration":104,"passed_time":4.2025065,"remaining_time":75.84523636,"test":[0.6733060254]},
|
||||
{"learn":[0.6708388195],"iteration":105,"passed_time":4.232975206,"remaining_time":75.63448151,"test":[0.6732755898]},
|
||||
{"learn":[0.6707885854],"iteration":106,"passed_time":4.261364958,"remaining_time":75.39031649,"test":[0.6732294722]},
|
||||
{"learn":[0.6707454167],"iteration":107,"passed_time":4.290824713,"remaining_time":75.1688922,"test":[0.6732035176]},
|
||||
{"learn":[0.6706973013],"iteration":108,"passed_time":4.324192493,"remaining_time":75.01878903,"test":[0.673196437]},
|
||||
{"learn":[0.6706577031],"iteration":109,"passed_time":4.351512102,"remaining_time":74.76688976,"test":[0.6731652709]},
|
||||
{"learn":[0.67061108],"iteration":110,"passed_time":4.38641502,"remaining_time":74.64808984,"test":[0.673138808]},
|
||||
{"learn":[0.6705625485],"iteration":111,"passed_time":4.424063991,"remaining_time":74.57707871,"test":[0.6731062725]},
|
||||
{"learn":[0.6705146484],"iteration":112,"passed_time":4.45863849,"remaining_time":74.45531709,"test":[0.6730726625]},
|
||||
{"learn":[0.6704704423],"iteration":113,"passed_time":4.497153675,"remaining_time":74.40027922,"test":[0.6730285927]},
|
||||
{"learn":[0.6704155922],"iteration":114,"passed_time":4.533368584,"remaining_time":74.30782417,"test":[0.6729872702]},
|
||||
{"learn":[0.6703687117],"iteration":115,"passed_time":4.564651269,"remaining_time":74.13623268,"test":[0.6729721425]},
|
||||
{"learn":[0.6703324232],"iteration":116,"passed_time":4.596824343,"remaining_time":73.98136956,"test":[0.6729564624]},
|
||||
{"learn":[0.6702884624],"iteration":117,"passed_time":4.628377967,"remaining_time":73.81870623,"test":[0.6729312424]},
|
||||
{"learn":[0.670253478],"iteration":118,"passed_time":4.668052254,"remaining_time":73.78660748,"test":[0.6729354345]},
|
||||
{"learn":[0.6702140804],"iteration":119,"passed_time":4.692108266,"remaining_time":73.50969617,"test":[0.6729085401]},
|
||||
{"learn":[0.6701682529],"iteration":120,"passed_time":4.723741667,"remaining_time":73.354633,"test":[0.6728898322]},
|
||||
{"learn":[0.6701320588],"iteration":121,"passed_time":4.756626425,"remaining_time":73.22085595,"test":[0.6728773638]},
|
||||
{"learn":[0.6700939824],"iteration":122,"passed_time":4.788008428,"remaining_time":73.06578714,"test":[0.6728618874]},
|
||||
{"learn":[0.6700655902],"iteration":123,"passed_time":4.815546648,"remaining_time":72.85456058,"test":[0.6728540413]},
|
||||
{"learn":[0.6700190743],"iteration":124,"passed_time":4.843186806,"remaining_time":72.64780209,"test":[0.6728441291]},
|
||||
{"learn":[0.6699792296],"iteration":125,"passed_time":4.875548614,"remaining_time":72.51411192,"test":[0.672815631]},
|
||||
{"learn":[0.6699379404],"iteration":126,"passed_time":4.916953662,"remaining_time":72.51538748,"test":[0.6728082021]},
|
||||
{"learn":[0.669895454],"iteration":127,"passed_time":4.952918369,"remaining_time":72.43643115,"test":[0.6727900064]},
|
||||
{"learn":[0.6698563938],"iteration":128,"passed_time":4.991585558,"remaining_time":72.39733782,"test":[0.6727649552]},
|
||||
{"learn":[0.6698215571],"iteration":129,"passed_time":5.028084166,"remaining_time":72.32705685,"test":[0.6727467657]},
|
||||
{"learn":[0.6697857067],"iteration":130,"passed_time":5.059198996,"remaining_time":72.18048033,"test":[0.6727396032]},
|
||||
{"learn":[0.6697449303],"iteration":131,"passed_time":5.096035515,"remaining_time":72.1166238,"test":[0.6727245271]},
|
||||
{"learn":[0.6697052425],"iteration":132,"passed_time":5.125282589,"remaining_time":71.94663604,"test":[0.6726955143]},
|
||||
{"learn":[0.6696695553],"iteration":133,"passed_time":5.156392608,"remaining_time":71.80469109,"test":[0.67269209]},
|
||||
{"learn":[0.6696269265],"iteration":134,"passed_time":5.190402292,"remaining_time":71.70444647,"test":[0.672677932]},
|
||||
{"learn":[0.6695969271],"iteration":135,"passed_time":5.221466142,"remaining_time":71.56480065,"test":[0.6726540285]},
|
||||
{"learn":[0.6695489786],"iteration":136,"passed_time":5.251144663,"remaining_time":71.40790151,"test":[0.6726288583]},
|
||||
{"learn":[0.6695173859],"iteration":137,"passed_time":5.274361693,"remaining_time":71.16566285,"test":[0.6725863431]},
|
||||
{"learn":[0.6694811164],"iteration":138,"passed_time":5.309398952,"remaining_time":71.08483058,"test":[0.6725837967]},
|
||||
{"learn":[0.6694477439],"iteration":139,"passed_time":5.344693175,"remaining_time":71.00806646,"test":[0.6725772977]},
|
||||
{"learn":[0.6694082161],"iteration":140,"passed_time":5.377737126,"remaining_time":70.90222211,"test":[0.6725685594]},
|
||||
{"learn":[0.6693679185],"iteration":141,"passed_time":5.416087925,"remaining_time":70.8668406,"test":[0.6725553829]},
|
||||
{"learn":[0.6693341916],"iteration":142,"passed_time":5.452286939,"remaining_time":70.80347444,"test":[0.6725484347]},
|
||||
{"learn":[0.6692933159],"iteration":143,"passed_time":5.490006789,"remaining_time":70.7600875,"test":[0.6725306172]},
|
||||
{"learn":[0.6692619696],"iteration":144,"passed_time":5.521869859,"remaining_time":70.64185233,"test":[0.672543149]},
|
||||
{"learn":[0.6692229289],"iteration":145,"passed_time":5.553520721,"remaining_time":70.5221056,"test":[0.6725196247]},
|
||||
{"learn":[0.6691840164],"iteration":146,"passed_time":5.582178524,"remaining_time":70.3658286,"test":[0.6725226452]},
|
||||
{"learn":[0.6691581406],"iteration":147,"passed_time":5.611368671,"remaining_time":70.21793769,"test":[0.6725056913]},
|
||||
{"learn":[0.6691177196],"iteration":148,"passed_time":5.636941079,"remaining_time":70.02669757,"test":[0.6724771476]},
|
||||
{"learn":[0.6690851126],"iteration":149,"passed_time":5.673704689,"remaining_time":69.97569117,"test":[0.6724439435]},
|
||||
{"learn":[0.6690518144],"iteration":150,"passed_time":5.706346207,"remaining_time":69.87439826,"test":[0.672442532]},
|
||||
{"learn":[0.6690149711],"iteration":151,"passed_time":5.738210991,"remaining_time":69.76456521,"test":[0.6724303064]},
|
||||
{"learn":[0.668993877],"iteration":152,"passed_time":5.765951318,"remaining_time":69.60596133,"test":[0.6724235788]},
|
||||
{"learn":[0.6689596579],"iteration":153,"passed_time":5.795573467,"remaining_time":69.47161442,"test":[0.6724294499]},
|
||||
{"learn":[0.6689372651],"iteration":154,"passed_time":5.81744896,"remaining_time":69.24640858,"test":[0.6724285935]},
|
||||
{"learn":[0.6689003045],"iteration":155,"passed_time":5.853529431,"remaining_time":69.19171968,"test":[0.6724172017]},
|
||||
{"learn":[0.6688680182],"iteration":156,"passed_time":5.888380392,"remaining_time":69.12283479,"test":[0.6724130745]},
|
||||
{"learn":[0.6688348164],"iteration":157,"passed_time":5.924601775,"remaining_time":69.07035741,"test":[0.6723860878]},
|
||||
{"learn":[0.6687947046],"iteration":158,"passed_time":5.964531924,"remaining_time":69.06102687,"test":[0.6723707604]},
|
||||
{"learn":[0.6687605251],"iteration":159,"passed_time":5.996805452,"remaining_time":68.9632627,"test":[0.6723566111]},
|
||||
{"learn":[0.668726253],"iteration":160,"passed_time":6.022341459,"remaining_time":68.78935368,"test":[0.6723469906]},
|
||||
{"learn":[0.6686862718],"iteration":161,"passed_time":6.05082584,"remaining_time":68.65072774,"test":[0.6723287161]},
|
||||
{"learn":[0.668663478],"iteration":162,"passed_time":6.079027554,"remaining_time":68.51026759,"test":[0.6723155898]},
|
||||
{"learn":[0.6686399521],"iteration":163,"passed_time":6.108511297,"remaining_time":68.38552891,"test":[0.6722970834]},
|
||||
{"learn":[0.6686058279],"iteration":164,"passed_time":6.140719309,"remaining_time":68.29224202,"test":[0.6722872244]},
|
||||
{"learn":[0.6685761282],"iteration":165,"passed_time":6.169540017,"remaining_time":68.16226742,"test":[0.6722800481]},
|
||||
{"learn":[0.6685469327],"iteration":166,"passed_time":6.2020892,"remaining_time":68.07442817,"test":[0.6722550973]},
|
||||
{"learn":[0.6685157003],"iteration":167,"passed_time":6.231576547,"remaining_time":67.95385854,"test":[0.6722394313]},
|
||||
{"learn":[0.6684805143],"iteration":168,"passed_time":6.263261652,"remaining_time":67.85817802,"test":[0.6722204135]},
|
||||
{"learn":[0.6684485765],"iteration":169,"passed_time":6.295102833,"remaining_time":67.7649305,"test":[0.6721982148]},
|
||||
{"learn":[0.6684144429],"iteration":170,"passed_time":6.325415964,"remaining_time":67.65605729,"test":[0.6721971176]},
|
||||
{"learn":[0.6683849752],"iteration":171,"passed_time":6.35697084,"remaining_time":67.56129474,"test":[0.6721880705]},
|
||||
{"learn":[0.6683568537],"iteration":172,"passed_time":6.395913563,"remaining_time":67.5452837,"test":[0.672179176]},
|
||||
{"learn":[0.6683266628],"iteration":173,"passed_time":6.437330522,"remaining_time":67.55497433,"test":[0.6721769709]},
|
||||
{"learn":[0.6682937842],"iteration":174,"passed_time":6.472195712,"remaining_time":67.49575528,"test":[0.6721693215]},
|
||||
{"learn":[0.6682657097],"iteration":175,"passed_time":6.503044842,"remaining_time":67.395192,"test":[0.6721581386]},
|
||||
{"learn":[0.6682301443],"iteration":176,"passed_time":6.533528251,"remaining_time":67.29164972,"test":[0.6721638661]},
|
||||
{"learn":[0.6681995916],"iteration":177,"passed_time":6.562589882,"remaining_time":67.17437509,"test":[0.6721598475]},
|
||||
{"learn":[0.6681658267],"iteration":178,"passed_time":6.590816982,"remaining_time":67.04959623,"test":[0.6721433342]},
|
||||
{"learn":[0.6681422687],"iteration":179,"passed_time":6.624646227,"remaining_time":66.98253407,"test":[0.6721335599]},
|
||||
{"learn":[0.6681216601],"iteration":180,"passed_time":6.655147334,"remaining_time":66.88239227,"test":[0.6721300594]},
|
||||
{"learn":[0.6680899019],"iteration":181,"passed_time":6.687788902,"remaining_time":66.80439684,"test":[0.6721153533]},
|
||||
{"learn":[0.6680676394],"iteration":182,"passed_time":6.718057043,"remaining_time":66.7033314,"test":[0.6721076397]},
|
||||
{"learn":[0.6680413672],"iteration":183,"passed_time":6.751300957,"remaining_time":66.6324051,"test":[0.6721009911]},
|
||||
{"learn":[0.6680088406],"iteration":184,"passed_time":6.784288393,"remaining_time":66.55936991,"test":[0.6720999252]},
|
||||
{"learn":[0.6679873982],"iteration":185,"passed_time":6.810905309,"remaining_time":66.42463565,"test":[0.6720953028]},
|
||||
{"learn":[0.6679663544],"iteration":186,"passed_time":6.832974292,"remaining_time":66.24696466,"test":[0.6720942505]},
|
||||
{"learn":[0.6679417375],"iteration":187,"passed_time":6.867184511,"remaining_time":66.18796986,"test":[0.6720856237]},
|
||||
{"learn":[0.6679100197],"iteration":188,"passed_time":6.918652024,"remaining_time":66.29459691,"test":[0.6720876136]},
|
||||
{"learn":[0.667881208],"iteration":189,"passed_time":6.96948149,"remaining_time":66.39348156,"test":[0.6720880182]},
|
||||
{"learn":[0.6678475427],"iteration":190,"passed_time":7.018176318,"remaining_time":66.47058094,"test":[0.6720743856]},
|
||||
{"learn":[0.6678310341],"iteration":191,"passed_time":7.074099623,"remaining_time":66.61443812,"test":[0.6720598415]},
|
||||
{"learn":[0.6678060257],"iteration":192,"passed_time":7.117099742,"remaining_time":66.63522919,"test":[0.6720563492]},
|
||||
{"learn":[0.6677789336],"iteration":193,"passed_time":7.191058554,"remaining_time":66.94356571,"test":[0.6720389527]},
|
||||
{"learn":[0.6677478773],"iteration":194,"passed_time":7.2421897,"remaining_time":67.03667902,"test":[0.6720317324]},
|
||||
{"learn":[0.6677212408],"iteration":195,"passed_time":7.282401129,"remaining_time":67.02781447,"test":[0.672000736]},
|
||||
{"learn":[0.667704316],"iteration":196,"passed_time":7.317019235,"remaining_time":66.96744,"test":[0.6719895017]},
|
||||
{"learn":[0.6676819639],"iteration":197,"passed_time":7.351194179,"remaining_time":66.90329248,"test":[0.6719725302]},
|
||||
{"learn":[0.6676554448],"iteration":198,"passed_time":7.389840926,"remaining_time":66.87991712,"test":[0.6719770493]},
|
||||
{"learn":[0.6676318346],"iteration":199,"passed_time":7.432994652,"remaining_time":66.89695187,"test":[0.6719667172]},
|
||||
{"learn":[0.6676074705],"iteration":200,"passed_time":7.471295231,"remaining_time":66.86995085,"test":[0.6719511616]},
|
||||
{"learn":[0.6675849784],"iteration":201,"passed_time":7.506377837,"remaining_time":66.8141948,"test":[0.6719427289]},
|
||||
{"learn":[0.6675631744],"iteration":202,"passed_time":7.540821494,"remaining_time":66.75298633,"test":[0.6719299116]},
|
||||
{"learn":[0.6675397619],"iteration":203,"passed_time":7.56808212,"remaining_time":66.62880141,"test":[0.6719106583]},
|
||||
{"learn":[0.6675169086],"iteration":204,"passed_time":7.605676901,"remaining_time":66.59604896,"test":[0.6718967065]},
|
||||
{"learn":[0.6674864762],"iteration":205,"passed_time":7.638300222,"remaining_time":66.51995436,"test":[0.671890967]},
|
||||
{"learn":[0.6674670714],"iteration":206,"passed_time":7.665554951,"remaining_time":66.39777791,"test":[0.6718896293]},
|
||||
{"learn":[0.6674375599],"iteration":207,"passed_time":7.700277678,"remaining_time":66.34085384,"test":[0.6718883534]},
|
||||
{"learn":[0.6674148457],"iteration":208,"passed_time":7.734145802,"remaining_time":66.27681881,"test":[0.6718827289]},
|
||||
{"learn":[0.6673974446],"iteration":209,"passed_time":7.766232144,"remaining_time":66.19788351,"test":[0.6718763224]},
|
||||
{"learn":[0.6673812139],"iteration":210,"passed_time":7.796801222,"remaining_time":66.1065279,"test":[0.67187262]},
|
||||
{"learn":[0.6673515687],"iteration":211,"passed_time":7.831891449,"remaining_time":66.05387693,"test":[0.6718590402]},
|
||||
{"learn":[0.6673197956],"iteration":212,"passed_time":7.871259964,"remaining_time":66.0372843,"test":[0.6718455115]},
|
||||
{"learn":[0.6672900754],"iteration":213,"passed_time":7.910110502,"remaining_time":66.01615587,"test":[0.6718253747]},
|
||||
{"learn":[0.6672550009],"iteration":214,"passed_time":7.951342226,"remaining_time":66.01463197,"test":[0.671794877]},
|
||||
{"learn":[0.6672271563],"iteration":215,"passed_time":7.989001461,"remaining_time":65.98323429,"test":[0.6717873786]},
|
||||
{"learn":[0.667204521],"iteration":216,"passed_time":8.025973631,"remaining_time":65.94613357,"test":[0.6717765089]},
|
||||
{"learn":[0.667181968],"iteration":217,"passed_time":8.058434478,"remaining_time":65.87215707,"test":[0.6717616726]},
|
||||
{"learn":[0.6671640023],"iteration":218,"passed_time":8.087145957,"remaining_time":65.76806826,"test":[0.6717499215]},
|
||||
{"learn":[0.66714351],"iteration":219,"passed_time":8.112590578,"remaining_time":65.63823286,"test":[0.6717326052]},
|
||||
{"learn":[0.6671167156],"iteration":220,"passed_time":8.148644349,"remaining_time":65.59474342,"test":[0.6717161937]},
|
||||
{"learn":[0.6670915937],"iteration":221,"passed_time":8.197662625,"remaining_time":65.65515382,"test":[0.6717056951]},
|
||||
{"learn":[0.6670595279],"iteration":222,"passed_time":8.239228431,"remaining_time":65.65519696,"test":[0.6717021438]},
|
||||
{"learn":[0.667033994],"iteration":223,"passed_time":8.268371203,"remaining_time":65.55637168,"test":[0.6716868488]},
|
||||
{"learn":[0.6670008246],"iteration":224,"passed_time":8.298555216,"remaining_time":65.46638004,"test":[0.6716751909]},
|
||||
{"learn":[0.6669858319],"iteration":225,"passed_time":8.327401394,"remaining_time":65.36641625,"test":[0.671670116]},
|
||||
{"learn":[0.6669553964],"iteration":226,"passed_time":8.357648377,"remaining_time":65.27802014,"test":[0.6716558757]},
|
||||
{"learn":[0.6669274683],"iteration":227,"passed_time":8.384989701,"remaining_time":65.16755154,"test":[0.6716559962]},
|
||||
{"learn":[0.666896348],"iteration":228,"passed_time":8.418297538,"remaining_time":65.1039517,"test":[0.6716487875]},
|
||||
{"learn":[0.6668698686],"iteration":229,"passed_time":8.453919972,"remaining_time":65.05842761,"test":[0.6716427451]},
|
||||
{"learn":[0.6668513411],"iteration":230,"passed_time":8.49049033,"remaining_time":65.02024846,"test":[0.6716323255]},
|
||||
{"learn":[0.6668309985],"iteration":231,"passed_time":8.523986676,"remaining_time":64.95865708,"test":[0.6716303547]},
|
||||
{"learn":[0.6668058585],"iteration":232,"passed_time":8.550998228,"remaining_time":64.84812819,"test":[0.6716309509]},
|
||||
{"learn":[0.6667845908],"iteration":233,"passed_time":8.575382398,"remaining_time":64.71848425,"test":[0.6716215401]},
|
||||
{"learn":[0.6667582863],"iteration":234,"passed_time":8.607602961,"remaining_time":64.64859245,"test":[0.6716162103]},
|
||||
{"learn":[0.6667332943],"iteration":235,"passed_time":8.6353786,"remaining_time":64.54579597,"test":[0.6716135097]},
|
||||
{"learn":[0.6667070085],"iteration":236,"passed_time":8.66085309,"remaining_time":64.42651476,"test":[0.6716156696]},
|
||||
{"learn":[0.6666907315],"iteration":237,"passed_time":8.691362456,"remaining_time":64.34529684,"test":[0.6716020054]},
|
||||
{"learn":[0.6666633028],"iteration":238,"passed_time":8.719983169,"remaining_time":64.25058728,"test":[0.6715921704]},
|
||||
{"learn":[0.6666406707],"iteration":239,"passed_time":8.746012652,"remaining_time":64.13742611,"test":[0.6715804466]},
|
||||
{"learn":[0.6666134624],"iteration":240,"passed_time":8.773898765,"remaining_time":64.03853912,"test":[0.6715882966]},
|
||||
{"learn":[0.6665850522],"iteration":241,"passed_time":8.803292064,"remaining_time":63.9511878,"test":[0.6715753942]},
|
||||
{"learn":[0.6665631193],"iteration":242,"passed_time":8.833976809,"remaining_time":63.87365125,"test":[0.6715752261]},
|
||||
{"learn":[0.6665412643],"iteration":243,"passed_time":8.862338006,"remaining_time":63.7797768,"test":[0.6715625509]},
|
||||
{"learn":[0.6665168385],"iteration":244,"passed_time":8.892424073,"remaining_time":63.69879285,"test":[0.6715628214]},
|
||||
{"learn":[0.6664904845],"iteration":245,"passed_time":8.932383667,"remaining_time":63.68862175,"test":[0.6715601629]},
|
||||
{"learn":[0.6664678274],"iteration":246,"passed_time":8.962911123,"remaining_time":63.61126801,"test":[0.6715576255]},
|
||||
{"learn":[0.6664539777],"iteration":247,"passed_time":8.991624872,"remaining_time":63.52147894,"test":[0.6715550274]},
|
||||
{"learn":[0.6664334121],"iteration":248,"passed_time":9.021847081,"remaining_time":63.44278811,"test":[0.6715448645]},
|
||||
{"learn":[0.6664121724],"iteration":249,"passed_time":9.05121341,"remaining_time":63.35849387,"test":[0.6715308166]},
|
||||
{"learn":[0.666392034],"iteration":250,"passed_time":9.085113431,"remaining_time":63.30622865,"test":[0.671519334]},
|
||||
{"learn":[0.666366899],"iteration":251,"passed_time":9.110250512,"remaining_time":63.19332498,"test":[0.6715184071]},
|
||||
{"learn":[0.6663414098],"iteration":252,"passed_time":9.137253573,"remaining_time":63.09399997,"test":[0.6715163019]},
|
||||
{"learn":[0.6663157816],"iteration":253,"passed_time":9.174559864,"remaining_time":63.06606899,"test":[0.6715096094]},
|
||||
{"learn":[0.6662989799],"iteration":254,"passed_time":9.196898204,"remaining_time":62.93563673,"test":[0.6714992963]},
|
||||
{"learn":[0.6662696102],"iteration":255,"passed_time":9.238149902,"remaining_time":62.9348962,"test":[0.6714917256]},
|
||||
{"learn":[0.6662479711],"iteration":256,"passed_time":9.267818291,"remaining_time":62.85528125,"test":[0.671477406]},
|
||||
{"learn":[0.6662231874],"iteration":257,"passed_time":9.297538986,"remaining_time":62.77640665,"test":[0.6714741542]},
|
||||
{"learn":[0.6661947927],"iteration":258,"passed_time":9.324772701,"remaining_time":62.68119411,"test":[0.6714576155]},
|
||||
{"learn":[0.6661669951],"iteration":259,"passed_time":9.357824574,"remaining_time":62.62544138,"test":[0.6714473645]},
|
||||
{"learn":[0.6661426137],"iteration":260,"passed_time":9.388345461,"remaining_time":62.55299907,"test":[0.6714427232]},
|
||||
{"learn":[0.6661216749],"iteration":261,"passed_time":9.427290804,"remaining_time":62.53676114,"test":[0.6714364275]},
|
||||
{"learn":[0.6660983123],"iteration":262,"passed_time":9.461913185,"remaining_time":62.49179925,"test":[0.6714339587]},
|
||||
{"learn":[0.6660803402],"iteration":263,"passed_time":9.496090562,"remaining_time":62.44398945,"test":[0.6714336287]},
|
||||
{"learn":[0.6660617842],"iteration":264,"passed_time":9.524189317,"remaining_time":62.35648477,"test":[0.6714283568]},
|
||||
{"learn":[0.6660443878],"iteration":265,"passed_time":9.55372419,"remaining_time":62.27878852,"test":[0.6714271895]},
|
||||
{"learn":[0.6660176079],"iteration":266,"passed_time":9.590356068,"remaining_time":62.2475171,"test":[0.671413471]},
|
||||
{"learn":[0.6659967546],"iteration":267,"passed_time":9.620235131,"remaining_time":62.17256436,"test":[0.6714072396]},
|
||||
{"learn":[0.6659751467],"iteration":268,"passed_time":9.645948482,"remaining_time":62.0711406,"test":[0.6714002677]},
|
||||
{"learn":[0.6659539329],"iteration":269,"passed_time":9.682675077,"remaining_time":62.04084401,"test":[0.6714001163]},
|
||||
{"learn":[0.6659263951],"iteration":270,"passed_time":9.711914203,"remaining_time":61.96272936,"test":[0.6713933952]},
|
||||
{"learn":[0.6659038921],"iteration":271,"passed_time":9.739142426,"remaining_time":61.87219894,"test":[0.6713926761]},
|
||||
{"learn":[0.6658767418],"iteration":272,"passed_time":9.768751964,"remaining_time":61.79719649,"test":[0.6713836619]},
|
||||
{"learn":[0.6658510507],"iteration":273,"passed_time":9.804576737,"remaining_time":61.76167682,"test":[0.6713772112]},
|
||||
{"learn":[0.6658210119],"iteration":274,"passed_time":9.848653906,"remaining_time":61.77791996,"test":[0.6713603715]},
|
||||
{"learn":[0.6657963011],"iteration":275,"passed_time":9.88663261,"remaining_time":61.75563268,"test":[0.6713560246]},
|
||||
{"learn":[0.6657748552],"iteration":276,"passed_time":9.925808942,"remaining_time":61.74068161,"test":[0.6713837913]},
|
||||
{"learn":[0.6657490013],"iteration":277,"passed_time":9.965409489,"remaining_time":61.72818396,"test":[0.6713684274]},
|
||||
{"learn":[0.665732402],"iteration":278,"passed_time":9.99537326,"remaining_time":61.65604796,"test":[0.6713619356]},
|
||||
{"learn":[0.6657118786],"iteration":279,"passed_time":10.02216777,"remaining_time":61.5647449,"test":[0.6713584836]},
|
||||
{"learn":[0.665684467],"iteration":280,"passed_time":10.05593393,"remaining_time":61.51654955,"test":[0.6713673572]},
|
||||
{"learn":[0.6656584634],"iteration":281,"passed_time":10.08025153,"remaining_time":61.41089406,"test":[0.6713625568]},
|
||||
{"learn":[0.6656309991],"iteration":282,"passed_time":10.11102202,"remaining_time":61.34496401,"test":[0.6713542652]},
|
||||
{"learn":[0.6656073482],"iteration":283,"passed_time":10.14714598,"remaining_time":61.31162855,"test":[0.6713512017]},
|
||||
{"learn":[0.6655890957],"iteration":284,"passed_time":10.17528061,"remaining_time":61.23019734,"test":[0.671342038]},
|
||||
{"learn":[0.6655665563],"iteration":285,"passed_time":10.2021403,"remaining_time":61.14149818,"test":[0.6713279798]},
|
||||
{"learn":[0.6655452454],"iteration":286,"passed_time":10.23423432,"remaining_time":61.08447174,"test":[0.6713123285]},
|
||||
{"learn":[0.6655255286],"iteration":287,"passed_time":10.26481698,"remaining_time":61.0186343,"test":[0.6713035326]},
|
||||
{"learn":[0.6655053548],"iteration":288,"passed_time":10.29945844,"remaining_time":60.97707056,"test":[0.6713022203]},
|
||||
{"learn":[0.6654893396],"iteration":289,"passed_time":10.32366496,"remaining_time":60.87402441,"test":[0.671296041]},
|
||||
{"learn":[0.6654648912],"iteration":290,"passed_time":10.35344703,"remaining_time":60.80426453,"test":[0.6712829551]},
|
||||
{"learn":[0.6654442759],"iteration":291,"passed_time":10.3949915,"remaining_time":60.8035804,"test":[0.6712769751]},
|
||||
{"learn":[0.6654173127],"iteration":292,"passed_time":10.43148765,"remaining_time":60.77320621,"test":[0.6712702915]},
|
||||
{"learn":[0.6653914518],"iteration":293,"passed_time":10.47162738,"remaining_time":60.76393303,"test":[0.6712379343]},
|
||||
{"learn":[0.6653648946],"iteration":294,"passed_time":10.50360107,"remaining_time":60.70725362,"test":[0.6712192006]},
|
||||
{"learn":[0.665344141],"iteration":295,"passed_time":10.53460819,"remaining_time":60.64517686,"test":[0.6712074061]},
|
||||
{"learn":[0.6653140817],"iteration":296,"passed_time":10.57659448,"remaining_time":60.64626395,"test":[0.6711953324]},
|
||||
{"learn":[0.665295365],"iteration":297,"passed_time":10.61260262,"remaining_time":60.61291829,"test":[0.6711891001]},
|
||||
{"learn":[0.6652787488],"iteration":298,"passed_time":10.63910358,"remaining_time":60.52546889,"test":[0.6711870526]},
|
||||
{"learn":[0.6652502991],"iteration":299,"passed_time":10.6681867,"remaining_time":60.45305797,"test":[0.6711812809]},
|
||||
{"learn":[0.665231168],"iteration":300,"passed_time":10.70260503,"remaining_time":60.41104967,"test":[0.6711768946]},
|
||||
{"learn":[0.6652136682],"iteration":301,"passed_time":10.72952096,"remaining_time":60.32690925,"test":[0.6711845012]},
|
||||
{"learn":[0.6651903001],"iteration":302,"passed_time":10.76489952,"remaining_time":60.29054288,"test":[0.6711869636]},
|
||||
{"learn":[0.6651697153],"iteration":303,"passed_time":10.80197155,"remaining_time":60.26363073,"test":[0.671186884]},
|
||||
{"learn":[0.6651525958],"iteration":304,"passed_time":10.82922271,"remaining_time":60.18207375,"test":[0.6711890401]},
|
||||
{"learn":[0.6651322685],"iteration":305,"passed_time":10.8578399,"remaining_time":60.10843394,"test":[0.6711868603]},
|
||||
{"learn":[0.6651113828],"iteration":306,"passed_time":10.89228879,"remaining_time":60.06724727,"test":[0.6711900892]},
|
||||
{"learn":[0.6650886807],"iteration":307,"passed_time":10.93056436,"remaining_time":60.04712628,"test":[0.6711884242]},
|
||||
{"learn":[0.6650622251],"iteration":308,"passed_time":10.97231236,"remaining_time":60.04589061,"test":[0.6711837119]},
|
||||
{"learn":[0.6650429987],"iteration":309,"passed_time":11.00296848,"remaining_time":59.98392494,"test":[0.6711766645]},
|
||||
{"learn":[0.665015513],"iteration":310,"passed_time":11.03002276,"remaining_time":59.90259947,"test":[0.671172959]},
|
||||
{"learn":[0.6650019022],"iteration":311,"passed_time":11.05828865,"remaining_time":59.82817707,"test":[0.6711740433]},
|
||||
{"learn":[0.664979951],"iteration":312,"passed_time":11.09287745,"remaining_time":59.78812863,"test":[0.6711715069]},
|
||||
{"learn":[0.6649549638],"iteration":313,"passed_time":11.1177757,"remaining_time":59.69608229,"test":[0.6711589843]},
|
||||
{"learn":[0.6649340455],"iteration":314,"passed_time":11.14959087,"remaining_time":59.64146228,"test":[0.6711446402]},
|
||||
{"learn":[0.6649162445],"iteration":315,"passed_time":11.18718772,"remaining_time":59.61779784,"test":[0.6711415366]},
|
||||
{"learn":[0.6649048119],"iteration":316,"passed_time":11.21179073,"remaining_time":59.52505932,"test":[0.6711359351]},
|
||||
{"learn":[0.6648796463],"iteration":317,"passed_time":11.24311165,"remaining_time":59.46828238,"test":[0.671143361]},
|
||||
{"learn":[0.6648605481],"iteration":318,"passed_time":11.27486028,"remaining_time":59.41391889,"test":[0.6711353638]},
|
||||
{"learn":[0.6648429084],"iteration":319,"passed_time":11.30400807,"remaining_time":59.34604237,"test":[0.6711444387]},
|
||||
{"learn":[0.6648238121],"iteration":320,"passed_time":11.33488419,"remaining_time":59.28744721,"test":[0.6711487352]},
|
||||
{"learn":[0.6647969527],"iteration":321,"passed_time":11.36208838,"remaining_time":59.20988915,"test":[0.67114436]},
|
||||
{"learn":[0.6647854723],"iteration":322,"passed_time":11.39429642,"remaining_time":59.15862259,"test":[0.6711444722]},
|
||||
{"learn":[0.6647589304],"iteration":323,"passed_time":11.4363998,"remaining_time":59.15866068,"test":[0.6711325635]},
|
||||
{"learn":[0.6647429024],"iteration":324,"passed_time":11.47751019,"remaining_time":59.15332173,"test":[0.6711269403]},
|
||||
{"learn":[0.6647237508],"iteration":325,"passed_time":11.5136833,"remaining_time":59.12241054,"test":[0.6711154078]},
|
||||
{"learn":[0.6647059396],"iteration":326,"passed_time":11.54795566,"remaining_time":59.08174257,"test":[0.6711203043]},
|
||||
{"learn":[0.664686288],"iteration":327,"passed_time":11.57245915,"remaining_time":58.99131613,"test":[0.6711241333]},
|
||||
{"learn":[0.6646532527],"iteration":328,"passed_time":11.60790333,"remaining_time":58.95685857,"test":[0.6711213497]},
|
||||
{"learn":[0.6646306438],"iteration":329,"passed_time":11.63787346,"remaining_time":58.89469298,"test":[0.6711231641]},
|
||||
{"learn":[0.6646098516],"iteration":330,"passed_time":11.66805718,"remaining_time":58.83379887,"test":[0.6711049215]},
|
||||
{"learn":[0.6645858284],"iteration":331,"passed_time":11.70070223,"remaining_time":58.78545579,"test":[0.6711031963]},
|
||||
{"learn":[0.6645707188],"iteration":332,"passed_time":11.724753,"remaining_time":58.69418391,"test":[0.6710996314]},
|
||||
{"learn":[0.6645485788],"iteration":333,"passed_time":11.75795297,"remaining_time":58.64895104,"test":[0.6710867309]},
|
||||
{"learn":[0.6645305696],"iteration":334,"passed_time":11.78053066,"remaining_time":58.55099567,"test":[0.6710914578]},
|
||||
{"learn":[0.6645108881],"iteration":335,"passed_time":11.81570271,"remaining_time":58.51586106,"test":[0.6710929585]},
|
||||
{"learn":[0.6644923286],"iteration":336,"passed_time":11.8448851,"remaining_time":58.45116888,"test":[0.6710984779]},
|
||||
{"learn":[0.6644805222],"iteration":337,"passed_time":11.86964023,"remaining_time":58.36491734,"test":[0.6710923199]},
|
||||
{"learn":[0.6644572776],"iteration":338,"passed_time":11.90591446,"remaining_time":58.33546879,"test":[0.6710893917]},
|
||||
{"learn":[0.6644320741],"iteration":339,"passed_time":11.94145444,"remaining_time":58.30239521,"test":[0.6710923306]},
|
||||
{"learn":[0.6644115048],"iteration":340,"passed_time":11.98658051,"remaining_time":58.31594449,"test":[0.6710927901]},
|
||||
{"learn":[0.6643949013],"iteration":341,"passed_time":12.02038848,"remaining_time":58.27428098,"test":[0.6711092802]},
|
||||
{"learn":[0.6643619789],"iteration":342,"passed_time":12.06653941,"remaining_time":58.29229096,"test":[0.6711012995]},
|
||||
{"learn":[0.6643389502],"iteration":343,"passed_time":12.12283646,"remaining_time":58.35877087,"test":[0.6711015305]},
|
||||
{"learn":[0.6643088915],"iteration":344,"passed_time":12.17733618,"remaining_time":58.41591705,"test":[0.6710975574]},
|
||||
{"learn":[0.664286972],"iteration":345,"passed_time":12.22133732,"remaining_time":58.42223099,"test":[0.6710899474]},
|
||||
{"learn":[0.664274149],"iteration":346,"passed_time":12.2642467,"remaining_time":58.42305415,"test":[0.671085152]},
|
||||
{"learn":[0.6642536926],"iteration":347,"passed_time":12.30091895,"remaining_time":58.39401755,"test":[0.6710814533]},
|
||||
{"learn":[0.6642357634],"iteration":348,"passed_time":12.32484094,"remaining_time":58.30462002,"test":[0.6710701892]},
|
||||
{"learn":[0.664207914],"iteration":349,"passed_time":12.35469303,"remaining_time":58.24355287,"test":[0.67105503]},
|
||||
{"learn":[0.6641853097],"iteration":350,"passed_time":12.40148755,"remaining_time":58.26225919,"test":[0.6710527861]},
|
||||
{"learn":[0.6641654917],"iteration":351,"passed_time":12.43803877,"remaining_time":58.23263605,"test":[0.6710508715]},
|
||||
{"learn":[0.664143804],"iteration":352,"passed_time":12.47995438,"remaining_time":58.22800245,"test":[0.6710560803]},
|
||||
{"learn":[0.6641290647],"iteration":353,"passed_time":12.51241326,"remaining_time":58.17918707,"test":[0.6710465693]},
|
||||
{"learn":[0.6641117244],"iteration":354,"passed_time":12.5417829,"remaining_time":58.11614893,"test":[0.6710440741]},
|
||||
{"learn":[0.6640880219],"iteration":355,"passed_time":12.5692936,"remaining_time":58.0447154,"test":[0.6710496913]},
|
||||
{"learn":[0.6640669415],"iteration":356,"passed_time":12.5976392,"remaining_time":57.97737034,"test":[0.6710404659]},
|
||||
{"learn":[0.6640462999],"iteration":357,"passed_time":12.62815847,"remaining_time":57.92021287,"test":[0.6710293986]},
|
||||
{"learn":[0.664030296],"iteration":358,"passed_time":12.65342509,"remaining_time":57.8391938,"test":[0.6710353817]},
|
||||
{"learn":[0.6640028542],"iteration":359,"passed_time":12.68233453,"remaining_time":57.77507954,"test":[0.6710271815]},
|
||||
{"learn":[0.6639813347],"iteration":360,"passed_time":12.72037964,"remaining_time":57.75263774,"test":[0.6710288077]},
|
||||
{"learn":[0.6639597941],"iteration":361,"passed_time":12.744473,"remaining_time":57.66698004,"test":[0.6710169894]},
|
||||
{"learn":[0.6639429832],"iteration":362,"passed_time":12.77086568,"remaining_time":57.59203063,"test":[0.6710119848]},
|
||||
{"learn":[0.6639222708],"iteration":363,"passed_time":12.81194554,"remaining_time":57.58335961,"test":[0.6710114775]},
|
||||
{"learn":[0.6639065546],"iteration":364,"passed_time":12.84133287,"remaining_time":57.52213492,"test":[0.6710013614]},
|
||||
{"learn":[0.6638823236],"iteration":365,"passed_time":12.87057337,"remaining_time":57.46042866,"test":[0.6709985657]},
|
||||
{"learn":[0.6638648195],"iteration":366,"passed_time":12.8971183,"remaining_time":57.38690512,"test":[0.6709948954]},
|
||||
{"learn":[0.6638436235],"iteration":367,"passed_time":12.93825161,"remaining_time":57.37833324,"test":[0.6709970591]},
|
||||
{"learn":[0.6638208732],"iteration":368,"passed_time":12.97444296,"remaining_time":57.3477411,"test":[0.6709739289]},
|
||||
{"learn":[0.6637956357],"iteration":369,"passed_time":13.00974924,"remaining_time":57.31321963,"test":[0.6709754911]},
|
||||
{"learn":[0.6637718453],"iteration":370,"passed_time":13.03832239,"remaining_time":57.24912984,"test":[0.6709717066]},
|
||||
{"learn":[0.663756918],"iteration":371,"passed_time":13.07843077,"remaining_time":57.23571316,"test":[0.67096845]},
|
||||
{"learn":[0.6637353525],"iteration":372,"passed_time":13.11729124,"remaining_time":57.21671005,"test":[0.6709739445]},
|
||||
{"learn":[0.6637143112],"iteration":373,"passed_time":13.14745329,"remaining_time":57.15978354,"test":[0.6709728881]},
|
||||
{"learn":[0.6636956547],"iteration":374,"passed_time":13.18118022,"remaining_time":57.11844761,"test":[0.6709694284]},
|
||||
{"learn":[0.663680995],"iteration":375,"passed_time":13.20539229,"remaining_time":57.03605604,"test":[0.6709604166]},
|
||||
{"learn":[0.66366728],"iteration":376,"passed_time":13.23563977,"remaining_time":56.97995583,"test":[0.6709605025]},
|
||||
{"learn":[0.6636487567],"iteration":377,"passed_time":13.27428255,"remaining_time":56.96001665,"test":[0.6709603727]},
|
||||
{"learn":[0.6636266904],"iteration":378,"passed_time":13.30625754,"remaining_time":56.91146033,"test":[0.670944339]},
|
||||
{"learn":[0.6636116064],"iteration":379,"passed_time":13.33327871,"remaining_time":56.84187241,"test":[0.6709447187]},
|
||||
{"learn":[0.6635902746],"iteration":380,"passed_time":13.36632239,"remaining_time":56.79809961,"test":[0.6709538679]},
|
||||
{"learn":[0.6635654896],"iteration":381,"passed_time":13.39639051,"remaining_time":56.74177969,"test":[0.6709640912]},
|
||||
{"learn":[0.6635393029],"iteration":382,"passed_time":13.42189438,"remaining_time":56.66632694,"test":[0.6709534847]},
|
||||
{"learn":[0.6635171734],"iteration":383,"passed_time":13.46730432,"remaining_time":56.6749057,"test":[0.6709471555]},
|
||||
{"learn":[0.663500789],"iteration":384,"passed_time":13.50832777,"remaining_time":56.66480351,"test":[0.6709506783]},
|
||||
{"learn":[0.663477743],"iteration":385,"passed_time":13.54029627,"remaining_time":56.61667921,"test":[0.6709546729]},
|
||||
{"learn":[0.6634584806],"iteration":386,"passed_time":13.56996301,"remaining_time":56.5590448,"test":[0.670930774]},
|
||||
{"learn":[0.6634337499],"iteration":387,"passed_time":13.59835745,"remaining_time":56.4962686,"test":[0.6709287322]},
|
||||
{"learn":[0.6634135584],"iteration":388,"passed_time":13.6279617,"remaining_time":56.43867943,"test":[0.6709198643]},
|
||||
{"learn":[0.6633868455],"iteration":389,"passed_time":13.65633448,"remaining_time":56.37615005,"test":[0.6709220389]},
|
||||
{"learn":[0.6633755323],"iteration":390,"passed_time":13.68565529,"remaining_time":56.31769658,"test":[0.6709230923]},
|
||||
{"learn":[0.663356103],"iteration":391,"passed_time":13.71789303,"remaining_time":56.27135714,"test":[0.670930414]},
|
||||
{"learn":[0.6633337631],"iteration":392,"passed_time":13.75060752,"remaining_time":56.2270389,"test":[0.6709354296]},
|
||||
{"learn":[0.663319422],"iteration":393,"passed_time":13.77167974,"remaining_time":56.13532403,"test":[0.6709351544]},
|
||||
{"learn":[0.6632911566],"iteration":394,"passed_time":13.80416242,"remaining_time":56.09033084,"test":[0.6709414935]},
|
||||
{"learn":[0.6632687875],"iteration":395,"passed_time":13.82525369,"remaining_time":55.9992599,"test":[0.6709445943]},
|
||||
{"learn":[0.6632431997],"iteration":396,"passed_time":13.85836516,"remaining_time":55.95707646,"test":[0.6709475685]},
|
||||
{"learn":[0.6632189331],"iteration":397,"passed_time":13.88898168,"remaining_time":55.90489613,"test":[0.6709533591]},
|
||||
{"learn":[0.663201035],"iteration":398,"passed_time":13.91726355,"remaining_time":55.84345598,"test":[0.6709592222]},
|
||||
{"learn":[0.6631898553],"iteration":399,"passed_time":13.95316828,"remaining_time":55.81267311,"test":[0.6709508704]},
|
||||
{"learn":[0.6631712482],"iteration":400,"passed_time":13.99418497,"remaining_time":55.80224881,"test":[0.6709479912]},
|
||||
{"learn":[0.663143025],"iteration":401,"passed_time":14.0253575,"remaining_time":55.75254052,"test":[0.6709417519]},
|
||||
{"learn":[0.663121538],"iteration":402,"passed_time":14.04844239,"remaining_time":55.67087467,"test":[0.6709476082]},
|
||||
{"learn":[0.6631087792],"iteration":403,"passed_time":14.0761289,"remaining_time":55.60767753,"test":[0.6709480979]},
|
||||
{"learn":[0.6630859067],"iteration":404,"passed_time":14.10555105,"remaining_time":55.55149118,"test":[0.6709448724]},
|
||||
{"learn":[0.663066483],"iteration":405,"passed_time":14.1427661,"remaining_time":55.52603242,"test":[0.6709421934]},
|
||||
{"learn":[0.6630443652],"iteration":406,"passed_time":14.18285552,"remaining_time":55.51176619,"test":[0.6709386261]},
|
||||
{"learn":[0.6630250376],"iteration":407,"passed_time":14.21458769,"remaining_time":55.46476372,"test":[0.6709461564]},
|
||||
{"learn":[0.6630007822],"iteration":408,"passed_time":14.24035708,"remaining_time":55.39464088,"test":[0.670934384]},
|
||||
{"learn":[0.6629768728],"iteration":409,"passed_time":14.26711915,"remaining_time":55.32858403,"test":[0.6709312987]},
|
||||
{"learn":[0.6629528093],"iteration":410,"passed_time":14.29943785,"remaining_time":55.28420133,"test":[0.670931806]},
|
||||
{"learn":[0.6629260936],"iteration":411,"passed_time":14.32489173,"remaining_time":55.21341763,"test":[0.6709286111]},
|
||||
{"learn":[0.6629102182],"iteration":412,"passed_time":14.35119075,"remaining_time":55.14610101,"test":[0.6709224729]},
|
||||
{"learn":[0.6628863488],"iteration":413,"passed_time":14.37946054,"remaining_time":55.08653242,"test":[0.6709236504]},
|
||||
{"learn":[0.6628648972],"iteration":414,"passed_time":14.41005914,"remaining_time":55.03600899,"test":[0.6709245901]},
|
||||
{"learn":[0.6628454339],"iteration":415,"passed_time":14.45103793,"remaining_time":55.02510598,"test":[0.6709463437]},
|
||||
{"learn":[0.6628200274],"iteration":416,"passed_time":14.48428995,"remaining_time":54.98472661,"test":[0.6709567049]},
|
||||
{"learn":[0.6627942591],"iteration":417,"passed_time":14.5135184,"remaining_time":54.92915339,"test":[0.670945606]},
|
||||
{"learn":[0.6627744647],"iteration":418,"passed_time":14.53698524,"remaining_time":54.85196578,"test":[0.6709479298]},
|
||||
{"learn":[0.662765485],"iteration":419,"passed_time":14.56542473,"remaining_time":54.79374067,"test":[0.6709464351]},
|
||||
{"learn":[0.6627503257],"iteration":420,"passed_time":14.58728594,"remaining_time":54.71098455,"test":[0.6709414048]},
|
||||
{"learn":[0.6627323029],"iteration":421,"passed_time":14.61501375,"remaining_time":54.65045425,"test":[0.6709414427]},
|
||||
{"learn":[0.6627111509],"iteration":422,"passed_time":14.64231614,"remaining_time":54.58849302,"test":[0.6709296343]},
|
||||
{"learn":[0.6626785863],"iteration":423,"passed_time":14.66665432,"remaining_time":54.51567739,"test":[0.670924721]},
|
||||
{"learn":[0.6626576561],"iteration":424,"passed_time":14.69050441,"remaining_time":54.44128104,"test":[0.670906284]},
|
||||
{"learn":[0.6626363113],"iteration":425,"passed_time":14.71910475,"remaining_time":54.38467341,"test":[0.6708996826]},
|
||||
{"learn":[0.6626181065],"iteration":426,"passed_time":14.73941058,"remaining_time":54.2976413,"test":[0.6708987677]},
|
||||
{"learn":[0.66259794],"iteration":427,"passed_time":14.77242451,"remaining_time":54.25759657,"test":[0.670909526]},
|
||||
{"learn":[0.6625765658],"iteration":428,"passed_time":14.79088688,"remaining_time":54.1642967,"test":[0.6709033226]},
|
||||
{"learn":[0.6625526572],"iteration":429,"passed_time":14.82430966,"remaining_time":54.12596783,"test":[0.6708750209]},
|
||||
{"learn":[0.66253135],"iteration":430,"passed_time":14.84439175,"remaining_time":54.03909666,"test":[0.6708752079]},
|
||||
{"learn":[0.6625035695],"iteration":431,"passed_time":14.8764415,"remaining_time":53.99597284,"test":[0.6708776566]},
|
||||
{"learn":[0.662480212],"iteration":432,"passed_time":14.90666075,"remaining_time":53.94627573,"test":[0.6708736133]},
|
||||
{"learn":[0.6624611632],"iteration":433,"passed_time":14.93845927,"remaining_time":53.90236684,"test":[0.6708754298]},
|
||||
{"learn":[0.6624332625],"iteration":434,"passed_time":14.98024104,"remaining_time":53.89443041,"test":[0.6708751084]},
|
||||
{"learn":[0.6624120584],"iteration":435,"passed_time":15.00605075,"remaining_time":53.82904442,"test":[0.6708642042]},
|
||||
{"learn":[0.6623941719],"iteration":436,"passed_time":15.03384083,"remaining_time":53.77092268,"test":[0.6708610465]},
|
||||
{"learn":[0.6623766304],"iteration":437,"passed_time":15.05972545,"remaining_time":53.70614417,"test":[0.6708574768]},
|
||||
{"learn":[0.6623623329],"iteration":438,"passed_time":15.08505889,"remaining_time":53.63958297,"test":[0.6708557953]},
|
||||
{"learn":[0.6623442925],"iteration":439,"passed_time":15.11080547,"remaining_time":53.57467393,"test":[0.670871378]},
|
||||
{"learn":[0.6623212715],"iteration":440,"passed_time":15.13466304,"remaining_time":53.50326458,"test":[0.6708640187]},
|
||||
{"learn":[0.6623025941],"iteration":441,"passed_time":15.16037021,"remaining_time":53.43859001,"test":[0.6708700565]},
|
||||
{"learn":[0.6622749791],"iteration":442,"passed_time":15.18471062,"remaining_time":53.36928767,"test":[0.6708667534]},
|
||||
{"learn":[0.6622534499],"iteration":443,"passed_time":15.21140556,"remaining_time":53.30843931,"test":[0.6708675383]},
|
||||
{"learn":[0.6622305473],"iteration":444,"passed_time":15.23498219,"remaining_time":53.23684787,"test":[0.6708740175]},
|
||||
{"learn":[0.6622059333],"iteration":445,"passed_time":15.26647355,"remaining_time":53.19304911,"test":[0.6708774523]},
|
||||
{"learn":[0.6621871707],"iteration":446,"passed_time":15.28793136,"remaining_time":53.11444609,"test":[0.6708697231]},
|
||||
{"learn":[0.6621638454],"iteration":447,"passed_time":15.31613827,"remaining_time":53.05947899,"test":[0.6708614971]},
|
||||
{"learn":[0.6621511296],"iteration":448,"passed_time":15.33689091,"remaining_time":52.9788815,"test":[0.6708607946]},
|
||||
{"learn":[0.6621349978],"iteration":449,"passed_time":15.36674634,"remaining_time":52.92990406,"test":[0.6708740865]},
|
||||
{"learn":[0.6621120424],"iteration":450,"passed_time":15.393642,"remaining_time":52.87084582,"test":[0.6708729562]},
|
||||
{"learn":[0.6620958271],"iteration":451,"passed_time":15.42984657,"remaining_time":52.84381082,"test":[0.6708674017]},
|
||||
{"learn":[0.6620793528],"iteration":452,"passed_time":15.46956188,"remaining_time":52.82872456,"test":[0.6708693088]},
|
||||
{"learn":[0.6620572713],"iteration":453,"passed_time":15.49032259,"remaining_time":52.74898396,"test":[0.6708712037]},
|
||||
{"learn":[0.6620395025],"iteration":454,"passed_time":15.52379393,"remaining_time":52.71266289,"test":[0.6708703905]},
|
||||
{"learn":[0.6620188044],"iteration":455,"passed_time":15.55053135,"remaining_time":52.65355352,"test":[0.6708577595]},
|
||||
{"learn":[0.6620017347],"iteration":456,"passed_time":15.57735398,"remaining_time":52.59487352,"test":[0.6708493546]},
|
||||
{"learn":[0.6619811454],"iteration":457,"passed_time":15.60434803,"remaining_time":52.53690973,"test":[0.6708523777]},
|
||||
{"learn":[0.6619695569],"iteration":458,"passed_time":15.63056555,"remaining_time":52.47647387,"test":[0.6708454134]},
|
||||
{"learn":[0.661952377],"iteration":459,"passed_time":15.656355,"remaining_time":52.41475368,"test":[0.6708404483]},
|
||||
{"learn":[0.6619237442],"iteration":460,"passed_time":15.68232112,"remaining_time":52.35377918,"test":[0.6708274771]},
|
||||
{"learn":[0.6619089407],"iteration":461,"passed_time":15.71164945,"remaining_time":52.30414904,"test":[0.6708244992]},
|
||||
{"learn":[0.6618886168],"iteration":462,"passed_time":15.7361944,"remaining_time":52.23872743,"test":[0.6708344314]},
|
||||
{"learn":[0.6618831383],"iteration":463,"passed_time":15.76527735,"remaining_time":52.18850433,"test":[0.6708279081]},
|
||||
{"learn":[0.6618690774],"iteration":464,"passed_time":15.78652262,"remaining_time":52.11249942,"test":[0.6708258106]},
|
||||
{"learn":[0.661845878],"iteration":465,"passed_time":15.81756836,"remaining_time":52.06899113,"test":[0.6708049714]},
|
||||
{"learn":[0.6618290213],"iteration":466,"passed_time":15.83979966,"remaining_time":51.99660146,"test":[0.670810989]},
|
||||
{"learn":[0.6618050064],"iteration":467,"passed_time":15.87342473,"remaining_time":51.9617237,"test":[0.6708212237]},
|
||||
{"learn":[0.6617832833],"iteration":468,"passed_time":15.90381555,"remaining_time":51.9162934,"test":[0.6708221741]},
|
||||
{"learn":[0.6617652311],"iteration":469,"passed_time":15.93502938,"remaining_time":51.87360627,"test":[0.6708259658]},
|
||||
{"learn":[0.6617443144],"iteration":470,"passed_time":15.96919221,"remaining_time":51.84054117,"test":[0.6708159692]},
|
||||
{"learn":[0.6617202619],"iteration":471,"passed_time":15.99477329,"remaining_time":51.77968981,"test":[0.6708136212]},
|
||||
{"learn":[0.6617005831],"iteration":472,"passed_time":16.02279091,"remaining_time":51.72685354,"test":[0.6708224942]},
|
||||
{"learn":[0.6616824419],"iteration":473,"passed_time":16.04763422,"remaining_time":51.66390258,"test":[0.6708363084]},
|
||||
{"learn":[0.6616538226],"iteration":474,"passed_time":16.07374645,"remaining_time":51.60518598,"test":[0.670850875]},
|
||||
{"learn":[0.6616314155],"iteration":475,"passed_time":16.09993591,"remaining_time":51.54685363,"test":[0.6708527236]},
|
||||
{"learn":[0.6616127861],"iteration":476,"passed_time":16.12811357,"remaining_time":51.49500411,"test":[0.6708453401]},
|
||||
{"learn":[0.6616029072],"iteration":477,"passed_time":16.15264086,"remaining_time":51.43163051,"test":[0.6708413844]},
|
||||
{"learn":[0.6615843751],"iteration":478,"passed_time":16.17696751,"remaining_time":51.36778201,"test":[0.6708364569]},
|
||||
{"learn":[0.661563216],"iteration":479,"passed_time":16.20551145,"remaining_time":51.31745293,"test":[0.6708251774]},
|
||||
{"learn":[0.6615432257],"iteration":480,"passed_time":16.22860577,"remaining_time":51.2500045,"test":[0.6708154393]},
|
||||
{"learn":[0.6615263324],"iteration":481,"passed_time":16.25544093,"remaining_time":51.19452144,"test":[0.6708111613]},
|
||||
{"learn":[0.6615033259],"iteration":482,"passed_time":16.27729221,"remaining_time":51.12350369,"test":[0.6708102339]},
|
||||
{"learn":[0.661484293],"iteration":483,"passed_time":16.30502335,"remaining_time":51.07110619,"test":[0.6707929623]},
|
||||
{"learn":[0.6614678231],"iteration":484,"passed_time":16.32842702,"remaining_time":51.00529266,"test":[0.6707900226]},
|
||||
{"learn":[0.6614463024],"iteration":485,"passed_time":16.36272839,"remaining_time":50.97360242,"test":[0.6707832384]},
|
||||
{"learn":[0.6614155436],"iteration":486,"passed_time":16.39272506,"remaining_time":50.92852776,"test":[0.6707739118]},
|
||||
{"learn":[0.6613958945],"iteration":487,"passed_time":16.42636604,"remaining_time":50.89480625,"test":[0.6707737538]},
|
||||
{"learn":[0.661380611],"iteration":488,"passed_time":16.4597142,"remaining_time":50.86018027,"test":[0.6707730234]},
|
||||
{"learn":[0.6613677802],"iteration":489,"passed_time":16.48056007,"remaining_time":50.78703206,"test":[0.6707796291]},
|
||||
{"learn":[0.6613530086],"iteration":490,"passed_time":16.51091177,"remaining_time":50.74331132,"test":[0.670791408]},
|
||||
{"learn":[0.6613248211],"iteration":491,"passed_time":16.53097438,"remaining_time":50.66810846,"test":[0.6707944906]},
|
||||
{"learn":[0.6613059359],"iteration":492,"passed_time":16.56161402,"remaining_time":50.62546112,"test":[0.6707835635]},
|
||||
{"learn":[0.6612729965],"iteration":493,"passed_time":16.5854633,"remaining_time":50.56216139,"test":[0.6707908928]},
|
||||
{"learn":[0.6612624948],"iteration":494,"passed_time":16.61302735,"remaining_time":50.51031547,"test":[0.670796262]},
|
||||
{"learn":[0.6612401679],"iteration":495,"passed_time":16.63896978,"remaining_time":50.45365029,"test":[0.6707877825]},
|
||||
{"learn":[0.6612191637],"iteration":496,"passed_time":16.663707,"remaining_time":50.39346403,"test":[0.6707854132]},
|
||||
{"learn":[0.6611912219],"iteration":497,"passed_time":16.69040179,"remaining_time":50.33932428,"test":[0.6707756206]},
|
||||
{"learn":[0.6611773017],"iteration":498,"passed_time":16.71612789,"remaining_time":50.28238068,"test":[0.6707707899]},
|
||||
{"learn":[0.6611638216],"iteration":499,"passed_time":16.74072553,"remaining_time":50.2221766,"test":[0.6707704386]},
|
||||
{"learn":[0.6611450533],"iteration":500,"passed_time":16.77346538,"remaining_time":50.18647626,"test":[0.6707621465]},
|
||||
{"learn":[0.6611179111],"iteration":501,"passed_time":16.80230735,"remaining_time":50.13915621,"test":[0.6707661931]},
|
||||
{"learn":[0.6610959069],"iteration":502,"passed_time":16.83637769,"remaining_time":50.10747,"test":[0.6707651988]},
|
||||
{"learn":[0.6610728788],"iteration":503,"passed_time":16.87382128,"remaining_time":50.08578697,"test":[0.6707607827]},
|
||||
{"learn":[0.6610436668],"iteration":504,"passed_time":16.92151611,"remaining_time":50.09438927,"test":[0.670760242]},
|
||||
{"learn":[0.6610188976],"iteration":505,"passed_time":16.9898618,"remaining_time":50.16374216,"test":[0.6707506008]},
|
||||
{"learn":[0.6610030555],"iteration":506,"passed_time":17.03818668,"remaining_time":50.17359509,"test":[0.6707452886]},
|
||||
{"learn":[0.6609831174],"iteration":507,"passed_time":17.06933058,"remaining_time":50.13275833,"test":[0.6707355189]},
|
||||
{"learn":[0.6609586562],"iteration":508,"passed_time":17.1106164,"remaining_time":50.12166807,"test":[0.6707312551]},
|
||||
{"learn":[0.660935882],"iteration":509,"passed_time":17.14537899,"remaining_time":50.09140137,"test":[0.6707199485]},
|
||||
{"learn":[0.6609202024],"iteration":510,"passed_time":17.19066307,"remaining_time":50.09177556,"test":[0.6707131947]},
|
||||
{"learn":[0.6609011137],"iteration":511,"passed_time":17.21958034,"remaining_time":50.04440537,"test":[0.6707154112]},
|
||||
{"learn":[0.6608726737],"iteration":512,"passed_time":17.24756917,"remaining_time":49.99441591,"test":[0.6706982346]},
|
||||
{"learn":[0.6608608849],"iteration":513,"passed_time":17.27150822,"remaining_time":49.93280391,"test":[0.6706988941]},
|
||||
{"learn":[0.6608387256],"iteration":514,"passed_time":17.29800365,"remaining_time":49.87870957,"test":[0.6706989098]},
|
||||
{"learn":[0.6608136063],"iteration":515,"passed_time":17.34332283,"remaining_time":49.87885868,"test":[0.670693306]},
|
||||
{"learn":[0.6607946343],"iteration":516,"passed_time":17.37393636,"remaining_time":49.83664916,"test":[0.6706944515]},
|
||||
{"learn":[0.6607703935],"iteration":517,"passed_time":17.4173655,"remaining_time":49.83114994,"test":[0.6706899688]},
|
||||
{"learn":[0.6607509625],"iteration":518,"passed_time":17.46008645,"remaining_time":49.82348368,"test":[0.6706909374]},
|
||||
{"learn":[0.6607238109],"iteration":519,"passed_time":17.4906988,"remaining_time":49.78121967,"test":[0.6706855074]},
|
||||
{"learn":[0.6606999858],"iteration":520,"passed_time":17.5186435,"remaining_time":49.7314275,"test":[0.6706787779]},
|
||||
{"learn":[0.6606813873],"iteration":521,"passed_time":17.54613056,"remaining_time":49.6804233,"test":[0.6706737082]},
|
||||
{"learn":[0.6606610372],"iteration":522,"passed_time":17.57100039,"remaining_time":49.62211774,"test":[0.6706761225]},
|
||||
{"learn":[0.660638456],"iteration":523,"passed_time":17.60084283,"remaining_time":49.5779466,"test":[0.670685455]},
|
||||
{"learn":[0.6606156483],"iteration":524,"passed_time":17.62599925,"remaining_time":49.52066456,"test":[0.6706693855]},
|
||||
{"learn":[0.6605968623],"iteration":525,"passed_time":17.65519625,"remaining_time":49.47482751,"test":[0.6706647216]},
|
||||
{"learn":[0.6605735776],"iteration":526,"passed_time":17.67910836,"remaining_time":49.41428199,"test":[0.6706569188]},
|
||||
{"learn":[0.6605517294],"iteration":527,"passed_time":17.70744827,"remaining_time":49.36621942,"test":[0.6706549134]},
|
||||
{"learn":[0.6605309239],"iteration":528,"passed_time":17.72943083,"remaining_time":49.3005534,"test":[0.6706547978]},
|
||||
{"learn":[0.6605086434],"iteration":529,"passed_time":17.75830336,"remaining_time":49.25416215,"test":[0.6706564214]},
|
||||
{"learn":[0.6604803349],"iteration":530,"passed_time":17.78141858,"remaining_time":49.19190939,"test":[0.6706559196]},
|
||||
{"learn":[0.6604566326],"iteration":531,"passed_time":17.80870208,"remaining_time":49.14130574,"test":[0.6706515072]},
|
||||
{"learn":[0.6604430839],"iteration":532,"passed_time":17.82904188,"remaining_time":49.07167811,"test":[0.6706474616]},
|
||||
{"learn":[0.6604273738],"iteration":533,"passed_time":17.86246645,"remaining_time":49.03815696,"test":[0.6706424204]},
|
||||
{"learn":[0.6604048016],"iteration":534,"passed_time":17.90552779,"remaining_time":49.03102469,"test":[0.6706520008]},
|
||||
{"learn":[0.6603845173],"iteration":535,"passed_time":18.02843143,"remaining_time":49.24183511,"test":[0.6706448306]},
|
||||
{"learn":[0.6603669212],"iteration":536,"passed_time":18.07245966,"remaining_time":49.23651485,"test":[0.6706415789]},
|
||||
{"learn":[0.6603488983],"iteration":537,"passed_time":18.10631942,"remaining_time":49.20341819,"test":[0.6706305359]},
|
||||
{"learn":[0.6603176881],"iteration":538,"passed_time":18.13531438,"remaining_time":49.1571323,"test":[0.6706152774]},
|
||||
{"learn":[0.6602953862],"iteration":539,"passed_time":18.16575265,"remaining_time":49.11481272,"test":[0.670616585]},
|
||||
{"learn":[0.6602672025],"iteration":540,"passed_time":18.20025584,"remaining_time":49.08349958,"test":[0.6705963243]},
|
||||
{"learn":[0.6602568636],"iteration":541,"passed_time":18.22381751,"remaining_time":49.02274158,"test":[0.6706027368]},
|
||||
{"learn":[0.660235705],"iteration":542,"passed_time":18.25438575,"remaining_time":48.98092088,"test":[0.6706003522]},
|
||||
{"learn":[0.6602152295],"iteration":543,"passed_time":18.28070524,"remaining_time":48.9277699,"test":[0.6706044301]},
|
||||
{"learn":[0.6601897709],"iteration":544,"passed_time":18.30768805,"remaining_time":48.87648827,"test":[0.6706047241]},
|
||||
{"learn":[0.6601683731],"iteration":545,"passed_time":18.33807201,"remaining_time":48.83435294,"test":[0.6706038235]},
|
||||
{"learn":[0.6601472267],"iteration":546,"passed_time":18.36776304,"remaining_time":48.79041993,"test":[0.6706026913]},
|
||||
{"learn":[0.6601262337],"iteration":547,"passed_time":18.41134623,"remaining_time":48.78334803,"test":[0.6705845786]},
|
||||
{"learn":[0.6601119991],"iteration":548,"passed_time":18.44405381,"remaining_time":48.74739905,"test":[0.6705873967]},
|
||||
{"learn":[0.6600869973],"iteration":549,"passed_time":18.47010718,"remaining_time":48.69391893,"test":[0.6705755426]},
|
||||
{"learn":[0.6600667497],"iteration":550,"passed_time":18.5036553,"remaining_time":48.66024779,"test":[0.6705715731]},
|
||||
{"learn":[0.6600397508],"iteration":551,"passed_time":18.53164471,"remaining_time":48.61199556,"test":[0.6705757153]},
|
||||
{"learn":[0.660016863],"iteration":552,"passed_time":18.5577607,"remaining_time":48.55891452,"test":[0.6705516814]},
|
||||
{"learn":[0.6599933158],"iteration":553,"passed_time":18.58492994,"remaining_time":48.50867995,"test":[0.6705530864]},
|
||||
{"learn":[0.6599632649],"iteration":554,"passed_time":18.62562092,"remaining_time":48.49373376,"test":[0.6705552479]},
|
||||
{"learn":[0.6599446007],"iteration":555,"passed_time":18.65010209,"remaining_time":48.43659608,"test":[0.6705563336]},
|
||||
{"learn":[0.6599138126],"iteration":556,"passed_time":18.67796421,"remaining_time":48.38833458,"test":[0.6705718544]},
|
||||
{"learn":[0.6598965504],"iteration":557,"passed_time":18.70319381,"remaining_time":48.33334314,"test":[0.6705688384]},
|
||||
{"learn":[0.6598785723],"iteration":558,"passed_time":18.72995694,"remaining_time":48.28241136,"test":[0.6705641528]},
|
||||
{"learn":[0.659860838],"iteration":559,"passed_time":18.75657945,"remaining_time":48.23120429,"test":[0.6705628467]},
|
||||
{"learn":[0.6598408724],"iteration":560,"passed_time":18.78181322,"remaining_time":48.17652269,"test":[0.670558488]},
|
||||
{"learn":[0.6598244857],"iteration":561,"passed_time":18.80867415,"remaining_time":48.12610931,"test":[0.6705544404]},
|
||||
{"learn":[0.6598082469],"iteration":562,"passed_time":18.83488797,"remaining_time":48.0741279,"test":[0.6705617451]},
|
||||
{"learn":[0.6597851673],"iteration":563,"passed_time":18.86939449,"remaining_time":48.04335193,"test":[0.6705631717]},
|
||||
{"learn":[0.6597683521],"iteration":564,"passed_time":18.90235988,"remaining_time":48.00864854,"test":[0.6705636201]},
|
||||
{"learn":[0.6597479006],"iteration":565,"passed_time":18.93001053,"remaining_time":47.96048604,"test":[0.6705537522]},
|
||||
{"learn":[0.6597310938],"iteration":566,"passed_time":18.95858079,"remaining_time":47.91472006,"test":[0.670555083]},
|
||||
{"learn":[0.6597096581],"iteration":567,"passed_time":18.9833487,"remaining_time":47.85942842,"test":[0.6705524541]},
|
||||
{"learn":[0.6596862311],"iteration":568,"passed_time":19.0162481,"remaining_time":47.82469425,"test":[0.6705503132]},
|
||||
{"learn":[0.6596574779],"iteration":569,"passed_time":19.03781666,"remaining_time":47.76154004,"test":[0.6705354602]},
|
||||
{"learn":[0.6596385418],"iteration":570,"passed_time":19.0681355,"remaining_time":47.72043018,"test":[0.6705387012]},
|
||||
{"learn":[0.6596189903],"iteration":571,"passed_time":19.09073714,"remaining_time":47.66009201,"test":[0.6705411923]},
|
||||
{"learn":[0.65959275],"iteration":572,"passed_time":19.11146842,"remaining_time":47.59522765,"test":[0.6705390018]},
|
||||
{"learn":[0.6595730662],"iteration":573,"passed_time":19.141368,"remaining_time":47.55329403,"test":[0.6705354939]},
|
||||
{"learn":[0.6595566809],"iteration":574,"passed_time":19.16428373,"remaining_time":47.49409447,"test":[0.670531296]},
|
||||
{"learn":[0.6595365076],"iteration":575,"passed_time":19.19652276,"remaining_time":47.45807015,"test":[0.6705377163]},
|
||||
{"learn":[0.6595163446],"iteration":576,"passed_time":19.21727405,"remaining_time":47.39372785,"test":[0.6705248875]},
|
||||
{"learn":[0.6594816637],"iteration":577,"passed_time":19.24969594,"remaining_time":47.35824848,"test":[0.6705252902]},
|
||||
{"learn":[0.6594570142],"iteration":578,"passed_time":19.27445137,"remaining_time":47.30396442,"test":[0.6705181562]},
|
||||
{"learn":[0.6594353055],"iteration":579,"passed_time":19.29822455,"remaining_time":47.24737734,"test":[0.6705123446]},
|
||||
{"learn":[0.6594162362],"iteration":580,"passed_time":19.32403522,"remaining_time":47.19587948,"test":[0.6705128345]},
|
||||
{"learn":[0.659395036],"iteration":581,"passed_time":19.35739555,"remaining_time":47.16286408,"test":[0.6705173712]},
|
||||
{"learn":[0.6593798831],"iteration":582,"passed_time":19.39112791,"remaining_time":47.13075172,"test":[0.670541941]},
|
||||
{"learn":[0.6593556719],"iteration":583,"passed_time":19.42704318,"remaining_time":47.1039266,"test":[0.6705463243]},
|
||||
{"learn":[0.6593292627],"iteration":584,"passed_time":19.46022169,"remaining_time":47.07045077,"test":[0.6705513215]},
|
||||
{"learn":[0.6592976737],"iteration":585,"passed_time":19.48332075,"remaining_time":47.01265452,"test":[0.6705455889]},
|
||||
{"learn":[0.6592754841],"iteration":586,"passed_time":19.5115578,"remaining_time":46.9673444,"test":[0.6705408087]},
|
||||
{"learn":[0.6592510441],"iteration":587,"passed_time":19.54275193,"remaining_time":46.92919341,"test":[0.6705510193]},
|
||||
{"learn":[0.6592290326],"iteration":588,"passed_time":19.56411389,"remaining_time":46.86751222,"test":[0.6705456751]},
|
||||
{"learn":[0.6592097404],"iteration":589,"passed_time":19.59700884,"remaining_time":46.8335296,"test":[0.6705402427]},
|
||||
{"learn":[0.6591876204],"iteration":590,"passed_time":19.62169623,"remaining_time":46.77998306,"test":[0.6705443402]},
|
||||
{"learn":[0.6591705995],"iteration":591,"passed_time":19.64747626,"remaining_time":46.72913272,"test":[0.67054441]},
|
||||
{"learn":[0.6591456195],"iteration":592,"passed_time":19.67090184,"remaining_time":46.67278059,"test":[0.6705441955]},
|
||||
{"learn":[0.6591107122],"iteration":593,"passed_time":19.69910949,"remaining_time":46.62785848,"test":[0.6705319356]},
|
||||
{"learn":[0.6590819533],"iteration":594,"passed_time":19.72694709,"remaining_time":46.58211876,"test":[0.6705358843]},
|
||||
{"learn":[0.6590551327],"iteration":595,"passed_time":19.7530808,"remaining_time":46.53242523,"test":[0.6705334396]},
|
||||
{"learn":[0.6590373916],"iteration":596,"passed_time":19.77835609,"remaining_time":46.48079328,"test":[0.6705320462]},
|
||||
{"learn":[0.6590177149],"iteration":597,"passed_time":19.80378809,"remaining_time":46.4296169,"test":[0.6705332043]},
|
||||
{"learn":[0.6589946095],"iteration":598,"passed_time":19.83052585,"remaining_time":46.38158048,"test":[0.6705328363]},
|
||||
{"learn":[0.6589697628],"iteration":599,"passed_time":19.8579153,"remaining_time":46.33513569,"test":[0.6705315638]},
|
||||
{"learn":[0.6589442269],"iteration":600,"passed_time":19.89600309,"remaining_time":46.31365777,"test":[0.6705274435]},
|
||||
{"learn":[0.6589182437],"iteration":601,"passed_time":19.92518872,"remaining_time":46.27145155,"test":[0.670509808]},
|
||||
{"learn":[0.6588837179],"iteration":602,"passed_time":19.95754179,"remaining_time":46.23662666,"test":[0.6705077789]},
|
||||
{"learn":[0.6588674101],"iteration":603,"passed_time":19.99116426,"remaining_time":46.20474388,"test":[0.6705212132]},
|
||||
{"learn":[0.6588406916],"iteration":604,"passed_time":20.01900069,"remaining_time":46.15951398,"test":[0.6705098442]},
|
||||
{"learn":[0.6588149945],"iteration":605,"passed_time":20.04735837,"remaining_time":46.11554053,"test":[0.6705061509]},
|
||||
{"learn":[0.6587866031],"iteration":606,"passed_time":20.07232044,"remaining_time":46.06382599,"test":[0.6705003071]},
|
||||
{"learn":[0.6587636648],"iteration":607,"passed_time":20.09871086,"remaining_time":46.01546959,"test":[0.6705045031]},
|
||||
{"learn":[0.6587502469],"iteration":608,"passed_time":20.12348304,"remaining_time":45.96348917,"test":[0.6705083194]},
|
||||
{"learn":[0.6587292784],"iteration":609,"passed_time":20.14920752,"remaining_time":45.91376797,"test":[0.6705329997]},
|
||||
{"learn":[0.6587104112],"iteration":610,"passed_time":20.17662353,"remaining_time":45.86797068,"test":[0.6705269987]},
|
||||
{"learn":[0.6586953782],"iteration":611,"passed_time":20.20202219,"remaining_time":45.81765818,"test":[0.6705315607]},
|
||||
{"learn":[0.6586641191],"iteration":612,"passed_time":20.23050051,"remaining_time":45.77439512,"test":[0.6705142835]},
|
||||
{"learn":[0.6586450136],"iteration":613,"passed_time":20.25381994,"remaining_time":45.71953492,"test":[0.6705165015]},
|
||||
{"learn":[0.6586136263],"iteration":614,"passed_time":20.28518384,"remaining_time":45.68289369,"test":[0.6705001061]},
|
||||
{"learn":[0.6585862768],"iteration":615,"passed_time":20.3078175,"remaining_time":45.62665489,"test":[0.6705013916]},
|
||||
{"learn":[0.6585585235],"iteration":616,"passed_time":20.33878033,"remaining_time":45.5891948,"test":[0.6705037253]},
|
||||
{"learn":[0.6585371631],"iteration":617,"passed_time":20.36122842,"remaining_time":45.53271469,"test":[0.67049647]},
|
||||
{"learn":[0.6585092632],"iteration":618,"passed_time":20.3943397,"remaining_time":45.50013429,"test":[0.6705005632]},
|
||||
{"learn":[0.6584914317],"iteration":619,"passed_time":20.42384285,"remaining_time":45.45952119,"test":[0.6704957943]},
|
||||
{"learn":[0.6584662432],"iteration":620,"passed_time":20.45411533,"remaining_time":45.42065225,"test":[0.6704955333]},
|
||||
{"learn":[0.6584454668],"iteration":621,"passed_time":20.488223,"remaining_time":45.39030754,"test":[0.6704961207]},
|
||||
{"learn":[0.6584249408],"iteration":622,"passed_time":20.51043528,"remaining_time":45.33365872,"test":[0.6704921459]},
|
||||
{"learn":[0.6583931228],"iteration":623,"passed_time":20.54384208,"remaining_time":45.30180561,"test":[0.6704751713]},
|
||||
{"learn":[0.6583660767],"iteration":624,"passed_time":20.56912557,"remaining_time":45.25207624,"test":[0.6704753101]},
|
||||
{"learn":[0.658354264],"iteration":625,"passed_time":20.59414123,"remaining_time":45.20183714,"test":[0.6704620888]},
|
||||
{"learn":[0.6583253625],"iteration":626,"passed_time":20.61901142,"remaining_time":45.15135993,"test":[0.6704604282]},
|
||||
{"learn":[0.6582968632],"iteration":627,"passed_time":20.6468542,"remaining_time":45.10745855,"test":[0.6704663192]},
|
||||
{"learn":[0.6582687399],"iteration":628,"passed_time":20.67583093,"remaining_time":45.06607981,"test":[0.6704680085]},
|
||||
{"learn":[0.658242535],"iteration":629,"passed_time":20.7010198,"remaining_time":45.01650336,"test":[0.670453228]},
|
||||
{"learn":[0.6582199874],"iteration":630,"passed_time":20.72783977,"remaining_time":44.97054302,"test":[0.6704577785]},
|
||||
{"learn":[0.6581918101],"iteration":631,"passed_time":20.75222724,"remaining_time":44.91937795,"test":[0.67046675]},
|
||||
{"learn":[0.6581735218],"iteration":632,"passed_time":20.78264004,"remaining_time":44.88130954,"test":[0.6704731863]},
|
||||
{"learn":[0.6581445869],"iteration":633,"passed_time":20.80459182,"remaining_time":44.82503538,"test":[0.6704811116]},
|
||||
{"learn":[0.6581202427],"iteration":634,"passed_time":20.83717209,"remaining_time":44.79171637,"test":[0.6704839644]},
|
||||
{"learn":[0.6580977862],"iteration":635,"passed_time":20.86231353,"remaining_time":44.74244599,"test":[0.6704854798]},
|
||||
{"learn":[0.6580724179],"iteration":636,"passed_time":20.89269601,"remaining_time":44.70446572,"test":[0.6704835837]},
|
||||
{"learn":[0.6580426322],"iteration":637,"passed_time":20.93117347,"remaining_time":44.68379039,"test":[0.6704736198]},
|
||||
{"learn":[0.6580111256],"iteration":638,"passed_time":20.96066949,"remaining_time":44.64392985,"test":[0.6704640242]},
|
||||
{"learn":[0.6579834747],"iteration":639,"passed_time":20.9941179,"remaining_time":44.61250055,"test":[0.670465663]},
|
||||
{"learn":[0.6579541367],"iteration":640,"passed_time":21.0224519,"remaining_time":44.57022174,"test":[0.6704646829]},
|
||||
{"learn":[0.6579254503],"iteration":641,"passed_time":21.0522529,"remaining_time":44.53108946,"test":[0.6704600961]},
|
||||
{"learn":[0.657898555],"iteration":642,"passed_time":21.08260618,"remaining_time":44.49315178,"test":[0.6704643207]},
|
||||
{"learn":[0.6578676875],"iteration":643,"passed_time":21.10716702,"remaining_time":44.44304112,"test":[0.6704600533]},
|
||||
{"learn":[0.6578324163],"iteration":644,"passed_time":21.13594828,"remaining_time":44.40187584,"test":[0.6704614691]},
|
||||
{"learn":[0.6578062223],"iteration":645,"passed_time":21.1601277,"remaining_time":44.35110357,"test":[0.6704728212]},
|
||||
{"learn":[0.6577760631],"iteration":646,"passed_time":21.18552999,"remaining_time":44.30297075,"test":[0.6704758731]},
|
||||
{"learn":[0.6577483474],"iteration":647,"passed_time":21.21048648,"remaining_time":44.25397797,"test":[0.6704833026]},
|
||||
{"learn":[0.6577249642],"iteration":648,"passed_time":21.23686209,"remaining_time":44.20801337,"test":[0.6704767664]},
|
||||
{"learn":[0.6576974966],"iteration":649,"passed_time":21.26287585,"remaining_time":44.16135753,"test":[0.6704702727]},
|
||||
{"learn":[0.657675114],"iteration":650,"passed_time":21.28806218,"remaining_time":44.11305051,"test":[0.6704671372]},
|
||||
{"learn":[0.6576447891],"iteration":651,"passed_time":21.31506267,"remaining_time":44.06856515,"test":[0.6704699936]},
|
||||
{"learn":[0.6576102356],"iteration":652,"passed_time":21.3435081,"remaining_time":44.02711394,"test":[0.6704587989]},
|
||||
{"learn":[0.6575793887],"iteration":653,"passed_time":21.37776713,"remaining_time":43.99766753,"test":[0.6704637668]},
|
||||
{"learn":[0.6575543309],"iteration":654,"passed_time":21.40301154,"remaining_time":43.94969545,"test":[0.6704653717]},
|
||||
{"learn":[0.6575340787],"iteration":655,"passed_time":21.44023109,"remaining_time":43.92632711,"test":[0.6704598273]},
|
||||
{"learn":[0.6575061464],"iteration":656,"passed_time":21.4778965,"remaining_time":43.903828,"test":[0.6704522865]},
|
||||
{"learn":[0.657476113],"iteration":657,"passed_time":21.50245582,"remaining_time":43.85455275,"test":[0.6704558586]},
|
||||
{"learn":[0.6574447014],"iteration":658,"passed_time":21.53379663,"remaining_time":43.81915217,"test":[0.6704466331]},
|
||||
{"learn":[0.6574247361],"iteration":659,"passed_time":21.55955041,"remaining_time":43.77242053,"test":[0.6704405886]},
|
||||
{"learn":[0.6574034983],"iteration":660,"passed_time":21.58626671,"remaining_time":43.72770215,"test":[0.6704463767]},
|
||||
{"learn":[0.6573783832],"iteration":661,"passed_time":21.61183918,"remaining_time":43.68072633,"test":[0.6704475216]},
|
||||
{"learn":[0.657357694],"iteration":662,"passed_time":21.6373217,"remaining_time":43.63363366,"test":[0.6704572386]},
|
||||
{"learn":[0.6573411592],"iteration":663,"passed_time":21.66283476,"remaining_time":43.58666753,"test":[0.6704658153]},
|
||||
{"learn":[0.6573118559],"iteration":664,"passed_time":21.68841321,"remaining_time":43.5398972,"test":[0.6704600945]},
|
||||
{"learn":[0.6572819076],"iteration":665,"passed_time":21.71420973,"remaining_time":43.4936273,"test":[0.6704561998]},
|
||||
{"learn":[0.6572430097],"iteration":666,"passed_time":21.74213421,"remaining_time":43.45167151,"test":[0.6704535154]},
|
||||
{"learn":[0.6572160391],"iteration":667,"passed_time":21.77174463,"remaining_time":43.41311953,"test":[0.6704413781]},
|
||||
{"learn":[0.6571931413],"iteration":668,"passed_time":21.81895309,"remaining_time":43.40960622,"test":[0.6704450013]},
|
||||
{"learn":[0.6571737099],"iteration":669,"passed_time":21.84627583,"remaining_time":43.36648784,"test":[0.6704422199]},
|
||||
{"learn":[0.6571532872],"iteration":670,"passed_time":21.88834724,"remaining_time":43.35262814,"test":[0.67044342]},
|
||||
{"learn":[0.6571208939],"iteration":671,"passed_time":21.93403139,"remaining_time":43.34582395,"test":[0.6704415341]},
|
||||
{"learn":[0.6570887673],"iteration":672,"passed_time":21.9714274,"remaining_time":43.32256191,"test":[0.6704439539]},
|
||||
{"learn":[0.6570633692],"iteration":673,"passed_time":22.01942449,"remaining_time":43.32011406,"test":[0.6704498197]},
|
||||
{"learn":[0.6570454361],"iteration":674,"passed_time":22.05319867,"remaining_time":43.2896122,"test":[0.6704452194]},
|
||||
{"learn":[0.6570231031],"iteration":675,"passed_time":22.09079747,"remaining_time":43.26659149,"test":[0.6704366524]},
|
||||
{"learn":[0.6570052089],"iteration":676,"passed_time":22.14192346,"remaining_time":43.26996269,"test":[0.6704427124]},
|
||||
{"learn":[0.6569855794],"iteration":677,"passed_time":22.17624471,"remaining_time":43.24040635,"test":[0.6704395579]},
|
||||
{"learn":[0.6569579709],"iteration":678,"passed_time":22.213192,"remaining_time":43.21594497,"test":[0.6704401246]},
|
||||
{"learn":[0.6569333354],"iteration":679,"passed_time":22.23966403,"remaining_time":43.17111253,"test":[0.6704415621]},
|
||||
{"learn":[0.6569069617],"iteration":680,"passed_time":22.27051241,"remaining_time":43.13481039,"test":[0.6704341343]},
|
||||
{"learn":[0.6568931857],"iteration":681,"passed_time":22.29625075,"remaining_time":43.08864881,"test":[0.6704369615]},
|
||||
{"learn":[0.6568734532],"iteration":682,"passed_time":22.32160622,"remaining_time":43.04180877,"test":[0.6704357425]},
|
||||
{"learn":[0.6568435196],"iteration":683,"passed_time":22.35059872,"remaining_time":43.00202911,"test":[0.6704294622]},
|
||||
{"learn":[0.6568108038],"iteration":684,"passed_time":22.37956576,"remaining_time":42.96223208,"test":[0.6704289794]},
|
||||
{"learn":[0.6567811374],"iteration":685,"passed_time":22.41993338,"remaining_time":42.94430389,"test":[0.6704272409]},
|
||||
{"learn":[0.6567467284],"iteration":686,"passed_time":22.45285267,"remaining_time":42.91207504,"test":[0.6704101162]},
|
||||
{"learn":[0.6567172734],"iteration":687,"passed_time":22.4848431,"remaining_time":42.8780729,"test":[0.6704069439]},
|
||||
{"learn":[0.6566967606],"iteration":688,"passed_time":22.51193834,"remaining_time":42.83476221,"test":[0.6704100747]},
|
||||
{"learn":[0.6566720128],"iteration":689,"passed_time":22.53798671,"remaining_time":42.78951101,"test":[0.6704122261]},
|
||||
{"learn":[0.6566441608],"iteration":690,"passed_time":22.57108439,"remaining_time":42.75766928,"test":[0.6704137826]},
|
||||
{"learn":[0.6566172287],"iteration":691,"passed_time":22.59836588,"remaining_time":42.7148303,"test":[0.6704207952]},
|
||||
{"learn":[0.6565952549],"iteration":692,"passed_time":22.62447507,"remaining_time":42.66982528,"test":[0.6704154834]},
|
||||
{"learn":[0.6565702687],"iteration":693,"passed_time":22.65349415,"remaining_time":42.63035067,"test":[0.6704253514]},
|
||||
{"learn":[0.6565392213],"iteration":694,"passed_time":22.68028991,"remaining_time":42.58673141,"test":[0.6704155636]},
|
||||
{"learn":[0.6565157938],"iteration":695,"passed_time":22.70844406,"remaining_time":42.54570555,"test":[0.6704141298]},
|
||||
{"learn":[0.6564902789],"iteration":696,"passed_time":22.73944116,"remaining_time":42.51003133,"test":[0.6704207635]},
|
||||
{"learn":[0.6564644734],"iteration":697,"passed_time":22.7613976,"remaining_time":42.45750671,"test":[0.6704268341]},
|
||||
{"learn":[0.6564349549],"iteration":698,"passed_time":22.79216825,"remaining_time":42.42147482,"test":[0.6704243126]},
|
||||
{"learn":[0.6564046572],"iteration":699,"passed_time":22.8167121,"remaining_time":42.37389389,"test":[0.6704235165]},
|
||||
{"learn":[0.6563744107],"iteration":700,"passed_time":22.84507296,"remaining_time":42.33345189,"test":[0.6704257736]},
|
||||
{"learn":[0.6563525063],"iteration":701,"passed_time":22.87088832,"remaining_time":42.28833766,"test":[0.6704247758]},
|
||||
{"learn":[0.6563189867],"iteration":702,"passed_time":22.90238907,"remaining_time":42.25376759,"test":[0.6704331799]},
|
||||
{"learn":[0.6562939062],"iteration":703,"passed_time":22.94246813,"remaining_time":42.23499815,"test":[0.6704252722]},
|
||||
{"learn":[0.6562739297],"iteration":704,"passed_time":22.97441688,"remaining_time":42.20123385,"test":[0.6704146644]},
|
||||
{"learn":[0.656256438],"iteration":705,"passed_time":23.00262167,"remaining_time":42.16061253,"test":[0.6704164122]},
|
||||
{"learn":[0.6562366475],"iteration":706,"passed_time":23.033437,"remaining_time":42.12480062,"test":[0.6704118954]},
|
||||
{"learn":[0.6562073096],"iteration":707,"passed_time":23.0545813,"remaining_time":42.07135458,"test":[0.6704043129]},
|
||||
{"learn":[0.6561864222],"iteration":708,"passed_time":23.08699831,"remaining_time":42.03852584,"test":[0.6703978198]},
|
||||
{"learn":[0.6561578826],"iteration":709,"passed_time":23.11590694,"remaining_time":41.99932387,"test":[0.6703935976]},
|
||||
{"learn":[0.6561208567],"iteration":710,"passed_time":23.14362702,"remaining_time":41.9579961,"test":[0.6703839683]},
|
||||
{"learn":[0.6560924703],"iteration":711,"passed_time":23.16985155,"remaining_time":41.91400112,"test":[0.6703843723]},
|
||||
{"learn":[0.6560656907],"iteration":712,"passed_time":23.19510285,"remaining_time":41.86829925,"test":[0.6703879502]},
|
||||
{"learn":[0.6560362588],"iteration":713,"passed_time":23.23034771,"remaining_time":41.84065429,"test":[0.6703895978]},
|
||||
{"learn":[0.6560124527],"iteration":714,"passed_time":23.25923754,"remaining_time":41.80156678,"test":[0.6703894359]},
|
||||
{"learn":[0.6559875055],"iteration":715,"passed_time":23.28703452,"remaining_time":41.76054794,"test":[0.6703928777]},
|
||||
{"learn":[0.6559547281],"iteration":716,"passed_time":23.31161175,"remaining_time":41.71380457,"test":[0.6703933128]},
|
||||
{"learn":[0.6559230866],"iteration":717,"passed_time":23.34170355,"remaining_time":41.67696929,"test":[0.6703844355]},
|
||||
{"learn":[0.6558924823],"iteration":718,"passed_time":23.37263658,"remaining_time":41.64165155,"test":[0.6703825151]},
|
||||
{"learn":[0.6558676469],"iteration":719,"passed_time":23.40571088,"remaining_time":41.61015268,"test":[0.6703983542]},
|
||||
{"learn":[0.6558459277],"iteration":720,"passed_time":23.4389719,"remaining_time":41.57898067,"test":[0.670399556]},
|
||||
{"learn":[0.6558149638],"iteration":721,"passed_time":23.48304084,"remaining_time":41.56693379,"test":[0.6703931808]},
|
||||
{"learn":[0.6557812248],"iteration":722,"passed_time":23.50734531,"remaining_time":41.5198893,"test":[0.6703886918]},
|
||||
{"learn":[0.6557546502],"iteration":723,"passed_time":23.54055835,"remaining_time":41.48860836,"test":[0.6703847574]},
|
||||
{"learn":[0.6557274948],"iteration":724,"passed_time":23.56652491,"remaining_time":41.44457829,"test":[0.6703885941]},
|
||||
{"learn":[0.6557044723],"iteration":725,"passed_time":23.59580183,"remaining_time":41.40640708,"test":[0.6703788615]},
|
||||
{"learn":[0.6556751811],"iteration":726,"passed_time":23.62334313,"remaining_time":41.36522119,"test":[0.6703799906]},
|
||||
{"learn":[0.6556539158],"iteration":727,"passed_time":23.64879831,"remaining_time":41.32042782,"test":[0.6703774518]},
|
||||
{"learn":[0.6556182915],"iteration":728,"passed_time":23.67755213,"remaining_time":41.28143862,"test":[0.6703783496]},
|
||||
{"learn":[0.6555977079],"iteration":729,"passed_time":23.70012944,"remaining_time":41.23173204,"test":[0.6703648854]},
|
||||
{"learn":[0.6555667903],"iteration":730,"passed_time":23.72866102,"remaining_time":41.19243615,"test":[0.6703716654]},
|
||||
{"learn":[0.6555394075],"iteration":731,"passed_time":23.75226732,"remaining_time":41.14463793,"test":[0.6703550938]},
|
||||
{"learn":[0.6555122742],"iteration":732,"passed_time":23.7844108,"remaining_time":41.11166233,"test":[0.6703467057]},
|
||||
{"learn":[0.6554814941],"iteration":733,"passed_time":23.80747563,"remaining_time":41.06303017,"test":[0.6703484503]},
|
||||
{"learn":[0.6554517373],"iteration":734,"passed_time":23.84023587,"remaining_time":41.03115425,"test":[0.6703549183]},
|
||||
{"learn":[0.655429552],"iteration":735,"passed_time":23.87042124,"remaining_time":40.99485387,"test":[0.6703501504]},
|
||||
{"learn":[0.655396579],"iteration":736,"passed_time":23.9087808,"remaining_time":40.97257823,"test":[0.6703672622]},
|
||||
{"learn":[0.6553735864],"iteration":737,"passed_time":23.94161529,"remaining_time":40.94081097,"test":[0.6703560249]},
|
||||
{"learn":[0.6553472597],"iteration":738,"passed_time":23.97478791,"remaining_time":40.90961779,"test":[0.6703547155]},
|
||||
{"learn":[0.6553252832],"iteration":739,"passed_time":24.00628859,"remaining_time":40.87557247,"test":[0.6703593236]},
|
||||
{"learn":[0.6552971659],"iteration":740,"passed_time":24.03623034,"remaining_time":40.83888528,"test":[0.6703606827]},
|
||||
{"learn":[0.6552763852],"iteration":741,"passed_time":24.06404686,"remaining_time":40.79861313,"test":[0.6703511404]},
|
||||
{"learn":[0.6552488203],"iteration":742,"passed_time":24.09270947,"remaining_time":40.75980593,"test":[0.6703431646]},
|
||||
{"learn":[0.65521229],"iteration":743,"passed_time":24.12724624,"remaining_time":40.73094258,"test":[0.6703475116]},
|
||||
{"learn":[0.6551949744],"iteration":744,"passed_time":24.15397955,"remaining_time":40.68891857,"test":[0.6703483634]},
|
||||
{"learn":[0.6551673797],"iteration":745,"passed_time":24.17955779,"remaining_time":40.64499392,"test":[0.6703475713]},
|
||||
{"learn":[0.6551421856],"iteration":746,"passed_time":24.20715317,"remaining_time":40.60450191,"test":[0.670360457]},
|
||||
{"learn":[0.6551255516],"iteration":747,"passed_time":24.23336836,"remaining_time":40.5617342,"test":[0.6703664352]},
|
||||
{"learn":[0.6551019608],"iteration":748,"passed_time":24.2614437,"remaining_time":40.52211759,"test":[0.6703617612]},
|
||||
{"learn":[0.6550758728],"iteration":749,"passed_time":24.29512083,"remaining_time":40.49186805,"test":[0.6703669926]},
|
||||
{"learn":[0.655051966],"iteration":750,"passed_time":24.31839238,"remaining_time":40.44430371,"test":[0.6703670837]},
|
||||
{"learn":[0.6550351058],"iteration":751,"passed_time":24.34977118,"remaining_time":40.41025856,"test":[0.6703706628]},
|
||||
{"learn":[0.6549998756],"iteration":752,"passed_time":24.3762114,"remaining_time":40.36804198,"test":[0.670369618]},
|
||||
{"learn":[0.6549721212],"iteration":753,"passed_time":24.40831154,"remaining_time":40.3352204,"test":[0.6703692351]},
|
||||
{"learn":[0.6549401744],"iteration":754,"passed_time":24.44267281,"remaining_time":40.30612934,"test":[0.6703624433]},
|
||||
{"learn":[0.6549207325],"iteration":755,"passed_time":24.47460721,"remaining_time":40.27303091,"test":[0.6703686285]},
|
||||
{"learn":[0.6548900891],"iteration":756,"passed_time":24.50826603,"remaining_time":40.24276708,"test":[0.6703598432]},
|
||||
{"learn":[0.6548682731],"iteration":757,"passed_time":24.54826542,"remaining_time":40.22288345,"test":[0.6703618766]},
|
||||
{"learn":[0.6548418938],"iteration":758,"passed_time":24.57546587,"remaining_time":40.18201996,"test":[0.6703694148]},
|
||||
{"learn":[0.6548234717],"iteration":759,"passed_time":24.60502723,"remaining_time":40.14504442,"test":[0.6703683652]},
|
||||
{"learn":[0.6547996833],"iteration":760,"passed_time":24.63261096,"remaining_time":40.10486856,"test":[0.6703604855]},
|
||||
{"learn":[0.6547726174],"iteration":761,"passed_time":24.66001655,"remaining_time":40.06443634,"test":[0.6703758987]},
|
||||
{"learn":[0.6547509314],"iteration":762,"passed_time":24.68929907,"remaining_time":40.02708119,"test":[0.6703773302]},
|
||||
{"learn":[0.6547168175],"iteration":763,"passed_time":24.71425118,"remaining_time":39.98274144,"test":[0.6703641028]},
|
||||
{"learn":[0.6546907846],"iteration":764,"passed_time":24.74589169,"remaining_time":39.94924999,"test":[0.6703649602]},
|
||||
{"learn":[0.6546671611],"iteration":765,"passed_time":24.76625006,"remaining_time":39.89758822,"test":[0.6703567811]},
|
||||
{"learn":[0.6546475893],"iteration":766,"passed_time":24.79734832,"remaining_time":39.86327312,"test":[0.6703544688]},
|
||||
{"learn":[0.6546206223],"iteration":767,"passed_time":24.82531049,"remaining_time":39.82393558,"test":[0.6703611821]},
|
||||
{"learn":[0.6545874193],"iteration":768,"passed_time":24.85435247,"remaining_time":39.78635616,"test":[0.6703527821]},
|
||||
{"learn":[0.6545620629],"iteration":769,"passed_time":24.88095966,"remaining_time":39.74490958,"test":[0.6703523616]},
|
||||
{"learn":[0.6545346297],"iteration":770,"passed_time":24.90935211,"remaining_time":39.70634726,"test":[0.6703616298]},
|
||||
{"learn":[0.6545172316],"iteration":771,"passed_time":24.94098876,"remaining_time":39.67297175,"test":[0.6703603551]},
|
||||
{"learn":[0.6544943049],"iteration":772,"passed_time":24.97035098,"remaining_time":39.6359905,"test":[0.6703675655]},
|
||||
{"learn":[0.6544632323],"iteration":773,"passed_time":25.00434422,"remaining_time":39.60636436,"test":[0.6703582411]},
|
||||
{"learn":[0.6544384097],"iteration":774,"passed_time":25.03067441,"remaining_time":39.56461439,"test":[0.6703581437]},
|
||||
{"learn":[0.6544084745],"iteration":775,"passed_time":25.05692652,"remaining_time":39.522781,"test":[0.6703551885]},
|
||||
{"learn":[0.6543765257],"iteration":776,"passed_time":25.08660163,"remaining_time":39.48637554,"test":[0.6703608491]},
|
||||
{"learn":[0.6543536123],"iteration":777,"passed_time":25.10764591,"remaining_time":39.43643098,"test":[0.6703674554]},
|
||||
{"learn":[0.6543303593],"iteration":778,"passed_time":25.13940138,"remaining_time":39.40334928,"test":[0.6703679619]},
|
||||
{"learn":[0.6543005831],"iteration":779,"passed_time":25.15916899,"remaining_time":39.35152074,"test":[0.6703701757]},
|
||||
{"learn":[0.6542678123],"iteration":780,"passed_time":25.18841105,"remaining_time":39.31456219,"test":[0.6703603462]},
|
||||
{"learn":[0.6542439303],"iteration":781,"passed_time":25.21444083,"remaining_time":39.27262012,"test":[0.670359801]},
|
||||
{"learn":[0.6542100401],"iteration":782,"passed_time":25.24017824,"remaining_time":39.23026426,"test":[0.6703523669]},
|
||||
{"learn":[0.6541836178],"iteration":783,"passed_time":25.2660091,"remaining_time":39.18809574,"test":[0.6703365674]},
|
||||
{"learn":[0.654158129],"iteration":784,"passed_time":25.28891553,"remaining_time":39.1414425,"test":[0.6703486118]},
|
||||
{"learn":[0.6541343464],"iteration":785,"passed_time":25.31589904,"remaining_time":39.10114686,"test":[0.6703450011]},
|
||||
{"learn":[0.6541092921],"iteration":786,"passed_time":25.34123581,"remaining_time":39.05834694,"test":[0.6703473135]},
|
||||
{"learn":[0.6540812254],"iteration":787,"passed_time":25.36728606,"remaining_time":39.01668871,"test":[0.670350998]},
|
||||
{"learn":[0.654060259],"iteration":788,"passed_time":25.39177931,"remaining_time":38.97268028,"test":[0.6703417767]},
|
||||
{"learn":[0.6540467253],"iteration":789,"passed_time":25.41712461,"remaining_time":38.9300263,"test":[0.6703349821]},
|
||||
{"learn":[0.6540306837],"iteration":790,"passed_time":25.44804125,"remaining_time":38.89593157,"test":[0.6703457717]},
|
||||
{"learn":[0.6540103667],"iteration":791,"passed_time":25.48249341,"remaining_time":38.86723743,"test":[0.6703506266]},
|
||||
{"learn":[0.6539821302],"iteration":792,"passed_time":25.51450657,"remaining_time":38.83481643,"test":[0.6703596395]},
|
||||
{"learn":[0.6539577914],"iteration":793,"passed_time":25.54216564,"remaining_time":38.79578307,"test":[0.6703799895]},
|
||||
{"learn":[0.653923724],"iteration":794,"passed_time":25.56982738,"remaining_time":38.75678238,"test":[0.6703687687]},
|
||||
{"learn":[0.6539086888],"iteration":795,"passed_time":25.59539769,"remaining_time":38.71464675,"test":[0.6703780675]},
|
||||
{"learn":[0.6538798424],"iteration":796,"passed_time":25.61874122,"remaining_time":38.66919157,"test":[0.670374835]},
|
||||
{"learn":[0.6538566996],"iteration":797,"passed_time":25.64394874,"remaining_time":38.62659947,"test":[0.6703831387]},
|
||||
{"learn":[0.6538290752],"iteration":798,"passed_time":25.66776244,"remaining_time":38.58195581,"test":[0.670377656]},
|
||||
{"learn":[0.6538051255],"iteration":799,"passed_time":25.69593415,"remaining_time":38.54390122,"test":[0.6703689741]},
|
||||
{"learn":[0.6537917354],"iteration":800,"passed_time":25.71651353,"remaining_time":38.49450652,"test":[0.6703709756]},
|
||||
{"learn":[0.6537684302],"iteration":801,"passed_time":25.74304126,"remaining_time":38.45406912,"test":[0.6703737517]},
|
||||
{"learn":[0.6537402991],"iteration":802,"passed_time":25.77084871,"remaining_time":38.41557398,"test":[0.6703818964]},
|
||||
{"learn":[0.6537165427],"iteration":803,"passed_time":25.79028824,"remaining_time":38.36465763,"test":[0.6703812173]},
|
||||
{"learn":[0.6536853601],"iteration":804,"passed_time":25.82203653,"remaining_time":38.3320915,"test":[0.6703960068]},
|
||||
{"learn":[0.6536681479],"iteration":805,"passed_time":25.84395064,"remaining_time":38.28495914,"test":[0.6703976729]},
|
||||
{"learn":[0.6536409101],"iteration":806,"passed_time":25.87390688,"remaining_time":38.24977808,"test":[0.6704024604]},
|
||||
{"learn":[0.6536120189],"iteration":807,"passed_time":25.89606204,"remaining_time":38.20310143,"test":[0.6704085008]},
|
||||
{"learn":[0.6535912493],"iteration":808,"passed_time":25.92585483,"remaining_time":38.16772942,"test":[0.6704076633]},
|
||||
{"learn":[0.6535617421],"iteration":809,"passed_time":25.95539059,"remaining_time":38.13199358,"test":[0.6704111719]},
|
||||
{"learn":[0.6535315174],"iteration":810,"passed_time":25.98822968,"remaining_time":38.10111601,"test":[0.6704220803]},
|
||||
{"learn":[0.6534972927],"iteration":811,"passed_time":26.02835773,"remaining_time":38.08089777,"test":[0.6704265011]},
|
||||
{"learn":[0.6534818476],"iteration":812,"passed_time":26.0558565,"remaining_time":38.04219146,"test":[0.6704251162]},
|
||||
{"learn":[0.6534498323],"iteration":813,"passed_time":26.08151817,"remaining_time":38.00083606,"test":[0.6704375472]},
|
||||
{"learn":[0.6534305025],"iteration":814,"passed_time":26.10848988,"remaining_time":37.96142393,"test":[0.6704319336]},
|
||||
{"learn":[0.6534081059],"iteration":815,"passed_time":26.13143346,"remaining_time":37.91619757,"test":[0.670437614]},
|
||||
{"learn":[0.6533765804],"iteration":816,"passed_time":26.15923661,"remaining_time":37.87806231,"test":[0.6704554331]},
|
||||
{"learn":[0.6533441549],"iteration":817,"passed_time":26.18805523,"remaining_time":37.84141966,"test":[0.6704603317]},
|
||||
{"learn":[0.6533053405],"iteration":818,"passed_time":26.2140726,"remaining_time":37.8007567,"test":[0.6704548042]},
|
||||
{"learn":[0.6532838469],"iteration":819,"passed_time":26.24289367,"remaining_time":37.76416405,"test":[0.6704502654]},
|
||||
{"learn":[0.6532604302],"iteration":820,"passed_time":26.27260776,"remaining_time":37.72887277,"test":[0.6704512072]},
|
||||
{"learn":[0.6532364412],"iteration":821,"passed_time":26.29880394,"remaining_time":37.68855358,"test":[0.6704433481]},
|
||||
{"learn":[0.6532100089],"iteration":822,"passed_time":26.32785215,"remaining_time":37.65234749,"test":[0.6704095112]},
|
||||
{"learn":[0.6531782515],"iteration":823,"passed_time":26.35925682,"remaining_time":37.61952188,"test":[0.6704086019]},
|
||||
{"learn":[0.6531449701],"iteration":824,"passed_time":26.38596096,"remaining_time":37.580005,"test":[0.6703987131]},
|
||||
{"learn":[0.653115452],"iteration":825,"passed_time":26.40854839,"remaining_time":37.53466805,"test":[0.6704019708]},
|
||||
{"learn":[0.6530787602],"iteration":826,"passed_time":26.44419918,"remaining_time":37.50791492,"test":[0.6704046556]},
|
||||
{"learn":[0.653052397],"iteration":827,"passed_time":26.47784276,"remaining_time":37.47829917,"test":[0.6704091961]},
|
||||
{"learn":[0.6530313579],"iteration":828,"passed_time":26.51701028,"remaining_time":37.45647652,"test":[0.6704103204]},
|
||||
{"learn":[0.6530010363],"iteration":829,"passed_time":26.53963123,"remaining_time":37.41128739,"test":[0.6704074257]},
|
||||
{"learn":[0.6529752146],"iteration":830,"passed_time":26.57362226,"remaining_time":37.38214732,"test":[0.6704115335]},
|
||||
{"learn":[0.652954801],"iteration":831,"passed_time":26.59767057,"remaining_time":37.33903754,"test":[0.6704041275]},
|
||||
{"learn":[0.6529330351],"iteration":832,"passed_time":26.62378941,"remaining_time":37.29887425,"test":[0.6704004556]},
|
||||
{"learn":[0.6528993709],"iteration":833,"passed_time":26.65024746,"remaining_time":37.25921887,"test":[0.6704037097]},
|
||||
{"learn":[0.6528665883],"iteration":834,"passed_time":26.67774911,"remaining_time":37.22105115,"test":[0.6704035477]},
|
||||
{"learn":[0.6528413041],"iteration":835,"passed_time":26.70473813,"remaining_time":37.1821952,"test":[0.6704025281]},
|
||||
{"learn":[0.6528217161],"iteration":836,"passed_time":26.72833235,"remaining_time":37.13865056,"test":[0.6704024549]},
|
||||
{"learn":[0.6527978782],"iteration":837,"passed_time":26.76384162,"remaining_time":37.11167537,"test":[0.670405721]},
|
||||
{"learn":[0.6527789461],"iteration":838,"passed_time":26.79137369,"remaining_time":37.07364106,"test":[0.6703983189]},
|
||||
{"learn":[0.6527432001],"iteration":839,"passed_time":26.82295602,"remaining_time":37.04122498,"test":[0.6704035256]},
|
||||
{"learn":[0.6527139767],"iteration":840,"passed_time":26.87217031,"remaining_time":37.03310985,"test":[0.6704047613]},
|
||||
{"learn":[0.6526857244],"iteration":841,"passed_time":26.92488006,"remaining_time":37.0297044,"test":[0.6704139617]},
|
||||
{"learn":[0.652657086],"iteration":842,"passed_time":26.98258041,"remaining_time":37.03303147,"test":[0.6704066193]},
|
||||
{"learn":[0.6526355016],"iteration":843,"passed_time":27.05424841,"remaining_time":37.05534497,"test":[0.670402892]},
|
||||
{"learn":[0.6526054936],"iteration":844,"passed_time":27.09765154,"remaining_time":37.03880181,"test":[0.6704081961]},
|
||||
{"learn":[0.6525793707],"iteration":845,"passed_time":27.12038959,"remaining_time":36.99400661,"test":[0.6704029862]},
|
||||
{"learn":[0.6525584692],"iteration":846,"passed_time":27.14691224,"remaining_time":36.95441537,"test":[0.6704014281]},
|
||||
{"learn":[0.6525279747],"iteration":847,"passed_time":27.18096334,"remaining_time":36.92508227,"test":[0.6704036115]},
|
||||
{"learn":[0.6525038765],"iteration":848,"passed_time":27.20686017,"remaining_time":36.88468322,"test":[0.6704016777]},
|
||||
{"learn":[0.6524849104],"iteration":849,"passed_time":27.23465701,"remaining_time":36.8468889,"test":[0.6704085392]},
|
||||
{"learn":[0.6524610603],"iteration":850,"passed_time":27.26094834,"remaining_time":36.80708536,"test":[0.6704042952]},
|
||||
{"learn":[0.6524357337],"iteration":851,"passed_time":27.28945577,"remaining_time":36.77029957,"test":[0.670394789]},
|
||||
{"learn":[0.6524082286],"iteration":852,"passed_time":27.31865398,"remaining_time":36.73446203,"test":[0.6703885644]},
|
||||
{"learn":[0.65238051],"iteration":853,"passed_time":27.34791322,"remaining_time":36.69872195,"test":[0.6703946813]},
|
||||
{"learn":[0.6523557826],"iteration":854,"passed_time":27.3865535,"remaining_time":36.67555995,"test":[0.6704042137]},
|
||||
{"learn":[0.6523391233],"iteration":855,"passed_time":27.41370907,"remaining_time":36.63701306,"test":[0.6704077517]},
|
||||
{"learn":[0.652325347],"iteration":856,"passed_time":27.43905921,"remaining_time":36.5960848,"test":[0.6704118698]},
|
||||
{"learn":[0.6522924958],"iteration":857,"passed_time":27.47159295,"remaining_time":36.56475425,"test":[0.6704114259]},
|
||||
{"learn":[0.6522623584],"iteration":858,"passed_time":27.50124299,"remaining_time":36.52959052,"test":[0.6704157567]},
|
||||
{"learn":[0.6522343891],"iteration":859,"passed_time":27.53509105,"remaining_time":36.50000442,"test":[0.6703837005]},
|
||||
{"learn":[0.6522094424],"iteration":860,"passed_time":27.57211091,"remaining_time":36.47460432,"test":[0.6703829482]},
|
||||
{"learn":[0.6521841478],"iteration":861,"passed_time":27.59555719,"remaining_time":36.43125764,"test":[0.6703818491]},
|
||||
{"learn":[0.6521657946],"iteration":862,"passed_time":27.6272049,"remaining_time":36.39876242,"test":[0.6703826129]},
|
||||
{"learn":[0.6521304278],"iteration":863,"passed_time":27.65462267,"remaining_time":36.36070759,"test":[0.6703834487]},
|
||||
{"learn":[0.6521045712],"iteration":864,"passed_time":27.68321566,"remaining_time":36.3242194,"test":[0.6703868275]},
|
||||
{"learn":[0.6520753696],"iteration":865,"passed_time":27.71151671,"remaining_time":36.28736714,"test":[0.6703853357]},
|
||||
{"learn":[0.6520519528],"iteration":866,"passed_time":27.73884016,"remaining_time":36.2492571,"test":[0.670450644]},
|
||||
{"learn":[0.6520216555],"iteration":867,"passed_time":27.76583897,"remaining_time":36.21074851,"test":[0.6704556991]},
|
||||
{"learn":[0.6519926935],"iteration":868,"passed_time":27.79498714,"remaining_time":36.17506382,"test":[0.6704535742]},
|
||||
{"learn":[0.6519734186],"iteration":869,"passed_time":27.82082723,"remaining_time":36.13509744,"test":[0.6704495915]}
|
||||
]}
|
||||
Binary file not shown.
@@ -0,0 +1,871 @@
|
||||
iter Logloss
|
||||
0 0.692389481
|
||||
1 0.6916338586
|
||||
2 0.6910159214
|
||||
3 0.6903417151
|
||||
4 0.6896961461
|
||||
5 0.6890979366
|
||||
6 0.6884946167
|
||||
7 0.6879503686
|
||||
8 0.6874528094
|
||||
9 0.6869036785
|
||||
10 0.6863761921
|
||||
11 0.6859038678
|
||||
12 0.685410175
|
||||
13 0.6849483392
|
||||
14 0.6845417792
|
||||
15 0.6841038875
|
||||
16 0.6836957422
|
||||
17 0.6832947461
|
||||
18 0.6829014105
|
||||
19 0.6825264546
|
||||
20 0.6822106577
|
||||
21 0.6818649349
|
||||
22 0.6815467855
|
||||
23 0.6812293319
|
||||
24 0.6808837443
|
||||
25 0.6805816494
|
||||
26 0.6803209634
|
||||
27 0.6800350862
|
||||
28 0.6797703947
|
||||
29 0.6794926675
|
||||
30 0.6792251865
|
||||
31 0.6789670166
|
||||
32 0.678722402
|
||||
33 0.678476935
|
||||
34 0.6782297335
|
||||
35 0.6780226701
|
||||
36 0.6778291026
|
||||
37 0.6776045324
|
||||
38 0.6773969079
|
||||
39 0.6771819602
|
||||
40 0.6769816736
|
||||
41 0.6767984027
|
||||
42 0.6766201184
|
||||
43 0.6764394377
|
||||
44 0.6762698797
|
||||
45 0.6760974263
|
||||
46 0.6759245179
|
||||
47 0.6757673909
|
||||
48 0.6756172628
|
||||
49 0.675474531
|
||||
50 0.6753286933
|
||||
51 0.6751900513
|
||||
52 0.6750574835
|
||||
53 0.6749329567
|
||||
54 0.6748033265
|
||||
55 0.6746797823
|
||||
56 0.674535525
|
||||
57 0.6744256514
|
||||
58 0.674310819
|
||||
59 0.6741967947
|
||||
60 0.6740879654
|
||||
61 0.6739772476
|
||||
62 0.67388281
|
||||
63 0.6737789726
|
||||
64 0.6736812332
|
||||
65 0.6735930009
|
||||
66 0.6734947116
|
||||
67 0.6733961481
|
||||
68 0.6732990195
|
||||
69 0.6732133575
|
||||
70 0.673111539
|
||||
71 0.6730080451
|
||||
72 0.6729157861
|
||||
73 0.6728347949
|
||||
74 0.6727640693
|
||||
75 0.6726808811
|
||||
76 0.6726029645
|
||||
77 0.6725356026
|
||||
78 0.6724606887
|
||||
79 0.6723849561
|
||||
80 0.6723050519
|
||||
81 0.6722508802
|
||||
82 0.6721773904
|
||||
83 0.6721007598
|
||||
84 0.6720353564
|
||||
85 0.6719790902
|
||||
86 0.6719140024
|
||||
87 0.6718573633
|
||||
88 0.671795602
|
||||
89 0.6717369134
|
||||
90 0.6716711079
|
||||
91 0.6716070843
|
||||
92 0.6715517232
|
||||
93 0.6714957378
|
||||
94 0.6714364567
|
||||
95 0.6713881758
|
||||
96 0.6713336502
|
||||
97 0.6712700267
|
||||
98 0.6712154424
|
||||
99 0.6711600413
|
||||
100 0.6711060533
|
||||
101 0.6710494943
|
||||
102 0.6709936897
|
||||
103 0.6709472183
|
||||
104 0.6708914508
|
||||
105 0.6708388195
|
||||
106 0.6707885854
|
||||
107 0.6707454167
|
||||
108 0.6706973013
|
||||
109 0.6706577031
|
||||
110 0.67061108
|
||||
111 0.6705625485
|
||||
112 0.6705146484
|
||||
113 0.6704704423
|
||||
114 0.6704155922
|
||||
115 0.6703687117
|
||||
116 0.6703324232
|
||||
117 0.6702884624
|
||||
118 0.670253478
|
||||
119 0.6702140804
|
||||
120 0.6701682529
|
||||
121 0.6701320588
|
||||
122 0.6700939824
|
||||
123 0.6700655902
|
||||
124 0.6700190743
|
||||
125 0.6699792296
|
||||
126 0.6699379404
|
||||
127 0.669895454
|
||||
128 0.6698563938
|
||||
129 0.6698215571
|
||||
130 0.6697857067
|
||||
131 0.6697449303
|
||||
132 0.6697052425
|
||||
133 0.6696695553
|
||||
134 0.6696269265
|
||||
135 0.6695969271
|
||||
136 0.6695489786
|
||||
137 0.6695173859
|
||||
138 0.6694811164
|
||||
139 0.6694477439
|
||||
140 0.6694082161
|
||||
141 0.6693679185
|
||||
142 0.6693341916
|
||||
143 0.6692933159
|
||||
144 0.6692619696
|
||||
145 0.6692229289
|
||||
146 0.6691840164
|
||||
147 0.6691581406
|
||||
148 0.6691177196
|
||||
149 0.6690851126
|
||||
150 0.6690518144
|
||||
151 0.6690149711
|
||||
152 0.668993877
|
||||
153 0.6689596579
|
||||
154 0.6689372651
|
||||
155 0.6689003045
|
||||
156 0.6688680182
|
||||
157 0.6688348164
|
||||
158 0.6687947046
|
||||
159 0.6687605251
|
||||
160 0.668726253
|
||||
161 0.6686862718
|
||||
162 0.668663478
|
||||
163 0.6686399521
|
||||
164 0.6686058279
|
||||
165 0.6685761282
|
||||
166 0.6685469327
|
||||
167 0.6685157003
|
||||
168 0.6684805143
|
||||
169 0.6684485765
|
||||
170 0.6684144429
|
||||
171 0.6683849752
|
||||
172 0.6683568537
|
||||
173 0.6683266628
|
||||
174 0.6682937842
|
||||
175 0.6682657097
|
||||
176 0.6682301443
|
||||
177 0.6681995916
|
||||
178 0.6681658267
|
||||
179 0.6681422687
|
||||
180 0.6681216601
|
||||
181 0.6680899019
|
||||
182 0.6680676394
|
||||
183 0.6680413672
|
||||
184 0.6680088406
|
||||
185 0.6679873982
|
||||
186 0.6679663544
|
||||
187 0.6679417375
|
||||
188 0.6679100197
|
||||
189 0.667881208
|
||||
190 0.6678475427
|
||||
191 0.6678310341
|
||||
192 0.6678060257
|
||||
193 0.6677789336
|
||||
194 0.6677478773
|
||||
195 0.6677212408
|
||||
196 0.667704316
|
||||
197 0.6676819639
|
||||
198 0.6676554448
|
||||
199 0.6676318346
|
||||
200 0.6676074705
|
||||
201 0.6675849784
|
||||
202 0.6675631744
|
||||
203 0.6675397619
|
||||
204 0.6675169086
|
||||
205 0.6674864762
|
||||
206 0.6674670714
|
||||
207 0.6674375599
|
||||
208 0.6674148457
|
||||
209 0.6673974446
|
||||
210 0.6673812139
|
||||
211 0.6673515687
|
||||
212 0.6673197956
|
||||
213 0.6672900754
|
||||
214 0.6672550009
|
||||
215 0.6672271563
|
||||
216 0.667204521
|
||||
217 0.667181968
|
||||
218 0.6671640023
|
||||
219 0.66714351
|
||||
220 0.6671167156
|
||||
221 0.6670915937
|
||||
222 0.6670595279
|
||||
223 0.667033994
|
||||
224 0.6670008246
|
||||
225 0.6669858319
|
||||
226 0.6669553964
|
||||
227 0.6669274683
|
||||
228 0.666896348
|
||||
229 0.6668698686
|
||||
230 0.6668513411
|
||||
231 0.6668309985
|
||||
232 0.6668058585
|
||||
233 0.6667845908
|
||||
234 0.6667582863
|
||||
235 0.6667332943
|
||||
236 0.6667070085
|
||||
237 0.6666907315
|
||||
238 0.6666633028
|
||||
239 0.6666406707
|
||||
240 0.6666134624
|
||||
241 0.6665850522
|
||||
242 0.6665631193
|
||||
243 0.6665412643
|
||||
244 0.6665168385
|
||||
245 0.6664904845
|
||||
246 0.6664678274
|
||||
247 0.6664539777
|
||||
248 0.6664334121
|
||||
249 0.6664121724
|
||||
250 0.666392034
|
||||
251 0.666366899
|
||||
252 0.6663414098
|
||||
253 0.6663157816
|
||||
254 0.6662989799
|
||||
255 0.6662696102
|
||||
256 0.6662479711
|
||||
257 0.6662231874
|
||||
258 0.6661947927
|
||||
259 0.6661669951
|
||||
260 0.6661426137
|
||||
261 0.6661216749
|
||||
262 0.6660983123
|
||||
263 0.6660803402
|
||||
264 0.6660617842
|
||||
265 0.6660443878
|
||||
266 0.6660176079
|
||||
267 0.6659967546
|
||||
268 0.6659751467
|
||||
269 0.6659539329
|
||||
270 0.6659263951
|
||||
271 0.6659038921
|
||||
272 0.6658767418
|
||||
273 0.6658510507
|
||||
274 0.6658210119
|
||||
275 0.6657963011
|
||||
276 0.6657748552
|
||||
277 0.6657490013
|
||||
278 0.665732402
|
||||
279 0.6657118786
|
||||
280 0.665684467
|
||||
281 0.6656584634
|
||||
282 0.6656309991
|
||||
283 0.6656073482
|
||||
284 0.6655890957
|
||||
285 0.6655665563
|
||||
286 0.6655452454
|
||||
287 0.6655255286
|
||||
288 0.6655053548
|
||||
289 0.6654893396
|
||||
290 0.6654648912
|
||||
291 0.6654442759
|
||||
292 0.6654173127
|
||||
293 0.6653914518
|
||||
294 0.6653648946
|
||||
295 0.665344141
|
||||
296 0.6653140817
|
||||
297 0.665295365
|
||||
298 0.6652787488
|
||||
299 0.6652502991
|
||||
300 0.665231168
|
||||
301 0.6652136682
|
||||
302 0.6651903001
|
||||
303 0.6651697153
|
||||
304 0.6651525958
|
||||
305 0.6651322685
|
||||
306 0.6651113828
|
||||
307 0.6650886807
|
||||
308 0.6650622251
|
||||
309 0.6650429987
|
||||
310 0.665015513
|
||||
311 0.6650019022
|
||||
312 0.664979951
|
||||
313 0.6649549638
|
||||
314 0.6649340455
|
||||
315 0.6649162445
|
||||
316 0.6649048119
|
||||
317 0.6648796463
|
||||
318 0.6648605481
|
||||
319 0.6648429084
|
||||
320 0.6648238121
|
||||
321 0.6647969527
|
||||
322 0.6647854723
|
||||
323 0.6647589304
|
||||
324 0.6647429024
|
||||
325 0.6647237508
|
||||
326 0.6647059396
|
||||
327 0.664686288
|
||||
328 0.6646532527
|
||||
329 0.6646306438
|
||||
330 0.6646098516
|
||||
331 0.6645858284
|
||||
332 0.6645707188
|
||||
333 0.6645485788
|
||||
334 0.6645305696
|
||||
335 0.6645108881
|
||||
336 0.6644923286
|
||||
337 0.6644805222
|
||||
338 0.6644572776
|
||||
339 0.6644320741
|
||||
340 0.6644115048
|
||||
341 0.6643949013
|
||||
342 0.6643619789
|
||||
343 0.6643389502
|
||||
344 0.6643088915
|
||||
345 0.664286972
|
||||
346 0.664274149
|
||||
347 0.6642536926
|
||||
348 0.6642357634
|
||||
349 0.664207914
|
||||
350 0.6641853097
|
||||
351 0.6641654917
|
||||
352 0.664143804
|
||||
353 0.6641290647
|
||||
354 0.6641117244
|
||||
355 0.6640880219
|
||||
356 0.6640669415
|
||||
357 0.6640462999
|
||||
358 0.664030296
|
||||
359 0.6640028542
|
||||
360 0.6639813347
|
||||
361 0.6639597941
|
||||
362 0.6639429832
|
||||
363 0.6639222708
|
||||
364 0.6639065546
|
||||
365 0.6638823236
|
||||
366 0.6638648195
|
||||
367 0.6638436235
|
||||
368 0.6638208732
|
||||
369 0.6637956357
|
||||
370 0.6637718453
|
||||
371 0.663756918
|
||||
372 0.6637353525
|
||||
373 0.6637143112
|
||||
374 0.6636956547
|
||||
375 0.663680995
|
||||
376 0.66366728
|
||||
377 0.6636487567
|
||||
378 0.6636266904
|
||||
379 0.6636116064
|
||||
380 0.6635902746
|
||||
381 0.6635654896
|
||||
382 0.6635393029
|
||||
383 0.6635171734
|
||||
384 0.663500789
|
||||
385 0.663477743
|
||||
386 0.6634584806
|
||||
387 0.6634337499
|
||||
388 0.6634135584
|
||||
389 0.6633868455
|
||||
390 0.6633755323
|
||||
391 0.663356103
|
||||
392 0.6633337631
|
||||
393 0.663319422
|
||||
394 0.6632911566
|
||||
395 0.6632687875
|
||||
396 0.6632431997
|
||||
397 0.6632189331
|
||||
398 0.663201035
|
||||
399 0.6631898553
|
||||
400 0.6631712482
|
||||
401 0.663143025
|
||||
402 0.663121538
|
||||
403 0.6631087792
|
||||
404 0.6630859067
|
||||
405 0.663066483
|
||||
406 0.6630443652
|
||||
407 0.6630250376
|
||||
408 0.6630007822
|
||||
409 0.6629768728
|
||||
410 0.6629528093
|
||||
411 0.6629260936
|
||||
412 0.6629102182
|
||||
413 0.6628863488
|
||||
414 0.6628648972
|
||||
415 0.6628454339
|
||||
416 0.6628200274
|
||||
417 0.6627942591
|
||||
418 0.6627744647
|
||||
419 0.662765485
|
||||
420 0.6627503257
|
||||
421 0.6627323029
|
||||
422 0.6627111509
|
||||
423 0.6626785863
|
||||
424 0.6626576561
|
||||
425 0.6626363113
|
||||
426 0.6626181065
|
||||
427 0.66259794
|
||||
428 0.6625765658
|
||||
429 0.6625526572
|
||||
430 0.66253135
|
||||
431 0.6625035695
|
||||
432 0.662480212
|
||||
433 0.6624611632
|
||||
434 0.6624332625
|
||||
435 0.6624120584
|
||||
436 0.6623941719
|
||||
437 0.6623766304
|
||||
438 0.6623623329
|
||||
439 0.6623442925
|
||||
440 0.6623212715
|
||||
441 0.6623025941
|
||||
442 0.6622749791
|
||||
443 0.6622534499
|
||||
444 0.6622305473
|
||||
445 0.6622059333
|
||||
446 0.6621871707
|
||||
447 0.6621638454
|
||||
448 0.6621511296
|
||||
449 0.6621349978
|
||||
450 0.6621120424
|
||||
451 0.6620958271
|
||||
452 0.6620793528
|
||||
453 0.6620572713
|
||||
454 0.6620395025
|
||||
455 0.6620188044
|
||||
456 0.6620017347
|
||||
457 0.6619811454
|
||||
458 0.6619695569
|
||||
459 0.661952377
|
||||
460 0.6619237442
|
||||
461 0.6619089407
|
||||
462 0.6618886168
|
||||
463 0.6618831383
|
||||
464 0.6618690774
|
||||
465 0.661845878
|
||||
466 0.6618290213
|
||||
467 0.6618050064
|
||||
468 0.6617832833
|
||||
469 0.6617652311
|
||||
470 0.6617443144
|
||||
471 0.6617202619
|
||||
472 0.6617005831
|
||||
473 0.6616824419
|
||||
474 0.6616538226
|
||||
475 0.6616314155
|
||||
476 0.6616127861
|
||||
477 0.6616029072
|
||||
478 0.6615843751
|
||||
479 0.661563216
|
||||
480 0.6615432257
|
||||
481 0.6615263324
|
||||
482 0.6615033259
|
||||
483 0.661484293
|
||||
484 0.6614678231
|
||||
485 0.6614463024
|
||||
486 0.6614155436
|
||||
487 0.6613958945
|
||||
488 0.661380611
|
||||
489 0.6613677802
|
||||
490 0.6613530086
|
||||
491 0.6613248211
|
||||
492 0.6613059359
|
||||
493 0.6612729965
|
||||
494 0.6612624948
|
||||
495 0.6612401679
|
||||
496 0.6612191637
|
||||
497 0.6611912219
|
||||
498 0.6611773017
|
||||
499 0.6611638216
|
||||
500 0.6611450533
|
||||
501 0.6611179111
|
||||
502 0.6610959069
|
||||
503 0.6610728788
|
||||
504 0.6610436668
|
||||
505 0.6610188976
|
||||
506 0.6610030555
|
||||
507 0.6609831174
|
||||
508 0.6609586562
|
||||
509 0.660935882
|
||||
510 0.6609202024
|
||||
511 0.6609011137
|
||||
512 0.6608726737
|
||||
513 0.6608608849
|
||||
514 0.6608387256
|
||||
515 0.6608136063
|
||||
516 0.6607946343
|
||||
517 0.6607703935
|
||||
518 0.6607509625
|
||||
519 0.6607238109
|
||||
520 0.6606999858
|
||||
521 0.6606813873
|
||||
522 0.6606610372
|
||||
523 0.660638456
|
||||
524 0.6606156483
|
||||
525 0.6605968623
|
||||
526 0.6605735776
|
||||
527 0.6605517294
|
||||
528 0.6605309239
|
||||
529 0.6605086434
|
||||
530 0.6604803349
|
||||
531 0.6604566326
|
||||
532 0.6604430839
|
||||
533 0.6604273738
|
||||
534 0.6604048016
|
||||
535 0.6603845173
|
||||
536 0.6603669212
|
||||
537 0.6603488983
|
||||
538 0.6603176881
|
||||
539 0.6602953862
|
||||
540 0.6602672025
|
||||
541 0.6602568636
|
||||
542 0.660235705
|
||||
543 0.6602152295
|
||||
544 0.6601897709
|
||||
545 0.6601683731
|
||||
546 0.6601472267
|
||||
547 0.6601262337
|
||||
548 0.6601119991
|
||||
549 0.6600869973
|
||||
550 0.6600667497
|
||||
551 0.6600397508
|
||||
552 0.660016863
|
||||
553 0.6599933158
|
||||
554 0.6599632649
|
||||
555 0.6599446007
|
||||
556 0.6599138126
|
||||
557 0.6598965504
|
||||
558 0.6598785723
|
||||
559 0.659860838
|
||||
560 0.6598408724
|
||||
561 0.6598244857
|
||||
562 0.6598082469
|
||||
563 0.6597851673
|
||||
564 0.6597683521
|
||||
565 0.6597479006
|
||||
566 0.6597310938
|
||||
567 0.6597096581
|
||||
568 0.6596862311
|
||||
569 0.6596574779
|
||||
570 0.6596385418
|
||||
571 0.6596189903
|
||||
572 0.65959275
|
||||
573 0.6595730662
|
||||
574 0.6595566809
|
||||
575 0.6595365076
|
||||
576 0.6595163446
|
||||
577 0.6594816637
|
||||
578 0.6594570142
|
||||
579 0.6594353055
|
||||
580 0.6594162362
|
||||
581 0.659395036
|
||||
582 0.6593798831
|
||||
583 0.6593556719
|
||||
584 0.6593292627
|
||||
585 0.6592976737
|
||||
586 0.6592754841
|
||||
587 0.6592510441
|
||||
588 0.6592290326
|
||||
589 0.6592097404
|
||||
590 0.6591876204
|
||||
591 0.6591705995
|
||||
592 0.6591456195
|
||||
593 0.6591107122
|
||||
594 0.6590819533
|
||||
595 0.6590551327
|
||||
596 0.6590373916
|
||||
597 0.6590177149
|
||||
598 0.6589946095
|
||||
599 0.6589697628
|
||||
600 0.6589442269
|
||||
601 0.6589182437
|
||||
602 0.6588837179
|
||||
603 0.6588674101
|
||||
604 0.6588406916
|
||||
605 0.6588149945
|
||||
606 0.6587866031
|
||||
607 0.6587636648
|
||||
608 0.6587502469
|
||||
609 0.6587292784
|
||||
610 0.6587104112
|
||||
611 0.6586953782
|
||||
612 0.6586641191
|
||||
613 0.6586450136
|
||||
614 0.6586136263
|
||||
615 0.6585862768
|
||||
616 0.6585585235
|
||||
617 0.6585371631
|
||||
618 0.6585092632
|
||||
619 0.6584914317
|
||||
620 0.6584662432
|
||||
621 0.6584454668
|
||||
622 0.6584249408
|
||||
623 0.6583931228
|
||||
624 0.6583660767
|
||||
625 0.658354264
|
||||
626 0.6583253625
|
||||
627 0.6582968632
|
||||
628 0.6582687399
|
||||
629 0.658242535
|
||||
630 0.6582199874
|
||||
631 0.6581918101
|
||||
632 0.6581735218
|
||||
633 0.6581445869
|
||||
634 0.6581202427
|
||||
635 0.6580977862
|
||||
636 0.6580724179
|
||||
637 0.6580426322
|
||||
638 0.6580111256
|
||||
639 0.6579834747
|
||||
640 0.6579541367
|
||||
641 0.6579254503
|
||||
642 0.657898555
|
||||
643 0.6578676875
|
||||
644 0.6578324163
|
||||
645 0.6578062223
|
||||
646 0.6577760631
|
||||
647 0.6577483474
|
||||
648 0.6577249642
|
||||
649 0.6576974966
|
||||
650 0.657675114
|
||||
651 0.6576447891
|
||||
652 0.6576102356
|
||||
653 0.6575793887
|
||||
654 0.6575543309
|
||||
655 0.6575340787
|
||||
656 0.6575061464
|
||||
657 0.657476113
|
||||
658 0.6574447014
|
||||
659 0.6574247361
|
||||
660 0.6574034983
|
||||
661 0.6573783832
|
||||
662 0.657357694
|
||||
663 0.6573411592
|
||||
664 0.6573118559
|
||||
665 0.6572819076
|
||||
666 0.6572430097
|
||||
667 0.6572160391
|
||||
668 0.6571931413
|
||||
669 0.6571737099
|
||||
670 0.6571532872
|
||||
671 0.6571208939
|
||||
672 0.6570887673
|
||||
673 0.6570633692
|
||||
674 0.6570454361
|
||||
675 0.6570231031
|
||||
676 0.6570052089
|
||||
677 0.6569855794
|
||||
678 0.6569579709
|
||||
679 0.6569333354
|
||||
680 0.6569069617
|
||||
681 0.6568931857
|
||||
682 0.6568734532
|
||||
683 0.6568435196
|
||||
684 0.6568108038
|
||||
685 0.6567811374
|
||||
686 0.6567467284
|
||||
687 0.6567172734
|
||||
688 0.6566967606
|
||||
689 0.6566720128
|
||||
690 0.6566441608
|
||||
691 0.6566172287
|
||||
692 0.6565952549
|
||||
693 0.6565702687
|
||||
694 0.6565392213
|
||||
695 0.6565157938
|
||||
696 0.6564902789
|
||||
697 0.6564644734
|
||||
698 0.6564349549
|
||||
699 0.6564046572
|
||||
700 0.6563744107
|
||||
701 0.6563525063
|
||||
702 0.6563189867
|
||||
703 0.6562939062
|
||||
704 0.6562739297
|
||||
705 0.656256438
|
||||
706 0.6562366475
|
||||
707 0.6562073096
|
||||
708 0.6561864222
|
||||
709 0.6561578826
|
||||
710 0.6561208567
|
||||
711 0.6560924703
|
||||
712 0.6560656907
|
||||
713 0.6560362588
|
||||
714 0.6560124527
|
||||
715 0.6559875055
|
||||
716 0.6559547281
|
||||
717 0.6559230866
|
||||
718 0.6558924823
|
||||
719 0.6558676469
|
||||
720 0.6558459277
|
||||
721 0.6558149638
|
||||
722 0.6557812248
|
||||
723 0.6557546502
|
||||
724 0.6557274948
|
||||
725 0.6557044723
|
||||
726 0.6556751811
|
||||
727 0.6556539158
|
||||
728 0.6556182915
|
||||
729 0.6555977079
|
||||
730 0.6555667903
|
||||
731 0.6555394075
|
||||
732 0.6555122742
|
||||
733 0.6554814941
|
||||
734 0.6554517373
|
||||
735 0.655429552
|
||||
736 0.655396579
|
||||
737 0.6553735864
|
||||
738 0.6553472597
|
||||
739 0.6553252832
|
||||
740 0.6552971659
|
||||
741 0.6552763852
|
||||
742 0.6552488203
|
||||
743 0.65521229
|
||||
744 0.6551949744
|
||||
745 0.6551673797
|
||||
746 0.6551421856
|
||||
747 0.6551255516
|
||||
748 0.6551019608
|
||||
749 0.6550758728
|
||||
750 0.655051966
|
||||
751 0.6550351058
|
||||
752 0.6549998756
|
||||
753 0.6549721212
|
||||
754 0.6549401744
|
||||
755 0.6549207325
|
||||
756 0.6548900891
|
||||
757 0.6548682731
|
||||
758 0.6548418938
|
||||
759 0.6548234717
|
||||
760 0.6547996833
|
||||
761 0.6547726174
|
||||
762 0.6547509314
|
||||
763 0.6547168175
|
||||
764 0.6546907846
|
||||
765 0.6546671611
|
||||
766 0.6546475893
|
||||
767 0.6546206223
|
||||
768 0.6545874193
|
||||
769 0.6545620629
|
||||
770 0.6545346297
|
||||
771 0.6545172316
|
||||
772 0.6544943049
|
||||
773 0.6544632323
|
||||
774 0.6544384097
|
||||
775 0.6544084745
|
||||
776 0.6543765257
|
||||
777 0.6543536123
|
||||
778 0.6543303593
|
||||
779 0.6543005831
|
||||
780 0.6542678123
|
||||
781 0.6542439303
|
||||
782 0.6542100401
|
||||
783 0.6541836178
|
||||
784 0.654158129
|
||||
785 0.6541343464
|
||||
786 0.6541092921
|
||||
787 0.6540812254
|
||||
788 0.654060259
|
||||
789 0.6540467253
|
||||
790 0.6540306837
|
||||
791 0.6540103667
|
||||
792 0.6539821302
|
||||
793 0.6539577914
|
||||
794 0.653923724
|
||||
795 0.6539086888
|
||||
796 0.6538798424
|
||||
797 0.6538566996
|
||||
798 0.6538290752
|
||||
799 0.6538051255
|
||||
800 0.6537917354
|
||||
801 0.6537684302
|
||||
802 0.6537402991
|
||||
803 0.6537165427
|
||||
804 0.6536853601
|
||||
805 0.6536681479
|
||||
806 0.6536409101
|
||||
807 0.6536120189
|
||||
808 0.6535912493
|
||||
809 0.6535617421
|
||||
810 0.6535315174
|
||||
811 0.6534972927
|
||||
812 0.6534818476
|
||||
813 0.6534498323
|
||||
814 0.6534305025
|
||||
815 0.6534081059
|
||||
816 0.6533765804
|
||||
817 0.6533441549
|
||||
818 0.6533053405
|
||||
819 0.6532838469
|
||||
820 0.6532604302
|
||||
821 0.6532364412
|
||||
822 0.6532100089
|
||||
823 0.6531782515
|
||||
824 0.6531449701
|
||||
825 0.653115452
|
||||
826 0.6530787602
|
||||
827 0.653052397
|
||||
828 0.6530313579
|
||||
829 0.6530010363
|
||||
830 0.6529752146
|
||||
831 0.652954801
|
||||
832 0.6529330351
|
||||
833 0.6528993709
|
||||
834 0.6528665883
|
||||
835 0.6528413041
|
||||
836 0.6528217161
|
||||
837 0.6527978782
|
||||
838 0.6527789461
|
||||
839 0.6527432001
|
||||
840 0.6527139767
|
||||
841 0.6526857244
|
||||
842 0.652657086
|
||||
843 0.6526355016
|
||||
844 0.6526054936
|
||||
845 0.6525793707
|
||||
846 0.6525584692
|
||||
847 0.6525279747
|
||||
848 0.6525038765
|
||||
849 0.6524849104
|
||||
850 0.6524610603
|
||||
851 0.6524357337
|
||||
852 0.6524082286
|
||||
853 0.65238051
|
||||
854 0.6523557826
|
||||
855 0.6523391233
|
||||
856 0.652325347
|
||||
857 0.6522924958
|
||||
858 0.6522623584
|
||||
859 0.6522343891
|
||||
860 0.6522094424
|
||||
861 0.6521841478
|
||||
862 0.6521657946
|
||||
863 0.6521304278
|
||||
864 0.6521045712
|
||||
865 0.6520753696
|
||||
866 0.6520519528
|
||||
867 0.6520216555
|
||||
868 0.6519926935
|
||||
869 0.6519734186
|
||||
|
@@ -0,0 +1,871 @@
|
||||
iter Passed Remaining
|
||||
0 46 93548
|
||||
1 83 83419
|
||||
2 132 88415
|
||||
3 162 81250
|
||||
4 196 78573
|
||||
5 230 76747
|
||||
6 269 76701
|
||||
7 319 79674
|
||||
8 364 80653
|
||||
9 411 81918
|
||||
10 456 82497
|
||||
11 491 81432
|
||||
12 522 79809
|
||||
13 555 78774
|
||||
14 595 78777
|
||||
15 630 78123
|
||||
16 662 77290
|
||||
17 700 77124
|
||||
18 730 76120
|
||||
19 764 75651
|
||||
20 804 75774
|
||||
21 835 75128
|
||||
22 886 76169
|
||||
23 920 75764
|
||||
24 960 75853
|
||||
25 989 75130
|
||||
26 1025 74941
|
||||
27 1060 74714
|
||||
28 1104 75079
|
||||
29 1141 74976
|
||||
30 1180 74975
|
||||
31 1213 74640
|
||||
32 1245 74260
|
||||
33 1287 74434
|
||||
34 1327 74528
|
||||
35 1376 75071
|
||||
36 1427 75741
|
||||
37 1468 75804
|
||||
38 1508 75857
|
||||
39 1549 75922
|
||||
40 1586 75781
|
||||
41 1621 75590
|
||||
42 1663 75705
|
||||
43 1701 75621
|
||||
44 1739 75591
|
||||
45 1776 75460
|
||||
46 1819 75616
|
||||
47 1869 76025
|
||||
48 1916 76288
|
||||
49 1953 76191
|
||||
50 1993 76197
|
||||
51 2038 76381
|
||||
52 2080 76420
|
||||
53 2158 77788
|
||||
54 2220 78529
|
||||
55 2286 79390
|
||||
56 2328 79372
|
||||
57 2367 79254
|
||||
58 2409 79257
|
||||
59 2444 79049
|
||||
60 2484 78985
|
||||
61 2521 78820
|
||||
62 2554 78528
|
||||
63 2593 78466
|
||||
64 2623 78111
|
||||
65 2660 77969
|
||||
66 2695 77776
|
||||
67 2725 77446
|
||||
68 2761 77291
|
||||
69 2791 76975
|
||||
70 2824 76739
|
||||
71 2861 76611
|
||||
72 2897 76476
|
||||
73 2935 76408
|
||||
74 3040 78027
|
||||
75 3097 78411
|
||||
76 3152 78741
|
||||
77 3216 79248
|
||||
78 3256 79195
|
||||
79 3305 79336
|
||||
80 3348 79320
|
||||
81 3381 79089
|
||||
82 3416 78911
|
||||
83 3480 79399
|
||||
84 3535 79649
|
||||
85 3581 79716
|
||||
86 3612 79428
|
||||
87 3644 79185
|
||||
88 3678 78975
|
||||
89 3712 78785
|
||||
90 3743 78531
|
||||
91 3775 78297
|
||||
92 3806 78047
|
||||
93 3837 77821
|
||||
94 3871 77629
|
||||
95 3913 77618
|
||||
96 3945 77403
|
||||
97 3989 77433
|
||||
98 4020 77204
|
||||
99 4053 77020
|
||||
100 4084 76789
|
||||
101 4116 76597
|
||||
102 4148 76401
|
||||
103 4176 76141
|
||||
104 4202 75845
|
||||
105 4232 75634
|
||||
106 4261 75390
|
||||
107 4290 75168
|
||||
108 4324 75018
|
||||
109 4351 74766
|
||||
110 4386 74648
|
||||
111 4424 74577
|
||||
112 4458 74455
|
||||
113 4497 74400
|
||||
114 4533 74307
|
||||
115 4564 74136
|
||||
116 4596 73981
|
||||
117 4628 73818
|
||||
118 4668 73786
|
||||
119 4692 73509
|
||||
120 4723 73354
|
||||
121 4756 73220
|
||||
122 4788 73065
|
||||
123 4815 72854
|
||||
124 4843 72647
|
||||
125 4875 72514
|
||||
126 4916 72515
|
||||
127 4952 72436
|
||||
128 4991 72397
|
||||
129 5028 72327
|
||||
130 5059 72180
|
||||
131 5096 72116
|
||||
132 5125 71946
|
||||
133 5156 71804
|
||||
134 5190 71704
|
||||
135 5221 71564
|
||||
136 5251 71407
|
||||
137 5274 71165
|
||||
138 5309 71084
|
||||
139 5344 71008
|
||||
140 5377 70902
|
||||
141 5416 70866
|
||||
142 5452 70803
|
||||
143 5490 70760
|
||||
144 5521 70641
|
||||
145 5553 70522
|
||||
146 5582 70365
|
||||
147 5611 70217
|
||||
148 5636 70026
|
||||
149 5673 69975
|
||||
150 5706 69874
|
||||
151 5738 69764
|
||||
152 5765 69605
|
||||
153 5795 69471
|
||||
154 5817 69246
|
||||
155 5853 69191
|
||||
156 5888 69122
|
||||
157 5924 69070
|
||||
158 5964 69061
|
||||
159 5996 68963
|
||||
160 6022 68789
|
||||
161 6050 68650
|
||||
162 6079 68510
|
||||
163 6108 68385
|
||||
164 6140 68292
|
||||
165 6169 68162
|
||||
166 6202 68074
|
||||
167 6231 67953
|
||||
168 6263 67858
|
||||
169 6295 67764
|
||||
170 6325 67656
|
||||
171 6356 67561
|
||||
172 6395 67545
|
||||
173 6437 67554
|
||||
174 6472 67495
|
||||
175 6503 67395
|
||||
176 6533 67291
|
||||
177 6562 67174
|
||||
178 6590 67049
|
||||
179 6624 66982
|
||||
180 6655 66882
|
||||
181 6687 66804
|
||||
182 6718 66703
|
||||
183 6751 66632
|
||||
184 6784 66559
|
||||
185 6810 66424
|
||||
186 6832 66246
|
||||
187 6867 66187
|
||||
188 6918 66294
|
||||
189 6969 66393
|
||||
190 7018 66470
|
||||
191 7074 66614
|
||||
192 7117 66635
|
||||
193 7191 66943
|
||||
194 7242 67036
|
||||
195 7282 67027
|
||||
196 7317 66967
|
||||
197 7351 66903
|
||||
198 7389 66879
|
||||
199 7432 66896
|
||||
200 7471 66869
|
||||
201 7506 66814
|
||||
202 7540 66752
|
||||
203 7568 66628
|
||||
204 7605 66596
|
||||
205 7638 66519
|
||||
206 7665 66397
|
||||
207 7700 66340
|
||||
208 7734 66276
|
||||
209 7766 66197
|
||||
210 7796 66106
|
||||
211 7831 66053
|
||||
212 7871 66037
|
||||
213 7910 66016
|
||||
214 7951 66014
|
||||
215 7989 65983
|
||||
216 8025 65946
|
||||
217 8058 65872
|
||||
218 8087 65768
|
||||
219 8112 65638
|
||||
220 8148 65594
|
||||
221 8197 65655
|
||||
222 8239 65655
|
||||
223 8268 65556
|
||||
224 8298 65466
|
||||
225 8327 65366
|
||||
226 8357 65278
|
||||
227 8384 65167
|
||||
228 8418 65103
|
||||
229 8453 65058
|
||||
230 8490 65020
|
||||
231 8523 64958
|
||||
232 8550 64848
|
||||
233 8575 64718
|
||||
234 8607 64648
|
||||
235 8635 64545
|
||||
236 8660 64426
|
||||
237 8691 64345
|
||||
238 8719 64250
|
||||
239 8746 64137
|
||||
240 8773 64038
|
||||
241 8803 63951
|
||||
242 8833 63873
|
||||
243 8862 63779
|
||||
244 8892 63698
|
||||
245 8932 63688
|
||||
246 8962 63611
|
||||
247 8991 63521
|
||||
248 9021 63442
|
||||
249 9051 63358
|
||||
250 9085 63306
|
||||
251 9110 63193
|
||||
252 9137 63093
|
||||
253 9174 63066
|
||||
254 9196 62935
|
||||
255 9238 62934
|
||||
256 9267 62855
|
||||
257 9297 62776
|
||||
258 9324 62681
|
||||
259 9357 62625
|
||||
260 9388 62552
|
||||
261 9427 62536
|
||||
262 9461 62491
|
||||
263 9496 62443
|
||||
264 9524 62356
|
||||
265 9553 62278
|
||||
266 9590 62247
|
||||
267 9620 62172
|
||||
268 9645 62071
|
||||
269 9682 62040
|
||||
270 9711 61962
|
||||
271 9739 61872
|
||||
272 9768 61797
|
||||
273 9804 61761
|
||||
274 9848 61777
|
||||
275 9886 61755
|
||||
276 9925 61740
|
||||
277 9965 61728
|
||||
278 9995 61656
|
||||
279 10022 61564
|
||||
280 10055 61516
|
||||
281 10080 61410
|
||||
282 10111 61344
|
||||
283 10147 61311
|
||||
284 10175 61230
|
||||
285 10202 61141
|
||||
286 10234 61084
|
||||
287 10264 61018
|
||||
288 10299 60977
|
||||
289 10323 60874
|
||||
290 10353 60804
|
||||
291 10394 60803
|
||||
292 10431 60773
|
||||
293 10471 60763
|
||||
294 10503 60707
|
||||
295 10534 60645
|
||||
296 10576 60646
|
||||
297 10612 60612
|
||||
298 10639 60525
|
||||
299 10668 60453
|
||||
300 10702 60411
|
||||
301 10729 60326
|
||||
302 10764 60290
|
||||
303 10801 60263
|
||||
304 10829 60182
|
||||
305 10857 60108
|
||||
306 10892 60067
|
||||
307 10930 60047
|
||||
308 10972 60045
|
||||
309 11002 59983
|
||||
310 11030 59902
|
||||
311 11058 59828
|
||||
312 11092 59788
|
||||
313 11117 59696
|
||||
314 11149 59641
|
||||
315 11187 59617
|
||||
316 11211 59525
|
||||
317 11243 59468
|
||||
318 11274 59413
|
||||
319 11304 59346
|
||||
320 11334 59287
|
||||
321 11362 59209
|
||||
322 11394 59158
|
||||
323 11436 59158
|
||||
324 11477 59153
|
||||
325 11513 59122
|
||||
326 11547 59081
|
||||
327 11572 58991
|
||||
328 11607 58956
|
||||
329 11637 58894
|
||||
330 11668 58833
|
||||
331 11700 58785
|
||||
332 11724 58694
|
||||
333 11757 58648
|
||||
334 11780 58550
|
||||
335 11815 58515
|
||||
336 11844 58451
|
||||
337 11869 58364
|
||||
338 11905 58335
|
||||
339 11941 58302
|
||||
340 11986 58315
|
||||
341 12020 58274
|
||||
342 12066 58292
|
||||
343 12122 58358
|
||||
344 12177 58415
|
||||
345 12221 58422
|
||||
346 12264 58423
|
||||
347 12300 58394
|
||||
348 12324 58304
|
||||
349 12354 58243
|
||||
350 12401 58262
|
||||
351 12438 58232
|
||||
352 12479 58228
|
||||
353 12512 58179
|
||||
354 12541 58116
|
||||
355 12569 58044
|
||||
356 12597 57977
|
||||
357 12628 57920
|
||||
358 12653 57839
|
||||
359 12682 57775
|
||||
360 12720 57752
|
||||
361 12744 57666
|
||||
362 12770 57592
|
||||
363 12811 57583
|
||||
364 12841 57522
|
||||
365 12870 57460
|
||||
366 12897 57386
|
||||
367 12938 57378
|
||||
368 12974 57347
|
||||
369 13009 57313
|
||||
370 13038 57249
|
||||
371 13078 57235
|
||||
372 13117 57216
|
||||
373 13147 57159
|
||||
374 13181 57118
|
||||
375 13205 57036
|
||||
376 13235 56979
|
||||
377 13274 56960
|
||||
378 13306 56911
|
||||
379 13333 56841
|
||||
380 13366 56798
|
||||
381 13396 56741
|
||||
382 13421 56666
|
||||
383 13467 56674
|
||||
384 13508 56664
|
||||
385 13540 56616
|
||||
386 13569 56559
|
||||
387 13598 56496
|
||||
388 13627 56438
|
||||
389 13656 56376
|
||||
390 13685 56317
|
||||
391 13717 56271
|
||||
392 13750 56227
|
||||
393 13771 56135
|
||||
394 13804 56090
|
||||
395 13825 55999
|
||||
396 13858 55957
|
||||
397 13888 55904
|
||||
398 13917 55843
|
||||
399 13953 55812
|
||||
400 13994 55802
|
||||
401 14025 55752
|
||||
402 14048 55670
|
||||
403 14076 55607
|
||||
404 14105 55551
|
||||
405 14142 55526
|
||||
406 14182 55511
|
||||
407 14214 55464
|
||||
408 14240 55394
|
||||
409 14267 55328
|
||||
410 14299 55284
|
||||
411 14324 55213
|
||||
412 14351 55146
|
||||
413 14379 55086
|
||||
414 14410 55036
|
||||
415 14451 55025
|
||||
416 14484 54984
|
||||
417 14513 54929
|
||||
418 14536 54851
|
||||
419 14565 54793
|
||||
420 14587 54710
|
||||
421 14615 54650
|
||||
422 14642 54588
|
||||
423 14666 54515
|
||||
424 14690 54441
|
||||
425 14719 54384
|
||||
426 14739 54297
|
||||
427 14772 54257
|
||||
428 14790 54164
|
||||
429 14824 54125
|
||||
430 14844 54039
|
||||
431 14876 53995
|
||||
432 14906 53946
|
||||
433 14938 53902
|
||||
434 14980 53894
|
||||
435 15006 53829
|
||||
436 15033 53770
|
||||
437 15059 53706
|
||||
438 15085 53639
|
||||
439 15110 53574
|
||||
440 15134 53503
|
||||
441 15160 53438
|
||||
442 15184 53369
|
||||
443 15211 53308
|
||||
444 15234 53236
|
||||
445 15266 53193
|
||||
446 15287 53114
|
||||
447 15316 53059
|
||||
448 15336 52978
|
||||
449 15366 52929
|
||||
450 15393 52870
|
||||
451 15429 52843
|
||||
452 15469 52828
|
||||
453 15490 52748
|
||||
454 15523 52712
|
||||
455 15550 52653
|
||||
456 15577 52594
|
||||
457 15604 52536
|
||||
458 15630 52476
|
||||
459 15656 52414
|
||||
460 15682 52353
|
||||
461 15711 52304
|
||||
462 15736 52238
|
||||
463 15765 52188
|
||||
464 15786 52112
|
||||
465 15817 52068
|
||||
466 15839 51996
|
||||
467 15873 51961
|
||||
468 15903 51916
|
||||
469 15935 51873
|
||||
470 15969 51840
|
||||
471 15994 51779
|
||||
472 16022 51726
|
||||
473 16047 51663
|
||||
474 16073 51605
|
||||
475 16099 51546
|
||||
476 16128 51495
|
||||
477 16152 51431
|
||||
478 16176 51367
|
||||
479 16205 51317
|
||||
480 16228 51250
|
||||
481 16255 51194
|
||||
482 16277 51123
|
||||
483 16305 51071
|
||||
484 16328 51005
|
||||
485 16362 50973
|
||||
486 16392 50928
|
||||
487 16426 50894
|
||||
488 16459 50860
|
||||
489 16480 50787
|
||||
490 16510 50743
|
||||
491 16530 50668
|
||||
492 16561 50625
|
||||
493 16585 50562
|
||||
494 16613 50510
|
||||
495 16638 50453
|
||||
496 16663 50393
|
||||
497 16690 50339
|
||||
498 16716 50282
|
||||
499 16740 50222
|
||||
500 16773 50186
|
||||
501 16802 50139
|
||||
502 16836 50107
|
||||
503 16873 50085
|
||||
504 16921 50094
|
||||
505 16989 50163
|
||||
506 17038 50173
|
||||
507 17069 50132
|
||||
508 17110 50121
|
||||
509 17145 50091
|
||||
510 17190 50091
|
||||
511 17219 50044
|
||||
512 17247 49994
|
||||
513 17271 49932
|
||||
514 17298 49878
|
||||
515 17343 49878
|
||||
516 17373 49836
|
||||
517 17417 49831
|
||||
518 17460 49823
|
||||
519 17490 49781
|
||||
520 17518 49731
|
||||
521 17546 49680
|
||||
522 17571 49622
|
||||
523 17600 49577
|
||||
524 17625 49520
|
||||
525 17655 49474
|
||||
526 17679 49414
|
||||
527 17707 49366
|
||||
528 17729 49300
|
||||
529 17758 49254
|
||||
530 17781 49191
|
||||
531 17808 49141
|
||||
532 17829 49071
|
||||
533 17862 49038
|
||||
534 17905 49031
|
||||
535 18028 49241
|
||||
536 18072 49236
|
||||
537 18106 49203
|
||||
538 18135 49157
|
||||
539 18165 49114
|
||||
540 18200 49083
|
||||
541 18223 49022
|
||||
542 18254 48980
|
||||
543 18280 48927
|
||||
544 18307 48876
|
||||
545 18338 48834
|
||||
546 18367 48790
|
||||
547 18411 48783
|
||||
548 18444 48747
|
||||
549 18470 48693
|
||||
550 18503 48660
|
||||
551 18531 48611
|
||||
552 18557 48558
|
||||
553 18584 48508
|
||||
554 18625 48493
|
||||
555 18650 48436
|
||||
556 18677 48388
|
||||
557 18703 48333
|
||||
558 18729 48282
|
||||
559 18756 48231
|
||||
560 18781 48176
|
||||
561 18808 48126
|
||||
562 18834 48074
|
||||
563 18869 48043
|
||||
564 18902 48008
|
||||
565 18930 47960
|
||||
566 18958 47914
|
||||
567 18983 47859
|
||||
568 19016 47824
|
||||
569 19037 47761
|
||||
570 19068 47720
|
||||
571 19090 47660
|
||||
572 19111 47595
|
||||
573 19141 47553
|
||||
574 19164 47494
|
||||
575 19196 47458
|
||||
576 19217 47393
|
||||
577 19249 47358
|
||||
578 19274 47303
|
||||
579 19298 47247
|
||||
580 19324 47195
|
||||
581 19357 47162
|
||||
582 19391 47130
|
||||
583 19427 47103
|
||||
584 19460 47070
|
||||
585 19483 47012
|
||||
586 19511 46967
|
||||
587 19542 46929
|
||||
588 19564 46867
|
||||
589 19597 46833
|
||||
590 19621 46779
|
||||
591 19647 46729
|
||||
592 19670 46672
|
||||
593 19699 46627
|
||||
594 19726 46582
|
||||
595 19753 46532
|
||||
596 19778 46480
|
||||
597 19803 46429
|
||||
598 19830 46381
|
||||
599 19857 46335
|
||||
600 19896 46313
|
||||
601 19925 46271
|
||||
602 19957 46236
|
||||
603 19991 46204
|
||||
604 20019 46159
|
||||
605 20047 46115
|
||||
606 20072 46063
|
||||
607 20098 46015
|
||||
608 20123 45963
|
||||
609 20149 45913
|
||||
610 20176 45867
|
||||
611 20202 45817
|
||||
612 20230 45774
|
||||
613 20253 45719
|
||||
614 20285 45682
|
||||
615 20307 45626
|
||||
616 20338 45589
|
||||
617 20361 45532
|
||||
618 20394 45500
|
||||
619 20423 45459
|
||||
620 20454 45420
|
||||
621 20488 45390
|
||||
622 20510 45333
|
||||
623 20543 45301
|
||||
624 20569 45252
|
||||
625 20594 45201
|
||||
626 20619 45151
|
||||
627 20646 45107
|
||||
628 20675 45066
|
||||
629 20701 45016
|
||||
630 20727 44970
|
||||
631 20752 44919
|
||||
632 20782 44881
|
||||
633 20804 44825
|
||||
634 20837 44791
|
||||
635 20862 44742
|
||||
636 20892 44704
|
||||
637 20931 44683
|
||||
638 20960 44643
|
||||
639 20994 44612
|
||||
640 21022 44570
|
||||
641 21052 44531
|
||||
642 21082 44493
|
||||
643 21107 44443
|
||||
644 21135 44401
|
||||
645 21160 44351
|
||||
646 21185 44302
|
||||
647 21210 44253
|
||||
648 21236 44208
|
||||
649 21262 44161
|
||||
650 21288 44113
|
||||
651 21315 44068
|
||||
652 21343 44027
|
||||
653 21377 43997
|
||||
654 21403 43949
|
||||
655 21440 43926
|
||||
656 21477 43903
|
||||
657 21502 43854
|
||||
658 21533 43819
|
||||
659 21559 43772
|
||||
660 21586 43727
|
||||
661 21611 43680
|
||||
662 21637 43633
|
||||
663 21662 43586
|
||||
664 21688 43539
|
||||
665 21714 43493
|
||||
666 21742 43451
|
||||
667 21771 43413
|
||||
668 21818 43409
|
||||
669 21846 43366
|
||||
670 21888 43352
|
||||
671 21934 43345
|
||||
672 21971 43322
|
||||
673 22019 43320
|
||||
674 22053 43289
|
||||
675 22090 43266
|
||||
676 22141 43269
|
||||
677 22176 43240
|
||||
678 22213 43215
|
||||
679 22239 43171
|
||||
680 22270 43134
|
||||
681 22296 43088
|
||||
682 22321 43041
|
||||
683 22350 43002
|
||||
684 22379 42962
|
||||
685 22419 42944
|
||||
686 22452 42912
|
||||
687 22484 42878
|
||||
688 22511 42834
|
||||
689 22537 42789
|
||||
690 22571 42757
|
||||
691 22598 42714
|
||||
692 22624 42669
|
||||
693 22653 42630
|
||||
694 22680 42586
|
||||
695 22708 42545
|
||||
696 22739 42510
|
||||
697 22761 42457
|
||||
698 22792 42421
|
||||
699 22816 42373
|
||||
700 22845 42333
|
||||
701 22870 42288
|
||||
702 22902 42253
|
||||
703 22942 42234
|
||||
704 22974 42201
|
||||
705 23002 42160
|
||||
706 23033 42124
|
||||
707 23054 42071
|
||||
708 23086 42038
|
||||
709 23115 41999
|
||||
710 23143 41957
|
||||
711 23169 41914
|
||||
712 23195 41868
|
||||
713 23230 41840
|
||||
714 23259 41801
|
||||
715 23287 41760
|
||||
716 23311 41713
|
||||
717 23341 41676
|
||||
718 23372 41641
|
||||
719 23405 41610
|
||||
720 23438 41578
|
||||
721 23483 41566
|
||||
722 23507 41519
|
||||
723 23540 41488
|
||||
724 23566 41444
|
||||
725 23595 41406
|
||||
726 23623 41365
|
||||
727 23648 41320
|
||||
728 23677 41281
|
||||
729 23700 41231
|
||||
730 23728 41192
|
||||
731 23752 41144
|
||||
732 23784 41111
|
||||
733 23807 41063
|
||||
734 23840 41031
|
||||
735 23870 40994
|
||||
736 23908 40972
|
||||
737 23941 40940
|
||||
738 23974 40909
|
||||
739 24006 40875
|
||||
740 24036 40838
|
||||
741 24064 40798
|
||||
742 24092 40759
|
||||
743 24127 40730
|
||||
744 24153 40688
|
||||
745 24179 40644
|
||||
746 24207 40604
|
||||
747 24233 40561
|
||||
748 24261 40522
|
||||
749 24295 40491
|
||||
750 24318 40444
|
||||
751 24349 40410
|
||||
752 24376 40368
|
||||
753 24408 40335
|
||||
754 24442 40306
|
||||
755 24474 40273
|
||||
756 24508 40242
|
||||
757 24548 40222
|
||||
758 24575 40182
|
||||
759 24605 40145
|
||||
760 24632 40104
|
||||
761 24660 40064
|
||||
762 24689 40027
|
||||
763 24714 39982
|
||||
764 24745 39949
|
||||
765 24766 39897
|
||||
766 24797 39863
|
||||
767 24825 39823
|
||||
768 24854 39786
|
||||
769 24880 39744
|
||||
770 24909 39706
|
||||
771 24940 39672
|
||||
772 24970 39635
|
||||
773 25004 39606
|
||||
774 25030 39564
|
||||
775 25056 39522
|
||||
776 25086 39486
|
||||
777 25107 39436
|
||||
778 25139 39403
|
||||
779 25159 39351
|
||||
780 25188 39314
|
||||
781 25214 39272
|
||||
782 25240 39230
|
||||
783 25266 39188
|
||||
784 25288 39141
|
||||
785 25315 39101
|
||||
786 25341 39058
|
||||
787 25367 39016
|
||||
788 25391 38972
|
||||
789 25417 38930
|
||||
790 25448 38895
|
||||
791 25482 38867
|
||||
792 25514 38834
|
||||
793 25542 38795
|
||||
794 25569 38756
|
||||
795 25595 38714
|
||||
796 25618 38669
|
||||
797 25643 38626
|
||||
798 25667 38581
|
||||
799 25695 38543
|
||||
800 25716 38494
|
||||
801 25743 38454
|
||||
802 25770 38415
|
||||
803 25790 38364
|
||||
804 25822 38332
|
||||
805 25843 38284
|
||||
806 25873 38249
|
||||
807 25896 38203
|
||||
808 25925 38167
|
||||
809 25955 38131
|
||||
810 25988 38101
|
||||
811 26028 38080
|
||||
812 26055 38042
|
||||
813 26081 38000
|
||||
814 26108 37961
|
||||
815 26131 37916
|
||||
816 26159 37878
|
||||
817 26188 37841
|
||||
818 26214 37800
|
||||
819 26242 37764
|
||||
820 26272 37728
|
||||
821 26298 37688
|
||||
822 26327 37652
|
||||
823 26359 37619
|
||||
824 26385 37580
|
||||
825 26408 37534
|
||||
826 26444 37507
|
||||
827 26477 37478
|
||||
828 26517 37456
|
||||
829 26539 37411
|
||||
830 26573 37382
|
||||
831 26597 37339
|
||||
832 26623 37298
|
||||
833 26650 37259
|
||||
834 26677 37221
|
||||
835 26704 37182
|
||||
836 26728 37138
|
||||
837 26763 37111
|
||||
838 26791 37073
|
||||
839 26822 37041
|
||||
840 26872 37033
|
||||
841 26924 37029
|
||||
842 26982 37033
|
||||
843 27054 37055
|
||||
844 27097 37038
|
||||
845 27120 36994
|
||||
846 27146 36954
|
||||
847 27180 36925
|
||||
848 27206 36884
|
||||
849 27234 36846
|
||||
850 27260 36807
|
||||
851 27289 36770
|
||||
852 27318 36734
|
||||
853 27347 36698
|
||||
854 27386 36675
|
||||
855 27413 36637
|
||||
856 27439 36596
|
||||
857 27471 36564
|
||||
858 27501 36529
|
||||
859 27535 36500
|
||||
860 27572 36474
|
||||
861 27595 36431
|
||||
862 27627 36398
|
||||
863 27654 36360
|
||||
864 27683 36324
|
||||
865 27711 36287
|
||||
866 27738 36249
|
||||
867 27765 36210
|
||||
868 27794 36175
|
||||
869 27820 36135
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import os
|
||||
import json
|
||||
import yaml
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
|
||||
class EnsembleConfig:
|
||||
_instance: Optional['EnsembleConfig'] = None
|
||||
_config: Dict[str, Any] = {}
|
||||
@@ -35,12 +37,79 @@ class EnsembleConfig:
|
||||
except (KeyError, TypeError):
|
||||
return default
|
||||
|
||||
|
||||
# Singleton accessor
|
||||
def get_config() -> EnsembleConfig:
|
||||
return EnsembleConfig()
|
||||
|
||||
|
||||
# ── Market Thresholds Loader ────────────────────────────────────────────
|
||||
|
||||
_market_thresholds_cache: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
def load_market_thresholds() -> Dict[str, Any]:
|
||||
"""
|
||||
Load market thresholds from JSON config file.
|
||||
Returns the full config dict with 'markets' and 'defaults' keys.
|
||||
Caches after first load for performance.
|
||||
"""
|
||||
global _market_thresholds_cache
|
||||
if _market_thresholds_cache is not None:
|
||||
return _market_thresholds_cache
|
||||
|
||||
config_path = os.path.join(os.path.dirname(__file__), 'market_thresholds.json')
|
||||
try:
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
_market_thresholds_cache = data
|
||||
print(f"✅ Market thresholds loaded: {len(data.get('markets', {}))} markets (v={data.get('_meta', {}).get('version', '?')})")
|
||||
return data
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to load market thresholds: {e} — using built-in defaults")
|
||||
_market_thresholds_cache = {"markets": {}, "defaults": {
|
||||
"calibration": 0.55,
|
||||
"min_conf": 55.0,
|
||||
"min_play_score": 68.0,
|
||||
"min_edge": 0.02,
|
||||
"odds_band_min_sample": 0.0,
|
||||
"odds_band_min_edge": 0.0,
|
||||
}}
|
||||
return _market_thresholds_cache
|
||||
|
||||
|
||||
def build_threshold_dict(field: str) -> Dict[str, float]:
|
||||
"""
|
||||
Build a flat {market: value} dict for a specific threshold field.
|
||||
|
||||
Usage:
|
||||
calibration_map = build_threshold_dict("calibration")
|
||||
# → {"MS": 0.62, "DC": 0.82, ...}
|
||||
"""
|
||||
data = load_market_thresholds()
|
||||
markets = data.get("markets", {})
|
||||
result: Dict[str, float] = {}
|
||||
for market, cfg in markets.items():
|
||||
if field in cfg:
|
||||
result[market] = float(cfg[field])
|
||||
return result
|
||||
|
||||
|
||||
def get_threshold_default(field: str) -> float:
|
||||
"""Get the default fallback value for a threshold field."""
|
||||
data = load_market_thresholds()
|
||||
defaults = data.get("defaults", {})
|
||||
return float(defaults.get(field, 0.0))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test
|
||||
cfg = get_config()
|
||||
print(f"Weights: {cfg.get('engine_weights')}")
|
||||
print(f"Team Weight: {cfg.get('engine_weights.team')}")
|
||||
print()
|
||||
print("--- Market Thresholds ---")
|
||||
for field in ["calibration", "min_conf", "min_play_score", "min_edge"]:
|
||||
d = build_threshold_dict(field)
|
||||
print(f"{field}: {d}")
|
||||
print(f"Default calibration: {get_threshold_default('calibration')}")
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
model_ensemble:
|
||||
xgb_weight: 0.50
|
||||
lgb_weight: 0.50
|
||||
temperature: 1.5
|
||||
default_ms_odds:
|
||||
home: 2.65
|
||||
draw: 3.20
|
||||
away: 2.65
|
||||
elo_staleness_days: 14
|
||||
odds_staleness_hours: 48
|
||||
|
||||
engine_weights:
|
||||
team: 0.30
|
||||
player: 0.25
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"_meta": {
|
||||
"version": "v34",
|
||||
"description": "Market-specific thresholds for the betting engine pipeline — V34 odds-aware gate fix",
|
||||
"rule": "max_reachable (100 × calibration) MUST be > min_conf + 8",
|
||||
"updated_at": "2026-05-10",
|
||||
"changelog": "V34: Reduced min_edge to realistic levels for odds-aware V25 model. Model output ≈ market-implied, so large EV edges are mathematically impossible."
|
||||
},
|
||||
"markets": {
|
||||
"MS": {
|
||||
"calibration": 0.62,
|
||||
"min_conf": 20.0,
|
||||
"min_play_score": 28.0,
|
||||
"min_edge": 0.005,
|
||||
"odds_band_min_sample": 8.0,
|
||||
"odds_band_min_edge": 0.005
|
||||
},
|
||||
"DC": {
|
||||
"calibration": 0.82,
|
||||
"min_conf": 40.0,
|
||||
"min_play_score": 50.0,
|
||||
"min_edge": 0.003,
|
||||
"odds_band_min_sample": 8.0,
|
||||
"odds_band_min_edge": 0.005
|
||||
},
|
||||
"OU15": {
|
||||
"calibration": 0.84,
|
||||
"min_conf": 45.0,
|
||||
"min_play_score": 50.0,
|
||||
"min_edge": 0.003,
|
||||
"odds_band_min_sample": 8.0,
|
||||
"odds_band_min_edge": 0.005
|
||||
},
|
||||
"OU25": {
|
||||
"calibration": 0.68,
|
||||
"min_conf": 30.0,
|
||||
"min_play_score": 40.0,
|
||||
"min_edge": 0.005,
|
||||
"odds_band_min_sample": 8.0,
|
||||
"odds_band_min_edge": 0.005
|
||||
},
|
||||
"OU35": {
|
||||
"calibration": 0.60,
|
||||
"min_conf": 20.0,
|
||||
"min_play_score": 30.0,
|
||||
"min_edge": 0.008,
|
||||
"odds_band_min_sample": 8.0,
|
||||
"odds_band_min_edge": 0.008
|
||||
},
|
||||
"BTTS": {
|
||||
"calibration": 0.65,
|
||||
"min_conf": 30.0,
|
||||
"min_play_score": 40.0,
|
||||
"min_edge": 0.005,
|
||||
"odds_band_min_sample": 8.0,
|
||||
"odds_band_min_edge": 0.005
|
||||
},
|
||||
"HT": {
|
||||
"calibration": 0.58,
|
||||
"min_conf": 20.0,
|
||||
"min_play_score": 28.0,
|
||||
"min_edge": 0.01,
|
||||
"odds_band_min_sample": 8.0,
|
||||
"odds_band_min_edge": 0.008
|
||||
},
|
||||
"HT_OU05": {
|
||||
"calibration": 0.68,
|
||||
"min_conf": 35.0,
|
||||
"min_play_score": 42.0,
|
||||
"min_edge": 0.005,
|
||||
"odds_band_min_sample": 8.0,
|
||||
"odds_band_min_edge": 0.005
|
||||
},
|
||||
"HT_OU15": {
|
||||
"calibration": 0.60,
|
||||
"min_conf": 25.0,
|
||||
"min_play_score": 32.0,
|
||||
"min_edge": 0.008,
|
||||
"odds_band_min_sample": 8.0,
|
||||
"odds_band_min_edge": 0.008
|
||||
},
|
||||
"OE": {
|
||||
"calibration": 0.62,
|
||||
"min_conf": 35.0,
|
||||
"min_play_score": 32.0,
|
||||
"min_edge": 0.005
|
||||
},
|
||||
"CARDS": {
|
||||
"calibration": 0.58,
|
||||
"min_conf": 30.0,
|
||||
"min_play_score": 35.0,
|
||||
"min_edge": 0.008
|
||||
},
|
||||
"HCAP": {
|
||||
"calibration": 0.56,
|
||||
"min_conf": 25.0,
|
||||
"min_play_score": 30.0,
|
||||
"min_edge": 0.015
|
||||
},
|
||||
"HTFT": {
|
||||
"calibration": 0.45,
|
||||
"min_conf": 10.0,
|
||||
"min_play_score": 18.0,
|
||||
"min_edge": 0.02
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
"calibration": 0.55,
|
||||
"min_conf": 55.0,
|
||||
"min_play_score": 60.0,
|
||||
"min_edge": 0.008,
|
||||
"odds_band_min_sample": 0.0,
|
||||
"odds_band_min_edge": 0.0
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class CalculationContext:
|
||||
is_surprise: bool = False
|
||||
|
||||
# XGBoost Predictions (New)
|
||||
xgboost_preds: dict[str, dict[str, Any]] = field(default_factory=dict)
|
||||
xgboost_preds: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
class BaseCalculator:
|
||||
|
||||
@@ -28,7 +28,7 @@ class RecommendationResult:
|
||||
|
||||
|
||||
class BetRecommender(BaseCalculator):
|
||||
def calculate(self,
|
||||
def calculate(self, # type: ignore[override]
|
||||
ctx: CalculationContext,
|
||||
ms_res: MatchResultPrediction,
|
||||
ou_res: OverUnderPrediction,
|
||||
|
||||
@@ -36,7 +36,7 @@ class ExpertResult:
|
||||
|
||||
|
||||
class ExpertRecommender(BaseCalculator):
|
||||
def calculate(self,
|
||||
def calculate(self, # type: ignore[override]
|
||||
ctx: CalculationContext,
|
||||
ms_res: MatchResultPrediction,
|
||||
ou_res: OverUnderPrediction,
|
||||
|
||||
@@ -31,7 +31,7 @@ class HalfTimeCalculator(BaseCalculator):
|
||||
return 1.0 if k == 0 else 0.0
|
||||
return (lam ** k) * math.exp(-lam) / math.factorial(k)
|
||||
|
||||
def calculate(self, ctx: CalculationContext) -> HalfTimePrediction:
|
||||
def calculate(self, ctx: CalculationContext) -> HalfTimePrediction: # type: ignore[override]
|
||||
team_pred = ctx.team_pred
|
||||
odds_pred = ctx.odds_pred
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ class MatchResultCalculator(BaseCalculator):
|
||||
def _get_engine_winner(self, home_prob: float, draw_prob: float, away_prob: float) -> str:
|
||||
"""Determine which outcome an engine favors."""
|
||||
probs = {"1": home_prob, "X": draw_prob, "2": away_prob}
|
||||
return max(probs, key=probs.get)
|
||||
return max(probs, key=probs.__getitem__)
|
||||
|
||||
def calculate(self, ctx: CalculationContext) -> MatchResultPrediction:
|
||||
def calculate(self, ctx: CalculationContext) -> MatchResultPrediction: # type: ignore[override]
|
||||
# Weights
|
||||
w_team = ctx.weights["team"]
|
||||
w_player = ctx.weights["player"]
|
||||
|
||||
@@ -28,7 +28,7 @@ class OtherMarketsPrediction:
|
||||
|
||||
|
||||
class OtherMarketsCalculator(BaseCalculator):
|
||||
def calculate(
|
||||
def calculate( # type: ignore[override]
|
||||
self,
|
||||
ctx: CalculationContext,
|
||||
ms_result: MatchResultPrediction,
|
||||
|
||||
@@ -55,7 +55,7 @@ class OverUnderCalculator(BaseCalculator):
|
||||
|
||||
return over_15, over_25, over_35, btts_yes
|
||||
|
||||
def calculate(self, ctx: CalculationContext) -> OverUnderPrediction:
|
||||
def calculate(self, ctx: CalculationContext) -> OverUnderPrediction: # type: ignore[override]
|
||||
odds_pred = ctx.odds_pred
|
||||
referee_mods = ctx.referee_mods
|
||||
|
||||
|
||||
@@ -67,12 +67,14 @@ class RiskAssessor(BaseCalculator):
|
||||
|
||||
if sport_key == "basketball":
|
||||
if is_top_league:
|
||||
return float(
|
||||
self.config.get("risk.surprise_threshold_basketball_top", self.config.get("risk.surprise_threshold_basketball", 0.30)),
|
||||
)
|
||||
return float(
|
||||
self.config.get("risk.surprise_threshold_basketball_non_top", 0.34),
|
||||
)
|
||||
top_val = self.config.get("risk.surprise_threshold_basketball_top")
|
||||
if top_val is not None:
|
||||
return float(top_val)
|
||||
base_val = self.config.get("risk.surprise_threshold_basketball")
|
||||
return float(base_val) if base_val is not None else 0.30
|
||||
|
||||
non_top_val = self.config.get("risk.surprise_threshold_basketball_non_top")
|
||||
return float(non_top_val) if non_top_val is not None else 0.34
|
||||
|
||||
if top_label not in ("1/2", "2/1"):
|
||||
return base_threshold
|
||||
@@ -81,27 +83,30 @@ class RiskAssessor(BaseCalculator):
|
||||
favorite_side, gap = self._favorite_profile_from_odds(ctx.odds_data)
|
||||
|
||||
if is_top_league:
|
||||
favorite_winner_threshold = float(
|
||||
self.config.get(
|
||||
"risk.surprise_threshold_favorite_reversal_top",
|
||||
self.config.get("risk.surprise_threshold_favorite_reversal", 0.26),
|
||||
),
|
||||
)
|
||||
underdog_winner_threshold = float(
|
||||
self.config.get(
|
||||
"risk.surprise_threshold_underdog_reversal_top",
|
||||
self.config.get("risk.surprise_threshold_underdog_reversal", 0.20),
|
||||
),
|
||||
)
|
||||
top_fav = self.config.get("risk.surprise_threshold_favorite_reversal_top")
|
||||
if top_fav is not None:
|
||||
favorite_winner_threshold = float(top_fav)
|
||||
else:
|
||||
favorite_winner_threshold = float(
|
||||
self.config.get("risk.surprise_threshold_favorite_reversal_non_top", 0.30),
|
||||
)
|
||||
underdog_winner_threshold = float(
|
||||
self.config.get("risk.surprise_threshold_underdog_reversal_non_top", 0.24),
|
||||
)
|
||||
gap_medium = float(self.config.get("risk.htft_reversal_gap_medium", 0.50))
|
||||
gap_strong = float(self.config.get("risk.htft_reversal_gap_strong", 1.00))
|
||||
base_fav = self.config.get("risk.surprise_threshold_favorite_reversal")
|
||||
favorite_winner_threshold = float(base_fav) if base_fav is not None else 0.26
|
||||
|
||||
top_ud = self.config.get("risk.surprise_threshold_underdog_reversal_top")
|
||||
if top_ud is not None:
|
||||
underdog_winner_threshold = float(top_ud)
|
||||
else:
|
||||
base_ud = self.config.get("risk.surprise_threshold_underdog_reversal")
|
||||
underdog_winner_threshold = float(base_ud) if base_ud is not None else 0.20
|
||||
else:
|
||||
nt_fav = self.config.get("risk.surprise_threshold_favorite_reversal_non_top")
|
||||
favorite_winner_threshold = float(nt_fav) if nt_fav is not None else 0.30
|
||||
nt_ud = self.config.get("risk.surprise_threshold_underdog_reversal_non_top")
|
||||
underdog_winner_threshold = float(nt_ud) if nt_ud is not None else 0.24
|
||||
|
||||
gm = self.config.get("risk.htft_reversal_gap_medium")
|
||||
gap_medium = float(gm) if gm is not None else 0.50
|
||||
|
||||
gs = self.config.get("risk.htft_reversal_gap_strong")
|
||||
gap_strong = float(gs) if gs is not None else 1.00
|
||||
|
||||
if favorite_side in ("H", "A"):
|
||||
threshold = (
|
||||
@@ -117,7 +122,7 @@ class RiskAssessor(BaseCalculator):
|
||||
|
||||
return base_threshold
|
||||
|
||||
def calculate(self, ctx: CalculationContext, ms_result=None) -> RiskAnalysis:
|
||||
def calculate(self, ctx: CalculationContext, ms_result: Any = None) -> RiskAnalysis: # type: ignore[override]
|
||||
"""
|
||||
Wrapper for assess_risk to match BaseCalculator interface but with extra arg.
|
||||
"""
|
||||
@@ -173,9 +178,15 @@ class RiskAssessor(BaseCalculator):
|
||||
|
||||
threshold = self._dynamic_reversal_threshold(ctx, top_label)
|
||||
if getattr(ctx, "is_top_league", False):
|
||||
min_gap = float(self.config.get("risk.surprise_min_top_gap_top", self.config.get("risk.surprise_min_top_gap", 0.02)))
|
||||
top_gap_val = self.config.get("risk.surprise_min_top_gap_top")
|
||||
if top_gap_val is not None:
|
||||
min_gap = float(top_gap_val)
|
||||
else:
|
||||
min_gap = float(self.config.get("risk.surprise_min_top_gap_non_top", 0.03))
|
||||
base_gap_val = self.config.get("risk.surprise_min_top_gap")
|
||||
min_gap = float(base_gap_val) if base_gap_val is not None else 0.02
|
||||
else:
|
||||
non_top_gap_val = self.config.get("risk.surprise_min_top_gap_non_top")
|
||||
min_gap = float(non_top_gap_val) if non_top_gap_val is not None else 0.03
|
||||
|
||||
# Trigger surprise only when reversal class is:
|
||||
# - top HT/FT outcome
|
||||
|
||||
@@ -3,7 +3,7 @@ import pickle
|
||||
import pandas as pd
|
||||
import xgboost as xgb
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Dict, Tuple
|
||||
from typing import List, Dict, Tuple, Optional
|
||||
import math
|
||||
from .base_calculator import BaseCalculator, CalculationContext
|
||||
from .confidence import calc_confidence_3way, calc_confidence_dc
|
||||
@@ -16,7 +16,7 @@ class ScorePrediction:
|
||||
ft_scores_top5: List[Dict]
|
||||
|
||||
# Reconciled MS/DC predictions (can be updated here)
|
||||
reconciled_ms: MatchResultPrediction = None
|
||||
reconciled_ms: Optional[MatchResultPrediction] = None
|
||||
|
||||
class ScoreCalculator(BaseCalculator):
|
||||
|
||||
@@ -57,7 +57,8 @@ class ScoreCalculator(BaseCalculator):
|
||||
return 1.0 if k == 0 else 0.0
|
||||
return (lam ** k) * math.exp(-lam) / math.factorial(k)
|
||||
|
||||
def calculate(self, ctx: CalculationContext, ms_result: MatchResultPrediction) -> ScorePrediction:
|
||||
def calculate(self, ctx: CalculationContext, ms_result: MatchResultPrediction) -> ScorePrediction: # type: ignore[override]
|
||||
predicted_ht = None
|
||||
# Default Lambdas (fallback)
|
||||
lambda_home = max(0.5, ctx.home_xg)
|
||||
lambda_away = max(0.5, ctx.away_xg)
|
||||
@@ -199,7 +200,7 @@ class ScoreCalculator(BaseCalculator):
|
||||
predicted_ft = top_overall_score
|
||||
|
||||
# If we didn't calculate HT via ML (exception case), do it now
|
||||
if 'predicted_ht' not in locals():
|
||||
if predicted_ht is None:
|
||||
ft_to_ht = self.config.get("half_time.ft_to_ht_ratio", 0.42)
|
||||
ht_h = round(lambda_home * ft_to_ht)
|
||||
ht_a = round(lambda_away * ft_to_ht)
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
# ai-engine/core/engines/__init__.py
|
||||
"""
|
||||
V20 Ensemble Prediction Engines
|
||||
Prediction Engines
|
||||
"""
|
||||
|
||||
from .team_predictor import TeamPredictorEngine, get_team_predictor
|
||||
from .player_predictor import PlayerPredictorEngine, get_player_predictor
|
||||
from .odds_predictor import OddsPredictorEngine, get_odds_predictor
|
||||
from .referee_predictor import RefereePredictorEngine, get_referee_predictor
|
||||
|
||||
__all__ = [
|
||||
"TeamPredictorEngine", "get_team_predictor",
|
||||
"PlayerPredictorEngine", "get_player_predictor",
|
||||
"OddsPredictorEngine", "get_odds_predictor",
|
||||
"RefereePredictorEngine", "get_referee_predictor"
|
||||
]
|
||||
|
||||
@@ -1,237 +0,0 @@
|
||||
"""
|
||||
Odds Predictor Engine - V20 Ensemble Component
|
||||
Uses market odds and Poisson mathematics for predictions.
|
||||
|
||||
Weight: 30% in ensemble
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from typing import Dict, Optional
|
||||
from dataclasses import dataclass
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from features.poisson_engine import get_poisson_engine
|
||||
from features.value_calculator import get_value_calculator
|
||||
|
||||
|
||||
@dataclass
|
||||
class OddsPrediction:
|
||||
"""Odds engine prediction output."""
|
||||
# Market-implied probabilities
|
||||
market_home_prob: float = 0.33
|
||||
market_draw_prob: float = 0.33
|
||||
market_away_prob: float = 0.33
|
||||
|
||||
# Poisson xG
|
||||
poisson_home_xg: float = 1.3
|
||||
poisson_away_xg: float = 1.1
|
||||
|
||||
# Over/Under probabilities
|
||||
over_15_prob: float = 0.75
|
||||
over_25_prob: float = 0.55
|
||||
over_35_prob: float = 0.30
|
||||
|
||||
# BTTS
|
||||
btts_yes_prob: float = 0.50
|
||||
|
||||
# Most likely scores
|
||||
most_likely_score: str = "1-1"
|
||||
second_likely_score: str = "1-0"
|
||||
third_likely_score: str = "2-1"
|
||||
|
||||
# Value bet opportunities
|
||||
value_bets: list = None
|
||||
|
||||
confidence: float = 0.0
|
||||
|
||||
def __post_init__(self):
|
||||
if self.value_bets is None:
|
||||
self.value_bets = []
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"market_home_prob": round(self.market_home_prob * 100, 1),
|
||||
"market_draw_prob": round(self.market_draw_prob * 100, 1),
|
||||
"market_away_prob": round(self.market_away_prob * 100, 1),
|
||||
"poisson_home_xg": round(self.poisson_home_xg, 2),
|
||||
"poisson_away_xg": round(self.poisson_away_xg, 2),
|
||||
"over_15_prob": round(self.over_15_prob * 100, 1),
|
||||
"over_25_prob": round(self.over_25_prob * 100, 1),
|
||||
"over_35_prob": round(self.over_35_prob * 100, 1),
|
||||
"btts_yes_prob": round(self.btts_yes_prob * 100, 1),
|
||||
"most_likely_score": self.most_likely_score,
|
||||
"second_likely_score": self.second_likely_score,
|
||||
"third_likely_score": self.third_likely_score,
|
||||
"value_bets": self.value_bets,
|
||||
"confidence": round(self.confidence, 1)
|
||||
}
|
||||
|
||||
|
||||
class OddsPredictorEngine:
|
||||
"""
|
||||
Odds-based prediction engine.
|
||||
|
||||
Uses:
|
||||
- Market odds to extract implied probabilities
|
||||
- Poisson distribution for mathematical xG
|
||||
- Value calculator for EV+ opportunities
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.poisson_engine = get_poisson_engine()
|
||||
try:
|
||||
self.value_calc = get_value_calculator()
|
||||
except Exception:
|
||||
self.value_calc = None
|
||||
self.default_ms_h = 2.65
|
||||
self.default_ms_d = 3.20
|
||||
self.default_ms_a = 2.65
|
||||
print("✅ OddsPredictorEngine initialized")
|
||||
|
||||
def _odds_to_prob(self, odds: float) -> float:
|
||||
"""Convert decimal odds to probability."""
|
||||
try:
|
||||
odds = float(odds)
|
||||
except (TypeError, ValueError):
|
||||
return 0.0
|
||||
if odds <= 1.0:
|
||||
return 0.0
|
||||
return 1.0 / odds
|
||||
|
||||
def predict(self,
|
||||
odds_data: Dict[str, float],
|
||||
home_goals_avg: float = 1.5,
|
||||
home_conceded_avg: float = 1.2,
|
||||
away_goals_avg: float = 1.2,
|
||||
away_conceded_avg: float = 1.4) -> OddsPrediction:
|
||||
"""
|
||||
Generate odds-based prediction.
|
||||
|
||||
Args:
|
||||
odds_data: Dict with keys like 'ms_h', 'ms_d', 'ms_a', 'ou25_o', 'btts_y'
|
||||
home_goals_avg: Home team's average goals scored
|
||||
home_conceded_avg: Home team's average goals conceded
|
||||
away_goals_avg: Away team's average goals scored
|
||||
away_conceded_avg: Away team's average goals conceded
|
||||
|
||||
Returns:
|
||||
OddsPrediction with market and Poisson analysis
|
||||
"""
|
||||
|
||||
# 1. Extract market probabilities from odds
|
||||
ms_h = odds_data.get("ms_h", self.default_ms_h)
|
||||
ms_d = odds_data.get("ms_d", self.default_ms_d)
|
||||
ms_a = odds_data.get("ms_a", self.default_ms_a)
|
||||
|
||||
# Remove vig to get fair probabilities
|
||||
raw_probs = [
|
||||
self._odds_to_prob(ms_h),
|
||||
self._odds_to_prob(ms_d),
|
||||
self._odds_to_prob(ms_a)
|
||||
]
|
||||
total = sum(raw_probs) or 1
|
||||
|
||||
market_home = raw_probs[0] / total
|
||||
market_draw = raw_probs[1] / total
|
||||
market_away = raw_probs[2] / total
|
||||
|
||||
# 2. Poisson prediction
|
||||
poisson_pred = self.poisson_engine.predict(
|
||||
home_goals_avg, home_conceded_avg,
|
||||
away_goals_avg, away_conceded_avg
|
||||
)
|
||||
|
||||
# 3. Get most likely scores
|
||||
likely_scores = poisson_pred.most_likely_scores[:3] if poisson_pred.most_likely_scores else []
|
||||
score_1 = likely_scores[0]["score"] if len(likely_scores) > 0 else "1-1"
|
||||
score_2 = likely_scores[1]["score"] if len(likely_scores) > 1 else "1-0"
|
||||
score_3 = likely_scores[2]["score"] if len(likely_scores) > 2 else "2-1"
|
||||
|
||||
# 4. Value bet detection
|
||||
value_bets = []
|
||||
|
||||
# Check if our Poisson model disagrees with market significantly
|
||||
if abs(poisson_pred.home_win_prob - market_home) > 0.10:
|
||||
if poisson_pred.home_win_prob > market_home:
|
||||
value_bets.append({
|
||||
"market": "MS 1",
|
||||
"edge": round((poisson_pred.home_win_prob - market_home) * 100, 1),
|
||||
"confidence": "medium"
|
||||
})
|
||||
else:
|
||||
value_bets.append({
|
||||
"market": "MS 2",
|
||||
"edge": round((poisson_pred.away_win_prob - market_away) * 100, 1),
|
||||
"confidence": "medium"
|
||||
})
|
||||
|
||||
# O/U value check
|
||||
ou25_o = odds_data.get("ou25_o", 1.9)
|
||||
market_over25 = self._odds_to_prob(ou25_o)
|
||||
if abs(poisson_pred.over_25_prob - market_over25) > 0.08:
|
||||
pick = "2.5 Üst" if poisson_pred.over_25_prob > market_over25 else "2.5 Alt"
|
||||
edge = abs(poisson_pred.over_25_prob - market_over25) * 100
|
||||
value_bets.append({
|
||||
"market": pick,
|
||||
"edge": round(edge, 1),
|
||||
"confidence": "high" if edge > 10 else "medium"
|
||||
})
|
||||
|
||||
# Calculate confidence
|
||||
# Higher when market and Poisson agree
|
||||
agreement = 1.0 - abs(poisson_pred.home_win_prob - market_home)
|
||||
confidence = 50.0 + (agreement * 40) + (len(value_bets) * 5)
|
||||
|
||||
return OddsPrediction(
|
||||
market_home_prob=market_home,
|
||||
market_draw_prob=market_draw,
|
||||
market_away_prob=market_away,
|
||||
poisson_home_xg=poisson_pred.home_xg,
|
||||
poisson_away_xg=poisson_pred.away_xg,
|
||||
over_15_prob=poisson_pred.over_15_prob,
|
||||
over_25_prob=poisson_pred.over_25_prob,
|
||||
over_35_prob=poisson_pred.over_35_prob,
|
||||
btts_yes_prob=poisson_pred.btts_yes_prob,
|
||||
most_likely_score=score_1,
|
||||
second_likely_score=score_2,
|
||||
third_likely_score=score_3,
|
||||
value_bets=value_bets,
|
||||
confidence=min(99.9, confidence)
|
||||
)
|
||||
|
||||
|
||||
# Singleton
|
||||
_engine: Optional[OddsPredictorEngine] = None
|
||||
|
||||
|
||||
def get_odds_predictor() -> OddsPredictorEngine:
|
||||
global _engine
|
||||
if _engine is None:
|
||||
_engine = OddsPredictorEngine()
|
||||
return _engine
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
engine = get_odds_predictor()
|
||||
|
||||
print("\n🧪 Odds Predictor Engine Test")
|
||||
print("=" * 50)
|
||||
|
||||
pred = engine.predict(
|
||||
odds_data={
|
||||
"ms_h": 1.85,
|
||||
"ms_d": 3.40,
|
||||
"ms_a": 4.20,
|
||||
"ou25_o": 1.90
|
||||
},
|
||||
home_goals_avg=1.8,
|
||||
home_conceded_avg=1.0,
|
||||
away_goals_avg=1.2,
|
||||
away_conceded_avg=1.5
|
||||
)
|
||||
|
||||
print(f"\n📊 Prediction:")
|
||||
for k, v in pred.to_dict().items():
|
||||
print(f" {k}: {v}")
|
||||
@@ -18,34 +18,36 @@ from features.sidelined_analyzer import get_sidelined_analyzer
|
||||
|
||||
@dataclass
|
||||
class PlayerPrediction:
|
||||
"""Player engine prediction output."""
|
||||
home_squad_quality: float = 50.0 # 0-100
|
||||
away_squad_quality: float = 50.0
|
||||
squad_diff: float = 0.0 # -100 to +100
|
||||
"""Player engine prediction output.
|
||||
|
||||
IMPORTANT: squad_quality uses the SAME composite formula as
|
||||
extract_training_data.py so that inference values match the
|
||||
distribution the model was trained on (~3-36 range).
|
||||
"""
|
||||
home_squad_quality: float = 12.0
|
||||
away_squad_quality: float = 12.0
|
||||
squad_diff: float = 0.0
|
||||
home_key_players: int = 0
|
||||
away_key_players: int = 0
|
||||
home_missing_impact: float = 0.0 # 0-1, how much weaker due to missing players
|
||||
home_missing_impact: float = 0.0
|
||||
away_missing_impact: float = 0.0
|
||||
home_goals_form: int = 0 # Goals in last 5 matches
|
||||
home_goals_form: int = 0
|
||||
away_goals_form: int = 0
|
||||
home_lineup_goals_per90: float = 0.0
|
||||
away_lineup_goals_per90: float = 0.0
|
||||
home_lineup_assists_per90: float = 0.0
|
||||
away_lineup_assists_per90: float = 0.0
|
||||
home_squad_continuity: float = 0.5
|
||||
away_squad_continuity: float = 0.5
|
||||
home_top_scorer_form: int = 0
|
||||
away_top_scorer_form: int = 0
|
||||
home_avg_player_exp: float = 0.0
|
||||
away_avg_player_exp: float = 0.0
|
||||
home_goals_diversity: float = 0.0
|
||||
away_goals_diversity: float = 0.0
|
||||
lineup_available: bool = False
|
||||
confidence: float = 0.0
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"home_squad_quality": round(self.home_squad_quality, 1),
|
||||
"away_squad_quality": round(self.away_squad_quality, 1),
|
||||
"squad_diff": round(self.squad_diff, 1),
|
||||
"home_key_players": self.home_key_players,
|
||||
"away_key_players": self.away_key_players,
|
||||
"home_missing_impact": round(self.home_missing_impact, 2),
|
||||
"away_missing_impact": round(self.away_missing_impact, 2),
|
||||
"home_goals_form": self.home_goals_form,
|
||||
"away_goals_form": self.away_goals_form,
|
||||
"lineup_available": self.lineup_available,
|
||||
"confidence": round(self.confidence, 1)
|
||||
}
|
||||
|
||||
|
||||
class PlayerPredictorEngine:
|
||||
"""
|
||||
@@ -67,9 +69,9 @@ class PlayerPredictorEngine:
|
||||
match_id: str,
|
||||
home_team_id: str,
|
||||
away_team_id: str,
|
||||
home_lineup: List[str] = None,
|
||||
away_lineup: List[str] = None,
|
||||
sidelined_data: Dict = None) -> PlayerPrediction:
|
||||
home_lineup: Optional[List[str]] = None,
|
||||
away_lineup: Optional[List[str]] = None,
|
||||
sidelined_data: Optional[Dict] = None) -> PlayerPrediction:
|
||||
"""
|
||||
Generate player-based prediction.
|
||||
|
||||
@@ -85,8 +87,9 @@ class PlayerPredictorEngine:
|
||||
"""
|
||||
|
||||
# Get squad features
|
||||
home_analysis = None
|
||||
away_analysis = None
|
||||
if home_lineup and away_lineup:
|
||||
# Use provided lineups (for live matches)
|
||||
home_analysis = self.squad_engine.analyze_squad_from_list(
|
||||
home_lineup, home_team_id
|
||||
)
|
||||
@@ -94,19 +97,19 @@ class PlayerPredictorEngine:
|
||||
away_lineup, away_team_id
|
||||
)
|
||||
lineup_available = True
|
||||
# Build features dict from analysis objects
|
||||
features = {
|
||||
"home_starting_11": home_analysis.starting_count or 11,
|
||||
"home_goals_last_5": home_analysis.total_goals_last_5,
|
||||
"home_assists_last_5": home_analysis.total_assists_last_5,
|
||||
"home_key_players": home_analysis.key_players_count,
|
||||
"home_forwards": home_analysis.forward_count or 2,
|
||||
"away_starting_11": away_analysis.starting_count or 11,
|
||||
"away_goals_last_5": away_analysis.total_goals_last_5,
|
||||
"away_assists_last_5": away_analysis.total_assists_last_5,
|
||||
"away_key_players": away_analysis.key_players_count,
|
||||
"away_forwards": away_analysis.forward_count or 2,
|
||||
}
|
||||
elif match_id:
|
||||
# Try to get from database
|
||||
try:
|
||||
features = self.squad_engine.get_features(
|
||||
match_id, home_team_id, away_team_id
|
||||
@@ -126,31 +129,27 @@ class PlayerPredictorEngine:
|
||||
)
|
||||
lineup_available = False
|
||||
|
||||
# Extract features
|
||||
home_goals = features.get("home_goals_last_5", 0)
|
||||
away_goals = features.get("away_goals_last_5", 0)
|
||||
home_key = features.get("home_key_players", 0)
|
||||
away_key = features.get("away_key_players", 0)
|
||||
home_goals = int(features.get("home_goals_last_5", 0))
|
||||
away_goals = int(features.get("away_goals_last_5", 0))
|
||||
home_key = int(features.get("home_key_players", 0))
|
||||
away_key = int(features.get("away_key_players", 0))
|
||||
home_starting = features.get("home_starting_11", 11)
|
||||
away_starting = features.get("away_starting_11", 11)
|
||||
home_fwd = features.get("home_forwards", 2)
|
||||
away_fwd = features.get("away_forwards", 2)
|
||||
|
||||
# Calculate squad quality (0-100)
|
||||
# Based on: goals scored, key players, assists
|
||||
home_quality = min(100, 50 + (home_goals * 3) + (home_key * 5) +
|
||||
features.get("home_assists_last_5", 0) * 2)
|
||||
away_quality = min(100, 50 + (away_goals * 3) + (away_key * 5) +
|
||||
features.get("away_assists_last_5", 0) * 2)
|
||||
|
||||
# Squad difference
|
||||
# Squad quality — matches V25 extract_training_data.py:579
|
||||
home_quality = home_starting * 0.3 + home_key * 3.0 + home_fwd * 1.5
|
||||
away_quality = away_starting * 0.3 + away_key * 3.0 + away_fwd * 1.5
|
||||
squad_diff = home_quality - away_quality
|
||||
|
||||
# Missing player impact
|
||||
# Priority: sidelined data (position-weighted) > lineup count (basic)
|
||||
if sidelined_data:
|
||||
home_impact, away_impact = self.sidelined_analyzer.analyze_match(sidelined_data)
|
||||
home_missing = home_impact.impact_score
|
||||
away_missing = away_impact.impact_score
|
||||
home_missing = min(1.0, max(0.0, home_impact.impact_score))
|
||||
away_missing = min(1.0, max(0.0, away_impact.impact_score))
|
||||
sidelined_available = True
|
||||
else:
|
||||
# Fallback: basic lineup count method
|
||||
expected_xi = 11
|
||||
actual_home_xi = features.get("home_starting_11", 11)
|
||||
actual_away_xi = features.get("away_starting_11", 11)
|
||||
@@ -158,7 +157,13 @@ class PlayerPredictorEngine:
|
||||
away_missing = (expected_xi - actual_away_xi) / expected_xi if actual_away_xi < expected_xi else 0
|
||||
sidelined_available = False
|
||||
|
||||
# Confidence: more data sources = higher confidence
|
||||
# Player-level features (matches extract_training_data.py:594-650)
|
||||
player_feats = self._compute_player_level_features(
|
||||
home_lineup or [], away_lineup or [],
|
||||
home_team_id, away_team_id,
|
||||
home_analysis, away_analysis,
|
||||
)
|
||||
|
||||
confidence = 70.0 if lineup_available else 35.0
|
||||
if home_goals + away_goals > 10:
|
||||
confidence += 15
|
||||
@@ -177,17 +182,147 @@ class PlayerPredictorEngine:
|
||||
away_missing_impact=away_missing,
|
||||
home_goals_form=home_goals,
|
||||
away_goals_form=away_goals,
|
||||
home_lineup_goals_per90=player_feats['home_lineup_goals_per90'],
|
||||
away_lineup_goals_per90=player_feats['away_lineup_goals_per90'],
|
||||
home_lineup_assists_per90=player_feats['home_lineup_assists_per90'],
|
||||
away_lineup_assists_per90=player_feats['away_lineup_assists_per90'],
|
||||
home_squad_continuity=player_feats['home_squad_continuity'],
|
||||
away_squad_continuity=player_feats['away_squad_continuity'],
|
||||
home_top_scorer_form=player_feats['home_top_scorer_form'],
|
||||
away_top_scorer_form=player_feats['away_top_scorer_form'],
|
||||
home_avg_player_exp=player_feats['home_avg_player_exp'],
|
||||
away_avg_player_exp=player_feats['away_avg_player_exp'],
|
||||
home_goals_diversity=player_feats['home_goals_diversity'],
|
||||
away_goals_diversity=player_feats['away_goals_diversity'],
|
||||
lineup_available=lineup_available,
|
||||
confidence=max(5.0, confidence)
|
||||
)
|
||||
|
||||
def _compute_player_level_features(
|
||||
self,
|
||||
home_lineup: List[str],
|
||||
away_lineup: List[str],
|
||||
home_team_id: str,
|
||||
away_team_id: str,
|
||||
home_analysis,
|
||||
away_analysis,
|
||||
) -> Dict[str, float]:
|
||||
defaults = {
|
||||
'home_lineup_goals_per90': 0.0, 'away_lineup_goals_per90': 0.0,
|
||||
'home_lineup_assists_per90': 0.0, 'away_lineup_assists_per90': 0.0,
|
||||
'home_squad_continuity': 0.5, 'away_squad_continuity': 0.5,
|
||||
'home_top_scorer_form': 0, 'away_top_scorer_form': 0,
|
||||
'home_avg_player_exp': 0.0, 'away_avg_player_exp': 0.0,
|
||||
'home_goals_diversity': 0.0, 'away_goals_diversity': 0.0,
|
||||
}
|
||||
conn = self.squad_engine.get_conn()
|
||||
if conn is None:
|
||||
return defaults
|
||||
|
||||
try:
|
||||
from psycopg2.extras import RealDictCursor
|
||||
result = {}
|
||||
for prefix, lineup, team_id in [
|
||||
('home', home_lineup, home_team_id),
|
||||
('away', away_lineup, away_team_id),
|
||||
]:
|
||||
if not lineup:
|
||||
for k in ('lineup_goals_per90', 'lineup_assists_per90',
|
||||
'squad_continuity', 'top_scorer_form',
|
||||
'avg_player_exp', 'goals_diversity'):
|
||||
result[f'{prefix}_{k}'] = defaults[f'{prefix}_{k}']
|
||||
continue
|
||||
|
||||
g90, a90, total_exp = 0.0, 0.0, 0
|
||||
best_scorer_total, best_scorer_id = 0, None
|
||||
scorers_in_lineup = 0
|
||||
|
||||
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
||||
for pid in lineup:
|
||||
cur.execute("""
|
||||
SELECT
|
||||
COUNT(*) as starts,
|
||||
COALESCE(SUM(CASE WHEN e.event_type = 'goal'
|
||||
AND (e.event_subtype IS NULL OR e.event_subtype NOT ILIKE '%%penaltı kaçırma%%')
|
||||
THEN 1 ELSE 0 END), 0) as goals,
|
||||
COALESCE((SELECT COUNT(*) FROM match_player_events
|
||||
WHERE assist_player_id = %s), 0) as assists
|
||||
FROM match_player_participation mpp
|
||||
LEFT JOIN match_player_events e
|
||||
ON e.match_id = mpp.match_id AND e.player_id = mpp.player_id
|
||||
WHERE mpp.player_id = %s AND mpp.is_starting = true
|
||||
""", (pid, pid))
|
||||
row = cur.fetchone()
|
||||
if not row or not row['starts']:
|
||||
continue
|
||||
starts = row['starts']
|
||||
goals = row['goals'] or 0
|
||||
assists = row['assists'] or 0
|
||||
g90 += goals / starts
|
||||
a90 += assists / starts
|
||||
total_exp += starts
|
||||
if goals > 0:
|
||||
scorers_in_lineup += 1
|
||||
if goals > best_scorer_total:
|
||||
best_scorer_total = goals
|
||||
best_scorer_id = pid
|
||||
|
||||
n_st = len(lineup) or 1
|
||||
|
||||
# Top scorer recent form (goals in last 5 starts)
|
||||
top_scorer_form = 0
|
||||
if best_scorer_id:
|
||||
cur.execute("""
|
||||
SELECT COUNT(*) as goals
|
||||
FROM match_player_events mpe
|
||||
WHERE mpe.player_id = %s AND mpe.event_type = 'goal'
|
||||
AND mpe.match_id IN (
|
||||
SELECT match_id FROM match_player_participation
|
||||
WHERE player_id = %s AND is_starting = true
|
||||
ORDER BY match_id DESC LIMIT 5
|
||||
)
|
||||
""", (best_scorer_id, best_scorer_id))
|
||||
tsf_row = cur.fetchone()
|
||||
if tsf_row:
|
||||
top_scorer_form = tsf_row['goals'] or 0
|
||||
|
||||
# Squad continuity (overlap with previous match lineup)
|
||||
squad_continuity = 0.5
|
||||
cur.execute("""
|
||||
SELECT mpp.player_id
|
||||
FROM match_player_participation mpp
|
||||
JOIN matches m ON mpp.match_id = m.id
|
||||
WHERE mpp.team_id = %s AND mpp.is_starting = true
|
||||
AND m.status = 'FT'
|
||||
ORDER BY m.mst_utc DESC
|
||||
LIMIT 11
|
||||
""", (team_id,))
|
||||
prev_starters = {r['player_id'] for r in cur.fetchall()}
|
||||
if prev_starters:
|
||||
overlap = len(set(lineup) & prev_starters)
|
||||
squad_continuity = overlap / n_st
|
||||
|
||||
result[f'{prefix}_lineup_goals_per90'] = round(g90, 3)
|
||||
result[f'{prefix}_lineup_assists_per90'] = round(a90, 3)
|
||||
result[f'{prefix}_squad_continuity'] = round(squad_continuity, 3)
|
||||
result[f'{prefix}_top_scorer_form'] = top_scorer_form
|
||||
result[f'{prefix}_avg_player_exp'] = round(total_exp / n_st, 1)
|
||||
result[f'{prefix}_goals_diversity'] = round(scorers_in_lineup / n_st, 3)
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
print(f"[PlayerPredictor] Player-level features failed: {e}")
|
||||
return defaults
|
||||
|
||||
def get_1x2_modifier(self, prediction: PlayerPrediction) -> Dict[str, float]:
|
||||
"""
|
||||
Calculate 1X2 probability modifiers based on squad analysis.
|
||||
|
||||
Returns modifiers to apply to base probabilities.
|
||||
squad_diff is in training scale (~-33 to +33), normalize to -1..+1.
|
||||
"""
|
||||
diff = prediction.squad_diff / 100 # -1 to +1
|
||||
diff = prediction.squad_diff / 33.0 # training-scale normalisation
|
||||
diff = max(-1.0, min(1.0, diff)) # clamp
|
||||
|
||||
return {
|
||||
"home_modifier": 1.0 + (diff * 0.3), # Up to +/-30%
|
||||
@@ -214,7 +349,7 @@ if __name__ == "__main__":
|
||||
print("=" * 50)
|
||||
|
||||
pred = engine.predict(
|
||||
match_id=None,
|
||||
match_id="test_match",
|
||||
home_team_id="test_home",
|
||||
away_team_id="test_away"
|
||||
)
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
"""
|
||||
Referee Predictor Engine - V20 Ensemble Component
|
||||
Analyzes referee patterns for cards, goals, and home bias.
|
||||
|
||||
Weight: 15% in ensemble
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from typing import Dict, Optional
|
||||
from dataclasses import dataclass
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from features.referee_engine import get_referee_engine
|
||||
|
||||
|
||||
@dataclass
|
||||
class RefereePrediction:
|
||||
"""Referee engine prediction output."""
|
||||
referee_name: str = ""
|
||||
matches_officiated: int = 0
|
||||
|
||||
# Card tendencies
|
||||
avg_yellow_cards: float = 4.0
|
||||
avg_red_cards: float = 0.2
|
||||
is_card_heavy: bool = False # Above average cards
|
||||
|
||||
# Goal tendencies
|
||||
avg_goals_per_match: float = 2.5
|
||||
over_25_rate: float = 0.50
|
||||
is_high_scoring: bool = False # Above average goals
|
||||
|
||||
# Home bias
|
||||
home_win_rate: float = 0.45
|
||||
home_bias: float = 0.0 # -1 to +1, positive = favors home
|
||||
|
||||
# Penalty tendency
|
||||
penalty_rate: float = 0.15
|
||||
|
||||
confidence: float = 0.0
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"referee_name": self.referee_name,
|
||||
"matches_officiated": self.matches_officiated,
|
||||
"avg_yellow_cards": round(self.avg_yellow_cards, 1),
|
||||
"avg_red_cards": round(self.avg_red_cards, 2),
|
||||
"is_card_heavy": self.is_card_heavy,
|
||||
"avg_goals_per_match": round(self.avg_goals_per_match, 2),
|
||||
"over_25_rate": round(self.over_25_rate * 100, 1),
|
||||
"is_high_scoring": self.is_high_scoring,
|
||||
"home_win_rate": round(self.home_win_rate * 100, 1),
|
||||
"home_bias": round(self.home_bias, 2),
|
||||
"penalty_rate": round(self.penalty_rate * 100, 1),
|
||||
"confidence": round(self.confidence, 1)
|
||||
}
|
||||
|
||||
|
||||
class RefereePredictorEngine:
|
||||
"""
|
||||
Referee-based prediction engine.
|
||||
|
||||
Analyzes:
|
||||
- Card tendency (sarı/kırmızı kart ortalaması)
|
||||
- Goal tendency (maç başına gol, 2.5 üst oranı)
|
||||
- Home bias (ev sahibi lehine karar oranı)
|
||||
- Penalty tendency (penaltı verme oranı)
|
||||
"""
|
||||
|
||||
# League average benchmarks
|
||||
LEAGUE_AVG_GOALS = 2.65
|
||||
LEAGUE_AVG_YELLOW = 4.0
|
||||
LEAGUE_HOME_WIN_RATE = 0.45
|
||||
|
||||
def __init__(self):
|
||||
self.referee_engine = get_referee_engine()
|
||||
print("✅ RefereePredictorEngine initialized")
|
||||
|
||||
def predict(self,
|
||||
match_id: str = None,
|
||||
referee_name: str = None,
|
||||
league_id: str = None) -> RefereePrediction:
|
||||
"""
|
||||
Generate referee-based prediction.
|
||||
|
||||
Args:
|
||||
match_id: Match ID to find referee
|
||||
referee_name: Or provide referee name directly
|
||||
league_id: League ID to scope stats (prevents name collisions)
|
||||
|
||||
Returns:
|
||||
RefereePrediction with referee analysis
|
||||
"""
|
||||
|
||||
# Get referee features
|
||||
if match_id:
|
||||
features = self.referee_engine.get_features(match_id, league_id=league_id)
|
||||
# Live flows may already have referee_name while match_officials table is sparse.
|
||||
# Prefer the richer profile if direct-name lookup has more history.
|
||||
if referee_name:
|
||||
name_features = self.referee_engine.get_features_by_name(referee_name, league_id=league_id)
|
||||
if (name_features.get("referee_matches", 0) or 0) > (features.get("referee_matches", 0) or 0):
|
||||
features = name_features
|
||||
elif referee_name:
|
||||
features = self.referee_engine.get_features_by_name(referee_name, league_id=league_id)
|
||||
else:
|
||||
# Return default
|
||||
return RefereePrediction(confidence=10.0)
|
||||
|
||||
ref_name = features.get("referee_name", "Unknown")
|
||||
matches = features.get("referee_matches", 0)
|
||||
|
||||
if matches < 5:
|
||||
# Not enough data
|
||||
return RefereePrediction(
|
||||
referee_name=ref_name,
|
||||
matches_officiated=matches,
|
||||
confidence=20.0
|
||||
)
|
||||
|
||||
# Extract features
|
||||
avg_yellow = features.get("referee_avg_yellow", 4.0)
|
||||
avg_red = features.get("referee_avg_red", 0.2)
|
||||
avg_goals = features.get("referee_avg_goals", 2.5)
|
||||
over25_rate = features.get("referee_over25_rate", 0.5)
|
||||
home_win_rate = features.get("referee_home_win_rate", 0.45) if "referee_home_win_rate" in features else 0.45
|
||||
home_bias = features.get("referee_home_bias", 0.0)
|
||||
penalty_rate = features.get("referee_penalty_rate", 0.15)
|
||||
|
||||
# Determine tendencies
|
||||
is_card_heavy = (avg_yellow + avg_red * 4) > (self.LEAGUE_AVG_YELLOW + 1)
|
||||
is_high_scoring = avg_goals > self.LEAGUE_AVG_GOALS
|
||||
|
||||
# Confidence based on matches officiated
|
||||
confidence = min(90.0, 30.0 + matches * 2)
|
||||
|
||||
return RefereePrediction(
|
||||
referee_name=ref_name,
|
||||
matches_officiated=matches,
|
||||
avg_yellow_cards=avg_yellow,
|
||||
avg_red_cards=avg_red,
|
||||
is_card_heavy=is_card_heavy,
|
||||
avg_goals_per_match=avg_goals,
|
||||
over_25_rate=over25_rate,
|
||||
is_high_scoring=is_high_scoring,
|
||||
home_win_rate=home_win_rate,
|
||||
home_bias=home_bias,
|
||||
penalty_rate=penalty_rate,
|
||||
confidence=confidence
|
||||
)
|
||||
|
||||
def get_modifiers(self, prediction: RefereePrediction) -> Dict[str, float]:
|
||||
"""
|
||||
Get modifiers to apply to other predictions based on referee profile.
|
||||
"""
|
||||
return {
|
||||
# Home team gets slight boost if referee has home bias
|
||||
"home_modifier": 1.0 + (prediction.home_bias * 0.05),
|
||||
# O/U modifier
|
||||
"over_25_modifier": 1.0 + (prediction.avg_goals_per_match - self.LEAGUE_AVG_GOALS) * 0.1,
|
||||
# Card modifier for card markets
|
||||
"cards_modifier": 1.0 + (prediction.avg_yellow_cards - self.LEAGUE_AVG_YELLOW) * 0.05
|
||||
}
|
||||
|
||||
|
||||
# Singleton
|
||||
_engine: Optional[RefereePredictorEngine] = None
|
||||
|
||||
|
||||
def get_referee_predictor() -> RefereePredictorEngine:
|
||||
global _engine
|
||||
if _engine is None:
|
||||
_engine = RefereePredictorEngine()
|
||||
return _engine
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
engine = get_referee_predictor()
|
||||
|
||||
print("\n🧪 Referee Predictor Engine Test")
|
||||
print("=" * 50)
|
||||
|
||||
pred = engine.predict(referee_name="Cüneyt Çakır")
|
||||
|
||||
print(f"\n📊 Prediction:")
|
||||
for k, v in pred.to_dict().items():
|
||||
print(f" {k}: {v}")
|
||||
@@ -1,286 +0,0 @@
|
||||
"""
|
||||
Team Predictor Engine - V20 Ensemble Component
|
||||
Combines ELO ratings, form stats, H2H records and team statistics.
|
||||
|
||||
Weight: 30% in ensemble
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from typing import Dict, Optional, Tuple, Any
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
# Add parent to path
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from features.elo_system import get_elo_system
|
||||
from features.h2h_engine import get_h2h_engine
|
||||
from features.momentum_engine import get_momentum_engine, MomentumData
|
||||
from features.team_stats_engine import get_team_stats_engine
|
||||
|
||||
|
||||
@dataclass
|
||||
class TeamPrediction:
|
||||
"""Team engine prediction output."""
|
||||
home_win_prob: float = 0.33
|
||||
draw_prob: float = 0.33
|
||||
away_win_prob: float = 0.33
|
||||
home_xg: float = 1.3
|
||||
away_xg: float = 1.1
|
||||
form_advantage: float = 0.0 # -1 to +1, positive = home advantage
|
||||
h2h_advantage: float = 0.0 # -1 to +1
|
||||
elo_diff: float = 0.0
|
||||
confidence: float = 0.0
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"home_win_prob": round(self.home_win_prob * 100, 1),
|
||||
"draw_prob": round(self.draw_prob * 100, 1),
|
||||
"away_win_prob": round(self.away_win_prob * 100, 1),
|
||||
"home_xg": round(self.home_xg, 2),
|
||||
"away_xg": round(self.away_xg, 2),
|
||||
"form_advantage": round(self.form_advantage, 2),
|
||||
"h2h_advantage": round(self.h2h_advantage, 2),
|
||||
"elo_diff": round(self.elo_diff, 0),
|
||||
"confidence": round(self.confidence, 1)
|
||||
}
|
||||
|
||||
raw_features: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
class TeamPredictorEngine:
|
||||
"""
|
||||
Team-based prediction engine.
|
||||
|
||||
Uses:
|
||||
- ELO Rating System (venue-adjusted, league-weighted)
|
||||
- H2H Engine (head-to-head history)
|
||||
- Momentum Engine (recent form)
|
||||
- Team Stats Engine (possession, shots, corners)
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.elo_system = get_elo_system()
|
||||
self.h2h_engine = get_h2h_engine()
|
||||
self.momentum_engine = get_momentum_engine()
|
||||
self.team_stats_engine = get_team_stats_engine()
|
||||
|
||||
print("✅ TeamPredictorEngine initialized")
|
||||
|
||||
def predict(self,
|
||||
home_team_id: str,
|
||||
away_team_id: str,
|
||||
match_date_ms: int,
|
||||
home_team_name: str = "",
|
||||
away_team_name: str = "") -> TeamPrediction:
|
||||
"""
|
||||
Generate team-based prediction.
|
||||
|
||||
Args:
|
||||
home_team_id: Home team ID
|
||||
away_team_id: Away team ID
|
||||
match_date_ms: Match date in milliseconds
|
||||
home_team_name: Home team name (for ELO)
|
||||
away_team_name: Away team name (for ELO)
|
||||
|
||||
Returns:
|
||||
TeamPrediction with 1X2 probabilities and xG
|
||||
"""
|
||||
|
||||
# 1. Get ELO predictions
|
||||
elo_pred = self.elo_system.predict_match(home_team_id, away_team_id)
|
||||
elo_features = self.elo_system.get_match_features(home_team_id, away_team_id)
|
||||
|
||||
# 2. Get H2H features
|
||||
try:
|
||||
h2h_features = self.h2h_engine.get_features(
|
||||
home_team_id, away_team_id, match_date_ms
|
||||
)
|
||||
except Exception:
|
||||
h2h_features = {
|
||||
"h2h_home_win_rate": 0.5,
|
||||
"h2h_away_win_rate": 0.5,
|
||||
"h2h_avg_goals": 2.5,
|
||||
"h2h_btts_rate": 0.5
|
||||
}
|
||||
|
||||
# 3. Get Momentum/Form features
|
||||
try:
|
||||
# key: form_score should be 0-1 derived from momentum_score (-1 to 1)
|
||||
home_mom_data = self.momentum_engine.calculate_momentum(home_team_id, match_date_ms)
|
||||
away_mom_data = self.momentum_engine.calculate_momentum(away_team_id, match_date_ms)
|
||||
|
||||
home_form_score = (home_mom_data.momentum_score + 1) / 2
|
||||
away_form_score = (away_mom_data.momentum_score + 1) / 2
|
||||
except Exception as e:
|
||||
print(f"⚠️ MomentumEngine error: {e}")
|
||||
home_mom_data = MomentumData()
|
||||
away_mom_data = MomentumData()
|
||||
home_form_score = 0.5
|
||||
away_form_score = 0.5
|
||||
|
||||
# 4. Get Team Stats
|
||||
home_stats = self.team_stats_engine.get_features(home_team_id, match_date_ms)
|
||||
away_stats = self.team_stats_engine.get_features(away_team_id, match_date_ms)
|
||||
|
||||
# 5. Combine predictions
|
||||
# ELO-based 1X2 (60% weight)
|
||||
elo_home = elo_pred.get("home_win_prob", 0.33)
|
||||
elo_draw = elo_pred.get("draw_prob", 0.33)
|
||||
elo_away = elo_pred.get("away_win_prob", 0.33)
|
||||
|
||||
# Adjust based on H2H (20% weight)
|
||||
h2h_home_rate = h2h_features.get("h2h_home_win_rate", 0.5)
|
||||
h2h_away_rate = h2h_features.get("h2h_away_win_rate", 0.5)
|
||||
|
||||
# Adjust based on form (20% weight)
|
||||
home_form = home_form_score
|
||||
away_form = away_form_score
|
||||
form_diff = (home_form - away_form) # -1 to +1
|
||||
|
||||
# Weighted combination
|
||||
final_home = elo_home * 0.6 + h2h_home_rate * 0.2 + (0.5 + form_diff * 0.3) * 0.2
|
||||
final_away = elo_away * 0.6 + h2h_away_rate * 0.2 + (0.5 - form_diff * 0.3) * 0.2
|
||||
final_draw = 1.0 - final_home - final_away
|
||||
|
||||
# Normalize
|
||||
total = final_home + final_draw + final_away
|
||||
if total > 0:
|
||||
final_home /= total
|
||||
final_draw /= total
|
||||
final_away /= total
|
||||
|
||||
# Calculate xG based on stats and form (conservative base)
|
||||
home_conversion = home_stats.get("shot_conversion_rate", 0.1)
|
||||
away_conversion = away_stats.get("shot_conversion_rate", 0.1)
|
||||
|
||||
base_home_xg = 1.35 + (home_conversion * 3.0)
|
||||
base_away_xg = 1.10 + (away_conversion * 2.5)
|
||||
|
||||
# Defense weakness factor: opponent's defensive quality affects xG
|
||||
# Higher shots on target against = weaker defense
|
||||
away_def_weakness = away_stats.get("shot_accuracy", 0.35) # opponent's shot accuracy as proxy
|
||||
home_def_weakness = home_stats.get("shot_accuracy", 0.35)
|
||||
|
||||
# Adjust xG: stronger opponent defense → lower xG
|
||||
home_xg = base_home_xg * (1 + form_diff * 0.15) * (0.8 + away_def_weakness * 0.6)
|
||||
away_xg = base_away_xg * (1 - form_diff * 0.15) * (0.8 + home_def_weakness * 0.6)
|
||||
|
||||
# Apply xG Underperformance Penalty directly to calculated xG
|
||||
# If a team chronically underperforms its xG, we subtract that historical difference here
|
||||
if hasattr(home_mom_data, 'xg_underperformance') and home_mom_data.xg_underperformance > 0.2:
|
||||
home_xg -= min(0.5, home_mom_data.xg_underperformance * 0.5)
|
||||
|
||||
if hasattr(away_mom_data, 'xg_underperformance') and away_mom_data.xg_underperformance > 0.2:
|
||||
away_xg -= min(0.5, away_mom_data.xg_underperformance * 0.5)
|
||||
|
||||
# H2H adjustment (more conservative)
|
||||
h2h_avg_goals = h2h_features.get("h2h_avg_goals", 2.5)
|
||||
if h2h_avg_goals > 3.0:
|
||||
home_xg *= 1.05
|
||||
away_xg *= 1.05
|
||||
elif h2h_avg_goals < 2.0:
|
||||
home_xg *= 0.95
|
||||
away_xg *= 0.95
|
||||
|
||||
# Clamp xG to reasonable range
|
||||
home_xg = max(0.5, min(3.5, home_xg))
|
||||
away_xg = max(0.3, min(3.0, away_xg))
|
||||
|
||||
# Calculate confidence
|
||||
# Higher when ELO, H2H, and Form all agree
|
||||
elo_winner = "H" if elo_home > max(elo_draw, elo_away) else ("A" if elo_away > elo_draw else "D")
|
||||
h2h_winner = "H" if h2h_home_rate > h2h_away_rate else "A"
|
||||
form_winner = "H" if form_diff > 0.1 else ("A" if form_diff < -0.1 else "D")
|
||||
|
||||
agreement = sum([
|
||||
elo_winner == h2h_winner,
|
||||
elo_winner == form_winner,
|
||||
h2h_winner == form_winner
|
||||
])
|
||||
|
||||
max_prob = max(final_home, final_draw, final_away)
|
||||
confidence = max_prob * 100 * (0.7 + agreement * 0.1)
|
||||
|
||||
# Collect Raw Features for XGBoost
|
||||
# Note: home_mom_data is an object now
|
||||
def get_rate(val): return val if val is not None else 0.5
|
||||
|
||||
raw_features = {
|
||||
**elo_features, # 8 features
|
||||
|
||||
# Form Features (need key mapping to match extract_training_data.py)
|
||||
"home_goals_avg": 1.5 + home_mom_data.goals_trend, # Proxy
|
||||
"home_conceded_avg": 1.5 - home_mom_data.conceded_trend, # Proxy
|
||||
"away_goals_avg": 1.5 + away_mom_data.goals_trend,
|
||||
"away_conceded_avg": 1.5 - away_mom_data.conceded_trend,
|
||||
|
||||
"home_clean_sheet_rate": 0.2, # Not in new MomentumData
|
||||
"away_clean_sheet_rate": 0.2,
|
||||
"home_scoring_rate": 0.8,
|
||||
"away_scoring_rate": 0.8,
|
||||
|
||||
"home_winning_streak": home_mom_data.winning_streak,
|
||||
"away_winning_streak": away_mom_data.winning_streak,
|
||||
"home_unbeaten_streak": home_mom_data.unbeaten_streak,
|
||||
"away_unbeaten_streak": away_mom_data.unbeaten_streak,
|
||||
|
||||
# H2H Features
|
||||
**h2h_features,
|
||||
|
||||
# Team Stats
|
||||
"home_avg_possession": home_stats.get("avg_possession", 0.5),
|
||||
"away_avg_possession": away_stats.get("avg_possession", 0.5),
|
||||
"home_avg_shots_on_target": home_stats.get("avg_shots_on_target", 3.5),
|
||||
"away_avg_shots_on_target": away_stats.get("avg_shots_on_target", 3.5),
|
||||
"home_shot_conversion": home_stats.get("shot_conversion_rate", 0.1),
|
||||
"away_shot_conversion": away_stats.get("shot_conversion_rate", 0.1),
|
||||
"home_avg_corners": home_stats.get("avg_corners", 4.5),
|
||||
"away_avg_corners": away_stats.get("avg_corners", 4.5),
|
||||
|
||||
# Derived
|
||||
"home_xga": 1.5 - home_mom_data.conceded_trend, # reusing as proxy
|
||||
"away_xga": 1.5 - away_mom_data.conceded_trend
|
||||
}
|
||||
|
||||
return TeamPrediction(
|
||||
home_win_prob=final_home,
|
||||
draw_prob=final_draw,
|
||||
away_win_prob=final_away,
|
||||
home_xg=home_xg,
|
||||
away_xg=away_xg,
|
||||
form_advantage=form_diff,
|
||||
h2h_advantage=h2h_home_rate - h2h_away_rate,
|
||||
elo_diff=elo_features.get("elo_diff", 0),
|
||||
confidence=confidence,
|
||||
raw_features=raw_features
|
||||
)
|
||||
|
||||
|
||||
# Singleton
|
||||
_engine: Optional[TeamPredictorEngine] = None
|
||||
|
||||
|
||||
def get_team_predictor() -> TeamPredictorEngine:
|
||||
global _engine
|
||||
if _engine is None:
|
||||
_engine = TeamPredictorEngine()
|
||||
return _engine
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
engine = get_team_predictor()
|
||||
|
||||
print("\n🧪 Team Predictor Engine Test")
|
||||
print("=" * 50)
|
||||
|
||||
# Test with sample IDs
|
||||
pred = engine.predict(
|
||||
home_team_id="test_home",
|
||||
away_team_id="test_away",
|
||||
match_date_ms=1707393600000
|
||||
)
|
||||
|
||||
print(f"\n📊 Prediction:")
|
||||
for k, v in pred.to_dict().items():
|
||||
print(f" {k}: {v}")
|
||||
@@ -0,0 +1 @@
|
||||
# data package
|
||||
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
Async Database Module — V2 Betting Engine
|
||||
==========================================
|
||||
Provides async SQLAlchemy sessions via asyncpg for the V2 router.
|
||||
|
||||
Usage:
|
||||
async with get_session() as session:
|
||||
result = await session.execute(text("SELECT ..."))
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import AsyncGenerator
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from sqlalchemy.ext.asyncio import (
|
||||
AsyncEngine,
|
||||
AsyncSession,
|
||||
async_sessionmaker,
|
||||
create_async_engine,
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
|
||||
_engine: AsyncEngine | None = None
|
||||
_session_maker: async_sessionmaker[AsyncSession] | None = None
|
||||
|
||||
|
||||
def _get_async_dsn() -> str:
|
||||
"""
|
||||
Convert DATABASE_URL to asyncpg-compatible format.
|
||||
|
||||
Handles:
|
||||
1. Prisma's ``?schema=public`` suffix → stripped
|
||||
2. ``postgresql://`` driver prefix → ``postgresql+asyncpg://``
|
||||
"""
|
||||
dsn = os.getenv(
|
||||
"DATABASE_URL",
|
||||
"postgresql://suggestbet:SuGGesT2026SecuRe@localhost:15432/boilerplate_db",
|
||||
)
|
||||
|
||||
# Strip Prisma's ?schema= parameter
|
||||
if "?" in dsn:
|
||||
base, query = dsn.split("?", 1)
|
||||
kept_parts = [
|
||||
part for part in query.split("&") if part and not part.startswith("schema=")
|
||||
]
|
||||
dsn = base if not kept_parts else f"{base}?{'&'.join(kept_parts)}"
|
||||
|
||||
# Convert driver prefix for asyncpg
|
||||
if dsn.startswith("postgresql://"):
|
||||
dsn = dsn.replace("postgresql://", "postgresql+asyncpg://", 1)
|
||||
elif dsn.startswith("postgres://"):
|
||||
dsn = dsn.replace("postgres://", "postgresql+asyncpg://", 1)
|
||||
|
||||
return dsn
|
||||
|
||||
|
||||
def _ensure_engine() -> AsyncEngine:
|
||||
global _engine, _session_maker
|
||||
if _engine is None:
|
||||
_engine = create_async_engine(
|
||||
_get_async_dsn(),
|
||||
pool_size=5,
|
||||
max_overflow=5,
|
||||
pool_timeout=10,
|
||||
pool_pre_ping=True,
|
||||
echo=False,
|
||||
)
|
||||
_session_maker = async_sessionmaker(
|
||||
bind=_engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False,
|
||||
)
|
||||
print("✅ Async database engine created (asyncpg)")
|
||||
return _engine
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def get_session() -> AsyncGenerator[AsyncSession, None]:
|
||||
"""Provide an async session context manager."""
|
||||
_ensure_engine()
|
||||
assert _session_maker is not None
|
||||
async with _session_maker() as session:
|
||||
yield session
|
||||
|
||||
|
||||
async def dispose_engine() -> None:
|
||||
"""Shut down the async engine cleanly."""
|
||||
global _engine, _session_maker
|
||||
if _engine is not None:
|
||||
await _engine.dispose()
|
||||
_engine = None
|
||||
_session_maker = None
|
||||
print("ℹ️ Async database engine disposed")
|
||||
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
Synchronous psycopg2 database helper for the AI Engine.
|
||||
Uses a thread-safe connection pool for legacy V20+ endpoints.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
from typing import Generator
|
||||
|
||||
import psycopg2
|
||||
from psycopg2 import pool
|
||||
from psycopg2.extensions import connection as PgConnection
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
# Safe default with no credentials — will fail fast if not configured.
|
||||
_DEFAULT_DSN = "postgresql://postgres:postgres@localhost:15432/boilerplate_db"
|
||||
|
||||
|
||||
def get_clean_dsn() -> str:
|
||||
"""
|
||||
Return a psycopg2-compatible DSN from DATABASE_URL.
|
||||
|
||||
Handles DSN cleanup issues that break raw usage:
|
||||
1. Prisma appends '?schema=public' which psycopg2 cannot parse.
|
||||
"""
|
||||
dsn: str = os.getenv("DATABASE_URL", _DEFAULT_DSN)
|
||||
connect_timeout: str = os.getenv("PGCONNECT_TIMEOUT", "5").strip() or "5"
|
||||
|
||||
# Strip Prisma's ?schema= query parameter while preserving any other query args.
|
||||
if "?" in dsn:
|
||||
base, query = dsn.split("?", 1)
|
||||
kept_parts: list[str] = [
|
||||
part for part in query.split("&") if part and not part.startswith("schema=")
|
||||
]
|
||||
dsn = base if not kept_parts else f"{base}?{'&'.join(kept_parts)}"
|
||||
|
||||
# Force bounded DB connect attempts so API calls do not hang indefinitely.
|
||||
if "connect_timeout=" not in dsn:
|
||||
separator = "&" if "?" in dsn else "?"
|
||||
dsn = f"{dsn}{separator}connect_timeout={connect_timeout}"
|
||||
return dsn
|
||||
|
||||
|
||||
class Database:
|
||||
_pool: pool.ThreadedConnectionPool | None = None
|
||||
|
||||
@classmethod
|
||||
def initialize(cls) -> None:
|
||||
if cls._pool is None:
|
||||
dsn: str = get_clean_dsn()
|
||||
try:
|
||||
cls._pool = pool.ThreadedConnectionPool(
|
||||
minconn=1,
|
||||
maxconn=10,
|
||||
dsn=dsn,
|
||||
)
|
||||
print("✅ Database connection pool created")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to create DB pool: {e}")
|
||||
raise
|
||||
|
||||
@classmethod
|
||||
def get_conn(cls) -> PgConnection:
|
||||
if cls._pool is None:
|
||||
cls.initialize()
|
||||
assert cls._pool is not None # guaranteed by initialize()
|
||||
return cls._pool.getconn()
|
||||
|
||||
@classmethod
|
||||
def return_conn(cls, conn: PgConnection) -> None:
|
||||
if cls._pool:
|
||||
cls._pool.putconn(conn)
|
||||
|
||||
@classmethod
|
||||
@contextmanager
|
||||
def connection(cls) -> Generator[PgConnection, None, None]:
|
||||
"""Context manager for safe connection handling."""
|
||||
conn: PgConnection = cls.get_conn()
|
||||
try:
|
||||
yield conn
|
||||
finally:
|
||||
cls.return_conn(conn)
|
||||
|
||||
@classmethod
|
||||
def close_all(cls) -> None:
|
||||
if cls._pool:
|
||||
cls._pool.closeall()
|
||||
print("ℹ️ Database connection pool closed")
|
||||
@@ -0,0 +1,726 @@
|
||||
{
|
||||
"version": "v1",
|
||||
"description": "Per-league odds reliability scores computed from Brier Score analysis",
|
||||
"min_matches_threshold": 50,
|
||||
"total_leagues": 265,
|
||||
"default_reliability": 0.35,
|
||||
"lookup": {
|
||||
"bx57cmq1edfq53ckfk791supi": 0.9476,
|
||||
"55hcphd1ccc6eai1ms77460on": 0.9445,
|
||||
"d9eaigzyfnfiraqc3ius757tl": 0.9402,
|
||||
"1gxlzw2ezkyeykhcaa5x8ozkk": 0.9259,
|
||||
"5jd0k2txwnq69frs79eulba8j": 0.9233,
|
||||
"6694fff47wqxl10lrd9tb91f8": 0.9193,
|
||||
"4jg7he1n3rb5dniq6hf49xorq": 0.9061,
|
||||
"59tpnfrwnvhnhzmnvfyug68hj": 0.8988,
|
||||
"ac42gi3penartj88fe9l6plpk": 0.8937,
|
||||
"3j81qr7yc4gdnakfwnxf95ovh": 0.8771,
|
||||
"9z5643nd06afqu01ea2wt8y4g": 0.8734,
|
||||
"482ofyysbdbeoxauk19yg7tdt": 0.8722,
|
||||
"ahl3vljaignq9ebaos4uqkrvo": 0.8696,
|
||||
"8x3sbh85gc8qir50utw39jl04": 0.865,
|
||||
"agpweohvn9tugnyl6ry4rhivp": 0.8428,
|
||||
"4c1nfi2j1m731hcay25fcgndq": 0.8425,
|
||||
"1j4ehtrbry9depwt6oghaq3lu": 0.8299,
|
||||
"40yjcbx2sq6oq736iqqqczwt1": 0.8237,
|
||||
"145hkd59i6foieuwr4mwi6wlq": 0.823,
|
||||
"34pl8szyvrbwcmfkuocjm3r6t": 0.8227,
|
||||
"cse5oqqt2pzfcy8uz6yz3tkbj": 0.8212,
|
||||
"zs18qaehvhg3w1208874zvfa": 0.8176,
|
||||
"57nu0wygurzkp6fuy5hhrtaa2": 0.8099,
|
||||
"1eruend45vd20g9hbrpiggs5u": 0.8083,
|
||||
"595nsvo7ykvoe690b1e4u5n56": 0.7987,
|
||||
"6vq8j5p3av14nr3iuyi4okhjt": 0.793,
|
||||
"486rhdgz7yc0sygziht7hje65": 0.7901,
|
||||
"9hh6n2f84k31zmlcxyvmc1w2y": 0.789,
|
||||
"3n5046abeu3x482ds3jwda238": 0.7863,
|
||||
"8yi6ejjd1zudcqtbn07haahg6": 0.7752,
|
||||
"byhmntnl1b4lxw0zz21im3zkd": 0.7719,
|
||||
"2bmwykmdlcc2u1c40ytoc39vy": 0.7668,
|
||||
"82jkgccg7phfjpd0mltdl3pat": 0.7643,
|
||||
"2nttcoriwf5co73vmz1vr8frm": 0.7641,
|
||||
"dr2xk7muj8aqcjdz2b3li1c0k": 0.759,
|
||||
"4yngyfinzd6bb1k7anqtqs0wt": 0.7586,
|
||||
"eog6knrkfei68si736fpquyzc": 0.756,
|
||||
"eg6s9f1jj7jr6stmbosn0g6c8": 0.7538,
|
||||
"ae1wva3zrzcp2zd15gpvsntg6": 0.7517,
|
||||
"cesdwwnxbc5fmajgroc0hqzy2": 0.7466,
|
||||
"8k1xcsyvxapl4jlsluh3eomre": 0.7463,
|
||||
"bdtat25m14jy85y484z3e6lf": 0.7437,
|
||||
"iu1vi94p4p28oozl1h9bvplr": 0.7411,
|
||||
"1r097lpxe0xn03ihb7wi98kao": 0.7391,
|
||||
"2kwbbcootiqqgmrzs6o5inle5": 0.7386,
|
||||
"9fuwphq8kvugrlc3ckm7k8wes": 0.7358,
|
||||
"civf31q1inxohs4a03y8reetf": 0.735,
|
||||
"ili150pwfuf39f7yfdch9lhw": 0.7286,
|
||||
"abs7n2ae3oydilk0tgmpnsj89": 0.7277,
|
||||
"9nbpdi9q3ywcm4q0j5u0ekwcq": 0.7254,
|
||||
"6by3h89i2eykc341oz7lv1ddd": 0.7252,
|
||||
"4qehj8hfxmy6o2ohp4fxinnzo": 0.7244,
|
||||
"9u4pm8x0lfmfq3r0pypmrls71": 0.7244,
|
||||
"c7b8o53flg36wbuevfzy3lb10": 0.7144,
|
||||
"89ovpy1rarewwzqvi30bfdr8b": 0.7068,
|
||||
"4d5d3sf6805n5u6jdoa0hdlog": 0.7052,
|
||||
"eqz64pn0qsp2y7aq4m9id3fn6": 0.7031,
|
||||
"8q60vlvn3krynkob6igrncdjq": 0.703,
|
||||
"6ihotpaocgiovlxw18e9r9prx": 0.7019,
|
||||
"c0r21rtokgnbtc0o2rldjmkxu": 0.7013,
|
||||
"1mpjd0vbxbtu9zw89yj09xk3z": 0.6996,
|
||||
"4zwgbb66rif2spcoeeol2motx": 0.6995,
|
||||
"bu1l7ckihyr0errxw61p0m05": 0.6995,
|
||||
"cv3tuitw3ho3v0opjjxpn83b9": 0.6974,
|
||||
"8r98daokeuzsamu5fmjtblqx5": 0.6922,
|
||||
"dvstmwnvw0mt5p38twn9yttyb": 0.688,
|
||||
"8y29fg2s85ppcb8uugm5ee8s4": 0.6866,
|
||||
"19q13y6ruzo0o84ipblcuouzs": 0.6858,
|
||||
"f4jc2cc5nq7flaoptpi5ua4k4": 0.6852,
|
||||
"4oogyu6o156iphvdvphwpck10": 0.684,
|
||||
"3e40pestup9xzagsu2o6c0i8u": 0.6824,
|
||||
"4rls982p5uzil6x30mhyhv9f3": 0.6812,
|
||||
"e21cf135btr8t3upw0vl6n6x0": 0.6771,
|
||||
"65q4uwm6ol1rkf5dp89m8omny": 0.6754,
|
||||
"46b141eaqq9q7o4gz5gtdpikk": 0.6752,
|
||||
"75i269i1ak43magshljadydrh": 0.6741,
|
||||
"3ab1uwtoyjopdj1y1fynyy9jg": 0.6737,
|
||||
"4mbfidy8zum5u0aqjqo0vuqs2": 0.673,
|
||||
"7wssxdqi4xihseeam8grqa2b8": 0.666,
|
||||
"61fzfjogstjuukzcehighq7mu": 0.6641,
|
||||
"6g8hw3acenrw828la7gwx4mvs": 0.663,
|
||||
"e1kxdivp5g4cpldgpwvnzl1vv": 0.6626,
|
||||
"9ikchyu9fb8bvx0s673jofj6s": 0.6622,
|
||||
"a9vrdkelbgif0gtu3wxsr75xo": 0.6618,
|
||||
"6sxm2iln2w45ux498pty9miw8": 0.6615,
|
||||
"ea0h6cf3bhl698hkxhpulh2zz": 0.661,
|
||||
"apdwh753fupxheygs8seahh7x": 0.6604,
|
||||
"er5745q30wnr8jv9nr863omzg": 0.659,
|
||||
"2z7257m7hj58zuxcjrsg4erzc": 0.6551,
|
||||
"2o9svokc5s7diish3ycrzk7jm": 0.655,
|
||||
"8usjlmziv3p2re0r2wwzezki9": 0.6549,
|
||||
"c0yqkbilbbg70ij2473xymmqv": 0.6506,
|
||||
"du6jsenbjql5e8f3yk880ox4g": 0.6494,
|
||||
"cbdbziaqczfuyuwqsylqi26zd": 0.6478,
|
||||
"725gd73msyt08xm76v7gkxj7u": 0.6445,
|
||||
"enzlj1as2raqm4ids1zyb07y1": 0.6442,
|
||||
"scf9p4y91yjvqvg5jndxzhxj": 0.6414,
|
||||
"5z8v4mj6cjs9ex6hdrpourjzh": 0.6389,
|
||||
"4zwjlzdszduqmxzusysvzymms": 0.6387,
|
||||
"7nmz249q89qg5ezcvzlheljji": 0.6381,
|
||||
"2mdmx668tyhy4u4z9zszwjv5v": 0.6345,
|
||||
"4a7o9rf7ytl8g3ejwpblc6p5n": 0.6306,
|
||||
"2ty8ihceabty8yddmu31iuuej": 0.6283,
|
||||
"dy8zaksw5e9nwrs1p5ss4o1nu": 0.628,
|
||||
"1b70m6qtxrp75b4vtk8hxh8c3": 0.6261,
|
||||
"ajxs0e0g6ryg5ol8qvw3evrcz": 0.6249,
|
||||
"a4fgj2rfbpf4ejo1qi624fefo": 0.6184,
|
||||
"akmkihra9ruad09ljapsm84b3": 0.6182,
|
||||
"907l7wtxdvugdo9i2249wcmr0": 0.6171,
|
||||
"6lwpjhktjhl9g7x2w7njmzva6": 0.6164,
|
||||
"ax1yf4nlzqpcji4j8epdgx3zl": 0.6163,
|
||||
"6ybvtzejh91761lqe7y1csrqo": 0.6158,
|
||||
"3btdfgw79qiz3jmyfudovtbu2": 0.6122,
|
||||
"5cwsxtx37les6m10xj71htkgf": 0.6101,
|
||||
"9p3nnxhdjahfn8qswpzy8oyc3": 0.61,
|
||||
"2xg0qvif1rh7du6wmk2eleku3": 0.6091,
|
||||
"1wwro3z1eb3fl601dju6inlc6": 0.6084,
|
||||
"gfskxsdituog2kqp9yiu7bzi": 0.6076,
|
||||
"zilopfej2h0n3vpan5tcynpo": 0.6051,
|
||||
"2hsidwomhjsaaytdy9u5niyi4": 0.6012,
|
||||
"1klyfth8tl6lu6ra7k8zmy2n2": 0.5996,
|
||||
"cegl2ivkc25blcatxp4jmk1ec": 0.5993,
|
||||
"7qf0jaayyxy3ruamsexv5p1kl": 0.5988,
|
||||
"erpufio3qaujd9gkszcqvb0bf": 0.5972,
|
||||
"cfesxhzb83yl8b779uv3revz1": 0.597,
|
||||
"3ww12jab49q8q8mk9avdwjqgk": 0.5961,
|
||||
"8t2o4huu2e48ij23dxnl9w5qx": 0.5928,
|
||||
"5vq1bl8h8dxdr34w0jaanokto": 0.5919,
|
||||
"ac112osli9fvox1epcg4ld3t6": 0.59,
|
||||
"3frp1zxrqulrlrnk503n6l4l": 0.5808,
|
||||
"c76z5d6j7dpi1e79tm8fpm39z": 0.5807,
|
||||
"6ifaeunfdelecgticvxanikzu": 0.5796,
|
||||
"81txfenlgw75nq3u2nfdkj92o": 0.5789,
|
||||
"yv73ms6v1995b5wny16jcfi3": 0.5787,
|
||||
"b3ufcd24wfnnd5j98ped6irfu": 0.5752,
|
||||
"29actv1ohj8r10kd9hu0jnb0n": 0.5737,
|
||||
"bfqezwfhot1l3p1cpk4oonh25": 0.5705,
|
||||
"5taraea6mqjjldg9zxswo825y": 0.5696,
|
||||
"7qdv1xae7ikfe8dft3oj29yqc": 0.5692,
|
||||
"dm5ka0os1e3dxcp3vh05kmp33": 0.5678,
|
||||
"ay4u6j7lfkcg7x21mx5q121j": 0.5676,
|
||||
"7af85xa75vozt2l4hzi6ryts7": 0.5663,
|
||||
"5k620c7y6dlbmcm88dt3eb7t": 0.5644,
|
||||
"ejunkmfhjz9weugd2bqrkgobb": 0.564,
|
||||
"3428tckxcirwwh3o3jgc1m8ji": 0.5597,
|
||||
"d6zovb8puwgcmsg91iya6rbtm": 0.5593,
|
||||
"2wolc27r8z03itcvwp43e38c5": 0.5592,
|
||||
"alpfd99yd3lfv7bhjo0biuq7b": 0.5582,
|
||||
"beqqnubkv05mamuwvimeum015": 0.5577,
|
||||
"4w7x0s5gfs5abasphlha5de8k": 0.5558,
|
||||
"9ynnnx1qmkizq1o3qr3v0nsuk": 0.554,
|
||||
"722fdbecxzcq9788l6jqclzlw": 0.5539,
|
||||
"287tckirbfj9nb8ar2k9r60vn": 0.5529,
|
||||
"esrunz7rjb0td98mx9e5cedoy": 0.5516,
|
||||
"32n2r9bl6x90psj0wa7bfs6vq": 0.5487,
|
||||
"50ap4sua1xyut3mpu7ehesp63": 0.5483,
|
||||
"5c96g1zm7vo5ons9c42uy2w3r": 0.5469,
|
||||
"3p81ltz6845appgkbgkzxueii": 0.5454,
|
||||
"3n9mk5b2mxmq831wfmv6pu86i": 0.5437,
|
||||
"5zr0b05eyx25km7z1k03ca9jx": 0.5424,
|
||||
"1owhvvge4wlx7e0e431b4vhqx": 0.5423,
|
||||
"3iwftmprsznl6yribr11a8l9m": 0.5393,
|
||||
"7r1f93t6ddrsa5n8v1nq6qlzm": 0.5393,
|
||||
"1gwajyt0pk2jm5fx5mu36v114": 0.5389,
|
||||
"581t4mywybx21wcpmpykhyzr3": 0.5388,
|
||||
"6wubmo7di3kdpflluf6s8c7vs": 0.5375,
|
||||
"bq89wbdvedtov6auzuh6rsv7s": 0.5363,
|
||||
"byu00jvt1j6csyv4y1lkt2fm2": 0.5359,
|
||||
"af79lqrc0ntom74zq13ccjslo": 0.5357,
|
||||
"3ri6juw2w6ma0jezszdlv1uqm": 0.5356,
|
||||
"3l29w00m506ex93t5bbh9cg2a": 0.5355,
|
||||
"1zp1du9n4rj36p1ss9zbxtqfb": 0.5353,
|
||||
"9chuiarcjofld1dkj9kysehmb": 0.5346,
|
||||
"5aw6uyw4pz2bpj24t5z8aacim": 0.5333,
|
||||
"by5nibd18nkt40t0j8a0j5yzx": 0.5332,
|
||||
"4yzidekywejmxxp77gqmdgopg": 0.5323,
|
||||
"7ntvbsyq31jnzoqoa8850b9b8": 0.5305,
|
||||
"a7247po5qs29o3zsfmt222ydu": 0.5299,
|
||||
"117yqo02rs8dykkxpm274w3bd": 0.5298,
|
||||
"193wqkyb0v5jnsblhvd2ocmyo": 0.5296,
|
||||
"8jh0jejuxfhrpawnoztz2jlv4": 0.5295,
|
||||
"5y0z0l2epprzbscvzsgldw8vu": 0.5288,
|
||||
"47s2kt0e8m444ftqvsrqa3bvq": 0.5268,
|
||||
"2hj3286pqov1g1g59k2t2qcgm": 0.5245,
|
||||
"7swf4kpu3v38i2it4h94c5s9k": 0.5227,
|
||||
"78wml3z5wrfxe5iky50tiotgu": 0.5196,
|
||||
"f39uq10c8xhg5e6rwwcf6lhgc": 0.5186,
|
||||
"bbajzna018c79opa1kl5kmkqo": 0.5172,
|
||||
"4davonpqws4a4ejl1awu98zdg": 0.5168,
|
||||
"1fedahp0rws09tj451onten8r": 0.5163,
|
||||
"aho73e5udydy96iun3tkzdzsi": 0.5149,
|
||||
"3aa4mumjl6zyetg6o9hwd5hhx": 0.5125,
|
||||
"7cwemnr3vi40znjq451zxkus6": 0.5115,
|
||||
"ajm86skyzse4ym8g6fpgzncxa": 0.5112,
|
||||
"bgen5kjer2ytfp7lo9949t72g": 0.5102,
|
||||
"8ey0ww2zsosdmwr8ehsorh6t7": 0.51,
|
||||
"8najqkluatpaxvqws78b9s17c": 0.5082,
|
||||
"8v97rcbthsxmzqk4ufxws9mug": 0.506,
|
||||
"degxm4y6gmvp011ccyrev6z5p": 0.5049,
|
||||
"3oa9e03e7w9nr8kqwqc3tlqz9": 0.5049,
|
||||
"5dycj9wdhxh3n33qubw18ohlk": 0.5036,
|
||||
"3is4bkgf3loxv9qfg3hm8zfqb": 0.5033,
|
||||
"f47f3717z2vtpxfxrpdd4jl1x": 0.498,
|
||||
"8ivsfwex4dfx1tvgsiq8askcx": 0.4972,
|
||||
"8vbck9a4mxjms783lf72779uu": 0.4946,
|
||||
"aql5z4osw5wmun0emnakfpwji": 0.4946,
|
||||
"e6vzdkz6l236s9p288mharefy": 0.4925,
|
||||
"4nidzmunvpvxk1ir9b6m8mpay": 0.4874,
|
||||
"ein4fkggto3pdh5msp8huafiq": 0.4856,
|
||||
"1q4ab2bpg5e8jl1g2udnakrju": 0.4852,
|
||||
"8ztsv3pzrsyq5w1r3a0nfk1y5": 0.4842,
|
||||
"1qd0wvt30rlswa4g6nu4na660": 0.4826,
|
||||
"jznihqxle06xych9ygwiwnsa": 0.4796,
|
||||
"2y8bntiif3a9y6gtmauv30gt": 0.4782,
|
||||
"477yyajzheg2z8u7uick0e13e": 0.4706,
|
||||
"bockl24qpr7ryjl8b6obukga": 0.4671,
|
||||
"7mxwwunvot2pi69pj1yr1kh8i": 0.466,
|
||||
"3w1hkk9k9gr8fwssyn4icvdfo": 0.4657,
|
||||
"1txej2dzohnydl21zc9pgx6hy": 0.464,
|
||||
"b8rae0ib0frjmwlca429bq19q": 0.4624,
|
||||
"b5udgm9vakjqz8dcmy5b2g0xt": 0.4582,
|
||||
"eitf7hulqfv1clb7toewkil24": 0.458,
|
||||
"7hl0svs2hg225i2zud0g3xzp2": 0.4559,
|
||||
"2aso72utuctat2ecs6nahjss6": 0.4521,
|
||||
"3ymqchdzk8tt6lfphf26xfvh0": 0.4519,
|
||||
"2yyjcbbryf1r10apyzl7c7jvp": 0.4507,
|
||||
"bly7ema5au6j40i0grhl0pnub": 0.4476,
|
||||
"b1rveez5u792gess9w3e7v5le": 0.4444,
|
||||
"8sdpk4aerruf515yh76ezo7vi": 0.4434,
|
||||
"32vph7vcjqgo1ksj1548di90n": 0.44,
|
||||
"65ggsqdi6drpa4m8y3gkll25k": 0.4394,
|
||||
"xaouuwuk8qyhv1libkeexwjh": 0.4347,
|
||||
"6qitd9h242qkvjenaytfdnsf2": 0.4312,
|
||||
"duuc1qczfnawwncru1ly6o66": 0.4213,
|
||||
"b60nisd3qn427jm0hrg9kvmab": 0.4203,
|
||||
"xwnjb1az11zffwty3m6vn8y6": 0.4197,
|
||||
"dkarmrybx9vx10rg7cywumth0": 0.4158,
|
||||
"75434tz9rc14xkkvudex742ui": 0.4137,
|
||||
"c1d9p6b2e9zr5tqlzx3ktjplg": 0.4129,
|
||||
"b73zounsynk9d3u1p9nvpu7i2": 0.4049,
|
||||
"913mb508il6jzwtlj28fl892h": 0.4044,
|
||||
"e0lck99w8meo9qoalfrxgo33o": 0.401,
|
||||
"8dn0w8zh7nbn2i904603eigwf": 0.3984,
|
||||
"ddyrh5latwfhesgfh4w401n92": 0.3973,
|
||||
"avs3xposm3t9x1x2vzsoxzcbu": 0.3957,
|
||||
"eu2g5j36zzxiazpd729osx0wm": 0.3924,
|
||||
"67uya58idol2eq18ljecsru5o": 0.3912,
|
||||
"23e698ls3x6vi9x8wl0mz7bsa": 0.3838,
|
||||
"6321dlqv4ziuwqte4xpohijtw": 0.382,
|
||||
"8o5tv5viv4hy1qg9jp94k7ayb": 0.381,
|
||||
"53tknno09wqihmwxrqcuwq9sa": 0.3782,
|
||||
"82wo38rqeizxlfjjhfjy4rx7u": 0.3781,
|
||||
"dvtl8sf1262pd2aqgu641qa7u": 0.3767,
|
||||
"663a54fmymndjeev47qm7d3nf": 0.3522,
|
||||
"macko16888165594668885588": 0.3309,
|
||||
"macko16698982162572521585": 0.3262,
|
||||
"6lkj3o21cr4g7bql6tb3fk222": 0.3261,
|
||||
"cu0rmpyff5692eo06ltddjo8a": 0.3161,
|
||||
"1cnx2c8g3hhp8ssxnwwli0mjb": 0.3121,
|
||||
"4vt0ldrcl6thpxpcs8zmpdq1g": 0.2926,
|
||||
"etta63x1t7tnkn4jheisjwk4p": 0.2907,
|
||||
"1n9l0ex47bu0762qg574hzjtd": 0.2626,
|
||||
"6jgwiu2gq3dllmrwt45pfdn2z": 0.2416,
|
||||
"392slbmf1kdqlr6sd1ckt71rs": 0.24,
|
||||
"8z3180hhw2pj1i65uftlk54uz": 0.2096
|
||||
},
|
||||
"details": [
|
||||
{
|
||||
"league_id": "bx57cmq1edfq53ckfk791supi",
|
||||
"league_name": "CAF Konfederasyon Kupası",
|
||||
"match_count": 98,
|
||||
"brier_score": 0.3046,
|
||||
"heavy_fav_win_pct": 84.1,
|
||||
"fav_win_pct": 63.3,
|
||||
"odds_reliability": 0.9476
|
||||
},
|
||||
{
|
||||
"league_id": "55hcphd1ccc6eai1ms77460on",
|
||||
"league_name": "Şampiyonlar Ligi Kadınlar",
|
||||
"match_count": 89,
|
||||
"brier_score": 0.3258,
|
||||
"heavy_fav_win_pct": 83.3,
|
||||
"fav_win_pct": 74.2,
|
||||
"odds_reliability": 0.9445
|
||||
},
|
||||
{
|
||||
"league_id": "d9eaigzyfnfiraqc3ius757tl",
|
||||
"league_name": "Kupa",
|
||||
"match_count": 78,
|
||||
"brier_score": 0.3141,
|
||||
"heavy_fav_win_pct": 81.2,
|
||||
"fav_win_pct": 73.1,
|
||||
"odds_reliability": 0.9402
|
||||
},
|
||||
{
|
||||
"league_id": "1gxlzw2ezkyeykhcaa5x8ozkk",
|
||||
"league_name": "Concacaf Orta Amerika Kupası",
|
||||
"match_count": 88,
|
||||
"brier_score": 0.3338,
|
||||
"heavy_fav_win_pct": 79.4,
|
||||
"fav_win_pct": 61.4,
|
||||
"odds_reliability": 0.9259
|
||||
},
|
||||
{
|
||||
"league_id": "5jd0k2txwnq69frs79eulba8j",
|
||||
"league_name": "Kupa",
|
||||
"match_count": 69,
|
||||
"brier_score": 0.3223,
|
||||
"heavy_fav_win_pct": 78.4,
|
||||
"fav_win_pct": 66.7,
|
||||
"odds_reliability": 0.9233
|
||||
},
|
||||
{
|
||||
"league_id": "6694fff47wqxl10lrd9tb91f8",
|
||||
"league_name": "Kupa",
|
||||
"match_count": 55,
|
||||
"brier_score": 0.3099,
|
||||
"heavy_fav_win_pct": 78.8,
|
||||
"fav_win_pct": 67.3,
|
||||
"odds_reliability": 0.9193
|
||||
},
|
||||
{
|
||||
"league_id": "4jg7he1n3rb5dniq6hf49xorq",
|
||||
"league_name": "Premier Lig",
|
||||
"match_count": 79,
|
||||
"brier_score": 0.3333,
|
||||
"heavy_fav_win_pct": 77.1,
|
||||
"fav_win_pct": 64.6,
|
||||
"odds_reliability": 0.9061
|
||||
},
|
||||
{
|
||||
"league_id": "59tpnfrwnvhnhzmnvfyug68hj",
|
||||
"league_name": "Libertadores Kupası",
|
||||
"match_count": 180,
|
||||
"brier_score": 0.3408,
|
||||
"heavy_fav_win_pct": 76.2,
|
||||
"fav_win_pct": 61.7,
|
||||
"odds_reliability": 0.8988
|
||||
},
|
||||
{
|
||||
"league_id": "ac42gi3penartj88fe9l6plpk",
|
||||
"league_name": "Premier Lig",
|
||||
"match_count": 185,
|
||||
"brier_score": 0.3148,
|
||||
"heavy_fav_win_pct": 70.7,
|
||||
"fav_win_pct": 68.1,
|
||||
"odds_reliability": 0.8937
|
||||
},
|
||||
{
|
||||
"league_id": "3j81qr7yc4gdnakfwnxf95ovh",
|
||||
"league_name": "Premier Lig",
|
||||
"match_count": 106,
|
||||
"brier_score": 0.333,
|
||||
"heavy_fav_win_pct": 72.2,
|
||||
"fav_win_pct": 60.4,
|
||||
"odds_reliability": 0.8771
|
||||
},
|
||||
{
|
||||
"league_id": "9z5643nd06afqu01ea2wt8y4g",
|
||||
"league_name": "Kuu Bara Ligi",
|
||||
"match_count": 110,
|
||||
"brier_score": 0.3294,
|
||||
"heavy_fav_win_pct": 70.3,
|
||||
"fav_win_pct": 53.6,
|
||||
"odds_reliability": 0.8734
|
||||
},
|
||||
{
|
||||
"league_id": "482ofyysbdbeoxauk19yg7tdt",
|
||||
"league_name": "Trendyol Süper Lig",
|
||||
"match_count": 342,
|
||||
"brier_score": 0.3627,
|
||||
"heavy_fav_win_pct": 80.7,
|
||||
"fav_win_pct": 59.6,
|
||||
"odds_reliability": 0.8722
|
||||
},
|
||||
{
|
||||
"league_id": "ahl3vljaignq9ebaos4uqkrvo",
|
||||
"league_name": "Kupa",
|
||||
"match_count": 105,
|
||||
"brier_score": 0.331,
|
||||
"heavy_fav_win_pct": 70.4,
|
||||
"fav_win_pct": 63.8,
|
||||
"odds_reliability": 0.8696
|
||||
},
|
||||
{
|
||||
"league_id": "8x3sbh85gc8qir50utw39jl04",
|
||||
"league_name": "UEFA Kadınlar Euro 2025 Elemeleri",
|
||||
"match_count": 88,
|
||||
"brier_score": 0.3421,
|
||||
"heavy_fav_win_pct": 75.5,
|
||||
"fav_win_pct": 61.4,
|
||||
"odds_reliability": 0.865
|
||||
},
|
||||
{
|
||||
"league_id": "agpweohvn9tugnyl6ry4rhivp",
|
||||
"league_name": "Eredivisie Kadınlar",
|
||||
"match_count": 51,
|
||||
"brier_score": 0.3356,
|
||||
"heavy_fav_win_pct": 72.0,
|
||||
"fav_win_pct": 56.9,
|
||||
"odds_reliability": 0.8428
|
||||
},
|
||||
{
|
||||
"league_id": "4c1nfi2j1m731hcay25fcgndq",
|
||||
"league_name": "Avrupa Ligi",
|
||||
"match_count": 242,
|
||||
"brier_score": 0.3625,
|
||||
"heavy_fav_win_pct": 77.6,
|
||||
"fav_win_pct": 61.6,
|
||||
"odds_reliability": 0.8425
|
||||
},
|
||||
{
|
||||
"league_id": "1j4ehtrbry9depwt6oghaq3lu",
|
||||
"league_name": "Süper Lig",
|
||||
"match_count": 84,
|
||||
"brier_score": 0.3201,
|
||||
"heavy_fav_win_pct": 65.9,
|
||||
"fav_win_pct": 60.7,
|
||||
"odds_reliability": 0.8299
|
||||
},
|
||||
{
|
||||
"league_id": "40yjcbx2sq6oq736iqqqczwt1",
|
||||
"league_name": "DK Elemeler",
|
||||
"match_count": 88,
|
||||
"brier_score": 0.3383,
|
||||
"heavy_fav_win_pct": 68.6,
|
||||
"fav_win_pct": 55.7,
|
||||
"odds_reliability": 0.8237
|
||||
},
|
||||
{
|
||||
"league_id": "145hkd59i6foieuwr4mwi6wlq",
|
||||
"league_name": "Pro Lig",
|
||||
"match_count": 143,
|
||||
"brier_score": 0.3546,
|
||||
"heavy_fav_win_pct": 73.8,
|
||||
"fav_win_pct": 60.1,
|
||||
"odds_reliability": 0.823
|
||||
},
|
||||
{
|
||||
"league_id": "34pl8szyvrbwcmfkuocjm3r6t",
|
||||
"league_name": "LaLiga",
|
||||
"match_count": 364,
|
||||
"brier_score": 0.3773,
|
||||
"heavy_fav_win_pct": 80.2,
|
||||
"fav_win_pct": 56.6,
|
||||
"odds_reliability": 0.8227
|
||||
},
|
||||
{
|
||||
"league_id": "cse5oqqt2pzfcy8uz6yz3tkbj",
|
||||
"league_name": "CAF Şampiyonlar Ligi",
|
||||
"match_count": 91,
|
||||
"brier_score": 0.3513,
|
||||
"heavy_fav_win_pct": 73.9,
|
||||
"fav_win_pct": 57.1,
|
||||
"odds_reliability": 0.8212
|
||||
},
|
||||
{
|
||||
"league_id": "zs18qaehvhg3w1208874zvfa",
|
||||
"league_name": "1. Lig",
|
||||
"match_count": 225,
|
||||
"brier_score": 0.3744,
|
||||
"heavy_fav_win_pct": 82.1,
|
||||
"fav_win_pct": 59.6,
|
||||
"odds_reliability": 0.8176
|
||||
},
|
||||
{
|
||||
"league_id": "57nu0wygurzkp6fuy5hhrtaa2",
|
||||
"league_name": "1. Lig",
|
||||
"match_count": 286,
|
||||
"brier_score": 0.3626,
|
||||
"heavy_fav_win_pct": 72.9,
|
||||
"fav_win_pct": 59.1,
|
||||
"odds_reliability": 0.8099
|
||||
},
|
||||
{
|
||||
"league_id": "1eruend45vd20g9hbrpiggs5u",
|
||||
"league_name": "Botola Pro",
|
||||
"match_count": 265,
|
||||
"brier_score": 0.3625,
|
||||
"heavy_fav_win_pct": 72.9,
|
||||
"fav_win_pct": 50.2,
|
||||
"odds_reliability": 0.8083
|
||||
},
|
||||
{
|
||||
"league_id": "595nsvo7ykvoe690b1e4u5n56",
|
||||
"league_name": "UEFA Uluslar Ligi",
|
||||
"match_count": 67,
|
||||
"brier_score": 0.3687,
|
||||
"heavy_fav_win_pct": 83.3,
|
||||
"fav_win_pct": 50.7,
|
||||
"odds_reliability": 0.7987
|
||||
},
|
||||
{
|
||||
"league_id": "6vq8j5p3av14nr3iuyi4okhjt",
|
||||
"league_name": "Süper Lig Kadınlar",
|
||||
"match_count": 70,
|
||||
"brier_score": 0.356,
|
||||
"heavy_fav_win_pct": 73.5,
|
||||
"fav_win_pct": 58.6,
|
||||
"odds_reliability": 0.793
|
||||
},
|
||||
{
|
||||
"league_id": "486rhdgz7yc0sygziht7hje65",
|
||||
"league_name": "Kupa",
|
||||
"match_count": 62,
|
||||
"brier_score": 0.3704,
|
||||
"heavy_fav_win_pct": 81.1,
|
||||
"fav_win_pct": 66.1,
|
||||
"odds_reliability": 0.7901
|
||||
},
|
||||
{
|
||||
"league_id": "9hh6n2f84k31zmlcxyvmc1w2y",
|
||||
"league_name": "2. Lig",
|
||||
"match_count": 204,
|
||||
"brier_score": 0.357,
|
||||
"heavy_fav_win_pct": 69.2,
|
||||
"fav_win_pct": 62.3,
|
||||
"odds_reliability": 0.789
|
||||
},
|
||||
{
|
||||
"league_id": "3n5046abeu3x482ds3jwda238",
|
||||
"league_name": "WE Lig Kadınlar",
|
||||
"match_count": 102,
|
||||
"brier_score": 0.3761,
|
||||
"heavy_fav_win_pct": 85.4,
|
||||
"fav_win_pct": 58.8,
|
||||
"odds_reliability": 0.7863
|
||||
},
|
||||
{
|
||||
"league_id": "8yi6ejjd1zudcqtbn07haahg6",
|
||||
"league_name": "Premier Lig",
|
||||
"match_count": 302,
|
||||
"brier_score": 0.3712,
|
||||
"heavy_fav_win_pct": 72.1,
|
||||
"fav_win_pct": 56.3,
|
||||
"odds_reliability": 0.7752
|
||||
},
|
||||
{
|
||||
"league_id": "byhmntnl1b4lxw0zz21im3zkd",
|
||||
"league_name": "Kupa",
|
||||
"match_count": 96,
|
||||
"brier_score": 0.3528,
|
||||
"heavy_fav_win_pct": 68.2,
|
||||
"fav_win_pct": 58.3,
|
||||
"odds_reliability": 0.7719
|
||||
},
|
||||
{
|
||||
"league_id": "2bmwykmdlcc2u1c40ytoc39vy",
|
||||
"league_name": "Açık Kupası",
|
||||
"match_count": 93,
|
||||
"brier_score": 0.3807,
|
||||
"heavy_fav_win_pct": 84.6,
|
||||
"fav_win_pct": 66.7,
|
||||
"odds_reliability": 0.7668
|
||||
},
|
||||
{
|
||||
"league_id": "82jkgccg7phfjpd0mltdl3pat",
|
||||
"league_name": "Süper Lig",
|
||||
"match_count": 289,
|
||||
"brier_score": 0.3782,
|
||||
"heavy_fav_win_pct": 74.0,
|
||||
"fav_win_pct": 57.4,
|
||||
"odds_reliability": 0.7643
|
||||
},
|
||||
{
|
||||
"league_id": "2nttcoriwf5co73vmz1vr8frm",
|
||||
"league_name": "Nesine 2. Lig",
|
||||
"match_count": 525,
|
||||
"brier_score": 0.3782,
|
||||
"heavy_fav_win_pct": 71.8,
|
||||
"fav_win_pct": 55.2,
|
||||
"odds_reliability": 0.7641
|
||||
},
|
||||
{
|
||||
"league_id": "dr2xk7muj8aqcjdz2b3li1c0k",
|
||||
"league_name": "Meistaradeildin",
|
||||
"match_count": 129,
|
||||
"brier_score": 0.3714,
|
||||
"heavy_fav_win_pct": 73.6,
|
||||
"fav_win_pct": 61.2,
|
||||
"odds_reliability": 0.759
|
||||
},
|
||||
{
|
||||
"league_id": "4yngyfinzd6bb1k7anqtqs0wt",
|
||||
"league_name": "Premier Lig",
|
||||
"match_count": 195,
|
||||
"brier_score": 0.3772,
|
||||
"heavy_fav_win_pct": 74.4,
|
||||
"fav_win_pct": 57.4,
|
||||
"odds_reliability": 0.7586
|
||||
},
|
||||
{
|
||||
"league_id": "eog6knrkfei68si736fpquyzc",
|
||||
"league_name": "Lig Kupası",
|
||||
"match_count": 120,
|
||||
"brier_score": 0.3632,
|
||||
"heavy_fav_win_pct": 69.9,
|
||||
"fav_win_pct": 66.7,
|
||||
"odds_reliability": 0.756
|
||||
},
|
||||
{
|
||||
"league_id": "eg6s9f1jj7jr6stmbosn0g6c8",
|
||||
"league_name": "Süper Lig",
|
||||
"match_count": 108,
|
||||
"brier_score": 0.3657,
|
||||
"heavy_fav_win_pct": 71.2,
|
||||
"fav_win_pct": 55.6,
|
||||
"odds_reliability": 0.7538
|
||||
},
|
||||
{
|
||||
"league_id": "ae1wva3zrzcp2zd15gpvsntg6",
|
||||
"league_name": "Ulusal Lig",
|
||||
"match_count": 278,
|
||||
"brier_score": 0.3783,
|
||||
"heavy_fav_win_pct": 72.7,
|
||||
"fav_win_pct": 55.0,
|
||||
"odds_reliability": 0.7517
|
||||
},
|
||||
{
|
||||
"league_id": "cesdwwnxbc5fmajgroc0hqzy2",
|
||||
"league_name": "Hazırlık Maçları Ülkeler",
|
||||
"match_count": 235,
|
||||
"brier_score": 0.3669,
|
||||
"heavy_fav_win_pct": 67.6,
|
||||
"fav_win_pct": 56.2,
|
||||
"odds_reliability": 0.7466
|
||||
},
|
||||
{
|
||||
"league_id": "8k1xcsyvxapl4jlsluh3eomre",
|
||||
"league_name": "Premier Lig",
|
||||
"match_count": 328,
|
||||
"brier_score": 0.385,
|
||||
"heavy_fav_win_pct": 74.2,
|
||||
"fav_win_pct": 45.7,
|
||||
"odds_reliability": 0.7463
|
||||
},
|
||||
{
|
||||
"league_id": "bdtat25m14jy85y484z3e6lf",
|
||||
"league_name": "Kupa",
|
||||
"match_count": 90,
|
||||
"brier_score": 0.3772,
|
||||
"heavy_fav_win_pct": 75.7,
|
||||
"fav_win_pct": 55.6,
|
||||
"odds_reliability": 0.7437
|
||||
},
|
||||
{
|
||||
"league_id": "iu1vi94p4p28oozl1h9bvplr",
|
||||
"league_name": "1. Lig",
|
||||
"match_count": 158,
|
||||
"brier_score": 0.3729,
|
||||
"heavy_fav_win_pct": 71.2,
|
||||
"fav_win_pct": 50.0,
|
||||
"odds_reliability": 0.7411
|
||||
},
|
||||
{
|
||||
"league_id": "1r097lpxe0xn03ihb7wi98kao",
|
||||
"league_name": "Serie A",
|
||||
"match_count": 359,
|
||||
"brier_score": 0.3732,
|
||||
"heavy_fav_win_pct": 67.8,
|
||||
"fav_win_pct": 56.5,
|
||||
"odds_reliability": 0.7391
|
||||
},
|
||||
{
|
||||
"league_id": "2kwbbcootiqqgmrzs6o5inle5",
|
||||
"league_name": "Premier Lig",
|
||||
"match_count": 369,
|
||||
"brier_score": 0.3791,
|
||||
"heavy_fav_win_pct": 70.2,
|
||||
"fav_win_pct": 54.2,
|
||||
"odds_reliability": 0.7386
|
||||
},
|
||||
{
|
||||
"league_id": "9fuwphq8kvugrlc3ckm7k8wes",
|
||||
"league_name": "Ligler Kupası",
|
||||
"match_count": 143,
|
||||
"brier_score": 0.3934,
|
||||
"heavy_fav_win_pct": 81.6,
|
||||
"fav_win_pct": 50.3,
|
||||
"odds_reliability": 0.7358
|
||||
},
|
||||
{
|
||||
"league_id": "civf31q1inxohs4a03y8reetf",
|
||||
"league_name": "Premier Lig",
|
||||
"match_count": 320,
|
||||
"brier_score": 0.3721,
|
||||
"heavy_fav_win_pct": 67.2,
|
||||
"fav_win_pct": 57.8,
|
||||
"odds_reliability": 0.735
|
||||
},
|
||||
{
|
||||
"league_id": "ili150pwfuf39f7yfdch9lhw",
|
||||
"league_name": "UEFA U21 Şampiyonası Elemeler",
|
||||
"match_count": 112,
|
||||
"brier_score": 0.3715,
|
||||
"heavy_fav_win_pct": 70.4,
|
||||
"fav_win_pct": 67.9,
|
||||
"odds_reliability": 0.7286
|
||||
},
|
||||
{
|
||||
"league_id": "abs7n2ae3oydilk0tgmpnsj89",
|
||||
"league_name": "Azadegan Ligi",
|
||||
"match_count": 217,
|
||||
"brier_score": 0.3801,
|
||||
"heavy_fav_win_pct": 71.4,
|
||||
"fav_win_pct": 45.2,
|
||||
"odds_reliability": 0.7277
|
||||
},
|
||||
{
|
||||
"league_id": "9nbpdi9q3ywcm4q0j5u0ekwcq",
|
||||
"league_name": "Serie D",
|
||||
"match_count": 232,
|
||||
"brier_score": 0.3718,
|
||||
"heavy_fav_win_pct": 67.2,
|
||||
"fav_win_pct": 54.7,
|
||||
"odds_reliability": 0.7254
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,243 @@
|
||||
"""
|
||||
V27 Rolling Window Feature Calculator
|
||||
======================================
|
||||
Computes rolling averages over 5/10/20 match windows,
|
||||
with home/away splits and trend detection.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from typing import Dict, List, Tuple
|
||||
import math
|
||||
|
||||
|
||||
def calc_rolling_features(
|
||||
team_matches: List[Tuple], # [(mst, is_home, team_goals, opp_goals, opp_id), ...]
|
||||
before_date: int,
|
||||
team_is_home: bool,
|
||||
) -> Dict[str, float]:
|
||||
"""Calculate rolling window features for a team before a given date."""
|
||||
valid = [m for m in team_matches if m[0] < before_date]
|
||||
|
||||
defaults = {
|
||||
"rolling5_goals_avg": 1.3, "rolling5_conceded_avg": 1.2,
|
||||
"rolling10_goals_avg": 1.3, "rolling10_conceded_avg": 1.2,
|
||||
"rolling20_goals_avg": 1.3, "rolling20_conceded_avg": 1.2,
|
||||
"rolling5_clean_sheets": 0.25,
|
||||
"venue_goals_avg": 1.3, "venue_conceded_avg": 1.2,
|
||||
"goal_trend": 0.0,
|
||||
}
|
||||
|
||||
if len(valid) < 3:
|
||||
return defaults
|
||||
|
||||
result = {}
|
||||
|
||||
for window in [5, 10, 20]:
|
||||
recent = valid[-window:] if len(valid) >= window else valid
|
||||
n = len(recent)
|
||||
g_sum = sum(m[2] for m in recent)
|
||||
c_sum = sum(m[3] for m in recent)
|
||||
result[f"rolling{window}_goals_avg"] = g_sum / n
|
||||
result[f"rolling{window}_conceded_avg"] = c_sum / n
|
||||
|
||||
# Clean sheet rate (last 5)
|
||||
r5 = valid[-5:] if len(valid) >= 5 else valid
|
||||
result["rolling5_clean_sheets"] = sum(1 for m in r5 if m[3] == 0) / len(r5)
|
||||
|
||||
# Venue-specific (home-only or away-only)
|
||||
venue_matches = [m for m in valid if m[1] == team_is_home]
|
||||
if venue_matches:
|
||||
vm = venue_matches[-10:] if len(venue_matches) >= 10 else venue_matches
|
||||
result["venue_goals_avg"] = sum(m[2] for m in vm) / len(vm)
|
||||
result["venue_conceded_avg"] = sum(m[3] for m in vm) / len(vm)
|
||||
else:
|
||||
result["venue_goals_avg"] = defaults["venue_goals_avg"]
|
||||
result["venue_conceded_avg"] = defaults["venue_conceded_avg"]
|
||||
|
||||
# Goal trend: compare last 3 vs previous 3
|
||||
if len(valid) >= 6:
|
||||
last3 = sum(m[2] for m in valid[-3:]) / 3
|
||||
prev3 = sum(m[2] for m in valid[-6:-3]) / 3
|
||||
result["goal_trend"] = last3 - prev3
|
||||
else:
|
||||
result["goal_trend"] = 0.0
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def calc_league_quality(
|
||||
all_matches: List[Tuple], # all FT matches in this league
|
||||
) -> Dict[str, float]:
|
||||
"""Calculate league-level quality features."""
|
||||
defaults = {
|
||||
"league_home_win_rate": 0.45,
|
||||
"league_draw_rate": 0.25,
|
||||
"league_btts_rate": 0.50,
|
||||
"league_ou25_rate": 0.50,
|
||||
"league_reliability_score": 0.50,
|
||||
}
|
||||
|
||||
if len(all_matches) < 20:
|
||||
return defaults
|
||||
|
||||
n = len(all_matches)
|
||||
home_wins = sum(1 for m in all_matches if m[2] > m[3])
|
||||
draws = sum(1 for m in all_matches if m[2] == m[3])
|
||||
btts = sum(1 for m in all_matches if m[2] > 0 and m[3] > 0)
|
||||
ou25 = sum(1 for m in all_matches if (m[2] + m[3]) > 2.5)
|
||||
|
||||
hw_rate = home_wins / n
|
||||
dr_rate = draws / n
|
||||
btts_rate = btts / n
|
||||
ou25_rate = ou25 / n
|
||||
|
||||
# Reliability: leagues closer to averages are more predictable
|
||||
predictability = 1.0 - abs(hw_rate - 0.45) - abs(dr_rate - 0.27) * 0.5
|
||||
reliability = max(0.2, min(0.95, predictability))
|
||||
|
||||
return {
|
||||
"league_home_win_rate": round(hw_rate, 4),
|
||||
"league_draw_rate": round(dr_rate, 4),
|
||||
"league_btts_rate": round(btts_rate, 4),
|
||||
"league_ou25_rate": round(ou25_rate, 4),
|
||||
"league_reliability_score": round(reliability, 4),
|
||||
}
|
||||
|
||||
|
||||
def calc_time_features(
|
||||
team_matches: List[Tuple],
|
||||
match_mst: int,
|
||||
) -> Dict[str, float]:
|
||||
"""Calculate time-based features."""
|
||||
from datetime import datetime
|
||||
|
||||
# Days since last match
|
||||
valid = [m for m in team_matches if m[0] < match_mst]
|
||||
if valid:
|
||||
last_mst = valid[-1][0]
|
||||
days_rest = (match_mst - last_mst) / 86_400_000 # ms to days
|
||||
days_rest = min(days_rest, 60.0) # cap at 60 days
|
||||
else:
|
||||
days_rest = 14.0
|
||||
|
||||
# Month and season flags
|
||||
try:
|
||||
dt = datetime.utcfromtimestamp(match_mst / 1000)
|
||||
month = dt.month
|
||||
is_season_start = 1.0 if month in (7, 8) else 0.0
|
||||
is_season_end = 1.0 if month in (5, 6) else 0.0
|
||||
except Exception:
|
||||
month = 6
|
||||
is_season_start = 0.0
|
||||
is_season_end = 0.0
|
||||
|
||||
return {
|
||||
"days_rest": round(days_rest, 2),
|
||||
"match_month": month,
|
||||
"is_season_start": is_season_start,
|
||||
"is_season_end": is_season_end,
|
||||
}
|
||||
|
||||
|
||||
def calc_advanced_h2h(
|
||||
team_matches: List[Tuple],
|
||||
home_id: int,
|
||||
away_id: int,
|
||||
before_date: int,
|
||||
) -> Dict[str, float]:
|
||||
"""Calculate advanced H2H features."""
|
||||
defaults = {
|
||||
"h2h_home_goals_avg": 1.3,
|
||||
"h2h_away_goals_avg": 1.1,
|
||||
"h2h_recent_trend": 0.0,
|
||||
"h2h_venue_advantage": 0.0,
|
||||
}
|
||||
|
||||
h2h = [m for m in team_matches if m[4] == away_id and m[0] < before_date]
|
||||
if not h2h:
|
||||
return defaults
|
||||
|
||||
recent = h2h[-10:]
|
||||
home_goals_total = 0
|
||||
away_goals_total = 0
|
||||
venue_home_wins = 0
|
||||
venue_total = 0
|
||||
|
||||
for mst, is_home, team_goals, opp_goals, _ in recent:
|
||||
if is_home:
|
||||
home_goals_total += team_goals
|
||||
away_goals_total += opp_goals
|
||||
venue_total += 1
|
||||
if team_goals > opp_goals:
|
||||
venue_home_wins += 1
|
||||
else:
|
||||
home_goals_total += opp_goals
|
||||
away_goals_total += team_goals
|
||||
|
||||
n = len(recent)
|
||||
result = {
|
||||
"h2h_home_goals_avg": home_goals_total / n,
|
||||
"h2h_away_goals_avg": away_goals_total / n,
|
||||
"h2h_venue_advantage": venue_home_wins / venue_total if venue_total > 0 else 0.5,
|
||||
}
|
||||
|
||||
# Recent trend: last 3 vs overall
|
||||
if len(h2h) >= 4:
|
||||
last3_pts = sum(
|
||||
1.0 if m[2] > m[3] else (0.5 if m[2] == m[3] else 0.0)
|
||||
for m in h2h[-3:]
|
||||
) / 3
|
||||
overall_pts = sum(
|
||||
1.0 if m[2] > m[3] else (0.5 if m[2] == m[3] else 0.0)
|
||||
for m in h2h
|
||||
) / len(h2h)
|
||||
result["h2h_recent_trend"] = round(last3_pts - overall_pts, 4)
|
||||
else:
|
||||
result["h2h_recent_trend"] = 0.0
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def calc_strength_diff(
|
||||
home_form: Dict[str, float],
|
||||
away_form: Dict[str, float],
|
||||
home_elo: Dict[str, float],
|
||||
away_elo: Dict[str, float],
|
||||
home_momentum: float,
|
||||
away_momentum: float,
|
||||
upset_potential: float,
|
||||
) -> Dict[str, float]:
|
||||
"""Calculate strength differential features."""
|
||||
# Attack vs Defense mismatches
|
||||
h_attack = home_form.get("goals_avg", 1.3)
|
||||
a_defense = away_form.get("conceded_avg", 1.2)
|
||||
a_attack = away_form.get("goals_avg", 1.3)
|
||||
h_defense = home_form.get("conceded_avg", 1.2)
|
||||
|
||||
atk_def_home = h_attack - a_defense # positive = home attack > away defense
|
||||
atk_def_away = a_attack - h_defense
|
||||
|
||||
# XG diff approximation
|
||||
xg_diff = (h_attack + a_defense) / 2 - (a_attack + h_defense) / 2
|
||||
|
||||
# Form × Momentum interaction
|
||||
form_mom = (home_momentum - away_momentum) * (
|
||||
home_form.get("scoring_rate", 0.75) - away_form.get("scoring_rate", 0.75)
|
||||
)
|
||||
|
||||
# ELO-Form consistency
|
||||
elo_diff = home_elo.get("overall", 1500) - away_elo.get("overall", 1500)
|
||||
form_diff = h_attack - a_attack
|
||||
elo_form_consistency = 1.0 if (elo_diff > 0 and form_diff > 0) or (elo_diff < 0 and form_diff < 0) else 0.0
|
||||
|
||||
# Upset × ELO gap
|
||||
elo_gap = abs(elo_diff)
|
||||
upset_x_elo = upset_potential * (elo_gap / 400.0)
|
||||
|
||||
return {
|
||||
"attack_vs_defense_home": round(atk_def_home, 4),
|
||||
"attack_vs_defense_away": round(atk_def_away, 4),
|
||||
"xg_diff": round(xg_diff, 4),
|
||||
"form_momentum_interaction": round(form_mom, 4),
|
||||
"elo_form_consistency": elo_form_consistency,
|
||||
"upset_x_elo_gap": round(upset_x_elo, 4),
|
||||
}
|
||||
@@ -15,13 +15,9 @@ Orijinal Faktörler:
|
||||
- Tarihsel upset pattern
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from typing import Dict, Any, Optional, Tuple, List
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
try:
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
|
||||
+199
-29
@@ -7,16 +7,24 @@ import time
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Any
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import uvicorn
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
import subprocess
|
||||
from pydantic import BaseModel
|
||||
|
||||
try:
|
||||
from models.basketball_v25 import get_basketball_v25_predictor
|
||||
HAS_BASKETBALL = True
|
||||
except ImportError:
|
||||
HAS_BASKETBALL = False
|
||||
from services.single_match_orchestrator import get_single_match_orchestrator
|
||||
from data.database import dispose_engine
|
||||
from services.v26_shadow_engine import get_v26_shadow_engine
|
||||
from models.league_model import get_league_model_loader
|
||||
|
||||
load_dotenv()
|
||||
|
||||
@@ -33,12 +41,30 @@ class CouponRequest(BaseModel):
|
||||
min_confidence: float | None = None
|
||||
|
||||
|
||||
class RetrainRequest(BaseModel):
|
||||
reason: str | None = "manual"
|
||||
markets: str | None = None # comma-separated, e.g. "MS,OU25,BTTS"
|
||||
trials: int | None = 50
|
||||
|
||||
|
||||
# ─── Retrain state tracking ──────────────────────────────────
|
||||
_retrain_state: dict[str, Any] = {
|
||||
"running": False,
|
||||
"last_started": None,
|
||||
"last_completed": None,
|
||||
"last_status": None,
|
||||
"last_error": None,
|
||||
"pid": None,
|
||||
}
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(_: FastAPI):
|
||||
try:
|
||||
print("🚀 Initializing V25 orchestrator...", flush=True)
|
||||
print("🚀 Initializing V28 orchestrator...", flush=True)
|
||||
get_single_match_orchestrator()
|
||||
print("✅ V25 orchestrator ready", flush=True)
|
||||
get_v26_shadow_engine()
|
||||
print("✅ V28 orchestrator ready", flush=True)
|
||||
except Exception as error:
|
||||
print(f"❌ Failed to initialize orchestrator: {error}", flush=True)
|
||||
import traceback
|
||||
@@ -47,14 +73,11 @@ async def lifespan(_: FastAPI):
|
||||
|
||||
yield
|
||||
|
||||
# Cleanup async DB connections on shutdown
|
||||
await dispose_engine()
|
||||
|
||||
|
||||
app = FastAPI(
|
||||
title="Suggest-Bet AI Engine",
|
||||
version="25.0.0",
|
||||
description="V25 Single Match Prediction Package API",
|
||||
version="28.0.0",
|
||||
description="V28 Single Match Prediction Package API",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
@@ -102,8 +125,9 @@ async def global_exception_handler(_: Request, exc: Exception):
|
||||
@app.get("/")
|
||||
def read_root() -> dict[str, Any]:
|
||||
return {
|
||||
"status": "Suggest-Bet AI Engine v25",
|
||||
"engine": "V25 Single Match Orchestrator",
|
||||
"status": "Suggest-Bet AI Engine v28",
|
||||
"engine": "V28 Single Match Orchestrator",
|
||||
"mode": os.getenv("AI_ENGINE_MODE", "v28"),
|
||||
"routes": [
|
||||
"POST /v20plus/analyze/{match_id}",
|
||||
"GET /v20plus/analyze-htms/{match_id}",
|
||||
@@ -111,6 +135,8 @@ def read_root() -> dict[str, Any]:
|
||||
"GET /v20plus/reversal-watchlist",
|
||||
"POST /v20plus/coupon",
|
||||
"GET /v20plus/daily-banker",
|
||||
"POST /v1/admin/retrain",
|
||||
"GET /v1/admin/retrain/status",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -118,33 +144,70 @@ def read_root() -> dict[str, Any]:
|
||||
@app.get("/health")
|
||||
def health_check() -> dict[str, Any]:
|
||||
try:
|
||||
get_single_match_orchestrator()
|
||||
orchestrator = get_single_match_orchestrator()
|
||||
shadow_engine = get_v26_shadow_engine()
|
||||
|
||||
# Per-market V25 model status
|
||||
v25_readiness: dict[str, Any] = {"fully_loaded": False}
|
||||
try:
|
||||
v25_predictor = orchestrator._get_v25_predictor()
|
||||
v25_readiness = v25_predictor.readiness_summary()
|
||||
except Exception as v25_err:
|
||||
v25_readiness = {"fully_loaded": False, "error": str(v25_err)}
|
||||
|
||||
if HAS_BASKETBALL:
|
||||
basketball_predictor = get_basketball_v25_predictor()
|
||||
basketball_readiness = basketball_predictor.readiness_summary()
|
||||
ready = bool(basketball_readiness["fully_loaded"])
|
||||
ready = bool(basketball_readiness.get("fully_loaded", True))
|
||||
else:
|
||||
basketball_readiness = {"fully_loaded": False, "error": "Basketball module not found"}
|
||||
ready = True
|
||||
|
||||
league_readiness = get_league_model_loader().readiness_summary()
|
||||
overall_ready = ready and v25_readiness.get("fully_loaded", False)
|
||||
return {
|
||||
"status": "healthy" if ready else "degraded",
|
||||
"engine": "v25.main",
|
||||
"ready": ready,
|
||||
"status": "healthy" if overall_ready else "degraded",
|
||||
"engine": "v28.main",
|
||||
"mode": os.getenv("AI_ENGINE_MODE", "v28"),
|
||||
"ready": overall_ready,
|
||||
"v25_football": v25_readiness,
|
||||
"league_specific": league_readiness,
|
||||
"basketball_v25": basketball_readiness,
|
||||
"v26_shadow": shadow_engine.readiness_summary(),
|
||||
"prediction_service_ready": True,
|
||||
"model_loaded": overall_ready,
|
||||
"orchestrator_mode": getattr(orchestrator, "engine_mode", "v28"),
|
||||
}
|
||||
except Exception as error:
|
||||
return {"status": "unhealthy", "ready": False, "error": str(error)}
|
||||
|
||||
|
||||
_REQUIRED_RESPONSE_FIELDS = ("match_info", "market_board", "main_pick", "bet_summary", "data_quality")
|
||||
|
||||
|
||||
@app.post("/v20plus/analyze/{match_id}")
|
||||
async def analyze_match_v20plus(match_id: str) -> dict[str, Any]:
|
||||
started_at = time.time()
|
||||
orchestrator = get_single_match_orchestrator()
|
||||
result = orchestrator.analyze_match(match_id)
|
||||
result = await asyncio.to_thread(orchestrator.analyze_match, match_id)
|
||||
elapsed_ms = int((time.time() - started_at) * 1000)
|
||||
|
||||
if not result:
|
||||
raise HTTPException(status_code=404, detail=f"Match not found: {match_id}")
|
||||
|
||||
# Response validation: log missing required fields (non-fatal)
|
||||
missing_fields = [f for f in _REQUIRED_RESPONSE_FIELDS if f not in result]
|
||||
if missing_fields:
|
||||
print(f"⚠️ [API] analyze/{match_id} response missing fields: {missing_fields} ({elapsed_ms}ms)")
|
||||
|
||||
result["timing_ms"] = elapsed_ms
|
||||
return result
|
||||
|
||||
|
||||
@app.get("/v20plus/analyze-htms/{match_id}")
|
||||
async def analyze_match_htms_v20plus(match_id: str) -> dict[str, Any]:
|
||||
orchestrator = get_single_match_orchestrator()
|
||||
result = orchestrator.analyze_match_htms(match_id)
|
||||
result = await asyncio.to_thread(orchestrator.analyze_match_htms, match_id)
|
||||
if not result:
|
||||
raise HTTPException(status_code=404, detail=f"Match not found: {match_id}")
|
||||
return result
|
||||
@@ -196,7 +259,7 @@ async def analyze_match_htft_v20plus(match_id: str, timeout_sec: int = 30) -> di
|
||||
key=lambda item: float(item[1]),
|
||||
)
|
||||
return {
|
||||
"engine": "v25.main",
|
||||
"engine": "v28.main",
|
||||
"match_info": result.get("match_info", {}),
|
||||
"timing_ms": int((time.time() - started_at) * 1000),
|
||||
"ht_ft_probs": htft_probs,
|
||||
@@ -215,11 +278,12 @@ async def analyze_match_htft_v20plus(match_id: str, timeout_sec: int = 30) -> di
|
||||
@app.post("/v20plus/coupon")
|
||||
async def generate_coupon_v20plus(request: CouponRequest) -> dict[str, Any]:
|
||||
orchestrator = get_single_match_orchestrator()
|
||||
return orchestrator.build_coupon(
|
||||
match_ids=request.match_ids,
|
||||
strategy=request.strategy or "BALANCED",
|
||||
max_matches=request.max_matches,
|
||||
min_confidence=request.min_confidence,
|
||||
return await asyncio.to_thread(
|
||||
orchestrator.build_coupon,
|
||||
request.match_ids,
|
||||
request.strategy or "BALANCED",
|
||||
request.max_matches,
|
||||
request.min_confidence,
|
||||
)
|
||||
|
||||
|
||||
@@ -229,7 +293,7 @@ async def get_daily_banker_v20plus(count: int = 3) -> dict[str, Any]:
|
||||
raise HTTPException(status_code=400, detail="count must be >= 1")
|
||||
|
||||
orchestrator = get_single_match_orchestrator()
|
||||
bankers = orchestrator.get_daily_bankers(count=count)
|
||||
bankers = await asyncio.to_thread(orchestrator.get_daily_bankers, count)
|
||||
return {"count": len(bankers), "bankers": bankers}
|
||||
|
||||
@app.get("/v20plus/reversal-watchlist")
|
||||
@@ -247,14 +311,120 @@ async def get_reversal_watchlist_v20plus(
|
||||
raise HTTPException(status_code=400, detail="min_score must be between 0 and 100")
|
||||
|
||||
orchestrator = get_single_match_orchestrator()
|
||||
return orchestrator.get_reversal_watchlist(
|
||||
count=count,
|
||||
horizon_hours=horizon_hours,
|
||||
min_score=min_score,
|
||||
top_leagues_only=top_leagues_only,
|
||||
return await asyncio.to_thread(
|
||||
orchestrator.get_reversal_watchlist,
|
||||
count,
|
||||
horizon_hours,
|
||||
min_score,
|
||||
top_leagues_only,
|
||||
)
|
||||
|
||||
|
||||
# ─── ADMIN: Retrain Pipeline ─────────────────────────────────
|
||||
|
||||
def _run_retrain_pipeline(markets: str | None, trials: int):
|
||||
"""Background function: extract data → train model → reload."""
|
||||
global _retrain_state
|
||||
ai_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
scripts_dir = os.path.join(ai_dir, "scripts")
|
||||
python = os.path.join(ai_dir, "venv", "bin", "python3")
|
||||
if not os.path.exists(python):
|
||||
python = sys.executable # fallback
|
||||
|
||||
try:
|
||||
# Step 1: Extract training data
|
||||
print("🔄 [RETRAIN] Step 1/3: Extracting training data...", flush=True)
|
||||
result = subprocess.run(
|
||||
[python, os.path.join(scripts_dir, "extract_training_data.py")],
|
||||
capture_output=True, text=True, timeout=600, cwd=ai_dir,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"Extract failed:\n{result.stderr[-500:]}")
|
||||
print(f"✅ [RETRAIN] Extract done", flush=True)
|
||||
|
||||
# Step 2: Train V25 Pro
|
||||
print("🔄 [RETRAIN] Step 2/3: Training V25 Pro model...", flush=True)
|
||||
train_cmd = [python, os.path.join(scripts_dir, "train_v25_pro.py")]
|
||||
if markets:
|
||||
train_cmd += ["--markets", markets]
|
||||
train_cmd += ["--trials", str(trials)]
|
||||
|
||||
result = subprocess.run(
|
||||
train_cmd, capture_output=True, text=True, timeout=3600, cwd=ai_dir,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"Training failed:\n{result.stderr[-500:]}")
|
||||
print(f"✅ [RETRAIN] Training done", flush=True)
|
||||
|
||||
# Step 3: Reload models in memory
|
||||
print("🔄 [RETRAIN] Step 3/3: Reloading models...", flush=True)
|
||||
try:
|
||||
orchestrator = get_single_match_orchestrator()
|
||||
v25 = orchestrator._get_v25_predictor()
|
||||
v25._loaded = False
|
||||
v25.load_models()
|
||||
print("✅ [RETRAIN] Models reloaded in memory", flush=True)
|
||||
except Exception as reload_err:
|
||||
print(f"⚠️ [RETRAIN] Hot reload failed (restart needed): {reload_err}", flush=True)
|
||||
|
||||
_retrain_state.update({
|
||||
"running": False,
|
||||
"last_completed": datetime.now().isoformat(),
|
||||
"last_status": "success",
|
||||
"last_error": None,
|
||||
})
|
||||
print("🎉 [RETRAIN] Pipeline complete!", flush=True)
|
||||
|
||||
except Exception as err:
|
||||
_retrain_state.update({
|
||||
"running": False,
|
||||
"last_completed": datetime.now().isoformat(),
|
||||
"last_status": "failed",
|
||||
"last_error": str(err),
|
||||
})
|
||||
print(f"❌ [RETRAIN] Pipeline failed: {err}", flush=True)
|
||||
|
||||
|
||||
@app.post("/v1/admin/retrain")
|
||||
async def admin_retrain(request: RetrainRequest) -> dict[str, Any]:
|
||||
"""Trigger full retrain pipeline: extract → train → reload."""
|
||||
if _retrain_state["running"]:
|
||||
return {
|
||||
"status": "already_running",
|
||||
"message": f"Retrain in progress since {_retrain_state['last_started']}",
|
||||
}
|
||||
|
||||
_retrain_state.update({
|
||||
"running": True,
|
||||
"last_started": datetime.now().isoformat(),
|
||||
"last_status": "running",
|
||||
"last_error": None,
|
||||
})
|
||||
|
||||
# Run in background thread
|
||||
import threading
|
||||
thread = threading.Thread(
|
||||
target=_run_retrain_pipeline,
|
||||
args=(request.markets, request.trials or 50),
|
||||
daemon=True,
|
||||
)
|
||||
thread.start()
|
||||
|
||||
return {
|
||||
"status": "triggered",
|
||||
"message": "Retrain pipeline started in background",
|
||||
"reason": request.reason,
|
||||
"markets": request.markets or "all",
|
||||
"trials": request.trials or 50,
|
||||
}
|
||||
|
||||
|
||||
@app.get("/v1/admin/retrain/status")
|
||||
async def admin_retrain_status() -> dict[str, Any]:
|
||||
"""Check retrain pipeline status."""
|
||||
return {**_retrain_state}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
port = int(os.getenv("PORT", "8000"))
|
||||
uvicorn.run("main:app", host="0.0.0.0", port=port, reload=True)
|
||||
|
||||
@@ -0,0 +1,456 @@
|
||||
"""
|
||||
Calibration Module for XGBoost Models
|
||||
=====================================
|
||||
Calibrates raw probabilities from XGBoost models using Isotonic Regression.
|
||||
Ensures that a predicted probability of 70% actually corresponds to a 70% win rate.
|
||||
|
||||
Usage:
|
||||
from ai_engine.models.calibration import Calibrator
|
||||
calibrator = Calibrator()
|
||||
calibrated_prob = calibrator.calibrate("ms", raw_prob)
|
||||
|
||||
# Training new calibration models:
|
||||
calibrator.train_calibration(valid_df, market="ms")
|
||||
"""
|
||||
|
||||
import os
|
||||
import pickle
|
||||
import json
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional, Tuple, Any
|
||||
from sklearn.isotonic import IsotonicRegression
|
||||
from sklearn.calibration import calibration_curve
|
||||
from sklearn.metrics import brier_score_loss
|
||||
|
||||
AI_ENGINE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
CALIBRATION_DIR = os.path.join(AI_ENGINE_DIR, "models", "calibration")
|
||||
|
||||
os.makedirs(CALIBRATION_DIR, exist_ok=True)
|
||||
|
||||
# Supported markets for calibration
|
||||
SUPPORTED_MARKETS = [
|
||||
"ms", # Match Result (1X2) - multi-class, calibrated per class
|
||||
"ms_home", # Standard Home win probability
|
||||
"ms_home_heavy_fav", # Context: home odds <= 1.40
|
||||
"ms_home_fav", # Context: 1.40 < home odds <= 1.80
|
||||
"ms_home_balanced", # Context: 1.80 < home odds <= 2.50
|
||||
"ms_home_underdog", # Context: home odds > 2.50
|
||||
"ms_draw", # Draw probability
|
||||
"ms_away", # Away win probability
|
||||
"ou15", # Over/Under 1.5
|
||||
"ou25", # Over/Under 2.5
|
||||
"ou35", # Over/Under 3.5
|
||||
"btts", # Both Teams to Score
|
||||
"ht_ft", # Half-Time/Full-Time
|
||||
"dc", # Double Chance
|
||||
"ht", # Half-Time Result
|
||||
"ht_home", # Half-Time Home win
|
||||
"ht_draw", # Half-Time Draw
|
||||
"ht_away", # Half-Time Away win
|
||||
]
|
||||
|
||||
|
||||
class CalibrationMetrics:
|
||||
"""Stores calibration quality metrics for a market."""
|
||||
|
||||
def __init__(self):
|
||||
self.brier_score: float = 0.0
|
||||
self.calibration_error: float = 0.0
|
||||
self.sample_count: int = 0
|
||||
self.last_trained: str = ""
|
||||
self.mean_predicted: float = 0.0
|
||||
self.mean_actual: float = 0.0
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
return {
|
||||
"brier_score": round(self.brier_score, 4),
|
||||
"calibration_error": round(self.calibration_error, 4),
|
||||
"sample_count": self.sample_count,
|
||||
"last_trained": self.last_trained,
|
||||
"mean_predicted": round(self.mean_predicted, 4),
|
||||
"mean_actual": round(self.mean_actual, 4),
|
||||
}
|
||||
|
||||
|
||||
class Calibrator:
|
||||
"""
|
||||
Probability calibration using Isotonic Regression.
|
||||
|
||||
Isotonic Regression is a non-parametric method that fits a piecewise
|
||||
constant function that is monotonically increasing. It's ideal for
|
||||
calibrating probabilities because:
|
||||
|
||||
1. It preserves ranking (if P(A) > P(B) before, P(A) > P(B) after)
|
||||
2. It doesn't assume a specific distribution shape
|
||||
3. It can correct systematic over/under-confidence
|
||||
|
||||
Example:
|
||||
# Before calibration: model predicts 70% but actual win rate is 60%
|
||||
# After calibration: model predicts 70% → calibrated to 60%
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.calibrators: Dict[str, IsotonicRegression] = {}
|
||||
self.metrics: Dict[str, CalibrationMetrics] = {}
|
||||
# Less aggressive shrinkage — only meaningful overconfident bands are pulled.
|
||||
# Default raised from ~0.85-0.90 to 0.95+ since the orchestrator and config
|
||||
# already apply market-level multipliers; double-shrinkage was the root cause
|
||||
# of 24-35pt avg calibrated-vs-raw drops in production traces.
|
||||
self.heuristic_fallback: Dict[str, float] = {
|
||||
"ms": 0.96,
|
||||
"ms_home": 0.96,
|
||||
"ms_home_heavy_fav": 0.98,
|
||||
"ms_home_fav": 0.96,
|
||||
"ms_home_balanced": 0.94,
|
||||
"ms_home_underdog": 0.92,
|
||||
"ms_draw": 0.94,
|
||||
"ms_away": 0.96,
|
||||
"ou15": 0.96,
|
||||
"ou25": 0.96,
|
||||
"ou35": 0.94,
|
||||
"btts": 0.96,
|
||||
"ht_ft": 0.92,
|
||||
"dc": 0.97,
|
||||
"ht": 0.92,
|
||||
"ht_home": 0.92,
|
||||
"ht_draw": 0.92,
|
||||
"ht_away": 0.92,
|
||||
}
|
||||
self._load_calibrators()
|
||||
|
||||
def _load_calibrators(self):
|
||||
"""Load trained calibrators for each market from disk."""
|
||||
for market in SUPPORTED_MARKETS:
|
||||
model_path = os.path.join(CALIBRATION_DIR, f"{market}_calibrator.pkl")
|
||||
metrics_path = os.path.join(CALIBRATION_DIR, f"{market}_metrics.json")
|
||||
|
||||
if os.path.exists(model_path):
|
||||
try:
|
||||
with open(model_path, "rb") as f:
|
||||
self.calibrators[market] = pickle.load(f)
|
||||
print(f"[Calibrator] Loaded calibration model for {market}")
|
||||
except Exception as e:
|
||||
print(f"[Calibrator] Warning: Failed to load {market}: {e}")
|
||||
|
||||
if os.path.exists(metrics_path):
|
||||
try:
|
||||
with open(metrics_path, "r") as f:
|
||||
data = json.load(f)
|
||||
metrics = CalibrationMetrics()
|
||||
metrics.brier_score = data.get("brier_score", 0.0)
|
||||
metrics.calibration_error = data.get("calibration_error", 0.0)
|
||||
metrics.sample_count = data.get("sample_count", 0)
|
||||
metrics.last_trained = data.get("last_trained", "")
|
||||
metrics.mean_predicted = data.get("mean_predicted", 0.0)
|
||||
metrics.mean_actual = data.get("mean_actual", 0.0)
|
||||
self.metrics[market] = metrics
|
||||
except Exception as e:
|
||||
print(f"[Calibrator] Warning: Failed to load metrics for {market}: {e}")
|
||||
|
||||
# Below this sample count, blend isotonic with raw_prob to dampen overfit jumps.
|
||||
# Above this count, trust isotonic fully.
|
||||
TRUSTED_SAMPLE_FLOOR = 30
|
||||
TRUSTED_SAMPLE_CEILING = 200
|
||||
# Hard cap on how far calibration can move probability in either direction.
|
||||
MAX_DELTA = 0.20
|
||||
|
||||
def calibrate(self, market_type: str, raw_prob: float, odds_val: Optional[float] = None) -> float:
|
||||
"""
|
||||
Calibrate a raw probability using Isotonic Regression with safeguards.
|
||||
|
||||
Args:
|
||||
market_type (str): 'ms_home', 'ou25', 'btts', 'ht_ft', etc.
|
||||
raw_prob (float): The raw probability from XGBoost (0.0 - 1.0)
|
||||
odds_val (float, optional): The pre-match odds, used for context-aware bucket mapping
|
||||
|
||||
Returns:
|
||||
float: Calibrated probability (0.0 - 1.0)
|
||||
|
||||
Safeguards:
|
||||
* Low-sample trained models are blended with raw_prob to dampen overfit.
|
||||
* MAX_DELTA caps the per-call adjustment (prevents 40pp swings).
|
||||
"""
|
||||
# Normalize market type
|
||||
market_key = market_type.lower().replace("-", "_")
|
||||
|
||||
# Route to bucket if ms_home and odds provided
|
||||
if market_key == "ms_home" and odds_val is not None and odds_val > 1.0:
|
||||
if odds_val <= 1.40:
|
||||
bucket_key = "ms_home_heavy_fav"
|
||||
elif odds_val <= 1.80:
|
||||
bucket_key = "ms_home_fav"
|
||||
elif odds_val <= 2.50:
|
||||
bucket_key = "ms_home_balanced"
|
||||
else:
|
||||
bucket_key = "ms_home_underdog"
|
||||
|
||||
if bucket_key in self.calibrators:
|
||||
market_key = bucket_key
|
||||
|
||||
# If we have a trained Isotonic Regression model, use it (with safeguards)
|
||||
if market_key in self.calibrators:
|
||||
try:
|
||||
iso_pred = float(self.calibrators[market_key].predict([raw_prob])[0])
|
||||
|
||||
# Sample-count weighted blend with raw probability.
|
||||
# Sparse models barely move probability; mature models dominate.
|
||||
metrics = self.metrics.get(market_key)
|
||||
n_samples = metrics.sample_count if metrics else 0
|
||||
if n_samples >= self.TRUSTED_SAMPLE_CEILING:
|
||||
iso_weight = 1.0
|
||||
elif n_samples <= self.TRUSTED_SAMPLE_FLOOR:
|
||||
# Very sparse: at least 30% trust to surface the signal
|
||||
iso_weight = max(0.30, n_samples / self.TRUSTED_SAMPLE_CEILING)
|
||||
else:
|
||||
# Linearly ramp 30% → 100% between floor and ceiling
|
||||
span = self.TRUSTED_SAMPLE_CEILING - self.TRUSTED_SAMPLE_FLOOR
|
||||
iso_weight = 0.30 + 0.70 * (n_samples - self.TRUSTED_SAMPLE_FLOOR) / span
|
||||
blended = iso_weight * iso_pred + (1.0 - iso_weight) * raw_prob
|
||||
|
||||
# Cap delta to avoid huge swings on noisy calibrators
|
||||
delta = blended - raw_prob
|
||||
if delta > self.MAX_DELTA:
|
||||
blended = raw_prob + self.MAX_DELTA
|
||||
elif delta < -self.MAX_DELTA:
|
||||
blended = raw_prob - self.MAX_DELTA
|
||||
|
||||
return float(np.clip(blended, 0.01, 0.99))
|
||||
except Exception as e:
|
||||
print(f"[Calibrator] Warning: Isotonic failed for {market_key}: {e}")
|
||||
# Fall through to heuristic
|
||||
|
||||
# Fallback to heuristic calibration
|
||||
return self._heuristic_calibrate(market_key, raw_prob)
|
||||
|
||||
def _heuristic_calibrate(self, market_type: str, raw_prob: float) -> float:
|
||||
"""
|
||||
Heuristic calibration fallback when no trained model exists.
|
||||
|
||||
This applies a conservative shrinkage towards the mean:
|
||||
- Binary markets (OU, BTTS): shrink towards 0.5
|
||||
- Multi-class (MS): shrink towards 0.33
|
||||
- HT/FT: stronger shrinkage due to higher variance
|
||||
"""
|
||||
# Get shrinkage factor for this market
|
||||
shrinkage = self.heuristic_fallback.get(market_type, 0.90)
|
||||
|
||||
if market_type in ["ms", "ms_home", "ms_home_heavy_fav", "ms_home_fav", "ms_home_balanced", "ms_home_underdog", "ms_draw", "ms_away"]:
|
||||
# Pull towards 0.33 (uniform for 3-class)
|
||||
return (raw_prob * shrinkage) + (0.33 * (1.0 - shrinkage))
|
||||
|
||||
elif market_type in ["ou15", "ou25", "ou35", "btts"]:
|
||||
# Pull towards 0.5 (uniform for binary)
|
||||
return (raw_prob * shrinkage) + (0.5 * (1.0 - shrinkage))
|
||||
|
||||
elif market_type in ["ht_ft", "ht"]:
|
||||
# Stronger shrinkage for high-variance markets
|
||||
return raw_prob * shrinkage
|
||||
|
||||
elif market_type == "dc":
|
||||
# Double chance is more reliable
|
||||
return (raw_prob * shrinkage) + (0.66 * (1.0 - shrinkage))
|
||||
|
||||
return raw_prob
|
||||
|
||||
def train_calibration(
|
||||
self,
|
||||
df: pd.DataFrame,
|
||||
market: str,
|
||||
prob_col: str,
|
||||
actual_col: str,
|
||||
min_samples: int = 100,
|
||||
save: bool = True,
|
||||
) -> CalibrationMetrics:
|
||||
"""
|
||||
Train an Isotonic Regression calibration model for a specific market.
|
||||
|
||||
Args:
|
||||
df: DataFrame with predictions and actual outcomes
|
||||
market: Market identifier (e.g., 'ms_home', 'ou25', 'btts')
|
||||
prob_col: Column name for raw probabilities
|
||||
actual_col: Column name for actual outcomes (0 or 1)
|
||||
min_samples: Minimum samples required to train
|
||||
save: Whether to save the model to disk
|
||||
|
||||
Returns:
|
||||
CalibrationMetrics with quality metrics
|
||||
"""
|
||||
# Filter valid data
|
||||
valid_df = df[[prob_col, actual_col]].dropna()
|
||||
n_samples = len(valid_df)
|
||||
|
||||
if n_samples < min_samples:
|
||||
print(f"[Calibrator] Warning: Only {n_samples} samples for {market}, "
|
||||
f"need at least {min_samples}")
|
||||
metrics = CalibrationMetrics()
|
||||
metrics.sample_count = n_samples
|
||||
return metrics
|
||||
|
||||
# Extract arrays
|
||||
raw_probs = valid_df[prob_col].values
|
||||
actuals = valid_df[actual_col].values
|
||||
|
||||
# Train Isotonic Regression
|
||||
iso = IsotonicRegression(out_of_bounds="clip", increasing=True)
|
||||
iso.fit(raw_probs, actuals)
|
||||
|
||||
# Calculate calibrated probabilities
|
||||
calibrated_probs = iso.predict(raw_probs)
|
||||
|
||||
# Calculate metrics
|
||||
metrics = CalibrationMetrics()
|
||||
metrics.sample_count = n_samples
|
||||
metrics.last_trained = datetime.utcnow().isoformat()
|
||||
metrics.brier_score = brier_score_loss(actuals, calibrated_probs)
|
||||
metrics.mean_predicted = np.mean(raw_probs)
|
||||
metrics.mean_actual = np.mean(actuals)
|
||||
|
||||
# Calculate Expected Calibration Error (ECE)
|
||||
metrics.calibration_error = self._calculate_ece(
|
||||
calibrated_probs, actuals, n_bins=10
|
||||
)
|
||||
|
||||
# Store in memory
|
||||
self.calibrators[market] = iso
|
||||
self.metrics[market] = metrics
|
||||
|
||||
# Save to disk
|
||||
if save:
|
||||
self._save_calibration(market, iso, metrics)
|
||||
|
||||
print(f"[Calibrator] Trained {market}: "
|
||||
f"Brier={metrics.brier_score:.4f}, "
|
||||
f"ECE={metrics.calibration_error:.4f}, "
|
||||
f"n={n_samples}")
|
||||
|
||||
return metrics
|
||||
|
||||
def train_all_markets(
|
||||
self,
|
||||
df: pd.DataFrame,
|
||||
market_config: Dict[str, Tuple[str, str]],
|
||||
min_samples: int = 100,
|
||||
) -> Dict[str, CalibrationMetrics]:
|
||||
"""
|
||||
Train calibration models for multiple markets at once.
|
||||
|
||||
Args:
|
||||
df: DataFrame with all predictions and outcomes
|
||||
market_config: Dict mapping market -> (prob_col, actual_col)
|
||||
e.g., {'ou25': ('ou25_over_prob', 'ou25_over_actual')}
|
||||
min_samples: Minimum samples per market
|
||||
|
||||
Returns:
|
||||
Dict of market -> CalibrationMetrics
|
||||
"""
|
||||
results = {}
|
||||
|
||||
for market, (prob_col, actual_col) in market_config.items():
|
||||
print(f"\n[Calibrator] Training {market}...")
|
||||
try:
|
||||
metrics = self.train_calibration(
|
||||
df=df,
|
||||
market=market,
|
||||
prob_col=prob_col,
|
||||
actual_col=actual_col,
|
||||
min_samples=min_samples,
|
||||
save=True,
|
||||
)
|
||||
results[market] = metrics
|
||||
except Exception as e:
|
||||
print(f"[Calibrator] Failed to train {market}: {e}")
|
||||
|
||||
return results
|
||||
|
||||
def _calculate_ece(
|
||||
self,
|
||||
probs: np.ndarray,
|
||||
actuals: np.ndarray,
|
||||
n_bins: int = 10
|
||||
) -> float:
|
||||
"""
|
||||
Calculate Expected Calibration Error (ECE).
|
||||
|
||||
ECE = sum(|bin_accuracy - bin_confidence| * bin_weight)
|
||||
|
||||
Lower is better. Perfect calibration = 0.
|
||||
"""
|
||||
bin_boundaries = np.linspace(0, 1, n_bins + 1)
|
||||
ece = 0.0
|
||||
|
||||
for i in range(n_bins):
|
||||
in_bin = (probs >= bin_boundaries[i]) & (probs < bin_boundaries[i + 1])
|
||||
prop_in_bin = np.mean(in_bin)
|
||||
|
||||
if prop_in_bin > 0:
|
||||
accuracy_in_bin = np.mean(actuals[in_bin])
|
||||
avg_confidence_in_bin = np.mean(probs[in_bin])
|
||||
ece += np.abs(accuracy_in_bin - avg_confidence_in_bin) * prop_in_bin
|
||||
|
||||
return ece
|
||||
|
||||
def _save_calibration(
|
||||
self,
|
||||
market: str,
|
||||
calibrator: IsotonicRegression,
|
||||
metrics: CalibrationMetrics
|
||||
):
|
||||
"""Save calibration model and metrics to disk."""
|
||||
# Save model
|
||||
model_path = os.path.join(CALIBRATION_DIR, f"{market}_calibrator.pkl")
|
||||
with open(model_path, "wb") as f:
|
||||
pickle.dump(calibrator, f)
|
||||
|
||||
# Save metrics
|
||||
metrics_path = os.path.join(CALIBRATION_DIR, f"{market}_metrics.json")
|
||||
with open(metrics_path, "w") as f:
|
||||
json.dump(metrics.to_dict(), f, indent=2)
|
||||
|
||||
print(f"[Calibrator] Saved {market} to {CALIBRATION_DIR}")
|
||||
|
||||
def get_calibration_report(self) -> Dict[str, Any]:
|
||||
"""Generate a summary report of all calibration models."""
|
||||
report = {
|
||||
"trained_markets": list(self.calibrators.keys()),
|
||||
"metrics": {},
|
||||
"heuristic_only": [],
|
||||
}
|
||||
|
||||
for market in SUPPORTED_MARKETS:
|
||||
if market in self.metrics:
|
||||
report["metrics"][market] = self.metrics[market].to_dict()
|
||||
elif market not in self.calibrators:
|
||||
report["heuristic_only"].append(market)
|
||||
|
||||
return report
|
||||
|
||||
def get_calibrated_probabilities(
|
||||
self,
|
||||
market: str,
|
||||
raw_probs: np.ndarray
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Batch calibration for array of probabilities.
|
||||
|
||||
Args:
|
||||
market: Market type
|
||||
raw_probs: Array of raw probabilities
|
||||
|
||||
Returns:
|
||||
Array of calibrated probabilities
|
||||
"""
|
||||
return np.array([self.calibrate(market, p) for p in raw_probs])
|
||||
|
||||
|
||||
# Singleton instance
|
||||
_calibrator_instance: Optional[Calibrator] = None
|
||||
|
||||
|
||||
def get_calibrator() -> Calibrator:
|
||||
"""Get or create the global Calibrator instance."""
|
||||
global _calibrator_instance
|
||||
if _calibrator_instance is None:
|
||||
_calibrator_instance = Calibrator()
|
||||
return _calibrator_instance
|
||||
@@ -0,0 +1,191 @@
|
||||
"""
|
||||
League-Specific Model Loader
|
||||
=============================
|
||||
Loads per-league XGBoost models + isotonic calibrators trained by
|
||||
scripts/train_league_models.py and provides a unified prediction interface.
|
||||
|
||||
Falls back to general V25 for any market/league without a dedicated model.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import pickle
|
||||
from functools import lru_cache
|
||||
from typing import Dict, Optional, Tuple
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import xgboost as xgb
|
||||
|
||||
AI_ENGINE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
LEAGUE_MODEL_DIR = os.path.join(AI_ENGINE_DIR, "models", "league_specific")
|
||||
|
||||
# Market file name → (num_class, label_list)
|
||||
MARKET_META: Dict[str, Tuple[int, list]] = {
|
||||
"ms": (3, ["1", "X", "2"]),
|
||||
"ou15": (2, ["Over", "Under"]),
|
||||
"ou25": (2, ["Over", "Under"]),
|
||||
"ou35": (2, ["Over", "Under"]),
|
||||
"btts": (2, ["Yes", "No"]),
|
||||
"ht": (3, ["1", "X", "2"]),
|
||||
"ht_ou05": (2, ["Over", "Under"]),
|
||||
"ht_ou15": (2, ["Over", "Under"]),
|
||||
"htft": (9, ["1/1","1/X","1/2","X/1","X/X","X/2","2/1","2/X","2/2"]),
|
||||
"oe": (2, ["Odd", "Even"]),
|
||||
"cards": (2, ["Over", "Under"]),
|
||||
"handicap": (3, ["1", "X", "2"]),
|
||||
}
|
||||
|
||||
# Signal key map (file key → uppercase signal key used in _get_v25_signal)
|
||||
FILE_TO_SIGNAL = {
|
||||
"ms": "MS", "ou15": "OU15", "ou25": "OU25", "ou35": "OU35",
|
||||
"btts": "BTTS", "ht": "HT", "ht_ou05": "HT_OU05", "ht_ou15": "HT_OU15",
|
||||
"htft": "HTFT", "oe": "OE", "cards": "CARDS", "handicap": "HCAP",
|
||||
}
|
||||
|
||||
|
||||
class LeagueModel:
|
||||
"""Holds XGBoost models + isotonic calibrators for one league."""
|
||||
|
||||
def __init__(self, league_id: str):
|
||||
self.league_id = league_id
|
||||
self.league_dir = os.path.join(LEAGUE_MODEL_DIR, league_id)
|
||||
self.models: Dict[str, xgb.Booster] = {} # market_key → booster
|
||||
self.calibrators: Dict[str, object] = {} # cal_key → isotonic
|
||||
self.feature_cols: Optional[list] = None
|
||||
self._loaded = False
|
||||
|
||||
def load(self) -> bool:
|
||||
if not os.path.isdir(self.league_dir):
|
||||
return False
|
||||
try:
|
||||
fc_path = os.path.join(self.league_dir, "feature_cols.json")
|
||||
if os.path.exists(fc_path):
|
||||
with open(fc_path) as f:
|
||||
self.feature_cols = json.load(f)
|
||||
|
||||
for mkey in MARKET_META:
|
||||
xgb_path = os.path.join(self.league_dir, f"xgb_{mkey}.json")
|
||||
if os.path.exists(xgb_path) and os.path.getsize(xgb_path) > 100:
|
||||
b = xgb.Booster()
|
||||
b.load_model(xgb_path)
|
||||
self.models[mkey] = b
|
||||
|
||||
for fname in os.listdir(self.league_dir):
|
||||
if fname.startswith("cal_") and fname.endswith(".pkl"):
|
||||
cal_key = fname[4:-4] # strip cal_ and .pkl
|
||||
with open(os.path.join(self.league_dir, fname), "rb") as f:
|
||||
self.calibrators[cal_key] = pickle.load(f)
|
||||
|
||||
self._loaded = bool(self.models or self.calibrators)
|
||||
return self._loaded
|
||||
except Exception as e:
|
||||
print(f"[LeagueModel] Load failed for {self.league_id}: {e}")
|
||||
return False
|
||||
|
||||
def has_market(self, mkey: str) -> bool:
|
||||
return mkey in self.models
|
||||
|
||||
def predict_market(
|
||||
self,
|
||||
mkey: str,
|
||||
feature_row: Dict[str, float],
|
||||
) -> Optional[Dict[str, float]]:
|
||||
"""
|
||||
Predict one market using league-specific XGBoost + isotonic calibration.
|
||||
Returns {label: prob} dict or None if no model available.
|
||||
"""
|
||||
if mkey not in self.models:
|
||||
return None
|
||||
|
||||
num_class, labels = MARKET_META[mkey]
|
||||
fc = self.feature_cols
|
||||
if fc is None:
|
||||
# Fallback to whatever the booster expects (it knows its feature names)
|
||||
fc = list(self.models[mkey].feature_names or [])
|
||||
|
||||
try:
|
||||
X = pd.DataFrame([{col: feature_row.get(col, 0.0) for col in fc}])
|
||||
dmat = xgb.DMatrix(X)
|
||||
raw = self.models[mkey].predict(dmat)
|
||||
|
||||
if num_class > 2:
|
||||
probs_arr = raw.reshape(-1, num_class)[0]
|
||||
probs = {labels[i]: float(probs_arr[i]) for i in range(num_class)}
|
||||
# Apply isotonic calibration per class
|
||||
cal_total = 0.0
|
||||
for i, label in enumerate(labels):
|
||||
cal_key = f"{mkey}_{i}"
|
||||
if cal_key in self.calibrators:
|
||||
p_cal = float(self.calibrators[cal_key].predict([probs_arr[i]])[0])
|
||||
probs[label] = max(0.01, min(0.99, p_cal))
|
||||
cal_total += probs[label]
|
||||
if cal_total > 0:
|
||||
probs = {k: v / cal_total for k, v in probs.items()}
|
||||
else:
|
||||
p = float(raw[0])
|
||||
cal_key = mkey
|
||||
if cal_key in self.calibrators:
|
||||
p = float(self.calibrators[cal_key].predict([p])[0])
|
||||
p = max(0.01, min(0.99, p))
|
||||
probs = {labels[0]: p, labels[1]: 1.0 - p}
|
||||
|
||||
return probs
|
||||
except Exception as e:
|
||||
print(f"[LeagueModel] predict_market({mkey}) failed for {self.league_id}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
class LeagueModelLoader:
|
||||
"""
|
||||
In-memory cache for league-specific models.
|
||||
Thread-safe for single-process async servers (FastAPI/uvicorn).
|
||||
"""
|
||||
|
||||
def __init__(self, max_cached: int = 80):
|
||||
self._cache: Dict[str, Optional[LeagueModel]] = {}
|
||||
self._max_cached = max_cached
|
||||
|
||||
def get(self, league_id: str) -> Optional[LeagueModel]:
|
||||
"""Return loaded LeagueModel for this league, or None if unavailable."""
|
||||
if league_id in self._cache:
|
||||
return self._cache[league_id]
|
||||
|
||||
# Evict oldest entry if cache is full
|
||||
if len(self._cache) >= self._max_cached:
|
||||
oldest = next(iter(self._cache))
|
||||
del self._cache[oldest]
|
||||
|
||||
model = LeagueModel(league_id)
|
||||
loaded = model.load()
|
||||
self._cache[league_id] = model if loaded else None
|
||||
if loaded:
|
||||
n_models = len(model.models)
|
||||
n_cals = len(model.calibrators)
|
||||
print(f"[LeagueModel] Loaded {league_id}: {n_models} XGB models, {n_cals} calibrators")
|
||||
return self._cache[league_id]
|
||||
|
||||
def available_leagues(self) -> list:
|
||||
if not os.path.isdir(LEAGUE_MODEL_DIR):
|
||||
return []
|
||||
return [d for d in os.listdir(LEAGUE_MODEL_DIR)
|
||||
if os.path.isdir(os.path.join(LEAGUE_MODEL_DIR, d))]
|
||||
|
||||
def readiness_summary(self) -> dict:
|
||||
leagues = self.available_leagues()
|
||||
return {
|
||||
"league_specific_dir": LEAGUE_MODEL_DIR,
|
||||
"available_leagues": len(leagues),
|
||||
"cached": len([v for v in self._cache.values() if v is not None]),
|
||||
}
|
||||
|
||||
|
||||
# ── Singleton ──────────────────────────────────────────────────────
|
||||
_loader: Optional[LeagueModelLoader] = None
|
||||
|
||||
|
||||
def get_league_model_loader() -> LeagueModelLoader:
|
||||
global _loader
|
||||
if _loader is None:
|
||||
_loader = LeagueModelLoader()
|
||||
return _loader
|
||||
@@ -0,0 +1,154 @@
|
||||
[
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
"elo_diff",
|
||||
"home_home_elo",
|
||||
"away_away_elo",
|
||||
"home_form_elo",
|
||||
"away_form_elo",
|
||||
"form_elo_diff",
|
||||
"home_goals_avg",
|
||||
"home_conceded_avg",
|
||||
"away_goals_avg",
|
||||
"away_conceded_avg",
|
||||
"home_clean_sheet_rate",
|
||||
"away_clean_sheet_rate",
|
||||
"home_scoring_rate",
|
||||
"away_scoring_rate",
|
||||
"home_winning_streak",
|
||||
"away_winning_streak",
|
||||
"home_unbeaten_streak",
|
||||
"away_unbeaten_streak",
|
||||
"h2h_total_matches",
|
||||
"h2h_home_win_rate",
|
||||
"h2h_draw_rate",
|
||||
"h2h_avg_goals",
|
||||
"h2h_btts_rate",
|
||||
"h2h_over25_rate",
|
||||
"home_avg_possession",
|
||||
"away_avg_possession",
|
||||
"home_avg_shots_on_target",
|
||||
"away_avg_shots_on_target",
|
||||
"home_shot_conversion",
|
||||
"away_shot_conversion",
|
||||
"home_avg_corners",
|
||||
"away_avg_corners",
|
||||
"odds_ms_h",
|
||||
"odds_ms_d",
|
||||
"odds_ms_a",
|
||||
"implied_home",
|
||||
"implied_draw",
|
||||
"implied_away",
|
||||
"odds_ht_ms_h",
|
||||
"odds_ht_ms_d",
|
||||
"odds_ht_ms_a",
|
||||
"odds_ou05_o",
|
||||
"odds_ou05_u",
|
||||
"odds_ou15_o",
|
||||
"odds_ou15_u",
|
||||
"odds_ou25_o",
|
||||
"odds_ou25_u",
|
||||
"odds_ou35_o",
|
||||
"odds_ou35_u",
|
||||
"odds_ht_ou05_o",
|
||||
"odds_ht_ou05_u",
|
||||
"odds_ht_ou15_o",
|
||||
"odds_ht_ou15_u",
|
||||
"odds_btts_y",
|
||||
"odds_btts_n",
|
||||
"odds_ms_h_present",
|
||||
"odds_ms_d_present",
|
||||
"odds_ms_a_present",
|
||||
"odds_ht_ms_h_present",
|
||||
"odds_ht_ms_d_present",
|
||||
"odds_ht_ms_a_present",
|
||||
"odds_ou05_o_present",
|
||||
"odds_ou05_u_present",
|
||||
"odds_ou15_o_present",
|
||||
"odds_ou15_u_present",
|
||||
"odds_ou25_o_present",
|
||||
"odds_ou25_u_present",
|
||||
"odds_ou35_o_present",
|
||||
"odds_ou35_u_present",
|
||||
"odds_ht_ou05_o_present",
|
||||
"odds_ht_ou05_u_present",
|
||||
"odds_ht_ou15_o_present",
|
||||
"odds_ht_ou15_u_present",
|
||||
"odds_btts_y_present",
|
||||
"odds_btts_n_present",
|
||||
"home_xga",
|
||||
"away_xga",
|
||||
"league_avg_goals",
|
||||
"league_zero_goal_rate",
|
||||
"upset_atmosphere",
|
||||
"upset_motivation",
|
||||
"upset_fatigue",
|
||||
"upset_potential",
|
||||
"referee_home_bias",
|
||||
"referee_avg_goals",
|
||||
"referee_cards_total",
|
||||
"referee_avg_yellow",
|
||||
"referee_experience",
|
||||
"home_momentum_score",
|
||||
"away_momentum_score",
|
||||
"momentum_diff",
|
||||
"home_squad_quality",
|
||||
"away_squad_quality",
|
||||
"squad_diff",
|
||||
"home_key_players",
|
||||
"away_key_players",
|
||||
"home_missing_impact",
|
||||
"away_missing_impact",
|
||||
"home_goals_form",
|
||||
"away_goals_form",
|
||||
"home_lineup_goals_per90",
|
||||
"away_lineup_goals_per90",
|
||||
"home_lineup_assists_per90",
|
||||
"away_lineup_assists_per90",
|
||||
"home_squad_continuity",
|
||||
"away_squad_continuity",
|
||||
"home_top_scorer_form",
|
||||
"away_top_scorer_form",
|
||||
"home_avg_player_exp",
|
||||
"away_avg_player_exp",
|
||||
"home_goals_diversity",
|
||||
"away_goals_diversity",
|
||||
"h2h_home_goals_avg",
|
||||
"h2h_away_goals_avg",
|
||||
"h2h_recent_trend",
|
||||
"h2h_venue_advantage",
|
||||
"home_rolling5_goals",
|
||||
"home_rolling5_conceded",
|
||||
"home_rolling10_goals",
|
||||
"home_rolling10_conceded",
|
||||
"home_rolling20_goals",
|
||||
"home_rolling20_conceded",
|
||||
"away_rolling5_goals",
|
||||
"away_rolling5_conceded",
|
||||
"away_rolling10_goals",
|
||||
"away_rolling10_conceded",
|
||||
"home_rolling5_cs",
|
||||
"away_rolling5_cs",
|
||||
"home_venue_goals",
|
||||
"home_venue_conceded",
|
||||
"away_venue_goals",
|
||||
"away_venue_conceded",
|
||||
"home_goal_trend",
|
||||
"away_goal_trend",
|
||||
"home_days_rest",
|
||||
"away_days_rest",
|
||||
"match_month",
|
||||
"is_season_start",
|
||||
"is_season_end",
|
||||
"attack_vs_defense_home",
|
||||
"attack_vs_defense_away",
|
||||
"xg_diff",
|
||||
"form_momentum_interaction",
|
||||
"elo_form_consistency",
|
||||
"upset_x_elo_gap",
|
||||
"league_home_win_rate",
|
||||
"league_draw_rate",
|
||||
"league_btts_rate",
|
||||
"league_ou25_rate",
|
||||
"league_reliability_score"
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,891 @@
|
||||
tree
|
||||
version=v4
|
||||
num_class=1
|
||||
num_tree_per_iteration=1
|
||||
label_index=0
|
||||
max_feature_idx=151
|
||||
objective=binary sigmoid:1
|
||||
feature_names=Column_0 Column_1 Column_2 Column_3 Column_4 Column_5 Column_6 Column_7 Column_8 Column_9 Column_10 Column_11 Column_12 Column_13 Column_14 Column_15 Column_16 Column_17 Column_18 Column_19 Column_20 Column_21 Column_22 Column_23 Column_24 Column_25 Column_26 Column_27 Column_28 Column_29 Column_30 Column_31 Column_32 Column_33 Column_34 Column_35 Column_36 Column_37 Column_38 Column_39 Column_40 Column_41 Column_42 Column_43 Column_44 Column_45 Column_46 Column_47 Column_48 Column_49 Column_50 Column_51 Column_52 Column_53 Column_54 Column_55 Column_56 Column_57 Column_58 Column_59 Column_60 Column_61 Column_62 Column_63 Column_64 Column_65 Column_66 Column_67 Column_68 Column_69 Column_70 Column_71 Column_72 Column_73 Column_74 Column_75 Column_76 Column_77 Column_78 Column_79 Column_80 Column_81 Column_82 Column_83 Column_84 Column_85 Column_86 Column_87 Column_88 Column_89 Column_90 Column_91 Column_92 Column_93 Column_94 Column_95 Column_96 Column_97 Column_98 Column_99 Column_100 Column_101 Column_102 Column_103 Column_104 Column_105 Column_106 Column_107 Column_108 Column_109 Column_110 Column_111 Column_112 Column_113 Column_114 Column_115 Column_116 Column_117 Column_118 Column_119 Column_120 Column_121 Column_122 Column_123 Column_124 Column_125 Column_126 Column_127 Column_128 Column_129 Column_130 Column_131 Column_132 Column_133 Column_134 Column_135 Column_136 Column_137 Column_138 Column_139 Column_140 Column_141 Column_142 Column_143 Column_144 Column_145 Column_146 Column_147 Column_148 Column_149 Column_150 Column_151
|
||||
feature_infos=[1150.3663761896189:1903.4781806887747] [1158.5088961211511:1916.84579108047] [-496.81477567713546:573.10259120534784] [1159.8767670517543:1884.5959848901657] [1151.7894548779084:1919.4116678360419] [1426.1496448360797:1585.9817930954068] [1427.9817118206745:1588.9895054335384] [-113.02538114266532:114.69704651598067] [0:5.9333333333333336] [0:6.2666666666666666] [0:6.0666666666666664] [0:5.2000000000000002] [0:1] [0:1] [0:1] [0:1] [0:5] [0:5] [0:5] [0:5] [0:8] [0:1] [0:1] [0:11] [0:1] [0:1] [0.20999999999999999:0.81000000000000005] [0.22500000000000001:0.76000000000000001] [0:13] [0:13] [0:6.333333333333333] [0:6.666666666666667] [0:4.5] [0:4.5] [0:22.550000000000001] [0:17.5] [0:35.5] [0.065285302506677897:0.80962131918207236] [0.1044438556117265:0.4288719106684401] [0.056651501780225801:0.79902050363656307] [0:26] [0:5.0899999999999999] [0:26.5] [0:1.0900000000000001] [0:13.050000000000001] [0:1.76] [0:13.25] [0:3.7400000000000002] [0:7.6500000000000004] [0:8.5600000000000005] [0:3.5299999999999998] [0:1.72] [0:7.3300000000000001] [0:5.0199999999999996] [0:2.5800000000000001] [0:3.77] [0:4.1299999999999999] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:1] [0:6.2666666666666666] [0:5.2000000000000002] [2.0046118370484245:4.1840324763193504] [0.018042399639151999:0.1552651806302843] [0:0.45000000000000001] none none [0:0.1575] [-0.92000000000000004:1] [0:7] none none [0:1] [-1:0.61250000000000004] [-1:0.59166666666666667] [-1.2908333333333333:1.3799999999999999] [0:40.799999999999997] [0:36.299999999999997] [-29.999999999999996:31.499999999999996] [0:10] [0:10] [0:1] [0:1] [0:5.7999999999999998] [0:5] [0:5] [0:6.25] [0:4] [0:5] [0:1] [0:1] [0:10] [0:11] [0:42.100000000000001] [0:41.799999999999997] [0:1] [0:1] [0:8] [0:8] [-1:1] [0:1] [0:5.7999999999999998] [0:6.5] [0:5.2000000000000002] [0:6.5] [0:4.4119999999999999] [0:6.5] [0:5.3330000000000002] [0:4.7999999999999998] [0:5.3330000000000002] [0:4.5] [0:1] [0:1] [0:7] [0:8] [0:6] [0:7] [-1.8:1.8] [-2.2000000000000002:2] [1:30] [1.2:30] [1:12] [0:1] [0:1] [-4.4669999999999996:4.5999999999999996] [-5.2000000000000002:5] [-4.133:5.3330000000000002] [-0.045499999999999999:0.088099999999999998] [0.063600000000000004:1] [0:0.088800000000000004] [0.35623409669211198:0.51556156968876865] [0.1051136363636363:0.32744043043812449] [0.40430438124519602:0.64185836716283262] [0.32129131437355879:0.77537212449255755] [0.70399999999999996:1]
|
||||
tree_sizes=946 1005 993 986 1007 997 994 1005 992 1009 984 1007 997 684 1002 999 897 992 991 990 1001 905 1003 995 896 1010 908 1005 1007 1014 1012 1005 1005 691 688
|
||||
|
||||
Tree=0
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=45 54 46 96 4 3 16
|
||||
split_gain=174.584 35.5641 24.9346 16.8552 14.3832 11.0732 10.8094
|
||||
threshold=1.155 1.405 2.5850000000000004 4.5000000000000009 1704.4687685416313 1441.3975280143418 1.0000000180025095e-35
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 3 5 -1 -4 -2 -3
|
||||
right_child=2 6 4 -5 -6 -7 -8
|
||||
leaf_value=0.87683759709368503 0.84782001969484888 0.90404985782878589 0.85793883408869098 0.90097077069702014 0.73127673195863474 0.81008235044809496 0.93515098554525133
|
||||
leaf_weight=939.87957760691643 113.51603642106056 283.89385342597961 418.36988925933838 472.32632339000702 9.9611878395080549 330.17187193036079 212.09029108285904
|
||||
leaf_count=4529 547 1368 2016 2276 48 1591 1022
|
||||
internal_value=0.875686 0.893342 0.837052 0.884909 0.85499 0.819737 0.91735
|
||||
internal_weight=2780.21 1908.19 872.019 1412.21 428.331 443.688 495.984
|
||||
internal_count=13397 9195 4202 6805 2064 2138 2390
|
||||
is_linear=0
|
||||
shrinkage=1
|
||||
|
||||
|
||||
Tree=1
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=47 48 49 86 141 133 150
|
||||
split_gain=140.072 28.5634 26.9673 23.4898 19.0464 14.6224 10.7313
|
||||
threshold=1.6850000000000003 2.1650000000000005 3.3850000000000002 3.3279569892473124 1.2620000000000002 0.31650000000000006 0.48538789856891795
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 3 5 -1 -4 -2 -3
|
||||
right_child=2 6 4 -5 -6 -7 -8
|
||||
leaf_value=0.0068305934170468773 -0.15031955758812821 -0.031384052219233315 -0.052289652303345355 0.062911487393225371 0.031971580220150592 -0.011449057668411599 0.04709896679065162
|
||||
leaf_weight=1284.6507234424353 8.3659095764160138 19.989385187625885 501.71516834199429 86.587033972144127 30.929726287722588 495.32643516361713 353.17835873365402
|
||||
leaf_count=6221 40 98 2370 419 146 2373 1730
|
||||
internal_value=-0.000699774 0.0173296 -0.0310475 0.0103722 -0.0473963 -0.0137584 0.0428942
|
||||
internal_weight=2780.74 1744.41 1036.34 1371.24 532.645 503.692 373.168
|
||||
internal_count=13397 8468 4929 6640 2516 2413 1828
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=2
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=45 50 53 39 96 28 150
|
||||
split_gain=110.988 23.3948 18.1465 13.3271 12.0676 11.8858 11.7377
|
||||
threshold=1.155 1.4150000000000003 3.3250000000000006 0.36187197152743827 4.5000000000000009 3.9500000000000006 0.48538789856891795
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 4 5 -4 -1 -2 -3
|
||||
right_child=2 6 3 -5 -6 -7 -8
|
||||
leaf_value=0.0012888166907150367 -0.037269165059036741 -0.040251887739062138 -0.05134333704385069 -0.13896201271441588 0.020859848952961575 -0.011322757210899898 0.041785901483228478
|
||||
leaf_weight=991.85861267149448 395.00907251238823 20.026126876473427 106.85419258475304 22.895305529236794 522.50144763290882 372.75306884944439 348.38899676501751
|
||||
leaf_count=4802 1851 97 494 106 2555 1751 1741
|
||||
internal_value=-0.000605268 0.0137719 -0.0307651 -0.0668133 0.00804152 -0.0246723 0.0373256
|
||||
internal_weight=2780.29 1882.78 897.512 129.749 1514.36 767.762 368.415
|
||||
internal_count=13397 9195 4202 600 7357 3602 1838
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=3
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=45 48 86 11 53 144 104
|
||||
split_gain=91.2855 30.6398 20.2224 17.4942 14.4128 10.3549 10.2483
|
||||
threshold=1.155 2.2850000000000006 3.3279569892473124 0.30952380952380959 3.4950000000000006 0.0075500000000000003 1.0005000000000002
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 2 -1 -3 6 -6 -2
|
||||
right_child=4 3 -4 -5 5 -7 -8
|
||||
leaf_value=0.0040069597334026 -0.027086096902533729 -0.1715719381102207 0.053726932967164444 0.048773966775503941 -0.061900987427835959 -0.23396750965062757 0.016338062312498316
|
||||
leaf_weight=1510.6300098896027 782.38381478190422 3.9652889966964713 94.400425717234612 257.22131448984146 73.248439565300941 3.9991354644298545 63.84617380797863
|
||||
leaf_count=7369 3628 20 471 1306 331 18 297
|
||||
internal_value=-0.000943352 0.0123188 0.00693156 0.0454226 -0.0277441 -0.0708364 -0.0238097
|
||||
internal_weight=2789.69 1866.22 1605.03 261.187 923.478 77.2476 846.23
|
||||
internal_count=13440 9166 7840 1326 4274 349 3925
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=4
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=49 35 6 53 44 126 38
|
||||
split_gain=70.6071 28.8927 21.165 13.8246 12.9797 16.2351 8.96833
|
||||
threshold=2.8350000000000004 3.3550000000000004 1541.1282734213053 1.9650000000000001 6.5150000000000006 1.8450000000000002 0.27603289214361998
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 2 -1 -3 5 -2 -6
|
||||
right_child=4 3 -4 -5 6 -7 -8
|
||||
leaf_value=-0.0027517198054468608 -0.029319791370752923 0.043695199840511914 0.083541694807877556 0.015347179328011443 -0.028790250204289367 -0.099217570104222469 -0.0022072689999685109
|
||||
leaf_weight=937.29022094607353 417.85833202302456 320.03798474371433 31.915322333574295 449.0833810120821 218.10735833644867 39.50090055167675 374.63410261273384
|
||||
leaf_count=4582 1898 1642 157 2211 1027 181 1742
|
||||
internal_value=-0.000829778 0.0120602 9.06525e-05 0.0271434 -0.0221677 -0.0353586 -0.0119891
|
||||
internal_weight=2788.43 1738.33 969.206 769.121 1050.1 457.359 592.741
|
||||
internal_count=13440 8592 4739 3853 4848 2079 2769
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=5
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=51 50 35 143 44 147 11
|
||||
split_gain=63.712 19.0005 13.7227 11.8206 10.6018 10.4763 6.84163
|
||||
threshold=1.2550000000000001 1.4650000000000001 5.035000000000001 -1.7974999999999997 7.2550000000000008 0.47199397614381194 2.6904761904761911
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 2 -1 6 -5 -3 -2
|
||||
right_child=3 5 -4 4 -6 -7 -8
|
||||
leaf_value=0.0047544573525090221 -0.065940592898403594 0.046851958603355899 0.063639968219378742 -0.028402060075216558 -0.0037183287940315865 -0.010933729592520421 -0.2419531318087022
|
||||
leaf_weight=1547.0932241082191 6.1367039680480939 207.7856714874506 44.20663620531559 674.41967558860779 262.60369572043419 40.760551080107689 3.9397892653942108
|
||||
leaf_count=7584 28 1099 222 3053 1221 215 18
|
||||
internal_value=-0.000729499 0.0105766 0.00639068 -0.0226927 -0.0214845 0.0373753 -0.134921
|
||||
internal_weight=2786.95 1839.85 1591.3 947.1 937.023 248.546 10.0765
|
||||
internal_count=13440 9120 7806 4320 4274 1314 46
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=6
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=41 130 54 48 99 34 105
|
||||
split_gain=42.6482 15.4832 11.8244 11.5616 10.5481 8.08864 5.28375
|
||||
threshold=1.9950000000000003 2.1835000000000004 1.6950000000000001 2.0250000000000004 0.13392857142857142 2.1550000000000007 1.0765000000000002
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 3 4 -1 -2 -3 -4
|
||||
right_child=2 5 6 -5 -6 -7 -8
|
||||
leaf_value=-0.012930103603477631 -0.025837088447404372 0.009985288944886676 0.093598551064641447 0.0827918792790409 0.017739499999769957 0.067684395437451708 -0.011462427181900188
|
||||
leaf_weight=1452.7631230950356 63.837418377399445 116.17519751191139 30.260349631309509 13.827319353818892 1098.0612086355686 34.140560820698738 6.2758191227912894
|
||||
leaf_count=6763 321 560 163 70 5525 162 34
|
||||
internal_value=0.00228156 -0.00876197 0.0171813 -0.0120271 0.0153451 0.0230937 0.0755539
|
||||
internal_weight=2815.34 1616.91 1198.43 1466.59 1161.9 150.316 36.5362
|
||||
internal_count=13598 7555 6043 6833 5846 722 197
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=7
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=41 135 130 21 53 102 16
|
||||
split_gain=34.421 15.4189 13.9177 13.4109 11.0563 13.2179 9.32957
|
||||
threshold=1.9950000000000003 -0.78349999999999997 2.1340000000000003 0.4642857142857143 2.0250000000000004 2.1120000000000005 1.5000000000000002
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 3 -3 -1 6 -6 -2
|
||||
right_child=4 2 -4 -5 5 -7 -8
|
||||
leaf_value=-0.039029586512136658 0.021256889774663217 -0.0093694924607743823 0.024219953555788796 -0.18450744298912586 0.010656362766355395 -0.067587490182026186 0.059808579820180507
|
||||
leaf_weight=29.468837857246399 324.64188092947006 1435.6618839651346 147.77664601802826 8.9744612574577314 753.81818389892578 24.195777088403702 86.313260063529015
|
||||
leaf_count=138 1721 6659 716 42 3733 122 467
|
||||
internal_value=0.00205658 -0.00781819 -0.00623458 -0.0730278 0.0155268 0.00822224 0.0293551
|
||||
internal_weight=2810.85 1621.88 1583.44 38.4433 1188.97 778.014 410.955
|
||||
internal_count=13598 7555 7375 180 6043 3855 2188
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=8
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=86 41 30 92 147 8 85
|
||||
split_gain=36.407 25.9777 15.256 11.9724 10.8984 10.2187 7.72256
|
||||
threshold=3.2679487179487183 1.8750000000000002 0.46841755319148942 -0.76249999999999984 0.40846280364372473 1.1083333333333336 -0.021724137931034445
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 2 -1 -3 5 -2 -6
|
||||
right_child=4 3 -4 -5 6 -7 -8
|
||||
leaf_value=-0.012159457825842523 0.064307005786210347 -0.085506751396820332 -0.10592968794102031 0.0081406001340509019 0.027805156010338752 -0.1311253894779951 0.074917050140623179
|
||||
leaf_weight=1030.2416722476482 5.0686567574739438 14.960604444146155 19.18836584687233 1572.8768468499184 63.189212292432785 6.7883874922990799 94.08995346724987
|
||||
leaf_count=4724 25 75 92 7835 328 34 485
|
||||
internal_value=0.00185481 -0.00115146 -0.0138749 0.00725775 0.0487278 -0.0475971 0.055992
|
||||
internal_weight=2806.4 2637.27 1049.43 1587.84 169.136 11.857 157.279
|
||||
internal_count=13598 12726 4816 7910 872 59 813
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=9
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=49 150 52 133 30 91 30
|
||||
split_gain=26.3296 13.6848 11.0511 10.5586 10.2017 14.0051 13.9274
|
||||
threshold=2.7850000000000006 0.60942003008724055 2.9650000000000003 2.7320000000000007 0.10822072072072071 -0.0024999999999999497 0.12681311751103638
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 3 -3 -1 5 -2 -6
|
||||
right_child=4 2 -4 -5 6 -7 -8
|
||||
leaf_value=0.0056197323214787877 0.0092359089138059399 0.013793895571876906 0.055269467591862222 -0.056030521488072138 0.020769131467870516 -0.044189842169712931 -0.012941111969465513
|
||||
leaf_weight=1335.4049420952797 73.188210651278496 194.0794630497694 108.95535556972027 30.86374768614769 165.2346598058939 196.06340071558952 684.92604845762253
|
||||
leaf_count=6712 329 987 589 157 747 879 3130
|
||||
internal_value=0.000378046 0.00867093 0.0287076 0.00422669 -0.0119885 -0.0296678 -0.00638913
|
||||
internal_weight=2788.72 1669.3 303.035 1366.27 1119.41 269.252 850.161
|
||||
internal_count=13530 8445 1576 6869 5085 1208 3877
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=10
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=86 41 0 6 110 102 53
|
||||
split_gain=38.5149 20.3558 13.2957 10.1636 9.93058 14.9217 6.02783
|
||||
threshold=3.0790020790020796 1.925 1413.5914823844723 1484.8073996434571 2.9500000000000006 0.65050000000000019 2.2150000000000003
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 2 -1 -3 6 -6 -2
|
||||
right_child=4 3 -4 -5 5 -7 -8
|
||||
leaf_value=0.0140954684441122 0.085154679849317738 0.026410531501952182 -0.017501548280296479 0.0014621478522681489 -0.046636790759956186 0.037447698249607933 0.031794502065436499
|
||||
leaf_weight=167.52074213325977 60.752241030335426 211.7988056242466 1059.5275938510895 1090.8869259208441 27.780392900109291 130.96362222731113 37.02421498298645
|
||||
leaf_count=765 323 1076 4848 5512 140 685 181
|
||||
internal_value=0.000347101 -0.00355495 -0.0131877 0.00551857 0.0388267 0.0227309 0.0649542
|
||||
internal_weight=2786.25 2529.73 1227.05 1302.69 256.52 158.744 97.7765
|
||||
internal_count=13530 12201 5613 6588 1329 825 504
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=11
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=86 122 0 132 136 148 145
|
||||
split_gain=31.5628 19.7563 17.5348 15.2483 8.699 11.6526 8.53686
|
||||
threshold=3.0790020790020796 1.0250000000000001 1444.5595683182339 0.68350000000000011 11.950000000000001 0.25070180469638537 0.63955000000000006
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 2 -1 -3 6 -6 -2
|
||||
right_child=4 3 -4 -5 5 -7 -8
|
||||
leaf_value=-0.0029563172992086786 0.032749371502154893 -0.027078470578097272 -0.04142343191974563 0.004355282116605525 0.042836068356502124 -0.043229223026257771 0.082015555649337976
|
||||
leaf_weight=268.76511310040951 128.29142379760742 184.50364246964455 247.01660700142384 1831.7398000508547 37.845243006944656 31.138597756624222 54.413731098175049
|
||||
leaf_count=1259 680 882 1128 8932 205 158 286
|
||||
internal_value=0.000321355 -0.0031776 -0.0213793 0.00147871 0.0355201 0.00398648 0.0474254
|
||||
internal_weight=2783.71 2532.03 515.782 2016.24 251.689 68.9838 182.705
|
||||
internal_count=13530 12201 2387 9814 1329 363 966
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=12
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=86 41 94 4 100 132 111
|
||||
split_gain=16.7583 14.9692 11.4051 9.36529 9.15568 6.8883 6.86331
|
||||
threshold=3.2679487179487183 2.5250000000000004 19.050000000000004 1454.7040164610194 2.1000000000000005 2.5855000000000006 1.6500000000000001
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 2 -1 4 -2 -3 -5
|
||||
right_child=3 5 -4 6 -6 -7 -8
|
||||
leaf_value=-0.0077031612935488866 -0.037300665068035477 0.045270021664037818 0.0069515620157151858 0.08783804546768309 0.1145092464596736 -0.063294467679354607 0.032433508236858831
|
||||
leaf_weight=1642.7547204941511 28.476281866431236 95.693837076425552 889.01127083599567 33.729012683033943 5.0763863474130622 6.7911695241928092 86.767844557762146
|
||||
leaf_count=7824 156 540 4317 188 29 38 478
|
||||
internal_value=0.000977474 -0.000976496 -0.00255725 0.0343884 -0.0143067 0.0380708 0.0479478
|
||||
internal_weight=2788.3 2634.25 2531.77 154.05 33.5527 102.485 120.497
|
||||
internal_count=13570 12719 12141 851 185 578 666
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=13
|
||||
num_leaves=5
|
||||
num_cat=0
|
||||
split_feature=49 91 105 52
|
||||
split_gain=12.5399 11.9283 13.2239 11.4266
|
||||
threshold=6.7950000000000008 0.46333333333333332 0.66550000000000009 3.1450000000000005
|
||||
decision_type=2 2 2 2
|
||||
left_child=1 3 -3 -1
|
||||
right_child=-2 2 -4 -5
|
||||
leaf_value=-0.00096173862485333158 -0.18852641170062942 -0.26944729151735436 0.0083924991906715318 0.019698628791158867
|
||||
leaf_weight=2444.542382568121 3.7820855826139441 3.7145474255084983 3.7179097086191177 330.02280321717262
|
||||
leaf_count=11674 16 18 18 1844
|
||||
internal_value=0.000884212 0.00114235 -0.13063 0.00149578
|
||||
internal_weight=2785.78 2782 7.43246 2774.57
|
||||
internal_count=13570 13554 36 13518
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=14
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=35 136 1 3 120 119 137
|
||||
split_gain=12.9995 12.1396 9.67922 10.2069 8.87434 8.76233 7.82142
|
||||
threshold=3.4050000000000007 3.1500000000000008 1408.1043210310934 1436.2136975970254 1.4145000000000001 0.55000000000000016 12.850000000000003
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 5 3 -2 -3 -1 -4
|
||||
right_child=2 4 6 -5 -6 -7 -8
|
||||
leaf_value=-0.065982224130305259 -0.028897601514020019 -0.010800039930687538 0.014598348000302748 0.051779760894765055 0.0040416852382515198 0.035952394418601007 -0.011457004864636652
|
||||
leaf_weight=9.93890532851219 20.277691304683685 1238.979572609067 441.32176646590233 106.29307742416859 676.56466819345951 115.69292894005775 174.66667002439499
|
||||
leaf_count=46 107 5755 2341 568 3282 548 923
|
||||
internal_value=0.000796137 -0.00349957 0.0126042 0.0388534 -0.00555799 0.0278839 0.00721016
|
||||
internal_weight=2783.74 2041.18 742.559 126.571 1915.54 125.632 615.988
|
||||
internal_count=13570 9631 3939 675 9037 594 3264
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=15
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=94 53 50 125 104 49 120
|
||||
split_gain=15.421 19.7004 11.5702 10.8982 8.46618 7.68903 0.284565
|
||||
threshold=17.550000000000004 3.5150000000000001 2.0050000000000003 0.45000000000000007 0.45750000000000007 1.8950000000000002 2.4365000000000001
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=2 5 3 -1 -3 -2 -4
|
||||
right_child=1 4 6 -5 -6 -7 -8
|
||||
leaf_value=-0.040237205117941127 0.029193543717025244 -0.027898057080941927 0.13173751892789104 -0.0049749588986612815 -0.15821400423700094 0.0070086816806797644 0.089170490566897243
|
||||
leaf_weight=101.41680394113064 210.2422444075346 11.58236499130726 5.4282936453819257 1551.7522683441639 10.168696627020834 879.85487067699432 2.6328659802675247
|
||||
leaf_count=475 1114 49 33 7519 43 4276 17
|
||||
internal_value=-0.000172415 0.00932761 -0.00653072 -0.00713841 -0.0888634 0.0112876 0.117955
|
||||
internal_weight=2773.08 1111.85 1661.23 1653.17 21.7511 1090.1 8.06116
|
||||
internal_count=13526 5482 8044 7994 92 5390 50
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=16
|
||||
num_leaves=7
|
||||
num_cat=0
|
||||
split_feature=54 49 149 143 133 120
|
||||
split_gain=13.5064 12.5454 12.7609 9.52104 4.64479 6.69198
|
||||
threshold=1.6950000000000001 4.1950000000000012 0.53851874003189804 -2.5639999999999996 1.2110000000000001 3.0500000000000003
|
||||
decision_type=2 2 2 2 2 2
|
||||
left_child=1 3 -3 -1 -2 -6
|
||||
right_child=4 2 -4 -5 5 -7
|
||||
leaf_value=-0.11165723984306615 -0.022805170561939463 -0.034049153155287885 0.071318390164509041 0.0013999129723307169 0.11023664759120146 -0.064625521974505357
|
||||
leaf_weight=8.1074528247117978 4.7248373478651073 177.6567175835371 13.41981780529022 2544.4961924999952 20.898206591606144 2.6760432869195929
|
||||
leaf_count=41 29 758 58 12491 132 17
|
||||
internal_value=-0.000148839 -0.000887801 -0.0266458 0.00104042 0.0714722 0.0903692
|
||||
internal_weight=2771.98 2743.68 191.077 2552.6 28.2991 23.5742
|
||||
internal_count=13526 13348 816 12532 178 149
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=17
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=86 89 86 49 89 92 95
|
||||
split_gain=12.3529 16.6857 29.867 13.2745 12.903 19.095 7.28162
|
||||
threshold=2.9298029556650254 1.0000000180025095e-35 1.8603896103896107 3.3850000000000002 0.27000000000000007 -0.62124999999999986 8.25
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 3 -3 -1 6 -6 -2
|
||||
right_child=4 2 -4 -5 5 -7 -8
|
||||
leaf_value=0.0076740310795042049 0.058197093639699833 -0.094247277907232085 -0.010401082561217358 -0.014939641022089629 -0.10188514738956803 0.012348640807380216 -0.023647947189664047
|
||||
leaf_weight=1310.5064302459359 93.891582764685154 49.551189616322517 670.03426378965378 359.25059229135513 16.932199478149414 257.1773796081543 13.500689059495924
|
||||
leaf_count=6543 499 228 3182 1562 87 1350 75
|
||||
internal_value=-0.000128236 -0.00290891 -0.016176 0.00280861 0.0172867 0.00528897 0.0479069
|
||||
internal_weight=2770.84 2389.34 719.585 1669.76 381.502 274.11 107.392
|
||||
internal_count=13526 11515 3410 8105 2011 1437 574
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=18
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=149 56 148 55 148 53 92
|
||||
split_gain=15.7381 11.9383 11.3441 10.9087 9.49514 8.82734 5.7139
|
||||
threshold=0.53433908520280893 1.7550000000000001 0.26796584848230992 1.905 0.24185130950380487 2.1250000000000004 -0.0099999999999999482
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 4 -3 5 -1 -2 -5
|
||||
right_child=3 2 -4 6 -6 -7 -8
|
||||
leaf_value=-0.054435660661113758 0.0153280247340685 0.02819107947162831 -0.017585503842290271 -0.01840079708748131 -0.010724207322160621 -0.0015876644117591623 0.10180483948952145
|
||||
leaf_weight=56.825720146298409 617.56049958616495 165.87729875743389 91.08600726723671 5.4966678321361568 1074.6435647159815 732.60994145274162 19.602076262235641
|
||||
leaf_count=283 3334 829 448 27 4917 3565 97
|
||||
internal_value=-0.000486987 -0.00831442 0.0119646 0.00741537 -0.01292 0.00614953 0.0754826
|
||||
internal_weight=2763.7 1388.43 256.963 1375.27 1131.47 1350.17 25.0987
|
||||
internal_count=13500 6477 1277 7023 5200 6899 124
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=19
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=149 55 135 1 27 115 54
|
||||
split_gain=12.6285 9.13776 8.91602 12.9607 10.4883 7.46851 5.43256
|
||||
threshold=0.53433908520280893 1.905 0.30550000000000005 1494.2870431281676 0.50845238095238099 2.3665000000000007 1.3650000000000004
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=2 5 3 -1 -4 -2 -3
|
||||
right_child=1 6 4 -5 -6 -7 -8
|
||||
leaf_value=0.00092966247827827358 0.0028048803388890822 0.099877401856329082 -0.0069739024068449437 -0.021042388628781803 0.03967412519492558 0.027656530085268364 -0.011168774035797255
|
||||
leaf_weight=536.86709239333868 1198.4237125739455 17.76616628468037 128.78820982575417 638.33134125173092 88.204329490661621 147.51364935934544 6.5457842200994483
|
||||
leaf_count=2501 6137 84 598 2961 417 762 40
|
||||
internal_value=-0.000430022 0.0066729 -0.007421 -0.0110049 0.0119887 0.00552878 0.0699864
|
||||
internal_weight=2762.44 1370.25 1392.19 1175.2 216.993 1345.94 24.312
|
||||
internal_count=13500 7023 6477 5462 1015 6899 124
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=20
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=86 29 98 85 111 54 1
|
||||
split_gain=13.4852 11.6743 11.2269 10.3561 7.51975 9.02896 6.85752
|
||||
threshold=3.3717607973421928 3.8452380952380958 0.53589743589743599 -0.45393665158371038 1.7500000000000002 1.3250000000000004 1430.9302281483838
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 2 -1 -3 6 -6 -2
|
||||
right_child=4 3 -4 -5 5 -7 -8
|
||||
leaf_value=0.010161271773601144 -0.030831897558424644 -0.065476683381506764 -0.016436452248208454 0.004566632375329463 -0.017458853996767588 0.051773327327659352 0.10280712840651235
|
||||
leaf_weight=220.18627671897411 5.0186129659414318 23.250052616000175 793.86604422330856 1612.9528871029615 39.005949392914772 43.020874515175819 24.570655010640621
|
||||
leaf_count=1065 29 113 3791 7879 209 264 150
|
||||
internal_value=-0.000380007 -0.0018746 -0.0106611 0.00357096 0.0351053 0.0188533 0.0801381
|
||||
internal_weight=2761.87 2650.26 1014.05 1636.2 111.616 82.0268 29.5893
|
||||
internal_count=13500 12848 4856 7992 652 473 179
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=21
|
||||
num_leaves=7
|
||||
num_cat=0
|
||||
split_feature=96 132 136 141 123 120
|
||||
split_gain=13.2003 16.5388 14.2713 13.2653 11.1928 9.3524
|
||||
threshold=4.5000000000000009 0.64600000000000013 7.8500000000000005 -2.3269999999999995 0.73000000000000009 3.1835000000000004
|
||||
decision_type=2 2 2 2 2 2
|
||||
left_child=1 2 -1 -2 -3 -5
|
||||
right_child=3 4 -4 5 -6 -7
|
||||
leaf_value=-0.060960557629992744 -0.18516760103419116 0.033438636082733196 -0.0037890386549494811 0.011554321509567651 -0.0045660462097220139 -0.095189759824081052
|
||||
leaf_weight=104.0578725785017 3.772067964076995 88.859501846134663 87.140555322170258 867.02598301321268 1593.5797092542052 8.9989516139030439
|
||||
leaf_count=484 19 416 405 4401 7693 51
|
||||
internal_value=-0.000914735 -0.00585967 -0.0349056 0.00961608 -0.00255862 0.0104568
|
||||
internal_weight=2753.43 1873.64 191.198 879.797 1682.44 876.025
|
||||
internal_count=13469 8998 889 4471 8109 4452
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=22
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=86 96 132 53 17 141 14
|
||||
split_gain=11.7147 10.9619 13.1395 10.1877 3.25091 5.30827 0.81688
|
||||
threshold=3.9466666666666668 4.5000000000000009 0.64600000000000013 2.0250000000000004 1.0000000180025095e-35 0.45950000000000008 0.63333333333333341
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 2 -1 -3 5 -2 -6
|
||||
right_child=4 3 -4 -5 6 -7 -8
|
||||
leaf_value=-0.03177439277102298 0.091437039897303479 0.024857814401883283 -0.0029740838488449607 0.00058337476930965298 0.07050981561257999 -0.054418786094624118 0.13381725959363758
|
||||
leaf_weight=191.83759662508965 7.081265255808832 274.07831323891878 1669.0744777023792 596.54252921044827 3.1656967699527723 4.3793987929821014 7.6569998264312744
|
||||
leaf_count=880 41 1511 8020 2927 18 26 46
|
||||
internal_value=-0.000813155 -0.00142725 -0.00594319 0.00822535 0.0744319 0.0357031 0.115377
|
||||
internal_weight=2753.82 2731.53 1860.91 870.621 22.2834 11.4607 10.8227
|
||||
internal_count=13469 13338 8900 4438 131 67 64
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=23
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=54 86 91 40 130 51 10
|
||||
split_gain=12.222 10.6549 10.0211 7.99429 8.88031 6.65012 3.52018
|
||||
threshold=1.5750000000000004 3.9115384615384619 0.46333333333333332 1.4550000000000003 3.4720000000000004 1.0650000000000002 1.2583333333333335
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 2 -1 4 -2 -5 -3
|
||||
right_child=3 6 -4 5 -6 -7 -8
|
||||
leaf_value=-0.0022469666970611943 0.016170707495608474 0.01400989076087111 -0.12852821652405796 0.092762096676041614 -0.16668567356812367 0.014555154480083921 0.10191215472125625
|
||||
leaf_weight=2637.7453966140747 32.849811799824238 7.6601853668689754 6.834140643477439 32.402883179485798 3.1530902385711661 18.586706772446632 13.987728200852869
|
||||
leaf_count=12765 209 44 31 200 20 112 88
|
||||
internal_value=-0.000723185 -0.00197755 -0.00257376 0.0377177 0.000116917 0.0642621 0.0708273
|
||||
internal_weight=2753.22 2666.23 2644.58 86.9925 36.0029 50.9896 21.6479
|
||||
internal_count=13469 12928 12796 541 229 312 132
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=24
|
||||
num_leaves=7
|
||||
num_cat=0
|
||||
split_feature=49 137 127 10 121 40
|
||||
split_gain=12.6198 16.4964 12.4085 9.96103 9.53044 3.42942
|
||||
threshold=3.3850000000000002 2.8500000000000001 1.4365000000000003 4.3000000000000007 2.7750000000000004 2.7850000000000006
|
||||
decision_type=2 2 2 2 2 2
|
||||
left_child=3 5 -3 4 -1 -2
|
||||
right_child=1 2 -4 -5 -6 -7
|
||||
leaf_value=0.0033265147337535523 -0.2293764266460791 -0.0035570344953408817 -0.040820787979045939 -0.15680120567767367 0.077284263807364459 -0.095337183505289247
|
||||
leaf_weight=2143.9148783534765 3.9466704875230771 459.63925896584988 123.04180367290974 4.1842633336782447 19.085369817912579 4.4060309380292892
|
||||
leaf_count=10807 17 1977 532 22 100 19
|
||||
internal_value=-1.32262e-05 -0.0135124 -0.0114264 0.00366831 0.00397942 -0.158858
|
||||
internal_weight=2758.22 591.034 582.681 2167.18 2163 8.3527
|
||||
internal_count=13474 2545 2509 10929 10907 36
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=25
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=49 6 94 56 127 104 7
|
||||
split_gain=10.0943 10.6231 11.9381 10.4719 9.83816 10.0485 9.11168
|
||||
threshold=3.3850000000000002 1485.1782059621762 17.550000000000004 2.1050000000000004 1.4365000000000003 0.42950000000000005 3.0523414722088096
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 3 -3 -1 6 -6 -2
|
||||
right_child=4 2 -4 -5 5 -7 -8
|
||||
leaf_value=0.014478237478344286 -0.015463014973786505 -0.0073632514981839545 0.0096110031081186563 0.078530598053871026 -0.0086524748202200501 -0.067456935030922358 0.015332839885013407
|
||||
leaf_weight=308.73068219423294 309.28241093456745 1017.4353533536196 807.05070595443249 30.452116876840591 63.547168910503387 62.713204026222229 157.49458535015583
|
||||
leaf_count=1561 1327 5053 4143 172 271 269 678
|
||||
internal_value=-7.78723e-06 0.00329399 0.000145221 0.0202309 -0.012054 -0.0378634 -0.00507213
|
||||
internal_weight=2756.71 2163.67 1824.49 339.183 593.037 126.26 466.777
|
||||
internal_count=13474 10929 9196 1733 2545 540 2005
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=26
|
||||
num_leaves=7
|
||||
num_cat=0
|
||||
split_feature=86 78 86 86 106 45
|
||||
split_gain=11.0807 19.2113 13.2975 16.5366 9.93615 9.16974
|
||||
threshold=2.6909814323607431 3.2071428571428577 1.0000000180025095e-35 1.8603896103896107 0.59050000000000014 1.1950000000000001
|
||||
decision_type=2 2 2 2 2 2
|
||||
left_child=2 4 5 -4 -2 -1
|
||||
right_child=1 -3 3 -5 -6 -7
|
||||
leaf_value=0.0057285473288771786 0.034425187503696976 -0.17261249206353166 -0.072289780038289356 -0.011539802081962551 0.0051253977786566941 -0.012137044156937154
|
||||
leaf_weight=1235.6497117057443 188.36661138385534 5.9902653545141211 54.218099623918533 475.07240612059832 377.99835128337145 417.49042452871799
|
||||
leaf_count=6255 973 33 239 2206 1968 1800
|
||||
internal_value=-1.60817e-06 0.0129056 -0.00338664 -0.0177639 0.0148706 0.00121666
|
||||
internal_weight=2754.79 572.355 2182.43 529.291 566.365 1653.14
|
||||
internal_count=13474 2974 10500 2445 2941 8055
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=27
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=148 141 115 107 104 4 134
|
||||
split_gain=10.1298 10.8135 12.2053 9.7232 15.8813 8.97442 7.29505
|
||||
threshold=0.17399267399267399 -0.79299999999999993 1.2250000000000003 0.40950000000000003 0.41250000000000003 1460.7202799166059 -0.30549999999999994
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=3 5 -3 6 -5 -2 -1
|
||||
right_child=1 2 -4 4 -6 -7 -8
|
||||
leaf_value=-0.10259253098907423 -0.03269054108208802 -0.0021082423175915925 0.015308582355162925 -0.13734624687852556 0.036154293999346325 -0.0035202193944307817 0.042506627852751083
|
||||
leaf_weight=4.9914454817771938 183.7718341127038 1603.1649219617248 600.83727415651083 20.186357498168949 7.9962391257286063 304.1783049851656 15.20464117079973
|
||||
leaf_count=25 894 7830 2977 102 43 1483 78
|
||||
internal_value=-0.0013178 -0.00046824 0.00263984 -0.0485807 -0.0881234 -0.0145068 0.00661435
|
||||
internal_weight=2740.33 2691.95 2204 48.3787 28.1826 487.95 20.1961
|
||||
internal_count=13432 13184 10807 248 145 2377 103
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=28
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=86 134 41 106 29 91 3
|
||||
split_gain=10.2371 10.3112 10.5364 10.1993 11.3955 9.81442 8.99213
|
||||
threshold=2.864250614250615 -0.43849999999999995 2.1050000000000004 0.95450000000000013 1.3875000000000002 -0.0054166666666666486 1670.9226550788155
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=3 2 -2 5 -5 -1 -3
|
||||
right_child=1 6 -4 4 -6 -7 -8
|
||||
leaf_value=-0.015667034131642239 -0.063328583368563382 0.021380817595676553 0.036600112461665557 -0.21945686012767968 -0.026560041934415114 0.0014656039323343243 -0.12083840209161807
|
||||
leaf_weight=461.02843705564737 34.460375130176544 373.77398503571749 17.162777505815029 3.4011466801166526 135.53743974119425 1711.2739861980081 4.8830340132117263
|
||||
leaf_count=2173 176 1994 103 17 658 8282 29
|
||||
internal_value=-0.0011736 0.0135866 -0.0301054 -0.00392153 -0.0312966 -0.00217051 0.0195438
|
||||
internal_weight=2741.52 430.28 51.6232 2311.24 138.939 2172.3 378.657
|
||||
internal_count=13432 2302 279 11130 675 10455 2023
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=29
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=12 84 115 141 136 11 110
|
||||
split_gain=9.65816 11.3219 10.1518 11.5361 10.0734 9.44977 6.86501
|
||||
threshold=1.0000000180025095e-35 0.017970779220779203 1.2915000000000003 -0.60349999999999981 3.6500000000000008 2.1083333333333338 27.750000000000004
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 6 4 -4 -2 -3 -1
|
||||
right_child=2 5 3 -5 -6 -7 -8
|
||||
leaf_value=0.01695486197013276 0.018272032851518873 -0.047853351303007219 -0.020306195413698006 0.015936397489881831 -0.011004436565708025 0.099082325881264394 -0.086337197330871959
|
||||
leaf_weight=520.42061326652765 139.83185759186745 50.192012257874012 122.51054417341948 430.93686553835869 1465.7739738970995 5.2428159564733496 7.0750899761915198
|
||||
leaf_count=2585 694 257 602 2140 7090 29 35
|
||||
internal_value=-0.00104481 0.0108592 -0.00425885 0.00791357 -0.00845466 -0.0339422 0.0155679
|
||||
internal_weight=2741.98 582.931 2159.05 553.447 1605.61 55.4348 527.496
|
||||
internal_count=13432 2906 10526 2742 7784 286 2620
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=30
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=120 37 38 38 105 85 100
|
||||
split_gain=12.0364 15.2453 11.9342 13.3835 10.0958 10.0578 10.0546
|
||||
threshold=1.4145000000000001 0.42084520042422408 0.23906113829447367 0.14593794095256588 1.1225000000000003 -0.37141065830721004 1.3650000000000002
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=2 6 3 -1 -3 -4 -2
|
||||
right_child=1 4 5 -5 -6 -7 -8
|
||||
leaf_value=-0.12038760226397151 -0.0090962232080863334 0.0034107878159782353 -0.060847190395801561 0.022337300771774038 -0.044228760615057698 -0.0060312321358531873 0.030222541925148897
|
||||
leaf_weight=7.3577561527490607 84.787891268730164 592.17002998292446 37.370823763310909 228.1118380650878 52.602927520871162 1330.5593820437789 423.39983003586531
|
||||
leaf_count=43 418 3057 171 1205 280 6147 2165
|
||||
internal_value=0.00204154 0.0101633 -0.00379863 0.0178729 -0.00047641 -0.00752917 0.0236624
|
||||
internal_weight=2756.36 1152.96 1603.4 235.47 644.773 1367.93 508.188
|
||||
internal_count=13486 5920 7566 1248 3337 6318 2583
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=31
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=86 86 86 1 127 31 122
|
||||
split_gain=10.4188 10.4465 11.9432 10.3703 15.906 10.0392 7.58651
|
||||
threshold=2.6939799331103687 1.0000000180025095e-35 1.8603896103896107 1467.9553804385575 2.1340000000000003 0.22980739360049704 2.3515000000000006
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=1 6 -3 5 -5 -2 -1
|
||||
right_child=3 2 -4 4 -6 -7 -8
|
||||
leaf_value=0.0014256124570572787 0.0018941270616234518 -0.058645654322910731 -0.0086447877444392647 0.02733676763309234 -0.12590863729503893 -0.082423971345859881 0.042831761918242235
|
||||
leaf_weight=1608.7813730537891 166.28092505782843 58.273459360003471 473.13907171785831 372.2649027556181 7.4961845725774756 16.887378051877022 49.535993173718452
|
||||
leaf_count=7823 854 251 2181 1963 43 94 277
|
||||
internal_value=0.00183871 -0.00141235 -0.0141287 0.0144848 0.0243088 -0.0058835 0.0026627
|
||||
internal_weight=2752.66 2189.73 531.413 562.929 379.761 183.168 1658.32
|
||||
internal_count=13486 10532 2432 2954 2006 948 8100
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=32
|
||||
num_leaves=8
|
||||
num_cat=0
|
||||
split_feature=93 144 112 94 104 93 120
|
||||
split_gain=10.1721 16.1941 11.2364 13.1765 9.78364 8.96284 8.48134
|
||||
threshold=20.550000000000004 0.013450000000000002 0.22750000000000004 5.5500000000000007 1.2865000000000002 25.050000000000004 2.0500000000000003
|
||||
decision_type=2 2 2 2 2 2 2
|
||||
left_child=2 4 6 -4 -2 -3 -1
|
||||
right_child=1 5 3 -5 -6 -7 -8
|
||||
leaf_value=0.0034609471658556285 0.020050548549636428 -0.009082334773132807 -0.11004659616173273 -0.007594432301873808 -0.021486441758403186 -0.13291664093499478 0.038221762386595907
|
||||
leaf_weight=820.33589915931225 594.67776323109865 13.120999380946161 13.791079476475714 1142.1561977639794 68.70460732281208 12.293409973382948 84.046657353639603
|
||||
leaf_count=3903 3054 71 65 5495 382 70 446
|
||||
internal_value=0.0016558 0.0126202 -0.00200979 -0.00881763 0.0157484 -0.0690103 0.00669173
|
||||
internal_weight=2749.13 688.797 2060.33 1155.95 663.382 25.4144 904.383
|
||||
internal_count=13486 3577 9909 5560 3436 141 4349
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=33
|
||||
num_leaves=5
|
||||
num_cat=0
|
||||
split_feature=91 141 123 7
|
||||
split_gain=9.64033 9.54985 10.2025 8.96435
|
||||
threshold=0.46333333333333332 -0.77399999999999991 2.2565000000000004 33.104396552584838
|
||||
decision_type=2 2 2 2
|
||||
left_child=1 3 -3 -1
|
||||
right_child=-2 2 -4 -5
|
||||
leaf_value=-0.015732035707484552 -0.12133869515037762 0.0038526132779362976 0.057630556320633561 0.031446003016811871
|
||||
leaf_weight=450.80555958300829 6.9056807309389105 2188.2216669544578 38.992827542126179 48.440718069672585
|
||||
leaf_count=2162 31 10769 213 250
|
||||
internal_value=0.00156231 0.00187402 0.00479437 -0.011154
|
||||
internal_weight=2733.37 2726.46 2227.21 499.246
|
||||
internal_count=13425 13394 10982 2412
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
Tree=34
|
||||
num_leaves=5
|
||||
num_cat=0
|
||||
split_feature=45 98 80 123
|
||||
split_gain=8.19305 8.37131 12.6079 10.3403
|
||||
threshold=1.6150000000000004 0.58114035087719318 0.043836402184014855 2.1715000000000004
|
||||
decision_type=2 2 2 2
|
||||
left_child=1 3 -3 -1
|
||||
right_child=-2 2 -4 -5
|
||||
leaf_value=0.008356015193296689 -0.14816646007318571 -0.033602776052007143 0.00019618273212945178 0.070940775625300892
|
||||
leaf_weight=749.49989224970341 3.9628616422414771 128.32899699360132 1819.463518589735 29.807201541960239
|
||||
leaf_count=3713 16 704 8830 162
|
||||
internal_value=0.00140384 0.0016217 -0.00203079 0.0107506
|
||||
internal_weight=2731.06 2727.1 1947.79 779.307
|
||||
internal_count=13425 13409 9534 3875
|
||||
is_linear=0
|
||||
shrinkage=0.104222
|
||||
|
||||
|
||||
end of trees
|
||||
|
||||
feature_importances:
|
||||
Column_86=18
|
||||
Column_49=9
|
||||
Column_53=8
|
||||
Column_41=6
|
||||
Column_54=6
|
||||
Column_120=6
|
||||
Column_141=6
|
||||
Column_45=5
|
||||
Column_91=5
|
||||
Column_104=5
|
||||
Column_1=4
|
||||
Column_94=4
|
||||
Column_96=4
|
||||
Column_132=4
|
||||
Column_136=4
|
||||
Column_148=4
|
||||
Column_3=3
|
||||
Column_4=3
|
||||
Column_6=3
|
||||
Column_11=3
|
||||
Column_30=3
|
||||
Column_35=3
|
||||
Column_38=3
|
||||
Column_48=3
|
||||
Column_50=3
|
||||
Column_85=3
|
||||
Column_92=3
|
||||
Column_105=3
|
||||
Column_115=3
|
||||
Column_123=3
|
||||
Column_127=3
|
||||
Column_130=3
|
||||
Column_133=3
|
||||
Column_149=3
|
||||
Column_150=3
|
||||
Column_0=2
|
||||
Column_7=2
|
||||
Column_10=2
|
||||
Column_16=2
|
||||
Column_29=2
|
||||
Column_40=2
|
||||
Column_44=2
|
||||
Column_51=2
|
||||
Column_52=2
|
||||
Column_55=2
|
||||
Column_56=2
|
||||
Column_89=2
|
||||
Column_93=2
|
||||
Column_98=2
|
||||
Column_100=2
|
||||
Column_102=2
|
||||
Column_106=2
|
||||
Column_110=2
|
||||
Column_111=2
|
||||
Column_122=2
|
||||
Column_134=2
|
||||
Column_135=2
|
||||
Column_137=2
|
||||
Column_143=2
|
||||
Column_144=2
|
||||
Column_147=2
|
||||
Column_8=1
|
||||
Column_12=1
|
||||
Column_14=1
|
||||
Column_17=1
|
||||
Column_21=1
|
||||
Column_27=1
|
||||
Column_28=1
|
||||
Column_31=1
|
||||
Column_34=1
|
||||
Column_37=1
|
||||
Column_39=1
|
||||
Column_46=1
|
||||
Column_47=1
|
||||
Column_78=1
|
||||
Column_80=1
|
||||
Column_84=1
|
||||
Column_95=1
|
||||
Column_99=1
|
||||
Column_107=1
|
||||
Column_112=1
|
||||
Column_119=1
|
||||
Column_121=1
|
||||
Column_125=1
|
||||
Column_126=1
|
||||
Column_145=1
|
||||
|
||||
parameters:
|
||||
[boosting: gbdt]
|
||||
[objective: binary]
|
||||
[metric: binary_logloss]
|
||||
[tree_learner: serial]
|
||||
[device_type: cpu]
|
||||
[data_sample_strategy: bagging]
|
||||
[data: ]
|
||||
[valid: ]
|
||||
[num_iterations: 1500]
|
||||
[learning_rate: 0.104222]
|
||||
[num_leaves: 8]
|
||||
[num_threads: 4]
|
||||
[seed: 42]
|
||||
[deterministic: 0]
|
||||
[force_col_wise: 0]
|
||||
[force_row_wise: 0]
|
||||
[histogram_pool_size: -1]
|
||||
[max_depth: 3]
|
||||
[min_data_in_leaf: 17]
|
||||
[min_sum_hessian_in_leaf: 0.001]
|
||||
[bagging_fraction: 0.709098]
|
||||
[pos_bagging_fraction: 1]
|
||||
[neg_bagging_fraction: 1]
|
||||
[bagging_freq: 3]
|
||||
[bagging_seed: 400]
|
||||
[bagging_by_query: 0]
|
||||
[feature_fraction: 0.58888]
|
||||
[feature_fraction_bynode: 1]
|
||||
[feature_fraction_seed: 30056]
|
||||
[extra_trees: 0]
|
||||
[extra_seed: 12879]
|
||||
[early_stopping_round: 0]
|
||||
[early_stopping_min_delta: 0]
|
||||
[first_metric_only: 0]
|
||||
[max_delta_step: 0]
|
||||
[lambda_l1: 7.81041e-07]
|
||||
[lambda_l2: 0.00942891]
|
||||
[linear_lambda: 0]
|
||||
[min_gain_to_split: 0]
|
||||
[drop_rate: 0.1]
|
||||
[max_drop: 50]
|
||||
[skip_drop: 0.5]
|
||||
[xgboost_dart_mode: 0]
|
||||
[uniform_drop: 0]
|
||||
[drop_seed: 17869]
|
||||
[top_rate: 0.2]
|
||||
[other_rate: 0.1]
|
||||
[min_data_per_group: 100]
|
||||
[max_cat_threshold: 32]
|
||||
[cat_l2: 10]
|
||||
[cat_smooth: 10]
|
||||
[max_cat_to_onehot: 4]
|
||||
[top_k: 20]
|
||||
[monotone_constraints: ]
|
||||
[monotone_constraints_method: basic]
|
||||
[monotone_penalty: 0]
|
||||
[feature_contri: ]
|
||||
[forcedsplits_filename: ]
|
||||
[refit_decay_rate: 0.9]
|
||||
[cegb_tradeoff: 1]
|
||||
[cegb_penalty_split: 0]
|
||||
[cegb_penalty_feature_lazy: ]
|
||||
[cegb_penalty_feature_coupled: ]
|
||||
[path_smooth: 0]
|
||||
[interaction_constraints: ]
|
||||
[verbosity: -1]
|
||||
[saved_feature_importance_type: 0]
|
||||
[use_quantized_grad: 0]
|
||||
[num_grad_quant_bins: 4]
|
||||
[quant_train_renew_leaf: 0]
|
||||
[stochastic_rounding: 1]
|
||||
[linear_tree: 0]
|
||||
[max_bin: 255]
|
||||
[max_bin_by_feature: ]
|
||||
[min_data_in_bin: 3]
|
||||
[bin_construct_sample_cnt: 200000]
|
||||
[data_random_seed: 175]
|
||||
[is_enable_sparse: 1]
|
||||
[enable_bundle: 1]
|
||||
[use_missing: 1]
|
||||
[zero_as_missing: 0]
|
||||
[feature_pre_filter: 1]
|
||||
[pre_partition: 0]
|
||||
[two_round: 0]
|
||||
[header: 0]
|
||||
[label_column: ]
|
||||
[weight_column: ]
|
||||
[group_column: ]
|
||||
[ignore_column: ]
|
||||
[categorical_feature: ]
|
||||
[forcedbins_filename: ]
|
||||
[precise_float_parser: 0]
|
||||
[parser_config_file: ]
|
||||
[objective_seed: 16083]
|
||||
[num_class: 1]
|
||||
[is_unbalance: 0]
|
||||
[scale_pos_weight: 1]
|
||||
[sigmoid: 1]
|
||||
[boost_from_average: 1]
|
||||
[reg_sqrt: 0]
|
||||
[alpha: 0.9]
|
||||
[fair_c: 1]
|
||||
[poisson_max_delta_step: 0.7]
|
||||
[tweedie_variance_power: 1.5]
|
||||
[lambdarank_truncation_level: 30]
|
||||
[lambdarank_norm: 1]
|
||||
[label_gain: ]
|
||||
[lambdarank_position_bias_regularization: 0]
|
||||
[eval_at: ]
|
||||
[multi_error_top_k: 1]
|
||||
[auc_mu_weights: ]
|
||||
[num_machines: 1]
|
||||
[local_listen_port: 12400]
|
||||
[time_out: 120]
|
||||
[machine_list_filename: ]
|
||||
[machines: ]
|
||||
[gpu_platform_id: -1]
|
||||
[gpu_device_id: -1]
|
||||
[gpu_use_dp: 0]
|
||||
[num_gpu: 1]
|
||||
|
||||
end of parameters
|
||||
|
||||
pandas_categorical:null
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,735 @@
|
||||
"""
|
||||
V25 Ensemble Predictor - NO TARGET LEAKAGE
|
||||
===========================================
|
||||
Multi-model ensemble for match prediction using XGBoost and LightGBM.
|
||||
|
||||
Features:
|
||||
- 73 engineered features (NO target leakage)
|
||||
- Market-specific models (MS, OU25, BTTS)
|
||||
- Weighted ensemble predictions
|
||||
- Value bet detection
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from typing import Dict, List, Optional, Any
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
import xgboost as xgb
|
||||
import lightgbm as lgb
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
try:
|
||||
from config.config_loader import get_config as _get_cfg
|
||||
except ImportError:
|
||||
_get_cfg = None # type: ignore[assignment]
|
||||
|
||||
# CatBoost is optional
|
||||
try:
|
||||
from catboost import CatBoostClassifier
|
||||
CATBOOST_AVAILABLE = True
|
||||
except ImportError:
|
||||
CatBoostClassifier = None
|
||||
CATBOOST_AVAILABLE = False
|
||||
|
||||
# Paths
|
||||
MODELS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'v25')
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarketPrediction:
|
||||
"""Prediction for a single betting market."""
|
||||
market_type: str
|
||||
pick: str
|
||||
probability: float
|
||||
confidence: float
|
||||
odds: float = 0.0
|
||||
is_value_bet: bool = False
|
||||
edge: float = 0.0
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'market_type': self.market_type,
|
||||
'pick': self.pick,
|
||||
'probability': round(self.probability * 100, 1),
|
||||
'confidence': round(self.confidence, 1),
|
||||
'odds': self.odds,
|
||||
'is_value_bet': self.is_value_bet,
|
||||
'edge': round(self.edge * 100, 1),
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ValueBet:
|
||||
"""Detected value bet opportunity."""
|
||||
market_type: str
|
||||
pick: str
|
||||
probability: float
|
||||
odds: float
|
||||
edge: float
|
||||
confidence: float
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'market_type': self.market_type,
|
||||
'pick': self.pick,
|
||||
'probability': round(self.probability * 100, 1),
|
||||
'odds': self.odds,
|
||||
'edge': round(self.edge * 100, 1),
|
||||
'confidence': round(self.confidence, 1),
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class MatchPrediction:
|
||||
"""Complete match prediction with all markets."""
|
||||
match_id: str
|
||||
home_team: str
|
||||
away_team: str
|
||||
|
||||
# MS predictions
|
||||
home_prob: float = 0.0
|
||||
draw_prob: float = 0.0
|
||||
away_prob: float = 0.0
|
||||
ms_pick: str = ''
|
||||
ms_confidence: float = 0.0
|
||||
|
||||
# OU25 predictions
|
||||
over_prob: float = 0.0
|
||||
under_prob: float = 0.0
|
||||
ou25_pick: str = ''
|
||||
ou25_confidence: float = 0.0
|
||||
|
||||
# BTTS predictions
|
||||
btts_yes_prob: float = 0.0
|
||||
btts_no_prob: float = 0.0
|
||||
btts_pick: str = ''
|
||||
btts_confidence: float = 0.0
|
||||
|
||||
# Value bets
|
||||
value_bets: List[ValueBet] = field(default_factory=list)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'match_id': self.match_id,
|
||||
'home_team': self.home_team,
|
||||
'away_team': self.away_team,
|
||||
'ms': {
|
||||
'home_prob': round(self.home_prob * 100, 1),
|
||||
'draw_prob': round(self.draw_prob * 100, 1),
|
||||
'away_prob': round(self.away_prob * 100, 1),
|
||||
'pick': self.ms_pick,
|
||||
'confidence': round(self.ms_confidence, 1),
|
||||
},
|
||||
'ou25': {
|
||||
'over_prob': round(self.over_prob * 100, 1),
|
||||
'under_prob': round(self.under_prob * 100, 1),
|
||||
'pick': self.ou25_pick,
|
||||
'confidence': round(self.ou25_confidence, 1),
|
||||
},
|
||||
'btts': {
|
||||
'yes_prob': round(self.btts_yes_prob * 100, 1),
|
||||
'no_prob': round(self.btts_no_prob * 100, 1),
|
||||
'pick': self.btts_pick,
|
||||
'confidence': round(self.btts_confidence, 1),
|
||||
},
|
||||
'value_bets': [vb.to_dict() for vb in self.value_bets],
|
||||
}
|
||||
|
||||
|
||||
class V25Predictor:
|
||||
"""
|
||||
V25 Ensemble Predictor - NO TARGET LEAKAGE
|
||||
|
||||
Uses market-specific XGBoost and LightGBM models.
|
||||
Each market (MS, OU25, BTTS) has its own trained models.
|
||||
"""
|
||||
|
||||
# Feature columns — loaded dynamically from feature_cols.json to stay
|
||||
# in sync with the trained models. The hardcoded list below is only a
|
||||
# fallback in case the JSON file is missing.
|
||||
_FALLBACK_FEATURE_COLS = [
|
||||
# ELO Features (8)
|
||||
'home_overall_elo', 'away_overall_elo', 'elo_diff',
|
||||
'home_home_elo', 'away_away_elo',
|
||||
'home_form_elo', 'away_form_elo', 'form_elo_diff',
|
||||
|
||||
# Form Features (12)
|
||||
'home_goals_avg', 'home_conceded_avg',
|
||||
'away_goals_avg', 'away_conceded_avg',
|
||||
'home_clean_sheet_rate', 'away_clean_sheet_rate',
|
||||
'home_scoring_rate', 'away_scoring_rate',
|
||||
'home_winning_streak', 'away_winning_streak',
|
||||
'home_unbeaten_streak', 'away_unbeaten_streak',
|
||||
|
||||
# H2H Features (6)
|
||||
'h2h_total_matches', 'h2h_home_win_rate', 'h2h_draw_rate',
|
||||
'h2h_avg_goals', 'h2h_btts_rate', 'h2h_over25_rate',
|
||||
|
||||
# Team Stats Features (8)
|
||||
'home_avg_possession', 'away_avg_possession',
|
||||
'home_avg_shots_on_target', 'away_avg_shots_on_target',
|
||||
'home_shot_conversion', 'away_shot_conversion',
|
||||
'home_avg_corners', 'away_avg_corners',
|
||||
|
||||
# Odds Features (24)
|
||||
'odds_ms_h', 'odds_ms_d', 'odds_ms_a',
|
||||
'implied_home', 'implied_draw', 'implied_away',
|
||||
'odds_ht_ms_h', 'odds_ht_ms_d', 'odds_ht_ms_a',
|
||||
'odds_ou05_o', 'odds_ou05_u',
|
||||
'odds_ou15_o', 'odds_ou15_u',
|
||||
'odds_ou25_o', 'odds_ou25_u',
|
||||
'odds_ou35_o', 'odds_ou35_u',
|
||||
'odds_ht_ou05_o', 'odds_ht_ou05_u',
|
||||
'odds_ht_ou15_o', 'odds_ht_ou15_u',
|
||||
'odds_btts_y', 'odds_btts_n',
|
||||
|
||||
# Odds Presence Flags (20)
|
||||
'odds_ms_h_present', 'odds_ms_d_present', 'odds_ms_a_present',
|
||||
'odds_ht_ms_h_present', 'odds_ht_ms_d_present', 'odds_ht_ms_a_present',
|
||||
'odds_ou05_o_present', 'odds_ou05_u_present',
|
||||
'odds_ou15_o_present', 'odds_ou15_u_present',
|
||||
'odds_ou25_o_present', 'odds_ou25_u_present',
|
||||
'odds_ou35_o_present', 'odds_ou35_u_present',
|
||||
'odds_ht_ou05_o_present', 'odds_ht_ou05_u_present',
|
||||
'odds_ht_ou15_o_present', 'odds_ht_ou15_u_present',
|
||||
'odds_btts_y_present', 'odds_btts_n_present',
|
||||
|
||||
# League Features (4)
|
||||
'home_xga', 'away_xga',
|
||||
'league_avg_goals', 'league_zero_goal_rate',
|
||||
|
||||
# Upset Engine (4)
|
||||
'upset_atmosphere', 'upset_motivation', 'upset_fatigue', 'upset_potential',
|
||||
|
||||
# Referee Engine (5)
|
||||
'referee_home_bias', 'referee_avg_goals', 'referee_cards_total',
|
||||
'referee_avg_yellow', 'referee_experience',
|
||||
|
||||
# Momentum Engine (3)
|
||||
'home_momentum_score', 'away_momentum_score', 'momentum_diff',
|
||||
|
||||
# Squad Features (9)
|
||||
'home_squad_quality', 'away_squad_quality', 'squad_diff',
|
||||
'home_key_players', 'away_key_players',
|
||||
'home_missing_impact', 'away_missing_impact',
|
||||
'home_goals_form', 'away_goals_form',
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _load_feature_cols() -> list:
|
||||
"""Load feature columns from feature_cols.json, falling back to hardcoded list."""
|
||||
feature_json = os.path.join(MODELS_DIR, 'feature_cols.json')
|
||||
try:
|
||||
if os.path.exists(feature_json):
|
||||
with open(feature_json, 'r', encoding='utf-8') as f:
|
||||
cols = json.load(f)
|
||||
if isinstance(cols, list) and len(cols) > 0:
|
||||
print(f"[V25] Loaded {len(cols)} feature columns from feature_cols.json")
|
||||
return cols
|
||||
except Exception as e:
|
||||
print(f"[V25] Warning: could not load feature_cols.json: {e}")
|
||||
print(f"[V25] Using fallback feature columns ({len(V25Predictor._FALLBACK_FEATURE_COLS)} features)")
|
||||
return V25Predictor._FALLBACK_FEATURE_COLS
|
||||
|
||||
# Model weights for ensemble (overridden from config in __init__)
|
||||
DEFAULT_WEIGHTS = {
|
||||
'xgb': 0.50,
|
||||
'lgb': 0.50,
|
||||
}
|
||||
|
||||
def __init__(self, models_dir: Optional[str] = None):
|
||||
"""
|
||||
Initialize V25 Predictor.
|
||||
|
||||
Args:
|
||||
models_dir: Directory containing model files. Defaults to v25/ directory.
|
||||
"""
|
||||
self.models_dir = models_dir or MODELS_DIR
|
||||
self.models = {} # market -> {'xgb': model, 'lgb': model}
|
||||
self._loaded = False
|
||||
self.FEATURE_COLS = self._load_feature_cols()
|
||||
# Load weights from config (falls back to class default 0.50/0.50)
|
||||
if _get_cfg is not None:
|
||||
try:
|
||||
cfg = _get_cfg()
|
||||
self.DEFAULT_WEIGHTS = {
|
||||
'xgb': float(cfg.get('model_ensemble.xgb_weight', 0.50)),
|
||||
'lgb': float(cfg.get('model_ensemble.lgb_weight', 0.50)),
|
||||
}
|
||||
except Exception:
|
||||
pass # keep class-level defaults
|
||||
|
||||
# All trained market models available in V25
|
||||
ALL_MARKETS = [
|
||||
'ms', 'ou25', 'btts', # Core markets
|
||||
'ou15', 'ou35', # Additional OU lines
|
||||
'ht_result', 'ht_ou05', 'ht_ou15', # HT markets
|
||||
'htft', # HT/FT combo
|
||||
'cards_ou45', # Cards market
|
||||
'handicap_ms', # Handicap
|
||||
'odd_even', # Odd/Even goals
|
||||
]
|
||||
|
||||
# Multi-class markets (output > 2 classes)
|
||||
MULTICLASS_MARKETS = {'ms', 'ht_result', 'htft', 'handicap_ms'}
|
||||
|
||||
def load_models(self) -> bool:
|
||||
"""Load all market-specific models from disk."""
|
||||
try:
|
||||
loaded_count = 0
|
||||
|
||||
for market in self.ALL_MARKETS:
|
||||
self.models[market] = {}
|
||||
|
||||
# Load XGBoost (read content in Python to avoid non-ASCII path issues)
|
||||
xgb_path = os.path.join(self.models_dir, f'xgb_v25_{market}.json')
|
||||
if os.path.exists(xgb_path) and os.path.getsize(xgb_path) > 0:
|
||||
with open(xgb_path, 'r', encoding='utf-8') as f:
|
||||
xgb_content = f.read()
|
||||
booster = xgb.Booster()
|
||||
booster.load_model(bytearray(xgb_content, 'utf-8'))
|
||||
# Corruption detection: verify model can run a dummy prediction
|
||||
try:
|
||||
_dummy = pd.DataFrame([{col: 0.0 for col in self.FEATURE_COLS}])
|
||||
booster.predict(xgb.DMatrix(_dummy))
|
||||
self.models[market]['xgb'] = booster
|
||||
loaded_count += 1
|
||||
except Exception as _ce:
|
||||
print(f"[V25] ⚠️ XGB model for {market} failed integrity check: {_ce} — skipping")
|
||||
|
||||
# Load LightGBM (read content in Python to avoid non-ASCII path issues)
|
||||
lgb_path = os.path.join(self.models_dir, f'lgb_v25_{market}.txt')
|
||||
if os.path.exists(lgb_path) and os.path.getsize(lgb_path) > 0:
|
||||
with open(lgb_path, 'r', encoding='utf-8') as f:
|
||||
model_str = f.read()
|
||||
lgb_model = lgb.Booster(model_str=model_str)
|
||||
# Corruption detection: verify model can run a dummy prediction
|
||||
try:
|
||||
_dummy = pd.DataFrame([{col: 0.0 for col in self.FEATURE_COLS}])
|
||||
lgb_model.predict(_dummy)
|
||||
self.models[market]['lgb'] = lgb_model
|
||||
loaded_count += 1
|
||||
except Exception as _ce:
|
||||
print(f"[V25] ⚠️ LGB model for {market} failed integrity check: {_ce} — skipping")
|
||||
|
||||
# Remove empty entries
|
||||
if not self.models[market]:
|
||||
del self.models[market]
|
||||
|
||||
print(f"[V25] Loaded {loaded_count} model files across {len(self.models)} markets: {list(self.models.keys())}")
|
||||
self._loaded = loaded_count > 0
|
||||
return self._loaded
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Error loading models: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def _ensure_loaded(self):
|
||||
"""Ensure models are loaded before prediction."""
|
||||
if not self._loaded:
|
||||
if not self.load_models():
|
||||
raise RuntimeError("Failed to load V25 models")
|
||||
|
||||
def readiness_summary(self) -> Dict[str, Any]:
|
||||
"""Return per-market model status for health check endpoint."""
|
||||
if not self._loaded:
|
||||
self.load_models()
|
||||
market_status = {}
|
||||
for market in self.ALL_MARKETS:
|
||||
m = self.models.get(market, {})
|
||||
market_status[market] = {
|
||||
"xgb": "xgb" in m,
|
||||
"lgb": "lgb" in m,
|
||||
"ready": bool(m),
|
||||
}
|
||||
loaded_markets = [k for k, v in market_status.items() if v["ready"]]
|
||||
return {
|
||||
"fully_loaded": len(loaded_markets) == len(self.ALL_MARKETS),
|
||||
"loaded_markets": loaded_markets,
|
||||
"missing_markets": [m for m in self.ALL_MARKETS if m not in loaded_markets],
|
||||
"weights": self.DEFAULT_WEIGHTS,
|
||||
}
|
||||
|
||||
def _prepare_features(self, features: Dict[str, float]) -> pd.DataFrame:
|
||||
"""Prepare feature vector for prediction."""
|
||||
X = pd.DataFrame([{col: features.get(col, 0.0) for col in self.FEATURE_COLS}])
|
||||
return X
|
||||
|
||||
def predict_ms(self, features: Dict[str, float]) -> tuple:
|
||||
"""
|
||||
Predict match result (1X2).
|
||||
|
||||
Returns:
|
||||
(home_prob, draw_prob, away_prob)
|
||||
"""
|
||||
self._ensure_loaded()
|
||||
|
||||
X = self._prepare_features(features)
|
||||
probs = []
|
||||
|
||||
# XGBoost
|
||||
if 'xgb' in self.models.get('ms', {}):
|
||||
dmat = xgb.DMatrix(X)
|
||||
xgb_proba = self.models['ms']['xgb'].predict(dmat)
|
||||
if len(xgb_proba.shape) == 1:
|
||||
xgb_proba = np.array([xgb_proba])
|
||||
probs.append(xgb_proba[0] * self.DEFAULT_WEIGHTS['xgb'])
|
||||
|
||||
# LightGBM
|
||||
if 'lgb' in self.models.get('ms', {}):
|
||||
lgb_proba = self.models['ms']['lgb'].predict(X)
|
||||
if len(lgb_proba.shape) == 2:
|
||||
probs.append(lgb_proba[0] * self.DEFAULT_WEIGHTS['lgb'])
|
||||
|
||||
if not probs:
|
||||
return 0.33, 0.33, 0.33
|
||||
|
||||
ensemble_proba = np.sum(probs, axis=0)
|
||||
ensemble_proba = ensemble_proba / ensemble_proba.sum()
|
||||
|
||||
return float(ensemble_proba[0]), float(ensemble_proba[1]), float(ensemble_proba[2])
|
||||
|
||||
def predict_ou25(self, features: Dict[str, float]) -> tuple:
|
||||
"""
|
||||
Predict Over/Under 2.5 goals.
|
||||
|
||||
Returns:
|
||||
(over_prob, under_prob)
|
||||
"""
|
||||
self._ensure_loaded()
|
||||
|
||||
X = self._prepare_features(features)
|
||||
probs = []
|
||||
|
||||
# XGBoost
|
||||
if 'xgb' in self.models.get('ou25', {}):
|
||||
dmat = xgb.DMatrix(X)
|
||||
xgb_proba = self.models['ou25']['xgb'].predict(dmat)
|
||||
if isinstance(xgb_proba, np.ndarray) and len(xgb_proba.shape) == 1:
|
||||
probs.append(xgb_proba[0])
|
||||
|
||||
# LightGBM
|
||||
if 'lgb' in self.models.get('ou25', {}):
|
||||
lgb_proba = self.models['ou25']['lgb'].predict(X)
|
||||
if isinstance(lgb_proba, np.ndarray):
|
||||
probs.append(lgb_proba[0])
|
||||
|
||||
if not probs:
|
||||
return 0.5, 0.5
|
||||
|
||||
# Average probability
|
||||
avg_prob = np.mean(probs)
|
||||
|
||||
return float(avg_prob), float(1 - avg_prob)
|
||||
|
||||
def predict_btts(self, features: Dict[str, float]) -> tuple:
|
||||
"""
|
||||
Predict Both Teams To Score.
|
||||
|
||||
Returns:
|
||||
(yes_prob, no_prob)
|
||||
"""
|
||||
self._ensure_loaded()
|
||||
|
||||
X = self._prepare_features(features)
|
||||
probs = []
|
||||
|
||||
# XGBoost
|
||||
if 'xgb' in self.models.get('btts', {}):
|
||||
dmat = xgb.DMatrix(X)
|
||||
xgb_proba = self.models['btts']['xgb'].predict(dmat)
|
||||
if isinstance(xgb_proba, np.ndarray) and len(xgb_proba.shape) == 1:
|
||||
probs.append(xgb_proba[0])
|
||||
|
||||
# LightGBM
|
||||
if 'lgb' in self.models.get('btts', {}):
|
||||
lgb_proba = self.models['btts']['lgb'].predict(X)
|
||||
if isinstance(lgb_proba, np.ndarray):
|
||||
probs.append(lgb_proba[0])
|
||||
|
||||
if not probs:
|
||||
return 0.5, 0.5
|
||||
|
||||
# Average probability
|
||||
avg_prob = np.mean(probs)
|
||||
|
||||
return float(avg_prob), float(1 - avg_prob)
|
||||
|
||||
def predict_market(self, market: str, features: Dict[str, float]) -> Optional[np.ndarray]:
|
||||
"""
|
||||
Generic prediction for any loaded market.
|
||||
|
||||
Args:
|
||||
market: Market key (e.g. 'ht_result', 'htft', 'cards_ou45')
|
||||
features: Feature dictionary.
|
||||
|
||||
Returns:
|
||||
numpy array of probabilities.
|
||||
For binary markets: [positive_prob]
|
||||
For multi-class markets: [class0_prob, class1_prob, ...]
|
||||
"""
|
||||
self._ensure_loaded()
|
||||
|
||||
if market not in self.models:
|
||||
return None
|
||||
|
||||
X = self._prepare_features(features)
|
||||
probs = []
|
||||
weights = []
|
||||
is_multiclass = market in self.MULTICLASS_MARKETS
|
||||
|
||||
# XGBoost
|
||||
if 'xgb' in self.models[market]:
|
||||
dmat = xgb.DMatrix(X)
|
||||
xgb_proba = self.models[market]['xgb'].predict(dmat)
|
||||
if isinstance(xgb_proba, np.ndarray):
|
||||
if is_multiclass and len(xgb_proba.shape) == 2:
|
||||
probs.append(xgb_proba[0])
|
||||
elif is_multiclass and len(xgb_proba.shape) == 1:
|
||||
probs.append(xgb_proba)
|
||||
else:
|
||||
probs.append(np.array([xgb_proba[0]]))
|
||||
weights.append(self.DEFAULT_WEIGHTS['xgb'])
|
||||
|
||||
# LightGBM
|
||||
if 'lgb' in self.models[market]:
|
||||
lgb_proba = self.models[market]['lgb'].predict(X)
|
||||
if isinstance(lgb_proba, np.ndarray):
|
||||
if is_multiclass and len(lgb_proba.shape) == 2:
|
||||
probs.append(lgb_proba[0])
|
||||
elif is_multiclass and len(lgb_proba.shape) == 1:
|
||||
probs.append(lgb_proba)
|
||||
else:
|
||||
probs.append(np.array([lgb_proba[0]]))
|
||||
weights.append(self.DEFAULT_WEIGHTS['lgb'])
|
||||
|
||||
if not probs:
|
||||
return None
|
||||
|
||||
# Weighted average
|
||||
if len(probs) == 1:
|
||||
return probs[0]
|
||||
|
||||
total_w = sum(weights[:len(probs)])
|
||||
result = np.zeros_like(probs[0])
|
||||
for p, w in zip(probs, weights):
|
||||
result += p * (w / total_w)
|
||||
|
||||
# Normalize multi-class
|
||||
if is_multiclass and result.sum() > 0:
|
||||
result = result / result.sum()
|
||||
|
||||
return result
|
||||
|
||||
def has_market(self, market: str) -> bool:
|
||||
"""Check if a specific market model is loaded."""
|
||||
return market in self.models
|
||||
|
||||
def predict_match(
|
||||
self,
|
||||
match_id: str,
|
||||
home_team: str,
|
||||
away_team: str,
|
||||
features: Dict[str, float],
|
||||
odds: Optional[Dict[str, float]] = None,
|
||||
) -> MatchPrediction:
|
||||
"""
|
||||
Predict all markets for a match.
|
||||
|
||||
Args:
|
||||
match_id: Match identifier.
|
||||
home_team: Home team name.
|
||||
away_team: Away team name.
|
||||
features: Feature dictionary.
|
||||
odds: Optional odds dictionary for value bet detection.
|
||||
|
||||
Returns:
|
||||
MatchPrediction object.
|
||||
"""
|
||||
# Get predictions for each market
|
||||
home_prob, draw_prob, away_prob = self.predict_ms(features)
|
||||
over_prob, under_prob = self.predict_ou25(features)
|
||||
btts_yes_prob, btts_no_prob = self.predict_btts(features)
|
||||
|
||||
# Determine picks
|
||||
ms_probs = {'1': home_prob, 'X': draw_prob, '2': away_prob}
|
||||
ms_pick = max(ms_probs, key=ms_probs.__getitem__)
|
||||
ms_confidence = ms_probs[ms_pick] * 100
|
||||
|
||||
ou25_probs = {'Over': over_prob, 'Under': under_prob}
|
||||
ou25_pick = max(ou25_probs, key=ou25_probs.__getitem__)
|
||||
ou25_confidence = ou25_probs[ou25_pick] * 100
|
||||
|
||||
btts_probs = {'Yes': btts_yes_prob, 'No': btts_no_prob}
|
||||
btts_pick = max(btts_probs, key=btts_probs.__getitem__)
|
||||
btts_confidence = btts_probs[btts_pick] * 100
|
||||
|
||||
# Create prediction
|
||||
prediction = MatchPrediction(
|
||||
match_id=match_id,
|
||||
home_team=home_team,
|
||||
away_team=away_team,
|
||||
home_prob=home_prob,
|
||||
draw_prob=draw_prob,
|
||||
away_prob=away_prob,
|
||||
ms_pick=ms_pick,
|
||||
ms_confidence=ms_confidence,
|
||||
over_prob=over_prob,
|
||||
under_prob=under_prob,
|
||||
ou25_pick=ou25_pick,
|
||||
ou25_confidence=ou25_confidence,
|
||||
btts_yes_prob=btts_yes_prob,
|
||||
btts_no_prob=btts_no_prob,
|
||||
btts_pick=btts_pick,
|
||||
btts_confidence=btts_confidence,
|
||||
)
|
||||
|
||||
# Detect value bets
|
||||
if odds:
|
||||
prediction.value_bets = self._detect_value_bets(
|
||||
prediction, odds, home_prob, draw_prob, away_prob,
|
||||
over_prob, under_prob, btts_yes_prob, btts_no_prob
|
||||
)
|
||||
|
||||
return prediction
|
||||
|
||||
def _detect_value_bets(
|
||||
self,
|
||||
prediction: MatchPrediction,
|
||||
odds: Dict[str, float],
|
||||
home_prob: float,
|
||||
draw_prob: float,
|
||||
away_prob: float,
|
||||
over_prob: float,
|
||||
under_prob: float,
|
||||
btts_yes_prob: float,
|
||||
btts_no_prob: float,
|
||||
) -> List[ValueBet]:
|
||||
"""Detect value bets based on model vs market odds."""
|
||||
value_bets = []
|
||||
# Market-specific minimum edge thresholds
|
||||
# MS: higher variance → require more edge
|
||||
# OU/BTTS: binary markets → tighter edge acceptable
|
||||
EDGE_THRESHOLDS = {
|
||||
'MS': 0.06,
|
||||
'OU25': 0.04,
|
||||
'BTTS': 0.04,
|
||||
}
|
||||
ms_edge = EDGE_THRESHOLDS['MS']
|
||||
ou_edge = EDGE_THRESHOLDS['OU25']
|
||||
btts_edge = EDGE_THRESHOLDS['BTTS']
|
||||
|
||||
# MS value bets
|
||||
if 'ms_h' in odds and odds['ms_h'] > 0:
|
||||
implied = 1 / odds['ms_h']
|
||||
edge = home_prob - implied
|
||||
if edge > ms_edge:
|
||||
value_bets.append(ValueBet(
|
||||
market_type='MS',
|
||||
pick='1',
|
||||
probability=home_prob,
|
||||
odds=odds['ms_h'],
|
||||
edge=edge,
|
||||
confidence=home_prob * 100,
|
||||
))
|
||||
|
||||
if 'ms_d' in odds and odds['ms_d'] > 0:
|
||||
implied = 1 / odds['ms_d']
|
||||
edge = draw_prob - implied
|
||||
if edge > ms_edge:
|
||||
value_bets.append(ValueBet(
|
||||
market_type='MS',
|
||||
pick='X',
|
||||
probability=draw_prob,
|
||||
odds=odds['ms_d'],
|
||||
edge=edge,
|
||||
confidence=draw_prob * 100,
|
||||
))
|
||||
|
||||
if 'ms_a' in odds and odds['ms_a'] > 0:
|
||||
implied = 1 / odds['ms_a']
|
||||
edge = away_prob - implied
|
||||
if edge > ms_edge:
|
||||
value_bets.append(ValueBet(
|
||||
market_type='MS',
|
||||
pick='2',
|
||||
probability=away_prob,
|
||||
odds=odds['ms_a'],
|
||||
edge=edge,
|
||||
confidence=away_prob * 100,
|
||||
))
|
||||
|
||||
# OU25 value bets
|
||||
if 'ou25_o' in odds and odds['ou25_o'] > 0:
|
||||
implied = 1 / odds['ou25_o']
|
||||
edge = over_prob - implied
|
||||
if edge > ou_edge:
|
||||
value_bets.append(ValueBet(
|
||||
market_type='OU25',
|
||||
pick='Over',
|
||||
probability=over_prob,
|
||||
odds=odds['ou25_o'],
|
||||
edge=edge,
|
||||
confidence=over_prob * 100,
|
||||
))
|
||||
|
||||
if 'ou25_u' in odds and odds['ou25_u'] > 0:
|
||||
implied = 1 / odds['ou25_u']
|
||||
edge = under_prob - implied
|
||||
if edge > ou_edge:
|
||||
value_bets.append(ValueBet(
|
||||
market_type='OU25',
|
||||
pick='Under',
|
||||
probability=under_prob,
|
||||
odds=odds['ou25_u'],
|
||||
edge=edge,
|
||||
confidence=under_prob * 100,
|
||||
))
|
||||
|
||||
# BTTS value bets
|
||||
if 'btts_y' in odds and odds['btts_y'] > 0:
|
||||
implied = 1 / odds['btts_y']
|
||||
edge = btts_yes_prob - implied
|
||||
if edge > btts_edge:
|
||||
value_bets.append(ValueBet(
|
||||
market_type='BTTS',
|
||||
pick='Yes',
|
||||
probability=btts_yes_prob,
|
||||
odds=odds['btts_y'],
|
||||
edge=edge,
|
||||
confidence=btts_yes_prob * 100,
|
||||
))
|
||||
|
||||
if 'btts_n' in odds and odds['btts_n'] > 0:
|
||||
implied = 1 / odds['btts_n']
|
||||
edge = btts_no_prob - implied
|
||||
if edge > btts_edge:
|
||||
value_bets.append(ValueBet(
|
||||
market_type='BTTS',
|
||||
pick='No',
|
||||
probability=btts_no_prob,
|
||||
odds=odds['btts_n'],
|
||||
edge=edge,
|
||||
confidence=btts_no_prob * 100,
|
||||
))
|
||||
|
||||
return value_bets
|
||||
|
||||
|
||||
# Singleton instance
|
||||
_v25_predictor: Optional[V25Predictor] = None
|
||||
|
||||
|
||||
def get_v25_predictor() -> V25Predictor:
|
||||
"""Get or create V25 predictor instance."""
|
||||
global _v25_predictor
|
||||
if _v25_predictor is None:
|
||||
_v25_predictor = V25Predictor()
|
||||
_v25_predictor.load_models()
|
||||
return _v25_predictor
|
||||
@@ -0,0 +1,343 @@
|
||||
"""
|
||||
V27 Pro Predictor — Odds-Free Fundamentals + Value Edge Detection
|
||||
|
||||
This module loads V27 ensemble models (XGBoost, LightGBM, CatBoost)
|
||||
and produces market-independent probability estimates.
|
||||
|
||||
The key insight: V27 is trained WITHOUT odds features, so it produces
|
||||
"true" probabilities unbiased by market pricing. The divergence between
|
||||
V25 (odds-aware) and V27 (odds-free) predictions signals market mispricing.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pickle
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
import numpy as np
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
V27_DIR = Path(__file__).parent / "v27"
|
||||
|
||||
|
||||
class V27Predictor:
|
||||
"""
|
||||
Loads V27 ensemble models and provides predictions using the
|
||||
82-feature odds-free vector.
|
||||
"""
|
||||
|
||||
MARKETS = ['ms', 'ou25', 'btts']
|
||||
|
||||
def __init__(self):
|
||||
self.models: Dict[str, Dict[str, object]] = {}
|
||||
self.feature_cols: List[str] = []
|
||||
self._loaded = False
|
||||
|
||||
def load_models(self) -> bool:
|
||||
"""Load all V27 ensemble models and feature column spec."""
|
||||
if self._loaded:
|
||||
return True
|
||||
|
||||
# Feature columns
|
||||
cols_path = V27_DIR / "v27_feature_cols.json"
|
||||
if not cols_path.exists():
|
||||
logger.error("[V27] Feature columns file not found: %s", cols_path)
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(cols_path, "r", encoding="utf-8") as f:
|
||||
self.feature_cols = json.load(f)
|
||||
logger.info("[V27] Loaded %d feature columns", len(self.feature_cols))
|
||||
except Exception as e:
|
||||
logger.error("[V27] Failed to load feature columns: %s", e)
|
||||
return False
|
||||
|
||||
# Load models per market
|
||||
model_types = {"xgb": "xgb", "lgb": "lgb"}
|
||||
|
||||
for market in self.MARKETS:
|
||||
self.models[market] = {}
|
||||
for short, label in model_types.items():
|
||||
# Try market-specific file first: v27_ms_xgb.pkl
|
||||
path = V27_DIR / f"v27_{market}_{short}.pkl"
|
||||
if not path.exists():
|
||||
# Fallback to generic: v27_xgboost.pkl (for MS only)
|
||||
generic_names = {"xgb": "v27_xgboost.pkl", "lgb": "v27_lightgbm.pkl", "cb": "v27_catboost.pkl"}
|
||||
path = V27_DIR / generic_names.get(short, "")
|
||||
if not path.exists():
|
||||
logger.warning("[V27] Model file not found for %s/%s", market, short)
|
||||
continue
|
||||
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
model = pickle.load(f)
|
||||
self.models[market][label] = model
|
||||
logger.info("[V27] ✓ Loaded %s/%s from %s", market, label, path.name)
|
||||
except Exception as e:
|
||||
logger.error("[V27] ✗ Failed to load %s/%s: %s", market, label, e)
|
||||
|
||||
loaded_count = sum(len(v) for v in self.models.values())
|
||||
if loaded_count == 0:
|
||||
logger.error("[V27] No models loaded!")
|
||||
return False
|
||||
|
||||
self._loaded = True
|
||||
logger.info("[V27] Total models loaded: %d across %d markets", loaded_count, len(self.models))
|
||||
return True
|
||||
|
||||
def _build_feature_array(self, features: Dict[str, float]) -> np.ndarray:
|
||||
"""
|
||||
Build ordered feature array from the full feature dict.
|
||||
V27 uses only its 82 features (odds-free subset).
|
||||
"""
|
||||
row = []
|
||||
for col in self.feature_cols:
|
||||
row.append(float(features.get(col, 0.0)))
|
||||
return np.array([row])
|
||||
|
||||
def _predict_with_model(self, model, X: np.ndarray, label: str, expected_classes: int) -> Optional[np.ndarray]:
|
||||
"""
|
||||
Predict probabilities from a model, handling both sklearn wrappers
|
||||
(predict_proba) and raw Booster objects (predict).
|
||||
|
||||
For raw XGBoost Boosters, DMatrix is created WITH feature_names
|
||||
to match the training schema.
|
||||
"""
|
||||
import xgboost as xgb
|
||||
import lightgbm as lgbm
|
||||
import pandas as pd
|
||||
|
||||
# 1. Try sklearn-style predict_proba first
|
||||
if hasattr(model, 'predict_proba'):
|
||||
try:
|
||||
proba = model.predict_proba(X)[0]
|
||||
if len(proba) == expected_classes:
|
||||
return proba
|
||||
logger.warning("[V27] %s predict_proba returned %d classes, expected %d", label, len(proba), expected_classes)
|
||||
except Exception:
|
||||
pass # Fall through to raw predict
|
||||
|
||||
# 2. Raw xgboost.Booster — MUST pass feature_names
|
||||
if isinstance(model, xgb.Booster):
|
||||
try:
|
||||
feature_names = self.feature_cols if self.feature_cols else None
|
||||
dmat = xgb.DMatrix(X, feature_names=feature_names)
|
||||
raw = model.predict(dmat)
|
||||
if isinstance(raw, np.ndarray):
|
||||
if raw.ndim == 2 and raw.shape[1] == expected_classes:
|
||||
return raw[0]
|
||||
elif raw.ndim == 1 and expected_classes == 2:
|
||||
p = float(raw[0])
|
||||
return np.array([1.0 - p, p])
|
||||
elif raw.ndim == 1 and len(raw) == expected_classes:
|
||||
return raw
|
||||
except Exception as e:
|
||||
logger.warning("[V27] %s xgb.Booster predict failed: %s", label, e)
|
||||
return None
|
||||
|
||||
# 3. Raw lightgbm.Booster — pass as DataFrame with column names
|
||||
if isinstance(model, lgbm.Booster):
|
||||
try:
|
||||
if self.feature_cols:
|
||||
X_named = pd.DataFrame(X, columns=self.feature_cols)
|
||||
raw = model.predict(X_named)
|
||||
else:
|
||||
raw = model.predict(X)
|
||||
if isinstance(raw, np.ndarray):
|
||||
if raw.ndim == 2 and raw.shape[1] == expected_classes:
|
||||
return raw[0]
|
||||
elif raw.ndim == 1 and expected_classes == 2:
|
||||
p = float(raw[0])
|
||||
return np.array([1.0 - p, p])
|
||||
elif raw.ndim == 1 and len(raw) == expected_classes:
|
||||
return raw
|
||||
except Exception as e:
|
||||
logger.warning("[V27] %s lgb.Booster predict failed: %s", label, e)
|
||||
return None
|
||||
|
||||
# 4. Generic fallback (CatBoost, etc.)
|
||||
try:
|
||||
if hasattr(model, 'predict'):
|
||||
raw = model.predict(X)
|
||||
if isinstance(raw, np.ndarray):
|
||||
if raw.ndim == 2 and raw.shape[1] == expected_classes:
|
||||
return raw[0]
|
||||
elif raw.ndim == 1 and expected_classes == 2:
|
||||
p = float(raw[0])
|
||||
return np.array([1.0 - p, p])
|
||||
elif raw.ndim == 1 and len(raw) == expected_classes:
|
||||
return raw
|
||||
except Exception as e:
|
||||
logger.warning("[V27] %s generic predict failed: %s", label, e)
|
||||
|
||||
return None
|
||||
|
||||
def predict_ms(self, features: Dict[str, float]) -> Optional[Dict[str, float]]:
|
||||
"""
|
||||
Predict Match Score probabilities (Home/Draw/Away).
|
||||
Returns dict with keys: home, draw, away.
|
||||
"""
|
||||
if not self._loaded or "ms" not in self.models or not self.models["ms"]:
|
||||
return None
|
||||
|
||||
X = self._build_feature_array(features)
|
||||
probs_list = []
|
||||
|
||||
for label, model in self.models["ms"].items():
|
||||
proba = self._predict_with_model(model, X, f"MS/{label}", expected_classes=3)
|
||||
if proba is not None and len(proba) == 3:
|
||||
probs_list.append(proba)
|
||||
|
||||
if not probs_list:
|
||||
return None
|
||||
|
||||
# Ensemble average
|
||||
avg = np.mean(probs_list, axis=0)
|
||||
return {
|
||||
"home": float(avg[0]),
|
||||
"draw": float(avg[1]),
|
||||
"away": float(avg[2]),
|
||||
}
|
||||
|
||||
def predict_ou25(self, features: Dict[str, float]) -> Optional[Dict[str, float]]:
|
||||
"""
|
||||
Predict Over/Under 2.5 probabilities.
|
||||
Returns dict with keys: under, over.
|
||||
"""
|
||||
if not self._loaded or "ou25" not in self.models or not self.models["ou25"]:
|
||||
return None
|
||||
|
||||
X = self._build_feature_array(features)
|
||||
probs_list = []
|
||||
|
||||
for label, model in self.models["ou25"].items():
|
||||
proba = self._predict_with_model(model, X, f"OU25/{label}", expected_classes=2)
|
||||
if proba is not None and len(proba) == 2:
|
||||
probs_list.append(proba)
|
||||
|
||||
if not probs_list:
|
||||
return None
|
||||
|
||||
avg = np.mean(probs_list, axis=0)
|
||||
return {
|
||||
"under": float(avg[0]),
|
||||
"over": float(avg[1]),
|
||||
}
|
||||
|
||||
def predict_btts(self, features: Dict[str, float]) -> Optional[Dict[str, float]]:
|
||||
"""
|
||||
Predict Both Teams To Score probabilities.
|
||||
Returns dict with keys: no, yes.
|
||||
"""
|
||||
if not self._loaded or 'btts' not in self.models or not self.models['btts']:
|
||||
return None
|
||||
|
||||
X = self._build_feature_array(features)
|
||||
probs_list = []
|
||||
|
||||
for label, model in self.models['btts'].items():
|
||||
proba = self._predict_with_model(model, X, f'BTTS/{label}', expected_classes=2)
|
||||
if proba is not None and len(proba) == 2:
|
||||
probs_list.append(proba)
|
||||
|
||||
if not probs_list:
|
||||
return None
|
||||
|
||||
avg = np.mean(probs_list, axis=0)
|
||||
return {
|
||||
'no': float(avg[0]),
|
||||
'yes': float(avg[1]),
|
||||
}
|
||||
|
||||
def predict_dc(self, features: Dict[str, float]) -> Optional[Dict[str, float]]:
|
||||
"""
|
||||
Predict Double Chance probabilities.
|
||||
|
||||
DC is algebraically derived from MS predictions:
|
||||
1X = home + draw
|
||||
X2 = draw + away
|
||||
12 = home + away
|
||||
|
||||
This gives an odds-free DC estimate for divergence detection.
|
||||
"""
|
||||
ms_probs = self.predict_ms(features)
|
||||
if not ms_probs:
|
||||
return None
|
||||
|
||||
home = ms_probs['home']
|
||||
draw = ms_probs['draw']
|
||||
away = ms_probs['away']
|
||||
|
||||
return {
|
||||
'1x': round(home + draw, 4),
|
||||
'x2': round(draw + away, 4),
|
||||
'12': round(home + away, 4),
|
||||
}
|
||||
|
||||
def predict_all(self, features: Dict[str, float]) -> Dict[str, Optional[Dict[str, float]]]:
|
||||
"""Run predictions for all supported markets."""
|
||||
return {
|
||||
'ms': self.predict_ms(features),
|
||||
'ou25': self.predict_ou25(features),
|
||||
'btts': self.predict_btts(features),
|
||||
'dc': self.predict_dc(features),
|
||||
}
|
||||
|
||||
|
||||
def compute_divergence(
|
||||
v25_probs: Dict[str, float],
|
||||
v27_probs: Dict[str, float],
|
||||
) -> Dict[str, float]:
|
||||
"""
|
||||
Compute the divergence signal between V25 (odds-aware) and V27 (odds-free).
|
||||
|
||||
Positive divergence = V27 thinks it's MORE likely than the market → VALUE BET
|
||||
Negative divergence = V27 thinks it's LESS likely than the market → PASS
|
||||
|
||||
Returns per-outcome divergence values.
|
||||
"""
|
||||
divergence = {}
|
||||
for key in v27_probs:
|
||||
v25_val = v25_probs.get(key, 0.33)
|
||||
v27_val = v27_probs.get(key, 0.33)
|
||||
divergence[key] = round(v27_val - v25_val, 4)
|
||||
return divergence
|
||||
|
||||
|
||||
def compute_value_edge(
|
||||
v25_probs: Dict[str, float],
|
||||
v27_probs: Dict[str, float],
|
||||
odds: Dict[str, float],
|
||||
) -> Dict[str, Dict]:
|
||||
"""
|
||||
Detect value bets by combining V25/V27 divergence with odds.
|
||||
|
||||
A value bet exists when:
|
||||
1. V27 (odds-free) probability > implied odds probability (model says it's underpriced)
|
||||
2. V27 and V25 divergence is positive (V27 sees more signal than the market)
|
||||
|
||||
Returns per-outcome: { probability, implied_prob, edge, is_value }
|
||||
"""
|
||||
results = {}
|
||||
for key in v27_probs:
|
||||
v27_p = v27_probs[key]
|
||||
v25_p = v25_probs.get(key, 0.33)
|
||||
odds_val = odds.get(key, 0.0)
|
||||
|
||||
implied_p = (1.0 / odds_val) if odds_val > 1.01 else 0.0
|
||||
divergence = v27_p - v25_p
|
||||
edge = v27_p - implied_p if implied_p > 0 else 0.0
|
||||
|
||||
results[key] = {
|
||||
"v27_prob": round(v27_p, 4),
|
||||
"v25_prob": round(v25_p, 4),
|
||||
"implied_prob": round(implied_p, 4),
|
||||
"divergence": round(divergence, 4),
|
||||
"edge": round(edge, 4),
|
||||
"is_value": edge > 0.05 and divergence > 0.02, # 5% edge + 2% divergence
|
||||
}
|
||||
|
||||
return results
|
||||
Binary file not shown.
@@ -0,0 +1,160 @@
|
||||
{
|
||||
"total_test": 23039,
|
||||
"thresholds": {
|
||||
"0.0": {
|
||||
"n_matches": 22227,
|
||||
"pct": 96.5,
|
||||
"markets": {
|
||||
"ms": {
|
||||
"hit_rate": 0.5363,
|
||||
"avg_roi": -0.0046,
|
||||
"total_roi": -103.02
|
||||
},
|
||||
"ou15": {
|
||||
"hit_rate": 0.7463,
|
||||
"avg_roi": 0.0144,
|
||||
"total_roi": 319.02
|
||||
},
|
||||
"ou25": {
|
||||
"hit_rate": 0.6111,
|
||||
"avg_roi": -0.006,
|
||||
"total_roi": -134.41
|
||||
},
|
||||
"ou35": {
|
||||
"hit_rate": 0.7302,
|
||||
"avg_roi": -0.014,
|
||||
"total_roi": -310.51
|
||||
},
|
||||
"btts": {
|
||||
"hit_rate": 0.5848,
|
||||
"avg_roi": 0.0031,
|
||||
"total_roi": 69.5
|
||||
}
|
||||
}
|
||||
},
|
||||
"0.1": {
|
||||
"n_matches": 23033,
|
||||
"pct": 100.0,
|
||||
"markets": {
|
||||
"ms": {
|
||||
"hit_rate": 0.546,
|
||||
"avg_roi": -0.0045,
|
||||
"total_roi": -104.38
|
||||
},
|
||||
"ou15": {
|
||||
"hit_rate": 0.7533,
|
||||
"avg_roi": 0.0145,
|
||||
"total_roi": 335.02
|
||||
},
|
||||
"ou25": {
|
||||
"hit_rate": 0.6193,
|
||||
"avg_roi": -0.0042,
|
||||
"total_roi": -96.97
|
||||
},
|
||||
"ou35": {
|
||||
"hit_rate": 0.7277,
|
||||
"avg_roi": -0.0147,
|
||||
"total_roi": -338.57
|
||||
},
|
||||
"btts": {
|
||||
"hit_rate": 0.5886,
|
||||
"avg_roi": 0.0025,
|
||||
"total_roi": 57.21
|
||||
}
|
||||
}
|
||||
},
|
||||
"0.2": {
|
||||
"n_matches": 23034,
|
||||
"pct": 100.0,
|
||||
"markets": {
|
||||
"ms": {
|
||||
"hit_rate": 0.5459,
|
||||
"avg_roi": -0.0046,
|
||||
"total_roi": -105.38
|
||||
},
|
||||
"ou15": {
|
||||
"hit_rate": 0.7533,
|
||||
"avg_roi": 0.0146,
|
||||
"total_roi": 335.26
|
||||
},
|
||||
"ou25": {
|
||||
"hit_rate": 0.6193,
|
||||
"avg_roi": -0.0043,
|
||||
"total_roi": -97.97
|
||||
},
|
||||
"ou35": {
|
||||
"hit_rate": 0.7276,
|
||||
"avg_roi": -0.0147,
|
||||
"total_roi": -339.57
|
||||
},
|
||||
"btts": {
|
||||
"hit_rate": 0.5887,
|
||||
"avg_roi": 0.0025,
|
||||
"total_roi": 57.62
|
||||
}
|
||||
}
|
||||
},
|
||||
"0.3": {
|
||||
"n_matches": 23039,
|
||||
"pct": 100.0,
|
||||
"markets": {
|
||||
"ms": {
|
||||
"hit_rate": 0.546,
|
||||
"avg_roi": -0.0045,
|
||||
"total_roi": -103.45
|
||||
},
|
||||
"ou15": {
|
||||
"hit_rate": 0.7534,
|
||||
"avg_roi": 0.0146,
|
||||
"total_roi": 335.6
|
||||
},
|
||||
"ou25": {
|
||||
"hit_rate": 0.6194,
|
||||
"avg_roi": -0.0042,
|
||||
"total_roi": -97.44
|
||||
},
|
||||
"ou35": {
|
||||
"hit_rate": 0.7277,
|
||||
"avg_roi": -0.0147,
|
||||
"total_roi": -339.26
|
||||
},
|
||||
"btts": {
|
||||
"hit_rate": 0.5887,
|
||||
"avg_roi": 0.0025,
|
||||
"total_roi": 58.61
|
||||
}
|
||||
}
|
||||
},
|
||||
"0.5": {
|
||||
"n_matches": 23039,
|
||||
"pct": 100.0,
|
||||
"markets": {
|
||||
"ms": {
|
||||
"hit_rate": 0.546,
|
||||
"avg_roi": -0.0045,
|
||||
"total_roi": -103.45
|
||||
},
|
||||
"ou15": {
|
||||
"hit_rate": 0.7534,
|
||||
"avg_roi": 0.0146,
|
||||
"total_roi": 335.6
|
||||
},
|
||||
"ou25": {
|
||||
"hit_rate": 0.6194,
|
||||
"avg_roi": -0.0042,
|
||||
"total_roi": -97.44
|
||||
},
|
||||
"ou35": {
|
||||
"hit_rate": 0.7277,
|
||||
"avg_roi": -0.0147,
|
||||
"total_roi": -339.26
|
||||
},
|
||||
"btts": {
|
||||
"hit_rate": 0.5887,
|
||||
"avg_roi": 0.0025,
|
||||
"total_roi": 58.61
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{
|
||||
"market": "MS-Ev",
|
||||
"min_edge": 0.02,
|
||||
"n":
|
||||
@@ -0,0 +1,267 @@
|
||||
{
|
||||
"generated_at": "2026-05-15T21:40:57.995899",
|
||||
"matches_processed": 3000,
|
||||
"matches_skipped": 0,
|
||||
"markets": {
|
||||
"MS": {
|
||||
"overall_accuracy": 54.97,
|
||||
"total_matches": 3000,
|
||||
"by_confidence_band": {
|
||||
"<50%": {
|
||||
"accuracy": 38.87,
|
||||
"count": 759,
|
||||
"mean_confidence": 45.58
|
||||
},
|
||||
"50-65%": {
|
||||
"accuracy": 52.62,
|
||||
"count": 1300,
|
||||
"mean_confidence": 57.19
|
||||
},
|
||||
"65-75%": {
|
||||
"accuracy": 66.99,
|
||||
"count": 624,
|
||||
"mean_confidence": 69.49
|
||||
},
|
||||
"75%+": {
|
||||
"accuracy": 79.5,
|
||||
"count": 317,
|
||||
"mean_confidence": 80.69
|
||||
}
|
||||
},
|
||||
"by_league": {
|
||||
"Bundesliga": {
|
||||
"accuracy": 46.77,
|
||||
"count": 62
|
||||
},
|
||||
"Ligue 1": {
|
||||
"accuracy": 58.73,
|
||||
"count": 63
|
||||
},
|
||||
"Serie A": {
|
||||
"accuracy": 56.25,
|
||||
"count": 64
|
||||
},
|
||||
"Other": {
|
||||
"accuracy": 55.03,
|
||||
"count": 2811
|
||||
}
|
||||
},
|
||||
"by_pick_direction": {
|
||||
"1": {
|
||||
"accuracy": 58.38,
|
||||
"count": 1946,
|
||||
"mean_confidence": 60.84
|
||||
},
|
||||
"2": {
|
||||
"accuracy": 48.72,
|
||||
"count": 1053,
|
||||
"mean_confidence": 56.44
|
||||
},
|
||||
"X": {
|
||||
"accuracy": 0.0,
|
||||
"count": 1,
|
||||
"mean_confidence": 56.07
|
||||
}
|
||||
}
|
||||
},
|
||||
"OU15": {
|
||||
"overall_accuracy": 74.4,
|
||||
"total_matches": 3000,
|
||||
"by_confidence_band": {
|
||||
"50-65%": {
|
||||
"accuracy": 70.97,
|
||||
"count": 62,
|
||||
"mean_confidence": 59.63
|
||||
},
|
||||
"65-75%": {
|
||||
"accuracy": 68.0,
|
||||
"count": 275,
|
||||
"mean_confidence": 71.1
|
||||
},
|
||||
"75%+": {
|
||||
"accuracy": 75.14,
|
||||
"count": 2663,
|
||||
"mean_confidence": 89.44
|
||||
}
|
||||
},
|
||||
"by_league": {
|
||||
"Bundesliga": {
|
||||
"accuracy": 67.74,
|
||||
"count": 62
|
||||
},
|
||||
"Ligue 1": {
|
||||
"accuracy": 76.19,
|
||||
"count": 63
|
||||
},
|
||||
"Serie A": {
|
||||
"accuracy": 70.31,
|
||||
"count": 64
|
||||
},
|
||||
"Other": {
|
||||
"accuracy": 74.6,
|
||||
"count": 2811
|
||||
}
|
||||
},
|
||||
"by_pick_direction": {
|
||||
"Over": {
|
||||
"accuracy": 74.4,
|
||||
"count": 3000,
|
||||
"mean_confidence": 87.14
|
||||
}
|
||||
}
|
||||
},
|
||||
"OU25": {
|
||||
"overall_accuracy": 51.77,
|
||||
"total_matches": 3000,
|
||||
"by_confidence_band": {
|
||||
"50-65%": {
|
||||
"accuracy": 49.33,
|
||||
"count": 1267,
|
||||
"mean_confidence": 57.13
|
||||
},
|
||||
"65-75%": {
|
||||
"accuracy": 54.53,
|
||||
"count": 453,
|
||||
"mean_confidence": 69.42
|
||||
},
|
||||
"75%+": {
|
||||
"accuracy": 53.2,
|
||||
"count": 1280,
|
||||
"mean_confidence": 90.2
|
||||
}
|
||||
},
|
||||
"by_league": {
|
||||
"Bundesliga": {
|
||||
"accuracy": 41.94,
|
||||
"count": 62
|
||||
},
|
||||
"Ligue 1": {
|
||||
"accuracy": 50.79,
|
||||
"count": 63
|
||||
},
|
||||
"Serie A": {
|
||||
"accuracy": 43.75,
|
||||
"count": 64
|
||||
},
|
||||
"Other": {
|
||||
"accuracy": 52.19,
|
||||
"count": 2811
|
||||
}
|
||||
},
|
||||
"by_pick_direction": {
|
||||
"Over": {
|
||||
"accuracy": 51.03,
|
||||
"count": 2432,
|
||||
"mean_confidence": 76.11
|
||||
},
|
||||
"Under": {
|
||||
"accuracy": 54.93,
|
||||
"count": 568,
|
||||
"mean_confidence": 60.17
|
||||
}
|
||||
}
|
||||
},
|
||||
"BTTS": {
|
||||
"overall_accuracy": 51.83,
|
||||
"total_matches": 3000,
|
||||
"by_confidence_band": {
|
||||
"50-65%": {
|
||||
"accuracy": 48.74,
|
||||
"count": 2214,
|
||||
"mean_confidence": 58.66
|
||||
},
|
||||
"65-75%": {
|
||||
"accuracy": 60.42,
|
||||
"count": 758,
|
||||
"mean_confidence": 68.19
|
||||
},
|
||||
"75%+": {
|
||||
"accuracy": 64.29,
|
||||
"count": 28,
|
||||
"mean_confidence": 77.44
|
||||
}
|
||||
},
|
||||
"by_league": {
|
||||
"Bundesliga": {
|
||||
"accuracy": 54.84,
|
||||
"count": 62
|
||||
},
|
||||
"Ligue 1": {
|
||||
"accuracy": 50.79,
|
||||
"count": 63
|
||||
},
|
||||
"Serie A": {
|
||||
"accuracy": 57.81,
|
||||
"count": 64
|
||||
},
|
||||
"Other": {
|
||||
"accuracy": 51.65,
|
||||
"count": 2811
|
||||
}
|
||||
},
|
||||
"by_pick_direction": {
|
||||
"No": {
|
||||
"accuracy": 50.26,
|
||||
"count": 2099,
|
||||
"mean_confidence": 61.56
|
||||
},
|
||||
"Yes": {
|
||||
"accuracy": 55.49,
|
||||
"count": 901,
|
||||
"mean_confidence": 60.51
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"calibration": {
|
||||
"ms_home": {
|
||||
"brier_score": 0.2054,
|
||||
"calibration_error": 0.0,
|
||||
"sample_count": 3000,
|
||||
"last_trained": "2026-05-15T21:40:58.026574",
|
||||
"mean_predicted": 0.4942,
|
||||
"mean_actual": 0.46
|
||||
},
|
||||
"ms_draw": {
|
||||
"brier_score": 0.1846,
|
||||
"calibration_error": 0.0,
|
||||
"sample_count": 3000,
|
||||
"last_trained": "2026-05-15T21:40:58.030886",
|
||||
"mean_predicted": 0.149,
|
||||
"mean_actual": 0.2493
|
||||
},
|
||||
"ms_away": {
|
||||
"brier_score": 0.1726,
|
||||
"calibration_error": 0.0,
|
||||
"sample_count": 3000,
|
||||
"last_trained": "2026-05-15T21:40:58.033980",
|
||||
"mean_predicted": 0.3567,
|
||||
"mean_actual": 0.2907
|
||||
},
|
||||
"ou15": {
|
||||
"brier_score": 0.1884,
|
||||
"calibration_error": 0.0,
|
||||
"sample_count": 3000,
|
||||
"last_trained": "2026-05-15T21:40:58.037204",
|
||||
"mean_predicted": 0.8714,
|
||||
"mean_actual": 0.744
|
||||
},
|
||||
"ou25": {
|
||||
"brier_score": 0.247,
|
||||
"calibration_error": 0.0,
|
||||
"sample_count": 3000,
|
||||
"last_trained": "2026-05-15T21:40:58.041152",
|
||||
"mean_predicted": 0.6924,
|
||||
"mean_actual": 0.499
|
||||
},
|
||||
"btts": {
|
||||
"brier_score": 0.2453,
|
||||
"calibration_error": 0.0,
|
||||
"sample_count": 3000,
|
||||
"last_trained": "2026-05-15T21:40:58.044344",
|
||||
"mean_predicted": 0.4506,
|
||||
"mean_actual": 0.5147
|
||||
}
|
||||
},
|
||||
"runtime_seconds": 94.1
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"trained_at": "2026-04-14 17:20:03",
|
||||
"trained_at": "2026-05-06 15:53:36",
|
||||
"market_results": {
|
||||
"MS": {
|
||||
"samples": 9791,
|
||||
"samples": 106428,
|
||||
"features_used": [
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
@@ -107,19 +107,19 @@
|
||||
"home_goals_form",
|
||||
"away_goals_form"
|
||||
],
|
||||
"train_samples": 6853,
|
||||
"val_samples": 1469,
|
||||
"test_samples": 1469,
|
||||
"xgb_accuracy": 0.8938,
|
||||
"xgb_logloss": 0.2263,
|
||||
"lgb_accuracy": 0.8938,
|
||||
"lgb_logloss": 0.2214,
|
||||
"ensemble_accuracy": 0.8945,
|
||||
"ensemble_logloss": 0.2226,
|
||||
"train_samples": 74499,
|
||||
"val_samples": 15964,
|
||||
"test_samples": 15965,
|
||||
"xgb_accuracy": 0.5437,
|
||||
"xgb_logloss": 0.9429,
|
||||
"lgb_accuracy": 0.5436,
|
||||
"lgb_logloss": 0.9423,
|
||||
"ensemble_accuracy": 0.5442,
|
||||
"ensemble_logloss": 0.9418,
|
||||
"class_count": 3
|
||||
},
|
||||
"OU15": {
|
||||
"samples": 9791,
|
||||
"samples": 106428,
|
||||
"features_used": [
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
@@ -224,19 +224,19 @@
|
||||
"home_goals_form",
|
||||
"away_goals_form"
|
||||
],
|
||||
"train_samples": 6853,
|
||||
"val_samples": 1469,
|
||||
"test_samples": 1469,
|
||||
"xgb_accuracy": 0.9088,
|
||||
"xgb_logloss": 0.1758,
|
||||
"lgb_accuracy": 0.9067,
|
||||
"lgb_logloss": 0.1783,
|
||||
"ensemble_accuracy": 0.9108,
|
||||
"ensemble_logloss": 0.1753,
|
||||
"train_samples": 74499,
|
||||
"val_samples": 15964,
|
||||
"test_samples": 15965,
|
||||
"xgb_accuracy": 0.753,
|
||||
"xgb_logloss": 0.5256,
|
||||
"lgb_accuracy": 0.7523,
|
||||
"lgb_logloss": 0.5262,
|
||||
"ensemble_accuracy": 0.7533,
|
||||
"ensemble_logloss": 0.5254,
|
||||
"class_count": 2
|
||||
},
|
||||
"OU25": {
|
||||
"samples": 9791,
|
||||
"samples": 106428,
|
||||
"features_used": [
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
@@ -341,19 +341,19 @@
|
||||
"home_goals_form",
|
||||
"away_goals_form"
|
||||
],
|
||||
"train_samples": 6853,
|
||||
"val_samples": 1469,
|
||||
"test_samples": 1469,
|
||||
"xgb_accuracy": 0.9204,
|
||||
"xgb_logloss": 0.1535,
|
||||
"lgb_accuracy": 0.9224,
|
||||
"lgb_logloss": 0.1523,
|
||||
"ensemble_accuracy": 0.9217,
|
||||
"ensemble_logloss": 0.1518,
|
||||
"train_samples": 74499,
|
||||
"val_samples": 15964,
|
||||
"test_samples": 15965,
|
||||
"xgb_accuracy": 0.6253,
|
||||
"xgb_logloss": 0.635,
|
||||
"lgb_accuracy": 0.6246,
|
||||
"lgb_logloss": 0.6347,
|
||||
"ensemble_accuracy": 0.6262,
|
||||
"ensemble_logloss": 0.6343,
|
||||
"class_count": 2
|
||||
},
|
||||
"OU35": {
|
||||
"samples": 9791,
|
||||
"samples": 106428,
|
||||
"features_used": [
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
@@ -458,19 +458,19 @@
|
||||
"home_goals_form",
|
||||
"away_goals_form"
|
||||
],
|
||||
"train_samples": 6853,
|
||||
"val_samples": 1469,
|
||||
"test_samples": 1469,
|
||||
"xgb_accuracy": 0.9578,
|
||||
"xgb_logloss": 0.1171,
|
||||
"lgb_accuracy": 0.9564,
|
||||
"lgb_logloss": 0.1144,
|
||||
"ensemble_accuracy": 0.9571,
|
||||
"ensemble_logloss": 0.1149,
|
||||
"train_samples": 74499,
|
||||
"val_samples": 15964,
|
||||
"test_samples": 15965,
|
||||
"xgb_accuracy": 0.7283,
|
||||
"xgb_logloss": 0.5463,
|
||||
"lgb_accuracy": 0.7304,
|
||||
"lgb_logloss": 0.546,
|
||||
"ensemble_accuracy": 0.7297,
|
||||
"ensemble_logloss": 0.5456,
|
||||
"class_count": 2
|
||||
},
|
||||
"BTTS": {
|
||||
"samples": 9791,
|
||||
"samples": 106428,
|
||||
"features_used": [
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
@@ -575,19 +575,19 @@
|
||||
"home_goals_form",
|
||||
"away_goals_form"
|
||||
],
|
||||
"train_samples": 6853,
|
||||
"val_samples": 1469,
|
||||
"test_samples": 1469,
|
||||
"xgb_accuracy": 0.9238,
|
||||
"xgb_logloss": 0.1439,
|
||||
"lgb_accuracy": 0.9265,
|
||||
"lgb_logloss": 0.143,
|
||||
"ensemble_accuracy": 0.9265,
|
||||
"ensemble_logloss": 0.1424,
|
||||
"train_samples": 74499,
|
||||
"val_samples": 15964,
|
||||
"test_samples": 15965,
|
||||
"xgb_accuracy": 0.5894,
|
||||
"xgb_logloss": 0.6636,
|
||||
"lgb_accuracy": 0.5928,
|
||||
"lgb_logloss": 0.6633,
|
||||
"ensemble_accuracy": 0.5897,
|
||||
"ensemble_logloss": 0.6628,
|
||||
"class_count": 2
|
||||
},
|
||||
"HT_RESULT": {
|
||||
"samples": 9786,
|
||||
"samples": 103208,
|
||||
"features_used": [
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
@@ -692,19 +692,19 @@
|
||||
"home_goals_form",
|
||||
"away_goals_form"
|
||||
],
|
||||
"train_samples": 6850,
|
||||
"val_samples": 1468,
|
||||
"test_samples": 1468,
|
||||
"xgb_accuracy": 0.5627,
|
||||
"xgb_logloss": 0.8712,
|
||||
"lgb_accuracy": 0.5715,
|
||||
"lgb_logloss": 0.8649,
|
||||
"ensemble_accuracy": 0.5811,
|
||||
"ensemble_logloss": 0.8649,
|
||||
"train_samples": 72245,
|
||||
"val_samples": 15481,
|
||||
"test_samples": 15482,
|
||||
"xgb_accuracy": 0.4695,
|
||||
"xgb_logloss": 1.0174,
|
||||
"lgb_accuracy": 0.4677,
|
||||
"lgb_logloss": 1.0166,
|
||||
"ensemble_accuracy": 0.4688,
|
||||
"ensemble_logloss": 1.0164,
|
||||
"class_count": 3
|
||||
},
|
||||
"HT_OU05": {
|
||||
"samples": 9786,
|
||||
"samples": 103208,
|
||||
"features_used": [
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
@@ -809,19 +809,19 @@
|
||||
"home_goals_form",
|
||||
"away_goals_form"
|
||||
],
|
||||
"train_samples": 6850,
|
||||
"val_samples": 1468,
|
||||
"test_samples": 1468,
|
||||
"xgb_accuracy": 0.7221,
|
||||
"xgb_logloss": 0.5122,
|
||||
"lgb_accuracy": 0.7268,
|
||||
"lgb_logloss": 0.5092,
|
||||
"ensemble_accuracy": 0.7275,
|
||||
"ensemble_logloss": 0.5084,
|
||||
"train_samples": 72245,
|
||||
"val_samples": 15481,
|
||||
"test_samples": 15482,
|
||||
"xgb_accuracy": 0.7011,
|
||||
"xgb_logloss": 0.5939,
|
||||
"lgb_accuracy": 0.7002,
|
||||
"lgb_logloss": 0.5936,
|
||||
"ensemble_accuracy": 0.7009,
|
||||
"ensemble_logloss": 0.5932,
|
||||
"class_count": 2
|
||||
},
|
||||
"HT_OU15": {
|
||||
"samples": 9786,
|
||||
"samples": 103208,
|
||||
"features_used": [
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
@@ -926,19 +926,19 @@
|
||||
"home_goals_form",
|
||||
"away_goals_form"
|
||||
],
|
||||
"train_samples": 6850,
|
||||
"val_samples": 1468,
|
||||
"test_samples": 1468,
|
||||
"xgb_accuracy": 0.752,
|
||||
"xgb_logloss": 0.5252,
|
||||
"lgb_accuracy": 0.7595,
|
||||
"lgb_logloss": 0.5213,
|
||||
"ensemble_accuracy": 0.7595,
|
||||
"ensemble_logloss": 0.5192,
|
||||
"train_samples": 72245,
|
||||
"val_samples": 15481,
|
||||
"test_samples": 15482,
|
||||
"xgb_accuracy": 0.6723,
|
||||
"xgb_logloss": 0.6126,
|
||||
"lgb_accuracy": 0.6736,
|
||||
"lgb_logloss": 0.6118,
|
||||
"ensemble_accuracy": 0.6734,
|
||||
"ensemble_logloss": 0.6117,
|
||||
"class_count": 2
|
||||
},
|
||||
"HTFT": {
|
||||
"samples": 9786,
|
||||
"samples": 103208,
|
||||
"features_used": [
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
@@ -1043,19 +1043,19 @@
|
||||
"home_goals_form",
|
||||
"away_goals_form"
|
||||
],
|
||||
"train_samples": 6850,
|
||||
"val_samples": 1468,
|
||||
"test_samples": 1468,
|
||||
"xgb_accuracy": 0.5136,
|
||||
"xgb_logloss": 1.1384,
|
||||
"lgb_accuracy": 0.5184,
|
||||
"lgb_logloss": 1.1469,
|
||||
"ensemble_accuracy": 0.5143,
|
||||
"ensemble_logloss": 1.1339,
|
||||
"train_samples": 72245,
|
||||
"val_samples": 15481,
|
||||
"test_samples": 15482,
|
||||
"xgb_accuracy": 0.3337,
|
||||
"xgb_logloss": 1.8208,
|
||||
"lgb_accuracy": 0.3332,
|
||||
"lgb_logloss": 1.8203,
|
||||
"ensemble_accuracy": 0.3358,
|
||||
"ensemble_logloss": 1.8186,
|
||||
"class_count": 9
|
||||
},
|
||||
"ODD_EVEN": {
|
||||
"samples": 9791,
|
||||
"samples": 106428,
|
||||
"features_used": [
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
@@ -1160,19 +1160,19 @@
|
||||
"home_goals_form",
|
||||
"away_goals_form"
|
||||
],
|
||||
"train_samples": 6853,
|
||||
"val_samples": 1469,
|
||||
"test_samples": 1469,
|
||||
"xgb_accuracy": 0.8863,
|
||||
"xgb_logloss": 0.3565,
|
||||
"lgb_accuracy": 0.8802,
|
||||
"lgb_logloss": 0.3338,
|
||||
"ensemble_accuracy": 0.8863,
|
||||
"ensemble_logloss": 0.3423,
|
||||
"train_samples": 74499,
|
||||
"val_samples": 15964,
|
||||
"test_samples": 15965,
|
||||
"xgb_accuracy": 0.5296,
|
||||
"xgb_logloss": 0.6841,
|
||||
"lgb_accuracy": 0.5359,
|
||||
"lgb_logloss": 0.6822,
|
||||
"ensemble_accuracy": 0.531,
|
||||
"ensemble_logloss": 0.6826,
|
||||
"class_count": 2
|
||||
},
|
||||
"CARDS_OU45": {
|
||||
"samples": 9791,
|
||||
"samples": 106428,
|
||||
"features_used": [
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
@@ -1277,19 +1277,19 @@
|
||||
"home_goals_form",
|
||||
"away_goals_form"
|
||||
],
|
||||
"train_samples": 6853,
|
||||
"val_samples": 1469,
|
||||
"test_samples": 1469,
|
||||
"xgb_accuracy": 0.6283,
|
||||
"xgb_logloss": 0.6174,
|
||||
"lgb_accuracy": 0.6413,
|
||||
"lgb_logloss": 0.615,
|
||||
"ensemble_accuracy": 0.6372,
|
||||
"ensemble_logloss": 0.6142,
|
||||
"train_samples": 74499,
|
||||
"val_samples": 15964,
|
||||
"test_samples": 15965,
|
||||
"xgb_accuracy": 0.6009,
|
||||
"xgb_logloss": 0.6489,
|
||||
"lgb_accuracy": 0.5988,
|
||||
"lgb_logloss": 0.6487,
|
||||
"ensemble_accuracy": 0.6024,
|
||||
"ensemble_logloss": 0.6479,
|
||||
"class_count": 2
|
||||
},
|
||||
"HANDICAP_MS": {
|
||||
"samples": 9791,
|
||||
"samples": 106428,
|
||||
"features_used": [
|
||||
"home_overall_elo",
|
||||
"away_overall_elo",
|
||||
@@ -1394,15 +1394,15 @@
|
||||
"home_goals_form",
|
||||
"away_goals_form"
|
||||
],
|
||||
"train_samples": 6853,
|
||||
"val_samples": 1469,
|
||||
"test_samples": 1469,
|
||||
"xgb_accuracy": 0.936,
|
||||
"xgb_logloss": 0.1903,
|
||||
"lgb_accuracy": 0.9346,
|
||||
"lgb_logloss": 0.1843,
|
||||
"ensemble_accuracy": 0.936,
|
||||
"ensemble_logloss": 0.1861,
|
||||
"train_samples": 74499,
|
||||
"val_samples": 15964,
|
||||
"test_samples": 15965,
|
||||
"xgb_accuracy": 0.6058,
|
||||
"xgb_logloss": 0.8691,
|
||||
"lgb_accuracy": 0.608,
|
||||
"lgb_logloss": 0.8677,
|
||||
"ensemble_accuracy": 0.6068,
|
||||
"ensemble_logloss": 0.8677,
|
||||
"class_count": 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,692 @@
|
||||
{
|
||||
"trained_at": "2026-05-20 21:06:17",
|
||||
"trainer": "v25_pro",
|
||||
"optuna_trials": 50,
|
||||
"total_features": 152,
|
||||
"markets": {
|
||||
"MS": {
|
||||
"market": "MS",
|
||||
"samples": 34335,
|
||||
"train": 20601,
|
||||
"val": 5150,
|
||||
"cal": 3433,
|
||||
"test": 5151,
|
||||
"features_used": 152,
|
||||
"xgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"eta": 0.01813533005537975,
|
||||
"subsample": 0.6006077004659048,
|
||||
"colsample_bytree": 0.5355789322633149,
|
||||
"min_child_weight": 6,
|
||||
"gamma": 1.3630627811027698e-08,
|
||||
"reg_lambda": 0.000883795032251414,
|
||||
"reg_alpha": 0.8575321662422171
|
||||
},
|
||||
"lgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"learning_rate": 0.020992610123700125,
|
||||
"feature_fraction": 0.5951238173352622,
|
||||
"bagging_fraction": 0.6452606057786048,
|
||||
"bagging_freq": 5,
|
||||
"min_child_samples": 16,
|
||||
"lambda_l1": 0.2277044617439506,
|
||||
"lambda_l2": 1.1531219311866832e-05
|
||||
},
|
||||
"xgb_best_iteration": 329,
|
||||
"lgb_best_iteration": 171,
|
||||
"xgb_optuna_best_logloss": 0.9665,
|
||||
"lgb_optuna_best_logloss": 0.9662,
|
||||
"test_xgb_raw": {
|
||||
"accuracy": 0.5152,
|
||||
"logloss": 0.9923
|
||||
},
|
||||
"test_xgb_calibrated": {
|
||||
"accuracy": 0.5172,
|
||||
"logloss": 0.9983
|
||||
},
|
||||
"test_lgb_raw": {
|
||||
"accuracy": 0.5145,
|
||||
"logloss": 0.9916
|
||||
},
|
||||
"test_lgb_calibrated": {
|
||||
"accuracy": 0.5131,
|
||||
"logloss": 0.995
|
||||
},
|
||||
"test_ensemble_raw": {
|
||||
"accuracy": 0.5131,
|
||||
"logloss": 0.9917
|
||||
},
|
||||
"test_ensemble_calibrated": {
|
||||
"accuracy": 0.5139,
|
||||
"logloss": 0.9949
|
||||
}
|
||||
},
|
||||
"OU15": {
|
||||
"market": "OU15",
|
||||
"samples": 34335,
|
||||
"train": 20601,
|
||||
"val": 5150,
|
||||
"cal": 3433,
|
||||
"test": 5151,
|
||||
"features_used": 152,
|
||||
"xgb_best_params": {
|
||||
"max_depth": 4,
|
||||
"eta": 0.010007100652565513,
|
||||
"subsample": 0.6423987996355262,
|
||||
"colsample_bytree": 0.5399591700794417,
|
||||
"min_child_weight": 9,
|
||||
"gamma": 5.180190831403823e-06,
|
||||
"reg_lambda": 0.04589987363138381,
|
||||
"reg_alpha": 0.3607359437854735
|
||||
},
|
||||
"lgb_best_params": {
|
||||
"max_depth": 5,
|
||||
"learning_rate": 0.014982384401894706,
|
||||
"feature_fraction": 0.5359420477684117,
|
||||
"bagging_fraction": 0.7746019204010285,
|
||||
"bagging_freq": 7,
|
||||
"min_child_samples": 14,
|
||||
"lambda_l1": 8.500808557234126e-07,
|
||||
"lambda_l2": 2.536478147708652e-05
|
||||
},
|
||||
"xgb_best_iteration": 374,
|
||||
"lgb_best_iteration": 351,
|
||||
"xgb_optuna_best_logloss": 0.4945,
|
||||
"lgb_optuna_best_logloss": 0.4939,
|
||||
"test_xgb_raw": {
|
||||
"accuracy": 0.7732,
|
||||
"logloss": 0.5214
|
||||
},
|
||||
"test_xgb_calibrated": {
|
||||
"accuracy": 0.7641,
|
||||
"logloss": 0.5324
|
||||
},
|
||||
"test_lgb_raw": {
|
||||
"accuracy": 0.7729,
|
||||
"logloss": 0.5224
|
||||
},
|
||||
"test_lgb_calibrated": {
|
||||
"accuracy": 0.7707,
|
||||
"logloss": 0.5427
|
||||
},
|
||||
"test_ensemble_raw": {
|
||||
"accuracy": 0.7732,
|
||||
"logloss": 0.5216
|
||||
},
|
||||
"test_ensemble_calibrated": {
|
||||
"accuracy": 0.7705,
|
||||
"logloss": 0.5298
|
||||
}
|
||||
},
|
||||
"OU25": {
|
||||
"market": "OU25",
|
||||
"samples": 34335,
|
||||
"train": 20601,
|
||||
"val": 5150,
|
||||
"cal": 3433,
|
||||
"test": 5151,
|
||||
"features_used": 152,
|
||||
"xgb_best_params": {
|
||||
"max_depth": 4,
|
||||
"eta": 0.01865626593315191,
|
||||
"subsample": 0.7105443580121279,
|
||||
"colsample_bytree": 0.6135695058254393,
|
||||
"min_child_weight": 4,
|
||||
"gamma": 3.0546982020131495e-05,
|
||||
"reg_lambda": 9.868745464853797,
|
||||
"reg_alpha": 0.011307758649267466
|
||||
},
|
||||
"lgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"learning_rate": 0.09100328259258599,
|
||||
"feature_fraction": 0.8534286719238086,
|
||||
"bagging_fraction": 0.8916028672163949,
|
||||
"bagging_freq": 6,
|
||||
"min_child_samples": 8,
|
||||
"lambda_l1": 7.374385355858303e-06,
|
||||
"lambda_l2": 1.1036250149900698e-07
|
||||
},
|
||||
"xgb_best_iteration": 273,
|
||||
"lgb_best_iteration": 61,
|
||||
"xgb_optuna_best_logloss": 0.6481,
|
||||
"lgb_optuna_best_logloss": 0.648,
|
||||
"test_xgb_raw": {
|
||||
"accuracy": 0.5937,
|
||||
"logloss": 0.6629
|
||||
},
|
||||
"test_xgb_calibrated": {
|
||||
"accuracy": 0.5937,
|
||||
"logloss": 0.6805
|
||||
},
|
||||
"test_lgb_raw": {
|
||||
"accuracy": 0.5954,
|
||||
"logloss": 0.6633
|
||||
},
|
||||
"test_lgb_calibrated": {
|
||||
"accuracy": 0.5935,
|
||||
"logloss": 0.6976
|
||||
},
|
||||
"test_ensemble_raw": {
|
||||
"accuracy": 0.5946,
|
||||
"logloss": 0.6628
|
||||
},
|
||||
"test_ensemble_calibrated": {
|
||||
"accuracy": 0.5937,
|
||||
"logloss": 0.6709
|
||||
}
|
||||
},
|
||||
"OU35": {
|
||||
"market": "OU35",
|
||||
"samples": 34335,
|
||||
"train": 20601,
|
||||
"val": 5150,
|
||||
"cal": 3433,
|
||||
"test": 5151,
|
||||
"features_used": 152,
|
||||
"xgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"eta": 0.0143268502217739,
|
||||
"subsample": 0.8172822498674736,
|
||||
"colsample_bytree": 0.506201148705069,
|
||||
"min_child_weight": 1,
|
||||
"gamma": 1.8443203495219952e-05,
|
||||
"reg_lambda": 0.0011170943053842998,
|
||||
"reg_alpha": 0.00020927636698484225
|
||||
},
|
||||
"lgb_best_params": {
|
||||
"max_depth": 5,
|
||||
"learning_rate": 0.020117886227998167,
|
||||
"feature_fraction": 0.6208946683116914,
|
||||
"bagging_fraction": 0.6562518216073271,
|
||||
"bagging_freq": 5,
|
||||
"min_child_samples": 45,
|
||||
"lambda_l1": 0.00036062002131658313,
|
||||
"lambda_l2": 3.05269304967777e-05
|
||||
},
|
||||
"xgb_best_iteration": 529,
|
||||
"lgb_best_iteration": 137,
|
||||
"xgb_optuna_best_logloss": 0.6145,
|
||||
"lgb_optuna_best_logloss": 0.6143,
|
||||
"test_xgb_raw": {
|
||||
"accuracy": 0.6898,
|
||||
"logloss": 0.5998
|
||||
},
|
||||
"test_xgb_calibrated": {
|
||||
"accuracy": 0.6871,
|
||||
"logloss": 0.6091
|
||||
},
|
||||
"test_lgb_raw": {
|
||||
"accuracy": 0.6904,
|
||||
"logloss": 0.6001
|
||||
},
|
||||
"test_lgb_calibrated": {
|
||||
"accuracy": 0.6904,
|
||||
"logloss": 0.6008
|
||||
},
|
||||
"test_ensemble_raw": {
|
||||
"accuracy": 0.6905,
|
||||
"logloss": 0.5998
|
||||
},
|
||||
"test_ensemble_calibrated": {
|
||||
"accuracy": 0.6907,
|
||||
"logloss": 0.6007
|
||||
}
|
||||
},
|
||||
"BTTS": {
|
||||
"market": "BTTS",
|
||||
"samples": 34335,
|
||||
"train": 20601,
|
||||
"val": 5150,
|
||||
"cal": 3433,
|
||||
"test": 5151,
|
||||
"features_used": 152,
|
||||
"xgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"eta": 0.021664948542761242,
|
||||
"subsample": 0.7099186087768442,
|
||||
"colsample_bytree": 0.7590713567483665,
|
||||
"min_child_weight": 6,
|
||||
"gamma": 5.5491508895403115e-06,
|
||||
"reg_lambda": 4.796666289138789,
|
||||
"reg_alpha": 0.00025842183659199874
|
||||
},
|
||||
"lgb_best_params": {
|
||||
"max_depth": 8,
|
||||
"learning_rate": 0.01933635083345398,
|
||||
"feature_fraction": 0.7004575834832659,
|
||||
"bagging_fraction": 0.6329726219722871,
|
||||
"bagging_freq": 5,
|
||||
"min_child_samples": 15,
|
||||
"lambda_l1": 0.002608953323098078,
|
||||
"lambda_l2": 0.0036308084751911743
|
||||
},
|
||||
"xgb_best_iteration": 255,
|
||||
"lgb_best_iteration": 160,
|
||||
"xgb_optuna_best_logloss": 0.6715,
|
||||
"lgb_optuna_best_logloss": 0.6717,
|
||||
"test_xgb_raw": {
|
||||
"accuracy": 0.5743,
|
||||
"logloss": 0.6781
|
||||
},
|
||||
"test_xgb_calibrated": {
|
||||
"accuracy": 0.5743,
|
||||
"logloss": 0.6872
|
||||
},
|
||||
"test_lgb_raw": {
|
||||
"accuracy": 0.5746,
|
||||
"logloss": 0.677
|
||||
},
|
||||
"test_lgb_calibrated": {
|
||||
"accuracy": 0.5733,
|
||||
"logloss": 0.7059
|
||||
},
|
||||
"test_ensemble_raw": {
|
||||
"accuracy": 0.5766,
|
||||
"logloss": 0.6772
|
||||
},
|
||||
"test_ensemble_calibrated": {
|
||||
"accuracy": 0.5768,
|
||||
"logloss": 0.6789
|
||||
}
|
||||
},
|
||||
"HT_RESULT": {
|
||||
"market": "HT_RESULT",
|
||||
"samples": 31774,
|
||||
"train": 19064,
|
||||
"val": 4766,
|
||||
"cal": 3177,
|
||||
"test": 4767,
|
||||
"features_used": 152,
|
||||
"xgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"eta": 0.010169971619421797,
|
||||
"subsample": 0.6203740676748326,
|
||||
"colsample_bytree": 0.5851318310393765,
|
||||
"min_child_weight": 7,
|
||||
"gamma": 2.3628372099543945e-05,
|
||||
"reg_lambda": 2.2038789911767867e-05,
|
||||
"reg_alpha": 1.4397584578583542e-08
|
||||
},
|
||||
"lgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"learning_rate": 0.016888148464727217,
|
||||
"feature_fraction": 0.5434647939749242,
|
||||
"bagging_fraction": 0.7069497418977599,
|
||||
"bagging_freq": 3,
|
||||
"min_child_samples": 12,
|
||||
"lambda_l1": 0.0014508638064857065,
|
||||
"lambda_l2": 0.00023588865029986223
|
||||
},
|
||||
"xgb_best_iteration": 553,
|
||||
"lgb_best_iteration": 237,
|
||||
"xgb_optuna_best_logloss": 1.035,
|
||||
"lgb_optuna_best_logloss": 1.0349,
|
||||
"test_xgb_raw": {
|
||||
"accuracy": 0.4504,
|
||||
"logloss": 1.0411
|
||||
},
|
||||
"test_xgb_calibrated": {
|
||||
"accuracy": 0.4495,
|
||||
"logloss": 1.0541
|
||||
},
|
||||
"test_lgb_raw": {
|
||||
"accuracy": 0.4491,
|
||||
"logloss": 1.0417
|
||||
},
|
||||
"test_lgb_calibrated": {
|
||||
"accuracy": 0.4458,
|
||||
"logloss": 1.0579
|
||||
},
|
||||
"test_ensemble_raw": {
|
||||
"accuracy": 0.4498,
|
||||
"logloss": 1.0413
|
||||
},
|
||||
"test_ensemble_calibrated": {
|
||||
"accuracy": 0.447,
|
||||
"logloss": 1.0572
|
||||
}
|
||||
},
|
||||
"HT_OU05": {
|
||||
"market": "HT_OU05",
|
||||
"samples": 31774,
|
||||
"train": 19064,
|
||||
"val": 4766,
|
||||
"cal": 3177,
|
||||
"test": 4767,
|
||||
"features_used": 152,
|
||||
"xgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"eta": 0.017924063045686854,
|
||||
"subsample": 0.8434007408500664,
|
||||
"colsample_bytree": 0.6701120513607601,
|
||||
"min_child_weight": 4,
|
||||
"gamma": 9.836792363275528e-07,
|
||||
"reg_lambda": 1.7187465006176214e-05,
|
||||
"reg_alpha": 3.014327116647346e-07
|
||||
},
|
||||
"lgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"learning_rate": 0.10422189537713934,
|
||||
"feature_fraction": 0.5888797409273676,
|
||||
"bagging_fraction": 0.7090975058803146,
|
||||
"bagging_freq": 3,
|
||||
"min_child_samples": 17,
|
||||
"lambda_l1": 7.810412584545967e-07,
|
||||
"lambda_l2": 0.009428914603962961
|
||||
},
|
||||
"xgb_best_iteration": 202,
|
||||
"lgb_best_iteration": 35,
|
||||
"xgb_optuna_best_logloss": 0.5704,
|
||||
"lgb_optuna_best_logloss": 0.5698,
|
||||
"test_xgb_raw": {
|
||||
"accuracy": 0.7134,
|
||||
"logloss": 0.5902
|
||||
},
|
||||
"test_xgb_calibrated": {
|
||||
"accuracy": 0.713,
|
||||
"logloss": 0.6081
|
||||
},
|
||||
"test_lgb_raw": {
|
||||
"accuracy": 0.7143,
|
||||
"logloss": 0.5915
|
||||
},
|
||||
"test_lgb_calibrated": {
|
||||
"accuracy": 0.7145,
|
||||
"logloss": 0.593
|
||||
},
|
||||
"test_ensemble_raw": {
|
||||
"accuracy": 0.7134,
|
||||
"logloss": 0.5906
|
||||
},
|
||||
"test_ensemble_calibrated": {
|
||||
"accuracy": 0.7141,
|
||||
"logloss": 0.5921
|
||||
}
|
||||
},
|
||||
"HT_OU15": {
|
||||
"market": "HT_OU15",
|
||||
"samples": 31774,
|
||||
"train": 19064,
|
||||
"val": 4766,
|
||||
"cal": 3177,
|
||||
"test": 4767,
|
||||
"features_used": 152,
|
||||
"xgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"eta": 0.017001754132211097,
|
||||
"subsample": 0.6180909155642152,
|
||||
"colsample_bytree": 0.6626651653816322,
|
||||
"min_child_weight": 4,
|
||||
"gamma": 1.481809088646707e-06,
|
||||
"reg_lambda": 0.287499823474079,
|
||||
"reg_alpha": 7.145401117237584e-06
|
||||
},
|
||||
"lgb_best_params": {
|
||||
"max_depth": 8,
|
||||
"learning_rate": 0.01064422725257703,
|
||||
"feature_fraction": 0.6662850991902617,
|
||||
"bagging_fraction": 0.7721868286695308,
|
||||
"bagging_freq": 3,
|
||||
"min_child_samples": 30,
|
||||
"lambda_l1": 0.9857429592173135,
|
||||
"lambda_l2": 3.960422300486119e-06
|
||||
},
|
||||
"xgb_best_iteration": 429,
|
||||
"lgb_best_iteration": 345,
|
||||
"xgb_optuna_best_logloss": 0.6435,
|
||||
"lgb_optuna_best_logloss": 0.6435,
|
||||
"test_xgb_raw": {
|
||||
"accuracy": 0.6381,
|
||||
"logloss": 0.6445
|
||||
},
|
||||
"test_xgb_calibrated": {
|
||||
"accuracy": 0.6381,
|
||||
"logloss": 0.6525
|
||||
},
|
||||
"test_lgb_raw": {
|
||||
"accuracy": 0.6419,
|
||||
"logloss": 0.6439
|
||||
},
|
||||
"test_lgb_calibrated": {
|
||||
"accuracy": 0.6402,
|
||||
"logloss": 0.7399
|
||||
},
|
||||
"test_ensemble_raw": {
|
||||
"accuracy": 0.6404,
|
||||
"logloss": 0.6439
|
||||
},
|
||||
"test_ensemble_calibrated": {
|
||||
"accuracy": 0.6394,
|
||||
"logloss": 0.6495
|
||||
}
|
||||
},
|
||||
"HTFT": {
|
||||
"market": "HTFT",
|
||||
"samples": 31774,
|
||||
"train": 19064,
|
||||
"val": 4766,
|
||||
"cal": 3177,
|
||||
"test": 4767,
|
||||
"features_used": 152,
|
||||
"xgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"eta": 0.013345313595371834,
|
||||
"subsample": 0.9479953293996353,
|
||||
"colsample_bytree": 0.6615608030428078,
|
||||
"min_child_weight": 9,
|
||||
"gamma": 7.455111012483033e-07,
|
||||
"reg_lambda": 8.89911456381185,
|
||||
"reg_alpha": 5.090564000963078e-06
|
||||
},
|
||||
"lgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"learning_rate": 0.013089530126848842,
|
||||
"feature_fraction": 0.7906053881599783,
|
||||
"bagging_fraction": 0.7440871339717291,
|
||||
"bagging_freq": 4,
|
||||
"min_child_samples": 44,
|
||||
"lambda_l1": 2.0129746481016167e-05,
|
||||
"lambda_l2": 9.879192474391198
|
||||
},
|
||||
"xgb_best_iteration": 366,
|
||||
"lgb_best_iteration": 252,
|
||||
"xgb_optuna_best_logloss": 1.8581,
|
||||
"lgb_optuna_best_logloss": 1.858,
|
||||
"test_xgb_raw": {
|
||||
"accuracy": 0.3168,
|
||||
"logloss": 1.8817
|
||||
},
|
||||
"test_xgb_calibrated": {
|
||||
"accuracy": 0.3168,
|
||||
"logloss": 1.9133
|
||||
},
|
||||
"test_lgb_raw": {
|
||||
"accuracy": 0.317,
|
||||
"logloss": 1.882
|
||||
},
|
||||
"test_lgb_calibrated": {
|
||||
"accuracy": 0.3178,
|
||||
"logloss": 1.9643
|
||||
},
|
||||
"test_ensemble_raw": {
|
||||
"accuracy": 0.3172,
|
||||
"logloss": 1.8815
|
||||
},
|
||||
"test_ensemble_calibrated": {
|
||||
"accuracy": 0.3184,
|
||||
"logloss": 1.9473
|
||||
}
|
||||
},
|
||||
"ODD_EVEN": {
|
||||
"market": "ODD_EVEN",
|
||||
"samples": 34335,
|
||||
"train": 20601,
|
||||
"val": 5150,
|
||||
"cal": 3433,
|
||||
"test": 5151,
|
||||
"features_used": 152,
|
||||
"xgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"eta": 0.0229001065742241,
|
||||
"subsample": 0.6062940462474563,
|
||||
"colsample_bytree": 0.6480436065939664,
|
||||
"min_child_weight": 3,
|
||||
"gamma": 0.0030457541200722615,
|
||||
"reg_lambda": 7.476931580627848e-05,
|
||||
"reg_alpha": 0.0009042146623162754
|
||||
},
|
||||
"lgb_best_params": {
|
||||
"max_depth": 7,
|
||||
"learning_rate": 0.04326506132261678,
|
||||
"feature_fraction": 0.5682671758256829,
|
||||
"bagging_fraction": 0.7270328719038639,
|
||||
"bagging_freq": 3,
|
||||
"min_child_samples": 16,
|
||||
"lambda_l1": 8.217437741607322e-08,
|
||||
"lambda_l2": 0.3516451122877441
|
||||
},
|
||||
"xgb_best_iteration": 83,
|
||||
"lgb_best_iteration": 68,
|
||||
"xgb_optuna_best_logloss": 0.6925,
|
||||
"lgb_optuna_best_logloss": 0.6922,
|
||||
"test_xgb_raw": {
|
||||
"accuracy": 0.5017,
|
||||
"logloss": 0.6938
|
||||
},
|
||||
"test_xgb_calibrated": {
|
||||
"accuracy": 0.5053,
|
||||
"logloss": 0.6944
|
||||
},
|
||||
"test_lgb_raw": {
|
||||
"accuracy": 0.5069,
|
||||
"logloss": 0.6962
|
||||
},
|
||||
"test_lgb_calibrated": {
|
||||
"accuracy": 0.5048,
|
||||
"logloss": 0.722
|
||||
},
|
||||
"test_ensemble_raw": {
|
||||
"accuracy": 0.5003,
|
||||
"logloss": 0.6944
|
||||
},
|
||||
"test_ensemble_calibrated": {
|
||||
"accuracy": 0.5055,
|
||||
"logloss": 0.6942
|
||||
}
|
||||
},
|
||||
"CARDS_OU45": {
|
||||
"market": "CARDS_OU45",
|
||||
"samples": 34335,
|
||||
"train": 20601,
|
||||
"val": 5150,
|
||||
"cal": 3433,
|
||||
"test": 5151,
|
||||
"features_used": 152,
|
||||
"xgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"eta": 0.05954573029858921,
|
||||
"subsample": 0.9384590363195691,
|
||||
"colsample_bytree": 0.7061263810064461,
|
||||
"min_child_weight": 3,
|
||||
"gamma": 0.12648985601647333,
|
||||
"reg_lambda": 0.0005539202839193393,
|
||||
"reg_alpha": 5.4993381294041956e-08
|
||||
},
|
||||
"lgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"learning_rate": 0.03822726574649208,
|
||||
"feature_fraction": 0.5171942605576092,
|
||||
"bagging_fraction": 0.9637281608315128,
|
||||
"bagging_freq": 2,
|
||||
"min_child_samples": 35,
|
||||
"lambda_l1": 3.1166541263980415e-06,
|
||||
"lambda_l2": 0.0004793052550782129
|
||||
},
|
||||
"xgb_best_iteration": 185,
|
||||
"lgb_best_iteration": 338,
|
||||
"xgb_optuna_best_logloss": 0.5583,
|
||||
"lgb_optuna_best_logloss": 0.5582,
|
||||
"test_xgb_raw": {
|
||||
"accuracy": 0.5917,
|
||||
"logloss": 0.6455
|
||||
},
|
||||
"test_xgb_calibrated": {
|
||||
"accuracy": 0.6014,
|
||||
"logloss": 0.6466
|
||||
},
|
||||
"test_lgb_raw": {
|
||||
"accuracy": 0.5925,
|
||||
"logloss": 0.6454
|
||||
},
|
||||
"test_lgb_calibrated": {
|
||||
"accuracy": 0.6034,
|
||||
"logloss": 0.6677
|
||||
},
|
||||
"test_ensemble_raw": {
|
||||
"accuracy": 0.5917,
|
||||
"logloss": 0.6451
|
||||
},
|
||||
"test_ensemble_calibrated": {
|
||||
"accuracy": 0.6038,
|
||||
"logloss": 0.6463
|
||||
}
|
||||
},
|
||||
"HANDICAP_MS": {
|
||||
"market": "HANDICAP_MS",
|
||||
"samples": 34335,
|
||||
"train": 20601,
|
||||
"val": 5150,
|
||||
"cal": 3433,
|
||||
"test": 5151,
|
||||
"features_used": 152,
|
||||
"xgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"eta": 0.016879778725504085,
|
||||
"subsample": 0.6294239018550948,
|
||||
"colsample_bytree": 0.6694819528702206,
|
||||
"min_child_weight": 5,
|
||||
"gamma": 2.9804488909930113e-05,
|
||||
"reg_lambda": 0.2590856513676408,
|
||||
"reg_alpha": 7.468057455332845e-06
|
||||
},
|
||||
"lgb_best_params": {
|
||||
"max_depth": 3,
|
||||
"learning_rate": 0.03267347269820124,
|
||||
"feature_fraction": 0.5009468855100953,
|
||||
"bagging_fraction": 0.7336123380897622,
|
||||
"bagging_freq": 1,
|
||||
"min_child_samples": 10,
|
||||
"lambda_l1": 1.3880195780862006e-06,
|
||||
"lambda_l2": 0.7568094447483327
|
||||
},
|
||||
"xgb_best_iteration": 406,
|
||||
"lgb_best_iteration": 231,
|
||||
"xgb_optuna_best_logloss": 0.907,
|
||||
"lgb_optuna_best_logloss": 0.9064,
|
||||
"test_xgb_raw": {
|
||||
"accuracy": 0.5838,
|
||||
"logloss": 0.9235
|
||||
},
|
||||
"test_xgb_calibrated": {
|
||||
"accuracy": 0.5865,
|
||||
"logloss": 0.9347
|
||||
},
|
||||
"test_lgb_raw": {
|
||||
"accuracy": 0.5797,
|
||||
"logloss": 0.9257
|
||||
},
|
||||
"test_lgb_calibrated": {
|
||||
"accuracy": 0.5832,
|
||||
"logloss": 0.9467
|
||||
},
|
||||
"test_ensemble_raw": {
|
||||
"accuracy": 0.5818,
|
||||
"logloss": 0.9244
|
||||
},
|
||||
"test_ensemble_calibrated": {
|
||||
"accuracy": 0.5836,
|
||||
"logloss": 0.9387
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": "v26.shadow.0",
|
||||
"calibration_version": "v26.shadow.calib.0",
|
||||
"train_rows": 6853,
|
||||
"validation_rows": 1469,
|
||||
"label_priors": {
|
||||
"MS": 0.4404,
|
||||
"OU25": 0.5214,
|
||||
"BTTS": 0.5398,
|
||||
"HT": 0.4275,
|
||||
"HTFT": 0.26,
|
||||
"CARDS": 0.6052
|
||||
},
|
||||
"artifact_path": "/Users/piton/Documents/GitHub/iddaai/iddaai-be/ai-engine/models/v26_shadow/market_profiles.json",
|
||||
"notes": [
|
||||
"v26.shadow runtime currently uses artifact-based calibration and ROI gating",
|
||||
"market profile JSON remains the source of truth for runtime thresholds"
|
||||
]
|
||||
}
|
||||
@@ -17,3 +17,4 @@ pyyaml>=6.0
|
||||
# V2 async database
|
||||
asyncpg>=0.29.0
|
||||
pydantic>=2.5.0
|
||||
pytest>=8.0.0
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
"""
|
||||
MatchData dataclass — core data transfer object used throughout the engine.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class MatchData:
|
||||
match_id: str
|
||||
home_team_id: str
|
||||
away_team_id: str
|
||||
home_team_name: str
|
||||
away_team_name: str
|
||||
match_date_ms: int
|
||||
sport: str
|
||||
league_id: Optional[str]
|
||||
league_name: str
|
||||
referee_name: Optional[str]
|
||||
odds_data: Dict[str, float]
|
||||
home_lineup: Optional[List[str]]
|
||||
away_lineup: Optional[List[str]]
|
||||
sidelined_data: Optional[Dict[str, Any]]
|
||||
home_goals_avg: float
|
||||
home_conceded_avg: float
|
||||
away_goals_avg: float
|
||||
away_conceded_avg: float
|
||||
home_position: int
|
||||
away_position: int
|
||||
lineup_source: str
|
||||
status: str = ""
|
||||
state: Optional[str] = None
|
||||
substate: Optional[str] = None
|
||||
current_score_home: Optional[int] = None
|
||||
current_score_away: Optional[int] = None
|
||||
lineup_confidence: float = 0.0
|
||||
source_table: str = "matches"
|
||||
@@ -0,0 +1,292 @@
|
||||
"""
|
||||
Shared prediction dataclasses used across the AI engine.
|
||||
|
||||
These were originally defined in models/v20_ensemble.py and are extracted here
|
||||
so they can be used without importing the full V20 ensemble.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from core.calculators.score_calculator import ScorePrediction
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarketPrediction:
|
||||
"""Prediction for a single betting market."""
|
||||
market_type: str
|
||||
pick: str
|
||||
probability: float
|
||||
confidence: float
|
||||
odds: float = 0.0
|
||||
is_recommended: bool = False
|
||||
is_value_bet: bool = False
|
||||
edge: float = 0.0 # Expected edge over market
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"market_type": self.market_type,
|
||||
"pick": self.pick,
|
||||
"probability": round(self.probability * 100, 1),
|
||||
"confidence": round(self.confidence, 1),
|
||||
"odds": self.odds,
|
||||
"is_recommended": self.is_recommended,
|
||||
"is_value_bet": self.is_value_bet,
|
||||
"edge": round(self.edge, 1)
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class FullMatchPrediction:
|
||||
"""Complete prediction for a match with ALL markets."""
|
||||
match_id: str
|
||||
home_team: str
|
||||
away_team: str
|
||||
match_date: str = ""
|
||||
|
||||
# === MAÇ SONUCU (1X2) ===
|
||||
ms_home_prob: float = 0.33
|
||||
ms_draw_prob: float = 0.33
|
||||
ms_away_prob: float = 0.33
|
||||
ms_pick: str = ""
|
||||
ms_confidence: float = 0.0
|
||||
|
||||
# === ÇİFTE ŞANS ===
|
||||
dc_1x_prob: float = 0.66
|
||||
dc_x2_prob: float = 0.66
|
||||
dc_12_prob: float = 0.66
|
||||
dc_pick: str = ""
|
||||
dc_confidence: float = 0.0
|
||||
|
||||
# === ALT/ÜST GOLLER ===
|
||||
# 1.5
|
||||
over_15_prob: float = 0.70
|
||||
under_15_prob: float = 0.30
|
||||
ou15_pick: str = ""
|
||||
ou15_confidence: float = 0.0
|
||||
|
||||
# 2.5
|
||||
over_25_prob: float = 0.50
|
||||
under_25_prob: float = 0.50
|
||||
ou25_pick: str = ""
|
||||
ou25_confidence: float = 0.0
|
||||
|
||||
# 3.5
|
||||
over_35_prob: float = 0.30
|
||||
under_35_prob: float = 0.70
|
||||
ou35_pick: str = ""
|
||||
ou35_confidence: float = 0.0
|
||||
|
||||
# === KARŞILIKLI GOL (BTTS) ===
|
||||
btts_yes_prob: float = 0.50
|
||||
btts_no_prob: float = 0.50
|
||||
btts_pick: str = ""
|
||||
btts_confidence: float = 0.0
|
||||
|
||||
# === İLK YARI SONUCU ===
|
||||
ht_home_prob: float = 0.30
|
||||
ht_draw_prob: float = 0.40
|
||||
ht_away_prob: float = 0.30
|
||||
ht_pick: str = ""
|
||||
ht_confidence: float = 0.0
|
||||
|
||||
# === SKOR TAHMİNLERİ ===
|
||||
score: Optional[ScorePrediction] = None
|
||||
predicted_ft_score: str = "1-1"
|
||||
predicted_ht_score: str = "0-0"
|
||||
ft_scores_top5: List[Dict] = field(default_factory=list)
|
||||
|
||||
# === xG (Expected Goals) ===
|
||||
home_xg: float = 1.3
|
||||
away_xg: float = 1.1
|
||||
total_xg: float = 2.4
|
||||
|
||||
# === RISK DEĞERLENDİRMESİ ===
|
||||
risk_level: str = "MEDIUM" # LOW, MEDIUM, HIGH, EXTREME
|
||||
risk_score: float = 0.0
|
||||
is_surprise_risk: bool = False
|
||||
surprise_type: str = ""
|
||||
risk_warnings: List[str] = field(default_factory=list)
|
||||
ht_ft_probs: Dict[str, float] = field(default_factory=dict)
|
||||
|
||||
# === GLM-5 SÜRPRİZ SKORU ===
|
||||
upset_score: int = 0 # 0-100 arası sürpriz skoru
|
||||
upset_level: str = "LOW" # LOW, MEDIUM, HIGH, EXTREME
|
||||
upset_reasons: List[str] = field(default_factory=list)
|
||||
|
||||
# === SÜRPRİZ PROFİLİ ===
|
||||
surprise_score: float = 0.0 # 0-100 overall surprise risk score
|
||||
surprise_comment: str = "" # Human-readable surprise commentary
|
||||
surprise_reasons: List[str] = field(default_factory=list) # Flagged risk reasons
|
||||
surprise_breakdown: List[Dict[str, Any]] = field(default_factory=list) # Per-factor {code, points, label}
|
||||
|
||||
# === ENGINE KATKILARI ===
|
||||
team_confidence: float = 0.0
|
||||
player_confidence: float = 0.0
|
||||
odds_confidence: float = 0.0
|
||||
referee_confidence: float = 0.0
|
||||
|
||||
# === KORNER & KART & DİĞER ===
|
||||
total_corners_pred: float = 9.5
|
||||
corner_pick: str = "9.5 Üst"
|
||||
|
||||
total_cards_pred: float = 4.5
|
||||
card_pick: str = "4.5 Alt"
|
||||
cards_over_prob: float = 0.50
|
||||
cards_under_prob: float = 0.50
|
||||
cards_confidence: float = 0.0
|
||||
|
||||
handicap_pick: str = ""
|
||||
handicap_home_prob: float = 0.33
|
||||
handicap_draw_prob: float = 0.34
|
||||
handicap_away_prob: float = 0.33
|
||||
handicap_confidence: float = 0.0
|
||||
|
||||
ht_over_05_prob: float = 0.65
|
||||
ht_under_05_prob: float = 0.35
|
||||
ht_over_15_prob: float = 0.30
|
||||
ht_under_15_prob: float = 0.70
|
||||
ht_ou_pick: str = "İY 0.5 Üst"
|
||||
ht_ou15_pick: str = "İY 1.5 Alt"
|
||||
|
||||
odd_even_pick: str = "Çift"
|
||||
odd_prob: float = 0.50 # Tek olasılığı
|
||||
even_prob: float = 0.50 # Çift olasılığı
|
||||
|
||||
# === TAVSİYELER (RECOMMENDATIONS) ===
|
||||
best_bet: Optional[MarketPrediction] = None
|
||||
recommended_bets: List[MarketPrediction] = field(default_factory=list)
|
||||
alternative_bet: Optional[MarketPrediction] = None
|
||||
expert_recommendation: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
# === DETAILED ANALYSIS ===
|
||||
analysis_details: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"match_info": {
|
||||
"match_id": self.match_id,
|
||||
"home_team": self.home_team,
|
||||
"away_team": self.away_team,
|
||||
"match_date": self.match_date
|
||||
},
|
||||
"predictions": {
|
||||
"match_result": {
|
||||
"1": round(self.ms_home_prob * 100, 1),
|
||||
"X": round(self.ms_draw_prob * 100, 1),
|
||||
"2": round(self.ms_away_prob * 100, 1),
|
||||
"pick": self.ms_pick,
|
||||
"confidence": round(self.ms_confidence, 1)
|
||||
},
|
||||
"double_chance": {
|
||||
"1X": round(self.dc_1x_prob * 100, 1),
|
||||
"X2": round(self.dc_x2_prob * 100, 1),
|
||||
"12": round(self.dc_12_prob * 100, 1),
|
||||
"pick": self.dc_pick,
|
||||
"confidence": round(self.dc_confidence, 1)
|
||||
},
|
||||
"over_under": {
|
||||
"1.5": {
|
||||
"over": round(self.over_15_prob * 100, 1),
|
||||
"under": round(self.under_15_prob * 100, 1),
|
||||
"pick": self.ou15_pick,
|
||||
"confidence": round(self.ou15_confidence, 1)
|
||||
},
|
||||
"2.5": {
|
||||
"over": round(self.over_25_prob * 100, 1),
|
||||
"under": round(self.under_25_prob * 100, 1),
|
||||
"pick": self.ou25_pick,
|
||||
"confidence": round(self.ou25_confidence, 1)
|
||||
},
|
||||
"3.5": {
|
||||
"over": round(self.over_35_prob * 100, 1),
|
||||
"under": round(self.under_35_prob * 100, 1),
|
||||
"pick": self.ou35_pick,
|
||||
"confidence": round(self.ou35_confidence, 1)
|
||||
}
|
||||
},
|
||||
"btts": {
|
||||
"yes": round(self.btts_yes_prob * 100, 1),
|
||||
"no": round(self.btts_no_prob * 100, 1),
|
||||
"pick": self.btts_pick,
|
||||
"confidence": round(self.btts_confidence, 1)
|
||||
},
|
||||
"first_half": {
|
||||
"1": round(self.ht_home_prob * 100, 1),
|
||||
"X": round(self.ht_draw_prob * 100, 1),
|
||||
"2": round(self.ht_away_prob * 100, 1),
|
||||
"pick": self.ht_pick,
|
||||
"confidence": round(self.ht_confidence, 1),
|
||||
"over_under_05": {
|
||||
"over": round(self.ht_over_05_prob * 100, 1),
|
||||
"under": round(self.ht_under_05_prob * 100, 1),
|
||||
"pick": self.ht_ou_pick
|
||||
},
|
||||
"over_under_15": {
|
||||
"over": round(self.ht_over_15_prob * 100, 1),
|
||||
"under": round(self.ht_under_15_prob * 100, 1),
|
||||
"pick": self.ht_ou15_pick
|
||||
}
|
||||
},
|
||||
"scores": {
|
||||
"predicted_ft": self.predicted_ft_score,
|
||||
"predicted_ht": self.predicted_ht_score,
|
||||
"top_5_ft_scores": self.ft_scores_top5
|
||||
},
|
||||
"others": {
|
||||
"handicap": {
|
||||
"pick": self.handicap_pick,
|
||||
"confidence": round(self.handicap_confidence, 1),
|
||||
"home": round(self.handicap_home_prob * 100, 1),
|
||||
"draw": round(self.handicap_draw_prob * 100, 1),
|
||||
"away": round(self.handicap_away_prob * 100, 1)
|
||||
},
|
||||
"corners": {
|
||||
"total": round(self.total_corners_pred, 1),
|
||||
"pick": self.corner_pick
|
||||
},
|
||||
"cards": {
|
||||
"total": round(self.total_cards_pred, 1),
|
||||
"pick": self.card_pick,
|
||||
"confidence": round(self.cards_confidence, 1),
|
||||
"over": round(self.cards_over_prob * 100, 1),
|
||||
"under": round(self.cards_under_prob * 100, 1)
|
||||
},
|
||||
"odd_even": {
|
||||
"pick": self.odd_even_pick,
|
||||
"tek": round(self.odd_prob * 100, 1),
|
||||
"cift": round(self.even_prob * 100, 1)
|
||||
}
|
||||
},
|
||||
"xg": {
|
||||
"home": round(self.home_xg, 2),
|
||||
"away": round(self.away_xg, 2),
|
||||
"total": round(self.total_xg, 2)
|
||||
}
|
||||
},
|
||||
"risk": {
|
||||
"level": self.risk_level,
|
||||
"score": round(self.risk_score, 1),
|
||||
"is_surprise_risk": self.is_surprise_risk,
|
||||
"surprise_type": self.surprise_type,
|
||||
"ht_ft_probs": {k: round(v * 100, 1) for k, v in self.ht_ft_probs.items()} if self.ht_ft_probs else {},
|
||||
"warnings": self.risk_warnings
|
||||
},
|
||||
"upset_analysis": {
|
||||
"score": self.upset_score,
|
||||
"level": self.upset_level,
|
||||
"reasons": self.upset_reasons
|
||||
},
|
||||
"engine_breakdown": {
|
||||
"team_engine": round(self.team_confidence, 1),
|
||||
"player_engine": round(self.player_confidence, 1),
|
||||
"odds_engine": round(self.odds_confidence, 1),
|
||||
"referee_engine": round(self.referee_confidence, 1)
|
||||
},
|
||||
"recommendations": {
|
||||
"best_bet": self.best_bet.to_dict() if self.best_bet else None,
|
||||
"all_recommended": [b.to_dict() for b in self.recommended_bets] if self.recommended_bets else [],
|
||||
"alternative_bet": self.alternative_bet.to_dict() if self.alternative_bet else None
|
||||
},
|
||||
"analysis_details": self.analysis_details
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
"""
|
||||
Calibration Backfill Script
|
||||
============================
|
||||
Runs V25 model against historical matches (using pre-computed ai_features + odds)
|
||||
to generate calibration training data, then trains isotonic calibration models.
|
||||
|
||||
Usage:
|
||||
python ai-engine/scripts/backfill_calibration.py
|
||||
python ai-engine/scripts/backfill_calibration.py --limit 5000
|
||||
python ai-engine/scripts/backfill_calibration.py --min-samples 50
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
from dotenv import load_dotenv
|
||||
|
||||
AI_ENGINE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, AI_ENGINE_DIR)
|
||||
|
||||
from models.v25_ensemble import V25Predictor
|
||||
from models.calibration import get_calibrator
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
def _normalize_pick(pick) -> str:
|
||||
return str(pick or "").strip().casefold()
|
||||
|
||||
|
||||
def resolve_actual(market, pick, score_home, score_away, ht_home, ht_away):
|
||||
if score_home is None or score_away is None:
|
||||
return None
|
||||
market = (market or "").upper()
|
||||
p = _normalize_pick(pick)
|
||||
total = score_home + score_away
|
||||
ht_total = (ht_home or 0) + (ht_away or 0) if ht_home is not None else None
|
||||
|
||||
if market == "MS":
|
||||
if p == "1": return int(score_home > score_away)
|
||||
if p in {"x", "0"}: return int(score_home == score_away)
|
||||
if p == "2": return int(score_away > score_home)
|
||||
return None
|
||||
if market in {"OU15", "OU25", "OU35"}:
|
||||
line = {"OU15": 1.5, "OU25": 2.5, "OU35": 3.5}[market]
|
||||
if "over" in p or "üst" in p or "ust" in p: return int(total > line)
|
||||
if "under" in p or "alt" in p: return int(total < line)
|
||||
return None
|
||||
if market == "BTTS":
|
||||
both = score_home > 0 and score_away > 0
|
||||
if "yes" in p or "var" in p: return int(both)
|
||||
if "no" in p or "yok" in p: return int(not both)
|
||||
return None
|
||||
if market == "HT":
|
||||
if ht_home is None or ht_away is None: return None
|
||||
if p == "1": return int(ht_home > ht_away)
|
||||
if p in {"x", "0"}: return int(ht_home == ht_away)
|
||||
if p == "2": return int(ht_away > ht_home)
|
||||
return None
|
||||
if market == "HTFT":
|
||||
if ht_home is None or ht_away is None or "/" not in p: return None
|
||||
ht_p, ft_p = p.split("/")
|
||||
ht_actual = "1" if ht_home > ht_away else "2" if ht_away > ht_home else "x"
|
||||
ft_actual = "1" if score_home > score_away else "2" if score_away > score_home else "x"
|
||||
return int(ht_p.strip() == ht_actual and ft_p.strip() == ft_actual)
|
||||
if market == "DC":
|
||||
norm = p.replace("-", "").upper()
|
||||
if norm == "1X": return int(score_home >= score_away)
|
||||
if norm == "X2": return int(score_away >= score_home)
|
||||
if norm == "12": return int(score_home != score_away)
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def calibrator_key(market, pick):
|
||||
m = (market or "").upper()
|
||||
p = _normalize_pick(pick)
|
||||
if m == "MS":
|
||||
if p == "1": return "ms_home"
|
||||
if p in {"x", "0"}: return "ms_draw"
|
||||
if p == "2": return "ms_away"
|
||||
return None
|
||||
if m == "DC": return "dc"
|
||||
if m == "OU15" and ("over" in p or "üst" in p): return "ou15"
|
||||
if m == "OU25" and ("over" in p or "üst" in p): return "ou25"
|
||||
if m == "OU35" and ("over" in p or "üst" in p): return "ou35"
|
||||
if m == "BTTS" and ("yes" in p or "var" in p): return "btts"
|
||||
if m == "HT":
|
||||
if p == "1": return "ht_home"
|
||||
if p in {"x", "0"}: return "ht_draw"
|
||||
if p == "2": return "ht_away"
|
||||
return None
|
||||
if m == "HTFT": return "ht_ft"
|
||||
return None
|
||||
|
||||
|
||||
def get_conn():
|
||||
db_url = os.getenv("DATABASE_URL", "")
|
||||
if "?schema=" in db_url:
|
||||
db_url = db_url.split("?schema=")[0]
|
||||
if not db_url:
|
||||
raise ValueError("DATABASE_URL not set")
|
||||
return psycopg2.connect(db_url, cursor_factory=RealDictCursor)
|
||||
|
||||
|
||||
ODD_CAT_MAP = {
|
||||
"maç sonucu": {"1": "ms_h", "0": "ms_d", "x": "ms_d", "2": "ms_a"},
|
||||
"1. yarı sonucu": {"1": "ht_ms_h", "0": "ht_ms_d", "x": "ht_ms_d", "2": "ht_ms_a"},
|
||||
}
|
||||
|
||||
ODD_CAT_KEYWORD_MAP = {
|
||||
"karşılıklı gol": {"var": "btts_y", "yok": "btts_n"},
|
||||
"0,5 alt/üst": {"alt": "ou05_u", "üst": "ou05_o"},
|
||||
"1,5 alt/üst": {"alt": "ou15_u", "üst": "ou15_o"},
|
||||
"2,5 alt/üst": {"alt": "ou25_u", "üst": "ou25_o"},
|
||||
"3,5 alt/üst": {"alt": "ou35_u", "üst": "ou35_o"},
|
||||
"ilk yarı 0,5 alt/üst": {"alt": "ht_ou05_u", "üst": "ht_ou05_o"},
|
||||
"ilk yarı 1,5 alt/üst": {"alt": "ht_ou15_u", "üst": "ht_ou15_o"},
|
||||
}
|
||||
|
||||
|
||||
def load_matches(cur, limit: int) -> List[Dict]:
|
||||
cur.execute("""
|
||||
SELECT m.id, m.score_home, m.score_away,
|
||||
m.ht_score_home, m.ht_score_away
|
||||
FROM matches m
|
||||
JOIN football_ai_features f ON f.match_id = m.id
|
||||
WHERE m.status = 'FT'
|
||||
AND m.sport = 'football'
|
||||
AND m.score_home IS NOT NULL
|
||||
AND m.score_away IS NOT NULL
|
||||
ORDER BY m.mst_utc DESC
|
||||
LIMIT %s
|
||||
""", (limit,))
|
||||
return cur.fetchall()
|
||||
|
||||
|
||||
def load_ai_features_batch(cur, match_ids: List[str]) -> Dict[str, Dict]:
|
||||
if not match_ids:
|
||||
return {}
|
||||
ph = ",".join(["%s"] * len(match_ids))
|
||||
cur.execute(f"""
|
||||
SELECT match_id,
|
||||
home_elo AS home_overall_elo,
|
||||
away_elo AS away_overall_elo,
|
||||
elo_diff,
|
||||
home_home_elo, away_away_elo,
|
||||
home_form_elo, away_form_elo,
|
||||
(home_form_elo - away_form_elo) AS form_elo_diff,
|
||||
home_goals_avg_5 AS home_goals_avg,
|
||||
home_conceded_avg_5 AS home_conceded_avg,
|
||||
away_goals_avg_5 AS away_goals_avg,
|
||||
away_conceded_avg_5 AS away_conceded_avg,
|
||||
home_clean_sheet_rate, away_clean_sheet_rate,
|
||||
home_scoring_rate, away_scoring_rate,
|
||||
home_win_streak AS home_winning_streak,
|
||||
away_win_streak AS away_winning_streak,
|
||||
0 AS home_unbeaten_streak,
|
||||
0 AS away_unbeaten_streak,
|
||||
h2h_total AS h2h_total_matches,
|
||||
h2h_home_win_rate,
|
||||
(1.0 - h2h_home_win_rate - 0.33) AS h2h_draw_rate,
|
||||
h2h_avg_goals,
|
||||
h2h_btts_rate, h2h_over25_rate,
|
||||
home_avg_possession, away_avg_possession,
|
||||
home_avg_shots_on_target, away_avg_shots_on_target,
|
||||
home_shot_conversion, away_shot_conversion,
|
||||
0.0 AS home_avg_corners, 0.0 AS away_avg_corners,
|
||||
implied_home, implied_draw, implied_away,
|
||||
league_avg_goals,
|
||||
0.0 AS league_zero_goal_rate,
|
||||
0.0 AS home_xga, 0.0 AS away_xga,
|
||||
0.0 AS upset_atmosphere, 0.0 AS upset_motivation,
|
||||
0.0 AS upset_fatigue, 0.0 AS upset_potential,
|
||||
referee_home_bias, referee_avg_goals,
|
||||
referee_avg_cards AS referee_cards_total,
|
||||
0.0 AS referee_avg_yellow,
|
||||
0.0 AS referee_experience,
|
||||
0.0 AS home_momentum_score, 0.0 AS away_momentum_score,
|
||||
0.0 AS momentum_diff,
|
||||
0.0 AS home_squad_quality, 0.0 AS away_squad_quality,
|
||||
0.0 AS squad_diff,
|
||||
0 AS home_key_players, 0 AS away_key_players,
|
||||
missing_players_impact AS home_missing_impact,
|
||||
0.0 AS away_missing_impact,
|
||||
home_goals_avg_5 AS home_goals_form,
|
||||
away_goals_avg_5 AS away_goals_form
|
||||
FROM football_ai_features
|
||||
WHERE match_id IN ({ph})
|
||||
""", match_ids)
|
||||
return {str(row["match_id"]): dict(row) for row in cur.fetchall()}
|
||||
|
||||
|
||||
def load_odds_batch(cur, match_ids: List[str]) -> Dict[str, Dict[str, float]]:
|
||||
if not match_ids:
|
||||
return {}
|
||||
ph = ",".join(["%s"] * len(match_ids))
|
||||
cur.execute(f"""
|
||||
SELECT oc.match_id, oc.name AS cat_name,
|
||||
os.name AS sel_name, os.odd_value
|
||||
FROM odd_selections os
|
||||
JOIN odd_categories oc ON os.odd_category_db_id = oc.db_id
|
||||
WHERE oc.match_id IN ({ph})
|
||||
""", match_ids)
|
||||
|
||||
odds: Dict[str, Dict[str, float]] = {}
|
||||
for row in cur.fetchall():
|
||||
mid = str(row["match_id"])
|
||||
cat = (row["cat_name"] or "").lower().strip()
|
||||
sel = (row["sel_name"] or "").strip()
|
||||
val = float(row["odd_value"]) if row["odd_value"] else 0
|
||||
if val <= 0:
|
||||
continue
|
||||
if mid not in odds:
|
||||
odds[mid] = {}
|
||||
|
||||
if cat in ODD_CAT_MAP:
|
||||
key = ODD_CAT_MAP[cat].get(sel.lower())
|
||||
if key:
|
||||
odds[mid][key] = val
|
||||
else:
|
||||
for cat_pattern, kw_map in ODD_CAT_KEYWORD_MAP.items():
|
||||
if cat == cat_pattern:
|
||||
for keyword, key in kw_map.items():
|
||||
if keyword in sel.lower():
|
||||
odds[mid][key] = val
|
||||
break
|
||||
return odds
|
||||
|
||||
|
||||
MARKETS_TO_PREDICT = [
|
||||
("MS", "1", lambda p: p[0]),
|
||||
("MS", "X", lambda p: p[1]),
|
||||
("MS", "2", lambda p: p[2]),
|
||||
("OU25", "Over 2.5", lambda p: p[0]),
|
||||
("BTTS", "Yes", lambda p: p[0]),
|
||||
("OU15", "Over 1.5", lambda p: p[0]),
|
||||
("OU35", "Over 3.5", lambda p: p[0]),
|
||||
("HT", "1", lambda p: p[0]),
|
||||
("HT", "X", lambda p: p[1]),
|
||||
("HT", "2", lambda p: p[2]),
|
||||
]
|
||||
|
||||
|
||||
def run_backfill(args):
|
||||
print("=" * 70)
|
||||
print("CALIBRATION BACKFILL")
|
||||
print("=" * 70)
|
||||
|
||||
conn = get_conn()
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
t0 = time.time()
|
||||
print(f"Loading matches (limit={args.limit})...")
|
||||
matches = load_matches(cur, args.limit)
|
||||
print(f" Found {len(matches)} finished matches with ai_features")
|
||||
|
||||
match_ids = [str(m["id"]) for m in matches]
|
||||
match_map = {str(m["id"]): m for m in matches}
|
||||
|
||||
print("Loading ai_features...")
|
||||
features_map = load_ai_features_batch(cur, match_ids)
|
||||
print(f" Loaded features for {len(features_map)} matches")
|
||||
|
||||
print("Loading odds...")
|
||||
odds_map = load_odds_batch(cur, match_ids)
|
||||
print(f" Loaded odds for {len(odds_map)} matches")
|
||||
|
||||
print(f"Data loading: {time.time() - t0:.1f}s")
|
||||
|
||||
print("\nLoading V25 model...")
|
||||
predictor = V25Predictor()
|
||||
predictor.load_models()
|
||||
|
||||
feature_cols = predictor.FEATURE_COLS
|
||||
|
||||
samples: List[Dict[str, Any]] = []
|
||||
skipped = 0
|
||||
processed = 0
|
||||
|
||||
print(f"\nRunning predictions on {len(match_ids)} matches...")
|
||||
t1 = time.time()
|
||||
|
||||
for i, mid in enumerate(match_ids):
|
||||
if mid not in features_map:
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
feat_row = features_map[mid]
|
||||
odds_row = odds_map.get(mid, {})
|
||||
match_row = match_map[mid]
|
||||
|
||||
feat_dict = {}
|
||||
for col in feature_cols:
|
||||
if col in feat_row and feat_row[col] is not None:
|
||||
feat_dict[col] = float(feat_row[col])
|
||||
elif col.startswith("odds_") and not col.endswith("_present"):
|
||||
odds_key = col.replace("odds_", "")
|
||||
feat_dict[col] = float(odds_row.get(odds_key, 0))
|
||||
elif col.endswith("_present"):
|
||||
base = col.replace("_present", "")
|
||||
odds_key = base.replace("odds_", "")
|
||||
feat_dict[col] = 1.0 if odds_row.get(odds_key, 0) > 0 else 0.0
|
||||
else:
|
||||
feat_dict[col] = 0.0
|
||||
|
||||
if odds_row.get("ms_h", 0) > 0:
|
||||
feat_dict["odds_ms_h"] = odds_row["ms_h"]
|
||||
if odds_row.get("ms_d", 0) > 0:
|
||||
feat_dict["odds_ms_d"] = odds_row["ms_d"]
|
||||
if odds_row.get("ms_a", 0) > 0:
|
||||
feat_dict["odds_ms_a"] = odds_row["ms_a"]
|
||||
|
||||
ms_h = feat_dict.get("odds_ms_h", 0)
|
||||
ms_d = feat_dict.get("odds_ms_d", 0)
|
||||
ms_a = feat_dict.get("odds_ms_a", 0)
|
||||
if ms_h > 0 and ms_d > 0 and ms_a > 0:
|
||||
raw_sum = 1/ms_h + 1/ms_d + 1/ms_a
|
||||
feat_dict["implied_home"] = (1/ms_h) / raw_sum
|
||||
feat_dict["implied_draw"] = (1/ms_d) / raw_sum
|
||||
feat_dict["implied_away"] = (1/ms_a) / raw_sum
|
||||
|
||||
sh = match_row["score_home"]
|
||||
sa = match_row["score_away"]
|
||||
ht_h = match_row.get("ht_score_home")
|
||||
ht_a = match_row.get("ht_score_away")
|
||||
|
||||
try:
|
||||
X = pd.DataFrame([{c: feat_dict.get(c, 0.0) for c in feature_cols}])
|
||||
|
||||
for market_name, model_key, market_list in [
|
||||
("ms", "ms", ["MS"]),
|
||||
("ou25", "ou25", ["OU25"]),
|
||||
("btts", "btts", ["BTTS"]),
|
||||
("ou15", "ou15", ["OU15"]),
|
||||
("ou35", "ou35", ["OU35"]),
|
||||
("ht_result", "ht_result", ["HT"]),
|
||||
]:
|
||||
if model_key not in predictor.models:
|
||||
continue
|
||||
|
||||
probs = predictor.predict_market(model_key, feat_dict)
|
||||
if probs is None:
|
||||
continue
|
||||
|
||||
if model_key == "ms":
|
||||
for pick, prob in [("1", probs[0]), ("X", probs[1]), ("2", probs[2])]:
|
||||
actual = resolve_actual("MS", pick, sh, sa, ht_h, ht_a)
|
||||
key = calibrator_key("MS", pick)
|
||||
if actual is not None and key:
|
||||
samples.append({
|
||||
"match_id": mid,
|
||||
"market": "MS",
|
||||
"pick": pick,
|
||||
"key": key,
|
||||
"raw_prob": float(prob),
|
||||
"actual": int(actual),
|
||||
})
|
||||
|
||||
elif model_key == "ht_result":
|
||||
if ht_h is None or ht_a is None:
|
||||
continue
|
||||
for pick, prob in [("1", probs[0]), ("X", probs[1]), ("2", probs[2])]:
|
||||
actual = resolve_actual("HT", pick, sh, sa, ht_h, ht_a)
|
||||
key = calibrator_key("HT", pick)
|
||||
if actual is not None and key:
|
||||
samples.append({
|
||||
"match_id": mid,
|
||||
"market": "HT",
|
||||
"pick": pick,
|
||||
"key": key,
|
||||
"raw_prob": float(prob),
|
||||
"actual": int(actual),
|
||||
})
|
||||
|
||||
elif model_key in ("ou25", "ou15", "ou35"):
|
||||
market_upper = model_key.upper()
|
||||
over_prob = float(probs[0]) if len(probs) > 0 else 0.5
|
||||
pick = f"Over"
|
||||
actual = resolve_actual(market_upper, "Over", sh, sa, ht_h, ht_a)
|
||||
key = calibrator_key(market_upper, "Over")
|
||||
if actual is not None and key:
|
||||
samples.append({
|
||||
"match_id": mid,
|
||||
"market": market_upper,
|
||||
"pick": pick,
|
||||
"key": key,
|
||||
"raw_prob": over_prob,
|
||||
"actual": int(actual),
|
||||
})
|
||||
|
||||
elif model_key == "btts":
|
||||
yes_prob = float(probs[0]) if len(probs) > 0 else 0.5
|
||||
actual = resolve_actual("BTTS", "Yes", sh, sa, ht_h, ht_a)
|
||||
key = calibrator_key("BTTS", "Yes")
|
||||
if actual is not None and key:
|
||||
samples.append({
|
||||
"match_id": mid,
|
||||
"market": "BTTS",
|
||||
"pick": "Yes",
|
||||
"key": key,
|
||||
"raw_prob": yes_prob,
|
||||
"actual": int(actual),
|
||||
})
|
||||
|
||||
processed += 1
|
||||
|
||||
except Exception as e:
|
||||
skipped += 1
|
||||
if skipped <= 5:
|
||||
print(f" Error on {mid}: {e}")
|
||||
|
||||
if (i + 1) % 5000 == 0:
|
||||
elapsed = time.time() - t1
|
||||
rate = (i + 1) / elapsed
|
||||
print(f" Processed {i+1}/{len(match_ids)} ({rate:.0f} matches/s)")
|
||||
|
||||
elapsed = time.time() - t1
|
||||
print(f"\nPrediction complete: {processed} matches, {skipped} skipped, {elapsed:.1f}s")
|
||||
|
||||
if not samples:
|
||||
print("No calibration samples generated!")
|
||||
cur.close()
|
||||
conn.close()
|
||||
return
|
||||
|
||||
df = pd.DataFrame(samples)
|
||||
print(f"\nTotal calibration samples: {len(df)}")
|
||||
print(f"Unique matches: {df['match_id'].nunique()}")
|
||||
print(f"\nPer-key counts:")
|
||||
for key, count in df["key"].value_counts().items():
|
||||
print(f" {key:<14} {count}")
|
||||
|
||||
print(f"\nTraining isotonic calibration models (min_samples={args.min_samples})...")
|
||||
calibrator = get_calibrator()
|
||||
results: Dict[str, Any] = {}
|
||||
keys = sorted(df["key"].unique())
|
||||
|
||||
for key in keys:
|
||||
sub = df[df["key"] == key].copy()
|
||||
sub = sub.drop_duplicates(subset=["match_id", "key"], keep="first")
|
||||
sub = sub.dropna(subset=["raw_prob", "actual"])
|
||||
sub = sub[(sub["raw_prob"] > 0.0) & (sub["raw_prob"] < 1.0)]
|
||||
|
||||
n = len(sub)
|
||||
if n < args.min_samples:
|
||||
results[key] = {"status": "skipped", "samples": n}
|
||||
continue
|
||||
|
||||
metrics = calibrator.train_calibration(
|
||||
df=sub,
|
||||
market=key,
|
||||
prob_col="raw_prob",
|
||||
actual_col="actual",
|
||||
min_samples=args.min_samples,
|
||||
save=True,
|
||||
)
|
||||
results[key] = {
|
||||
"status": "trained",
|
||||
"samples": metrics.sample_count,
|
||||
"brier": round(metrics.brier_score, 4),
|
||||
"ece": round(metrics.calibration_error, 4),
|
||||
"mean_predicted": round(metrics.mean_predicted, 4),
|
||||
"mean_actual": round(metrics.mean_actual, 4),
|
||||
}
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("CALIBRATION RESULTS")
|
||||
print("=" * 70)
|
||||
print(f"{'market':<14} {'status':<10} {'n':<8} {'brier':<9} {'ece':<8} {'pred_avg':<9} {'actual_avg'}")
|
||||
print("-" * 70)
|
||||
for key, info in sorted(results.items()):
|
||||
if info["status"] == "trained":
|
||||
print(
|
||||
f"{key:<14} {'OK':<10} {info['samples']:<8} "
|
||||
f"{info['brier']:<9.4f} {info['ece']:<8.4f} "
|
||||
f"{info['mean_predicted']:<9.4f} {info['mean_actual']}"
|
||||
)
|
||||
else:
|
||||
print(f"{key:<14} {'SKIP':<10} {info['samples']:<8}")
|
||||
print("=" * 70)
|
||||
|
||||
total_time = time.time() - t0
|
||||
print(f"\nTotal time: {total_time:.1f}s")
|
||||
print(f"Calibration models saved to: {os.path.join(AI_ENGINE_DIR, 'models', 'calibration')}/")
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Backfill calibration from historical matches")
|
||||
parser.add_argument("--limit", type=int, default=50000,
|
||||
help="Max matches to process (default: 50000)")
|
||||
parser.add_argument("--min-samples", type=int, default=100,
|
||||
help="Min samples per market for calibration (default: 100)")
|
||||
args = parser.parse_args()
|
||||
run_backfill(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,352 @@
|
||||
"""
|
||||
Tutarsızlık Bazlı Backtest
|
||||
============================
|
||||
Modeller arası tutarsızlığı ölçer, tutarlı maçlarda bahis açılsaydı
|
||||
ROI ne olurdu hesaplar.
|
||||
|
||||
Mantık:
|
||||
- Her maç için market'ler arası çelişkileri tespit et
|
||||
- Tutarsız maçları filtrele
|
||||
- Tutarlı maçlarda hit rate ve ROI hesapla
|
||||
|
||||
Usage:
|
||||
python scripts/backtest_consistency.py
|
||||
"""
|
||||
|
||||
import os, sys, json
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import xgboost as xgb
|
||||
from sklearn.metrics import accuracy_score
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
DATA_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
'data', 'training_data.csv')
|
||||
MODELS_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
'models', 'v25')
|
||||
|
||||
SKIP_COLS = {
|
||||
'match_id','home_team_id','away_team_id','league_id','mst_utc',
|
||||
'score_home','score_away','total_goals','ht_score_home','ht_score_away','ht_total_goals',
|
||||
'label_ms','label_ou05','label_ou15','label_ou25','label_ou35','label_btts',
|
||||
'label_ht_result','label_ht_ou05','label_ht_ou15','label_ht_ft',
|
||||
'label_odd_even','label_yellow_cards','label_cards_ou45','label_handicap_ms',
|
||||
}
|
||||
|
||||
|
||||
def load_model(market: str):
|
||||
path = os.path.join(MODELS_DIR, f'xgb_v25_{market}.json')
|
||||
if not os.path.exists(path):
|
||||
return None
|
||||
b = xgb.Booster()
|
||||
b.load_model(path)
|
||||
return b
|
||||
|
||||
|
||||
def predict_proba(model, X: np.ndarray, feature_cols: list, n_class: int):
|
||||
dmat = xgb.DMatrix(pd.DataFrame(X, columns=feature_cols))
|
||||
raw = model.predict(dmat)
|
||||
if n_class > 2:
|
||||
return raw.reshape(-1, n_class)
|
||||
return np.column_stack([1 - raw, raw])
|
||||
|
||||
|
||||
def consistency_score(probs: dict) -> tuple[float, list]:
|
||||
"""
|
||||
Market'ler arası tutarsızlığı hesapla.
|
||||
0 = tamamen tutarlı, 1 = tamamen çelişkili.
|
||||
|
||||
Kontrol edilen çelişkiler:
|
||||
1. OU15 üst yüksek ama OU25 üst de yüksek → ok
|
||||
OU15 üst yüksek ama OU25 alt yüksek → ÇELISKI (1 gol bekleniyor ama 2.5+ da bekleniyor?)
|
||||
|
||||
2. HT_OU05 üst yüksek ama HT sonucu draw yüksek → ÇELISKI
|
||||
|
||||
3. OU35 üst yüksek ama BTTS düşük → şüpheli
|
||||
|
||||
4. MS home yüksek ama HT away yüksek → çelişkili
|
||||
"""
|
||||
conflicts = []
|
||||
total_weight = 0
|
||||
total_conflict = 0
|
||||
|
||||
# OU tutarlılığı: P(OU25>0.5) <= P(OU15>0.5) matematiksel zorunluluk
|
||||
ou15_over = probs.get('ou15_over', 0.5)
|
||||
ou25_over = probs.get('ou25_over', 0.5)
|
||||
ou35_over = probs.get('ou35_over', 0.5)
|
||||
|
||||
# OU hiyerarşisi: ou35 <= ou25 <= ou15 olmalı
|
||||
if ou25_over > ou15_over + 0.05:
|
||||
gap = ou25_over - ou15_over
|
||||
conflicts.append(f'OU25>{ou25_over:.0%} > OU15>{ou15_over:.0%} (imkansız)')
|
||||
total_conflict += gap * 2
|
||||
total_weight += 1
|
||||
|
||||
if ou35_over > ou25_over + 0.05:
|
||||
gap = ou35_over - ou25_over
|
||||
conflicts.append(f'OU35>{ou35_over:.0%} > OU25>{ou25_over:.0%} (imkansız)')
|
||||
total_conflict += gap * 2
|
||||
total_weight += 1
|
||||
|
||||
# HT_OU05 ve HT sonuç tutarlılığı
|
||||
ht_ou05_over = probs.get('ht_ou05_over', 0.5)
|
||||
ht_draw_prob = probs.get('ht_draw', 0.34)
|
||||
|
||||
# İlk yarıda gol bekleniyor ama beraberlik de bekleniyor (0-0 draw?)
|
||||
# HT_OU05 >%70 ama HT draw >%50 → çelişkili (0-0 berabere çok?)
|
||||
if ht_ou05_over > 0.70 and ht_draw_prob > 0.50:
|
||||
conflict = min(ht_ou05_over - 0.5, ht_draw_prob - 0.4)
|
||||
conflicts.append(f'HT_OU05>{ht_ou05_over:.0%} ama HT_Draw>{ht_draw_prob:.0%}')
|
||||
total_conflict += conflict
|
||||
total_weight += 1
|
||||
|
||||
# HT_OU05 ve HT_OU15 tutarlılığı
|
||||
ht_ou15_over = probs.get('ht_ou15_over', 0.3)
|
||||
if ht_ou15_over > ht_ou05_over + 0.05:
|
||||
gap = ht_ou15_over - ht_ou05_over
|
||||
conflicts.append(f'HT_OU15>{ht_ou15_over:.0%} > HT_OU05>{ht_ou05_over:.0%} (imkansız)')
|
||||
total_conflict += gap * 2
|
||||
total_weight += 1
|
||||
|
||||
# MS ve OU tutarlılığı
|
||||
ms_home = probs.get('ms_home', 0.33)
|
||||
ms_away = probs.get('ms_away', 0.33)
|
||||
btts_yes = probs.get('btts_yes', 0.5)
|
||||
|
||||
# Tek takım galibiyeti kuvvetli ama BTTS yüksek → şüpheli
|
||||
dominant = max(ms_home, ms_away)
|
||||
if dominant > 0.65 and btts_yes > 0.65:
|
||||
conflict = (dominant - 0.5) * (btts_yes - 0.5)
|
||||
conflicts.append(f'MS dominant>{dominant:.0%} ama BTTS_Yes>{btts_yes:.0%}')
|
||||
total_conflict += conflict * 0.5
|
||||
total_weight += 1
|
||||
|
||||
# OU25 ve BTTS tutarlılığı
|
||||
# BTTS yüksekse en az 2 gol → OU25 üst de yüksek olmalı
|
||||
if btts_yes > 0.65 and ou25_over < 0.45:
|
||||
conflict = btts_yes - ou25_over
|
||||
conflicts.append(f'BTTS_Yes>{btts_yes:.0%} ama OU25>{ou25_over:.0%} düşük')
|
||||
total_conflict += conflict
|
||||
total_weight += 1
|
||||
|
||||
# OU35 üst yüksek ama BTTS düşük → şüpheli (3+ gol ama tek takım mı?)
|
||||
if ou35_over > 0.45 and btts_yes < 0.40:
|
||||
conflict = (ou35_over - 0.35) * (0.5 - btts_yes)
|
||||
conflicts.append(f'OU35>{ou35_over:.0%} ama BTTS_Yes<{btts_yes:.0%}')
|
||||
total_conflict += conflict
|
||||
total_weight += 1
|
||||
|
||||
score = min(1.0, total_conflict / max(total_weight * 0.3, 0.1))
|
||||
return score, conflicts
|
||||
|
||||
|
||||
def main():
|
||||
print('Loading data...')
|
||||
df = pd.read_csv(DATA_PATH, low_memory=False)
|
||||
|
||||
# Son %20 = test seti (kronolojik)
|
||||
df = df.sort_values('mst_utc')
|
||||
n_test = int(len(df) * 0.20)
|
||||
df_test = df.tail(n_test).copy()
|
||||
print(f'Test seti: {len(df_test):,} maç')
|
||||
|
||||
feature_cols = [c for c in df.columns if c not in SKIP_COLS]
|
||||
|
||||
# Modelleri yükle
|
||||
print('Modeller yükleniyor...')
|
||||
models = {
|
||||
'ms': (load_model('ms'), 3),
|
||||
'ou15': (load_model('ou15'), 2),
|
||||
'ou25': (load_model('ou25'), 2),
|
||||
'ou35': (load_model('ou35'), 2),
|
||||
'btts': (load_model('btts'), 2),
|
||||
'ht_result':(load_model('ht_result'), 3),
|
||||
'ht_ou05': (load_model('ht_ou05'), 2),
|
||||
'ht_ou15': (load_model('ht_ou15'), 2),
|
||||
}
|
||||
models = {k: v for k, v in models.items() if v[0] is not None}
|
||||
print(f'Yüklenen model: {list(models.keys())}')
|
||||
|
||||
X = df_test[feature_cols].fillna(0).values
|
||||
|
||||
# Tüm tahminleri al
|
||||
print('Tahminler yapılıyor...')
|
||||
preds = {}
|
||||
for mkey, (model, n_class) in models.items():
|
||||
p = predict_proba(model, X, feature_cols, n_class)
|
||||
preds[mkey] = p
|
||||
|
||||
# Her maç için tutarsızlık skoru ve tahmin kararı
|
||||
results = []
|
||||
for i in range(len(df_test)):
|
||||
row = df_test.iloc[i]
|
||||
|
||||
# Olasılıkları topla
|
||||
probs = {}
|
||||
if 'ms' in preds:
|
||||
probs['ms_home'] = preds['ms'][i][0]
|
||||
probs['ms_draw'] = preds['ms'][i][1]
|
||||
probs['ms_away'] = preds['ms'][i][2]
|
||||
if 'ou15' in preds:
|
||||
probs['ou15_over'] = preds['ou15'][i][1]
|
||||
if 'ou25' in preds:
|
||||
probs['ou25_over'] = preds['ou25'][i][1]
|
||||
if 'ou35' in preds:
|
||||
probs['ou35_over'] = preds['ou35'][i][1]
|
||||
if 'btts' in preds:
|
||||
probs['btts_yes'] = preds['btts'][i][1]
|
||||
if 'ht_result' in preds:
|
||||
probs['ht_home'] = preds['ht_result'][i][0]
|
||||
probs['ht_draw'] = preds['ht_result'][i][1]
|
||||
probs['ht_away'] = preds['ht_result'][i][2]
|
||||
if 'ht_ou05' in preds:
|
||||
probs['ht_ou05_over'] = preds['ht_ou05'][i][1]
|
||||
if 'ht_ou15' in preds:
|
||||
probs['ht_ou15_over'] = preds['ht_ou15'][i][1]
|
||||
|
||||
c_score, conflicts = consistency_score(probs)
|
||||
|
||||
# Gerçek sonuçlar
|
||||
actual = {
|
||||
'ms': int(row.get('label_ms', -1)),
|
||||
'ou15': int(row.get('label_ou15', -1)),
|
||||
'ou25': int(row.get('label_ou25', -1)),
|
||||
'ou35': int(row.get('label_ou35', -1)),
|
||||
'btts': int(row.get('label_btts', -1)),
|
||||
}
|
||||
|
||||
# Her market için tahmin ve doğruluk
|
||||
market_results = {}
|
||||
for mkt, label_key in [('ms','ms'),('ou15','ou15'),('ou25','ou25'),
|
||||
('ou35','ou35'),('btts','btts')]:
|
||||
if mkt not in preds or actual[label_key] < 0:
|
||||
continue
|
||||
pred_class = int(np.argmax(preds[mkt][i]))
|
||||
correct = int(pred_class == actual[label_key])
|
||||
|
||||
# Odds (implied prob → odds = 1/prob)
|
||||
pred_prob = float(preds[mkt][i][pred_class])
|
||||
implied_odds = 1 / pred_prob if pred_prob > 0.01 else 10.0
|
||||
# ROI hesabı: 1 birim bahis, kazanırsa (odds-1) kazanç, kaybederse -1
|
||||
roi = (implied_odds - 1) * correct - (1 - correct)
|
||||
|
||||
market_results[mkt] = {
|
||||
'pred': pred_class,
|
||||
'actual': actual[label_key],
|
||||
'correct': correct,
|
||||
'prob': pred_prob,
|
||||
'roi': roi,
|
||||
}
|
||||
|
||||
results.append({
|
||||
'idx': i,
|
||||
'consistency_score': c_score,
|
||||
'conflicts': conflicts,
|
||||
'probs': probs,
|
||||
'market_results': market_results,
|
||||
})
|
||||
|
||||
df_results = pd.DataFrame([{
|
||||
'consistency_score': r['consistency_score'],
|
||||
'n_conflicts': len(r['conflicts']),
|
||||
**{f'{m}_correct': r['market_results'].get(m, {}).get('correct', None)
|
||||
for m in ['ms','ou15','ou25','ou35','btts']},
|
||||
**{f'{m}_roi': r['market_results'].get(m, {}).get('roi', None)
|
||||
for m in ['ms','ou15','ou25','ou35','btts']},
|
||||
} for r in results])
|
||||
|
||||
# ── Analiz ──────────────────────────────────────────────────────────
|
||||
print(f'\n{"="*70}')
|
||||
print('TUTARSIZLIK ANALİZİ')
|
||||
print(f'{"="*70}')
|
||||
|
||||
thresholds = [0.0, 0.1, 0.2, 0.3, 0.5]
|
||||
markets = ['ms', 'ou15', 'ou25', 'ou35', 'btts']
|
||||
|
||||
for t in thresholds:
|
||||
mask = df_results['consistency_score'] <= t
|
||||
n = mask.sum()
|
||||
if n < 50:
|
||||
continue
|
||||
|
||||
print(f'\n[Tutarsızlık <= {t:.1f}] → {n:,} maç ({n/len(df_results)*100:.0f}%)')
|
||||
print(f' {"Market":<8} {"HitRate":>8} {"ROI/bahis":>10} {"Toplam ROI":>12}')
|
||||
print(f' {"-"*42}')
|
||||
for m in markets:
|
||||
col_c = f'{m}_correct'
|
||||
col_r = f'{m}_roi'
|
||||
if col_c not in df_results.columns:
|
||||
continue
|
||||
sub = df_results[mask][col_c].dropna()
|
||||
roi_sub = df_results[mask][col_r].dropna()
|
||||
if len(sub) < 20:
|
||||
continue
|
||||
hit = sub.mean()
|
||||
avg_roi = roi_sub.mean()
|
||||
total_roi = roi_sub.sum()
|
||||
print(f' {m:<8} {hit:>7.1%} {avg_roi:>+9.3f} {total_roi:>+11.1f}')
|
||||
|
||||
# Çelişki türlerine göre breakdown
|
||||
print(f'\n{"="*70}')
|
||||
print('EN SIK ÇELIŞKILER')
|
||||
print(f'{"="*70}')
|
||||
all_conflicts = [c for r in results for c in r['conflicts']]
|
||||
from collections import Counter
|
||||
for conflict, cnt in Counter(all_conflicts).most_common(10):
|
||||
print(f' {cnt:>5}x {conflict}')
|
||||
|
||||
# Tutarsızlık dağılımı
|
||||
print(f'\n{"="*70}')
|
||||
print('TUTARSIZLIK DAĞILIMI')
|
||||
print(f'{"="*70}')
|
||||
for label, lo, hi in [
|
||||
('Tamamen tutarlı', 0.0, 0.05),
|
||||
('Çok tutarlı', 0.05, 0.15),
|
||||
('Orta', 0.15, 0.30),
|
||||
('Tutarsız', 0.30, 0.50),
|
||||
('Çok tutarsız', 0.50, 1.01),
|
||||
]:
|
||||
mask = (df_results['consistency_score'] >= lo) & (df_results['consistency_score'] < hi)
|
||||
n = mask.sum()
|
||||
ou25_hit = df_results[mask]['ou25_correct'].mean()
|
||||
ms_hit = df_results[mask]['ms_correct'].mean()
|
||||
print(f' {label:<20} {n:>6,} maç ({n/len(df_results)*100:>4.0f}%) | '
|
||||
f'MS={ms_hit:.0%} OU25={ou25_hit:.0%}')
|
||||
|
||||
# Raporu kaydet
|
||||
report = {
|
||||
'total_test': len(df_results),
|
||||
'thresholds': {},
|
||||
}
|
||||
for t in thresholds:
|
||||
mask = df_results['consistency_score'] <= t
|
||||
n = mask.sum()
|
||||
report['thresholds'][str(t)] = {
|
||||
'n_matches': int(n),
|
||||
'pct': round(n/len(df_results)*100, 1),
|
||||
'markets': {},
|
||||
}
|
||||
for m in markets:
|
||||
col_c = f'{m}_correct'
|
||||
col_r = f'{m}_roi'
|
||||
if col_c not in df_results.columns:
|
||||
continue
|
||||
sub_c = df_results[mask][col_c].dropna()
|
||||
sub_r = df_results[mask][col_r].dropna()
|
||||
if len(sub_c) > 0:
|
||||
report['thresholds'][str(t)]['markets'][m] = {
|
||||
'hit_rate': round(float(sub_c.mean()), 4),
|
||||
'avg_roi': round(float(sub_r.mean()), 4),
|
||||
'total_roi': round(float(sub_r.sum()), 2),
|
||||
}
|
||||
|
||||
out_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
'reports', 'backtest_consistency.json')
|
||||
with open(out_path, 'w') as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(f'\nRapor: {out_path}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,146 @@
|
||||
import os
|
||||
import sys
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
|
||||
# Path ayarları
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from services.single_match_orchestrator import SingleMatchOrchestrator
|
||||
from services.feature_enrichment import FeatureEnrichmentService
|
||||
|
||||
DSN = "postgresql://suggestbet:SuGGesT2026SecuRe@localhost:15432/boilerplate_db"
|
||||
|
||||
def run_backtest(target_date="2026-05-03"):
|
||||
conn = psycopg2.connect(DSN)
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
# 1. Hedef tarihteki bitmiş maçları ve takım isimlerini getir
|
||||
cur.execute("""
|
||||
SELECT m.id, m.score_home, m.score_away, m.mst_utc,
|
||||
t1.name as home_name, t2.name as away_name
|
||||
FROM matches m
|
||||
LEFT JOIN teams t1 ON m.home_team_id = t1.id
|
||||
LEFT JOIN teams t2 ON m.away_team_id = t2.id
|
||||
WHERE m.status IN ('FT', 'AET', 'PEN')
|
||||
AND to_timestamp(m.mst_utc / 1000.0)::date = %s::date
|
||||
AND m.score_home IS NOT NULL
|
||||
ORDER BY m.mst_utc ASC
|
||||
""", (target_date,))
|
||||
matches = cur.fetchall()
|
||||
|
||||
if not matches:
|
||||
print(f"❌ {target_date} tarihinde bitmiş maç bulunamadı.")
|
||||
return
|
||||
|
||||
print(f"🚀 {target_date} için Orkestratör Backtesti Başlatılıyor... ({len(matches)} maç bulundu)")
|
||||
print("-" * 60)
|
||||
|
||||
orchestrator = SingleMatchOrchestrator()
|
||||
|
||||
bets_placed = 0
|
||||
won = 0
|
||||
lost = 0
|
||||
total_odds_won = 0.0
|
||||
|
||||
for match in matches:
|
||||
# 3. Üst Akıl (Orkestratör) analizi yapar
|
||||
try:
|
||||
package = orchestrator.analyze_match(match['id'])
|
||||
except Exception as e:
|
||||
print(f"Hata ({match['id']}): {e}")
|
||||
continue
|
||||
|
||||
if not package:
|
||||
continue
|
||||
|
||||
package_data = package
|
||||
|
||||
# 4. Üst akıl bu maça bahis yapmaya karar verdi mi?
|
||||
bet_advice = package_data.get("bet_advice", {})
|
||||
if bet_advice.get("playable") == True:
|
||||
bets_placed += 1
|
||||
main_pick = package_data.get("main_pick", {})
|
||||
market = main_pick.get("market")
|
||||
pick = main_pick.get("pick")
|
||||
odds = float(main_pick.get("odds", 0.0) or 0.0)
|
||||
|
||||
# Skora göre kazanıp kazanmadığını kontrol et
|
||||
is_won = False
|
||||
h = match['score_home']
|
||||
a = match['score_away']
|
||||
|
||||
if market == "MS":
|
||||
if pick == "1" and h > a: is_won = True
|
||||
elif pick in ("X", "0") and h == a: is_won = True
|
||||
elif pick == "2" and a > h: is_won = True
|
||||
elif market == "OU25":
|
||||
if pick == "Üst" and (h+a) > 2.5: is_won = True
|
||||
elif pick == "Alt" and (h+a) < 2.5: is_won = True
|
||||
elif market == "OU15":
|
||||
if pick == "Üst" and (h+a) > 1.5: is_won = True
|
||||
elif pick == "Alt" and (h+a) < 1.5: is_won = True
|
||||
elif market == "BTTS":
|
||||
if pick == "KG Var" and h > 0 and a > 0: is_won = True
|
||||
elif pick == "KG Yok" and (h == 0 or a == 0): is_won = True
|
||||
elif market == "DC":
|
||||
if pick == "1X" and h >= a: is_won = True
|
||||
elif pick == "12" and h != a: is_won = True
|
||||
elif pick == "X2" and h <= a: is_won = True
|
||||
|
||||
if is_won:
|
||||
won += 1
|
||||
total_odds_won += odds
|
||||
res = "✅ KAZANDI"
|
||||
else:
|
||||
lost += 1
|
||||
res = "❌ KAYBETTİ"
|
||||
|
||||
print(f"[{res}] {match['home_name']} {h}-{a} {match['away_name']} | Tahmin: {market} {pick} (Oran: {odds})")
|
||||
else:
|
||||
main_pick = package_data.get("main_pick", {})
|
||||
reasons = main_pick.get("reasons", ["Bilinmeyen Neden"]) if main_pick else ["No main pick"]
|
||||
reason = " | ".join(reasons) if isinstance(reasons, list) else str(reasons)
|
||||
|
||||
market_board = package_data.get("market_board", {})
|
||||
main_pick_market = main_pick.get('market', 'N/A') if main_pick else 'N/A'
|
||||
main_pick_pick = main_pick.get('pick', 'N/A') if main_pick else 'N/A'
|
||||
print(f"[PAS] {match['home_name']} {match['score_home']}-{match['score_away']} {match['away_name']} | Reddedilen: {main_pick_market} {main_pick_pick} -> Neden: {reason}")
|
||||
if "market_passed_all_gates" in reason:
|
||||
print(f" DEBUG: bet_advice = {bet_advice}")
|
||||
|
||||
v25_ms = market_board.get("MS", {}).get("probs", {})
|
||||
v27_ms = {} # V27 is merged into V25 probabilities in market_board, or we don't have separate V27 access here
|
||||
|
||||
# Skora göre ms kontrolü
|
||||
h = match['score_home']
|
||||
a = match['score_away']
|
||||
actual_ms = "1" if h > a else ("X" if h == a else "2")
|
||||
|
||||
v25_top = max(v25_ms, key=v25_ms.get) if v25_ms else "N/A"
|
||||
v27_top = "N/A"
|
||||
|
||||
rejected_market = main_pick.get("market", "N/A") if main_pick else "N/A"
|
||||
rejected_pick = main_pick.get("pick", "N/A") if main_pick else "N/A"
|
||||
|
||||
print(f"[PAS] {match['home_name']} {h}-{a} {match['away_name']} | Reddedilen: {rejected_market} {rejected_pick} -> Neden: {reason}")
|
||||
print(f" [V25 MS Raw: {v25_top}] [Gerçek MS: {actual_ms}]")
|
||||
|
||||
# Sonuç Raporu
|
||||
print("\n" + "=" * 60)
|
||||
print(f"📊 BACKTEST SONUÇLARI ({target_date})")
|
||||
print("=" * 60)
|
||||
print(f"Toplam Maç Sayısı : {len(matches)}")
|
||||
print(f"Oynanan Bahis Sayısı: {bets_placed} (Oynama Oranı: %{bets_placed/len(matches)*100:.1f})")
|
||||
print(f"Riskli Bulunup Pas Geçilen: {len(matches) - bets_placed}")
|
||||
|
||||
if bets_placed > 0:
|
||||
win_rate = won / bets_placed * 100
|
||||
roi = ((total_odds_won - bets_placed) / bets_placed) * 100
|
||||
print(f"Kazanılan : {won}")
|
||||
print(f"Kaybedilen : {lost}")
|
||||
print(f"İsabet Oranı : %{win_rate:.1f}")
|
||||
print(f"Net Kar (ROI) : %{roi:.1f} {'📈' if roi > 0 else '📉'}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_backtest("2026-05-03")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user