def run(self, username = '', password = '', app_key = '', aus = False): # create the API object self.username = username self.api = API(aus, ssl_prefix = username) self.api.app_key = app_key self.logger = Logger(aus) self.logger.bot_version = __version__ # login to betfair api-ng self.do_login(username, password) while self.session: self.do_throttle() self.keep_alive() #Get and display all events eventsList = self.api.get_event_types() ###user interaction #print(json.dumps(eventsList,sort_keys=True,indent=4,separators=(',',': '))) print('Here are the list of available events: ') for event in eventsList: eventName = event['eventType']['name'] print(eventName) #eg: Soccer eventChoice = input('Please input an option from the above list: ') ###Get Id of selected event eventId = None for event in eventsList: eventName = event['eventType']['name'] eventId = None if (eventName == eventChoice): eventId = event['eventType']['id'] else: continue break ###Display all markets for selected event eventMarkets = self.api.get_market_types({'eventTypeIds':[eventId]}) for market in eventMarkets: print(market["marketType"]) #eg: OVER_UNDER_15 marketChoice = input("Please select from the list of available Markets below: ") print("You selected "+ marketChoice) ###Get market-catalogue for user selected event marketCatalogue = self.getMarketCatalogue(eventId,marketChoice) print("This is the list of runners to bet on in the "+marketChoice+" market:") for runner in marketCatalogue[0]["runners"]: print(runner["runnerName"]) #eg: Under 1.5 goals ###Get market and selection (i.e runner) ids #self.prettyPrint(marketCatalogue) market_id = self.getMarketId(marketCatalogue) print("Market " + marketChoice + " has id: " + market_id) selection_id = self.getSelectionId(marketCatalogue) print("selction_id: " + str(selection_id)) ###Get Market Book - best offers only marketBook = self.getMarketBookBestOffers(market_id) self.printPrices(marketBook) #sign_out? self.session = False if not self.session: msg = 'SESSION TIMEOUT' print(msg)
def run(self, username='', password='', app_key='', aus=False): # create the API object self.username = username self.api = API(aus, ssl_prefix=username) self.api.app_key = app_key self.logger = Logger(aus) self.logger.bot_version = __version__ # login to betfair api-ng self.do_login(username, password) while self.session: self.do_throttle() self.keep_alive() eventTypeId = self.selectEventType() #e.g select Sport eventId = self.selectEvent(eventTypeId) #e.g select Match eventMarkets = self.getMarkets( eventId) #all markets for selected Match #self.prettyPrint(eventMarkets) over_unders = self.selectOverUnders( eventMarkets ) #subset of eventMarkets i.e only Over/Under x.5's print('\nList of Over/Under x.5 markets: ') for market in over_unders: print(market["marketName"] ) #print list of available Over/Under x.5's illiquidChoice = input("Select arbitrage-market from list: \n") illiquidMarketId = self.selectMarkets( illiquidChoice, eventMarkets) #select appropriate x.5 market #print("IlliquidMarketId: "+ str(illiquidMarketId)) liquidMarketId = self.selectMarkets( 'Correct Score', eventMarkets) #Correct Score market, automatically selected if (liquidMarketId == []): print( "Correct Score Market not available for this event. Please select another: " ) continue marketIds = self.combineMarkets(liquidMarketId, illiquidMarketId) lockIn = True skip = False inputList = [] while lockIn: marketBooks = self.getMarketPrices(marketIds) #dict encapsulatedBook = self.encapsulatePrices( marketBooks, eventMarkets) #object if (len(inputList) == 0): encapsulatedBook.print_liquidities() encapsulatedBook.getLiquidMarket().printRunners() inputList = input( "Select runners from 'Correct-Score' market. Seperate with ',':\n" ) #skip = True encapsulatedBook.getLiquidMarket().selectRunners(inputList) #encapsulatedBook.printBooks() encapsulatedBook.callArbitrage() #lockIn = False #self.session = False if not self.session: msg = 'SESSION TIMEOUT' print(msg)
def run(self, username = '', password = '', app_key = '', aus = False): # create the API object self.username = username self.api = API(aus, ssl_prefix = username) self.api.app_key = app_key self.logger = Logger(aus) self.logger.bot_version = __version__ # login to betfair api-ng self.do_login(username, password) while self.session: self.do_throttle() self.keep_alive() # refresh login session (every 15 mins) # check bet count betcount = self.get_betcount() if betcount < settings.max_transactions: # get menu paths & filter all_menu_paths = self.api.get_menu_paths(self.ignores) market_paths = self.filter_menu_path(all_menu_paths) # get markets (req'd to get selection ids for runners) market_ids = list(market_paths.keys()) markets = self.get_markets(market_ids) # maximum size = 1000 if markets: msg = 'FOUND %s NEW MARKETS...' % len(markets) self.logger.xprint(msg) # create bets all_bets = self.create_bets(markets, market_paths) # place bets self.place_bets(all_bets) else: # none of the markets left in menu will go in-play for market_id in market_ids: self.update_ignores(market_id) sleep(5) # cpu saver (menu parse is intensive!) else: # bet count limit reached for this hour utcnow = datetime.utcnow() nextdate = utcnow + timedelta(hours = 1) nextdate = datetime(nextdate.year, nextdate.month, nextdate.day, nextdate.hour) wait = (nextdate - utcnow).total_seconds() if wait > 0: mins, secs = divmod(wait, 60) msg = 'WARNING: TRANSACTION LIMIT REACHED FOR CURRENT HOUR\n' msg += 'Sleeping for %dm %ds' % (mins, secs) self.logger.xprint(msg) # wait until next hour, keeping session alive time_target = time() + wait while time() < time_target: self.keep_alive() # refresh login session (runs every 15 mins) sleep(0.5) # CPU saver! if not self.session: msg = 'SESSION TIMEOUT' raise Exception(msg)
def run(self, username = '', password = '', app_key = '', aus = False): # create the API object self.username = username self.api = API(aus, ssl_prefix = username) self.api.app_key = app_key self.logger = Logger(aus) self.logger.bot_version = __version__ # login to betfair api-ng self.do_login(username, password) while self.session: self.do_throttle() self.keep_alive() eventTypeId = self.selectEventType() #e.g returns 1 for Soccer eventId = self.selectEvent(eventTypeId) #e.g return 27632951 for Lucena CF v Coria CF eventMarkets = self.getMarkets(eventId) #returns all markets for a given event #self.prettyPrint(eventMarkets) # --------------- # #At this point we need to jump directly into selecting arb-choices # --------------- # for market in eventMarkets: print(market["marketName"]) liquidChoices = input("Input liquid markets. Note: max = 2. Separate using ',' and no spaces between choices:\n") liquidMarketIds = self.selectMarkets(liquidChoices, eventMarkets) illiquidChoices = input("Input Non-liquid markets. Note: max = 2. Separate using ',' and no spaces between choices:\n") illiquidMarketIds = self.selectMarkets(illiquidChoices, eventMarkets) marketIds = self.combineMarkets(liquidMarketIds, illiquidMarketIds) print("\nAcquired choice Ids: ") for each in marketIds: print(each) lockIn = True while lockIn: marketBooks = self.getMarketPrices(marketIds) #returns array of marketbooks for each selected market #self.prettyPrint(marketBooks) #self.printPrices(marketBooks) encapsulatedBook = self.encapsulatePrices(marketBooks, eventMarkets) #encapsulatedBook.printBooks() encapsulatedBook.callArbitrage() #lockIn = False #self.session = False if not self.session: msg = 'SESSION TIMEOUT' print(msg)
def soccer_run(self, username = '', password = '', app_key = '', aus = False): self.username = username self.api = API(aus, ssl_prefix = username) self.api.app_key = app_key self.logger = Logger(aus) self.logger.bot_version = __version__ # login to betfair api-ng self.do_login(username, password) while self.session: self.do_throttle() self.keep_alive() eventTypeId = 1 #Soccer eventId = self.selectEvent(eventTypeId) eventMarkets = self.showMarkets(eventId) #returns all markets for a given event for market in eventMarkets: print(market["marketName"]) liquidMarkets = self.selectMarkets('Correct Score', eventMarkets) print("Correct Score market has been set as Liquid market. Select Choice of Over-Under:\n") illiquidChoices = input("Input Non-liquid markets. Note: max = 2. Separate using ',' and no spaces between choices:\n") illiquidMarketIds = self.selectMarkets(illiquidChoices, eventMarkets) marketIds = self.combineMarkets(liquidMarketIds, illiquidMarketIds) print("\nAcquired choice Ids: ") for each in marketIds: print(each) lockIn = True while lockIn: marketBooks = self.getMarketPrices(marketIds) #returns array of marketbooks for each selected market #self.prettyPrint(marketBooks) #self.printPrices(marketBooks) encapsulatedBook = self.encapsulatePrices(marketBooks, eventMarkets) #encapsulatedBook.printBooks() encapsulatedBook.callArbitrage() #lockIn = False #self.session = False if not self.session: msg = 'SESSION TIMEOUT' print(msg)
# Retrieve live mode status from the environment (defaults to False) LIVE_MODE = 'LIVE_MODE' in os.environ and os.environ['LIVE_MODE'] == 'true' if not USERNAME: logger.error('BETFAIR_USERNAME is not set, exiting.') exit() if not PASSWORD: logger.error('BETFAIR_PASSWORD is not set, exiting.') exit() if not APP_KEY: logger.error('BETFAIR_APP_KEY is not set, exiting.') exit() api = API(False, ssl_prefix=USERNAME) session_manager = threads.SessionManager(api, USERNAME, PASSWORD, APP_KEY) session_manager.start() sleep(5) # Allow the session manager time to log in. market_manager = threads.MarketManager(api) market_book_manager = threads.MarketBookManager(api) statistics_manager = threads.StatisticsManager(api) account_manager = threads.AccountManager(api) order_manager = threads.OrderManager(api) report_manager = threads.ReportManager(api) strategy_manager = threads.StrategyManager(api, LIVE_MODE) result_scraper = threads.ResultScraper() market_manager.start() market_book_manager.start()
class Pixie(object): """betfair laying bot - lays the field using settings.py parameters""" def __init__(self): self.username = '' # set by run() function at startup self.logger = None # set by run() function at startup self.api = None # set by run() function at startup self.abs_path = os.path.abspath(os.path.dirname(__file__)) self.ignores_path = '%s/ignores.pkl' % self.abs_path self.ignores = self.unpickle_data(self.ignores_path, []) # list of market ids self.betcount_path = '%s/betcount.pkl' % self.abs_path self.betcount = self.unpickle_data(self.betcount_path, {}) # keys = hours, vals = market ids self.throttle = { 'next': time(), # time we can send next request. auto-updated in do_throttle() 'wait': 1.0, # time in seconds between requests 'keep_alive': time(), # auto-updated in keep_alive() 'update_closed': time() # auto-updated in update_ignores() } self.session = False def pickle_data(self, filepath = '', data = None): """pickle object to file""" f = open(filepath, 'wb') pickle.dump(data, f) f.close() def unpickle_data(self, filepath = '', default_object = None): """unpickle file to object. returns object""" if os.path.exists(filepath): f = open(filepath, 'rb') data = pickle.load(f) f.close() return data return default_object # return default object (empty) def update_ignores(self, market_id = ''): """update ignores list""" if market_id: # add market to ignores dict if market_id not in self.ignores: self.ignores.append(market_id) self.pickle_data(self.ignores_path, self.ignores) else: # check for closed markets (once every 2 hours) count = len(self.ignores) now = time() if count > 0 and now > self.throttle['update_closed']: secs = 2 * 60 * 60 # 2 hours self.throttle['update_closed'] = now + secs msg = 'CHECKING %s MARKETS FOR CLOSED STATUS...' % count self.logger.xprint(msg) for i in range(0, count, 5): market_ids = self.ignores[i:i+5] # list of upto 5 market ids self.do_throttle() books = self.get_market_books(market_ids) for book in books: if book['status'] == 'CLOSED': # remove from ignores self.ignores.remove(book['marketId']) self.pickle_data(self.ignores_path, self.ignores) def update_betcount(self, betcount = 0): """update bet count to avoid exceeding 1000 bets per hour""" hour = datetime.utcnow().hour if hour not in self.betcount: # new hour self.betcount[hour] = [betcount] # remove 'old' keys for key in self.betcount: if key != hour: self.betcount.pop(key) else: # current hour self.betcount[hour].append(betcount) # pickle self.pickle_data(self.betcount_path, self.betcount) def get_betcount(self): """returns bet count for current hour as integer""" betcount = 0 hour = datetime.utcnow().hour if hour in self.betcount: betcount = sum(self.betcount[hour]) return betcount def do_throttle(self): """return when it's safe to continue""" now = time() if now < self.throttle['next']: wait = self.throttle['next'] - now sleep(wait) self.throttle['next'] = time() + self.throttle['wait'] return def do_login(self, username = '', password = ''): """login to betfair & set session status""" self.session = False resp = self.api.login(username, password) if resp == 'SUCCESS': self.session = True else: self.session = False # failed login msg = 'api.login() resp = %s' % resp raise Exception(msg) def keep_alive(self): """refresh login session. sessions expire after 20 mins. NOTE: betfair throttle = 1 req every 7 mins """ now = time() if now > self.throttle['keep_alive']: # refresh self.session = False resp = self.api.keep_alive() if resp == 'SUCCESS': self.throttle['keep_alive'] = now + (15 * 60) # add 15 mins self.session = True else: self.session = False msg = 'api.keep_alive() resp = %s' % resp raise Exception(msg) def get_markets(self, market_ids = None): """returns a list of markets @market_ids: type = list. list of market ids to get info for. HINT: market ids can be filtered from menu paths """ if market_ids: params = { 'filter': { 'marketTypeCodes': settings.market_types, 'marketBettingTypes': ['ODDS'], 'turnInPlayEnabled': True, # will go in-play 'inPlayOnly': False, # market NOT currently in-play 'marketIds': market_ids }, 'marketProjection': ['RUNNER_DESCRIPTION'], 'maxResults': 1000, # maximum allowed by betfair 'sort': 'FIRST_TO_START' } # send the request markets = self.api.get_markets(params) if type(markets) is list: return markets else: msg = 'api.get_markets() resp = %s' % markets raise Exception(msg) def create_bets(self, markets = None, market_paths = None): """returns a dict of bets. keys = market ids, vals = list of bets""" market_bets = {} # loop through markets for market in markets: # get bet settings for this market market_id = market['marketId'] if market_id in market_paths: bets_index = market_paths[market_id]['bets_index'] bets = settings.market_bets[bets_index] # create bets for this market market_path = market_paths[market_id]['market_path'] market_bets[market_id] = {'bets': [], 'market_path': market_path} for runner in market['runners']: for bet in bets: new_bet = {} new_bet['selectionId'] = runner['selectionId'] new_bet['side'] = bet['side'] new_bet['orderType'] = 'LIMIT' new_bet['limitOrder'] = { 'size': bet['stake'], 'price': bet['price'], 'persistenceType': 'PERSIST' # KEEP at in-play. Set as 'LAPSE' to cancel. } market_bets[market_id]['bets'].append(new_bet) return market_bets # max bet count = 1000 def place_bets(self, market_bets = None): """loop through markets and place bets @market_bets: type = dict returned from create_bets() NOTE: market_bets will contain up to 1000 bets! """ for market_id in market_bets: bets = market_bets[market_id]['bets'] if bets: # update & check bet count new_betcount = len(bets) self.update_betcount(new_betcount) betcount = self.get_betcount() # total bets placed in current hour if betcount >= settings.max_transactions: return # place bets... market_path = market_bets[market_id]['market_path'] msg = 'MARKET PATH: %s\n' % market_path msg += 'PLACING %s BETS...\n' % len(bets) for i, bet in enumerate(bets): msg += '%s: %s\n' % (i, bet) self.logger.xprint(msg) self.do_throttle() resp = self.api.place_bets(market_id, bets) if (type(resp) is dict and 'status' in resp ): if resp['status'] == 'SUCCESS': # add to ignores self.update_ignores(market_id) msg = 'PLACE BETS: SUCCESS' self.logger.xprint(msg) else: if resp['errorCode'] == 'INSUFFICIENT_FUNDS': msg = 'PLACE BETS: FAIL (%s)' % resp['errorCode'] self.logger.xprint(msg) sleep(180) # wait 3 minutes else: msg = 'PLACE BETS: FAIL (%s)' % resp['errorCode'] self.logger.xprint(msg, True) # do not raise error - allow bot to continue # add to ignores self.update_ignores(market_id) else: msg = 'PLACE BETS: FAIL\n%s' % resp raise Exception(msg) def filter_menu_path(self, menu_paths = None): """returns list of paths matching filters specified in settings.py @menu_paths: dict of menu paths. keys = market ids, vals = menu paths """ keepers = {} # loop through all menu paths for market_id in menu_paths: market_path = menu_paths[market_id] path_texts = market_path.split('/') # check filters for filter_index, filter in enumerate(settings.menu_filters): # check if ALL search text matches this market matched_all = False for text in filter: if text in path_texts: matched_all = True else: matched_all = False break # keep this market? if matched_all: keepers[market_id] = { 'bets_index': filter_index, 'market_path': market_path } return keepers #My-addition def prettyPrint(self, json_dict): print(json.dumps(json_dict,sort_keys=True,indent=4,separators=(',',': '))) def callAping(self, jsonrpc_req): #need to abstract later try: req = urllib.request.Request("https://api.betfair.com/exchange/betting/json-rpc/v1", jsonrpc_req.encode('utf-8'), headers = {'X-Application':self.api.app_key , 'X-Authentication':self.api.session_token, 'content-type': 'application/json'}) response = urllib.request.urlopen(req) jsonResponse = response.read() return jsonResponse.decode('utf-8') except urllib.error.URLError as e: print (e.reason) print ('Oops no service available at ' + str(url)) exit() except urllib.error.HTTPError: print ('Oops not a valid operation from the service ' + str(url)) exit() def getMarketCatalogue(self, eventTypeID, marketName): #Note: a market catalogue gives all the outcomes for "sale" in a market if (eventTypeID is not None): print ('Calling listMarketCatalouge Operation to get MarketID and selectionId') now = datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ') market_catalogue_req = '{"jsonrpc": "2.0", "method": "SportsAPING/v1.0/listMarketCatalogue", "params": {"filter":{"eventTypeIds":["' + eventTypeID + '"],"marketCountries":["GB"],"marketTypeCodes":["'+marketName+ '"],'\ '"marketStartTime":{"from":"' + now + '"}},"sort":"FIRST_TO_START","maxResults":"1","marketProjection":["RUNNER_METADATA"]}, "id": 1}' market_catalogue_response = self.callAping(market_catalogue_req) market_catalogue_loads = json.loads(market_catalogue_response) try: market_catalogue_results = market_catalogue_loads['result'] return market_catalogue_results except: print ('Exception from API-NG' + str(market_catalogue_results['error'])) exit() def getMarketId(self, marketCatalogueResult): if( marketCatalogueResult is not None): for market in marketCatalogueResult: return market['marketId'] def getSelectionId(self, marketCatalogueResult): #NEED to modify so user can select (multiple) runners? if(marketCatalogueResult is not None): for market in marketCatalogueResult: return market['runners'][0]['selectionId'] def getMarketBookBestOffers(self, marketId): print ('Calling listMarketBook to read prices for the Market with ID :' + marketId) market_book_req = '{"jsonrpc": "2.0", "method": "SportsAPING/v1.0/listMarketBook", "params": {"marketIds":["' + marketId + '"],"priceProjection":{"priceData":["EX_BEST_OFFERS"]}}, "id": 1}' market_book_response = self.callAping(market_book_req) market_book_loads = json.loads(market_book_response) try: market_book_result = market_book_loads['result'] #self.prettyPrint(market_book_result) return market_book_result except: print ('Exception from API-NG' + str(market_book_result['error'])) exit() def printPrices(self, market_book_result): if(market_book_result is not None): print ('Please find Best three available prices for the runners') for marketBook in market_book_result: runners = marketBook['runners'] for runner in runners: print ('Selection id is ' + str(runner['selectionId'])) if (runner['status'] == 'ACTIVE'): print ('Available to back price :' + str(runner['ex']['availableToBack'])) print ('Available to lay price :' + str(runner['ex']['availableToLay'])) else: print ('This runner is not active') def run(self, username = '', password = '', app_key = '', aus = False): # create the API object self.username = username self.api = API(aus, ssl_prefix = username) self.api.app_key = app_key self.logger = Logger(aus) self.logger.bot_version = __version__ # login to betfair api-ng self.do_login(username, password) while self.session: self.do_throttle() self.keep_alive() #Get and display all events eventsList = self.api.get_event_types() ###user interaction #print(json.dumps(eventsList,sort_keys=True,indent=4,separators=(',',': '))) print('Here are the list of available events: ') for event in eventsList: eventName = event['eventType']['name'] print(eventName) #eg: Soccer eventChoice = input('Please input an option from the above list: ') ###Get Id of selected event eventId = None for event in eventsList: eventName = event['eventType']['name'] eventId = None if (eventName == eventChoice): eventId = event['eventType']['id'] else: continue break ###Display all markets for selected event eventMarkets = self.api.get_market_types({'eventTypeIds':[eventId]}) for market in eventMarkets: print(market["marketType"]) #eg: OVER_UNDER_15 marketChoice = input("Please select from the list of available Markets below: ") print("You selected "+ marketChoice) ###Get market-catalogue for user selected event marketCatalogue = self.getMarketCatalogue(eventId,marketChoice) print("This is the list of runners to bet on in the "+marketChoice+" market:") for runner in marketCatalogue[0]["runners"]: print(runner["runnerName"]) #eg: Under 1.5 goals ###Get market and selection (i.e runner) ids #self.prettyPrint(marketCatalogue) market_id = self.getMarketId(marketCatalogue) print("Market " + marketChoice + " has id: " + market_id) selection_id = self.getSelectionId(marketCatalogue) print("selction_id: " + str(selection_id)) ###Get Market Book - best offers only marketBook = self.getMarketBookBestOffers(market_id) self.printPrices(marketBook) #sign_out? self.session = False if not self.session: msg = 'SESSION TIMEOUT' print(msg)
def run(self, username='', password='', app_key='', aus=False): # create the API object self.username = username self.api = API(aus, ssl_prefix=username) self.api.app_key = app_key self.logger = Logger(aus) self.logger.bot_version = __version__ # login to betfair api-ng self.do_login(username, password) while self.session: self.do_throttle() self.keep_alive() #Get and display all events eventsList = self.api.get_event_types() ###user interaction #print(json.dumps(eventsList,sort_keys=True,indent=4,separators=(',',': '))) print('Here are the list of available events: ') for event in eventsList: eventName = event['eventType']['name'] print(eventName) #eg: Soccer eventChoice = input('Please input an option from the above list: ') ###Get Id of selected event eventId = None for event in eventsList: eventName = event['eventType']['name'] eventId = None if (eventName == eventChoice): eventId = event['eventType']['id'] else: continue break ###Display all markets for selected event eventMarkets = self.api.get_market_types( {'eventTypeIds': [eventId]}) for market in eventMarkets: print(market["marketType"]) #eg: OVER_UNDER_15 marketChoice = input( "Please select from the list of available Markets below: ") print("You selected " + marketChoice) ###Get market-catalogue for user selected event marketCatalogue = self.getMarketCatalogue(eventId, marketChoice) print("This is the list of runners to bet on in the " + marketChoice + " market:") for runner in marketCatalogue[0]["runners"]: print(runner["runnerName"]) #eg: Under 1.5 goals ###Get market and selection (i.e runner) ids #self.prettyPrint(marketCatalogue) market_id = self.getMarketId(marketCatalogue) print("Market " + marketChoice + " has id: " + market_id) selection_id = self.getSelectionId(marketCatalogue) print("selction_id: " + str(selection_id)) ###Get Market Book - best offers only marketBook = self.getMarketBookBestOffers(market_id) self.printPrices(marketBook) #sign_out? self.session = False if not self.session: msg = 'SESSION TIMEOUT' print(msg)
class Pixie(object): """betfair laying bot - lays the field using settings.py parameters""" def __init__(self): self.username = '' # set by run() function at startup self.logger = None # set by run() function at startup self.api = None # set by run() function at startup self.abs_path = os.path.abspath(os.path.dirname(__file__)) self.ignores_path = '%s/ignores.pkl' % self.abs_path self.ignores = self.unpickle_data(self.ignores_path, []) # list of market ids self.betcount_path = '%s/betcount.pkl' % self.abs_path self.betcount = self.unpickle_data( self.betcount_path, {}) # keys = hours, vals = market ids self.throttle = { 'next': time( ), # time we can send next request. auto-updated in do_throttle() 'wait': 1.0, # time in seconds between requests 'keep_alive': time(), # auto-updated in keep_alive() 'update_closed': time() # auto-updated in update_ignores() } self.session = False def pickle_data(self, filepath='', data=None): """pickle object to file""" f = open(filepath, 'wb') pickle.dump(data, f) f.close() def unpickle_data(self, filepath='', default_object=None): """unpickle file to object. returns object""" if os.path.exists(filepath): f = open(filepath, 'rb') data = pickle.load(f) f.close() return data return default_object # return default object (empty) def update_ignores(self, market_id=''): """update ignores list""" if market_id: # add market to ignores dict if market_id not in self.ignores: self.ignores.append(market_id) self.pickle_data(self.ignores_path, self.ignores) else: # check for closed markets (once every 2 hours) count = len(self.ignores) now = time() if count > 0 and now > self.throttle['update_closed']: secs = 2 * 60 * 60 # 2 hours self.throttle['update_closed'] = now + secs msg = 'CHECKING %s MARKETS FOR CLOSED STATUS...' % count self.logger.xprint(msg) for i in range(0, count, 5): market_ids = self.ignores[i:i + 5] # list of upto 5 market ids self.do_throttle() books = self.get_market_books(market_ids) for book in books: if book['status'] == 'CLOSED': # remove from ignores self.ignores.remove(book['marketId']) self.pickle_data(self.ignores_path, self.ignores) def update_betcount(self, betcount=0): """update bet count to avoid exceeding 1000 bets per hour""" hour = datetime.utcnow().hour if hour not in self.betcount: # new hour self.betcount[hour] = [betcount] # remove 'old' keys for key in self.betcount: if key != hour: self.betcount.pop(key) else: # current hour self.betcount[hour].append(betcount) # pickle self.pickle_data(self.betcount_path, self.betcount) def get_betcount(self): """returns bet count for current hour as integer""" betcount = 0 hour = datetime.utcnow().hour if hour in self.betcount: betcount = sum(self.betcount[hour]) return betcount def do_throttle(self): """return when it's safe to continue""" now = time() if now < self.throttle['next']: wait = self.throttle['next'] - now sleep(wait) self.throttle['next'] = time() + self.throttle['wait'] return def do_login(self, username='', password=''): """login to betfair & set session status""" self.session = False resp = self.api.login(username, password) if resp == 'SUCCESS': self.session = True else: self.session = False # failed login msg = 'api.login() resp = %s' % resp raise Exception(msg) def keep_alive(self): """refresh login session. sessions expire after 20 mins. NOTE: betfair throttle = 1 req every 7 mins """ now = time() if now > self.throttle['keep_alive']: # refresh self.session = False resp = self.api.keep_alive() if resp == 'SUCCESS': self.throttle['keep_alive'] = now + (15 * 60) # add 15 mins self.session = True else: self.session = False msg = 'api.keep_alive() resp = %s' % resp raise Exception(msg) def get_markets(self, market_ids=None): """returns a list of markets @market_ids: type = list. list of market ids to get info for. HINT: market ids can be filtered from menu paths """ if market_ids: params = { 'filter': { 'marketTypeCodes': settings.market_types, 'marketBettingTypes': ['ODDS'], 'turnInPlayEnabled': True, # will go in-play 'inPlayOnly': False, # market NOT currently in-play 'marketIds': market_ids }, 'marketProjection': ['RUNNER_DESCRIPTION'], 'maxResults': 1000, # maximum allowed by betfair 'sort': 'FIRST_TO_START' } # send the request markets = self.api.get_markets(params) if type(markets) is list: return markets else: msg = 'api.get_markets() resp = %s' % markets raise Exception(msg) def create_bets(self, markets=None, market_paths=None): """returns a dict of bets. keys = market ids, vals = list of bets""" market_bets = {} # loop through markets for market in markets: # get bet settings for this market market_id = market['marketId'] if market_id in market_paths: bets_index = market_paths[market_id]['bets_index'] bets = settings.market_bets[bets_index] # create bets for this market market_path = market_paths[market_id]['market_path'] market_bets[market_id] = { 'bets': [], 'market_path': market_path } for runner in market['runners']: for bet in bets: new_bet = {} new_bet['selectionId'] = runner['selectionId'] new_bet['side'] = bet['side'] new_bet['orderType'] = 'LIMIT' new_bet['limitOrder'] = { 'size': bet['stake'], 'price': bet['price'], 'persistenceType': 'PERSIST' # KEEP at in-play. Set as 'LAPSE' to cancel. } market_bets[market_id]['bets'].append(new_bet) return market_bets # max bet count = 1000 def place_bets(self, market_bets=None): """loop through markets and place bets @market_bets: type = dict returned from create_bets() NOTE: market_bets will contain up to 1000 bets! """ for market_id in market_bets: bets = market_bets[market_id]['bets'] if bets: # update & check bet count new_betcount = len(bets) self.update_betcount(new_betcount) betcount = self.get_betcount( ) # total bets placed in current hour if betcount >= settings.max_transactions: return # place bets... market_path = market_bets[market_id]['market_path'] msg = 'MARKET PATH: %s\n' % market_path msg += 'PLACING %s BETS...\n' % len(bets) for i, bet in enumerate(bets): msg += '%s: %s\n' % (i, bet) self.logger.xprint(msg) self.do_throttle() resp = self.api.place_bets(market_id, bets) if (type(resp) is dict and 'status' in resp): if resp['status'] == 'SUCCESS': # add to ignores self.update_ignores(market_id) msg = 'PLACE BETS: SUCCESS' self.logger.xprint(msg) else: if resp['errorCode'] == 'INSUFFICIENT_FUNDS': msg = 'PLACE BETS: FAIL (%s)' % resp['errorCode'] self.logger.xprint(msg) sleep(180) # wait 3 minutes else: msg = 'PLACE BETS: FAIL (%s)' % resp['errorCode'] self.logger.xprint( msg, True ) # do not raise error - allow bot to continue # add to ignores self.update_ignores(market_id) else: msg = 'PLACE BETS: FAIL\n%s' % resp raise Exception(msg) def filter_menu_path(self, menu_paths=None): """returns list of paths matching filters specified in settings.py @menu_paths: dict of menu paths. keys = market ids, vals = menu paths """ keepers = {} # loop through all menu paths for market_id in menu_paths: market_path = menu_paths[market_id] path_texts = market_path.split('/') # check filters for filter_index, filter in enumerate(settings.menu_filters): # check if ALL search text matches this market matched_all = False for text in filter: if text in path_texts: matched_all = True else: matched_all = False break # keep this market? if matched_all: keepers[market_id] = { 'bets_index': filter_index, 'market_path': market_path } return keepers #My-addition def prettyPrint(self, json_dict): print( json.dumps(json_dict, sort_keys=True, indent=4, separators=(',', ': '))) def callAping(self, jsonrpc_req): #need to abstract later try: req = urllib.request.Request( "https://api.betfair.com/exchange/betting/json-rpc/v1", jsonrpc_req.encode('utf-8'), headers={ 'X-Application': self.api.app_key, 'X-Authentication': self.api.session_token, 'content-type': 'application/json' }) response = urllib.request.urlopen(req) jsonResponse = response.read() return jsonResponse.decode('utf-8') except urllib.error.URLError as e: print(e.reason) print('Oops no service available at ' + str(url)) exit() except urllib.error.HTTPError: print('Oops not a valid operation from the service ' + str(url)) exit() def getMarketCatalogue(self, eventTypeID, marketName): #Note: a market catalogue gives all the outcomes for "sale" in a market if (eventTypeID is not None): print( 'Calling listMarketCatalouge Operation to get MarketID and selectionId' ) now = datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ') market_catalogue_req = '{"jsonrpc": "2.0", "method": "SportsAPING/v1.0/listMarketCatalogue", "params": {"filter":{"eventTypeIds":["' + eventTypeID + '"],"marketCountries":["GB"],"marketTypeCodes":["'+marketName+ '"],'\ '"marketStartTime":{"from":"' + now + '"}},"sort":"FIRST_TO_START","maxResults":"1","marketProjection":["RUNNER_METADATA"]}, "id": 1}' market_catalogue_response = self.callAping(market_catalogue_req) market_catalogue_loads = json.loads(market_catalogue_response) try: market_catalogue_results = market_catalogue_loads['result'] return market_catalogue_results except: print('Exception from API-NG' + str(market_catalogue_results['error'])) exit() def getMarketId(self, marketCatalogueResult): if (marketCatalogueResult is not None): for market in marketCatalogueResult: return market['marketId'] def getSelectionId(self, marketCatalogueResult): #NEED to modify so user can select (multiple) runners? if (marketCatalogueResult is not None): for market in marketCatalogueResult: return market['runners'][0]['selectionId'] def getMarketBookBestOffers(self, marketId): print( 'Calling listMarketBook to read prices for the Market with ID :' + marketId) market_book_req = '{"jsonrpc": "2.0", "method": "SportsAPING/v1.0/listMarketBook", "params": {"marketIds":["' + marketId + '"],"priceProjection":{"priceData":["EX_BEST_OFFERS"]}}, "id": 1}' market_book_response = self.callAping(market_book_req) market_book_loads = json.loads(market_book_response) try: market_book_result = market_book_loads['result'] #self.prettyPrint(market_book_result) return market_book_result except: print('Exception from API-NG' + str(market_book_result['error'])) exit() def printPrices(self, market_book_result): if (market_book_result is not None): print('Please find Best three available prices for the runners') for marketBook in market_book_result: runners = marketBook['runners'] for runner in runners: print('Selection id is ' + str(runner['selectionId'])) if (runner['status'] == 'ACTIVE'): print('Available to back price :' + str(runner['ex']['availableToBack'])) print('Available to lay price :' + str(runner['ex']['availableToLay'])) else: print('This runner is not active') def run(self, username='', password='', app_key='', aus=False): # create the API object self.username = username self.api = API(aus, ssl_prefix=username) self.api.app_key = app_key self.logger = Logger(aus) self.logger.bot_version = __version__ # login to betfair api-ng self.do_login(username, password) while self.session: self.do_throttle() self.keep_alive() #Get and display all events eventsList = self.api.get_event_types() ###user interaction #print(json.dumps(eventsList,sort_keys=True,indent=4,separators=(',',': '))) print('Here are the list of available events: ') for event in eventsList: eventName = event['eventType']['name'] print(eventName) #eg: Soccer eventChoice = input('Please input an option from the above list: ') ###Get Id of selected event eventId = None for event in eventsList: eventName = event['eventType']['name'] eventId = None if (eventName == eventChoice): eventId = event['eventType']['id'] else: continue break ###Display all markets for selected event eventMarkets = self.api.get_market_types( {'eventTypeIds': [eventId]}) for market in eventMarkets: print(market["marketType"]) #eg: OVER_UNDER_15 marketChoice = input( "Please select from the list of available Markets below: ") print("You selected " + marketChoice) ###Get market-catalogue for user selected event marketCatalogue = self.getMarketCatalogue(eventId, marketChoice) print("This is the list of runners to bet on in the " + marketChoice + " market:") for runner in marketCatalogue[0]["runners"]: print(runner["runnerName"]) #eg: Under 1.5 goals ###Get market and selection (i.e runner) ids #self.prettyPrint(marketCatalogue) market_id = self.getMarketId(marketCatalogue) print("Market " + marketChoice + " has id: " + market_id) selection_id = self.getSelectionId(marketCatalogue) print("selction_id: " + str(selection_id)) ###Get Market Book - best offers only marketBook = self.getMarketBookBestOffers(market_id) self.printPrices(marketBook) #sign_out? self.session = False if not self.session: msg = 'SESSION TIMEOUT' print(msg)
class BetBot(object): """betfair laying bot - lays the field using settings.py parameters""" def __init__(self): self.username = '' # set by run() function at startup self.logger = None # set by run() function at startup self.api = None # set by run() function at startup self.abs_path = os.path.abspath(os.path.dirname(__file__)) self.ignores_path = '%s/ignores.pkl' % self.abs_path self.ignores = self.unpickle_data(self.ignores_path, []) # list of market ids self.betcount_path = '%s/betcount.pkl' % self.abs_path self.betcount = self.unpickle_data(self.betcount_path, {}) # keys = hours, vals = market ids self.throttle = { 'next': time(), # time we can send next request. auto-updated in do_throttle() 'wait': 1.0, # time in seconds between requests 'keep_alive': time(), # auto-updated in keep_alive() 'update_closed': time() # auto-updated in update_ignores() } self.session = False def pickle_data(self, filepath = '', data = None): """pickle object to file""" f = open(filepath, 'wb') pickle.dump(data, f) f.close() def unpickle_data(self, filepath = '', default_object = None): """unpickle file to object. returns object""" if os.path.exists(filepath): f = open(filepath, 'rb') data = pickle.load(f) f.close() return data return default_object # return default object (empty) def update_ignores(self, market_id = ''): """update ignores list""" if market_id: # add market to ignores dict if market_id not in self.ignores: self.ignores.append(market_id) self.pickle_data(self.ignores_path, self.ignores) else: # check for closed markets (once every 2 hours) count = len(self.ignores) now = time() if count > 0 and now > self.throttle['update_closed']: secs = 2 * 60 * 60 # 2 hours self.throttle['update_closed'] = now + secs msg = 'CHECKING %s MARKETS FOR CLOSED STATUS...' % count self.logger.xprint(msg) for i in range(0, count, 5): market_ids = self.ignores[i:i+5] # list of upto 5 market ids self.do_throttle() books = self.get_market_books(market_ids) print(books) for book in books: if book['status'] == 'CLOSED': # remove from ignores self.ignores.remove(book['marketId']) self.pickle_data(self.ignores_path, self.ignores) def update_betcount(self, betcount = 0): """update bet count to avoid exceeding 1000 bets per hour""" hour = datetime.utcnow().hour if hour not in self.betcount: # new hour self.betcount[hour] = [betcount] # remove 'old' keys for key in self.betcount: if key != hour: self.betcount.pop(key) else: # current hour self.betcount[hour].append(betcount) # pickle self.pickle_data(self.betcount_path, self.betcount) def get_betcount(self): """returns bet count for current hour as integer""" betcount = 0 hour = datetime.utcnow().hour if hour in self.betcount: betcount = sum(self.betcount[hour]) return betcount def do_throttle(self): """return when it's safe to continue""" now = time() if now < self.throttle['next']: wait = self.throttle['next'] - now sleep(wait) self.throttle['next'] = time() + self.throttle['wait'] return def do_login(self, username = '', password = ''): """login to betfair & set session status""" self.session = False resp = self.api.login(username, password) if resp == 'SUCCESS': self.session = True else: self.session = False # failed login msg = 'api.login() resp = %s' % resp raise Exception(msg) def keep_alive(self): """refresh login session. sessions expire after 20 mins. NOTE: betfair throttle = 1 req every 7 mins """ now = time() if now > self.throttle['keep_alive']: # refresh self.session = False resp = self.api.keep_alive() if resp == 'SUCCESS': self.throttle['keep_alive'] = now + (15 * 60) # add 15 mins self.session = True else: self.session = False msg = 'api.keep_alive() resp = %s' % resp raise Exception(msg) def get_markets(self, market_ids = None): """returns a list of markets @market_ids: type = list. list of market ids to get info for. HINT: market ids can be filtered from menu paths """ if market_ids: params = { 'filter': { 'marketTypeCodes': settings.market_types, 'marketBettingTypes': ['ODDS'], 'turnInPlayEnabled': True, # will go in-play 'inPlayOnly': False, # market NOT currently in-play 'marketIds': market_ids }, 'marketProjection': ['RUNNER_DESCRIPTION'], 'maxResults': 1000, # maximum allowed by betfair 'sort': 'FIRST_TO_START' } # send the request markets = self.api.get_markets(params) if type(markets) is list: return markets else: msg = 'api.get_markets() resp = %s' % markets raise Exception(msg) def create_bets(self, markets = None, market_paths = None): """returns a dict of bets. keys = market ids, vals = list of bets""" market_bets = {} # loop through markets for market in markets: # get bet settings for this market market_id = market['marketId'] if market_id in market_paths: bets_index = market_paths[market_id]['bets_index'] bets = settings.market_bets[bets_index] # create bets for this market market_path = market_paths[market_id]['market_path'] market_bets[market_id] = {'bets': [], 'market_path': market_path} for runner in market['runners']: for bet in bets: new_bet = {} new_bet['selectionId'] = runner['selectionId'] new_bet['side'] = bet['side'] new_bet['orderType'] = 'LIMIT' new_bet['limitOrder'] = { 'size': bet['stake'], 'price': bet['price'], 'persistenceType': 'PERSIST' # KEEP at in-play. Set as 'LAPSE' to cancel. } market_bets[market_id]['bets'].append(new_bet) return market_bets # max bet count = 1000 def place_bets(self, market_bets = None): """loop through markets and place bets @market_bets: type = dict returned from create_bets() NOTE: market_bets will contain up to 1000 bets! """ for market_id in market_bets: bets = market_bets[market_id]['bets'] if bets: # update & check bet count new_betcount = len(bets) self.update_betcount(new_betcount) betcount = self.get_betcount() # total bets placed in current hour if betcount >= settings.max_transactions: return # place bets... market_path = market_bets[market_id]['market_path'] msg = 'MARKET PATH: %s\n' % market_path msg += 'PLACING %s BETS...\n' % len(bets) for i, bet in enumerate(bets): msg += '%s: %s\n' % (i, bet) self.logger.xprint(msg) self.do_throttle() resp = self.api.place_bets(market_id, bets) if (type(resp) is dict and 'status' in resp ): if resp['status'] == 'SUCCESS': # add to ignores self.update_ignores(market_id) msg = 'PLACE BETS: SUCCESS' self.logger.xprint(msg) else: if resp['errorCode'] == 'INSUFFICIENT_FUNDS': msg = 'PLACE BETS: FAIL (%s)' % resp['errorCode'] self.logger.xprint(msg) sleep(180) # wait 3 minutes else: msg = 'PLACE BETS: FAIL (%s)' % resp['errorCode'] self.logger.xprint(msg, True) # do not raise error - allow bot to continue # add to ignores self.update_ignores(market_id) else: msg = 'PLACE BETS: FAIL\n%s' % resp raise Exception(msg) def filter_menu_path(self, menu_paths = None): """returns list of paths matching filters specified in settings.py @menu_paths: dict of menu paths. keys = market ids, vals = menu paths """ keepers = {} # loop through all menu paths for market_id in menu_paths: market_path = menu_paths[market_id] path_texts = market_path.split('/') # check filters for filter_index, filter in enumerate(settings.menu_filters): # check if ALL search text matches this market matched_all = False for text in filter: if text in path_texts: matched_all = True else: matched_all = False break # keep this market? if matched_all: keepers[market_id] = { 'bets_index': filter_index, 'market_path': market_path } return keepers def run(self, username = '', password = '', app_key = '', aus = False): # create the API object self.username = username self.api = API(aus, ssl_prefix = username) self.api.app_key = app_key self.logger = Logger(aus) self.logger.bot_version = __version__ # login to betfair api-ng self.do_login(username, password) while self.session: self.do_throttle() self.keep_alive() # refresh login session (every 15 mins) # check bet count betcount = self.get_betcount() if betcount < settings.max_transactions: # get menu paths & filter all_menu_paths = self.api.get_menu_paths(self.ignores) market_paths = self.filter_menu_path(all_menu_paths) # get markets (req'd to get selection ids for runners) market_ids = list(market_paths.keys()) markets = self.get_markets(market_ids) # maximum size = 1000 if markets: msg = 'FOUND %s NEW MARKETS...' % len(markets) self.logger.xprint(msg) # create bets all_bets = self.create_bets(markets, market_paths) # place bets self.place_bets(all_bets) else: # none of the markets left in menu will go in-play for market_id in market_ids: self.update_ignores(market_id) sleep(5) # cpu saver (menu parse is intensive!) else: # bet count limit reached for this hour utcnow = datetime.utcnow() nextdate = utcnow + timedelta(hours = 1) nextdate = datetime(nextdate.year, nextdate.month, nextdate.day, nextdate.hour) wait = (nextdate - utcnow).total_seconds() if wait > 0: mins, secs = divmod(wait, 60) msg = 'WARNING: TRANSACTION LIMIT REACHED FOR CURRENT HOUR\n' msg += 'Sleeping for %dm %ds' % (mins, secs) self.logger.xprint(msg) # wait until next hour, keeping session alive time_target = time() + wait while time() < time_target: self.keep_alive() # refresh login session (runs every 15 mins) sleep(0.5) # CPU saver! if not self.session: msg = 'SESSION TIMEOUT' raise Exception(msg)