Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
MBetterc
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Mbetter
MBetterc
Commits
a4fd9ee8
Commit
a4fd9ee8
authored
Dec 23, 2025
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Resitribution daily correction
parent
3b50b307
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
147 additions
and
4 deletions
+147
-4
games_thread.py
mbetterclient/core/games_thread.py
+73
-3
migrations.py
mbetterclient/database/migrations.py
+55
-0
models.py
mbetterclient/database/models.py
+19
-1
No files found.
mbetterclient/core/games_thread.py
View file @
a4fd9ee8
...
@@ -12,7 +12,7 @@ from typing import Optional, Dict, Any, List
...
@@ -12,7 +12,7 @@ from typing import Optional, Dict, Any, List
from
.thread_manager
import
ThreadedComponent
from
.thread_manager
import
ThreadedComponent
from
.message_bus
import
MessageBus
,
Message
,
MessageType
,
MessageBuilder
from
.message_bus
import
MessageBus
,
Message
,
MessageType
,
MessageBuilder
from
..database.manager
import
DatabaseManager
from
..database.manager
import
DatabaseManager
from
..database.models
import
MatchModel
,
MatchStatus
,
BetDetailModel
,
MatchOutcomeModel
,
GameConfigModel
,
ExtractionAssociationModel
from
..database.models
import
MatchModel
,
MatchStatus
,
BetDetailModel
,
MatchOutcomeModel
,
GameConfigModel
,
ExtractionAssociationModel
,
DailyRedistributionShortfallModel
logger
=
logging
.
getLogger
(
__name__
)
logger
=
logging
.
getLogger
(
__name__
)
...
@@ -1880,6 +1880,62 @@ class GamesThread(ThreadedComponent):
...
@@ -1880,6 +1880,62 @@ class GamesThread(ThreadedComponent):
logger
.
error
(
f
"Failed to get redistribution CAP: {e}"
)
logger
.
error
(
f
"Failed to get redistribution CAP: {e}"
)
return
70.0
return
70.0
def
_get_daily_shortfall
(
self
,
date
,
session
)
->
float
:
"""Get accumulated shortfall for a specific date"""
try
:
shortfall_record
=
session
.
query
(
DailyRedistributionShortfallModel
)
.
filter_by
(
date
=
date
)
.
first
()
if
shortfall_record
:
logger
.
debug
(
f
"Found shortfall record for {date}: {shortfall_record.accumulated_shortfall}"
)
return
shortfall_record
.
accumulated_shortfall
else
:
logger
.
debug
(
f
"No shortfall record found for {date}, returning 0.0"
)
return
0.0
except
Exception
as
e
:
logger
.
error
(
f
"Failed to get daily shortfall for {date}: {e}"
)
return
0.0
def
_update_daily_shortfall
(
self
,
date
,
payin_amount
,
redistributed_amount
,
cap_percentage
,
session
):
"""Update daily shortfall tracking after extraction"""
try
:
# Calculate the shortfall for this extraction
expected_redistribution
=
payin_amount
*
(
cap_percentage
/
100.0
)
shortfall
=
max
(
0
,
expected_redistribution
-
redistributed_amount
)
logger
.
info
(
f
"💰 [SHORTFALL DEBUG] Payin: {payin_amount:.2f}, Expected: {expected_redistribution:.2f}, Redistributed: {redistributed_amount:.2f}, Shortfall: {shortfall:.2f}"
)
# Get or create the daily record
shortfall_record
=
session
.
query
(
DailyRedistributionShortfallModel
)
.
filter_by
(
date
=
date
)
.
first
()
if
not
shortfall_record
:
shortfall_record
=
DailyRedistributionShortfallModel
(
date
=
date
,
accumulated_shortfall
=
shortfall
,
total_payin
=
payin_amount
,
total_redistributed
=
redistributed_amount
,
cap_percentage
=
cap_percentage
)
session
.
add
(
shortfall_record
)
logger
.
info
(
f
"Created new shortfall record for {date} with shortfall {shortfall:.2f}"
)
else
:
# Update existing record
shortfall_record
.
accumulated_shortfall
+=
shortfall
shortfall_record
.
total_payin
+=
payin_amount
shortfall_record
.
total_redistributed
+=
redistributed_amount
shortfall_record
.
cap_percentage
=
cap_percentage
# Update to latest
logger
.
info
(
f
"Updated shortfall record for {date}, new accumulated shortfall: {shortfall_record.accumulated_shortfall:.2f}"
)
session
.
commit
()
except
Exception
as
e
:
logger
.
error
(
f
"Failed to update daily shortfall for {date}: {e}"
)
session
.
rollback
()
def
_weighted_random_selection
(
self
,
under_coeff
:
float
,
over_coeff
:
float
)
->
str
:
def
_weighted_random_selection
(
self
,
under_coeff
:
float
,
over_coeff
:
float
)
->
str
:
"""Weighted random selection based on inverse coefficients"""
"""Weighted random selection based on inverse coefficients"""
try
:
try
:
...
@@ -2308,8 +2364,17 @@ class GamesThread(ThreadedComponent):
...
@@ -2308,8 +2364,17 @@ class GamesThread(ThreadedComponent):
# Step 5: Get redistribution CAP
# Step 5: Get redistribution CAP
logger
.
info
(
f
"🎯 [EXTRACTION DEBUG] Step 5: Retrieving redistribution CAP"
)
logger
.
info
(
f
"🎯 [EXTRACTION DEBUG] Step 5: Retrieving redistribution CAP"
)
cap_percentage
=
self
.
_get_redistribution_cap
()
cap_percentage
=
self
.
_get_redistribution_cap
()
cap_threshold
=
total_bet_amount
*
(
cap_percentage
/
100.0
)
logger
.
info
(
f
"🎯 [EXTRACTION DEBUG] CAP percentage: {cap_percentage}
%
, threshold: {cap_threshold:.2f}"
)
# Step 5.1: Get accumulated shortfall from previous extractions for today
logger
.
info
(
f
"🎯 [EXTRACTION DEBUG] Step 5.1: Retrieving accumulated shortfall for today"
)
today
=
datetime
.
now
()
.
date
()
accumulated_shortfall
=
self
.
_get_daily_shortfall
(
today
,
session
)
logger
.
info
(
f
"🎯 [EXTRACTION DEBUG] Accumulated shortfall: {accumulated_shortfall:.2f}"
)
# Step 5.2: Calculate CAP threshold including shortfall
base_cap_threshold
=
total_bet_amount
*
(
cap_percentage
/
100.0
)
cap_threshold
=
base_cap_threshold
+
accumulated_shortfall
logger
.
info
(
f
"🎯 [EXTRACTION DEBUG] CAP percentage: {cap_percentage}
%
, base threshold: {base_cap_threshold:.2f}, shortfall: {accumulated_shortfall:.2f}, final threshold: {cap_threshold:.2f}"
)
logger
.
info
(
f
"📊 [EXTRACTION DEBUG] Extraction summary - {len(payouts)} results, total_bet_amount={total_bet_amount:.2f}, CAP={cap_percentage}
%
, threshold={cap_threshold:.2f}"
)
logger
.
info
(
f
"📊 [EXTRACTION DEBUG] Extraction summary - {len(payouts)} results, total_bet_amount={total_bet_amount:.2f}, CAP={cap_percentage}
%
, threshold={cap_threshold:.2f}"
)
logger
.
info
(
f
"📊 [EXTRACTION DEBUG] Payouts: {payouts}"
)
logger
.
info
(
f
"📊 [EXTRACTION DEBUG] Payouts: {payouts}"
)
...
@@ -2345,6 +2410,11 @@ class GamesThread(ThreadedComponent):
...
@@ -2345,6 +2410,11 @@ class GamesThread(ThreadedComponent):
logger
.
info
(
f
"📈 [EXTRACTION DEBUG] Step 9: Collecting match statistics"
)
logger
.
info
(
f
"📈 [EXTRACTION DEBUG] Step 9: Collecting match statistics"
)
self
.
_collect_match_statistics
(
match_id
,
fixture_id
,
selected_result
,
session
)
self
.
_collect_match_statistics
(
match_id
,
fixture_id
,
selected_result
,
session
)
# Step 10: Update daily shortfall tracking
logger
.
info
(
f
"💰 [EXTRACTION DEBUG] Step 10: Updating daily shortfall tracking"
)
today
=
datetime
.
now
()
.
date
()
self
.
_update_daily_shortfall
(
today
,
total_bet_amount
,
payouts
[
selected_result
],
cap_percentage
,
session
)
logger
.
info
(
f
"✅ [EXTRACTION DEBUG] Result extraction completed successfully: selected {selected_result}"
)
logger
.
info
(
f
"✅ [EXTRACTION DEBUG] Result extraction completed successfully: selected {selected_result}"
)
# DEBUG: Final check - ensure result is not None
# DEBUG: Final check - ensure result is not None
...
...
mbetterclient/database/migrations.py
View file @
a4fd9ee8
...
@@ -2677,6 +2677,60 @@ class Migration_034_AddDefaultLicenseText(DatabaseMigration):
...
@@ -2677,6 +2677,60 @@ class Migration_034_AddDefaultLicenseText(DatabaseMigration):
return
False
return
False
class
Migration_035_AddDailyRedistributionShortfallTable
(
DatabaseMigration
):
"""Add daily_redistribution_shortfall table for tracking CAP shortfalls across matches"""
def
__init__
(
self
):
super
()
.
__init__
(
"035"
,
"Add daily_redistribution_shortfall table for tracking CAP shortfalls"
)
def
up
(
self
,
db_manager
)
->
bool
:
"""Create daily_redistribution_shortfall table"""
try
:
with
db_manager
.
engine
.
connect
()
as
conn
:
# Create daily_redistribution_shortfall table
conn
.
execute
(
text
(
"""
CREATE TABLE IF NOT EXISTS daily_redistribution_shortfall (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date DATE NOT NULL UNIQUE,
accumulated_shortfall REAL DEFAULT 0.0 NOT NULL,
total_payin REAL DEFAULT 0.0 NOT NULL,
total_redistributed REAL DEFAULT 0.0 NOT NULL,
cap_percentage REAL DEFAULT 70.0 NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL
)
"""
))
# Create indexes for daily_redistribution_shortfall table
indexes
=
[
"CREATE INDEX IF NOT EXISTS ix_daily_redistribution_shortfall_date ON daily_redistribution_shortfall(date)"
,
]
for
index_sql
in
indexes
:
conn
.
execute
(
text
(
index_sql
))
conn
.
commit
()
logger
.
info
(
"Daily redistribution shortfall table created successfully"
)
return
True
except
Exception
as
e
:
logger
.
error
(
f
"Failed to create daily_redistribution_shortfall table: {e}"
)
return
False
def
down
(
self
,
db_manager
)
->
bool
:
"""Drop daily_redistribution_shortfall table"""
try
:
with
db_manager
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
text
(
"DROP TABLE IF EXISTS daily_redistribution_shortfall"
))
conn
.
commit
()
logger
.
info
(
"Daily redistribution shortfall table dropped"
)
return
True
except
Exception
as
e
:
logger
.
error
(
f
"Failed to drop daily_redistribution_shortfall table: {e}"
)
return
False
# Registry of all migrations in order
# Registry of all migrations in order
# Registry of all migrations in order
# Registry of all migrations in order
...
@@ -2715,6 +2769,7 @@ MIGRATIONS: List[DatabaseMigration] = [
...
@@ -2715,6 +2769,7 @@ MIGRATIONS: List[DatabaseMigration] = [
Migration_032_FixExtractionAssociationDefaults
(),
Migration_032_FixExtractionAssociationDefaults
(),
Migration_033_AddBarcodeFieldsToBets
(),
Migration_033_AddBarcodeFieldsToBets
(),
Migration_034_AddDefaultLicenseText
(),
Migration_034_AddDefaultLicenseText
(),
Migration_035_AddDailyRedistributionShortfallTable
(),
]
]
...
...
mbetterclient/database/models.py
View file @
a4fd9ee8
...
@@ -7,7 +7,7 @@ import hashlib
...
@@ -7,7 +7,7 @@ import hashlib
from
datetime
import
datetime
,
timedelta
,
timezone
from
datetime
import
datetime
,
timedelta
,
timezone
from
typing
import
Dict
,
Any
,
Optional
,
List
from
typing
import
Dict
,
Any
,
Optional
,
List
from
sqlalchemy
import
(
from
sqlalchemy
import
(
Column
,
Integer
,
String
,
Text
,
DateTime
,
Boolean
,
Float
,
Column
,
Integer
,
String
,
Text
,
DateTime
,
Date
,
Boolean
,
Float
,
JSON
,
ForeignKey
,
UniqueConstraint
,
Index
,
create_engine
,
Enum
JSON
,
ForeignKey
,
UniqueConstraint
,
Index
,
create_engine
,
Enum
)
)
from
sqlalchemy.ext.declarative
import
declarative_base
from
sqlalchemy.ext.declarative
import
declarative_base
...
@@ -946,3 +946,21 @@ class ExtractionStatsModel(BaseModel):
...
@@ -946,3 +946,21 @@ class ExtractionStatsModel(BaseModel):
def
__repr__
(
self
):
def
__repr__
(
self
):
return
f
'<ExtractionStats Match {self.match_id}: {self.total_bets} bets, {self.total_amount_collected:.2f} collected, {self.actual_result}>'
return
f
'<ExtractionStats Match {self.match_id}: {self.total_bets} bets, {self.total_amount_collected:.2f} collected, {self.actual_result}>'
class
DailyRedistributionShortfallModel
(
BaseModel
):
"""Daily redistribution shortfall tracking for CAP adjustments"""
__tablename__
=
'daily_redistribution_shortfall'
__table_args__
=
(
Index
(
'ix_daily_redistribution_shortfall_date'
,
'date'
),
UniqueConstraint
(
'date'
,
name
=
'uq_daily_redistribution_shortfall_date'
),
)
date
=
Column
(
Date
,
nullable
=
False
,
unique
=
True
,
comment
=
'Date for shortfall tracking'
)
accumulated_shortfall
=
Column
(
Float
(
precision
=
2
),
default
=
0.0
,
nullable
=
False
,
comment
=
'Accumulated shortfall from previous extractions'
)
total_payin
=
Column
(
Float
(
precision
=
2
),
default
=
0.0
,
nullable
=
False
,
comment
=
'Total payin for the day'
)
total_redistributed
=
Column
(
Float
(
precision
=
2
),
default
=
0.0
,
nullable
=
False
,
comment
=
'Total redistributed for the day'
)
cap_percentage
=
Column
(
Float
(
precision
=
2
),
default
=
70.0
,
nullable
=
False
,
comment
=
'CAP percentage used for calculations'
)
def
__repr__
(
self
):
return
f
'<DailyRedistributionShortfall {self.date}: shortfall={self.accumulated_shortfall:.2f}, payin={self.total_payin:.2f}, redistributed={self.total_redistributed:.2f}>'
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment