Using VB2008 to acccess the Betfair API: A tutorial

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Vadim
    Junior Member
    • Mar 2009
    • 12

    #46
    "," and "."

    Thank you, indeed the whole problem of uses ",".

    Comment

    • elmariachi
      Junior Member
      • Feb 2009
      • 16

      #47
      Originally posted by Dodgee View Post
      Could it be some issue with the time conversion from the UTC timestamp? I tried experimenting with .toDate, changing it to Today.AddDays(2) but this also produced an error. I also tried requesting .EventDate in the call to GetAllMarkets and this just came back with 10am on whichever day I was calling it for every market, e.g. 12/03/2009 10:00:00.
      Same that goes for the double issue, applys for all serialization of locale dependent types (string / double / datetime).
      This could very well be the issue here. Ff your time is serialized in the above described format then the code should not be able to work since its expects a soap compliant datetime string.
      In the thread "calling getAllMarkets - is it ignoring toDate and fromDate?" james head pointed out:
      Originally posted by James Head
      2009-02-18T23:31:40:729Z
      is wrong it should be
      2009-02-18T23:31:40.729Z
      The last 4 characters represent the timezone. Im not sure which one is used here but there should be a definition of all timezones somewhere on the net

      Try and implement this format when serializing the datetime and let us know if this solves the issue.

      E

      Comment

      • HBBF
        Junior Member
        • Feb 2009
        • 4

        #48
        re:Mumbles0

        My god
        You are a genius
        Thank you very much


        Originally posted by Mumbles0 View Post
        You are not the first person to have a problem with the getMarketProfitAndLoss response. Betfair sure know how to test our understanding of objects!

        This problem arises because, for the .annotations array, we are expecting an array of type BFUK.ProfitAndLoss, but what we actually receive is an array of BFUK.MultiWinnerOddsLine. This is a class derived from BFUK.ProfitAndLoss and has an additional “ifLoss” property. This applies to both single-winner and multi-winner markets, even though the API Guide suggests otherwise. Just to make things even more complicated, Asian handicap markets return something different again.

        An easy way to fix this is to create a response object from the generic Object class (instead of BFUK.GetMarketProfitAndLossResp). This makes all returned properties accessible, regardless of the returned .annotations array type. For example:

        Code:
        Dim zReq As New BFUK.GetMarketProfitAndLossReq
        Dim zResp As New Object           '<<< This is different
        With zReq
          .header = oHeaderUK
          .marketID =  [i]Your  marketId of interest[/i]
        End With
        zResp = BetfairUK.getMarketProfitAndLoss(zReq)       'Call the API
        With zResp
          CheckHeader(.header)
          Print(.errorCode.ToString)
          If .errorCode = BFUK.GetMarketProfitAndLossErrorEnum.OK Then
            For i = 0 To UBound(.annotations)
              With .annotations(i)
                zPrice1(i).Value = .selectionName
                zPrice2(i).Value = .ifWin
                zPrice3(i).Value = .ifLoss
              End With
            Next
          End If
        End With
        Note that “Intellisense” does not operate for the zResp object - you must enter the property names correctly.

        Try this out. I hope it works.

        Comment

        • Dodgee
          Junior Member
          • Jan 2009
          • 17

          #49
          GetAllMarkets

          Originally posted by elmariachi View Post
          The last 4 characters represent the timezone. Im not sure which one is used here but there should be a definition of all timezones somewhere on the net

          Try and implement this format when serializing the datetime and let us know if this solves the issue.

          E
          UTC and timezone conversions always seem to cause problems! In the example from the other thread, the final 3 digits look more like milliseconds and I thought the Z would normally be substituted for the timezone offset (e.g. "+6"), with "Z" just meaning no offset. I don't know though, I could be completely wrong.

          I thought I might be able to do a bit more testing anyway now that I kind of know the format the API is expecting, so I tried just entering 2 strings for .fromDate and .toDate:

          Code:
          .fromDate = "2009-03-13T14:20:00.729Z"
          .toDate = "2009-03-14T14:20:00.729Z"
          This still caused the same error. When I debugged it and had a look at oMarketsReq, I noticed that in the actual request, fromDate and toDate look like this:

          fromDate #3/13/2009 2:20:00 PM#
          toDate #3/14/2009 2:20:00 PM#

          So I guess there's still some conversion going on before the request is sent, but I don't really know how to go about correcting this, if it does in fact need correcting!

          The other interesting thing is that the documentation says that .fromDate and .toDate are optional parameters, but if I omit them altogether it fails in the same way.

          Comment

          • gohawk
            Junior Member
            • Feb 2009
            • 4

            #50
            Please another one question
            How we call the api in some cases like this


            Dim Req As New betfair.UK.exchange.GetAccountFundsReq()
            Dim Resp As New betfair.UK.exchange.GetAccountFundsResp()
            With Req
            .header = oHeaderUK()
            End With


            '-------------------------------------------------------------
            Resp = betfair.UK.exchange.GetAccountFunds(Req)
            'GetAccountFunds is not avaiable
            -----------------------------------------------------------
            With Resp
            CheckHeader(.header)
            If .errorCode = betfair.UK.exchange.GetAccountFundsErrorEnum.OK Then
            lbl.Text = .availBalance
            End If
            End With

            Comment

            • Mumbles0
              Junior Member
              • Jan 2009
              • 240

              #51
              Reply to gohawk (Re: getAccountFunds)

              The line where you call the API is wrong.

              Code:
              Dim Req As New betfair.UK.exchange.GetAccountFundsReq()
              Dim Resp As New betfair.UK.exchange.GetAccountFundsResp()
              With Req
                .header = oHeaderUK()
              End With
              
              '-------------------------------------------------------------
              'Resp = betfair.UK.exchange.GetAccountFunds(Req)   <<  Incorrect!
              'GetAccountFunds is not avaiable
              
              Resp = BetFairUK.getAccountFunds(Req)       '<<< Should be this
              
              '-----------------------------------------------------------
              With Resp
                CheckHeader(.header)
                If .errorCode = betfair.UK.exchange.GetAccountFundsErrorEnum.OK Then
                  lbl.Text = .availBalance
                End If
              End With
              I think you are confusing the namespace (betfair.UK.exchange) with the service object (BetFairUK). Elsewhere in your project you should have something like:

              Private BetFairUK As New betfair.UK.exchange.BFExchangeService

              Comment

              • gohawk
                Junior Member
                • Feb 2009
                • 4

                #52
                Originally posted by Mumbles0 View Post
                I think you are confusing the namespace (betfair.UK.exchange) with the service object (BetFairUK).
                Private BetFairUK As New betfair.UK.exchange.BFExchangeService
                Thank you very much
                You have right
                its so stupid my mistake

                Comment

                • Mumbles0
                  Junior Member
                  • Jan 2009
                  • 240

                  #53
                  Reply to Dodgee (Re: getAllMarkets)

                  Please post more information regarding the deserialization error. If the Exception Assistant is displayed, what is the full message? Under “Actions” click “Copy exception detail to the clipboard”. Paste this into your post.

                  Also (if you don’t mind me asking), what country are you calling from?

                  In Control Panel select “Regional and Language Options”. What region is selected under “Standards and Formats”?

                  Comment

                  • Dodgee
                    Junior Member
                    • Jan 2009
                    • 17

                    #54
                    Success

                    Aha, sorted.

                    Thanks Mumbles; I was trying to find a way of seeing the full exception information and once you gave me a pointer I was able to solve the problem.

                    What was actually happening was that at certain times, the string returned was too long, causing the following exception:

                    -------------------------------------------------
                    InnerException: System.Xml.XmlException
                    LineNumber=0
                    LinePosition=0
                    Message="The maximum string content length quota (8192) has been exceeded while reading XML data. This quota may be increased by changing the MaxStringContentLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 2, position 13185."
                    Source="System.Runtime.Serialization"
                    -------------------------------------------------

                    This makes sense; earlier in the day I guess there would be more markets available over the next 24 hours and therefore the string would be longer.

                    I searched the solution for references to maxStringContentLength and doubled it from 8192 to 16384. It now works like a charm.

                    Probably doesn't make too much difference now but for reference, I'm calling from the UK.

                    Comment

                    • xManx
                      Junior Member
                      • Mar 2009
                      • 2

                      #55
                      Dim oHeaderGL As New BFGlobal.APIRequestHeader ' OK
                      Dim BetfairGL As New BFGlobal.BFGlobalService ' "New" may not be used in the interface

                      What is wrong?

                      if cod
                      Dim BetfairGL As BFGlobal.BFGlobalService

                      Then error

                      Dim oLoginReq As New BFGlobal.LoginReq
                      Dim oLoginResp As New BFGlobal.LoginResp
                      With oLoginReq
                      .username = "YourUsername"
                      .password = "YourPassword"
                      .productId = 82 'For free API
                      End With
                      oLoginResp = BetfairGL.login(oLoginReq) 'Error 1 Can not convert type "BetfairX.BFGlobal.LoginReq" in "BetfairX.BFGlobal.loginIn".



                      <System.ServiceModel.OperationContractAttribute(Ac tion:="login", ReplyAction:="*"), _
                      System.ServiceModel.XmlSerializerFormatAttribute() , _
                      System.ServiceModel.ServiceKnownTypeAttribute(GetT ype(APIRequest)), _
                      System.ServiceModel.ServiceKnownTypeAttribute(GetT ype(APIResponse))> _
                      Function login(ByVal request As BFGlobal.loginIn) As <System.ServiceModel.MessageParameterAttribute(Nam e:="Result")> BFGlobal.loginOut

                      <System.ServiceModel.OperationContractAttribute(Ac tion:="logout", ReplyAction:="*"), _
                      System.ServiceModel.XmlSerializerFormatAttribute() , _
                      System.ServiceModel.ServiceKnownTypeAttribute(GetT ype(APIRequest)), _
                      System.ServiceModel.ServiceKnownTypeAttribute(GetT ype(APIResponse))> _
                      Function logout(ByVal request As BFGlobal.LogoutReq) As <System.ServiceModel.MessageParameterAttribute(Nam e:="Result")> BFGlobal.LogoutResp


                      Why is the description of
                      Function logout(ByVal request As BFGlobal.LogoutReq) 'OK
                      Function login(ByVal request As BFGlobal.loginIn) ???

                      Comment

                      • Mumbles0
                        Junior Member
                        • Jan 2009
                        • 240

                        #56
                        Reply to xManx...

                        What have you done ??? I think in Step 2 you have created a Service Reference instead of a Web Reference. In Solution Explorer you will probably see “BFGlobal” under the “Service References” folder. Right-click on this and Delete.

                        Commence Step 2 again. When you click on the “Add Service Reference” menu item the “Add Service Reference” form is displayed. Do not type in anything here! Only click the “Advanced” button. This displays the “Service Reference Settings” form. Only click the “Add Web Reference...” button. This displays the “Add Web Reference” form. Now you can enter the URL and proceed as per Step 2. (If you get a “Security Information” message box, answer “Yes”.) If it has worked, “BFGlobal” should now appear under the “Web References” folder.

                        The Service Reference is a .NET 3.5 feature and I don’t really know what its advantages are. It is possible to access the Betfair API using a Service Reference instead of a Web Reference, but this introduces an extra level of complexity which we don’t need at this stage. There are also some other reasons we don’t go this way. In this tutorial we stay with the .NET 2.0 Web Reference.

                        Comment

                        • xManx
                          Junior Member
                          • Mar 2009
                          • 2

                          #57
                          Thank you very much, all works! :-)

                          Error was in the wrong choice. After selecting the "Web Reference" all OK!

                          Comment

                          • Vadim
                            Junior Member
                            • Mar 2009
                            • 12

                            #58
                            I can not unpack GetMarketPricesCompressed. Made to suit GetCompleteMarketPricesCompresed. Suggest the idea?
                            Last edited by Vadim; 17-03-2009, 07:47 PM.

                            Comment

                            • Mumbles0
                              Junior Member
                              • Jan 2009
                              • 240

                              #59
                              Reply to Vadim (re: getMarketPricesCompressed).

                              Yes. The unpacking classes for the “compressed” services must be “tailor-made” for each response string. The classes must match the different data structures, so for getMarketPricesCompressed we need some new classes. We should be able to re-use Class RemovedRunnerType and Function NextField.

                              I will put on a step soon showing how to unpack getMarketPricesCompressed.

                              Comment

                              • Mumbles0
                                Junior Member
                                • Jan 2009
                                • 240

                                #60
                                Step 14. Calling getMarketPricesCompressed.

                                getMarketPricesCompressed is a compressed version of the getMarketPrices service. These services return (almost) identical data. (Note the .asianLineId parameter is missing in getMarketPricesCompressed.). Providing you have the resources to unpack the string, and if you can get by without .asianLineId, getMarketPricesCompressed is a better call to make because its response is short and it can be called 60/min with the free API (only 10/min for getMarketPrices). Both calls return the best 3 available back prices and the best 3 available lay prices with amounts available. This is similar to what is shown on Betfair’s website for the market.

                                On the other hand, getCompleteMarketPricesCompressed which we looked at in step 10 returns the entire set of prices & amounts available for back and lay. Accordingly its response is larger. It also returns fewer market parameters (note that the handy .marketStatus parameter is not returned).

                                For this exercise we will unpack the .marketPrices string returned in the getMarketPricesCompressed response. In .NET terminology the unpacking process is called deserialization. Perhaps we should be using this term.

                                Add the following class to Module Unpack2:

                                Code:
                                Class UnpackMarketPricesCompressed
                                  Inherits BFUK.MarketPrices
                                  Private Const ColonCode = "&%^@"  'The substitute code for "\:"
                                
                                  Sub New(ByVal MarketPrices As String)      'Unpack the string
                                    Dim Mprices, Part, Field As String(), n As Integer
                                
                                    Mprices = MarketPrices.Replace("\:", ColonCode).Split(":")    'Split header and runner data
                                    Field = Mprices(0).Replace("\~", "-").Split("~")     'Split market data fields
                                    marketId = Field(0)     'Assign the market data
                                    currencyCode = Field(1)
                                    marketStatus = [Enum].Parse(GetType(BFUK.MarketStatusEnum), Field(2), True)
                                    delay = Field(3)
                                    numberOfWinners = Field(4)
                                    marketInfo = Field(5).Replace(ColonCode, ":")
                                    discountAllowed = (Field(6).ToLower = "true")
                                    marketBaseRate = Val(Field(7))
                                    lastRefresh = Field(8)
                                    removedRunners = Field(9).Replace(ColonCode, ":")
                                    bspMarket = (Field(10) = "Y")
                                
                                    n = UBound(Mprices) - 1
                                    ReDim runnerPrices(n)
                                    For i = 0 To n    'For each runner
                                      Part = Mprices(i + 1).Split("|")    'Split runner string into 3 parts
                                      Field = Part(0).Split("~")         'Split runner data fields
                                      runnerPrices(i) = New BFUK.RunnerPrices
                                      With runnerPrices(i)     'Assign the runner data
                                        .selectionId = Field(0)
                                        .sortOrder = Field(1)
                                        .totalAmountMatched = Val(Field(2))
                                        .lastPriceMatched = Val(Field(3))
                                        .handicap = Val(Field(4))
                                        .reductionFactor = Val(Field(5))
                                        .vacant = (Field(6).ToLower = "true")
                                        .farBSP = Val(Field(7))
                                        .nearBSP = Val(Field(8))
                                        .actualBSP = Val(Field(9))
                                        .bestPricesToBack = Prices(Part(1))
                                        .bestPricesToLay = Prices(Part(2))
                                      End With
                                    Next
                                  End Sub
                                
                                  Private Function Prices(ByVal PriceString As String) As BFUK.Price()
                                    Dim Field As String(), Price As BFUK.Price(), k, m As Integer
                                
                                    Field = PriceString.Split("~")  'Split price fields
                                    m = UBound(Field) \ 4 - 1  'm = number of prices - 1 
                                    ReDim Price(m)
                                    For i = 0 To m
                                      Price(i) = New BFUK.Price
                                      With Price(i)
                                        .price = Val(Field(k + 0))  'Assign price data
                                        .amountAvailable = Val(Field(k + 1))
                                        .betType = [Enum].Parse(GetType(BFUK.BetTypeEnum), Field(k + 2), True)
                                        .depth = Field(k + 3)
                                        k += 4
                                      End With
                                    Next
                                    Return Price  'Return the array of prices
                                  End Function
                                
                                End Class
                                This class performs the deserialization. The Inherits statement gives this class the same properties as class BFUK.MarketPrices. We also use the existing classes BFUK.RunnerPrice and BFUK.Price. This makes an object created from this class identical to the .runnerPrices object returned by the getMarketPrices service.

                                When an instance of this class is created, Sub New deserializes the MarketPrices string. Its operation is similar to that described in Step 10 for getCompleteMarketPricesCompressed. The logic is slightly different to match the different data structure. As discussed in Step 6, if "\:" appears in the string it is not to be treated as a delimiter, so we replace it with ColonCode to avoid a possible problem.

                                The MarketPrices input string is split using ":" into the Mprices array. Mprices(0) is split using "~" into its component fields which are the market parameters (.marketId, .currencyCode, etc.). The remaining elements contain info for each runner. Each element is split into 3 parts using "|". Part(0) contains the runner info fields (.selectionId, .sortOrder, etc.) while Part(1) contains .bestPricesToBack and Part(2) contains .bestPricesToLay. These two price strings are split using "~" by Function Prices and the values are loaded into the appropriate arrays.

                                To test this code add yet another button to the TestForm. Name it “bGetMPricesComp” and change its text property to something like “MarketPrices”. Double-click it to create its event handler Sub bGetMPricesComp_Click. Add this code:

                                Code:
                                Private Sub bGetMPricesComp_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bGetMPricesComp.Click
                                  Print("*** MarketPricesCompressed ****")
                                  Dim oMPCreq As New BFUK.GetMarketPricesCompressedReq
                                  With oMPCreq
                                    .header = oHeaderUK()
                                    .marketId = [i]YourMarketIdOfInterest[/i]   'an active market ID
                                  End With
                                  StateCount += 1
                                  BetFairUK.getMarketPricesCompressedAsync(oMPCreq, StateCount)  'Call the API
                                End Sub
                                Use a valid market ID for a currently active market. Because a typical app will call this fairly frequently we make this an async call which requires an event handler. In the Class Name box select “BetFairUK”. In the Method Name box click on “getMarketPricesCompressedCompleted”. Add the following code to the event handler:

                                Code:
                                Private Sub BetFairUK_getMarketPricesCompressedCompleted(ByVal sender As Object, ByVal e As BFUK.getMarketPricesCompressedCompletedEventArgs) Handles BetFairUK.getMarketPricesCompressedCompleted
                                  Try
                                    If Not e.Cancelled Then
                                      With e.Result
                                        CheckHeader(.header)
                                        Print("ErrorCode = " & .errorCode.ToString)
                                        If .errorCode = BFUK.GetMarketPricesErrorEnum.OK Then
                                          Dim oMarketPrices As New UnpackMarketPricesCompressed(.marketPrices)
                                          With oMarketPrices
                                            Print(.marketId & " " & .marketStatus.ToString & " " & .runnerPrices.Length & " runners")
                                            'Process returned market prices here.
                                            For i = 0 To .runnerPrices.Length - 1
                                              With .runnerPrices(i)
                                                Print(.sortOrder & " " & .selectionId & " " & .totalAmountMatched & " " & .lastPriceMatched)
                                              End With
                                            Next
                                          End With
                                        End If
                                      End With
                                    End If
                                  Catch ex As ApplicationException
                                    Print(e.Error.Message)
                                  End Try
                                End Sub
                                Now test the code. If all is OK the oMarketPrices object contains the deserialized data. We print a few parameters to test its operation.
                                Last edited by Mumbles0; 11-04-2009, 03:08 PM. Reason: Code revised to use String.Split method

                                Comment

                                Working...
                                X