COM from Python

Discuss anything related to using the program (eg. triggered betting tactics)

Moderator: 2020vision

COM from Python

Postby GaryRussell » Mon Oct 24, 2022 12:25 pm

For anyone interested It is possible. Here is an example which scans Betfair and Oddschecker to find markets where the bookmaker is offering better odds for the place market than Betfair. It demonstrates using COM with Betting Assistant and web scraping with webdriver. In theory it's possible to make a risk-free profit when this happens. I am not claiming this is true or that this code is 100% accurate and I am only providing it for educational purposes. It is intended as example code only and not a full solution. I developed this in Visual Studio code and it's my first attempt at using Python. I am using Python version 3.10.8

You will need to install a few libraries from the command prompt.
Code: Select all
pip3 install selenium
pip3 install beautifulsoup4
pip3 install lxml
pip3 install tabulate
pip3 install pywin32


You will need to have Google Chrome browser installed and also chromedriver.exe needs to be copied to the same folder as your .py file.
get it here

Python code here:-
Code: Select all
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from datetime import date
from datetime import datetime
from dataclasses import dataclass,field
from typing import Dict
from tabulate import tabulate
import time
import win32com.client

def isTodaysMarket(venue):
    todayStr = date.today().strftime("%d{dMod} %b")
    d = date.today().day
    if d == 1 or d == 21 or d == 31 : dMod = "st"
    elif d == 2 or d == 22: dMod = "nd"
    elif d == 3 or d == 23: dMod = "rd"
    else: dMod = "th"
    todayStr = todayStr.replace("{dMod}",dMod)
    return venue.find(todayStr) != -1

@dataclass
class BAPrice:
    backOdds:float
    layOdds:float

@dataclass
class BAMarket:
    venue: str
    winMarketId: str
    placeMarketId: str
    winPrices: Dict[str, BAPrice] = field(default_factory=dict)
    placePrices: Dict[str, BAPrice] = field(default_factory=dict)

def checkBA(BA):
    baMarkets = {}
    excludeMarkets = ["Each Way","TBP","Match Betting","To Win By Over","Without"]
    sports = BA.getSports
    for sport in sports:
        if sport.sport == "Horse Racing":
            countries = BA.getEvents(sport.sportId)
            for country in countries:
                if country.eventName == "GB" or country.eventName == "IRE":
                    countryCode = country.eventName
                    if countryCode == "GB": countryCode = "UK"
                    venues = BA.getEvents(country.eventId)
                    for venue in venues:
                        if isTodaysMarket(venue.eventName):
                            markets = BA.getEvents(venue.EventId)
                            placeMarketId = 0
                            winMarketId = 0
                            for market in markets:
                                if not any(ele in market.eventName for ele in excludeMarkets):
                                    if market.eventName == "To Be Placed":
                                        placeMarketId = market.eventId
                                    else:
                                        winMarketId = market.eventId
                                    if winMarketId !=0 and placeMarketId !=0:
                                        baMarket = BAMarket(venue.eventName, winMarketId, placeMarketId)
                                        winMarketId = 0
                                        placeMarketId = 0
                                        startTime = market.startTime.strftime("%d%m%y_%H:%M")
                                        #print(f"{baMarket.venue} {startTime} {baMarket.winMarketId} {baMarket.placeMarketId}")
                                        baMarkets[f"{countryCode}_{startTime}"] = baMarket
    return baMarkets
 
def getBAPrices(BA, baMarket):
    # get win prices
    while True:
        baPrices = BA.getPrices
        marketId = ''
        for bfOdds in baPrices:
            bfPrice = BAPrice(float(bfOdds.backOdds1), float(bfOdds.layOdds1))
            baMarket.winPrices[bfOdds.Selection] = bfPrice
            marketId = bfOdds.marketId
        if int(marketId) == baMarket.winMarketId:
            break
        else:
            baMarket.winPrices.clear()
            time.sleep(1)
    # get place prices
    BA.openMarket(baMarket.placeMarketId, 1)
    while True:
        baPrices = BA.getPrices
        marketId = ''
        for bfOdds in baPrices:
            bfPrice = BAPrice(float(bfOdds.backOdds1), float(bfOdds.layOdds1))
            baMarket.placePrices[bfOdds.Selection] = bfPrice
            marketId = bfOdds.marketId
        if int(marketId) == baMarket.placeMarketId:
            break
        else:
            baMarket.placePrices.clear()
            time.sleep(1)

def getBookieNames(soup):
    bookieNames = {}
    bookies = soup.find_all('a', class_ = 'bk-logo-main-90 bk-logo-click')
    for bookie in bookies:
        bookieNames[bookie['data-bk']] = bookie['title']
    return bookieNames

def checkRacingPost(BA,baMarkets):
    options = Options()
    options.headless = True
    options.add_argument("--window-size=1920x1080")
    options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36")
    options.add_experimental_option('excludeSwitches', ['enable-logging'])
    url = 'https://www.oddschecker.com'
    browser = webdriver.Chrome(options=options)
    browser.get(url)
    soup = BeautifulSoup(browser.page_source, 'lxml')
    browser.quit()
    venues = soup.find_all('div', class_ = 'race-details')
    for venue in venues:
        venueDetails = venue.find('div', class_ = 'venue-details')
        country = venueDetails.span.text
        venueName = venueDetails.a.text
        if country == 'UK' or country == 'IRE':
            races = venue.find_all('div', class_ = 'racing-time')
            for race in races:
                raceURL = race.a['href']
                if race.a.has_attr('data-time'):
                    raceTime = race.a['data-time']
                    date1 = datetime.now()
                    date2 = datetime.strptime(raceTime,"%Y-%m-%d %H:%M:%S")
                    diff = date2 - date1
                    if diff.total_seconds() >=300:
                        raceTimeStr = date2.strftime("%d%m%y_%H:%M")
                        baMarketsKey = f"{country}_{raceTimeStr}"
                        if baMarketsKey in baMarkets.keys():
                            baMarket = baMarkets[baMarketsKey]
                            BA.openMarket(baMarket.winMarketId, 1)
                            #print(f"Checking {country} {venueName} {raceURL}")
                            browser = webdriver.Chrome(options=options)
                            browser.get(f"https://www.oddschecker.com{raceURL}")
                            soup1 = BeautifulSoup(browser.page_source, 'lxml')
                            browser.quit()
                            bookieNames = getBookieNames(soup1)
                            # create list of Betfair prices
                            getBAPrices(BA, baMarket)
                            horses = soup1.find_all('tr', class_ = 'diff-row evTabRow bc')
                            # get list of Oddschecer prices
                            for horse in horses:
                                horseName=horse['data-bname']
                                bookies = horse.find_all('td')
                                for bookie in bookies:
                                    if bookie.has_attr('data-bk'):
                                        bookieName = bookieNames[bookie['data-bk']]
                                        odds = float(bookie['data-odig'])
                                        ewDenom = '0'
                                        placeOdds = 0.00
                                        if bookie.has_attr('data-ew-denom'): ewDenom = bookie['data-ew-denom']
                                        if ewDenom != '0':
                                            placeOdds = float(((float(odds) - 1) / int(ewDenom)) + 1)
                                            if horseName in baMarket.winPrices.keys() and horseName in baMarket.placePrices.keys():
                                                bfWinPrice = baMarket.winPrices[horseName]
                                                bfPlacePrice = baMarket.placePrices[horseName]
                                                if bfPlacePrice.layOdds < placeOdds and bfWinPrice.layOdds <= float(odds) and bfPlacePrice.layOdds > 0 and bfWinPrice.layOdds > 0 and int(ewDenom) > 1:
                                                    ifWinBookie = (stake*(odds-1)) + (stake*(placeOdds-1))
                                                    ifPlaceBookie =  (stake*(placeOdds-1)) - stake
                                                    ifLoseBookie = -(stake * 2)
                                                    ifWinBf = -((stake*(bfWinPrice.layOdds-1)) + (stake*(bfPlacePrice.layOdds-1)))
                                                    ifPlaceBf =  -((stake*(bfPlacePrice.layOdds-1))) + stake
                                                    ifLoseBf = stake * 2
                                                    print(tabulate([[f"{raceTime} {venueName}",horseName]],tablefmt="fancy_grid"))
                                                    print(tabulate([[bookieName,odds,placeOdds,ifWinBookie,ifPlaceBookie,ifLoseBookie],
                                                    ['Betfair',bfWinPrice.layOdds,bfPlacePrice.layOdds,ifWinBf,ifPlaceBf,ifLoseBf],
                                                    ['Profit','','',ifWinBookie+ifWinBf,ifPlaceBookie+ifPlaceBf,ifLoseBookie+ifLoseBf]],
                                                    headers=['Bookmaker','Win Odds','Place Odds','If Win','If Place','If Lose'],
                                                    tablefmt="fancy_grid",floatfmt=".2f"),end="\n\n")
                                                    #print(f"{horseName} Bookie:{bookieName} EW denom:{ewDenom} Win odds:{odds} Place Odds:{placeOdds} Betfair Win Odds:{bfWinPrice.layOdds} Betfair Place Odds:{bfPlacePrice.layOdds}")

stake = 50.00
BA = win32com.client.Dispatch('BettingAssistantCom.Application.ComClass')
baMarkets = checkBA(BA)
checkRacingPost(BA,baMarkets)
User avatar
GaryRussell
Site Admin
 
Posts: 9813
Joined: Fri Nov 18, 2005 8:09 pm
Location: Birmingham, UK

Re: COM from Python

Postby Captain Sensible » Tue Oct 25, 2022 12:21 am

Thanks, I never knew we could use the com as a standalone with python. Been a while since I've used python but certainly give it a look when I get the chance.
User avatar
Captain Sensible
 
Posts: 2911
Joined: Sat Nov 19, 2005 2:29 pm


Return to Discussion

Who is online

Users browsing this forum: No registered users and 6 guests

Sports betting software from Gruss Software


The strength of Gruss Software is that it’s been designed by one of you, a frustrated sports punter, and then developed by listening to dozens of like-minded enthusiasts.

Gruss is owned and run by brothers Gary and Mark Russell. Gary discovered Betfair in 2004 and soon realised that using bespoke software to place bets was much more efficient than merely placing them through the website.

Gary built his own software and then enhanced its features after trialling it through other Betfair users and reacting to their improvement ideas, something that still happens today.

He started making a small monthly charge so he could work on it full-time and then recruited Mark to help develop the products and Gruss Software was born.

We think it’s the best of its kind and so do a lot of our customers. But you can never stand still in this game and we’ll continue to improve the software if any more great ideas emerge.