-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add finance plugin with stock price support
- Loading branch information
Showing
11 changed files
with
247 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Pinpoint.Core; | ||
|
||
namespace Pinpoint.Plugin.Finance | ||
{ | ||
public class FinancePlugin : IPlugin | ||
{ | ||
private readonly YahooFinanceApi _yahooFinanceApi = new YahooFinanceApi(TimeSpan.FromMinutes(2)); | ||
|
||
public PluginMeta Meta { get; set; } = new PluginMeta("Finance Plugin", PluginPriority.Highest); | ||
|
||
public void Load() | ||
{ | ||
} | ||
|
||
public void Unload() | ||
{ | ||
} | ||
|
||
public async Task<bool> Activate(Query query) | ||
{ | ||
return query.Parts.Length == 1 | ||
&& query.Prefix().Equals("$") | ||
&& query.RawQuery.Length >= 3 | ||
&& query.Parts[0].Substring(1).All(ch => !char.IsDigit(ch) && (char.IsUpper(ch) || !char.IsLetter(ch))) | ||
&& query.Parts[0].Substring(1).Length < 5; | ||
} | ||
|
||
public async IAsyncEnumerable<AbstractQueryResult> Process(Query query) | ||
{ | ||
// $GME => GME | ||
var ticker = query.Parts[0].Substring(1); | ||
var response = await _yahooFinanceApi.Lookup(ticker); | ||
if (response != null) | ||
{ | ||
yield return new FinanceSearchResult(response); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
using System; | ||
using FontAwesome5; | ||
using Pinpoint.Core; | ||
|
||
namespace Pinpoint.Plugin.Finance | ||
{ | ||
public class FinanceSearchResult : UrlQueryResult | ||
{ | ||
public FinanceSearchResult(YahooFinanceResponse response) : base(response.Url) | ||
{ | ||
var pricePrefix = response.RegularMarketPrice > response.PreviousClose | ||
? "+" | ||
: "-"; | ||
var priceChange = Math.Abs(PriceChange(response)); | ||
var percentChange = Math.Abs(PercentChange(response)); | ||
|
||
Title = response.Symbol + " @ " + response.RegularMarketPrice + " " + pricePrefix + priceChange + " (" + pricePrefix + percentChange + "%)"; | ||
} | ||
|
||
private double PriceChange(YahooFinanceResponse response) | ||
{ | ||
return Math.Round(response.RegularMarketPrice - response.PreviousClose, 2); | ||
} | ||
|
||
private double PercentChange(YahooFinanceResponse response) | ||
{ | ||
return Math.Round(Math.Abs(response.PreviousClose - response.RegularMarketPrice) / | ||
((response.PreviousClose + response.RegularMarketPrice) / 2) * 100f, 2); | ||
} | ||
|
||
public override EFontAwesomeIcon FontAwesomeIcon => EFontAwesomeIcon.Solid_ChartLine; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp3.1</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Pinpoint.Core\Pinpoint.Core.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using Newtonsoft.Json; | ||
using Newtonsoft.Json.Linq; | ||
|
||
namespace Pinpoint.Plugin.Finance | ||
{ | ||
public class YahooFinanceApi | ||
{ | ||
private const string Url = "https://query1.finance.yahoo.com/v8/finance/chart/{0}?interval=2m&useYfid=true&range=1d"; | ||
|
||
public YahooFinanceApi(TimeSpan cacheTimeout) | ||
{ | ||
Repository = new YahooFinanceRepository(cacheTimeout); | ||
} | ||
|
||
public YahooFinanceRepository Repository { get; } | ||
|
||
public async Task<YahooFinanceResponse> Lookup(string ticker) | ||
{ | ||
// Check if ticker is cached | ||
if (Repository.TryGet(ticker, out var response)) | ||
{ | ||
return response; | ||
} | ||
|
||
var httpResponse = string.Empty; | ||
try | ||
{ | ||
using var httpClient = new HttpClient(); | ||
httpResponse = await httpClient.GetStringAsync(string.Format(Url, ticker)); | ||
} | ||
catch (HttpRequestException) | ||
{ | ||
return null; | ||
} | ||
|
||
var result = JObject.Parse(httpResponse)["chart"]["result"].ToArray(); | ||
if (result.Length == 0) | ||
{ | ||
return null; | ||
} | ||
|
||
var meta = result[0]["meta"].ToString(); | ||
response = JsonConvert.DeserializeObject<YahooFinanceResponse>(meta); | ||
|
||
// Don't consider non-equity instrument types | ||
if (!response.InstrumentType.Equals("EQUITY")) | ||
{ | ||
return null; | ||
} | ||
|
||
// Cache ticker response | ||
Repository.Put(ticker, response); | ||
return response; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Pinpoint.Plugin.Finance | ||
{ | ||
public class YahooFinanceRepository | ||
{ | ||
public YahooFinanceRepository(TimeSpan cacheTimeout) | ||
{ | ||
CacheTimeout = cacheTimeout; | ||
} | ||
|
||
public TimeSpan CacheTimeout { get; set; } | ||
|
||
public Dictionary<string, YahooFinanceResponse> ResponseCache = new Dictionary<string, YahooFinanceResponse>(); | ||
|
||
public Dictionary<string, long> UpdateTimeCache = new Dictionary<string, long>(); | ||
|
||
public bool TryGet(string ticker, out YahooFinanceResponse response) | ||
{ | ||
if (!ResponseCache.ContainsKey(ticker)) | ||
{ | ||
response = null; | ||
return false; | ||
} | ||
|
||
var now = DateTimeOffset.Now.ToUnixTimeMilliseconds(); | ||
if (now - UpdateTimeCache[ticker] >= CacheTimeout.Milliseconds) | ||
{ | ||
response = null; | ||
return false; | ||
} | ||
|
||
response = ResponseCache[ticker]; | ||
return true; | ||
} | ||
|
||
public void Put(string ticker, YahooFinanceResponse response) | ||
{ | ||
ResponseCache[ticker] = response; | ||
UpdateTimeCache[ticker] = DateTimeOffset.Now.ToUnixTimeMilliseconds(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using Newtonsoft.Json; | ||
|
||
namespace Pinpoint.Plugin.Finance | ||
{ | ||
public class YahooFinanceResponse | ||
{ | ||
[JsonProperty("currency")] | ||
public string Currency { get; set; } | ||
|
||
[JsonProperty("symbol")] | ||
public string Symbol { get; set; } | ||
|
||
[JsonProperty("exchangeName")] | ||
public string ExchangeName { get; set; } | ||
|
||
[JsonProperty("instrumentType")] | ||
public string InstrumentType { get; set; } | ||
|
||
[JsonProperty("regularMarketPrice")] | ||
public float RegularMarketPrice { get; set; } | ||
|
||
[JsonProperty("previousClose")] | ||
public float PreviousClose { get; set; } | ||
|
||
public string Url => "https://finance.yahoo.com/quote/" + Symbol; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters