BrandonWoodward / BetfairDotNet

An easy to use client for the Betfair API. Includes functionality for login, accounts, betting and streaming.
MIT License
7 stars 4 forks source link

MarketPriceFilterEnum.EX_BEST_OFFERS_DISP alway return no data #2

Open firatbaran opened 8 months ago

firatbaran commented 8 months ago

Hi Brandon.

My code is:

`var streamConfiguration = new StreamConfiguration() { SessionToken = betfairSessionToken, RecoveryThresholdMs = 3_000, MaxRecoveryWaitMs = 120_000 };

StreamingMarketDataFilter marketDataFilter = new() { LadderLevels = 3, Fields = new List() { MarketPriceFilterEnum.EX_BEST_OFFERS_DISP } };

StreamingMarketFilter marketFilter = new() { MarketIds = new List(), BettingTypes = new List() { MarketBettingTypeEnum.ODDS, MarketBettingTypeEnum.ASIAN_HANDICAP_DOUBLE_LINE, MarketBettingTypeEnum.ASIAN_HANDICAP_SINGLE_LINE, MarketBettingTypeEnum.LINE } };`

When I change field to "MarketPriceFilterEnum.EX_ALL_OFFERS", I got the data without VirtualBets.

BrandonWoodward commented 8 months ago

Thank you for bringing this to my attention. I have quickly put together a fix. Update your package to v1.2.5 and let me know if there are any further issues.

firatbaran commented 8 months ago

Hi again Brandon.

Virtual Data sounds great. There's no problem. But within a few minutes, I get the following error. I tried many times but the same error always appears.

internal void AddOrUpdateLevelByDepth(int depth, PriceSize level) { if(depth >= 0 && depth < _byRank.Count) { var price = _byRank.Keys[depth]; _byPrice[price] = level; _byRank[price] = level; } else { _byRank.Add(level.Price, level); _byPrice.Add(level.Price, level); } }

(at PriceLadder.cs)

An item with the same key has already been added. Key: 1,01 (Parameter 'key')

BrandonWoodward commented 8 months ago

I can't seem to reproduce the error. Under what circumstances did this exception occur? Could you provide a reproducible example so I can investigate? I couldn't replicate your StreamingMarketFilter as this would result in subscribing to too many markets, which causes an exception.

Here is the code I ran and did not get any issues (note, I have streamlined the subscription for the latest version, and I am hoping to do something similar for some of the other operations as well):

var client = new BetfairClient(apiKey);
var session = await client.Login.CertificateLogin(username, password, certPath);

await client.Streaming
    .CreateStream(session.SessionToken)
    .WithAutoReconnection(3_000, 120_000)
    .ForEventTypeIds("7")
    .ForCountryCodes("GB", "IE")
    .ForMarketTypes("WIN")
    .ReturningMarketDataFor(3, MarketDataFilterEnum.EX_BEST_OFFERS_DISP)
    .OnMarketChange(snap => Console.WriteLine(snap.Timestamp))
    .OnException(ex => Console.WriteLine(ex))
    .Subscribe();
firatbaran commented 8 months ago
EventType: Football, Basketball
----------------------------------
Market Type: "MATCH_ODDS",
     "HALF_TIME",
     "HALF_TIME_FULL_TIME",
     "OVER_UNDER_05",
     "OVER_UNDER_15",
     "OVER_UNDER_25",
     "OVER_UNDER_35",
     "OVER_UNDER_45",
     "OVER_UNDER_55",
     "OVER_UNDER_65",
     "FIRST_HALF_GOALS_15",
     "FIRST_HALF_GOALS_25",
     "DOUBLE_CHANCE",
     "BOTH_TEAMS_TO_SCORE",
     "ASIAN_HANDICAP",
     "ODD_OR_EVEN",
     "OVER_UNDER_85_CORNR",
     "OVER_UNDER_105_CORNR",
     "OVER_UNDER_135_CORNR",
     "CORNER_MATCH_BET",
     "PENALTY_TAKEN",
     "SENDING_OFF",
     "TEAM_A_WIN_TO_NIL",
     "TEAM_B_WIN_TO_NIL",
     "CLEAN_SHEET",
     "WIN_BOTH_HALVES",
--------------------------------
List<string> eventLeagueIDs = new(){ 
   eventLeagueIDs.Add("194215");  // Football
    eventLeagueIDs.Add("10932509");// Football
    eventLeagueIDs.Add("59");       // Football
    eventLeagueIDs.Add("117");      // Football
    eventLeagueIDs.Add("81");      // Football
    eventLeagueIDs.Add("55");      // Football
    eventLeagueIDs.Add("9404054");   // Football
    eventLeagueIDs.Add("89979");   // Football
    eventLeagueIDs.Add("99");      // Football
    eventLeagueIDs.Add("129");     // Football 
    eventLeagueIDs.Add("10547864"); // NBA
    eventLeagueIDs.Add("10534437"); // EuroLeague
}
marketFilter.CompetitionIds = eventLeagueIDs;
----------------------------------
marketFilter.MarketStartTime = next 5 days
----------------------------------

If possible, you can try above filters, first 200 MarketID's (30 basketball, 170 football) This is the filter that gives me error

BrandonWoodward commented 8 months ago

Thank you, I have isolated the issue, which was a wider problem with the depth-based ladders. I hope that the issue is now fixed, I will push a pre-release version of the package.

Here is the code I ran with the latest fix, and the exception was not thrown:

var marketTypes = new List<string>
{
    "MATCH_ODDS",
    "HALF_TIME",
    "HALF_TIME_FULL_TIME",
    "OVER_UNDER_05",
    "OVER_UNDER_15",
    "OVER_UNDER_25",
    "OVER_UNDER_35",
    "OVER_UNDER_45",
    "OVER_UNDER_55",
    "OVER_UNDER_65",
    "FIRST_HALF_GOALS_15",
    "FIRST_HALF_GOALS_25",
    "DOUBLE_CHANCE",
    "BOTH_TEAMS_TO_SCORE",
    "ASIAN_HANDICAP",
    "ODD_OR_EVEN",
    "OVER_UNDER_85_CORNR",
    "OVER_UNDER_105_CORNR",
    "OVER_UNDER_135_CORNR",
    "CORNER_MATCH_BET",
    "PENALTY_TAKEN",
    "SENDING_OFF",
    "TEAM_A_WIN_TO_NIL",
    "TEAM_B_WIN_TO_NIL",
    "CLEAN_SHEET",
    "WIN_BOTH_HALVES",
};

var competitionIds = new List<string>
{
    "194215", // Football
    "10932509", // Football
    "59", // Football
    "117", // Football
    "81", // Football
    "55", // Football
    "9404054", // Football
    "89979", // Football
    "99", // Football
    "129", // Football
    "10547864", // NBA
    "10534437", // EuroLeague
};

var markets = await client.Betting.ListMarketCatalogue(
    new() { CompetitionIds = competitionIds, MarketTypeCodes = marketTypes},
    new List<MarketProjectionEnum> { MarketProjectionEnum.EVENT },
    MarketSortEnum.FIRST_TO_START,
    maxResults: 200
);

await client.Streaming
    .CreateStream(session.SessionToken)
    .WithAutoReconnection(3_000, 120_000)
    .ForMarketIds(markets.Select(m => m.MarketId).ToArray())
    .ReturningMarketDataFor(3, MarketDataFilterEnum.EX_BEST_OFFERS_DISP)
    .OnMarketChange(snap => Console.WriteLine(snap.Timestamp))
    .OnException(ex => Console.WriteLine(ex))
    .Subscribe();
firatbaran commented 8 months ago

Thank you. I'll try when I get the new release. :)

BrandonWoodward commented 8 months ago

Should be v1.2.6 on NuGet

firatbaran commented 8 months ago

You're the rock! Looks great, I haven't gotten any errors.

firatbaran commented 8 months ago

EqualLayBack

Virtual Back Equal Virtual Lay

Hello Brandon. At the same price, both back and lay price are equal. Can you check it?

(Also, in this scenario (BestOffer Virtual), can be Virtual Back/Lay price count be more than 3 ?)

Same snapshot, another runner:

EqualLayBack

firatbaran commented 8 months ago

This occurs only MarketDataFilterEnum.EX_BEST_OFFERS_DISP markets.

Edit: Sorry, occurs even MarketDataFilterEnum.EX_BEST_OFFERS!

Cancel this comment: There is no problem at MarketDataFilterEnum.EX_BEST_OFFERS

Last; RunnerSnapshop need to be Handicap property at: -ProcessRunnersImage -ProcessRunnersDelta

BrandonWoodward commented 8 months ago

I have just pushed a commit to try and address this, however the issue with there being more prices than ladder levels specified persists. I have added a test case for this which is failing:

[Fact]
public void DepthBasedPriceLadder_UpdatesPriceAndSize_WhenLevelExists() 
{
    // Arrange
    var ladder = new PriceLadder(SideEnum.BACK)
    {
        [0] = new(3.5, 100),
        [1] = new(2.5, 100),
        [2] = new(1.5, 100),
    };

    // Act
    ladder[0] = new(2.5, 100);
    ladder[1] = new(2.0, 150);

    // Assert
    ladder.Depth.Should().Be(3);
    ladder[0]?.Size.Should().Be(100);
    ladder[0]?.Price.Should().Be(2.5);
    ladder[1]?.Size.Should().Be(150);
    ladder[1]?.Price.Should().Be(2.0);
    ladder[2]?.Size.Should().Be(100);
    ladder[2]?.Price.Should().Be(1.5);
}

This is due to stale prices not being removed from the SortedList<double, PriceSize> in the PriceLadder class. The offending code is here:

public PriceSize? this[int index]
    {
        get
        {
            return index >= 0 && index < _byRank.Values.Count
                ? _byRank.Values.ElementAt(index)
                : default;
        }
        internal set
        {
            if (value != null && (value.Price == 0 || value.Size == 0) && (index < _byRank.Values.Count))
            {
                var key = _byRank.Keys.ElementAt(index);
                _byRank.RemoveAt(index);
                _byPrice.Remove(key);
            }
            else if(value != null && _byRank.ContainsKey(value.Price))
            {
                var key = _byRank.Keys.ElementAt(index);
                _byRank[key] = value;
                _byPrice[key] = value;
            }
            else if (value is { Price: > 0, Size: > 0 })
            {
                _byRank[value.Price] = value;
                _byPrice[value.Price] = value;
            }
        }
    }

It is proving quite tricky to iron out, but I will push a fix as soon as I can.

firatbaran commented 8 months ago

You can arrange the Ladder from the beginning and instead of removing section, you can fill the Ladder sections that do not have data as 0.0.