Using VB2008 to acccess the Betfair API: A tutorial

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

    #76
    Reply to willyray1 (Re TreeView)

    I can’t see exactly what you are getting at.... I thought the TreeView of Step 15 already did what you want. The events and markets returned by a prior getAllMarkets call will be shown in the TreeView. You restrict what is shown by assigning the eventTypeIds, countries, fromDate and toDate parameters to filter the markets. Try this code:

    Code:
    Private Sub bMarkets_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bMarkets.Click
      Print("*** All Markets ***")
      Dim oMarketsReq As New BFUK.GetAllMarketsReq
      oMarketsReq.header = oHeaderUK()
      With BetFairUK.getAllMarkets(oMarketsReq)  'Call the API
        CheckHeader(.header)
        Print("ErrorCode = " & .errorCode.ToString)
        If .errorCode = BFUK.GetAllMarketsErrorEnum.OK Then
          PopulateTreeView(New UnpackAllMarkets(.marketData), tvMarkets)
        End If
      End With
    End Sub
    Here getAllMarkets is called with no filter. All available Betfair markets are returned and displayed in the TreeView.
    (It a good idea to use the revised UnpackAllMarkets class for this.)

    Comment

    • willyray1
      Junior Member
      • Apr 2009
      • 9

      #77
      That is bizzare! When I've been running the code all that was loading was one event and their markets not the full list, must be cos it was filtering dates and markets. With your new code it's displaying everything that I was expecting so thanks for the UnpackAllMarkets tip. I was resorting to loading all the events, then appending the markets for the specific market to the loaded events.

      Comment

      • Mumbles0
        Junior Member
        • Jan 2009
        • 240

        #78
        Reply to Drifter (Re 1970)

        That clears that one up... I’m not sure I will be around in 2038. The API could be using a Long (64-bit) for the “milliseconds elapsed since ....” values. If so, the planet may not be around when that one runs out.

        Comment

        • Mumbles0
          Junior Member
          • Jan 2009
          • 240

          #79
          Revision to Steps 10 and 14 (Unpacking compressed market price strings)

          The unpacking classes, UnpackCompleteMarketPricesCompressed in step 10 and UnpackMarketPricesCompressed in step 14 have now been revised. They now use the String.Split method to separate the substrings (in a similar manner to the revised Step 6 (UnpackAllMarkets)).

          For a horse race market of 18 runners, UnpackCompleteMarketPricesCompressed now runs about 3 times faster while UnpackMarketPricesCompressed runs a bit faster. In both cases the code is a bit simpler.

          Note that some of the property names in UnpackCompleteMarketPricesCompressed have been changed to be more consistent with getMarketPrices (for example .orderIndex is now .sortOrder).

          Note also that we do not now unpack the .removedRunners string. This will be handled in a separate step. I will include this shortly.

          Comment

          • willyray1
            Junior Member
            • Apr 2009
            • 9

            #80
            Great work again Mumbles, minor typo:
            With runnerPrices(i)) 'Assign the runner data
            .selectionId = Field(0
            should be
            With runnerPrices(i) 'Assign the runner data
            .selectionId = Field(0)

            Comment

            • Mumbles0
              Junior Member
              • Jan 2009
              • 240

              #81
              Thanks wr1. Fixed.

              Comment

              • Dodgee
                Junior Member
                • Jan 2009
                • 17

                #82
                Processing market prices elsewhere...

                Probably a dumb question, but...

                I'm making use of the BetFairUK.getCompleteMarketPricesCompressedAsync service to display my prices and followed step 10 to achieve this. How would I make the data in the oMarketPrices array available outside the event handler sub?

                For example, currently the code looks like this:

                Code:
                    Private Sub BetFairUK_getCompleteMarketPricesCompressedCompleted(ByVal sender As Object, ByVal e As BFUK.getCompleteMarketPricesCompressedCompletedEventArgs) Handles BetFairUK.getCompleteMarketPricesCompressedCompleted
                        Try
                            If Not e.Cancelled Then
                                With e.Result
                                    If .errorCode = BFUK.GetCompleteMarketPricesErrorEnum.OK Then
                                        Dim oMarketPrices As New UnpackCompleteMarketPricesCompressed(.completeMarketPrices)
                                        With oMarketPrices
                                            Print(.marketId & "  " & .runnerInfo.Length & " runners")
                                            'Process returned market prices here.
                                            For i = 0 To .runnerInfo.Length - 1
                                                With .runnerInfo(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
                I need to process the returned market prices in another sub. So basically I just want to be able to populate oMarketPrices with the prices and then retrieve it in a different sub for further processing.

                I can't seem to get this to work in the same way as it seems to have worked in the other steps.

                Comment

                • Tam's Loup
                  Junior Member
                  • Jan 2009
                  • 4

                  #83
                  Dodgee

                  Not a dumb question.

                  My answer would be to store the contents of the response in a data structure that you make global. ie declare it at the top of your (TestForm) class.
                  Last edited by Tam's Loup; 17-04-2009, 09:17 PM.

                  Comment

                  • Dodgee
                    Junior Member
                    • Jan 2009
                    • 17

                    #84
                    Yeah that's what I wanted to do, but I get errors when I try and declare it, for example if I declare it in the main testform class like so:

                    Code:
                    Private oMarketPrices As New UnpackCompleteMarketPricesCompressed(.completeMarketPrices)
                    I get "Leading '.' can only appear inside a 'With' Statement".

                    And if I try:

                    Code:
                        Private oMarketPrices As New UnpackCompleteMarketPricesCompressed
                    I get "Argument not supplied for parameter 'MarketPrices' of Public Sub New(MarketPrices As String)"

                    I'm not sure of the right way to set up the objects I guess.

                    Comment

                    • Tam's Loup
                      Junior Member
                      • Jan 2009
                      • 4

                      #85
                      What I meant was to store the contents of the response in a new global variable, a data structure that reflects the contents of the response after it is unpacked.

                      So where you are currently printing the contents, you would store them in your new variable which would look a bit like this:

                      Class MarketPricesType
                      Public MarketId As Integer
                      Public RemovedRunners() As RemovedRunnerType
                      Public Runners() As RunnerType
                      End Class

                      Class RunnerType
                      Public SelectionId As Integer
                      Public Prices() As PriceType
                      End Class

                      Comment

                      • Mumbles0
                        Junior Member
                        • Jan 2009
                        • 240

                        #86
                        Reply to Dodgee (Re: Scope of oMarketPrices)

                        You’re on the right track. I gather you are trying to make the oMarketPrices object available to all subs in the TestForm class.

                        In the code of step 10 the oMarketPrices object is only accessible from within the event handler Sub BetFairUK_getCompleteMarketPricesCompressedComplet ed. To make it accessible outside this sub add this line near the top of the TestForm code:

                        Code:
                        Private oMarketPrices As UnpackCompleteMarketPricesCompressed
                        Let’s have a good look at this. Because we have not used the New keyword this statement instructs the compiler to reserve space to hold an object of type UnpackCompleteMarketPricesCompressed. It does not create an object. It is only creates an empty “pigeon hole” named oMarketPrices. This is usually called a variable. The Private keyword specifies the accessibility (or scope) of oMarketPrices. Private allows access from anywhere within the containing class or module. We could have used Friend or Dim. Friend allows access from anywhere within the current project. Dim provides the default access level which, in this case, is Private.

                        Now test this out. Add this temporary statement within Sub bKeepAlive_Click:

                        Code:
                        Print(oMarketPrices.marketId)
                        Run the project and click “KeepAlive”. What happens? We get the dreaded “Object reference not set to an instance of an object” exception. Why? Because we have not yet put an object into oMarketPrices!

                        Keep trying. Re-run the project, but first click “CompletePrices”. This should create the oMarketPrices object (assuming we are using an active marketId). Now click “KeepAlive”. But the same problem occurs. Why? Have a good look at the Dim statement within Sub BetFairUK_getCompleteMarketPricesCompressedComplet ed:

                        Code:
                        Dim oMarketPrices As New UnpackCompleteMarketPricesCompressed(.completeMarketPrices)
                        The part: New UnpackCompleteMarketPricesCompressed(.completeMark etPrices) evaluates to an object of type UnpackCompleteMarketPricesCompressed. This is the pigeon! The Dim statement instructs the complier to assign space for a another variable named oMarketPrices and, because the New keyword is used, assign the new object to this variable. So now we have two variables, both named oMarketPrices. One is local to this sub (it now contains the object) , and the other one at the top (which is accessible to all other procedures) remains empty.

                        This is not what we want so we change this Dim statement so that it becomes an assignment statement:

                        Code:
                        oMarketPrices = New UnpackCompleteMarketPricesCompressed(.completeMarketPrices)
                        This assigns the new object to the global oMarketPrices variable and it should now work.
                        That’s it! oMarketPrices is now accessible to all procedures within the TestForm class.


                        Reply to Tam’s Loup...

                        I can’t see much point in creating another object just to hold a subset of the data. To me it seems far simpler to give oMarketPrices a wider scope. Dodgee was attempting to do this.
                        Last edited by Mumbles0; 19-04-2009, 01:02 AM.

                        Comment

                        • Mumbles0
                          Junior Member
                          • Jan 2009
                          • 240

                          #87
                          Step 16. Unpacking Removed Runners

                          For each market a list of removed runners is returned by getMarketPrices, getMarketPricesCompressed and getCompleteMarketPricesCompressed. This info returns in a “packed string within a packed string” and is the same for each service. If there are no removed runners (which is often the case), removedRunners = "".

                          An example of a removedRunners string for a horserace market is:

                          "Mud Monkey,7.52,2.3;Bobby Gee,7.38,7.7;Pelo Du Bief,7.39,3.6;"

                          To unpack this string first add this class to Module Unpack2:

                          Code:
                          Class RemovedRunnerType
                            Public selectionName As String
                            Public removedDate As String
                            Public adjustmentFactor As Double
                          End Class
                          This class defines the data structure for the object we will build. Note that the details for these items given in the API Guide is a bit misleading. removedDate (given as DateTime) actually returns a string representing a number (e.g. "7.52"), presumably the time of the removal. Also, we convert adjustmentFactor (given as String) to Double.
                          Also add this class to Module Unpack2:

                          Code:
                          Class UnpackRemovedRunners
                            Public removedRunners As RemovedRunnerType()
                          
                            Sub New(ByVal RemRunners As String)
                              Dim Runner, Field As String(), n As Integer
                              Runner = RemRunners.Split(";")
                              n = UBound(Runner) - 1      'Number of removed runners - 1
                              ReDim removedRunners(n)
                              For i = 0 To n
                                Field = Runner(i).Split(",")
                                removedRunners(i) = New RemovedRunnerType
                                With removedRunners(i)      'Assign values to array
                                  .selectionName = Field(0)
                                  .removedDate = Field(1)
                                  .adjustmentFactor = Val(Field(2))
                                End With
                              Next
                            End Sub
                          
                          End Class
                          When an instance of this class is created, Sub New splits the input RemRunners string using ";" into strings for each runner, which are in turn split using "," to get the individual data items which are put into the removedRunners array. To test this class add a few more lines to Sub BetFairUK_getMarketPricesCompressedCompleted:

                          Code:
                          [COLOR="Gray"]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")[/COLOR]
                          
                          [COLOR="Black"]            Dim NonRunners As New UnpackRemovedRunners(.removedRunners)   'Unpack the removed runners
                                      For i = 0 To NonRunners.removedRunners.Length - 1
                                        With NonRunners.removedRunners(i)    'Print removed runner info
                                          Print(.selectionName & " " & .removedDate & " " & .adjustmentFactor)
                                        End With
                                      Next[/COLOR]
                          
                          [COLOR="Gray"]            '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[/COLOR]
                          Now when you click the “MarketPrices” button data for the removed runners will be printed (if there are any).
                          Last edited by Mumbles0; 27-04-2009, 01:09 AM. Reason: Minor code change

                          Comment

                          • Dodgee
                            Junior Member
                            • Jan 2009
                            • 17

                            #88
                            Re: Scope of oMarketPrices

                            Cool, that makes sense. Now works fine and is a lot clearer!

                            Comment

                            • hughmac
                              Junior Member
                              • Apr 2009
                              • 2

                              #89
                              Error 1 Value of type 'Betfair.BFUK.APIResponseHeader'

                              Originally posted by Mumbles0 View Post
                              Most of the action occurs on Betfair’s UK Exchange API. We will now add the facility to access this. In step 2 we added a reference to the Global API. We proceed in a similar manner. On the Project menu, select “Add Web Reference” to show the “Add Web Reference” dialogue form. (If this menu item is not there, proceed as per step 2.). N.B. Do not use the “Add Service Reference” dialogue. Add the URL for the UK Exchange service: https://api.betfair.com/exchange/v5/BFExchangeService.wsdl

                              Click “Go“. The BFExchangeService should now be found. Change the Web reference name to “BFUK”. Click “Add Reference”. After a while the “BFUK” icon should appear in the Solution Explorer near the existing “BFGlobal” icon. If you look again in the Object Browser you will see that the entire resources of the UK Exchange API are now available to our project.

                              Add this line near the top of the TestForm class:

                              Code:
                              Dim BetFairUK As New BFUK.BFExchangeService     'The UK ExchangeService object
                              Now a problem arises. All requests to BetfairUK require a request header (as per BetfairGlobal). We already have an object for this purpose, oHeaderGL, in which we are storing the session token. It would be nice if we could assign this to the “header” property of these requests, but if we attempt to do this we get a “type mismatch” compiler error. This is because the oHeader object has type “BFGlobal.APIRequestHeader”, but the compiler requires an object of type “BFUK.APIRequestHeader”. Even though these classes are identical, the compiler does not know this. So we create a compatible header object by adding this function:

                              Code:
                              Function oHeaderUK() As BFUK.APIRequestHeader
                                Dim Header As New BFUK.APIRequestHeader
                                Header.sessionToken = oHeaderGL.sessionToken
                                Return Header
                              End Function
                              For the same reason, the existing CheckHeader sub will not work for response headers from calls to BetfairUK. So we add another CheckHeader sub:

                              Code:
                              Sub CheckHeader(ByVal Header As BFUK.APIResponseHeader)
                                With Header
                                  Print("HeaderCode = " & .errorCode.ToString)
                                  oHeaderGL.sessionToken = .sessionToken
                                End With
                              End Sub
                              Note that we now have two CheckHeader subs which are identical except for the parameter type. The compiler selects the appropriate one. (This is called “overloading”.)
                              We will now call the getAllMarkets service to illustrate how data is obtained from BetfairUK. To do this, add another button to the Betfair Tester (you should be familiar with how to do this by now). Rename this button “blMarkets”, and change its text property to “Markets”. Double-click it to create the event handler sub bMarkets_Click. Add this code:

                              Code:
                              Private Sub bMarkets_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bMarkets.Click
                                Print("*** Markets ***")
                                Dim oMarketsReq As New BFUK.GetAllMarketsReq
                                Dim oMarketsResp As New BFUK.GetAllMarketsResp
                                With oMarketsReq
                                  .header = oHeaderUK()
                                  ReDim .eventTypeIds(0) : .eventTypeIds(0) = 7  ‘For horse racing
                                  ReDim .countries(1) : .countries(0) = "GBR" : .countries(1) = "ZAF"
                                  .fromDate = Today
                                  .toDate = Today.AddDays(1)
                                End With
                                oMarketsResp = BetFairUK.getAllMarkets(oMarketsReq)  'Call the UK API
                                With oMarketsResp
                                  CheckHeader(.header)
                                  Print("ErrorCode = " & .errorCode.ToString)
                                  If .errorCode = BFUK.GetAllMarketsErrorEnum.OK Then
                                    Print(.marketData)
                                  End If
                                End With
                              End Sub
                              This sub performs the usual calling sequence. For this call several optional request parameters can be specified as detailed in the API Guide. Fortunately VB2008’s data types are compatible. Two parameters (eventTypeIds, countries) require an array because multiple values can be used. The code shows an easy way of setting up arrays for this purpose. EventTypeIds are obtained from the ’getAllActiveEventTypes call which we looked at in Step 4 (7 is horse racing). The two Date parameters (fromDate, toDate) will accept any of VB2008’s Date methods. Note that a date value also contains a time component. The example specifies the time range from the start of today to the start of tomorrow.

                              Try it. The returned data is shown in the Textbox. Unlike “getActiveEventTypes” which we looked at in Step 4, this call returns a packed string (marketData) containing the requested data. This is unfortunate because we now require extra code to unpack this string into a usable form for subsequent processing. In Step 6 we look at a way doing this.


                              Accessing the Australian Exchange API.

                              Betfair’s Australian Exchange API is used primarily for Australian sports markets. To access the Australian Exchange we proceed in exactly same way as for the UK Exchange described above, except when we create the Web Reference use the URL:
                              https://api-au.betfair.com/exchange/v5/BFExchangeService.wsdl. Name this reference “BFAU“. In the code samples substitute all occurrences of “AU” for “UK” This works because both Exchanges are identical in operation, and BFAU contains the same set of classes as BFUK.
                              im working my way (slowly) thro these steps but have hit a problem with this one that i cant seem to resolve. Im seeing the following error message

                              Error 1 Value of type 'Betfair.BFUK.APIResponseHeader' cannot be converted to 'Betfair.BFGlobal.APIResponseHeader'. C:\Users\admin\Documents\Visual Studio 2008\Projects\Betfair\Betfair\TestForm.vb 111 25 Betfair

                              I have double checked for spelling or corrupted text etc and also checked through this thread to see if anyone else had a similar issue but cant find anything related. Can anyone give me a pointer to what the problem might be?

                              kind regards

                              Comment

                              • Mumbles0
                                Junior Member
                                • Jan 2009
                                • 240

                                #90
                                Reply to hughmac...

                                Have you got two CheckHeader subs ???

                                You will get this error if you‘ve only got the one from Step 2. In this step (5) we add another one. You require both.

                                Code:
                                Sub CheckHeader(ByVal Header As BFGlobal.APIResponseHeader)
                                  With Header
                                    Print("HeaderCode = " & .errorCode.ToString)
                                    oHeaderGL.sessionToken = .sessionToken
                                  End With
                                End Sub
                                
                                Sub CheckHeader(ByVal Header As BFUK.APIResponseHeader)
                                  With Header
                                    Print("HeaderCode = " & .errorCode.ToString)
                                    oHeaderGL.sessionToken = .sessionToken
                                  End With
                                End Sub

                                Comment

                                Working...
                                X