From 88a81456d19bcdc0804a7a378d3b4e53b4567a1a Mon Sep 17 00:00:00 2001 From: richard jeffries Date: Fri, 31 Mar 2017 22:45:37 +0100 Subject: [PATCH 1/5] Add support for betfair scores API --- betfair/betfair.py | 90 +++++++++++++++++++++++++++++++++++++++----- betfair/constants.py | 20 +++++++++- betfair/models.py | 49 +++++++++++++++++++++--- 3 files changed, 143 insertions(+), 16 deletions(-) diff --git a/betfair/betfair.py b/betfair/betfair.py index 0d9987c..db48372 100644 --- a/betfair/betfair.py +++ b/betfair/betfair.py @@ -14,7 +14,7 @@ from betfair import utils from betfair import models from betfair import exceptions - +from betfair.models import ScoreEvent, Score, Incident, Incidents IDENTITY_URLS = collections.defaultdict( lambda: 'https://identitysso.betfair.com/api/', @@ -26,6 +26,16 @@ australia='https://api-au.betfair.com/exchange/betting/json-rpc/v1', ) +SCORE_URLS = collections.defaultdict( + lambda: 'https://api.betfair.com/exchange/scores/json-rpc/v1' +) + +urls = { + 'Betting': 'https://api.betfair.com/exchange/betting/json-rpc/v1', + 'Accounts': 'https://api.betfair.com/exchange/account/json-rpc/v1', + 'Score': 'https://api.betfair.com/exchange/scores/json-rpc/v1' +} + class Betfair(object): """Betfair API client. @@ -38,6 +48,7 @@ class Betfair(object): :param Session session: Optional Requests session :param int timeout: Optional timeout duration (seconds) """ + def __init__(self, app_key, cert_file, content_type='application/json', locale=None, session=None, timeout=None): self.app_key = app_key @@ -56,6 +67,10 @@ def identity_url(self): def api_url(self): return API_URLS[self.locale] + @property + def score_url(self): + return SCORE_URLS[self.locale] + @property def headers(self): return { @@ -76,10 +91,13 @@ def make_auth_request(self, method): if data.get('status') != 'SUCCESS': raise exceptions.AuthError(response, data) - def make_api_request(self, base, method, params, codes=None, model=None): + def make_api_request(self, base, method, params, codes=None, model=None, api='Betting'): payload = utils.make_payload(base, method, params) + url = urls.get(api) + if not url: + raise exceptions.ApiError("no such API", method) response = self.session.post( - self.api_url, + url, data=json.dumps(payload, cls=utils.BetfairEncoder), headers=self.headers, timeout=self.timeout, @@ -235,7 +253,7 @@ def list_venues(self, filter=None, locale=None): filter = filter or models.MarketFilter() return self.make_api_request( 'Sports', - 'listVenues', + 'listCountries', utils.get_kwargs(locals()), model=models.VenueResult, ) @@ -409,7 +427,7 @@ def cancel_orders(self, market_id, instructions, customer_ref=None): 'Sports', 'cancelOrders', utils.get_kwargs(locals()), - model=models.CancelExecutionReport, + model=models.CancelInstructionReport, ) @utils.requires_login @@ -449,12 +467,14 @@ def get_account_funds(self, wallet=None): :param Wallet wallet: Name of the wallet in question """ - return self.make_api_request( + result = self.make_api_request( 'Account', 'getAccountFunds', utils.get_kwargs(locals()), model=models.AccountFundsResponse, + api='Accounts' ) + return result @utils.requires_login def get_account_statement( @@ -469,7 +489,7 @@ def get_account_statement( :param IncludeItem include_item: Which items to include :param Wallet wallte: Which wallet to return statementItems for """ - return self.make_api_request( + result = self.make_api_request( 'Account', 'getAccountStatement', utils.get_kwargs(locals()), @@ -481,12 +501,14 @@ def get_account_details(self): """Returns the details relating your account, including your discount rate and Betfair point balance. """ - return self.make_api_request( + result = self.make_api_request( 'Account', 'getAccountDetails', utils.get_kwargs(locals()), model=models.AccountDetailsResponse, + api='Accounts' ) + return result @utils.requires_login def list_currency_rates(self, from_currency=None): @@ -494,7 +516,7 @@ def list_currency_rates(self, from_currency=None): :param str from_currency: The currency from which the rates are computed """ - return self.make_api_request( + result = self.make_api_request( 'Account', 'listCurrencyRates', utils.get_kwargs(locals()), @@ -509,9 +531,57 @@ def transfer_funds(self, from_, to, amount): :param Wallet to: Destination wallet :param float amount: Amount to transfer """ - return self.make_api_request( + result = self.make_api_request( 'Account', 'transferFunds', utils.get_kwargs(locals()), model=models.TransferResponse, ) + + @utils.requires_login + def list_scores(self, update_keys): + """List scores of keys in update keys from scores API + + :param List update_keys: Keys to update + """ + update_keys = [{'eventId': k} for k in update_keys] + del k # Stop k from propagiting as part of locals() + return self.make_api_request( + 'Scores', + 'listScores', + utils.get_kwargs(locals()), + api='Score', + model=Score + ) + + @utils.requires_login + def list_available_events(self, event_ids=None, event_type_ids=None, event_status=None): + """List events that one can query with the scores API + + :param event_ids: + :param event_type_ids: + :param event_status: + """ + return self.make_api_request( + 'Scores', + 'listAvailableEvents', + utils.get_kwargs(locals()), + api='Score', + model=ScoreEvent + ) + + @utils.requires_login + def list_incidents(self, update_keys): + """List incidents for the keys in update_keys + + :param List update_keys: list of keys to get incidents for + """ + update_keys = [{'eventId': k} for k in update_keys] + del k # Stop k from propagiting as part of locals() + return self.make_api_request( + 'Scores', + 'listIncidents', + utils.get_kwargs(locals()), + api='Score', + model=Incidents + ) diff --git a/betfair/constants.py b/betfair/constants.py index 3f3b76b..b9d6bab 100644 --- a/betfair/constants.py +++ b/betfair/constants.py @@ -6,7 +6,6 @@ from enum import Enum - MarketProjection = Enum( 'MarketProjection', [ 'COMPETITION', @@ -174,6 +173,12 @@ ] ) +TimeInForce = Enum( + 'TimeInForce', [ + 'FILL_OR_KILL' + ] +) + InstructionReportStatus = Enum( 'InstructionReportStatus', [ 'SUCCESS', @@ -270,3 +275,16 @@ 'UNKNOWN', ] ) + +EventStatus = Enum( + 'EventStatus', [ + 'IN_PROGRESS', + 'PENDING', + 'NO_NEW_UPDATES', + 'NO_LIVE_DATA_AVAILABLE', + 'SERVICE_UNAVAILABLE', + 'UNEXPECTED_ERROR', + 'LIVE_DATA_TEMPORARILY_UNAVAILABLE', + 'FINISHED' + ] +) diff --git a/betfair/models.py b/betfair/models.py index bb95e92..f72984e 100644 --- a/betfair/models.py +++ b/betfair/models.py @@ -210,6 +210,8 @@ class LimitOrder(BetfairModel): size = FloatType(required=True) price = FloatType(required=True) persistence_type = EnumType(constants.PersistenceType, required=True) + time_in_force = EnumType(constants.TimeInForce) + min_fill_size = FloatType() class LimitOnCloseOrder(BetfairModel): @@ -224,7 +226,6 @@ class MarketOnCloseOrder(BetfairModel): # Results class CompetitionResult(BetfairModel): - competition = ModelType(Competition) market_count = IntType() competition_region = StringType() @@ -236,13 +237,11 @@ class CountryCodeResult(BetfairModel): class EventResult(BetfairModel): - event = ModelType(Event) market_count = IntType() class EventTypeResult(BetfairModel): - event_type = ModelType(EventType) market_count = IntType() @@ -253,7 +252,6 @@ class MarketTypeResult(BetfairModel): class TimeRangeResult(BetfairModel): - time_range = ModelType(TimeRange) market_count = IntType() @@ -350,7 +348,6 @@ class ClearedOrderSummary(BetfairModel): size_settled = FloatType() profit = FloatType() size_cancelled = FloatType() - bet_outcome = StringType() class ClearedOrderSummaryReport(BetfairModel): @@ -480,3 +477,45 @@ class CurrencyRate(BetfairModel): class TransferResponse(BetfairModel): transaction_id = StringType() + + +# Score + +class ScoreEvent(BetfairModel): + event_id = IntType() + event_status = StringType() + event_type_id = IntType() + + +class ScoreContext(BetfairModel): + event_time = StringType() + last_updated = DateTimeType() + update_sequence = IntType() + update_type = StringType() + + +class Score(BetfairModel): + event_id = IntType() + event_type_id = IntType() + update_context = ModelType(ScoreContext) + values = DictType(StringType) + event_status = EnumType(constants.EventStatus, required=True) + + +class UpdateContext(BetfairModel): + event_type = StringType() + last_updated = DateTimeType() + update_sequence = IntType() + update_type = StringType() + + +class Incident(BetfairModel): + update_context = ModelType(UpdateContext) + values = DictType(StringType) + + +class Incidents(BetfairModel): + event_id = IntType() + event_type_id = IntType() + event_status = EnumType(constants.EventStatus, required=True) + incidents = ListType(ModelType(Incident)) From d6b94c9605c7782de1bd7667fa793e77323340c7 Mon Sep 17 00:00:00 2001 From: richard jeffries Date: Mon, 3 Apr 2017 07:23:17 +0100 Subject: [PATCH 2/5] tidying up betfair.py --- betfair/betfair.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/betfair/betfair.py b/betfair/betfair.py index db48372..15cc2e6 100644 --- a/betfair/betfair.py +++ b/betfair/betfair.py @@ -14,7 +14,7 @@ from betfair import utils from betfair import models from betfair import exceptions -from betfair.models import ScoreEvent, Score, Incident, Incidents +from betfair.models import ScoreEvent, Score, Incidents IDENTITY_URLS = collections.defaultdict( lambda: 'https://identitysso.betfair.com/api/', @@ -545,7 +545,7 @@ def list_scores(self, update_keys): :param List update_keys: Keys to update """ update_keys = [{'eventId': k} for k in update_keys] - del k # Stop k from propagiting as part of locals() + del k # Stop k from propagiting as part of locals() return self.make_api_request( 'Scores', 'listScores', @@ -577,7 +577,7 @@ def list_incidents(self, update_keys): :param List update_keys: list of keys to get incidents for """ update_keys = [{'eventId': k} for k in update_keys] - del k # Stop k from propagiting as part of locals() + del k # Stop k from propagiting as part of locals() return self.make_api_request( 'Scores', 'listIncidents', From 7b76e30d9e6bee37f65770184bbd49c80b65486d Mon Sep 17 00:00:00 2001 From: richard jeffries Date: Mon, 3 Apr 2017 07:26:42 +0100 Subject: [PATCH 3/5] fixing unit tests --- betfair/betfair.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/betfair/betfair.py b/betfair/betfair.py index 15cc2e6..2b1954e 100644 --- a/betfair/betfair.py +++ b/betfair/betfair.py @@ -544,6 +544,7 @@ def list_scores(self, update_keys): :param List update_keys: Keys to update """ + k = None update_keys = [{'eventId': k} for k in update_keys] del k # Stop k from propagiting as part of locals() return self.make_api_request( @@ -576,6 +577,7 @@ def list_incidents(self, update_keys): :param List update_keys: list of keys to get incidents for """ + k = None update_keys = [{'eventId': k} for k in update_keys] del k # Stop k from propagiting as part of locals() return self.make_api_request( From 2e454034c793db5fa5f05ee2cf6ed130c2708b26 Mon Sep 17 00:00:00 2001 From: richard jeffries Date: Mon, 3 Apr 2017 07:40:37 +0100 Subject: [PATCH 4/5] fixing unit tests --- betfair/betfair.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/betfair/betfair.py b/betfair/betfair.py index 2b1954e..205781a 100644 --- a/betfair/betfair.py +++ b/betfair/betfair.py @@ -544,8 +544,8 @@ def list_scores(self, update_keys): :param List update_keys: Keys to update """ - k = None update_keys = [{'eventId': k} for k in update_keys] + k = None del k # Stop k from propagiting as part of locals() return self.make_api_request( 'Scores', @@ -577,8 +577,8 @@ def list_incidents(self, update_keys): :param List update_keys: list of keys to get incidents for """ - k = None update_keys = [{'eventId': k} for k in update_keys] + k = None del k # Stop k from propagiting as part of locals() return self.make_api_request( 'Scores', From ccdefe46a904d69975ab1d1612072f42ec02f5fa Mon Sep 17 00:00:00 2001 From: rjjeffries Date: Tue, 27 Jun 2017 10:01:32 +0100 Subject: [PATCH 5/5] Update models.py Fix for python 3.6 and latest schematics --- betfair/meta/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/betfair/meta/models.py b/betfair/meta/models.py index 08856bf..8ab19da 100644 --- a/betfair/meta/models.py +++ b/betfair/meta/models.py @@ -21,7 +21,7 @@ def __new__(meta, name, bases, attrs): class BetfairModel(six.with_metaclass(BetfairModelMeta, models.Model)): - def __init__(self, **data): + def __init__(self, *args, **data): super(BetfairModel, self).__init__() self.import_data(data)