diff --git a/Examples/Examples.sln b/Examples/Examples.sln
index 5f1a7a7..904ae53 100644
--- a/Examples/Examples.sln
+++ b/Examples/Examples.sln
@@ -7,6 +7,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kraken.Examples.Api", "Krak
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kraken.Examples.Console", "Kraken.Examples.Console\Kraken.Examples.Console.csproj", "{FD4F95C8-D9B7-4F81-9245-4CE667DFD421}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kraken.Net", "..\Kraken.Net\Kraken.Net.csproj", "{00A92FF0-9CA4-4EBE-8642-18AC90974F3A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kraken.Examples.OrderBook", "Kraken.Examples.OrderBook\Kraken.Examples.OrderBook.csproj", "{9838F221-C2AC-4DF8-A1E7-58DD78EF027C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kraken.Examples.Tracker", "Kraken.Examples.Tracker\Kraken.Examples.Tracker.csproj", "{494D13E5-964C-4C0B-A44F-9B22584E78E6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +27,18 @@ Global
{FD4F95C8-D9B7-4F81-9245-4CE667DFD421}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD4F95C8-D9B7-4F81-9245-4CE667DFD421}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD4F95C8-D9B7-4F81-9245-4CE667DFD421}.Release|Any CPU.Build.0 = Release|Any CPU
+ {00A92FF0-9CA4-4EBE-8642-18AC90974F3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {00A92FF0-9CA4-4EBE-8642-18AC90974F3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {00A92FF0-9CA4-4EBE-8642-18AC90974F3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {00A92FF0-9CA4-4EBE-8642-18AC90974F3A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9838F221-C2AC-4DF8-A1E7-58DD78EF027C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9838F221-C2AC-4DF8-A1E7-58DD78EF027C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9838F221-C2AC-4DF8-A1E7-58DD78EF027C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9838F221-C2AC-4DF8-A1E7-58DD78EF027C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {494D13E5-964C-4C0B-A44F-9B22584E78E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {494D13E5-964C-4C0B-A44F-9B22584E78E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {494D13E5-964C-4C0B-A44F-9B22584E78E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {494D13E5-964C-4C0B-A44F-9B22584E78E6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Examples/Kraken.Examples.Api/Kraken.Examples.Api.csproj b/Examples/Kraken.Examples.Api/Kraken.Examples.Api.csproj
index 0e836b5..f3b04b5 100644
--- a/Examples/Kraken.Examples.Api/Kraken.Examples.Api.csproj
+++ b/Examples/Kraken.Examples.Api/Kraken.Examples.Api.csproj
@@ -1,16 +1,19 @@
- net7.0
+ net8.0
enable
enable
true
-
+
+
+
+
diff --git a/Examples/Kraken.Examples.Console/Kraken.Examples.Console.csproj b/Examples/Kraken.Examples.Console/Kraken.Examples.Console.csproj
index da25bfc..8852d8e 100644
--- a/Examples/Kraken.Examples.Console/Kraken.Examples.Console.csproj
+++ b/Examples/Kraken.Examples.Console/Kraken.Examples.Console.csproj
@@ -2,13 +2,13 @@
Exe
- net7.0
+ net8.0
enable
enable
-
+
diff --git a/Examples/Kraken.Examples.Console/Program.cs b/Examples/Kraken.Examples.Console/Program.cs
index cd2b83c..419fcd2 100644
--- a/Examples/Kraken.Examples.Console/Program.cs
+++ b/Examples/Kraken.Examples.Console/Program.cs
@@ -19,7 +19,7 @@
var socketClient = new KrakenSocketClient(logFactory);
var subscription = await socketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH/USDT", update =>
{
- Console.WriteLine($"Websocket client ticker price for ETHUSDT: {update.Data.LastTrade.Price}");
+ Console.WriteLine($"Websocket client ticker price for ETHUSDT: {update.Data.LastPrice}");
});
Console.ReadLine();
diff --git a/Examples/Kraken.Examples.OrderBook/Kraken.Examples.OrderBook.csproj b/Examples/Kraken.Examples.OrderBook/Kraken.Examples.OrderBook.csproj
new file mode 100644
index 0000000..978d3ef
--- /dev/null
+++ b/Examples/Kraken.Examples.OrderBook/Kraken.Examples.OrderBook.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/Kraken.Examples.OrderBook/Program.cs b/Examples/Kraken.Examples.OrderBook/Program.cs
new file mode 100644
index 0000000..10f0dc5
--- /dev/null
+++ b/Examples/Kraken.Examples.OrderBook/Program.cs
@@ -0,0 +1,52 @@
+using Kraken.Net.Interfaces;
+using CryptoExchange.Net;
+using CryptoExchange.Net.SharedApis;
+using Microsoft.Extensions.DependencyInjection;
+using Spectre.Console;
+
+var collection = new ServiceCollection();
+collection.AddKraken();
+var provider = collection.BuildServiceProvider();
+
+var bookFactory = provider.GetRequiredService();
+
+// Creat and start the order book
+var book = bookFactory.Create(new SharedSymbol(TradingMode.Spot, "ETH", "USD"));
+var result = await book.StartAsync();
+if (!result.Success)
+{
+ Console.WriteLine(result.Error);
+ return;
+}
+
+// Create Spectre table
+var table = new Table();
+table.ShowRowSeparators = true;
+table.AddColumn("Bid Quantity", x => { x.RightAligned(); })
+ .AddColumn("Bid Price", x => { x.RightAligned(); })
+ .AddColumn("Ask Price", x => { x.LeftAligned(); })
+ .AddColumn("Ask Quantity", x => { x.LeftAligned(); });
+
+for(var i = 0; i < 10; i++)
+ table.AddEmptyRow();
+
+await AnsiConsole.Live(table)
+ .StartAsync(async ctx =>
+ {
+ while (true)
+ {
+ var snapshot = book.Book;
+ for (var i = 0; i < 10; i++)
+ {
+ var bid = snapshot.bids.ElementAt(i);
+ var ask = snapshot.asks.ElementAt(i);
+ table.UpdateCell(i, 0, ExchangeHelpers.Normalize(bid.Quantity).ToString());
+ table.UpdateCell(i, 1, ExchangeHelpers.Normalize(bid.Price).ToString());
+ table.UpdateCell(i, 2, ExchangeHelpers.Normalize(ask.Price).ToString());
+ table.UpdateCell(i, 3, ExchangeHelpers.Normalize(ask.Quantity).ToString());
+ }
+
+ ctx.Refresh();
+ await Task.Delay(500);
+ }
+ });
diff --git a/Examples/Kraken.Examples.Tracker/Kraken.Examples.Tracker.csproj b/Examples/Kraken.Examples.Tracker/Kraken.Examples.Tracker.csproj
new file mode 100644
index 0000000..978d3ef
--- /dev/null
+++ b/Examples/Kraken.Examples.Tracker/Kraken.Examples.Tracker.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/Kraken.Examples.Tracker/Program.cs b/Examples/Kraken.Examples.Tracker/Program.cs
new file mode 100644
index 0000000..09707ab
--- /dev/null
+++ b/Examples/Kraken.Examples.Tracker/Program.cs
@@ -0,0 +1,104 @@
+using Kraken.Net.Interfaces;
+using CryptoExchange.Net.SharedApis;
+using Microsoft.Extensions.DependencyInjection;
+using Spectre.Console;
+using System.Globalization;
+
+var collection = new ServiceCollection();
+collection.AddKraken();
+var provider = collection.BuildServiceProvider();
+
+var trackerFactory = provider.GetRequiredService();
+
+// Creat and start the tracker, keep track of the last 10 minutes
+var tracker = trackerFactory.CreateTradeTracker(new SharedSymbol(TradingMode.Spot, "ETH", "USD"), period: TimeSpan.FromMinutes(10));
+var result = await tracker.StartAsync();
+if (!result.Success)
+{
+ Console.WriteLine(result.Error);
+ return;
+}
+
+// Create Spectre table
+var table = new Table();
+table.ShowRowSeparators = true;
+table.AddColumn("5 Min Data").AddColumn("-5 Min", x => { x.RightAligned(); })
+ .AddColumn("Now", x => { x.RightAligned(); })
+ .AddColumn("Dif", x => { x.RightAligned(); });
+
+table.AddRow("Count", "", "", "");
+table.AddRow("Average price", "", "", "");
+table.AddRow("Average weighted price", "", "", "");
+table.AddRow("Buy/Sell Ratio", "", "", "");
+table.AddRow("Volume", "", "", "");
+table.AddRow("Value", "", "", "");
+table.AddRow("Complete", "", "", "");
+table.AddRow("", "", "", "");
+table.AddRow("Status", "", "", "");
+table.AddRow("Synced From", "", "", "");
+
+// Set default culture for currency display
+CultureInfo ci = new CultureInfo("en-US");
+Thread.CurrentThread.CurrentCulture = ci;
+Thread.CurrentThread.CurrentUICulture = ci;
+
+await AnsiConsole.Live(table)
+ .StartAsync(async ctx =>
+ {
+ while (true)
+ {
+ // Get the stats from 10 minutes until 5 minutes ago
+ var secondLastMinute = tracker.GetStats(DateTime.UtcNow.AddMinutes(-10), DateTime.UtcNow.AddMinutes(-5));
+
+ // Get the stats from 5 minutes ago until now
+ var lastMinute = tracker.GetStats(DateTime.UtcNow.AddMinutes(-5));
+
+ // Get the differences between them
+ var compare = secondLastMinute.CompareTo(lastMinute);
+
+ // Update the columns
+ UpdateDec(0, 1, secondLastMinute.TradeCount);
+ UpdateDec(0, 2, lastMinute.TradeCount);
+ UpdateStr(0, 3, $"[{(compare.TradeCountDif.Difference < 0 ? "red" : "green")}]{compare.TradeCountDif.Difference} / {compare.TradeCountDif.PercentageDifference}%[/]");
+
+ UpdateStr(1, 1, secondLastMinute.AveragePrice?.ToString("C"));
+ UpdateStr(1, 2, lastMinute.AveragePrice?.ToString("C"));
+ UpdateStr(1, 3, $"[{(compare.AveragePriceDif?.Difference < 0 ? "red" : "green")}]{compare.AveragePriceDif?.Difference?.ToString("C")} / {compare.AveragePriceDif?.PercentageDifference}%[/]");
+
+ UpdateStr(2, 1, secondLastMinute.VolumeWeightedAveragePrice?.ToString("C"));
+ UpdateStr(2, 2, lastMinute.VolumeWeightedAveragePrice?.ToString("C"));
+ UpdateStr(2, 3, $"[{(compare.VolumeWeightedAveragePriceDif?.Difference < 0 ? "red" : "green")}]{compare.VolumeWeightedAveragePriceDif?.Difference?.ToString("C")} / {compare.VolumeWeightedAveragePriceDif?.PercentageDifference}%[/]");
+
+ UpdateDec(3, 1, secondLastMinute.BuySellRatio);
+ UpdateDec(3, 2, lastMinute.BuySellRatio);
+ UpdateStr(3, 3, $"[{(compare.BuySellRatioDif?.Difference < 0 ? "red" : "green")}]{compare.BuySellRatioDif?.Difference} / {compare.BuySellRatioDif?.PercentageDifference}%[/]");
+
+ UpdateDec(4, 1, secondLastMinute.Volume);
+ UpdateDec(4, 2, lastMinute.Volume);
+ UpdateStr(4, 3, $"[{(compare.VolumeDif.Difference < 0 ? "red" : "green")}]{compare.VolumeDif.Difference} / {compare.VolumeDif.PercentageDifference}%[/]");
+
+ UpdateStr(5, 1, secondLastMinute.QuoteVolume.ToString("C"));
+ UpdateStr(5, 2, lastMinute.QuoteVolume.ToString("C"));
+ UpdateStr(5, 3, $"[{(compare.QuoteVolumeDif.Difference < 0 ? "red" : "green")}]{compare.QuoteVolumeDif.Difference?.ToString("C")} / {compare.QuoteVolumeDif.PercentageDifference}%[/]");
+
+ UpdateStr(6, 1, secondLastMinute.Complete.ToString());
+ UpdateStr(6, 2, lastMinute.Complete.ToString());
+
+ UpdateStr(8, 1, tracker.Status.ToString());
+ UpdateStr(9, 1, tracker.SyncedFrom?.ToString());
+
+ ctx.Refresh();
+ await Task.Delay(500);
+ }
+ });
+
+
+void UpdateDec(int row, int col, decimal? val)
+{
+ table.UpdateCell(row, col, val?.ToString() ?? string.Empty);
+}
+
+void UpdateStr(int row, int col, string? val)
+{
+ table.UpdateCell(row, col, val ?? string.Empty);
+}
diff --git a/Examples/README.md b/Examples/README.md
index be0558c..e32e768 100644
--- a/Examples/README.md
+++ b/Examples/README.md
@@ -4,4 +4,10 @@
A minimal API showing how to integrate Kraken.Net in a web API project
### Kraken.Examples.Console
-A simple console client demonstrating basic usage
\ No newline at end of file
+A simple console client demonstrating basic usage
+
+### Kraken.Examples.OrderBook
+Example of using the client side order book implementation
+
+### Kraken.Examples.Tracker
+Example of using the trade tracker
\ No newline at end of file
diff --git a/Kraken.Net/Kraken.Net.csproj b/Kraken.Net/Kraken.Net.csproj
index 20a8072..0f7f803 100644
--- a/Kraken.Net/Kraken.Net.csproj
+++ b/Kraken.Net/Kraken.Net.csproj
@@ -51,12 +51,10 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
\ No newline at end of file
diff --git a/Kraken.Net/Kraken.Net.xml b/Kraken.Net/Kraken.Net.xml
index a319336..f536d90 100644
--- a/Kraken.Net/Kraken.Net.xml
+++ b/Kraken.Net/Kraken.Net.xml
@@ -3772,6 +3772,11 @@
+
+
+ ctor
+
+
ctor
diff --git a/Kraken.Net/SymbolOrderBooks/KrakenOrderBookFactory.cs b/Kraken.Net/SymbolOrderBooks/KrakenOrderBookFactory.cs
index 699219f..14f6874 100644
--- a/Kraken.Net/SymbolOrderBooks/KrakenOrderBookFactory.cs
+++ b/Kraken.Net/SymbolOrderBooks/KrakenOrderBookFactory.cs
@@ -37,10 +37,10 @@ public KrakenOrderBookFactory(IServiceProvider serviceProvider)
///
public ISymbolOrderBook Create(SharedSymbol symbol, Action? options = null)
{
- var symbolName = KrakenExchange.FormatSymbol(symbol.BaseAsset, symbol.QuoteAsset, symbol.TradingMode, symbol.DeliverTime);
if (symbol.TradingMode == TradingMode.Spot)
- return CreateSpot(symbolName, options);
+ return CreateSpot(symbol.BaseAsset + "/" + symbol.QuoteAsset, options);
+ var symbolName = KrakenExchange.FormatSymbol(symbol.BaseAsset, symbol.QuoteAsset, symbol.TradingMode, symbol.DeliverTime);
return CreateFutures(symbolName, options);
}