Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions Polygon.Net.Tests/FunctionalTests/NewsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
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";
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()
Comment thread
davidfmb93 marked this conversation as resolved.
{
var newsResponse = await PolygonTestClient.GetNewsAsync();

Assert.IsInstanceOfType(newsResponse.Results, typeof(List<NewsInfo>));
Assert.IsNotNull(newsResponse);
Assert.AreEqual(STATUS_OK, newsResponse.Status);
}

[TestMethod]
public async Task GetNewsWithParametersSucceedsAsync()
{
int countNews = 5;
String ticker = "GOOGL";
DateTime dateTime = START_TIME;

var newsResponse = await PolygonTestClient.GetNewsAsync(START_TIME, END_TIME, ticker, "asc", "published_utc", limit: countNews);

Assert.IsInstanceOfType(newsResponse.Results, typeof(List<NewsInfo>));
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.PublishedUtc);

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(ticker: "ABCXYZ");

Assert.IsTrue(newsResponse.Count == 0, "the news count should be empty");
Assert.AreEqual(STATUS_OK, newsResponse.Status);
}

[TestMethod]
public async Task GetTodayNewsSucceedsAsync()
{
DateTime currentDate = DateTime.Now.Date;
var newsResponse = await PolygonTestClient.GetNewsAsync(currentDate);

Assert.IsInstanceOfType(newsResponse.Results, typeof(List<NewsInfo>));
Assert.IsNotNull(newsResponse);
Assert.AreEqual(STATUS_OK, newsResponse.Status);

foreach (var news in newsResponse.Results)
{
Assert.AreEqual(currentDate.ToString("yyyy-MM-dd"), DateTime.Parse(news.PublishedUtc).ToString("yyyy-MM-dd"));
}
}

[TestMethod]
public async Task GetNextPageNewsAsync()
{
var newsResponse = await PolygonTestClient.GetNewsAsync(START_TIME, END_TIME);
var nextPage = await PolygonTestClient.GetNewsAsync(nextPage: newsResponse.HashNextUrl);

Assert.IsInstanceOfType(nextPage.Results, typeof(List<NewsInfo>));
Assert.IsNotNull(nextPage);
Assert.AreEqual(STATUS_OK, nextPage.Status);
}
}
}
4 changes: 3 additions & 1 deletion Polygon.Net.Tests/Polygon.Net.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>

<IsPackable>false</IsPackable>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NullableContextOptions>enable</NullableContextOptions>
</PropertyGroup>

<ItemGroup>
Expand Down
40 changes: 40 additions & 0 deletions Polygon.Net/API/NewsApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Http.Extensions;
using Newtonsoft.Json;
using Polygon.Net.Http;
using Polygon.Net.Models;

namespace Polygon.Net;

public partial class PolygonClient
{
private const string NEWS_ENDPOINT = "/v2/reference/news";

/// <summary>
/// Get the Polygon news.
/// </summary>
/// <param name="startTime">Return results published after this date</param>
/// <param name="endTime">Return results published before this date</param>
/// <param name="ticker">The polygon API ticker by default is desc</param>
/// <param name="order">Order results based on the sort field</param>
/// <param name="limit">Limit the number of results returned, default is 10 and max is 1000</param>
/// <param name="sort">Sort field used for ordering</param>
/// <param name="nextPage">next page </param>
/// <returns>NewsResponse</returns>
///
public async Task<NewsResponse> 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();
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);

return String.IsNullOrEmpty(contentStr) ? new NewsResponse() : JsonConvert.DeserializeObject<NewsResponse>(contentStr);
}
}
16 changes: 12 additions & 4 deletions Polygon.Net/Client/IPolygonClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Polygon.Net.Models;
using Polygon.Net.Models;

namespace Polygon.Net
{
Expand Down Expand Up @@ -55,6 +53,16 @@ Task<AggregatesBarsResponse> GetAggregatesBarsAsync(

Task<List<MarketHoliday>> GetMarketHolidaysAsync();

Task<PolygonResponse<TickerType>> GetTickerTypesAsync(string assetClass = default, string locale = default);
Task<PolygonResponse<TickerType>> GetTickerTypesAsync(string? assetClass = default, string? locale = default);

Task<NewsResponse> GetNewsAsync(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion is to keep only one and remove the GetTodayNewsAsync and GetNextPageNewsAsync

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function removed
Fixed it

DateTime? startTime = null,
DateTime? endTime = null,
string? ticker = null,
string? order = null,
string? sort = null,
int limit = 0,
string? nextPage = null
);
}
}
10 changes: 3 additions & 7 deletions Polygon.Net/Client/PolygonClient.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -26,7 +22,7 @@ private async Task<string> 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);

Expand All @@ -48,7 +44,7 @@ private string GetQueryParameterString(Dictionary<string, string> queryParams)
{
if (qp.Value != null)
{
sb.Append($"&{ qp.Key }={ qp.Value }");
sb.Append($"&{qp.Key}={qp.Value}");
}
}

Expand Down
14 changes: 14 additions & 0 deletions Polygon.Net/Http/QueryStringExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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);
}
}
}
30 changes: 30 additions & 0 deletions Polygon.Net/Models/NewsInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Newtonsoft.Json;

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 PublishedUtc { get; set; }

[JsonProperty("article_url")]
public string ArticleUrl { get; set; }

[JsonProperty("keywords")]
public List<string> Keywords { get; set; } = new List<string>() { };

[JsonProperty("tickers")]
public List<string> Tickers { get; set; } = new List<string>() { };
}
39 changes: 39 additions & 0 deletions Polygon.Net/Models/NewsResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Web;
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; } = 0;

private string? _hashNextUrl;

[JsonProperty("next_url")]
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<NewsInfo> Results { get; set; } = new List<NewsInfo>() { };
}
11 changes: 8 additions & 3 deletions Polygon.Net/Polygon.Net.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Version>1.2.0</Version>
<AssemblyVersion>1.2.0.0</AssemblyVersion>
<FileVersion>1.2.0.0</FileVersion>
<Version>1.2.2</Version>
<AssemblyVersion>1.2.2.0</AssemblyVersion>
<FileVersion>1.2.2.0</FileVersion>
<Authors>MILL5</Authors>
<Description>A .NET class library for use against the Polygon APIs supporting .NET Standard, .NET Core, .NET 5.0, and the new .NET 6.0</Description>
<Copyright>Copyright © MILL5, LLC 2021</Copyright>
Expand All @@ -19,11 +19,16 @@
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>polygon.png</PackageIcon>
<UserSecretsId>e0e1f4de-446a-4f5c-879a-30ff77b4e606</UserSecretsId>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" />
<PackageReference Include="Cph.FinancialDataSanitizer" Version="1.0.17" />
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="Pineapple" Version="1.0.41" />
Expand Down