Using VB2008 to acccess the Betfair API: A tutorial

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

  • Mumbles0
    replied
    Your Trading App

    Monkeymagix,

    Perhaps I should give a disclaimer. Note that anything I say about the way the exchange operates is purely my own (unofficial) view. As you know, some API details are not clearly documented, hence we must learn through our own observations.

    Re queuing of an unmatched portion: I’m not 100% sure if this exactly what happens, but think this is the case. Indeed changing the price (with updateBets) effectively creates a new bet which joins the end of the queue at the new price.

    When you place a bet it looks like you are having a “quick stab” at the market, cancelling if the bet (or any part) is not matched immediately. If you left the unmatched bet sitting in there for a while there’s a good chance of getting a match at the price you want. But to do this you would have to poll the bet’s status to see when (or if) it is matched using say, getMUBets. This would increase the complexity of your program but it might be worth it.

    Saving the session token:

    Your code looks OK on the surface. I’m not all that familiar with the order in which things execute. The session token should be read from the file when the program starts, and saved when the program terminates. Make sure you call CheckHeader after every API call (especially login). You’ve got a lot of debug code in there - you tell me what’s wrong.

    Since you are using an application setting to hold the path to the auxiliary file, why not save the session token in an app setting instead? It would be a lot simpler.

    By the way, this is a VB tutorial thread. I don’t think it’s a good idea to paste too much C# code here - it may confuse some readers.
    Last edited by Mumbles0; 08-12-2010, 11:38 PM.

    Leave a comment:


  • Monairda
    replied
    How to control the Data Request Charge?

    Good morning I read about this issue.

    "Charges Apply to Both API and website data requests. Only market data (for example market prices) and private betting data (for example current bets, P & L) Requests Are subject to the charge. At the end of Every Day. And Any customer making More Than 20 Requests data in one second will be charged 0.1p per data requestabove 20. "

    But how can we control these calls not to exceed that limit? Controlling the number of calls over statecount every second!

    Code:
    Private StateCount As Integer 'The unique state object
    Thanks!

    Leave a comment:


  • monkeymagix
    replied
    Handling Partial Matches

    Hi Mumbles

    Cheers for getting back to me.

    My logic is obviously changing all the time as I haven't been able to find any kind of logical flow diagram that describes how a system engaging the betfair API should proceed in terms of logical steps (e.g how to place a bet and handle all possible consequences). If it hadn't have been for this tutorial I don't know where I would have started as some of the other free code I have downloaded off the web has been pretty awful to say the least.

    In terms of my "perfect" selection system, nothing is ever perfect and I didn't mean that all my selections were 100% accurate just that my back end logic for selecting daily runners to place bets on is working fine. It is only the front end logic that engages the Betfair API that is throwing up the odd curve ball.

    Not give my game away but I have spent the best part of a year working on a racing system and I am basing my trading choices on a number of factors that reduce the likelihood of unexpected price drifts one way or another. Not to say that this doesn't happen but I am trading on runners that have a good chance of winning so that if the LAY bet cannot be matched at ideal prices I have very good odds of gaining the full profit from a win bet rather than just the difference between the WIN and LAY price. Overall (with my system) it seems that this method seems to return better profits than just always trying to LAY even if that price is higher.

    At the moment I am not using getMUBets in my code as I am literally placing a bet, checking the matched size matched the desired size and if not cancelling ASAP.

    For "just unmatched" I meant that the bet hadn't been matched on the exchange market and was flagged with a status of U (with no matching parts at all).

    I didn't realise that my logic of cancelling ASAP would affect my pricing strategy because of the queueing system as if I cancel the unmatched portion of the bet there will be a whole "logical loop" iteration before any re-attempt is taken place which involves:

    -updating the DB to tell the system that the bet has been placed along with the matched amount and price taken
    -a creation of new records in the DB to take the place of any unmatched part
    -a new BOT call to the API which will look to my DB for any new PENDING bets
    -a new call to the market to get the best available price at that moment
    -logic to decide whether or not to place the bet

    So when the unmatched part is re-attempted the chances are that a new price will be given and taken anyway. I don't know if I could retain my unmatched bet AND modify the price so that it keeps its position in the queue?

    Also on a side note I never seem to be able to keep my previous session ID from my last API request and always end up having to re-login. I have tested this by outputting the Session ID once it has been obtained, saved and on use.

    My application is a C# console app that is currently running on a scheduled job once a minute.

    The main code that handles the session storage and re-use is here (taken from your tutorial)

    Can you see anything wrong with it or think of a reason why I would not be able to use re-use the session?



    Code:
    public class BetfairBot
    {
    
    	private BFGlobal.APIRequestHeader HeaderGL;
    	private BFGlobal.BFGlobalService BetfairGL;
    	private BFUK.BFExchangeService BetfairUK;
    
    	private BetfairRaceCourseAbreviations RacecourseABV;
    
    	// I store the path to the text file in my config file
    	private string SessionPath = ConfigurationManager.AppSettings["SessionPath"].ToString();
    
    
    	public BetfairBot()
            {
                HeaderGL = new BFGlobal.APIRequestHeader();
                BetfairGL = new BFGlobal.BFGlobalService();
                BetfairUK = new BFUK.BFExchangeService();
    
                // load in any session values
                string sessionval = File.ReadAllText(SessionPath);
    
    	    // This is just a debug message that logs to a file so I can see later on what went wrong!
                Program.ShowMsg("Set session value to " + sessionval.ToString());
    
                HeaderGL.sessionToken = sessionval;
            }
    
    	private BFUK.APIRequestHeader oHeaderUK(){
                BFUK.APIRequestHeader Header = new BFUK.APIRequestHeader();
                Header.sessionToken = HeaderGL.sessionToken;
    
                return Header;
            }
    
    	private void CheckHeader(BFGlobal.APIResponseHeader Header)
            {
                HeaderGL.sessionToken = Header.sessionToken;
    
                // if we have broken the API request limit wait until the next minute when it gets reset
                if (Header.errorCode.ToString() == "EXCEEDED_THROTTLE")
                {
                    // wait until the next minute
                    BeatThrottleLimit();
                }           
    
            }
    
    	public void SaveSession(){
    
                Program.ShowMsg("Save session value = " + HeaderGL.sessionToken.ToString());
    
                File.WriteAllText(SessionPath, HeaderGL.sessionToken);
            }
    
    }

    Leave a comment:


  • Mumbles0
    replied
    Your Trading App

    Monkeymagix,

    I’m pleased to hear that your database is selecting runners perfectly. I wish mine would select perfect runners.

    I don’t know if I’m the best person to discuss trading methods. I’m sure there are traders out there who know a lot more about it than I do. But for what it’s worth, allow me to make a few comments.

    Betting strategies are personal things. If you get onto a good one then it’s probably best to keep it to yourself.

    Perhaps your system is a bit idealistic. After your back bet has been matched it’s all very well setting a best and max lay price, but what do you do if your runner’s price won’t behave itself and drifts up?

    It certainly would be nice to have a test facility. No doubt you have had some “dry runs”, simply observing when your bets would have been matched had they been placed.

    Partial matching occurs frequently. Dealing with partially-matched bets certainly makes any betting system a lot more complicated but they must be catered for. It’s interesting that you have noticed that both portions of a partially-matched bet retain the same betId. It would also be interesting to know if the bets array returned by getMUBets lists these the portions as separate bets with the same betId.

    Have I got this right? If your back bet is partially matched you immediately cancel the unmatched portion and resubmit a new back bet for the same price and amount (as the just-cancelled unmatched portion). Must you do this? It would be better to work with the unmatched portion as is (if possible) because it retains its position at the head of the queue, where as a new bet at the same price comes in at the end of the queue.

    Also, what do mean by “just unmatched” ?

    Leave a comment:


  • monkeymagix
    replied
    Handling Partial Matches Safely

    Hi Mumbles

    I am in the process of testing my auto trading bot which aims to automatically place trade bets on selections that have been pre-determined by a backend DB. My database is complete and selecting runners perfectly and I am in the process of testing with live data and money. On a side note it is a real pain not having a test mode to simulate real betting as I am having to waste real money just to debug my system under live conditions.

    Everything is working fine apart from partial matches as obviously I need the lay amount to be equal to (or more if overlaying) to the back stake.

    If I place the WIN part of the trade and it is only partially matched say £2 out of £10 then I don't want to place the LAY part at £2 if the remaining £8 is then matched later on for the WIN either on the market or at SP as I could lose out big time. This has actually happened today and because the win bet was matched in two stages the lay part was set at a lower figure than it should have been which meant when the horse lost I lost instead of winning money!

    Therefore I have come up with the following business logic. I would appreciate it if you could check this logic for me and tell me if it looks correct to you.


    1. My back end system calculates the runners to place trades on at intervals throughout the day and sets up records in a PENDING_BETS tables that specifies all the parameters such as Selection, Market, Bet Amount, Maximum Liability, Max / Min odds, best price, current status and so on.

    2. For TRADE bets there are 2 corresponding records linked by a Trade ID key and at first only the WIN part of the bet is set to PENDING whilst the LAY part is not active. This way a LAY bet is only ever placed once the WIN part has already been matched.

    3. My BOT polls this table at intervals checking for new bets to be placed and to check for settled bets or changes in statuses.

    4. A WIN bet is placed at the best possible price and if the bet is matched then the LAY part of the TRADE is set to PENDING and it has it MAX Price set to the WIN bets Matched Price and a BEST Price set to something lower.

    5. The bot tries to match the BEST price for the LAY bet up until the race is near and if it has not matched the BEST price by then it will resort to matching up to the MAX Price. The code will also calculate overlays when the best price can be obtained to obtain profits from losing runners.

    Now the tricky bit with partial matches

    I am currently checking for PARTIAL MATCHES in my PlaceBet method by checking the Size Matched against my initial desired amount to see if it is under. If they are equal then the full bet amount was taken.

    I have found from my tests that a PARTIAL MATCH bet will use the same BetID for all parts. Therefore once I know that at least some of the bet has been matched I take this shared BetID from the successfully matched part and call a CancelBet method straight away. In theory this will only ever cancel any unmatched parts of the bet as you cannot cancel a matched bet.

    Also if the bet was just unmatched rather than partially matched then this shouldn't cause any problems as the whole bet will be cancelled and then re-attempted on the next poll loop.

    If the CancelBet call is unsuccessful due to the unmatched part having been taken OR only partially cancelled due to a slice of it being taken then I call my GetBetStatus method to get the current matched size of the bet safely knowing that this will now not change as I have cancelled everything I possibly can.

    I can then call my SaveBetStatus method which will update my PENDING_BETS table in the DB so that the LAY bet amount is set to the correct amount.

    For the remaining amount that was cancelled I can then create a new PAIR of WIN & LAY bets in my PENDING_BETS table.

    Can you see any problems with this logic?

    Thanks for your help on this matter.

    Leave a comment:


  • Mumbles0
    replied
    Gunhero,

    The Total Goals market is treated as an Asian handcap, so the .asianLineId parameter in the placeBets request should be set to the corresponding value given in the .runners array returned by getMarket.

    Leave a comment:


  • gunhero
    replied
    The mistake was quiet simple
    The Asian Line Id was wrong.

    Greetings gunhero

    Leave a comment:


  • gunhero
    replied
    error: BET_IN_PROGRESS

    Hi Mumbles0 and others,
    I have some problem with an errorcode after the placeBetsReqest.
    The errorcode is "BET_IN_PROGRESS". At betfair I read, that this means, that it's possible or not that the bet is placed. It's a very silly statement.

    I will show you my Request:

    Code:
    'TestValues
    handicap = 1
    marketId = 101970857
    selectionId = 285469
    tempQuote = 5
    tempStake = 100
    betType = "B"
    
    Dim oPlaceBetResp As New BFUK.PlaceBetsResp
    Dim oBets(0 To 0) As BFUK.PlaceBets
    
    oBets(0) = New BFUK.PlaceBets
            With oBets(0)
                .asianLineId = handicap
                .betCategoryType = BFUK.BetCategoryTypeEnum.E
                .betPersistenceType = BFUK.BetPersistenceTypeEnum.NONE
                If betType = "B" Then
                    .betType = BFUK.BetTypeEnum.B
                ElseIf betType = "L" Then
                    .betType = BFUK.BetTypeEnum.L
                End If
    
                .bspLiability = 0
                .marketId = marketId
                .selectionId = selectionId
                .price = tempQuote
                .size = tempStake
    
            End With
    
            With oPlaceBetReq
                .header = oHeaderUK()
                .bets = oBets
            End With
    
    oPlaceBetResp = BetfairUK.placeBets(oPlaceBetReq)
    
    If oPlaceBetResp.errorCode = BFUK.PlaceBetsErrorEnum.OK Then
    ' xxx
    else
    Print("Error: " & oPlaceBetResp.errorCode.ToString)
    end if
    I want to place a bet in the Total Goals market (Game: Aston Villa v Arsenal) at Back "1 goal or more".
    I think the error is couse of the asian line ID. Can someone explain me the sense of this part? The error by handicap = -1 is "INCORRECT ASIAN_LINE_ID", by handicap = 1 is "BET_IN_PROGRESS".

    How I have to change the code to place also bets at "Total Goals"?

    Thanks for your help

    gunhero

    Leave a comment:


  • JayBee
    replied
    Sorry, I hadn't noticed that you changed the For statement from

    For i = 0 To RowCount

    For i = 0 To Runners.Length - 1

    Thanks for your help. I have no idea how it works but as I learned in functional maths in my comp. sci. degree, "Don't ask why, it just is."

    PS... My first live run, today, made two trades on a race and the field was completely green. Thanks again!


    Originally posted by Mumbles0 View Post
    JayBee,

    There is no problem !!!

    If you increase the RowCount property by one you get an extra row at the bottom of the grid. Isn't this what you want?

    Leave a comment:


  • Mumbles0
    replied
    JayBee,

    There is no problem !!!

    If you increase the RowCount property by one you get an extra row at the bottom of the grid. Isn't this what you want?

    Leave a comment:


  • JayBee
    replied
    I used that method of adding a row and got the same problem as yourself, namely the row is in the wrong place.

    That's not very helpful when it comes to totalling things up.

    At the moment I am putting my totals into labels, elsewhere on the form.

    Other ideas I have is to have label objects inside the tab or to declare a second grid view without headers and to pass data between the two or to sort the grid or to add a dummy runner.

    Originally posted by Mumbles0 View Post

    Extra grid row

    Try setting the RowCount property to one more than the number of runners.
    Change these two lines in the RunnerGrid class:

    Code:
    [COLOR="Gray"][COLOR="Black"]  RowCount = Runners.Length + 1  'A row for each runner (plus extra row)
      For i = 0 To Runners.Length - 1[/COLOR]
        With Runners(i)
          Item(colRunner, i).Value = .name  'Add the runner name
          Item(colSelId, i).Value = .selectionId  'Add the selectionId
        End With
      Next[/COLOR]
    (I don't know why the Rows.Add method inserts a new row before the last row)

    Leave a comment:


  • Mumbles0
    replied
    Reply to JayBee (using DataGridView)

    Cell colouring

    That’s the way to go. The CellFormating event fires whenever a cell’s value changes, allowing you to change the cell’s colour depending on its new value (as you’ve done).

    Extra grid row

    Try setting the RowCount property to one more than the number of runners.
    Change these two lines in the RunnerGrid class:

    Code:
    [COLOR="Gray"][COLOR="Black"]  RowCount = Runners.Length + 1  'A row for each runner (plus extra row)
      For i = 0 To Runners.Length - 1[/COLOR]
        With Runners(i)
          Item(colRunner, i).Value = .name  'Add the runner name
          Item(colSelId, i).Value = .selectionId  'Add the selectionId
        End With
      Next[/COLOR]
    (I don't know why the Rows.Add method inserts a new row before the last row)

    Leave a comment:


  • Mumbles0
    replied
    Reply to Tachikoma

    Hi Mumbles0, thanks for your great work.
    Referring to your Step 9. Making Async API Calls.
    BetFairUK.getMarketPricesAsync(MpricesReq)
    Why I can't find the Async version of getMarketPrices method?
    It’s there.

    If you have declared the service object BetFairUK as:

    Private WithEvents BetFairUK As New BFUK.BFExchangeService 'The UK ExchangeService object
    then when you type:

    BetFairUK.
    the getMarketPricesAsync method should appear on the Intellisense list.

    Leave a comment:


  • JayBee
    replied
    New Problem...

    I want to add a new row to the bottom of my table, into which I can place totals.

    Any ideas?

    I've tried row.add() but it adds the row above the last runner.

    I have no idea why it does this.

    Leave a comment:


  • JayBee
    replied
    Found a solution after a day of hair pulling...

    Using the existing variable ref for currently selected grid...

    Public WithEvents SelectedGrid As RunnerGrid 'A variable to hold a reference to the currently-selected grid

    I added this event handler...

    Private Sub dataGridView1_CellFormatting(ByVal sender As Object, ByVal e As DataGridViewCellFormattingEventArgs) Handles SelectedGrid.CellFormatting
    If Me.SelectedGrid.Columns(e.ColumnIndex).Name = "F-B" ThenIf e.Value > 0 Then
    e.CellStyle.BackColor = Color.LightGreenEnd If
    End If

    End Sub


    Originally posted by JayBee View Post
    Having started programming in 1981 on a Tandy TRS-80, I have yet to evolve the ability to perform object-oriented programming.

    I have a working program now and am certain it would give a younger programmer a heart attack.

    What I would like to do now is use the existing code in this excellent thread but colour code individual cells dependent on value range.

    Any ideas?

    I would prefer not to do a complete rewrite of all the excellent work already done in this thread.

    Thanks.

    Leave a comment:

Working...
X