using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Arbitrage { public class BaseDataService : IDataService { private LadbrokesService mLadBrokesService { get; set; } private UnibetService mUnibetService { get; set; } private BookerkeeperEntityComparer mBookerkeeperEntityComparer { get; set; } public BaseDataService() { mLadBrokesService = new LadbrokesService(); mUnibetService = new UnibetService(); mBookerkeeperEntityComparer = new BookerkeeperEntityComparer(); } public async Task InitialiseAsync() { await mLadBrokesService.InitialiseAsync(); await mUnibetService.InitialiseAsync(); } public async Task>> ReconcileSportEventsAsync(Sport sport) { Task> ladbrokesEventsTask = mLadBrokesService.GetSportEventsAsync(sport); Task> unibetEventsTask = mUnibetService.GetSportEventsAsync(sport); Task.WaitAll(ladbrokesEventsTask, unibetEventsTask); List ladbrokesEvents = ladbrokesEventsTask.Result.ToList(); List unibetEvents = unibetEventsTask.Result.ToList(); List> allBookkeeperEvents = new List>(); allBookkeeperEvents.Add(ladbrokesEvents); allBookkeeperEvents.Add(unibetEvents); IEnumerable> bookkeeperEventGroups = FindEquiavelentBookkeeperEventsInLists(allBookkeeperEvents); Dictionary> events = new Dictionary>(); foreach (List group in bookkeeperEventGroups) { events.Add(new Event() { Competition = group[0].Competition, Name = group[0].Name, Sport = sport, Commencement = group[0].Commencement, TMPUnibetEventId = group[1].BookkeeperEventId }, group); } return events; } public async Task>> ReconcileEventMarketsAsync(Event @event, IEnumerable bookkeeperEvents) { if (@event.Name == "Tepatitlan FC vs CD Tapatio") { var v = ""; } Task> ladbrokesMarketsTask = mLadBrokesService .GetEventMarketsAsync(bookkeeperEvents.First(x => x.Bookkeeper == Bookkeeper.Ladbrokes)); Task> unibetMarketsTask = mUnibetService .GetEventMarketsAsync(bookkeeperEvents.First(x => x.Bookkeeper == Bookkeeper.Unibet)); Task.WaitAll(ladbrokesMarketsTask, unibetMarketsTask); List ladbrokesMarkets = ladbrokesMarketsTask.Result.ToList(); List unibetMarkets = unibetMarketsTask.Result.ToList(); List> allBookkeeperMarkets = new List>(); allBookkeeperMarkets.Add(ladbrokesMarkets); allBookkeeperMarkets.Add(unibetMarkets); IEnumerable> bookkeeperMarketGroups = FindEquiavelentBookkeeperMarketsInLists(allBookkeeperMarkets, @event); Dictionary> markets = new Dictionary>(); foreach (List group in bookkeeperMarketGroups) { markets.Add(new Market() { Name = group[0].Name }, group); } return markets; } // A market is only valid if all odds for all bookkeepers operating in that market can be reconciled public async Task> ReconcileMarketOddsAsync(Event @event, IEnumerable bookkeeperMarkets) { Task> ladbrokesOddsTask = mLadBrokesService .GetMarketOddsAsync(@event, bookkeeperMarkets.First(x => x.Bookkeeper == Bookkeeper.Ladbrokes)); Task> unibetOddsTask = mUnibetService .GetMarketOddsAsync(@event, bookkeeperMarkets.First(x => x.Bookkeeper == Bookkeeper.Unibet)); Task.WaitAll(ladbrokesOddsTask, unibetOddsTask); List ladbrokesOdds = ladbrokesOddsTask.Result.ToList(); List unibetOdds = unibetOddsTask.Result.ToList(); List>> bookkeeperOdds = new List>>(); bookkeeperOdds.Add(new KeyValuePair>(Bookkeeper.Ladbrokes, ladbrokesOdds)); bookkeeperOdds.Add(new KeyValuePair>(Bookkeeper.Unibet, unibetOdds)); int numOddsPerBookkeeper = bookkeeperOdds.First().Value.Count; foreach (List odds in bookkeeperOdds.Select(x => x.Value)) { if (odds.Count != numOddsPerBookkeeper) { return new List(); } } List> bookkeeperOddsGroups = new List>(); for (int i = 0; i < bookkeeperOdds.Count - 1; i++) { List bookkeeperOdds1 = bookkeeperOdds[i].Value; List bookkeeperOdds2 = bookkeeperOdds[i + 1].Value; for (int m = 0; m < numOddsPerBookkeeper; m++) { bookkeeperOddsGroups.Add(new List()); bool foundMatch = false; for (int n = 0; n < numOddsPerBookkeeper; n++) { if (mBookerkeeperEntityComparer.OddsEqual(bookkeeperOdds1[m], bookkeeperOdds2[n], @event)) { foundMatch = true; bookkeeperOddsGroups[m].Add(bookkeeperOdds1[m]); if (i == bookkeeperOdds.Count - 2) { bookkeeperOddsGroups[m].Add(bookkeeperOdds2[n]); } break; } } if (!foundMatch) { return new List(); } } } List standardisedOdds = new List(); foreach (List bookkeeperOddsGroup in bookkeeperOddsGroups) { standardisedOdds.Add(new Odds() { Outcome = bookkeeperOddsGroup[0].Outcome, BookkeeperOdds = bookkeeperOddsGroup }); } return standardisedOdds; } private IEnumerable> FindEquiavelentBookkeeperEventsInLists(List> bookkeeperEvents) { List> groups = new List>(); for (int i = 0; i < bookkeeperEvents.Count; i++) { for (int j = i + 1; j < bookkeeperEvents.Count; j++) { for (int m = 0; m < bookkeeperEvents[i].Count; m++) { for (int n = 0; n < bookkeeperEvents[j].Count; n++) { if (mBookerkeeperEntityComparer.EventsEqual(bookkeeperEvents[i][m], bookkeeperEvents[j][n])) { bool foundMatchToExistingGroup = false; for (int k = 0; k < groups.Count; k++) { if (groups[k][0] == bookkeeperEvents[i][m]) // Works if this is reference based { foundMatchToExistingGroup = true; groups[k].Add(bookkeeperEvents[j][n]); break; } } if (!foundMatchToExistingGroup) { groups.Add(new List() { bookkeeperEvents[i][m], bookkeeperEvents[j][n] }); } bookkeeperEvents[j].RemoveAt(n); break; } } } } } return groups; } private IEnumerable> FindEquiavelentBookkeeperMarketsInLists(List> bookkeeperMarkets, Event @event) { List> groups = new List>(); for (int i = 0; i < bookkeeperMarkets.Count; i++) { for (int j = i + 1; j < bookkeeperMarkets.Count; j++) { // Get all pairings with scores List> bookkeeperMarketPairings = new List>(); for (int m = 0; m < bookkeeperMarkets[i].Count; m++) { for (int n = 0; n < bookkeeperMarkets[j].Count; n++) { double similarity = mBookerkeeperEntityComparer.CalculateMarketSimilarity( bookkeeperMarkets[i][m], bookkeeperMarkets[j][n], @event); if (similarity > 0.8) { bookkeeperMarketPairings.Add(new KeyValuePair<(BookkeeperMarket, BookkeeperMarket), double>( (bookkeeperMarkets[i][m], bookkeeperMarkets[j][n]), similarity)); } } } // Order by score and then by string length similarity bookkeeperMarketPairings = bookkeeperMarketPairings.OrderByDescending(x => x.Value) .ThenBy(x => Math.Abs(x.Key.Item1.Name.Length - x.Key.Item2.Name.Length)) .ToList(); foreach (var pairing in bookkeeperMarketPairings) { Console.WriteLine($"{pairing.Value}:{pairing.Key.Item1.Name}:{pairing.Key.Item2.Name}"); } Console.WriteLine(); while (bookkeeperMarketPairings.Any()) { (BookkeeperMarket bookkeeperMarket1, BookkeeperMarket bookkeeperMarket2) = bookkeeperMarketPairings.First().Key; bool foundMatchToExistingGroup = false; for (int k = 0; k < groups.Count; k++) { if (groups[k].Contains(bookkeeperMarket1)) // Works if this is reference based { foundMatchToExistingGroup = true; groups[k].Add(bookkeeperMarket2); break; } } if (!foundMatchToExistingGroup) { groups.Add(new List() { bookkeeperMarket1, bookkeeperMarket2 }); } bookkeeperMarketPairings.RemoveAll(x => { if (x.Key.Item1 == bookkeeperMarket1 || x.Key.Item1 == bookkeeperMarket2 || x.Key.Item2 == bookkeeperMarket1 || x.Key.Item2 == bookkeeperMarket2) { return true; } return false; }); } } } return groups; } } }