Skip to content

Commit

Permalink
Security Profile
Browse files Browse the repository at this point in the history
  • Loading branch information
jabacrack authored and vlad-rob committed Sep 11, 2024
1 parent cb999f4 commit 483b3b8
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 0 deletions.
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);
}
}
}

0 comments on commit 483b3b8

Please sign in to comment.