def ParsePlaceOrdersNoReceipt(resp, olist): """Return list of order objects.""" _check_errors(resp) tstamp = resp.Timestamp # list of order refs - I am presuming BDAQ returns them in the order # the orders were given! orefs = resp.OrderHandles.OrderHandle # create and return order object. Note we set status to UNMATCHED, # and unmatched stake and matched stake accordingly. allorders = {} for (o, ref) in zip(olist, orefs): allorders[ref] = order.Order( const.BDAQID, o.sid, o.stake, o.price, o.polarity, **{ 'oref': ref, 'mid': o.mid, 'status': order.UNMATCHED, 'matchedstake': 0.0, 'unmatchedstake': o.stake, # we will write t placed to the DB 'tplaced': tstamp, 'tupdated': tstamp }) return allorders
def ParsegetMUBets(res, odict): # here we override checking of errors, since the BF API returns an # error if no results are returned, but from our perspective there # is no problem with this, we just return an empty dict. if res.errorCode == 'NO_RESULTS': return {} _check_errors(res) #if len(res.bets.MUBet) != len(odict): # betlog.betlog.debug('Got {0} results for updating {1} orders'\ # .format(len(res.bets.MUBet), len(odict))) # print res.bets.MUBet # print odict #print res.bets.MUBet # The following is slightly complicated, this is because the BF # API can return multiple orders with the same betid, (although # they will have a different transactionId). We will get this if a # bet has been 'partially' matched. From our perspective, this is # a single 'unmatched' bet. print 'odict from engine', odict print 'result', res # dictionary of orders we will return allorders = {} # first initialise each order as unmatched for oref in odict: o = odict[oref] idict = {'mid': o.mid, 'oref': oref, 'status' : order.UNMATCHED, 'matchedstake': 0.0, 'unmatchedstake': o.stake} allorders[oref] = order.Order(const.BFID, o.sid, o.stake, o.price, o.polarity, **idict) # go through each MUBet, and add the amount matched (if any) to # the appropriate order object. for r in res.bets.MUBet: if r.betStatus == 'M': # get the order in the order dict that has the matching id o = allorders[r.betId] # add matched amount o.matchedstake += r.size o.unmatchedstake -= r.size # go through all orders, and changed status of those with # matchedstake equal to original placed stake to order.MATCHED. for o in allorders.values(): # fp arithmetic ms = o.matchedstake s = o.stake #print o, ms, s if (ms > s - _EPS) and (ms < s + _EPS): o.status = order.MATCHED return allorders
def return_orders(self, sqlstr, sqlargs=None): """ Execute query sqlquery, which should return a list of Orders. This convenience function will convert the database representation to a list of Order objects. """ try: res = self.cursor.execute(sqlstr, sqlargs) except sqlite3.OperationalError: # query string was malformed somehow raise DbError, 'received malformed SQL statement' except ValueError: raise DbError, ('wrong parameters passed to SQL ' 'statement') odata = res.fetchall() # create Order objects from results of market data # pid and pname both None since we are not storing this info # in the database! # Note also we need to convert sqlite int into bool for # inrunning status of market. orders = [ order.Order( o[1], o[3], o[6], o[5], o[7], **{ 'oref': o[0], 'status': o[10], 'matchedstake': o[8], 'unmatchedstake': o[9], 'strategy': o[4], 'mid': o[2] }) for o in odata ] return orders
def ParseplaceBets(res, olist): # _check_errors only checks errors in the header and the footer, # we can have other errors that are returned in resultCode of each # (PlaceBetsResult) e.g resultCode = "INVALID_SIZE", and this even # if the main errorcode is "OK". This is a 'known issue' in the # BF API, as detailed by the documentation # BetfairSportsExchangeAPIReferenceGuidev6.pdf, p114. print res _check_errors(res) tstamp = res.header.timestamp # print 'parse place bets:' # print olist # print res # check that we have one result for each order executed if len(res.betResults.PlaceBetsResult) != len(olist): raise ApiError, ('did not receive the correct number' 'of results from PlaceBets') allorders = {} # go through all results in turn and add to allorders list for betres, o in zip(res.betResults.PlaceBetsResult, olist): # first check that the bet was ok, there can be many possible # errors here, e.g. INVALID_SIZE: see # BetfairSportsExchangeAPIReferenceGuidev6.pdf, p119 for the # full list. if betres.resultCode != "OK": # we don't want to raise an exception here since the other # orders could have gone through ok, so print a warning # and skip to next order order id. betlog.betlog.debug('Warning: order {0} returned result {1}'\ .format(o, betres.resultCode)) oref = betres.betId # check if we were matched matched = betres.sizeMatched if matched == o.stake: status = order.MATCHED else: # TODO: should we also have a part matched type? Probably. status = order.UNMATCHED odict = {'mid': o.mid, 'oref': oref, 'status': status, 'matchedstake': matched, 'unmatchedstake': o.stake - matched, 'tplaced' : tstamp, 'tupdated': tstamp} allorders[oref] = order.Order(const.BFID, o.sid, o.stake, o.price, o.polarity, **odict) return allorders
def store_opportunity(self, slay, sback, olay, oback, inst=False): """Store details of betting opportunity.""" self.opp = True self.slay = slay self.sback = sback self.olay = olay self.oback = oback self.instant = inst # Figure out how much we want to back and how much to lay. For # now let us bet the minimum amount possible. For this we # should note that BDAQ has a minimum bet of 0.5, and BF has a # minimum bet of 2.0. First the lay bet: if we are laying on # BDAQ, we want to lay the smallest amount possible such that # the matching back is for 2.0. If we are laying on BF, again # we want to lay the minimum amount possible such that the back # is at least 0.5. bstake, lstake = self.get_stakes(sback.exid, self.oback, slay.exid, self.olay) # create both back and lay orders. self.border = order.Order( sback.exid, sback.id, bstake, self.oback, 1, **{ 'mid': sback.mid, 'src': sback.src, 'wsn': sback.wsn, 'sname': sback.name }) self.lorder = order.Order( slay.exid, slay.id, lstake, self.olay, 2, **{ 'mid': slay.mid, 'src': slay.src, 'wsn': slay.wsn, 'sname': slay.name })
def ParseListOrdersChangedSince(resp): """Returns list of orders that have changed""" _check_errors(resp) tstamp = resp.Timestamp if not hasattr(resp, 'Orders'): # no orders have changed return {} # store the sequence numbers of the orders: we need to return the # maximum sequence number so that the next time we call the API # function we won't return this again! seqnums = [] allorders = {} for o in resp.Orders.Order: # From API docs, order _Status can be # 1 - Unmatched. Order has SOME amount available for matching. # 2 - Matched (but not settled). # 3 - Cancelled (at least some part of the order was unmatched). # 4 - Settled. # 5 - Void. # 6 - Suspended. At least some part unmatched but is suspended. # Note: at the moment we are not storing all of the data that # comes from the BDAQ API function, only the information that # seems useful... # example of a full order dict returned is: # { # _Polarity = 1 # _Status = 1 # _TotalForSideTakeStake = 0.0 # _TotalForSideMakeStake = 0.0 # _CancelOnInRunning = True # _SequenceNumber = 7857 # _SelectionId = 21412244 # _MatchedPrice = 0.0 # _MatchedStake = 0.0 # _MatchedAgainstStake = 0.0 # _CancelIfSelectionReset = False # _MarketId = 3886077 # _UnmatchedStake = 1.0 # _MakeCommissionRate = 5.0 # _PunterReferenceNumber = 0 # _TakeCommissionRate = 5.0 # _IssuedAt = 2014-03-01 12:12:44.563000 # _RequestedPrice = 6.6 # _IsCurrentlyInRunning = False # _Id = 2184011240 # _PunterCommissionBasis = 1 # } odict = { 'oref': o._Id, 'status': o._Status, 'mid': o._MarketId, 'matchedstake': o._MatchedStake, 'unmatchedstake': o._UnmatchedStake, 'tupdated': tstamp } allorders[o._Id] = order.Order(const.BDAQID, o._SelectionId, o._MatchedStake + o._UnmatchedStake, o._RequestedPrice, o._Polarity, **odict) # store sequence number seqnums.append(o._SequenceNumber) return allorders, max(seqnums)
def ParseListBootstrapOrders(resp): """ Parse a single order, return order object. Note there are a few things the Api is returning that we are ignoring here. """ _check_errors(resp) # no orders returned; end of bootstrapping process. if not hasattr(resp, 'Orders'): return {} # create and return list of order objects. allorders = {} for o in resp.Orders.Order: # The complete information returned by BDAQ for each order # looks like this: # { # _Polarity = 1 # _Status = 1 # _TotalForSideTakeStake = 0.0 # _TotalForSideMakeStake = 0.0 # _CancelOnInRunning = True # _SequenceNumber = 7857 # _SelectionId = 21412244 # _MatchedPrice = 0.0 # _MatchedStake = 0.0 # _MatchedAgainstStake = 0.0 # _CancelIfSelectionReset = False # _MarketId = 3886077 # _UnmatchedStake = 1.0 # _MakeCommissionRate = 5.0 # _PunterReferenceNumber = 0 # _TakeCommissionRate = 5.0 # _IssuedAt = 2014-03-01 12:12:44.563000 # _RequestedPrice = 6.6 # _IsCurrentlyInRunning = False # _Id = 2184011240 # _PunterCommissionBasis = 1 # OrderCommissionInformation = # (OrderCommissionInformationType){ # _OrderCommission = 0.0 # } # } sid = o._SelectionId mid = o._MarketId ustake = o._UnmatchedStake mstake = o._MatchedStake stake = ustake + mstake price = o._RequestedPrice pol = o._Polarity oref = o._Id status = o._Status allorders[oref] = order.Order( const.BDAQID, sid, stake, price, pol, **{ 'oref': oref, 'mid': mid, 'status': status, 'matchedstake': mstake, 'unmatchedstake': ustake }) return allorders
def create_orders(self): """Create both back and lay orders.""" # minimum stake at the moment exid = self.sel.exid # back and lay odds oback = self.sel.make_best_lay() olay = self.sel.make_best_back() if exid == const.BDAQID: # although minimum is 0.5, there are some difficulties # getting this to show up when calling prices from nonAPI # method, so lets go for 1 bstake = 1 else: # this will be 2, the minimum bet on BF bstake = _MINBETS[self.sel.exid] # we set the lay stake so that we are 'neutral' on whether the # selection pays out or not. This means laying a slightly # larger stake than the back stake - how much larger depends # on the difference in the odds. Note we are rounding (to # nearest penny) here, meaning we will typically win very # slightly more or less if the selection comes through versus # if it doesn't (see my notes). This isn't the only sensible # staking strategy, we can stake any value between bstake and # this current lstake. lstake = round(bstake * (1.0 + oback) / (1.0 + olay), 2) sel = self.sel # note we put cancelrunning = False here self.border = order.Order( sel.exid, sel.id, bstake, oback, 1, **{ 'mid': sel.mid, 'src': sel.src, 'wsn': sel.wsn, 'sname': sel.name, # note we set both # cancel running (for # BDAQ) and persistence # (for BF) 'cancelrunning': True, # warning: only some BF # markets allow 'IP' # persistence! (and # only some allow SP # persistence) 'persistence': 'NONE' }) self.lorder = order.Order( sel.exid, sel.id, lstake, olay, 2, **{ 'mid': sel.mid, 'src': sel.src, 'wsn': sel.wsn, 'sname': sel.name, 'cancelrunning': True, 'persistence': 'NONE' })
# testorders.py # an order dictionary that won't be matched from betman import order, const # BDAQ # six nations, england o1 = order.Order(1, 17676144, 0.5, 100.0, 1, **{'mid': 3291920}) # six nations, wales o2 = order.Order(1, 17676142, 0.5, 1.22, 2, **{'mid': 3291920}) # BF # six nations, england o3 = order.Order(1, 15593, 2.0, 10.0, 1, **{ 'mid': 109590806, 'persistence': 'NONE' }) # six nations, wales o4 = order.Order(2, 14118, 2.05, 10.0, 1, **{ 'mid': 109590806, 'persistence': 'NONE' }) d = {1: [], 2: []}