Change to spaces for indentation

This commit is contained in:
Daan Koning
2023-01-22 22:15:58 +01:00
parent 94e9e2d7a3
commit d03391c271
2 changed files with 98 additions and 98 deletions

100
logic.py
View File

@@ -6,74 +6,74 @@ BASE_URL = "https://api.the-odds-api.com/v4"
class AuthenticationException(RuntimeError): class AuthenticationException(RuntimeError):
pass pass
class RateLimitException(RuntimeError): class RateLimitException(RuntimeError):
pass pass
def handle_faulty_response(response: requests.Response): def handle_faulty_response(response: requests.Response):
if response.status_code == 401: if response.status_code == 401:
raise AuthenticationException("Failed to authenticate with the API. is the API key valid?") raise AuthenticationException("Failed to authenticate with the API. is the API key valid?")
elif response.status_code == 429: elif response.status_code == 429:
raise RateLimitException("Encountered API rate limit.") raise RateLimitException("Encountered API rate limit.")
def get_sports(key: str) -> set[str]: def get_sports(key: str) -> set[str]:
url = f"{BASE_URL}/sports/" url = f"{BASE_URL}/sports/"
querystring = {"apiKey": key} querystring = {"apiKey": key}
response = requests.get(url, params=querystring) response = requests.get(url, params=querystring)
if not response: if not response:
handle_faulty_response(response) handle_faulty_response(response)
return {item["group"] for item in response.json()} return {item["group"] for item in response.json()}
def get_data(key: str, sport: str, region: str = "eu"): def get_data(key: str, sport: str, region: str = "eu"):
url = f"{BASE_URL}/sports/{sport}/odds/" url = f"{BASE_URL}/sports/{sport}/odds/"
querystring = { querystring = {
"apiKey": key, "apiKey": key,
"regions": region, "regions": region,
"oddsFormat": "decimal", "oddsFormat": "decimal",
"dateFormat": "unix" "dateFormat": "unix"
} }
response = requests.get(url, params=querystring) response = requests.get(url, params=querystring)
if not response: if not response:
handle_faulty_response(response) handle_faulty_response(response)
return response.json() return response.json()
def process_data(matches: Iterable, include_started_matches: bool = True) -> Generator[dict, None, None]: def process_data(matches: Iterable, include_started_matches: bool = True) -> Generator[dict, None, None]:
"""Extracts all matches that are available and calculates some things about them, such as the time to start and """Extracts all matches that are available and calculates some things about them, such as the time to start and
the best available implied odds.""" the best available implied odds."""
for match in matches: for match in matches:
start_time = int(match["commence_time"]) start_time = int(match["commence_time"])
if not include_started_matches and start_time < time.time(): if not include_started_matches and start_time < time.time():
continue continue
best_odd_per_outcome = {} best_odd_per_outcome = {}
for bookmaker in match["bookmakers"]: for bookmaker in match["bookmakers"]:
bookie_name = bookmaker["title"] bookie_name = bookmaker["title"]
for outcome in bookmaker["markets"][0]["outcomes"]: for outcome in bookmaker["markets"][0]["outcomes"]:
outcome_name = outcome["name"] outcome_name = outcome["name"]
odd = outcome["price"] odd = outcome["price"]
if outcome_name not in best_odd_per_outcome.keys() or \ if outcome_name not in best_odd_per_outcome.keys() or \
odd > best_odd_per_outcome[outcome_name][1]: odd > best_odd_per_outcome[outcome_name][1]:
best_odd_per_outcome[outcome_name] = (bookie_name, odd) best_odd_per_outcome[outcome_name] = (bookie_name, odd)
total_implied_odds = sum(1/i[1] for i in best_odd_per_outcome.values()) total_implied_odds = sum(1/i[1] for i in best_odd_per_outcome.values())
match_name = f"{match['home_team']} v. {match['away_team']}" match_name = f"{match['home_team']} v. {match['away_team']}"
time_to_start = (start_time - time.time())/3600 time_to_start = (start_time - time.time())/3600
league = match["sport_key"] league = match["sport_key"]
yield { yield {
"match_name": match_name, "match_name": match_name,
"match_start_time": start_time, "match_start_time": start_time,
"hours_to_start": time_to_start, "hours_to_start": time_to_start,
"league": league, "league": league,
"best_outcome_odds": best_odd_per_outcome, "best_outcome_odds": best_odd_per_outcome,
"total_implied_odds": total_implied_odds, "total_implied_odds": total_implied_odds,
} }

96
main.py
View File

@@ -11,60 +11,60 @@ from rich import print
def main(): def main():
load_dotenv() load_dotenv()
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
prog="Arbitrage Finder", prog="Arbitrage Finder",
description=__doc__ description=__doc__
)
parser.add_argument(
"-k", "--key",
default=os.environ.get("API_KEY"),
help="The API key from The Odds API. If left blank it will default to the value of $API_KEY."
)
parser.add_argument(
"-r", "--region",
choices=["eu", "us", "au", "uk"],
default="eu",
help="The region in which to look for arbitrage opportunities."
)
parser.add_argument(
"-u", "--unformatted",
action="store_true",
help="If set, turn output into the json dump from the opportunities."
)
parser.add_argument(
"-c", "--cutoff",
type=float,
default=0,
help="The minimum profit margin required for an arb to be displayed. Inputted as a percentage."
) )
args = parser.parse_args() parser.add_argument(
"-k", "--key",
default=os.environ.get("API_KEY"),
help="The API key from The Odds API. If left blank it will default to the value of $API_KEY."
)
parser.add_argument(
"-r", "--region",
choices=["eu", "us", "au", "uk"],
default="eu",
help="The region in which to look for arbitrage opportunities."
)
parser.add_argument(
"-u", "--unformatted",
action="store_true",
help="If set, turn output into the json dump from the opportunities."
)
parser.add_argument(
"-c", "--cutoff",
type=float,
default=0,
help="The minimum profit margin required for an arb to be displayed. Inputted as a percentage."
)
args = parser.parse_args()
key = args.key key = args.key
region = args.region region = args.region
print_unformatted = args.unformatted print_unformatted = args.unformatted
cutoff = args.cutoff/100 cutoff = args.cutoff/100
# logic # logic
sports = get_sports(key) sports = get_sports(key)
data = chain.from_iterable(get_data(key, sport, region=region) for sport in sports) data = chain.from_iterable(get_data(key, sport, region=region) for sport in sports)
data = filter(lambda x: x != "message", data) data = filter(lambda x: x != "message", data)
results = process_data(data) results = process_data(data)
arbitrage_opportunities = filter(lambda x: x["total_implied_odds"] < 1-cutoff, results) arbitrage_opportunities = filter(lambda x: x["total_implied_odds"] < 1-cutoff, results)
if print_unformatted: if print_unformatted:
print(arbitrage_opportunities) print(arbitrage_opportunities)
else: else:
arbitrage_opportunities = list(arbitrage_opportunities) arbitrage_opportunities = list(arbitrage_opportunities)
print(f"{len(arbitrage_opportunities)} arbitrage opportunities found {':money-mouth_face:' if len(arbitrage_opportunities) > 0 else ':man_shrugging:'}") print(f"{len(arbitrage_opportunities)} arbitrage opportunities found {':money-mouth_face:' if len(arbitrage_opportunities) > 0 else ':man_shrugging:'}")
for arb in arbitrage_opportunities: for arb in arbitrage_opportunities:
print(f"\t[italic]{arb['match_name']} in {arb['league']} [/italic]") print(f"\t[italic]{arb['match_name']} in {arb['league']} [/italic]")
print(f"\t\tTotal implied odds: {arb['total_implied_odds']} with these odds:") print(f"\t\tTotal implied odds: {arb['total_implied_odds']} with these odds:")
for key, value in arb['best_outcome_odds'].items(): for key, value in arb['best_outcome_odds'].items():
print(f"\t\t[bold red]{key}[/bold red] with [green]{value[0]}[/green] for {value[1]}") print(f"\t\t[bold red]{key}[/bold red] with [green]{value[0]}[/green] for {value[1]}")
if __name__ == '__main__': if __name__ == '__main__':
main() main()