Using VB2008 to acccess the Betfair API: A tutorial

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Muqbil
    Junior Member
    • Jan 2009
    • 4

    #16
    Love this article The extration code makes my own look rather crude, it did work but with quite a few more lines of code!! Your new routine is now my defacto standard for unpacking the compressed calls...


    A question that will probably appear rather trivial to most!

    1) What are the implications of firing off several async requests using the same session token? ie; 3+ api calls all using the current (correct at the time of submission) token? Is it possible the first call would be turned around with a new token before the later requests with the now old token have been received?

    Comment

    • Muqbil
      Junior Member
      • Jan 2009
      • 4

      #17
      One downside I see with the async call is timeout?

      If handling the normal non async calls using threads it is possible to timeout the thread doing the call.

      How are timeouts handled in the real betfair api world by the commercial / pro developers? Anyone willing to share....?

      Comment

      • mmmDotNet
        Junior Member
        • Feb 2009
        • 1

        #18
        I'm not a pro betfair developer, but I would use the standard WCF timeouts which you can specify on the <binding> element in App.config. You can then catch any TimeoutExceptions thrown by your calls to the client object. I also use Spring.NETs AoP framework to execute my retry strategy.

        These links might help...

        http://msdn.microsoft.com/en-us/library/ms731361.aspx
        http://msdn.microsoft.com/en-us/library/ms735103.aspx

        Putting the call on a seperate thread will not break your timeout logic. Any exceptions generated on the delegate just get rethrown when you call EndInvoke() in the callback.

        Comment

        • Mumbles0
          Junior Member
          • Jan 2009
          • 240

          #19
          Reply to Muqbil ...

          Thanks for the kind words.

          1. Re: Session token change.

          A session token is not expected to change very often - perhaps every day or so. I think you can send bursts of async calls with confidence. However, your app should cater for the possibility of a session token change.

          In the unlikely event that the API changes the session token, and you send a new request before you receive a response from a previous request informing you of this change, you should get the NO_SESSION response header error. Your code can detect this and take appropriate action, for example, resend the failed request.

          Even if this occurs, it is a transient problem which will quickly go away. All requests sent after the new session token is received should succeed.

          2. Re: Async call timeout.

          You have raised a good point. The “Timeout” property of the service object controls the timeout period for sync calls only. Unfortunately, this has no effect on async calls.

          This makes a good case for using sync calls on separate threads. I am now scratching my head and considering mmmDotNet’s suggestions.
          Last edited by Mumbles0; 25-02-2009, 08:48 AM.

          Comment

          • vossie
            BDP Team
            • Sep 2008
            • 40

            #20
            Originally posted by Mumbles0 View Post
            Thanks for the kind words.
            2. Re: Async call timeout.

            You have raised a good point. The “Timeout” property of the service object controls the timeout period for sync calls. Unfortunately, this has no effect on async calls.

            This makes a good case for using sync calls on separate threads. I am now scratching my head and considering mmmDotNet’s suggestions.
            I'm thinking out loud, tell me if I am losing it but...

            The easiest would be to store the IAsyncResult that you get when you fire the asynchronous method to a Dictionary where the key is DateTime.Now and the value the IAsyncResult object. use a timer to iterate over this lot at 200ms intervals any item with a time span of greater than x that Is not Completed you can manually force end on using a factory and the get type method.
            "Why don't you just fix your little problem and light this candle?" - Alan B. Shepard, Jr

            Comment

            • Oldb
              Junior Member
              • Feb 2009
              • 6

              #21
              VB Express 2008 Sample Runner.name

              Marvelous help in using the API.

              The code is excellent and very easy to implement.

              Thanks very much for offering this help, it has made a big difference.

              I would be very interested in implementing one small other item, i have tried but do not seem to be able to complete.

              I am access Horse Racing and need to get the horse number and name of the runners..........
              ie. 1. Wild Pride 3. Getty II
              I can see there is a Class Runner with .name

              This is the area i need the info

              For i = 0 To .runnerPrices.Length - 1

              With .runnerPrices(i)

              ???? ' For Each runner As BFAU.Runner In market.runners ????


              Would really appreciate the code to implement this

              Regards Robert

              Comment

              • Mumbles0
                Junior Member
                • Jan 2009
                • 240

                #22
                Reply to Oldb ...

                Robert,

                What you require is a call to the “getMarket” service. I have added a new Step 12 to show you how to do this. The response object contains the array of runners. I think this is what you’re looking for.

                Comment

                • Mumbles0
                  Junior Member
                  • Jan 2009
                  • 240

                  #23
                  Step 12. Getting a List of Runners.

                  Once you know the marketId for your market of interest, you can call the .getMarket service to obtain information pertaining to this market. Of particular interest is the list of runners. From this list you can get the selectionId for your runner of interest, required when placing a bet.

                  To demonstrate this call, add another button to our Betfair Tester as we’ve done in previous steps. Rename this button to “bRunners” and change its Text property to “Runners”. Add the following code to Sub bRunners_Click:

                  Code:
                  Private Sub bRunners_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bRunners.Click
                    Print("*** Runners ***")
                    Dim oMarketReq As New BFUK.GetMarketReq       'Create the request object
                    Dim oMarketResp As BFUK.GetMarketResp     'Create a varaible for the response object
                    With oMarketReq
                      .header = oHeaderUK()
                      .marketId = [i]Your marketId of interest[/i]
                    End With
                    oMarketResp = BetFairUK.getMarket(oMarketReq)    'Call the API
                  
                    With oMarketResp                      'Process the response
                      CheckHeader(.header)
                      Print("ErrorCode = " & .errorCode.ToString)
                      If .errorCode = BFUK.GetMarketErrorEnum.OK Then
                        With .market
                          Print("MarketId = " & .marketId)    'Print id, name and status
                          Print("Name = " & .name)
                          Print("Status = " & .marketStatus.ToString)
                          For i = 0 To .runners.Length - 1
                            With .runners(i)
                              Print(.selectionId & "  " & .name)   'Print list of runners
                            End With
                          Next
                        End With
                      End If
                    End With
                  End Sub
                  This is a standard (sync) call and follows the procedure shown in Step 4. After the API call is made the response object (oMarketResp) contains a lot of information regarding the market (as detailed in the API Guide). For the exercise we print .marketId, .name and .marketStatus. Because the runners array lies deep within the oMarketResponse object, we use nested With statements to dig this out. Each runner’s .name and .selectionId is printed in a For-Next loop. You can print other data if you wish.

                  Notice how the structure of oMarketResp corresponds to the structure of the output data shown in the API Guide for the getMarket call.


                  Relating data in other arrays to the list of runners

                  This is the result I obtained for a typical GB horserace market:

                  *** Runners ***
                  HeaderCode = OK
                  ErrorCode = OK
                  MarketId = 100392990
                  Name = 7f Hcap
                  Status = ACTIVE
                  3275914 Emirates World
                  3106868 Markyg
                  3082129 Auld Arty
                  2933155 Grand Honour
                  3013615 Rio Royale
                  2986876 Asian Tale
                  3174052 Give Us A Song

                  For these runners you will be interested getting data from other API calls, such as getMarketPrices. A word of warning: The order of arrays containing runner data obtained from other calls does not necessarily match the order of this list. For example, suppose that you wanted to know prices for Rio Royale (element 4 in the .runners array). You call getMarketPrices to return the array of runnerPrices (as in Step 7). Do not assume that element 4 in .runnerPrices always contains the prices for Rio Royale.

                  To get direct correspondence you must first sort the .runnerPrices array in order of .sortOrder value.

                  Alternatively you can “look up” the .runnerPrices array to find a matching selectionId as shown in this code example:

                  Code:
                  With MpriceResp.marketPrices
                    For i = 0 To .runnerPrices.Length - 1    'Look up the runnerPrices array
                      With .runnerPrices(i)
                        If .selectionId = 3013615 Then    'selectionId match
                  
                           'The price data for Rio Royale is available here
                  
                          Exit For
                        End If
                      End With
                    Next
                  End With
                  When the selectionIds match you can be certain that you have found the data for the selected runner.
                  Last edited by Mumbles0; 21-06-2009, 03:26 AM. Reason: Minor change

                  Comment

                  • Oldb
                    Junior Member
                    • Feb 2009
                    • 6

                    #24
                    Originally posted by Mumbles0 View Post
                    Robert,

                    What you require is a call to the “getMarket” service. I have added a new Step 12 to show you how to do this. The response object contains the array of runners. I think this is what you’re looking for.
                    Many thanks.. it is exactly what i am after.
                    Once again.. really appreciate the help.. worked 100% 1st time...wish i had your knowledge !!!

                    Comment

                    • Flow
                      Junior Member
                      • Feb 2009
                      • 2

                      #25
                      cancelBetsByMarket

                      Hello,

                      I've an error when I try to cancel all bets on a market.
                      The market ID is needed in input via an array of markets but wich class is to use ?

                      For updateBets, it's an array of bets via the class UpdateBets.
                      I try with market class without success.

                      Could you help me please ?

                      Comment

                      • Mumbles0
                        Junior Member
                        • Jan 2009
                        • 240

                        #26
                        Reply to Flow ...

                        CancelBetsByMarket can handle several markets with one call. For this reason you put your marketId(s) in the .markets array. This is a simple array of type Integer - it does not require any special class. An example of how to set up the request object:

                        Code:
                        Dim oCancelBBMreq As New BFUK.CancelBetsByMarketReq
                        With oCancelBBMreq
                          .header = oHeaderUK()
                          ReDim .markets(0)                      'Set up the array    
                          .markets(0) = [i]Your marketId Of Interest[/i]    'Put your marketId in the array
                        End With
                        This example shows how to input a single marketId. Note that the “0” in the Redim statement specifies the upper bound of the array. Because array elements start at zero, the .markets array has only one element (.markets(0)).

                        If you wanted to cancel all bets on, say, 3 markets, you could do this:

                        Code:
                        ReDim .markets(2)
                        .markets(0) = [i]marketId1[/i]
                        .markets(1) = [i]marketId2[/i]
                        .markets(2) = [i]marketId3[/i]
                        I use the Redim statement to set up the array because I got used to it with VB6. There are other ways of doing it.
                        Last edited by Mumbles0; 25-02-2009, 11:55 PM.

                        Comment

                        • Flow
                          Junior Member
                          • Feb 2009
                          • 2

                          #27
                          Arf ... a simple array
                          I think too hard

                          Thanks !

                          Comment

                          • Mumbles0
                            Junior Member
                            • Jan 2009
                            • 240

                            #28
                            Step 13. Enabling gzip Compression

                            If requested, the API will compress responses. The compression method used is gzip. This produces smaller response files which take less time to send over the Internet. Because compression acts to reduce your overall API call time, it is worth considering.

                            According to the documentation, to request compression you simply set the “EnableDecompression” property of the service object to “True”. But when I tried this it did not work! Using a network analyser, I observed that the length of the response (in bytes) remained the same irrespective of the value of EnableDecompression. To track down this problem I commenced a lengthy investigation.

                            When we make an API request, .NET generates a Soap request preceded by an HTTP header which looks something like this:


                            Code:
                            POST https://api.betfair.com/global/v3/BFGlobalService HTTP/1.1
                            User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.3053)
                            Content-Type: text/xml; charset=UTF-8
                            SOAPAction: "getActiveEventTypes"
                            Host: api.betfair.com
                            Content-Length: 550
                            Expect: 100-continue
                            Connection: Keep-Alive
                            Accept-Encoding: gzip
                            
                            [i]The SOAP request goes here...[/i]
                            The “Accept-Endoding: gzip” field makes the request for compression. When present, the server should send back a compressed response. The EnableDecompression property controls whether this field is present or not.

                            I “fiddled” with this header and, after much trial and error, I noticed that the server would inhibit gzip compression if the default User-Agent value (shown in the above example) was present. I can’t think of any good reason for this and I suspect it is a bug within the API server. When I removed the User-Agent field, gzip worked.

                            So, to enable gzip response compression on the Betfair tester, add extra code into Sub TestForm_Load:

                            Code:
                            Private Sub TestForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
                              oHeaderGL.sessionToken = My.Computer.FileSystem.ReadAllText(SessTokFile)      '(from Step 3)
                              BetFairGL.UserAgent = ""      'Remove User-Agent field
                              BetFairUK.UserAgent = ""
                              BetFairGL.EnableDecompression = True    'Request compression
                              BetFairUK.EnableDecompression = True
                            End Sub
                            Here we set the UserAgent string value to null to remove the corresponding field from the HTTP header. Now run the project and test some of the API calls. Using a network analyser you can verify that compression is working (I used Wireshark for this purpose). Note that because all data transfers between the project and the API are encrypted, you will not be able to examine the source data. However you should be able to see the byte length of both request and response.

                            This table shows typical effect of compression on response length (bytes):

                            Code:
                            [u]API call[/u]             [u]Without gzip[/u]     [u]With gzip[/u]     [u]Reduction factor[/u]
                            
                            getActiveEventTypes	 8423		1292		 6.5
                            getAllMarkets		17023		3335		 5.1
                            getMarketPrices		33840		3352		10.1
                            These results clearly demonstrate how effective gzip is. It is certainly worth enabling!

                            Note:

                            Since writing this post I have noticed that the problem with the User-Agent field has gone away. Gzip compression can now be enabled regardless of the value of the UserAgent string. Note that no harm is done if UserAgent remains set to null "". (This makes the request a bit shorter.)
                            Last edited by Mumbles0; 17-04-2009, 09:21 AM. Reason: Footnote added

                            Comment

                            • Monairda
                              Junior Member
                              • Jan 2009
                              • 32

                              #29
                              Good morning,
                              I am a newbie and this thread has helped a lot.
                              I would like to know how we could obtain the information of the horse, age, shape, days after the last race ...
                              Thanks

                              Comment

                              • HBBF
                                Junior Member
                                • Feb 2009
                                • 4

                                #30
                                Why do occasionally make mistakes?

                                Why do occasionally make mistakes?


                                Private Sub bPrices_Click() Handles Lay.Click
                                Dim zPriceaceBetsReq As New BFUK.PlaceBetsReq
                                Dim zPriceaceBetsResp As New BFUK.PlaceBetsResp
                                Dim zBets(0 To 0) As BFUK.PlaceBets
                                For i = 0 To 0
                                With zBets(i)
                                .asianLineId = 0
                                .betCategoryType = BFUK.BetCategoryTypeEnum.E
                                .betPersistenceType = BFUK.BetPersistenceTypeEnum.NONE
                                .betType = BFUK.BetTypeEnum.L
                                .bspLiability = 0
                                .marketId = 103852415
                                .selectionId = 512453
                                .price = 2.04
                                .size = 50
                                End With
                                Next
                                With zPriceaceBetsReq
                                .header = oHeaderUK()
                                .bets = zBets
                                End With
                                zPriceaceBetsResp = BetFairUK.placeBets(zPriceaceBetsReq)
                                End Sub
                                Last edited by HBBF; 28-02-2009, 04:34 PM.

                                Comment

                                Working...
                                X