using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using SuccessfulCode.BetfairApi.BettingModels; using SuccessfulCode.BetfairApi.BettingModels.Enums; namespace SuccessfulCode.BetfairApi { public class BettingApiClient { private readonly HttpClient _client; private Authentication _authentication; private const string _endpoint = "https://api.betfair.com/exchange/betting/rest/v1.0/"; public BettingApiClient(Authentication authentication) { _client = new HttpClient(); _authentication = authentication; } private Task ExecuteRequest(string endpoint, object request = null) { return _authentication.ExecuteRequest(endpoint, request); } /// /// Returns a list of Event Types (i.e. Sports) associated with the markets selected by the /// MarketFilter. /// public async Task ListEventTypes(MarketFilter filter, string locale = null) { var request = new { filter, locale }; var result = await ExecuteRequest(_endpoint + "listEventTypes/", request); return result; } /// /// Returns a list of Competitions (i.e., World Cup 2013) associated with the markets selected by the /// MarketFilter. Currently only Football markets have an associated competition. /// public async Task ListCompetitions(MarketFilter filter, string locale = null) { var request = new { filter, locale }; var result = await ExecuteRequest(_endpoint + "listCompetitions/", request); return result; } /// /// Returns a list of time ranges in the granularity specified in the request (i.e. 3PM to 4PM, Aug /// 14th to Aug 15th) associated with the markets selected by the MarketFilter. /// public async Task ListTimeRanges(MarketFilter filter, TimeGranularity granularity) { var request = new { filter, granularity }; var result = await ExecuteRequest(_endpoint + "listTimeRanges/", request); return result; } /// /// Returns a list of Events (i.e, Reading vs. Man United) associated with the markets selected by the /// MarketFilter. /// public async Task ListEvents(MarketFilter filter, string locale = null) { var request = new { filter, locale }; var result = await ExecuteRequest(_endpoint + "listEvents/", request); return result; } /// /// Returns a list of market types (i.e. MATCH_ODDS, NEXT_GOAL) associated with the markets selected by /// the MarketFilter. The market types are always the same, regardless of locale. /// public async Task ListMarketTypes(MarketFilter filter, string locale = null) { var request = new { filter, locale }; var result = await ExecuteRequest(_endpoint + "listMarketTypes/", request); return result; } /// /// Returns a list of Countries associated with the markets selected by the MarketFilter. /// public async Task ListCountries(MarketFilter filter, string locale = null) { var request = new { filter, locale }; var result = await ExecuteRequest(_endpoint + "listCountries/", request); return result; } /// /// Returns a list of Venues (i.e. Cheltenham, Ascot) associated with the markets selected by the /// MarketFilter. Currently, only Horse Racing markets are associated with a Venue. /// public async Task ListVenues(MarketFilter filter, string locale = null) { var request = new { filter, locale }; var result = await ExecuteRequest(_endpoint + "listVenues/", request); return result; } /// /// Returns a list of information about markets that does not change (or changes very rarely). You use /// listMarketCatalogue to retrieve the name of the market, the names of selections and other information about /// markets. /// public async Task ListMarketCatalogue(MarketFilter filter, MarketProjection[] marketProjection = null, int? maxResults = null, MarketSort? sort = null, string locale = null) { var request = new { filter, marketProjection, sort, maxResults, locale }; var result = await ExecuteRequest(_endpoint + "listMarketCatalogue/", request); return result; } /// /// Returns a list of dynamic data about markets. Dynamic data includes prices, the status of the /// market, the status of selections, the traded volume, and the status of any orders you have placed in the /// market. /// public async Task ListMarketBook(string[] marketIds, PriceProjection priceProjection = null, OrderProjection? orderProjection = null, MatchProjection? matchProjection = null, bool? includeOverallPosition = null, bool? partitionMatchedByStrategyRef = null, string[] customerStrategyRefs = null, string currencyCode = null, string locale = null, DateTime? matchedSince = null, string[] betIds = null) { var request = new { marketIds, priceProjection, orderProjection, matchProjection, includeOverallPosition, partitionMatchedByStrategyRef, customerStrategyRefs, currencyCode, locale, matchedSince, betIds }; var result = await ExecuteRequest(_endpoint + "listMarketBook/", request); return result; } /// /// Returns a list of dynamic data about a market and a specified runner. Dynamic data includes prices, the status of the /// market, the status of selections, the traded volume, and the status of any orders you have placed in the /// market. /// public async Task ListRunnerBook(string marketId,long selectionId, double? handicap = null, PriceProjection priceProjection = null, OrderProjection? orderProjection = null, MatchProjection? matchProjection = null, bool? includeOverallPosition = null, bool? partitionMatchedByStrategyRef = null, string[] customerStrategyRefs = null, string currencyCode = null, string locale = null, DateTime? matchedSince = null, string[] betIds = null) { var request = new { marketId, selectionId, handicap, priceProjection, orderProjection, matchProjection, includeOverallPosition, partitionMatchedByStrategyRef, customerStrategyRefs, currencyCode, locale, matchedSince, betIds }; var result = await ExecuteRequest(_endpoint + "listRunnerBook/", request); return result; } /// /// Returns a list of your current orders. Optionally you can filter and sort your current orders using /// the /// various parameters, setting none of the parameters will return all of your current orders, up to a maximum /// of 1000 bets, ordered BY_BET and sorted /// EARLIEST_TO_LATEST. To retrieve more than 1000 orders, you need to make use of the fromRecord and /// recordCount parameters. /// public async Task ListCurrentOrders(string[] betIds = null, string[] marketIds = null, OrderProjection? orderProjection = null, string[] customerOrderRefs = null, string[] customerStrategyRefs = null, TimeRange placedDateRange = null, TimeRange dateRange = null, OrderBy? orderBy = null, SortDir? sortDir = null, int? fromRecord = null, int? recordCount = null) { var request = new { betIds, marketIds, orderProjection, customerOrderRefs, customerStrategyRefs, placedDateRange, dateRange, orderBy, sortDir, fromRecord, recordCount }; var result = await ExecuteRequest(_endpoint + "listCurrentOrders/", request); return result; } /// /// Returns a List of bets based on the bet status, ordered by settled date /// public async Task ListClearedOrders(BetStatus betStatus, string[] eventTypeIds = null, string[] eventIds = null, string[] marketIds = null, RunnerId[] runnerIds = null, string[] betIds = null, string[] customerOrderRefs = null, string[] customerStrategyRefs = null, Side? side = null, TimeRange settledDateRange = null, GroupBy? groupBy = null, bool? includeItemDescription = null, string locale = null, int? fromRecord = null, int? recordCount = null) { var request = new { betStatus, eventTypeIds, eventIds, marketIds, runnerIds, betIds, customerOrderRefs, customerStrategyRefs, side, settledDateRange, groupBy, includeItemDescription, locale, fromRecord, recordCount }; var result = await ExecuteRequest(_endpoint + "listClearedOrders/", request); return result; } /// /// Place new orders into market. LIMIT orders below the minimum bet size are allowed if /// there is an unmatched bet at the same price in the market. This operation is atomic in that /// all orders will be placed or none will be placed. /// public async Task PlaceOrders(string marketId, PlaceInstruction[] instructions, string customerRef = null, MarketVersion marketVersion = null, string customerStrategyRef = null, bool? async = null) { var request = new { marketId, instructions, customerRef, marketVersion, customerStrategyRef, async }; var result = await ExecuteRequest(_endpoint + "placeOrders/", request); return result; } /// /// Cancel all bets OR cancel all bets on a market OR fully or partially cancel particular orders on a /// market. Only LIMIT orders an be cancelled or partially cancelled once placed. /// public async Task CancelOrders(string marketId = null, CancelInstruction[] instructions = null, string customerRef = null) { var request = new { marketId, instructions, customerRef }; var result = await ExecuteRequest(_endpoint + "cancelOrders/", request); return result; } /// /// This operation is logically a bulk cancel followed by a bulk place. The cancel is completed first then /// the new orders are placed. The new orders will be placed atomically in that they will all be placed or none /// will be placed. /// In the case where the new orders cannot be placed the cancellations will not be rolled back. /// See ReplaceInstruction. /// public async Task ReplaceOrders(string marketId, ReplaceInstruction[] instructions, string customerRef = null, MarketVersion marketVersion = null, bool? async = null) { var request = new { marketId, instructions, customerRef, marketVersion, async }; var result = await ExecuteRequest(_endpoint + "replaceOrders/", request); return result; } /// /// Update non-exposure changing fields /// public async Task UpdateOrders(string marketId, UpdateInstruction[] instructions, string customerRef = null) { var request = new { marketId, instructions, customerRef }; var result = await ExecuteRequest(_endpoint + "updateOrders/", request); return result; } /// /// Retrieve profit and loss for a given list of markets. The values are calculated using matched bets and optionally settled bets. /// Only odds markets are implemented, markets of other types are silently ignored. /// public async Task ListMarketProfitAndLoss(string[] marketIds, bool? includeSettledBets = null, bool? includeBspBets = null, bool? netOfCommission = null) { var request = new { marketIds, includeSettledBets, includeBspBets, netOfCommission }; var result = await ExecuteRequest(_endpoint + "listMarketProfitAndLoss/", request); return result; } /// /// Create/update default exposure limit for market groups of given type. /// New value and breach action will be immediately applied to existing instances of this type (unless overridden using setExposureLimitForMarketGroup). /// If default values are overridden for market groups (using setExposureLimitForMarketGroup), overrides will NOT be /// touched. /// In order to clear this limit "removeDefaultExposureLimitForMarketGroups" operation should be used. It's not allowed to set default limit to an /// empty limit (see type ExposureLimit). /// public async Task SetDefaultExposureLimitForMarketGroups(MarketGroupType marketGroupType, ExposureLimit limit) { var request = new { marketGroupType, limit }; var result = await ExecuteRequest(_endpoint + "setDefaultExposureLimitForMarketGroups/", request); return result; } /// /// Create/update exposure limit for a market group. /// New limit will be applied immediately (even if a default limit exists for this type). /// The limit will be deleted upon account action (see deleteMarketGroupExposureLimit) or when no active markets /// remain under market group. /// It is possible to "invalidate" default limit for a specific group by using a "empty" limit (see type ExposureLimit). /// Upon successful execution of the request, the effective limit for this group will be the one set by this request (Properties will NOT be inherited from default limit). /// public async Task SetExposureLimitForMarketGroup(MarketGroup marketGroup, ExposureLimit limit) { var request = new { marketGroup, limit }; var result = await ExecuteRequest(_endpoint + "setExposureLimitForMarketGroup/", request); return result; } /// /// Remove default exposure limit for a market group type. /// This operation will NOT remove/update any market group limits. /// public async Task RemoveDefaultExposureLimitForMarketGroups(MarketGroupType marketGroupType) { var request = new { marketGroupType }; var result = await ExecuteRequest(_endpoint + "removeDefaultExposureLimitForMarketGroups/", request); return result; } /// /// Delete exposure limit for a market group. /// If a default exposure limit exist for market type, it takes effect immediately. /// public async Task RemoveExposureLimitForMarketGroup(MarketGroup marketGroup) { var request = new { marketGroup }; var result = await ExecuteRequest(_endpoint + "removeExposureLimitForMarketGroup/", request); return result; } /// /// Response to this request returns default group limit and group limits grouped by type. /// If marketGroupTypeFilter is not populated values for all types are returned. /// The response will always contain the default limit. It is possible to control which groups to return using marketGroupsFilter parameter. /// If marketGroupsFilter is not set all group limits are returned. If an emtpy list is passed only default limit(s) is returned. /// When marketGroupTypeFilter and marketGroupsFilter used together, all groups in marketGroupsFilter are required to be of same type (type used in marketGroupTypeFilter). /// public async Task ListExposureLimitsForMarketGroups(MarketGroupType? marketGroupTypeFilter = null, MarketGroup[] marketGroupFilter = null) { var request = new { marketGroupTypeFilter, marketGroupFilter }; var result = await ExecuteRequest(_endpoint + "listExposureLimitsForMarketGroups/", request); return result; } /// /// Unblock a market group after it has been blocked due to the breach of a previously set exposure limit. /// public async Task UnblockMarketGroup(MarketGroup marketGroup) { var request = new { marketGroup }; var result = await ExecuteRequest(_endpoint + "unblockMarketGroup/", request); return result; } /// /// Retrieves events from exposure reuse enabled events list. To edit this list use /// addExposureReuseEnabledEvents and removeExposureReuseEnabledEvents operations. /// public async Task GetExposureReuseEnabledEvents() { var result = await ExecuteRequest(_endpoint + "getExposureReuseEnabledEvents/"); return result; } /// /// Enables events for exposure reuse by appending them to the current list of events already enabled. /// public async Task AddExposureReuseEnabledEvents(long[] eventIds) { var request = new { eventIds }; var result = await ExecuteRequest(_endpoint + "addExposureReuseEnabledEvents/", request); return result; } /// /// Removes events from exposure reuse enabled events list. /// public async Task RemoveExposureReuseEnabledEvents(long[] eventIds) { var request = new { eventIds }; var result = await ExecuteRequest(_endpoint + "removeExposureReuseEnabledEvents/", request); return result; } } }