Skip to content

Commit

Permalink
Refactored standard client, added new endpoints, and created new WebS…
Browse files Browse the repository at this point in the history
…ocket streaming client
  • Loading branch information
ShravanJ committed Sep 26, 2021
1 parent dacf1ca commit f64abf7
Show file tree
Hide file tree
Showing 14 changed files with 359 additions and 53 deletions.
13 changes: 13 additions & 0 deletions FinnHubSharp/DataModels/Request/StreamingSubscription.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Newtonsoft.Json;

namespace FinnHubSharp.DataModels.Request
{
public class StreamingSubscription
{
[JsonProperty("type")]
public string Type { get; set; }

[JsonProperty("symbol")]
public string Symbol { get; set; }
}
}
28 changes: 28 additions & 0 deletions FinnHubSharp/DataModels/Response/ListedSymbol.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Newtonsoft.Json;

namespace FinnHubSharp.DataModels.Response
{
public class ListedSymbol
{
[JsonProperty("currency")]
public string Currency { get; set; }

[JsonProperty("description")]
public string Description { get; set; }

[JsonProperty("displaySymbol")]
public string DisplaySymbol { get; set; }

[JsonProperty("figi")]
public string Figi { get; set; }

[JsonProperty("mic")]
public string Mic { get; set; }

[JsonProperty("symbol")]
public string Symbol { get; set; }

[JsonProperty("type")]
public string Type { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;

namespace FinnHubSharp.DataModels
namespace FinnHubSharp.DataModels.Response
{
public class Quote
{
Expand Down
33 changes: 33 additions & 0 deletions FinnHubSharp/DataModels/Response/StreamingQuote.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace FinnHubSharp.DataModels.Response
{
public class StreamingQuote
{
[JsonProperty("data")]
public List<Data> Data { get; set; }

[JsonProperty("type")]
public string Type { get; set; }
}

public class Data
{
[JsonProperty("p")]
public double Price { get; set; }

[JsonProperty("s")]
public string Symbol { get; set; }

[JsonProperty("t")]
public long Timestamp { get; set; }

[JsonProperty("v")]
public double Volume { get; set; }

[JsonProperty("c")]
public string TradeCondition { get; set; }
}
}
29 changes: 29 additions & 0 deletions FinnHubSharp/DataModels/Response/SymbolInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Collections.Generic;
using Newtonsoft.Json;

namespace FinnHubSharp.DataModels.Response
{
public class SymbolInfo
{
[JsonProperty("count")]
public long Count { get; set; }

[JsonProperty("result")]
public List<Result> Result { get; set; }
}

public partial class Result
{
[JsonProperty("description")]
public string Description { get; set; }

[JsonProperty("displaySymbol")]
public string DisplaySymbol { get; set; }

[JsonProperty("symbol")]
public string Symbol { get; set; }

[JsonProperty("type")]
public string Type { get; set; }
}
}
37 changes: 0 additions & 37 deletions FinnHubSharp/FinnHubClient.cs

This file was deleted.

4 changes: 3 additions & 1 deletion FinnHubSharp/FinnHubSharp.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
<PackageId>FinnHubSharp</PackageId>
<Version>1.0.0</Version>
<Authors>Shravan Jambukesan</Authors>
Expand All @@ -13,9 +13,11 @@
<PackageLicenseExpression></PackageLicenseExpression>
<PackageReleaseNotes>* Initial release</PackageReleaseNotes>
<PackageTags>FinnHub</PackageTags>
<LangVersion>8</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>

Expand Down
86 changes: 86 additions & 0 deletions FinnHubSharp/Implementations/FinnHubClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using FinnHubSharp.DataModels.Response;
using FinnHubSharp.Interfaces;
using FinnHubSharp.Logging;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace FinnHubSharp.Implementations
{
public class FinnHubClient : IFinnHubClient
{
private readonly string _apiKey;
private readonly HttpClient _client;
private readonly ILogger _logger;

public FinnHubClient(HttpClient client, string apiKey, ILogger logger)
{
_client = client ?? new HttpClient();
_apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
_logger = logger ?? new Logger<FinnHubSharpLogger>(new LoggerFactory());
}

public async Task<Quote> GetQuoteAsync(string symbol)
{
try
{
var endpoint = $"https://finnhub.io/api/v1/quote?symbol={symbol}&token={_apiKey}";
var response = await _client.GetAsync(endpoint);
string responseString = await response.Content.ReadAsStringAsync();

_logger.LogInformation(FinnHubSharpLogger.BuildHttpLogUsing(response));

var jsonResponse = JsonConvert.DeserializeObject<Quote>(responseString);
return jsonResponse;
}
catch (Exception e)
{
_logger.LogCritical(e.ToString());
throw;
}
}

public async Task<SymbolInfo> GetSymbolInfoAsync(string symbolOrSecurityName)
{
try
{
var endpoint = $"https://finnhub.io/api/v1/search?q={symbolOrSecurityName}&token={_apiKey}";
var response = await _client.GetAsync(endpoint);

_logger.LogInformation(FinnHubSharpLogger.BuildHttpLogUsing(response));

string responseString = await response.Content.ReadAsStringAsync();
var jsonResponse = JsonConvert.DeserializeObject<SymbolInfo>(responseString);
return jsonResponse;
}
catch (Exception e)
{
_logger.LogCritical(e.ToString());
throw;
}
}

public async Task<IEnumerable<ListedSymbol>> GetAllSymbols(string exchange)
{
try
{
var endpoint = $"https://finnhub.io/api/v1/stock/symbol?exchange={exchange}&token={_apiKey}";
var response = await _client.GetAsync(endpoint);

_logger.LogInformation(FinnHubSharpLogger.BuildHttpLogUsing(response));

string responseString = await response.Content.ReadAsStringAsync();
var jsonResponse = JsonConvert.DeserializeObject<List<ListedSymbol>>(responseString);
return jsonResponse;
}
catch (Exception e)
{
_logger.LogCritical(e.ToString());
throw;
}
}
}
}
87 changes: 87 additions & 0 deletions FinnHubSharp/Implementations/FinnHubStreamerClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using FinnHubSharp.DataModels.Request;
using FinnHubSharp.DataModels.Response;
using FinnHubSharp.Interfaces;
using Newtonsoft.Json;

namespace FinnHubSharp.Implementations
{
public class FinnHubStreamerClient : IFinnHubStreamerClient, IDisposable
{
private readonly string _apiKey;
private readonly ClientWebSocket _webSocketClient;

public FinnHubStreamerClient(ClientWebSocket webSocketClient, string apiKey)
{
_webSocketClient = webSocketClient ?? new ClientWebSocket();
_apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
}

public async IAsyncEnumerable<IAsyncEnumerable<StreamingQuote>> GetStreamingQuotes(
params StreamingSubscription[] subscriptions)
{
while(true)
{
using (var socket = _webSocketClient)
{
await socket.ConnectAsync(new Uri($"wss://ws.finnhub.io?token={_apiKey}"), CancellationToken.None);
foreach (var sub in subscriptions.ToList())
{
await SendSubscription(socket, JsonConvert.SerializeObject(sub));
}
yield return ReceiveStreamingData(socket);
}
}
}

public async Task Disconnect()
{
if (_webSocketClient is {State: WebSocketState.Open})
{
await _webSocketClient.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
}
}

private async Task SendSubscription(ClientWebSocket socket, string data) =>
await socket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(data)), WebSocketMessageType.Text, true, CancellationToken.None);

private async IAsyncEnumerable<StreamingQuote> ReceiveStreamingData(ClientWebSocket socket)
{
var buffer = new ArraySegment<byte>(new byte[2048]);
while (true)
{
using (var memoryStream = new MemoryStream())
{
WebSocketReceiveResult result;
do
{
result = await socket.ReceiveAsync(buffer, CancellationToken.None);
memoryStream.Write(buffer.Array ?? Array.Empty<byte>(), buffer.Offset, result.Count);
} while (!result.EndOfMessage);

if (result.MessageType == WebSocketMessageType.Close)
await Disconnect();

memoryStream.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(memoryStream, Encoding.UTF8))
{
yield return JsonConvert.DeserializeObject<StreamingQuote>(await reader.ReadToEndAsync());
}
}
}
}

public async void Dispose()
{
await Disconnect();
_webSocketClient?.Dispose();
}
}
}
15 changes: 15 additions & 0 deletions FinnHubSharp/Interfaces/IFinnHubClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using FinnHubSharp.DataModels.Response;

namespace FinnHubSharp.Interfaces
{
public interface IFinnHubClient
{
Task<Quote> GetQuoteAsync(string symbol);

Task<SymbolInfo> GetSymbolInfoAsync(string symbolOrSecurityName);

Task<IEnumerable<ListedSymbol>> GetAllSymbols(string exchange);
}
}
15 changes: 15 additions & 0 deletions FinnHubSharp/Interfaces/IFinnHubStreamerClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using FinnHubSharp.DataModels.Request;
using FinnHubSharp.DataModels.Response;

namespace FinnHubSharp.Interfaces
{
public interface IFinnHubStreamerClient
{
IAsyncEnumerable<IAsyncEnumerable<StreamingQuote>> GetStreamingQuotes(params StreamingSubscription[] subscriptions);

Task Disconnect();
}
}
Loading

0 comments on commit f64abf7

Please sign in to comment.