From ad872794a6245abbbe530563c9f71578850183f1 Mon Sep 17 00:00:00 2001 From: David Martinez Date: Wed, 1 Mar 2023 12:17:57 -0500 Subject: [PATCH 1/8] News endpoint was done, model, API and test were created --- .../FunctionalTests/NewsTests.cs | 26 +++++++++++++++++++ Polygon.Net/API/NewsApi.cs | 21 +++++++++++++++ Polygon.Net/Client/IPolygonClient.cs | 2 ++ Polygon.Net/Models/NewsInfo.cs | 20 ++++++++++++++ Polygon.Net/Models/NewsResponse.cs | 25 ++++++++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 Polygon.Net.Tests/FunctionalTests/NewsTests.cs create mode 100644 Polygon.Net/API/NewsApi.cs create mode 100644 Polygon.Net/Models/NewsInfo.cs create mode 100644 Polygon.Net/Models/NewsResponse.cs diff --git a/Polygon.Net.Tests/FunctionalTests/NewsTests.cs b/Polygon.Net.Tests/FunctionalTests/NewsTests.cs new file mode 100644 index 0000000..f42ba10 --- /dev/null +++ b/Polygon.Net.Tests/FunctionalTests/NewsTests.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Polygon.Net.Models; +using static Polygon.Net.Tests.TestManager; + +namespace Polygon.Net.Tests.FunctionalTests +{ + [TestClass] + public class NewsApiTests + { + private const string STATUS_OK = "OK"; + + [TestMethod] + public async Task GetNewsSucceedsAsync() + { + var NewsResponse = await PolygonTestClient.GetNewsAsync(); + + Assert.IsInstanceOfType(NewsResponse.Results, typeof(List)); + Assert.IsNotNull(NewsResponse); + Assert.AreEqual(STATUS_OK, NewsResponse.Status); + } + } +} diff --git a/Polygon.Net/API/NewsApi.cs b/Polygon.Net/API/NewsApi.cs new file mode 100644 index 0000000..6d5b826 --- /dev/null +++ b/Polygon.Net/API/NewsApi.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Polygon.Net.Models; +using static Pineapple.Common.Preconditions; + +namespace Polygon.Net; + +public partial class PolygonClient +{ + private const string NEWS_ENDPOINT = "/v2/reference/news"; + + public async Task GetNewsAsync() + { + var RequestUrl = $"{_polygonSettings.ApiBaseUrl}{NEWS_ENDPOINT}"; + var Request = await Get(RequestUrl).ConfigureAwait(false); + + return JsonConvert.DeserializeObject(Request); + } +} diff --git a/Polygon.Net/Client/IPolygonClient.cs b/Polygon.Net/Client/IPolygonClient.cs index 06c2e54..7a3f4c9 100644 --- a/Polygon.Net/Client/IPolygonClient.cs +++ b/Polygon.Net/Client/IPolygonClient.cs @@ -56,5 +56,7 @@ Task GetAggregatesBarsAsync( Task> GetMarketHolidaysAsync(); Task> GetTickerTypesAsync(string assetClass = default, string locale = default); + + Task GetNewsAsync(); } } diff --git a/Polygon.Net/Models/NewsInfo.cs b/Polygon.Net/Models/NewsInfo.cs new file mode 100644 index 0000000..69db7ec --- /dev/null +++ b/Polygon.Net/Models/NewsInfo.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Polygon.Net.Models; + +public class NewsInfo +{ + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("keywords")] + public List Keywords { get; set; } +} diff --git a/Polygon.Net/Models/NewsResponse.cs b/Polygon.Net/Models/NewsResponse.cs new file mode 100644 index 0000000..db0dc71 --- /dev/null +++ b/Polygon.Net/Models/NewsResponse.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Polygon.Net.Models; +public class NewsResponse +{ + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("request_id")] + public string RequestId { get; set; } + + [JsonProperty("count")] + public int Count { get; set; } + + [JsonProperty("next_url")] + public string NextUrl { get; set; } + + [JsonProperty("results")] + public List Results { get; set; } +} From 345ab2f34a11eb642c58e60bc57cf61cb846619f Mon Sep 17 00:00:00 2001 From: David Martinez Date: Thu, 2 Mar 2023 11:55:36 -0500 Subject: [PATCH 2/8] New fields were added and two instances have been created for the news --- Polygon.Net/API/NewsApi.cs | 36 ++++++++++++++++++++++++---- Polygon.Net/Client/IPolygonClient.cs | 7 ++++-- Polygon.Net/Models/NewsInfo.cs | 12 ++++++++++ 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/Polygon.Net/API/NewsApi.cs b/Polygon.Net/API/NewsApi.cs index 6d5b826..3aa4f75 100644 --- a/Polygon.Net/API/NewsApi.cs +++ b/Polygon.Net/API/NewsApi.cs @@ -11,11 +11,39 @@ public partial class PolygonClient { private const string NEWS_ENDPOINT = "/v2/reference/news"; - public async Task GetNewsAsync() + public async Task GetNewsAsync( + DateTime? startTime, + DateTime? endTime, + string ticker = null, + string order = null, + int? limit = null, + string sort = null + ) { - var RequestUrl = $"{_polygonSettings.ApiBaseUrl}{NEWS_ENDPOINT}"; - var Request = await Get(RequestUrl).ConfigureAwait(false); + var queryParams = new Dictionary + { + { nameof(ticker), ticker }, + { "published_utc.gte", startTime?.ToString() }, + { "published_utc.lte", endTime?.ToString() }, + { nameof(order), order }, + { nameof(limit), limit?.ToString() }, + { nameof(sort), sort }, + }; - return JsonConvert.DeserializeObject(Request); + var queryParamStr = GetQueryParameterString(queryParams); + var requestUrl = $"{_polygonSettings.ApiBaseUrl}{NEWS_ENDPOINT}{queryParamStr}"; + var contentStr = await Get(requestUrl).ConfigureAwait(false); + + return JsonConvert.DeserializeObject(contentStr); + } + + public async Task GetTodayNews( + string ticker = null, + string order = null, + int? limit = null, + string sort = null + ) + { + return await GetNewsAsync(DateTime.Now.Date, null, ticker, order, limit, sort); } } diff --git a/Polygon.Net/Client/IPolygonClient.cs b/Polygon.Net/Client/IPolygonClient.cs index 7a3f4c9..74ef99d 100644 --- a/Polygon.Net/Client/IPolygonClient.cs +++ b/Polygon.Net/Client/IPolygonClient.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; using Polygon.Net.Models; @@ -57,6 +58,8 @@ Task GetAggregatesBarsAsync( Task> GetTickerTypesAsync(string assetClass = default, string locale = default); - Task GetNewsAsync(); + Task GetNewsAsync(DateTime? startTime = null, DateTime? endTime = null, string ticker = null, string order = null, int? limit = null, string sort = null); + + Task GetTodayNews(string ticker = null, string order = default, int? limit = null, string sort = null); } } diff --git a/Polygon.Net/Models/NewsInfo.cs b/Polygon.Net/Models/NewsInfo.cs index 69db7ec..33628fb 100644 --- a/Polygon.Net/Models/NewsInfo.cs +++ b/Polygon.Net/Models/NewsInfo.cs @@ -9,12 +9,24 @@ namespace Polygon.Net.Models; public class NewsInfo { + [JsonProperty("id")] + public string id { get; set; } + [JsonProperty("title")] public string Title { get; set; } [JsonProperty("description")] public string Description { get; set; } + [JsonProperty("author")] + public string author { get; set; } + + [JsonProperty("published_utc")] + public string published_utc { get; set; } + [JsonProperty("keywords")] public List Keywords { get; set; } + + [JsonProperty("tickers")] + public List tickers { get; set; } } From c8fe667d06667c921c2f2effc329cde998aab5ad Mon Sep 17 00:00:00 2001 From: David Martinez Date: Fri, 3 Mar 2023 11:19:15 -0500 Subject: [PATCH 3/8] New comments have been added in Api rest, unit tests were created as well and fix some fields --- .../FunctionalTests/NewsTests.cs | 17 ++++++++++++++ Polygon.Net/API/NewsApi.cs | 22 +++++++++++++++++-- Polygon.Net/Models/NewsInfo.cs | 8 +++---- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/Polygon.Net.Tests/FunctionalTests/NewsTests.cs b/Polygon.Net.Tests/FunctionalTests/NewsTests.cs index f42ba10..b613a1e 100644 --- a/Polygon.Net.Tests/FunctionalTests/NewsTests.cs +++ b/Polygon.Net.Tests/FunctionalTests/NewsTests.cs @@ -22,5 +22,22 @@ public async Task GetNewsSucceedsAsync() Assert.IsNotNull(NewsResponse); Assert.AreEqual(STATUS_OK, NewsResponse.Status); } + + + [TestMethod] + public async Task GetTodayNewsSucceedsAsync() + { + string currentDate = DateTime.Now.Date.ToString("yyyy-MM-dd"); + var newsResponse = await PolygonTestClient.GetTodayNews(); + + Assert.IsInstanceOfType(newsResponse.Results, typeof(List)); + Assert.IsNotNull(newsResponse); + Assert.AreEqual(STATUS_OK, newsResponse.Status); + + foreach (var news in newsResponse.Results) + { + Assert.AreEqual(currentDate, DateTime.Parse(news.Published_utc).ToString("yyyy-MM-dd")); + } + } } } diff --git a/Polygon.Net/API/NewsApi.cs b/Polygon.Net/API/NewsApi.cs index 3aa4f75..f0e837a 100644 --- a/Polygon.Net/API/NewsApi.cs +++ b/Polygon.Net/API/NewsApi.cs @@ -11,6 +11,16 @@ public partial class PolygonClient { private const string NEWS_ENDPOINT = "/v2/reference/news"; + /// + /// Get the news from Polygon. + /// + /// Return results published after this date + /// Return results published before this date + /// The polygon API ticker by default is desc + /// Order results based on the sort field + /// Limit the number of results returned, default is 10 and max is 1000 + /// Sort field used for ordering + /// NewsResponse public async Task GetNewsAsync( DateTime? startTime, DateTime? endTime, @@ -23,8 +33,8 @@ public async Task GetNewsAsync( var queryParams = new Dictionary { { nameof(ticker), ticker }, - { "published_utc.gte", startTime?.ToString() }, - { "published_utc.lte", endTime?.ToString() }, + { "published_utc.gte", startTime?.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss") }, + { "published_utc.lte", endTime?.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss") }, { nameof(order), order }, { nameof(limit), limit?.ToString() }, { nameof(sort), sort }, @@ -37,6 +47,14 @@ public async Task GetNewsAsync( return JsonConvert.DeserializeObject(contentStr); } + /// + /// Get the today's news from Polygon. + /// + /// The polygon API ticker by default is desc + /// Order results based on the sort field + /// Limit the number of results returned, default is 10 and max is 1000 + /// Sort field used for ordering + /// NewsResponse public async Task GetTodayNews( string ticker = null, string order = null, diff --git a/Polygon.Net/Models/NewsInfo.cs b/Polygon.Net/Models/NewsInfo.cs index 33628fb..40e5257 100644 --- a/Polygon.Net/Models/NewsInfo.cs +++ b/Polygon.Net/Models/NewsInfo.cs @@ -10,7 +10,7 @@ namespace Polygon.Net.Models; public class NewsInfo { [JsonProperty("id")] - public string id { get; set; } + public string Id { get; set; } [JsonProperty("title")] public string Title { get; set; } @@ -19,14 +19,14 @@ public class NewsInfo public string Description { get; set; } [JsonProperty("author")] - public string author { get; set; } + public string Author { get; set; } [JsonProperty("published_utc")] - public string published_utc { get; set; } + public string Published_utc { get; set; } [JsonProperty("keywords")] public List Keywords { get; set; } [JsonProperty("tickers")] - public List tickers { get; set; } + public List Tickers { get; set; } } From 4d3e96ba105d46fefb34ef2b1ee928b5a60a210a Mon Sep 17 00:00:00 2001 From: David Martinez Date: Mon, 6 Mar 2023 15:28:46 -0500 Subject: [PATCH 4/8] solved comments --- .../FunctionalTests/NewsTests.cs | 48 +++++++++++++++++-- Polygon.Net/API/NewsApi.cs | 15 +++--- Polygon.Net/Client/IPolygonClient.cs | 4 +- Polygon.Net/Models/NewsInfo.cs | 4 +- Polygon.Net/Models/NewsResponse.cs | 4 +- 5 files changed, 57 insertions(+), 18 deletions(-) diff --git a/Polygon.Net.Tests/FunctionalTests/NewsTests.cs b/Polygon.Net.Tests/FunctionalTests/NewsTests.cs index b613a1e..8ddd521 100644 --- a/Polygon.Net.Tests/FunctionalTests/NewsTests.cs +++ b/Polygon.Net.Tests/FunctionalTests/NewsTests.cs @@ -16,11 +16,49 @@ public class NewsApiTests [TestMethod] public async Task GetNewsSucceedsAsync() { - var NewsResponse = await PolygonTestClient.GetNewsAsync(); + var newsResponse = await PolygonTestClient.GetNewsAsync(); - Assert.IsInstanceOfType(NewsResponse.Results, typeof(List)); - Assert.IsNotNull(NewsResponse); - Assert.AreEqual(STATUS_OK, NewsResponse.Status); + Assert.IsInstanceOfType(newsResponse.Results, typeof(List)); + Assert.IsNotNull(newsResponse); + Assert.AreEqual(STATUS_OK, newsResponse.Status); + } + + [TestMethod] + public async Task GetNewsWithParametersSucceedsAsync() + { + DateTime startTime = new DateTime(2023, 03, 05); // Start Specific Day + DateTime endTime = new DateTime(2023, 03, 06); // End Specific Day + int countNews = 5; + String ticker = "GOOGL"; + DateTime dateTime = startTime; + + var newsResponse = await PolygonTestClient.GetNewsAsync(startTime, endTime, ticker, "asc", countNews, "published_utc"); + + Assert.IsInstanceOfType(newsResponse.Results, typeof(List)); + Assert.IsNotNull(newsResponse); + Assert.AreEqual(STATUS_OK, newsResponse.Status); + Assert.AreEqual(newsResponse.Count, countNews); + + foreach (var news in newsResponse.Results) + { + DateTime publishedNews = DateTime.Parse(news.Published_utc); + + Assert.AreEqual(startTime.ToString("yyyy-MM-dd"), publishedNews.ToString("yyyy-MM-dd")); + Assert.IsTrue(news.Tickers.Contains(ticker), "The ticker was not found inside tickers"); + Assert.IsTrue(publishedNews > dateTime, "Date current news is not greater than before date news"); + + dateTime = publishedNews; + } + + } + + [TestMethod] + public async Task GetNewsEmptyAsync() + { + var newsResponse = await PolygonTestClient.GetNewsAsync(null, null, "ABCXYZ"); + + Assert.IsTrue(newsResponse.Count == 0, "the news count should be empty"); + Assert.IsNull(newsResponse.Status); } @@ -28,7 +66,7 @@ public async Task GetNewsSucceedsAsync() public async Task GetTodayNewsSucceedsAsync() { string currentDate = DateTime.Now.Date.ToString("yyyy-MM-dd"); - var newsResponse = await PolygonTestClient.GetTodayNews(); + var newsResponse = await PolygonTestClient.GetTodayNewsAsync(); Assert.IsInstanceOfType(newsResponse.Results, typeof(List)); Assert.IsNotNull(newsResponse); diff --git a/Polygon.Net/API/NewsApi.cs b/Polygon.Net/API/NewsApi.cs index f0e837a..68959fe 100644 --- a/Polygon.Net/API/NewsApi.cs +++ b/Polygon.Net/API/NewsApi.cs @@ -22,8 +22,8 @@ public partial class PolygonClient /// Sort field used for ordering /// NewsResponse public async Task GetNewsAsync( - DateTime? startTime, - DateTime? endTime, + DateTime? startTime, + DateTime? endTime, string ticker = null, string order = null, int? limit = null, @@ -40,11 +40,12 @@ public async Task GetNewsAsync( { nameof(sort), sort }, }; - var queryParamStr = GetQueryParameterString(queryParams); - var requestUrl = $"{_polygonSettings.ApiBaseUrl}{NEWS_ENDPOINT}{queryParamStr}"; - var contentStr = await Get(requestUrl).ConfigureAwait(false); + string queryParamStr = GetQueryParameterString(queryParams); + string requestUrl = $"{_polygonSettings.ApiBaseUrl}{NEWS_ENDPOINT}{queryParamStr}"; + string contentStr = await Get(requestUrl).ConfigureAwait(false); + NewsResponse newsResponse = JsonConvert.DeserializeObject(contentStr); - return JsonConvert.DeserializeObject(contentStr); + return newsResponse.Results.Count == 0 ? new NewsResponse() : newsResponse; } /// @@ -55,7 +56,7 @@ public async Task GetNewsAsync( /// Limit the number of results returned, default is 10 and max is 1000 /// Sort field used for ordering /// NewsResponse - public async Task GetTodayNews( + public async Task GetTodayNewsAsync( string ticker = null, string order = null, int? limit = null, diff --git a/Polygon.Net/Client/IPolygonClient.cs b/Polygon.Net/Client/IPolygonClient.cs index 74ef99d..48ca6d7 100644 --- a/Polygon.Net/Client/IPolygonClient.cs +++ b/Polygon.Net/Client/IPolygonClient.cs @@ -58,8 +58,8 @@ Task GetAggregatesBarsAsync( Task> GetTickerTypesAsync(string assetClass = default, string locale = default); - Task GetNewsAsync(DateTime? startTime = null, DateTime? endTime = null, string ticker = null, string order = null, int? limit = null, string sort = null); + Task GetNewsAsync(DateTime? startTime = default, DateTime? endTime = default, string ticker = null, string order = null, int? limit = null, string sort = null); - Task GetTodayNews(string ticker = null, string order = default, int? limit = null, string sort = null); + Task GetTodayNewsAsync(string ticker = default, string order = null, int? limit = null, string sort = null); } } diff --git a/Polygon.Net/Models/NewsInfo.cs b/Polygon.Net/Models/NewsInfo.cs index 40e5257..1a378df 100644 --- a/Polygon.Net/Models/NewsInfo.cs +++ b/Polygon.Net/Models/NewsInfo.cs @@ -25,8 +25,8 @@ public class NewsInfo public string Published_utc { get; set; } [JsonProperty("keywords")] - public List Keywords { get; set; } + public List Keywords { get; set; } = new List() { }; [JsonProperty("tickers")] - public List Tickers { get; set; } + public List Tickers { get; set; } = new List() { }; } diff --git a/Polygon.Net/Models/NewsResponse.cs b/Polygon.Net/Models/NewsResponse.cs index db0dc71..54f0812 100644 --- a/Polygon.Net/Models/NewsResponse.cs +++ b/Polygon.Net/Models/NewsResponse.cs @@ -15,11 +15,11 @@ public class NewsResponse public string RequestId { get; set; } [JsonProperty("count")] - public int Count { get; set; } + public int Count { get; set; } = 0; [JsonProperty("next_url")] public string NextUrl { get; set; } [JsonProperty("results")] - public List Results { get; set; } + public List Results { get; set; } = new List() { }; } From 48daa7b10ea001e8f1b700a012f25d193dba47e4 Mon Sep 17 00:00:00 2001 From: David Martinez Date: Mon, 6 Mar 2023 11:39:07 -0500 Subject: [PATCH 5/8] version updated --- Polygon.Net/Polygon.Net.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Polygon.Net/Polygon.Net.csproj b/Polygon.Net/Polygon.Net.csproj index a6a8e5c..b0b229a 100644 --- a/Polygon.Net/Polygon.Net.csproj +++ b/Polygon.Net/Polygon.Net.csproj @@ -2,9 +2,9 @@ net7.0 - 1.2.0 - 1.2.0.0 - 1.2.0.0 + 1.2.1 + 1.2.1.0 + 1.2.1.0 MILL5 A .NET class library for use against the Polygon APIs supporting .NET Standard, .NET Core, .NET 5.0, and the new .NET 6.0 Copyright © MILL5, LLC 2021 From bda4142a28a50c3908b65fa815bf91d93e9e20e8 Mon Sep 17 00:00:00 2001 From: David Martinez Date: Tue, 7 Mar 2023 14:23:08 -0500 Subject: [PATCH 6/8] New fields have been created(ArticleUrl) and others have been modified(PublishUtc,HashNextUrl). Furthermore, new tests and methods services have been created --- .../FunctionalTests/NewsTests.cs | 15 +++-- Polygon.Net/API/NewsApi.cs | 58 ++++++++++++------- Polygon.Net/Client/IPolygonClient.cs | 20 ++++++- Polygon.Net/Models/NewsInfo.cs | 5 +- Polygon.Net/Models/NewsResponse.cs | 20 ++++++- Polygon.Net/Polygon.Net.csproj | 8 ++- 6 files changed, 93 insertions(+), 33 deletions(-) diff --git a/Polygon.Net.Tests/FunctionalTests/NewsTests.cs b/Polygon.Net.Tests/FunctionalTests/NewsTests.cs index 8ddd521..a5a0e30 100644 --- a/Polygon.Net.Tests/FunctionalTests/NewsTests.cs +++ b/Polygon.Net.Tests/FunctionalTests/NewsTests.cs @@ -41,7 +41,7 @@ public async Task GetNewsWithParametersSucceedsAsync() foreach (var news in newsResponse.Results) { - DateTime publishedNews = DateTime.Parse(news.Published_utc); + DateTime publishedNews = DateTime.Parse(news.PublishedUtc); Assert.AreEqual(startTime.ToString("yyyy-MM-dd"), publishedNews.ToString("yyyy-MM-dd")); Assert.IsTrue(news.Tickers.Contains(ticker), "The ticker was not found inside tickers"); @@ -49,7 +49,6 @@ public async Task GetNewsWithParametersSucceedsAsync() dateTime = publishedNews; } - } [TestMethod] @@ -61,7 +60,6 @@ public async Task GetNewsEmptyAsync() Assert.IsNull(newsResponse.Status); } - [TestMethod] public async Task GetTodayNewsSucceedsAsync() { @@ -74,8 +72,17 @@ public async Task GetTodayNewsSucceedsAsync() foreach (var news in newsResponse.Results) { - Assert.AreEqual(currentDate, DateTime.Parse(news.Published_utc).ToString("yyyy-MM-dd")); + Assert.AreEqual(currentDate, DateTime.Parse(news.PublishedUtc).ToString("yyyy-MM-dd")); } } + + [TestMethod] + public async Task GetNextPageNewsAsync() + { + var newsResponse = await PolygonTestClient.GetNextPageNewsAsync("YXA9MjAyMy0wMy0wN1QxNCUzQTAxJTNBMDBaJmFzPUpvSnFQM0s4Zl9NSXVsY01Kb0NiaUNZeXBuQTZDdDd0RldrMmRiLVhYTkEmb3JkZXI9ZGVzY2VuZGluZw"); + Assert.IsInstanceOfType(newsResponse.Results, typeof(List)); + Assert.IsNotNull(newsResponse); + Assert.AreEqual(STATUS_OK, newsResponse.Status); + } } } diff --git a/Polygon.Net/API/NewsApi.cs b/Polygon.Net/API/NewsApi.cs index 68959fe..25673ed 100644 --- a/Polygon.Net/API/NewsApi.cs +++ b/Polygon.Net/API/NewsApi.cs @@ -12,7 +12,7 @@ public partial class PolygonClient private const string NEWS_ENDPOINT = "/v2/reference/news"; /// - /// Get the news from Polygon. + /// Get the Polygon news. /// /// Return results published after this date /// Return results published before this date @@ -22,23 +22,28 @@ public partial class PolygonClient /// Sort field used for ordering /// NewsResponse public async Task GetNewsAsync( - DateTime? startTime, - DateTime? endTime, - string ticker = null, - string order = null, - int? limit = null, - string sort = null + DateTime? startTime = default, + DateTime? endTime = default, + string ticker = null, + string order = null, + int? limit = null, + string sort = null, + string nextPage = null ) { - var queryParams = new Dictionary - { - { nameof(ticker), ticker }, - { "published_utc.gte", startTime?.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss") }, - { "published_utc.lte", endTime?.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss") }, - { nameof(order), order }, - { nameof(limit), limit?.ToString() }, - { nameof(sort), sort }, - }; + var queryParams = new Dictionary(); + + if(nextPage != null) { + queryParams.Add("cursor", nextPage); + }else { + if(ticker != null) queryParams.Add(nameof(ticker), ticker); + if (order != null) queryParams.Add(nameof(order), order); + if (limit != null) queryParams.Add(nameof(limit), limit + ""); + if (sort != null) queryParams.Add(nameof(sort), sort); + + queryParams.Add("published_utc.gte", startTime?.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss")); + queryParams.Add("published_utc.lte", endTime?.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss")); + } string queryParamStr = GetQueryParameterString(queryParams); string requestUrl = $"{_polygonSettings.ApiBaseUrl}{NEWS_ENDPOINT}{queryParamStr}"; @@ -49,7 +54,7 @@ public async Task GetNewsAsync( } /// - /// Get the today's news from Polygon. + /// Get the today's Polygon news. /// /// The polygon API ticker by default is desc /// Order results based on the sort field @@ -57,12 +62,21 @@ public async Task GetNewsAsync( /// Sort field used for ordering /// NewsResponse public async Task GetTodayNewsAsync( - string ticker = null, - string order = null, - int? limit = null, - string sort = null + string ticker = null, + string order = null, + int? limit = null, + string sort = null, + string nextPage = null ) { - return await GetNewsAsync(DateTime.Now.Date, null, ticker, order, limit, sort); + return await GetNewsAsync(DateTime.Now.Date, null, ticker, order, limit, sort, nextPage); + } + + /// + /// Get the next page of Polygon news. + /// + /// hash of the next page + public async Task GetNextPageNewsAsync(string nextPage = null){ + return await GetNewsAsync(null, null, null, null, null, null, nextPage); } } diff --git a/Polygon.Net/Client/IPolygonClient.cs b/Polygon.Net/Client/IPolygonClient.cs index 48ca6d7..3362995 100644 --- a/Polygon.Net/Client/IPolygonClient.cs +++ b/Polygon.Net/Client/IPolygonClient.cs @@ -58,8 +58,24 @@ Task GetAggregatesBarsAsync( Task> GetTickerTypesAsync(string assetClass = default, string locale = default); - Task GetNewsAsync(DateTime? startTime = default, DateTime? endTime = default, string ticker = null, string order = null, int? limit = null, string sort = null); + Task GetNewsAsync( + DateTime? startTime = default, + DateTime? endTime = default, + string ticker = null, + string order = null, + int? limit = null, + string sort = null, + string nextPage = null + ); - Task GetTodayNewsAsync(string ticker = default, string order = null, int? limit = null, string sort = null); + Task GetTodayNewsAsync( + string ticker = default, + string order = null, + int? limit = null, + string sort = null, + string nextPage = null + ); + + Task GetNextPageNewsAsync(string nextPage = null); } } diff --git a/Polygon.Net/Models/NewsInfo.cs b/Polygon.Net/Models/NewsInfo.cs index 1a378df..fa6d4fc 100644 --- a/Polygon.Net/Models/NewsInfo.cs +++ b/Polygon.Net/Models/NewsInfo.cs @@ -22,7 +22,10 @@ public class NewsInfo public string Author { get; set; } [JsonProperty("published_utc")] - public string Published_utc { get; set; } + public string PublishedUtc { get; set; } + + [JsonProperty("article_url")] + public string ArticleUrl { get; set; } [JsonProperty("keywords")] public List Keywords { get; set; } = new List() { }; diff --git a/Polygon.Net/Models/NewsResponse.cs b/Polygon.Net/Models/NewsResponse.cs index 54f0812..d4b085b 100644 --- a/Polygon.Net/Models/NewsResponse.cs +++ b/Polygon.Net/Models/NewsResponse.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Web; using Newtonsoft.Json; namespace Polygon.Net.Models; @@ -17,8 +18,25 @@ public class NewsResponse [JsonProperty("count")] public int Count { get; set; } = 0; + private string _hashNextUrl; + [JsonProperty("next_url")] - public string NextUrl { get; set; } + public string HashNextUrl + { + get { return _hashNextUrl; } + set + { + string hash = null; + if (value != null) + { + Uri uri = new Uri(value); + var query = HttpUtility.ParseQueryString(uri.Query); + hash = query.Get("cursor"); + } + + _hashNextUrl = hash != null ? hash : value; + } + } [JsonProperty("results")] public List Results { get; set; } = new List() { }; diff --git a/Polygon.Net/Polygon.Net.csproj b/Polygon.Net/Polygon.Net.csproj index b0b229a..d7aa403 100644 --- a/Polygon.Net/Polygon.Net.csproj +++ b/Polygon.Net/Polygon.Net.csproj @@ -2,9 +2,9 @@ net7.0 - 1.2.1 - 1.2.1.0 - 1.2.1.0 + 1.2.2 + 1.2.2.0 + 1.2.2.0 MILL5 A .NET class library for use against the Polygon APIs supporting .NET Standard, .NET Core, .NET 5.0, and the new .NET 6.0 Copyright © MILL5, LLC 2021 @@ -19,11 +19,13 @@ snupkg MIT polygon.png + e0e1f4de-446a-4f5c-879a-30ff77b4e606 + From e4336ddd5a580afa157b67a3eebf7a5617979a76 Mon Sep 17 00:00:00 2001 From: David Martinez Date: Wed, 8 Mar 2023 11:24:23 -0500 Subject: [PATCH 7/8] Fixed comments --- .../FunctionalTests/NewsTests.cs | 42 +++++------ Polygon.Net.Tests/Polygon.Net.Tests.csproj | 4 +- Polygon.Net/API/NewsApi.cs | 74 ++++--------------- Polygon.Net/Client/IPolygonClient.cs | 31 +++----- Polygon.Net/Http/QueryStringExtension.cs | 13 ++++ Polygon.Net/Models/NewsResponse.cs | 15 ++-- Polygon.Net/Polygon.Net.csproj | 3 + 7 files changed, 70 insertions(+), 112 deletions(-) create mode 100644 Polygon.Net/Http/QueryStringExtension.cs diff --git a/Polygon.Net.Tests/FunctionalTests/NewsTests.cs b/Polygon.Net.Tests/FunctionalTests/NewsTests.cs index a5a0e30..721913a 100644 --- a/Polygon.Net.Tests/FunctionalTests/NewsTests.cs +++ b/Polygon.Net.Tests/FunctionalTests/NewsTests.cs @@ -1,17 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Polygon.Net.Models; using static Polygon.Net.Tests.TestManager; +#nullable enable namespace Polygon.Net.Tests.FunctionalTests { [TestClass] public class NewsApiTests { private const string STATUS_OK = "OK"; + private static DateTime START_TIME = new DateTime(2023, 03, 05); // Start Specific Day + private static DateTime END_TIME = new DateTime(2023, 03, 06); // End Specific Day [TestMethod] public async Task GetNewsSucceedsAsync() @@ -26,13 +25,11 @@ public async Task GetNewsSucceedsAsync() [TestMethod] public async Task GetNewsWithParametersSucceedsAsync() { - DateTime startTime = new DateTime(2023, 03, 05); // Start Specific Day - DateTime endTime = new DateTime(2023, 03, 06); // End Specific Day - int countNews = 5; - String ticker = "GOOGL"; - DateTime dateTime = startTime; + int countNews = 5; + String ticker = "GOOGL"; + DateTime dateTime = START_TIME; - var newsResponse = await PolygonTestClient.GetNewsAsync(startTime, endTime, ticker, "asc", countNews, "published_utc"); + var newsResponse = await PolygonTestClient.GetNewsAsync(START_TIME, END_TIME, ticker, "asc", "published_utc", limit: countNews); Assert.IsInstanceOfType(newsResponse.Results, typeof(List)); Assert.IsNotNull(newsResponse); @@ -43,28 +40,29 @@ public async Task GetNewsWithParametersSucceedsAsync() { DateTime publishedNews = DateTime.Parse(news.PublishedUtc); - Assert.AreEqual(startTime.ToString("yyyy-MM-dd"), publishedNews.ToString("yyyy-MM-dd")); + Assert.AreEqual(START_TIME.ToString("yyyy-MM-dd"), publishedNews.ToString("yyyy-MM-dd")); Assert.IsTrue(news.Tickers.Contains(ticker), "The ticker was not found inside tickers"); Assert.IsTrue(publishedNews > dateTime, "Date current news is not greater than before date news"); dateTime = publishedNews; } + } [TestMethod] public async Task GetNewsEmptyAsync() { - var newsResponse = await PolygonTestClient.GetNewsAsync(null, null, "ABCXYZ"); + var newsResponse = await PolygonTestClient.GetNewsAsync(ticker: "ABCXYZ"); Assert.IsTrue(newsResponse.Count == 0, "the news count should be empty"); - Assert.IsNull(newsResponse.Status); + Assert.AreEqual(STATUS_OK, newsResponse.Status); } [TestMethod] public async Task GetTodayNewsSucceedsAsync() { - string currentDate = DateTime.Now.Date.ToString("yyyy-MM-dd"); - var newsResponse = await PolygonTestClient.GetTodayNewsAsync(); + DateTime currentDate = DateTime.Now.Date; + var newsResponse = await PolygonTestClient.GetNewsAsync(currentDate); Assert.IsInstanceOfType(newsResponse.Results, typeof(List)); Assert.IsNotNull(newsResponse); @@ -72,17 +70,19 @@ public async Task GetTodayNewsSucceedsAsync() foreach (var news in newsResponse.Results) { - Assert.AreEqual(currentDate, DateTime.Parse(news.PublishedUtc).ToString("yyyy-MM-dd")); + Assert.AreEqual(currentDate.ToString("yyyy-MM-dd"), DateTime.Parse(news.PublishedUtc).ToString("yyyy-MM-dd")); } } [TestMethod] public async Task GetNextPageNewsAsync() { - var newsResponse = await PolygonTestClient.GetNextPageNewsAsync("YXA9MjAyMy0wMy0wN1QxNCUzQTAxJTNBMDBaJmFzPUpvSnFQM0s4Zl9NSXVsY01Kb0NiaUNZeXBuQTZDdDd0RldrMmRiLVhYTkEmb3JkZXI9ZGVzY2VuZGluZw"); - Assert.IsInstanceOfType(newsResponse.Results, typeof(List)); - Assert.IsNotNull(newsResponse); - Assert.AreEqual(STATUS_OK, newsResponse.Status); + var newsResponse = await PolygonTestClient.GetNewsAsync(START_TIME, END_TIME); + var nextPage = await PolygonTestClient.GetNewsAsync(nextPage: newsResponse.HashNextUrl); + + Assert.IsInstanceOfType(nextPage.Results, typeof(List)); + Assert.IsNotNull(nextPage); + Assert.AreEqual(STATUS_OK, nextPage.Status); } } } diff --git a/Polygon.Net.Tests/Polygon.Net.Tests.csproj b/Polygon.Net.Tests/Polygon.Net.Tests.csproj index df0a273..51d3a0d 100644 --- a/Polygon.Net.Tests/Polygon.Net.Tests.csproj +++ b/Polygon.Net.Tests/Polygon.Net.Tests.csproj @@ -2,8 +2,10 @@ net7.0 - false + enable + enable + enable diff --git a/Polygon.Net/API/NewsApi.cs b/Polygon.Net/API/NewsApi.cs index 25673ed..7fdd5a0 100644 --- a/Polygon.Net/API/NewsApi.cs +++ b/Polygon.Net/API/NewsApi.cs @@ -1,9 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; +using Microsoft.AspNetCore.Http.Extensions; using Newtonsoft.Json; +using Polygon.Net.Http; using Polygon.Net.Models; -using static Pineapple.Common.Preconditions; namespace Polygon.Net; @@ -20,63 +18,23 @@ public partial class PolygonClient /// Order results based on the sort field /// Limit the number of results returned, default is 10 and max is 1000 /// Sort field used for ordering + /// next page /// NewsResponse - public async Task GetNewsAsync( - DateTime? startTime = default, - DateTime? endTime = default, - string ticker = null, - string order = null, - int? limit = null, - string sort = null, - string nextPage = null - ) + /// + public async Task GetNewsAsync(DateTime? startTime = null, DateTime? endTime = null, string? ticker = null, string? order = null, string? sort = null, int limit = 0, string? nextPage = null) { - var queryParams = new Dictionary(); - - if(nextPage != null) { - queryParams.Add("cursor", nextPage); - }else { - if(ticker != null) queryParams.Add(nameof(ticker), ticker); - if (order != null) queryParams.Add(nameof(order), order); - if (limit != null) queryParams.Add(nameof(limit), limit + ""); - if (sort != null) queryParams.Add(nameof(sort), sort); - - queryParams.Add("published_utc.gte", startTime?.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss")); - queryParams.Add("published_utc.lte", endTime?.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss")); - } - - string queryParamStr = GetQueryParameterString(queryParams); - string requestUrl = $"{_polygonSettings.ApiBaseUrl}{NEWS_ENDPOINT}{queryParamStr}"; + var qb = new QueryBuilder(); + qb.AddIf(nextPage != null, "cursor", nextPage); + qb.AddIf(ticker != null, nameof(ticker), ticker); + qb.AddIf(order != null, nameof(order), order); + qb.AddIf(limit != 0, nameof(limit), limit + ""); + qb.AddIf(sort != null, nameof(sort), sort); + qb.AddIf(startTime != null, "published_utc.gte", startTime?.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss")); + qb.AddIf(endTime != null, "published_utc.lte", endTime?.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss")); + + string requestUrl = $"{_polygonSettings.ApiBaseUrl}{NEWS_ENDPOINT}{qb.ToString()}"; string contentStr = await Get(requestUrl).ConfigureAwait(false); - NewsResponse newsResponse = JsonConvert.DeserializeObject(contentStr); - - return newsResponse.Results.Count == 0 ? new NewsResponse() : newsResponse; - } - /// - /// Get the today's Polygon news. - /// - /// The polygon API ticker by default is desc - /// Order results based on the sort field - /// Limit the number of results returned, default is 10 and max is 1000 - /// Sort field used for ordering - /// NewsResponse - public async Task GetTodayNewsAsync( - string ticker = null, - string order = null, - int? limit = null, - string sort = null, - string nextPage = null - ) - { - return await GetNewsAsync(DateTime.Now.Date, null, ticker, order, limit, sort, nextPage); - } - - /// - /// Get the next page of Polygon news. - /// - /// hash of the next page - public async Task GetNextPageNewsAsync(string nextPage = null){ - return await GetNewsAsync(null, null, null, null, null, null, nextPage); + return String.IsNullOrEmpty(contentStr) ? new NewsResponse() : JsonConvert.DeserializeObject(contentStr); } } diff --git a/Polygon.Net/Client/IPolygonClient.cs b/Polygon.Net/Client/IPolygonClient.cs index 3362995..677afce 100644 --- a/Polygon.Net/Client/IPolygonClient.cs +++ b/Polygon.Net/Client/IPolygonClient.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Polygon.Net.Models; +using Polygon.Net.Models; namespace Polygon.Net { @@ -56,26 +53,16 @@ Task GetAggregatesBarsAsync( Task> GetMarketHolidaysAsync(); - Task> GetTickerTypesAsync(string assetClass = default, string locale = default); + Task> GetTickerTypesAsync(string? assetClass = default, string? locale = default); Task GetNewsAsync( - DateTime? startTime = default, - DateTime? endTime = default, - string ticker = null, - string order = null, - int? limit = null, - string sort = null, - string nextPage = null + DateTime? startTime = null, + DateTime? endTime = null, + string? ticker = null, + string? order = null, + string? sort = null, + int limit = 0, + string? nextPage = null ); - - Task GetTodayNewsAsync( - string ticker = default, - string order = null, - int? limit = null, - string sort = null, - string nextPage = null - ); - - Task GetNextPageNewsAsync(string nextPage = null); } } diff --git a/Polygon.Net/Http/QueryStringExtension.cs b/Polygon.Net/Http/QueryStringExtension.cs new file mode 100644 index 0000000..abab954 --- /dev/null +++ b/Polygon.Net/Http/QueryStringExtension.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Http.Extensions; + +namespace Polygon.Net.Http; +internal static class QueryStringExtension +{ + public static void AddIf(this QueryBuilder query, bool conditional, string? name, string? value) + { + if (conditional) + { + query.Add(name, value); + } + } +} diff --git a/Polygon.Net/Models/NewsResponse.cs b/Polygon.Net/Models/NewsResponse.cs index d4b085b..03cc695 100644 --- a/Polygon.Net/Models/NewsResponse.cs +++ b/Polygon.Net/Models/NewsResponse.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web; +using System.Web; using Newtonsoft.Json; namespace Polygon.Net.Models; @@ -18,15 +13,15 @@ public class NewsResponse [JsonProperty("count")] public int Count { get; set; } = 0; - private string _hashNextUrl; + private string? _hashNextUrl; [JsonProperty("next_url")] - public string HashNextUrl + public string? HashNextUrl { get { return _hashNextUrl; } set { - string hash = null; + string? hash = null; if (value != null) { Uri uri = new Uri(value); @@ -39,5 +34,5 @@ public string HashNextUrl } [JsonProperty("results")] - public List Results { get; set; } = new List() { }; + public List Results { get; set; } = new List() { }; } diff --git a/Polygon.Net/Polygon.Net.csproj b/Polygon.Net/Polygon.Net.csproj index d7aa403..29c862f 100644 --- a/Polygon.Net/Polygon.Net.csproj +++ b/Polygon.Net/Polygon.Net.csproj @@ -20,11 +20,14 @@ MIT polygon.png e0e1f4de-446a-4f5c-879a-30ff77b4e606 + enable + enable + From ee06b527bbebe81b5065fc992750325f5229b6a5 Mon Sep 17 00:00:00 2001 From: David Martinez Date: Wed, 8 Mar 2023 11:36:45 -0500 Subject: [PATCH 8/8] The code has been cleaned --- Polygon.Net.Tests/FunctionalTests/NewsTests.cs | 2 -- Polygon.Net/API/NewsApi.cs | 2 +- Polygon.Net/Client/PolygonClient.cs | 10 +++------- Polygon.Net/Http/QueryStringExtension.cs | 1 + Polygon.Net/Models/NewsInfo.cs | 7 +------ Polygon.Net/Models/NewsResponse.cs | 1 + 6 files changed, 7 insertions(+), 16 deletions(-) diff --git a/Polygon.Net.Tests/FunctionalTests/NewsTests.cs b/Polygon.Net.Tests/FunctionalTests/NewsTests.cs index 721913a..b53df0b 100644 --- a/Polygon.Net.Tests/FunctionalTests/NewsTests.cs +++ b/Polygon.Net.Tests/FunctionalTests/NewsTests.cs @@ -2,7 +2,6 @@ using Polygon.Net.Models; using static Polygon.Net.Tests.TestManager; -#nullable enable namespace Polygon.Net.Tests.FunctionalTests { [TestClass] @@ -46,7 +45,6 @@ public async Task GetNewsWithParametersSucceedsAsync() dateTime = publishedNews; } - } [TestMethod] diff --git a/Polygon.Net/API/NewsApi.cs b/Polygon.Net/API/NewsApi.cs index 7fdd5a0..6415d14 100644 --- a/Polygon.Net/API/NewsApi.cs +++ b/Polygon.Net/API/NewsApi.cs @@ -20,7 +20,7 @@ public partial class PolygonClient /// Sort field used for ordering /// next page /// NewsResponse - /// + /// public async Task GetNewsAsync(DateTime? startTime = null, DateTime? endTime = null, string? ticker = null, string? order = null, string? sort = null, int limit = 0, string? nextPage = null) { var qb = new QueryBuilder(); diff --git a/Polygon.Net/Client/PolygonClient.cs b/Polygon.Net/Client/PolygonClient.cs index 91edde2..2936871 100644 --- a/Polygon.Net/Client/PolygonClient.cs +++ b/Polygon.Net/Client/PolygonClient.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; +using System.Text; using AutoMapper; using static Pineapple.Common.Preconditions; @@ -26,7 +22,7 @@ private async Task Get(string requestUrl) { var client = _dependencies.HttpClientFactory.CreateClient(_polygonSettings.HttpClientName); - requestUrl = $"{ requestUrl }{ (requestUrl.Contains("?") ? "&" : "?") }apikey={ _polygonSettings.ApiKey }"; + requestUrl = $"{requestUrl}{(requestUrl.Contains("?") ? "&" : "?")}apikey={_polygonSettings.ApiKey}"; var request = new HttpRequestMessage(HttpMethod.Get, requestUrl); @@ -48,7 +44,7 @@ private string GetQueryParameterString(Dictionary queryParams) { if (qp.Value != null) { - sb.Append($"&{ qp.Key }={ qp.Value }"); + sb.Append($"&{qp.Key}={qp.Value}"); } } diff --git a/Polygon.Net/Http/QueryStringExtension.cs b/Polygon.Net/Http/QueryStringExtension.cs index abab954..12d4d9d 100644 --- a/Polygon.Net/Http/QueryStringExtension.cs +++ b/Polygon.Net/Http/QueryStringExtension.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Http.Extensions; namespace Polygon.Net.Http; + internal static class QueryStringExtension { public static void AddIf(this QueryBuilder query, bool conditional, string? name, string? value) diff --git a/Polygon.Net/Models/NewsInfo.cs b/Polygon.Net/Models/NewsInfo.cs index fa6d4fc..45dffe2 100644 --- a/Polygon.Net/Models/NewsInfo.cs +++ b/Polygon.Net/Models/NewsInfo.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Newtonsoft.Json; +using Newtonsoft.Json; namespace Polygon.Net.Models; diff --git a/Polygon.Net/Models/NewsResponse.cs b/Polygon.Net/Models/NewsResponse.cs index 03cc695..7943541 100644 --- a/Polygon.Net/Models/NewsResponse.cs +++ b/Polygon.Net/Models/NewsResponse.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; namespace Polygon.Net.Models; + public class NewsResponse { [JsonProperty("status")]