Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
namibsun
python
BetBot
Commits
e29fa291
Commit
e29fa291
authored
Sep 14, 2020
by
Hermann Krumrey
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into 'master'
Develop See merge request
!4
parents
168d902e
91133e09
Changes
33
Hide whitespace changes
Inline
Side-by-side
Showing
33 changed files
with
1645 additions
and
7 deletions
+1645
-7
.gitlab-ci.yml
.gitlab-ci.yml
+1
-1
CHANGELOG
CHANGELOG
+2
-0
Dockerfile
Dockerfile
+1
-0
betbot/api/ApiConnection.py
betbot/api/ApiConnection.py
+2
-0
betbot/api/Bet.py
betbot/api/Bet.py
+6
-0
betbot/api/Match.py
betbot/api/Match.py
+6
-2
betbot/neural/__init__.py
betbot/neural/__init__.py
+18
-0
betbot/neural/data/__init__.py
betbot/neural/data/__init__.py
+18
-0
betbot/neural/data/oddsportal/__init__.py
betbot/neural/data/oddsportal/__init__.py
+0
-0
betbot/neural/data/oddsportal/prototype.py
betbot/neural/data/oddsportal/prototype.py
+17
-0
betbot/neural/data/openligadb/HistoryRecord.py
betbot/neural/data/openligadb/HistoryRecord.py
+125
-0
betbot/neural/data/openligadb/InputVector.py
betbot/neural/data/openligadb/InputVector.py
+88
-0
betbot/neural/data/openligadb/Match.py
betbot/neural/data/openligadb/Match.py
+62
-0
betbot/neural/data/openligadb/OutputVector.py
betbot/neural/data/openligadb/OutputVector.py
+39
-0
betbot/neural/data/openligadb/TableEntry.py
betbot/neural/data/openligadb/TableEntry.py
+101
-0
betbot/neural/data/openligadb/Team.py
betbot/neural/data/openligadb/Team.py
+58
-0
betbot/neural/data/openligadb/__init__.py
betbot/neural/data/openligadb/__init__.py
+18
-0
betbot/neural/data/openligadb/processing.py
betbot/neural/data/openligadb/processing.py
+217
-0
betbot/neural/keras/BetOddsTrainer.py
betbot/neural/keras/BetOddsTrainer.py
+138
-0
betbot/neural/keras/BetPredictorTrainer.py
betbot/neural/keras/BetPredictorTrainer.py
+63
-0
betbot/neural/keras/TableHistoryTrainer.py
betbot/neural/keras/TableHistoryTrainer.py
+66
-0
betbot/neural/keras/Trainer.py
betbot/neural/keras/Trainer.py
+210
-0
betbot/neural/keras/__init__.py
betbot/neural/keras/__init__.py
+18
-0
betbot/prediction/Predictor.py
betbot/prediction/Predictor.py
+12
-0
betbot/prediction/TableHistoryNNPredictor.py
betbot/prediction/TableHistoryNNPredictor.py
+107
-0
betbot/prediction/__init__.py
betbot/prediction/__init__.py
+3
-1
betbot/test/TestOpenligaDbVectors.py
betbot/test/TestOpenligaDbVectors.py
+74
-0
betbot/test/__init__.py
betbot/test/__init__.py
+18
-0
bin/load-openligadb
bin/load-openligadb
+24
-0
bin/train-betbot.py
bin/train-betbot.py
+101
-0
resources/data/Potential.md
resources/data/Potential.md
+28
-1
setup.py
setup.py
+3
-1
version
version
+1
-1
No files found.
.gitlab-ci.yml
View file @
e29fa291
...
...
@@ -6,7 +6,7 @@ stages:
-
release
default
:
image
:
namboy94/ci-docker-environment:0.
9
.0
image
:
namboy94/ci-docker-environment:0.
10
.0
before_script
:
-
echo "$SERVER_ACCESS_KEY" > ~/.ssh/id_rsa
-
chmod 0600 ~/.ssh/id_rsa
...
...
CHANGELOG
View file @
e29fa291
V 0.4.0:
- Added betting bot that uses a neural network to predict matches
V 0.3.0:
- Added Random Betting
V 0.2.0:
...
...
Dockerfile
View file @
e29fa291
...
...
@@ -4,6 +4,7 @@ MAINTAINER Hermann Krumrey <hermann@krumreyh.com>
ENV
DEBIAN_FRONTEND=noninteractive
RUN
apt update
&&
apt
install
-y
python3 python3-pip
RUN
pip3
install
tensorflow keras
ADD
. bot
RUN
cd
bot
&&
python3 setup.py
install
...
...
betbot/api/ApiConnection.py
View file @
e29fa291
...
...
@@ -100,6 +100,8 @@ class ApiConnection:
bet_dict
=
{}
for
bet
in
bets
:
bet_dict
.
update
(
bet
.
to_dict
())
self
.
logger
.
info
(
f
"Generated bet:
{
bet
}
"
)
data
=
self
.
execute_api_call
(
"bet"
,
"PUT"
,
True
,
bet_dict
)
if
data
[
"status"
]
!=
"ok"
:
self
.
logger
.
error
(
"Failed to place bets"
)
...
...
betbot/api/Bet.py
View file @
e29fa291
...
...
@@ -44,3 +44,9 @@ class Bet:
f
"
{
self
.
match_id
}
-home"
:
self
.
home_score
,
f
"
{
self
.
match_id
}
-away"
:
self
.
away_score
}
def
__str__
(
self
)
->
str
:
"""
:return: A string representation of the bet
"""
return
f
"[
{
self
.
match_id
}
]
{
self
.
home_score
}
:
{
self
.
away_score
}
"
betbot/api/Match.py
View file @
e29fa291
...
...
@@ -30,7 +30,8 @@ class Match:
_id
:
int
,
matchday
:
int
,
home_team
:
str
,
away_team
:
str
away_team
:
str
,
finished
:
bool
):
"""
Initializes the Match
...
...
@@ -38,11 +39,13 @@ class Match:
:param matchday: The matchday of the match
:param home_team: The name of the home team
:param away_team: The name of the away team
:param finished: Whether the match is already finished or not
"""
self
.
id
=
_id
self
.
matchday
=
matchday
self
.
home_team
=
home_team
self
.
away_team
=
away_team
self
.
finished
=
finished
@
classmethod
def
from_json
(
cls
,
json_data
:
Dict
[
str
,
Any
]):
...
...
@@ -55,5 +58,6 @@ class Match:
json_data
[
"id"
],
json_data
[
"matchday"
],
json_data
[
"home_team"
][
"abbreviation"
],
json_data
[
"away_team"
][
"abbreviation"
]
json_data
[
"away_team"
][
"abbreviation"
],
json_data
[
"finished"
]
)
betbot/neural/__init__.py
0 → 100644
View file @
e29fa291
"""LICENSE
Copyright 2020 Hermann Krumrey <hermann@krumreyh.com>
This file is part of betbot.
betbot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
betbot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with betbot. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
betbot/neural/data/__init__.py
0 → 100644
View file @
e29fa291
"""LICENSE
Copyright 2020 Hermann Krumrey <hermann@krumreyh.com>
This file is part of betbot.
betbot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
betbot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with betbot. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
betbot/neural/data/oddsportal/__init__.py
0 → 100644
View file @
e29fa291
betbot/neural/data/oddsportal/prototype.py
0 → 100644
View file @
e29fa291
import
requests
from
bs4
import
BeautifulSoup
def
load_odds
(
country
:
str
,
league
:
str
,
season
:
int
):
url
=
f
"https://www.oddsportal.com/soccer/"
\
f
"
{
country
}
/
{
league
}
-
{
season
}
-
{
season
+
1
}
/results/"
print
(
url
)
data
=
requests
.
get
(
url
)
soup
=
BeautifulSoup
(
data
.
text
,
"html.parser"
)
for
x
in
soup
.
select
(
".deactivate"
):
print
(
x
.
text
)
if
__name__
==
'__main__'
:
load_odds
(
"germany"
,
"bundesliga"
,
2019
)
betbot/neural/data/openligadb/HistoryRecord.py
0 → 100644
View file @
e29fa291
"""LICENSE
Copyright 2020 Hermann Krumrey <hermann@krumreyh.com>
This file is part of betbot.
betbot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
betbot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with betbot. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
from
typing
import
Dict
,
Tuple
from
betbot.neural.data.openligadb.Team
import
Team
from
betbot.neural.data.openligadb.TableEntry
import
TableEntry
class
HistoryRecord
:
"""
Class that keeps track of a team's recent history
Only keeps track of stats within a league!
"""
def
__init__
(
self
,
team
:
Team
):
"""
Initializes the HistoryRecord object
"""
self
.
team
=
team
self
.
_history
:
Dict
[
int
,
Dict
[
int
,
Tuple
[
int
,
int
,
int
]]]
\
=
{}
def
add_table_entry
(
self
,
entry
:
TableEntry
):
"""
Adds points and goals history data based on a table entry
:param entry: The table entry
:return: None
"""
if
entry
.
season
not
in
self
.
_history
:
self
.
_history
[
entry
.
season
]
=
{}
self
.
_history
[
entry
.
season
][
entry
.
matchday
]
=
(
entry
.
points
,
entry
.
goals_for
,
entry
.
goals_against
)
def
get_stats
(
self
,
season
:
int
,
matchday
:
int
,
previous
:
bool
)
\
->
Tuple
[
int
,
int
,
int
]:
"""
Retrieves stats for a season and matchday for this team's history
If no history is found, 0 values will be returned
:param season: The season
:param matchday: The matchday
:param previous: Whether or not to use the previous matchday
This is useful if a match is already finished
:return: The points, goals for and goals against
"""
if
previous
:
if
matchday
==
1
:
if
(
season
-
1
)
not
in
self
.
_history
:
return
0
,
0
,
0
else
:
season
-=
1
matchday
=
max
(
self
.
_history
[
season
].
keys
())
else
:
matchday
-=
1
stats
=
self
.
_history
.
get
(
season
,
{}).
get
(
matchday
)
return
stats
if
stats
is
not
None
else
(
0
,
0
,
0
)
def
get_stats_interval
(
self
,
season
:
int
,
matchday
:
int
,
interval
:
int
,
finished
:
bool
)
->
Tuple
[
int
,
int
,
int
]:
"""
Retrieves stats for an interval
:param season: The season
:param matchday: The 'anchor' matchday
:param interval: The size of the interval
:param finished: If the match was finished or not
:return: The points and goals during the interval
"""
# To actually include the first entry in the interval
interval
+=
1
points
,
goals_for
,
goals_against
=
\
self
.
get_stats
(
season
,
matchday
,
finished
)
if
matchday
-
interval
<=
0
and
(
season
-
1
)
in
self
.
_history
:
max_matchday
=
max
(
self
.
_history
[
season
-
1
].
keys
())
start_matchday
=
max_matchday
-
(
interval
-
matchday
)
start_points
,
start_goals_for
,
start_goals_against
=
\
self
.
get_stats
(
season
-
1
,
start_matchday
,
False
)
end_points
,
end_goals_for
,
end_goals_against
=
\
self
.
get_stats
(
season
-
1
,
max_matchday
,
False
)
points
+=
(
end_points
-
start_points
)
goals_for
+=
(
end_goals_for
-
start_goals_for
)
goals_against
+=
(
end_goals_against
-
start_goals_against
)
elif
matchday
==
1
:
return
0
,
0
,
0
elif
matchday
-
interval
<=
0
:
pass
# In the first season, simply use current stats
else
:
start_points
,
start_goals_for
,
start_goals_against
=
\
self
.
get_stats
(
season
,
matchday
-
interval
,
False
)
points
-=
start_points
goals_for
-=
start_goals_for
goals_against
-=
start_goals_against
return
points
,
goals_for
,
goals_against
def
get_max_matchday
(
self
,
season
:
int
):
return
max
(
self
.
_history
[
season
].
keys
())
betbot/neural/data/openligadb/InputVector.py
0 → 100644
View file @
e29fa291
"""LICENSE
Copyright 2020 Hermann Krumrey <hermann@krumreyh.com>
This file is part of betbot.
betbot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
betbot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with betbot. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
from
typing
import
List
,
Union
from
betbot.neural.data.openligadb.HistoryRecord
import
HistoryRecord
class
InputVector
:
"""
Class that models an input vector to be used in training for a neural
network that predicts scores.
"""
def
__init__
(
self
,
season
:
int
,
matchday
:
int
,
finished
:
bool
,
home_team_history
:
HistoryRecord
,
away_team_history
:
HistoryRecord
):
"""
Initializes the InputVector
:param season: The season of the input
:param matchday: The match day
:param finished: Whether or not the match has finished
:param home_team_history: The table history of the home team
:param away_team_history: The table history of the away team
"""
self
.
season
=
season
self
.
matchday
=
matchday
self
.
home_team_history
=
home_team_history
self
.
away_team_history
=
away_team_history
self
.
current_stats
=
\
list
(
home_team_history
.
get_stats
(
season
,
matchday
,
finished
))
+
\
list
(
away_team_history
.
get_stats
(
season
,
matchday
,
finished
))
self
.
history_stats
=
{}
for
interval
in
[
5
]:
self
.
history_stats
[
interval
]
=
\
list
(
home_team_history
.
get_stats_interval
(
season
,
matchday
,
interval
,
finished
))
+
\
list
(
away_team_history
.
get_stats_interval
(
season
,
matchday
,
interval
,
finished
))
@
property
def
vector
(
self
)
->
List
[
int
]:
"""
:return: The input vector as a list of integers
"""
vector
=
self
.
current_stats
for
key
in
sorted
(
self
.
history_stats
.
keys
()):
vector
+=
self
.
history_stats
[
key
]
return
vector
def
normalize
(
self
,
vector
:
List
[
Union
[
int
,
float
]])
->
List
[
float
]:
"""
Normalizes the input vector to values between 0 and 1
:param vector: The vector to normalize
:return: The normalized vector
"""
vector
=
list
(
vector
)
# copy
for
overall_index
in
range
(
0
,
6
):
vector
[
overall_index
]
=
\
min
(
1.0
,
vector
[
overall_index
]
/
(
self
.
matchday
*
3
))
for
history_index
in
range
(
6
,
len
(
vector
)):
vector
[
history_index
]
=
\
min
(
1.0
,
vector
[
history_index
]
/
(
5
*
3
))
return
vector
betbot/neural/data/openligadb/Match.py
0 → 100644
View file @
e29fa291
"""LICENSE
Copyright 2020 Hermann Krumrey <hermann@krumreyh.com>
This file is part of betbot.
betbot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
betbot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with betbot. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
from
typing
import
Dict
,
Any
from
betbot.neural.data.openligadb.Team
import
Team
class
Match
:
"""
Class that models an OpenligaDB match
"""
def
__init__
(
self
,
league
:
str
,
season
:
int
,
match_json
:
Dict
[
str
,
Any
],
teams
:
Dict
[
int
,
Team
]
):
"""
Initializes the match object
:param league: The league of the match
:param season: The season of the match
:param match_json: The OpenligaDB JSON data for the match
:param teams: The teams mapped to their IDs
"""
finished
=
match_json
[
"MatchIsFinished"
]
if
finished
:
results
=
match_json
[
"MatchResults"
]
if
results
[
0
][
"ResultName"
]
==
"Endergebnis"
:
result
=
results
[
0
]
else
:
result
=
results
[
1
]
home_score
=
result
[
"PointsTeam1"
]
away_score
=
result
[
"PointsTeam2"
]
else
:
home_score
,
away_score
=
None
,
None
self
.
league
=
league
self
.
season
=
season
self
.
matchday
=
match_json
[
"Group"
][
"GroupOrderID"
]
self
.
home_team
=
teams
[
match_json
[
"Team1"
][
"TeamId"
]]
self
.
away_team
=
teams
[
match_json
[
"Team2"
][
"TeamId"
]]
self
.
home_score
=
home_score
self
.
away_score
=
away_score
self
.
finished
=
finished
betbot/neural/data/openligadb/OutputVector.py
0 → 100644
View file @
e29fa291
"""LICENSE
Copyright 2020 Hermann Krumrey <hermann@krumreyh.com>
This file is part of betbot.
betbot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
betbot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with betbot. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
from
typing
import
List
class
OutputVector
:
"""
Class that models the output vector for a neural network training dataset
"""
def
__init__
(
self
,
home_score
:
int
,
away_score
:
int
):
"""
Initializes the OutputVector object
:param home_score: The home score
:param away_score: The away score
"""
self
.
home_score
=
home_score
self
.
away_score
=
away_score
@
property
def
vector
(
self
)
->
List
[
int
]:
return
[
self
.
home_score
,
self
.
away_score
]
betbot/neural/data/openligadb/TableEntry.py
0 → 100644
View file @
e29fa291
"""LICENSE
Copyright 2020 Hermann Krumrey <hermann@krumreyh.com>
This file is part of betbot.
betbot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
betbot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with betbot. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
from
betbot.neural.data.openligadb.Team
import
Team
from
betbot.neural.data.openligadb.Match
import
Match
class
TableEntry
:
"""
Class that models a league table entry for one club
"""
def
__init__
(
self
,
league
:
str
,
season
:
int
,
matchday
:
int
,
team
:
Team
,
points
:
int
,
goals_for
:
int
,
goals_against
:
int
):
"""
Initializes the TableEntry object
:param league: The league of the entry
:param season: The season of the entry
:param matchday: The matchday of the entry
:param team: The team for which this is an entry
:param points: The points of the team
:param goals_for: The goals the team scored
:param goals_against: The goals the team conceded
"""
self
.
league
=
league
self
.
season
=
season
self
.
matchday
=
matchday
self
.
team
=
team
self
.
points
=
points
self
.
goals_for
=
goals_for
self
.
goals_against
=
goals_against
def
merge
(
self
,
entry
:
"TableEntry"
):
"""
Merges another table entry's points and goals data into this one
:param entry: The entry to merge into this one
:return: Nonew
"""
self
.
points
+=
entry
.
points
self
.
goals_for
+=
entry
.
goals_for
self
.
goals_against
+=
entry
.
goals_against
@
classmethod
def
from_match
(
cls
,
match
:
Match
,
team
:
Team
)
->
"TableEntry"
:
"""
Generates a table entry based on a match
:param match: The match
:param team: The team for which to generate the entry
:return: The table entry
"""
is_home_team
=
team
.
id
==
match
.
home_team
.
id
if
is_home_team
:
score_for
,
score_against
=
match
.
home_score
,
match
.
away_score
else
:
score_for
,
score_against
=
match
.
away_score
,
match
.
home_score
if
not
match
.
finished
:
points
=
0
score_for
=
0
score_against
=
0
elif
score_for
>
score_against
:
points
=
3
elif
score_for
==
score_against
:
points
=
1
else
:
points
=
0
return
cls
(
match
.
league
,
match
.
season
,
match
.
matchday
,
team
,
points
,
score_for
,
score_against
)
betbot/neural/data/openligadb/Team.py
0 → 100644
View file @
e29fa291
"""LICENSE
Copyright 2020 Hermann Krumrey <hermann@krumreyh.com>
This file is part of betbot.
betbot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
betbot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with betbot. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
from
typing
import
Dict
,
Any
class
Team
:
"""
Class that models an OpenLigaDB Team
"""
def
__init__
(
self
,
team_json
:
Dict
[
str
,
Any
]):
"""
Initializes the team object
:param team_json: The OpenLigaDB JSON for the team
"""
self
.
id
=
team_json
[
"TeamId"
]
self
.
team_name
=
team_json
[
"TeamName"
]
self
.
abbreviation
=
{
"1. FC Nürnberg"
:
"FCN"
,
"1. FSV Mainz 05"
:
"M05"
,
"Bayer Leverkusen"
:
"B04"
,
"Borussia Dortmund"
:
"BVB"
,
"Borussia Mönchengladbach"
:
"BMG"
,
"Eintracht Frankfurt"
:
"SGE"
,
"FC Augsburg"
:
"FCA"
,
"FC Bayern"
:
"FCB"
,
"FC Schalke 04"
:
"S04"
,
"Fortuna Düsseldorf"
:
"F95"
,
"Hannover 96"
:
"H96"
,