Using VB2008 to acccess the Betfair API: A tutorial

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts

  • Ferbar11
    replied
    Originally posted by BigSprout View Post
    I think your problem is here:

    Code:
            With Req
                .header = oHeaderGL
            End With
    GetAccountFunds is on the exchange server(s), you are calling the global server

    If you check the code you quoted from Mumbles it is:

    Code:
            With Req
                .header = oHeaderUK()
            End With
    
    
              or simply:
    
    Req.header = oHeaderUK()
    cheers
    No, isn't work.. This show me 0€ but i got more.

    Dim BetfairGL As New BFGlobal.BFGlobalService
    Dim oHeaderUK As New BFUK.APIRequestHeader
    Dim BetFairUK As New BFUK.BFExchangeService


    ....


    Dim Req As New BFUK.GetAccountFundsReq
    Dim Resp As BFUK.GetAccountFundsResp

    Req.header = oHeaderUK

    Resp = BetFairUK.getAccountFunds(Req)

    lShowUser.Text = Login.tbUser.Text
    lShowSaldo.Text = Resp.availBalance


    what is wrong? :s

    Leave a comment:


  • BigSprout
    replied
    MM,
    I don't understand why you are calling for individual prices for a horse in a market??? - each call registers as one access, hence the exceeded throttle exception you are receiving.

    You can get all of the prices for the full field in one call (takes less than one second) and make several calls in a minute and not receive "Exceeded Throttle" error:

    GetMarketPrices.....10 times per minute (get the full market prices for a race 10 times)
    GetMarketPricesCompressed.....60/minute
    GetCompleteMarketPricesCompressed.....60/minute

    each of the above have been fully documented with coding by Mumbles in this thread

    Post #443 on this page, lists all the different types of procedures to access the free APi
    http://forum.bdp.betfair.com/showthr...?t=112&page=45


    If you use one or all of the above, you will be able to "get around" using the web service and the bot, in fact, you could get the bot to do both by:
    one market using GetMarketPricesCompressed
    the other calling GetCompleteMarketPricesCompressed

    The API has a much faster reaction time

    Cheers

    Edit:
    Err, just noticed you are not coding in VB2008/10 - the link will help in the layout of the calls and a short note on how they work
    Last edited by BigSprout; 19-04-2011, 01:54 AM. Reason: Edit:

    Leave a comment:


  • monkeymagix
    replied
    A problem with the Throttle limit

    Hi Mumbles

    I am wondering if you or anyone else can help me with the error that sometimes arises usually when collecting runner prices.

    The error is caused when I call your CheckHeader method and receive the EXCEEDED_THROTTLE error code. This means I have reached the Betfair API request limit.

    This error usually comes up when I try to call a method that collects all the current prices for a market. I thought that rather than get a single horse at one time I would just loop through all the selections in the market and return a list object of a custom struct (bet selection) that could then be saved to the DB.

    However I keep running into these errors > EXCEEDED_THROTTLE

    so I came up with a little function to wait until the next minute before carrying on e.g


    Code:
    private void BeatThrottleLimit()
    {
        DateTime now = DateTime.UtcNow;
    
        // add 1 minute to the current time
        DateTime almostNextUpdateTime = now.AddMinutes(1);
    
        // get new datetime based on almostNextUpdateTime and set the seconds to 0
        DateTime nextUpdateTime = new DateTime(almostNextUpdateTime.Year, almostNextUpdateTime.Month, almostNextUpdateTime.Day, almostNextUpdateTime.Hour, almostNextUpdateTime.Minute, 0);
    
        // sleep for the difference between now and nextUpdateTime
        Thread.Sleep(nextUpdateTime.Subtract(now));
    
    }
    However when I come back from this function I seem to have lost all references to the relevant API object as subsequent function calls raise errors such as NULL object reference exceptions or in the example code provided which returns prices for all selections in a market I get Index was outside the bounds of the array error. This error only happens when I have to make a call to the BeatThrottleLimit function.

    I suspect it might be something to do with the Thread.Sleep command but I am not totally sure and I am wondering if anyone else has had similar problems or issues when using the free API and working around the request limit.

    Is there some function I could call after doing a "wait" / "beat throttle" method call so that I don't lose all my references.

    It is a bit hard to debug due to it being a windows service and not being able to throw the throttle error on demand. However thoughts would be appreciated.

    The relevant code is below


    Code:
    private void BeatThrottleLimit()
    {
        DateTime now = DateTime.UtcNow;
    
        // add 1 minute to the current time
        DateTime almostNextUpdateTime = now.AddMinutes(1);
    
        // get new datetime based on almostNextUpdateTime and set the seconds to 0
        DateTime nextUpdateTime = new DateTime(almostNextUpdateTime.Year, almostNextUpdateTime.Month, almostNextUpdateTime.Day, almostNextUpdateTime.Hour, almostNextUpdateTime.Minute, 0);
    
        // sleep for the difference between now and nextUpdateTime
        Thread.Sleep(nextUpdateTime.Subtract(now));
    
    }
    
    // similar overriden method exists for the BFGlobal.APIResponseHeader Header
    private void CheckHeader(BFUK.APIResponseHeader Header)
    {
        APIHeaderGL.sessionToken = Header.sessionToken;
    
        // set flag so other calls know if we had an issue
        ThrottleLimitError = false;
    
        // if we have broken the API request limit wait until the next minute when it gets reset
        if (Header.errorCode.ToString() == "EXCEEDED_THROTTLE")
        {
    	BeatThrottleLimit();
    
    	ThrottleLimitError = true;
    	
        }
    }
    
    
    // my function will return a list of SelectionPrice objects
    public struct SelectionPrice
    {
    	public int SelectionID;
    
    	public double BackPrice;
    
    	public double LayPrice;
    }
    
    // example of function that will fail due to this error
    public List<SelectionPrice> SaveAllSelectionPrices(int marketID, string betType)
    {
    
        List<SelectionPrice> runnerPrice = new List<SelectionPrice>();
        
        BFUK.GetMarketPricesResp MpriceResp = BetfairUK.getMarketPrices(MpricesReq(marketID));
    
        // Error might get raised here!
        CheckHeader(MpriceResp.header);
    
    
        if (MpriceResp.errorCode == BFUK.GetMarketPricesErrorEnum.INVALID_MARKET)
        {
    	// save market is invalid
    
    	return runnerPrice.ToList();
    
        }
        else if (MpriceResp.errorCode == BFUK.GetMarketPricesErrorEnum.OK)
        {
          
    	if (MpriceResp.marketPrices.marketStatus == BFUK.MarketStatusEnum.CLOSED)
    	{
    
    	    // save market closed
    
    	    return runnerPrice.ToList();
    
    	}
    	else
    	{
    
    	    double backPrice, layPrice;
    	    int selectionID;
    
    
    	    // loop through all selections and save their best price in turn
    	    for (int i = 0; i <= MpriceResp.marketPrices.runnerPrices.Length - 1; i++)
    	    {
    
    		backPrice = layPrice = 0;
    		selectionID = MpriceResp.marketPrices.runnerPrices[i].selectionId;
    
    		try
    		{
    
    		    // get both back and lay
    		   backPrice = MpriceResp.marketPrices.runnerPrices[i].bestPricesToBack[0].price;
    
    		   layPrice = MpriceResp.marketPrices.runnerPrices[i].bestPricesToLay[0].price;
    		}
    		catch (Exception e)
    		{
    		    // log message 
    		    // after a call to CheckHeader and then BeatThrottleLimit the error is usually >>  Error getting selection Price: Index was outside the bounds of the array.
    		    HelperLib.LogMsg("Error getting selection Price: " + e.Message.ToString());
    
    		}
    
    
    		if (backPrice > 0 || layPrice > 0)
    		{
    		    // add an item to my list which is a collection of stucts (my BetPrice object)                            
    		    runnerPrice.Add(new SelectionPrice { SelectionID = selectionID, BackPrice = backPrice, LayPrice = layPrice });
    
    		}
    
    	    }
    	}
        }
    
        return runnerPrice.ToList();
    
    }

    I am also wondering whether this problem is happening because I am running two different BOTs on different machines. One is my local PC and one is a webserver. One BOT just places BETS the other just gets prices.

    Is the API Request Limit linked to the Betfair account that is used to make the request or is it linked to the IP address of the PC making it? If I used different Betfair accounts but ran the BOT from the same PC would this get round the problem or would running multiple BOTS with the same account from different IP's get round it?

    Any help would be much appreciated.

    Thanks

    Leave a comment:


  • BigSprout
    replied
    I think your problem is here:

    Code:
            With Req
                .header = oHeaderGL
            End With
    GetAccountFunds is on the exchange server(s), you are calling the global server

    If you check the code you quoted from Mumbles it is:

    Code:
            With Req
                .header = oHeaderUK()
            End With
    
    
              or simply:
    
    Req.header = oHeaderUK()
    cheers

    Leave a comment:


  • Ferbar11
    replied
    Originally posted by Mumbles0 View Post
    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
    I try it but dint work here.

    Code:
    Dim Req As New BFUK.GetAccountFundsReq
            Dim Resp As New BFUK.GetAccountFundsResp
            With Req
                .header = oHeaderGL
            End With
    
            Resp = BetFairUK.getAccountFunds(Req)
    
            lShowUser.Text = Login.tbUser.Text
            lShowSaldo.Text = Resp.availBalance.ToString
    Last edited by Ferbar11; 17-04-2011, 03:29 PM.

    Leave a comment:


  • 1nner
    replied
    Connection closed exception

    Hey,

    I'm having a problem with an exception being thrown that I do not understand. I'm calling getMarketPricesCompressedAsync once per second (for different markets) and it's been working fine up until today.

    This morning however, my program made 15 calls in a row with no response and then an error was thrown with the message "The underlying connection was closed: An unexpected error occurred on a receive."

    Has anyone encountered anything similar to this before?

    Any help would be appreciated.

    Regards,

    Andrew

    Leave a comment:


  • Mumbles0
    replied
    Place markets

    Waldjunge,

    For a horse race the "Place" market is a separate market.

    If you call getAllMarkets and look at the marketId, menuPath and marketName parameters in the marketData array you might have, for example:
    102596266 \Horse Racing\GB\Wolv 11th Apr 1m Hcap
    102596267 \Horse Racing\GB\Wolv 11th Apr To Be Placed
    102596268 \Horse Racing\GB\Wolv 11th Apr 5f Claim Stks
    102596269 \Horse Racing\GB\Wolv 11th Apr To Be Placed
    102596270 \Horse Racing\GB\Wolv 11th Apr 6f Hcap
    102596271 \Horse Racing\GB\Wolv 11th Apr To Be Placed
    102596272 \Horse Racing\GB\Wolv 11th Apr 7f Hcap
    102596273 \Horse Racing\GB\Wolv 11th Apr To Be Placed
    102596274 \Horse Racing\GB\Wolv 11th Apr 1m Mdn Stks
    102596275 \Horse Racing\GB\Wolv 11th Apr To Be Placed
    102596276 \Horse Racing\GB\Wolv 11th Apr 1m1f Claim Stks
    102596277 \Horse Racing\GB\Wolv 11th Apr To Be Placed
    102596278 \Horse Racing\GB\Wolv 11th Apr 6f Hcap
    102596279 \Horse Racing\GB\Wolv 11th Apr To Be Placed
    Every second market is, in fact, a "Place" market. So 10259266 is the "Win" market for Wolv 11th Apr 1m Hcp, while 10259267 is the "Place" market for the same race, and so on. The "Place" market allows you to bet a horse to run 1st, 2nd or 3rd.

    Leave a comment:


  • waldjunge
    replied
    There are Place, Lay, Back bets on betfair.

    I can place Lay and Back bets with the API with no problem. How to place "Place" bets where the horse has place 1,2 or 3 with the free API?

    Leave a comment:


  • Mumbles0
    replied
    It's a good idea.

    If the marketId property is given in the response object then it's OK to use this, but there may be times when it doesn't return, for example if an error occurs.

    Leave a comment:


  • 1nner
    replied
    Thanks for the replies, very helpful. Regarding the posts you linked to mumbles - does this mean I should pass the marketId with every Async call and never rely on the marketId property that is returned in the response object?

    Leave a comment:


  • Mumbles0
    replied
    UserState parameter

    1nner,

    Using the userState parameter in the async call is the way to do it. You can plug the marketId directly into the call like this:

    BetFairUK.getMarketTradedVolumeAsync(Req, Req.marketId)
    then get the marketId from the response like this:

    marketId = e.UserState
    But the problem here is that you cannot have any outstanding async requests (i.e. requests that are waiting for a response). So it’s better to use a userState object containing the marketId:

    BetFairUK.getMarketTradedVolumeAsync(Req, New UserState(Req.marketId))
    then extract the marketId from the response:

    marketId = e.UserState.Param
    where UserState is this simple class:

    Code:
    Class UserState  'A class for userState objects
        Property Param As Object
        Sub New(ByVal Value As Object)
          Param = Value
        End Sub
      End Class
    You could add another property to this class to hold the selectionId if you wanted to.

    Tagorac has suggested including a random number in the object as well as the marketId. This certainly would work OK, but I don’t think it’s necessary. The objects created from class UserState should be unique enough.

    This topic has been discussed previously. Refer here & here.
    Last edited by Mumbles0; 05-04-2011, 12:31 PM.

    Leave a comment:


  • tagorac
    replied
    Originally posted by 1nner View Post
    [...]

    ie does the e.UserState property always contain the right value?

    [...]
    It does, but i don't think the marketId alone is unique enough for (potentially) multiple/concurrent asynchronous calls. I'm sending a custom object containing the marketId + a random number.

    Stephan

    Leave a comment:


  • Monairda
    replied
    Good morning
    I know how to solve the problem. As simple as removing the following:
    Code:
    Dim buttonBack As New DataGridViewButtonColumn()
                With buttonBack
                    .Name = butback
                    [B][COLOR="Red"].UseColumnTextForButtonValue = True[/COLOR][/B]
                    .AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells
                    .FlatStyle = FlatStyle.Flat
                    .CellTemplate.Style.BackColor = Color.Blue
                    .DisplayIndex = 0
                End With
                Columns.Add(buttonBack)
    If we take to next line
    .UseColumnTextForButtonValue = True
    everything works fine

    Now the question I have is like the button to display the two values. The value of the odd and the amount available

    Greetings

    Leave a comment:


  • 1nner
    replied
    getMarketTradedVolumeAsync

    Hi,

    I have a question regarding that getMarketTradedVolumeAsync call. I am supplying a marketId and selectionId, and getting the response as expected. The problem I'm having is that the response does not contain the marketId paramater, and because I'm making many of these async calls I cannot tell which reponse is which.

    The only way I can think of to solve this is to use the userState parameter to pass the marketId, which I can then examine later but I'm not sure if this is the best way of doing things (ie does the e.UserState property always contain the right value)?

    Any help would be greatly appreciated.

    Regards,

    Andrew
    Last edited by 1nner; 03-04-2011, 07:00 AM.

    Leave a comment:


  • Monairda
    replied
    Insert a datagridviewButtonColumn

    Good morning

    I'm trying to insert a button in a DataGridView cell.

    Use the code provided by Mumbles0 and following the advice of BigSprout:

    Code:
    Public Class RunnerGrid   'A customised DataGridView
    
        Inherits DataGridView
    
        Public Const colRunner = "Runner"  'Runner names column
        Public Const colSelId = "SelId"    'SelectionId column
        Public Const colBack = "Back"      'Back price column
        Public Const colLay = "Lay"        'Lay price column
        Public Const butback = "BBack"    'Back price button column
    
    
        Sub New(ByVal Runners As BFUK.Runner())
    
            RowHeadersVisible = False  'Turn off row headers
            Dock = DockStyle.Fill       'The grid fills the Tab
            If Runners.Length > 0 Then   'Runner data exists
    
                Dim NameColumn As New DataGridViewTextBoxColumn  'Add column   for runner names 
                With NameColumn
                    .Name = colRunner
                    .ReadOnly = True
                    .AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells  'Column width is automatic
                End With
                Columns.Add(NameColumn)  'Add to columns collection
    
                Dim SelIdColumn As New DataGridViewTextBoxColumn  'Add column for selectionId
                With SelIdColumn
                    .Name = colSelId
                    .Width = 70       'Column width is fixed
                    .ReadOnly = True
                End With
    
                Columns.Add(SelIdColumn)  'Add to columns collection
    
                RowCount = Runners.Length  'A row for each runner 
                For i = 0 To RowCount - 1
                    With Runners(i)
                        Item(colRunner, i).Value = .name  'Add the runner name
                        Item(colSelId, i).Value = .selectionId  'Add the selectionId
                    End With
                Next
    
                Dim BackColumn As New DataGridViewTextBoxColumn  'Add column for Back prices
                With BackColumn
                    .Name = colBack
                    .Width = 50
                    .DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                    .DefaultCellStyle.Format = "f2"
                    .ReadOnly = True
                End With
                Columns.Add(BackColumn)
    
                Dim LayColumn As New DataGridViewTextBoxColumn  'Add column for Lay prices
                With LayColumn
                    .Name = colLay
                    .Width = 50
                    .DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                    .DefaultCellStyle.Format = "f2"
                    .ReadOnly = True
                End With
                Columns.Add(LayColumn)
    
                Dim buttonBack As New DataGridViewButtonColumn()
                With buttonBack
                    .Name = butback
                    .UseColumnTextForButtonValue = True
                    .AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells
                    .FlatStyle = FlatStyle.Flat
                    .CellTemplate.Style.BackColor = Color.Blue
                    .DisplayIndex = 0
                End With
                Columns.Add(buttonBack)
    
            End If
        End Sub
    
        Sub UpdatePrices(ByVal RunnerPrices As BFUK.RunnerPrices())  'Method to update the Prices
            Dim i, j, SelectionId As Integer
    
            For i = 0 To RowCount - 1  'For each row in grid
                SelectionId = Item(colSelId, i).Value
                j = Array.FindIndex(RunnerPrices, Function(x As BFUK.RunnerPrices) x.selectionId = SelectionId) 'Array index for this runner
                If j >= 0 Then 'Prices exist for this runner
                    With RunnerPrices(j)
                        If .bestPricesToBack.Length > 0 Then
                            Item(colBack, i).Value = .bestPricesToBack(0).price
                            Item(butback, i).Value = .bestPricesToBack(0).price
                        Else
                            Item(colBack, i).Value = ""
                            Item(butback, i).Value =""
                        End If
                        If .bestPricesToLay.Length > 0 Then
                            Item(colLay, i).Value = .bestPricesToLay(0).price
                        Else
                            Item(colLay, i).Value = ""
                        End If
                        'Item(colBack, i).Value = If(.bestPricesToBack.Length > 0, .bestPricesToBack(0).price, "")   'Update best Back price
                        'Item(colLay, i).Value = If(.bestPricesToLay.Length > 0, .bestPricesToLay(0).price, "")      'Update best Lay price
                    End With
                End If
            Next
    
        End Sub
        
    End Class
    But when I run it does not fill the price value on the button

    Can you help me? Thanks

    Leave a comment:

Working...
X