Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request data from security profile page #74

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
44 changes: 44 additions & 0 deletions YahooFinanceApi.Tests/ProfileTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace YahooFinanceApi.Tests;

public class ProfileTests
{
[Fact]
public async Task TestProfileAsync()
{
const string AAPL = "AAPL";

var aaplProfile = await Yahoo.QueryProfileAsync(AAPL);

Assert.NotNull(aaplProfile.Address1);
Assert.NotNull(aaplProfile.AuditRisk);
Assert.NotNull(aaplProfile.BoardRisk);
Assert.NotNull(aaplProfile.City);
Assert.NotNull(aaplProfile.CompanyOfficers);
Assert.NotNull(aaplProfile.CompensationAsOfEpochDate);
Assert.NotNull(aaplProfile.CompensationRisk);
Assert.NotNull(aaplProfile.Country);
Assert.NotNull(aaplProfile.FullTimeEmployees);
Assert.NotNull(aaplProfile.GovernanceEpochDate);
Assert.NotNull(aaplProfile.Industry);
Assert.NotNull(aaplProfile.IndustryDisp);
Assert.NotNull(aaplProfile.IndustryKey);
Assert.NotNull(aaplProfile.LongBusinessSummary);
Assert.NotNull(aaplProfile.MaxAge);
Assert.NotNull(aaplProfile.State);
Assert.NotNull(aaplProfile.Zip);
Assert.NotNull(aaplProfile.Phone);
Assert.NotNull(aaplProfile.Website);
Assert.NotNull(aaplProfile.Sector);
Assert.NotNull(aaplProfile.SectorKey);
Assert.NotNull(aaplProfile.SectorDisp);
Assert.NotNull(aaplProfile.ShareHolderRightsRisk);
Assert.NotNull(aaplProfile.OverallRisk);
}
}
29 changes: 29 additions & 0 deletions YahooFinanceApi/ProfileFields.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace YahooFinanceApi;

public enum ProfileFields
{
Address1,
City,
State,
Zip,
Country,
Phone,
Website,
Industry,
IndustryKey,
IndustryDisp,
Sector,
SectorKey,
SectorDisp,
LongBusinessSummary,
FullTimeEmployees,
CompanyOfficers,
AuditRisk,
BoardRisk,
CompensationRisk,
ShareHolderRightsRisk,
OverallRisk,
GovernanceEpochDate,
CompensationAsOfEpochDate,
MaxAge,
}
40 changes: 40 additions & 0 deletions YahooFinanceApi/SecurityProfile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;

namespace YahooFinanceApi;

public class SecurityProfile
{
public IReadOnlyDictionary<string, dynamic> Fields { get; private set; }

// ctor
internal SecurityProfile(IReadOnlyDictionary<string, dynamic> fields) => Fields = fields;

public dynamic this[string fieldName] => Fields[fieldName];
public dynamic this[ProfileFields field] => Fields[field.ToString()];

public string Address1 => this[ProfileFields.Address1];
public string City => this[ProfileFields.City];
public string State => this[ProfileFields.State];
public string Zip => this[ProfileFields.Zip];
public string Country => this[ProfileFields.Country];
public string Phone => this[ProfileFields.Phone];
public string Website => this[ProfileFields.Website];
public string Industry => this[ProfileFields.Industry];
public string IndustryKey => this[ProfileFields.IndustryKey];
public string IndustryDisp => this[ProfileFields.IndustryDisp];
public string Sector => this[ProfileFields.Sector];
public string SectorKey => this[ProfileFields.SectorKey];
public string SectorDisp => this[ProfileFields.SectorDisp];
public string LongBusinessSummary => this[ProfileFields.LongBusinessSummary];
public long FullTimeEmployees => this[ProfileFields.FullTimeEmployees];
public List<dynamic> CompanyOfficers => this[ProfileFields.CompanyOfficers];
public long AuditRisk => this[ProfileFields.AuditRisk];
public long BoardRisk => this[ProfileFields.BoardRisk];
public long CompensationRisk => this[ProfileFields.CompensationRisk];
public long ShareHolderRightsRisk => this[ProfileFields.ShareHolderRightsRisk];
public long OverallRisk => this[ProfileFields.OverallRisk];
public DateTime GovernanceEpochDate => DateTimeOffset.FromUnixTimeSeconds((long)this[ProfileFields.GovernanceEpochDate]).LocalDateTime;
public DateTime CompensationAsOfEpochDate => DateTimeOffset.FromUnixTimeSeconds((long)this[ProfileFields.CompensationAsOfEpochDate]).LocalDateTime;
public long MaxAge => this[ProfileFields.MaxAge];
}
67 changes: 67 additions & 0 deletions YahooFinanceApi/Yahoo - Profile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Flurl;
using Flurl.Http;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace YahooFinanceApi
{
public sealed partial class Yahoo
{

public static async Task<SecurityProfile> QueryProfileAsync(string symbol, CancellationToken token = default)
{
await YahooSession.InitAsync(token);

var url = $"https://query2.finance.yahoo.com/v10/finance/quoteSummary/{symbol}"
.SetQueryParam("modules", "assetProfile,convert_dates")
.SetQueryParam("crumb", YahooSession.Crumb);

// Invalid symbols as part of a request are ignored by Yahoo.
// So the number of symbols returned may be less than requested.
// If there are no valid symbols, an exception is thrown by Flurl.
// This exception is caught (below) and an empty dictionary is returned.
// There seems to be no easy way to reliably identify changed symbols.

dynamic data = null;

try
{
data = await url
.WithCookie(YahooSession.Cookie.Name, YahooSession.Cookie.Value)
.WithHeader(YahooSession.UserAgentKey, YahooSession.UserAgentValue)
.GetAsync(token)
.ReceiveJson()
.ConfigureAwait(false);
}
catch (FlurlHttpException ex)
{
if (ex.Call.Response.StatusCode == (int)System.Net.HttpStatusCode.NotFound)
{
return null;
}
else
{
throw;
}
}

var response = data.quoteSummary;

var error = response.error;
if (error != null)
{
throw new InvalidDataException($"An error was returned by Yahoo: {error}");
}

var result = response.result[0].assetProfile;
var pascalDictionary = ((IDictionary<string, dynamic>) result).ToDictionary(x => x.Key.ToPascal(), x => x.Value);


return new SecurityProfile(pascalDictionary);
}
}
}