Skip to content

Commit

Permalink
BREAKING: Abstract Symbol/Asset Cache and redirect static definitions
Browse files Browse the repository at this point in the history
For issue #89. Change Asset.Cache and Symbol.Cache from IDictionary to IAssetCache and ISymbolCache respectively. Redirect static asset and symbol definitions to the cache to utilize run-time updates. Remove string-to-Asset implicit conversion. Update static assets and symbols.
  • Loading branch information
sonvister committed May 2, 2018
1 parent 423e93b commit 7b936e2
Show file tree
Hide file tree
Showing 16 changed files with 1,154 additions and 1,174 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ webSocketCache.Error += (s, e) => { Console.WriteLine(e.Exception.Message); };
webSocketCache.Subscribe(Symbol.BTC_USDT, evt =>
{
// Get symbol from cache (update cache if a symbol is missing).
var symbol = Symbol.Get(evt.OrderBook.Symbol);
var symbol = Symbol.Cache.Get(evt.OrderBook.Symbol);

var minBidPrice = evt.OrderBook.Bids.Last().Price;
var maxAskPrice = evt.OrderBook.Asks.Last().Price;
Expand Down
87 changes: 11 additions & 76 deletions samples/BinanceCodeGenerator/Asset.template.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// ReSharper disable InconsistentNaming
using System;
using System.Collections.Generic;
using System.Linq;

namespace Binance
{
Expand All @@ -21,7 +19,7 @@ public sealed class Asset : IComparable<Asset>, IEquatable<Asset>
// <<insert assets>>

// Redirect (BCH) Bitcoin Cash (BCC = BitConnect)
public static readonly Asset BCH;
public static Asset BCH => BCC;

#endregion Public Constants

Expand All @@ -33,24 +31,14 @@ public sealed class Asset : IComparable<Asset>, IEquatable<Asset>

public static implicit operator string(Asset asset) => asset?.ToString();

public static implicit operator Asset(string s)
{
if (s == null) return null;
var _s = s.FormatSymbol();
lock (_sync)
{
return Cache.ContainsKey(_s) ? Cache[_s] : null;
}
}

#endregion Implicit Operators

#region Public Properties

/// <summary>
/// Asset cache.
/// </summary>
public static IDictionary<string, Asset> Cache { get; }
public static IAssetCache Cache { get; set; }

/// <summary>
/// Get the asset symbol.
Expand All @@ -76,16 +64,15 @@ static Asset()
{
try
{
Cache = new InMemoryAssetCache();

Cache.Load(
new[] {
// <<insert asset definitions>>
});

// Redirect (BCH) Bitcoin Cash (BCC = BitConnect)
BCH = BCC;

Cache = new Dictionary<string, Asset>
{
// <<insert asset definitions>>

// Redirect (BCH) Bitcoin Cash (BCC = BitConnect)
{ "BCH", BCC }
};
Cache.Set("BCH", Cache.Get("BCC"));
}
catch (Exception e)
{
Expand Down Expand Up @@ -124,11 +111,7 @@ public static bool IsValid(string asset)

asset = asset.FormatSymbol();

lock (_sync)
{
return Cache.ContainsKey(asset)
&& Cache[asset].ToString() == asset;
}
return Cache.Get(asset) == asset;
}

public override bool Equals(object obj)
Expand All @@ -155,54 +138,6 @@ public override string ToString()

#endregion Public Methods

#region Internal Methods

/// <summary>
/// Update the asset cache.
/// </summary>
/// <param name="symbols">The symbols.</param>
/// <returns></returns>
internal static void UpdateCache(IEnumerable<Symbol> symbols)
{
Throw.IfNull(symbols, nameof(symbols));

// ReSharper disable once PossibleMultipleEnumeration
if (!symbols.Any())
throw new ArgumentException("Enumerable must not be empty.", nameof(symbols));

var assets = new List<Asset>();

// ReSharper disable once PossibleMultipleEnumeration
foreach (var symbol in symbols)
{
if (!assets.Contains(symbol.BaseAsset))
assets.Add(symbol.BaseAsset);

if (!assets.Contains(symbol.QuoteAsset))
assets.Add(symbol.QuoteAsset);
}

lock (_sync)
{
// Remove any old assets (preserves redirections).
foreach (var asset in Cache.Values.ToArray())
{
if (!assets.Contains(asset))
{
Cache.Remove(asset);
}
}

// Update existing and add any new assets.
foreach (var asset in assets)
{
Cache[string.Intern(asset)] = asset;
}
}
}

#endregion Internal Methods

#region IComparable<Asset>

public int CompareTo(Asset other)
Expand Down
10 changes: 5 additions & 5 deletions samples/BinanceCodeGenerator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ private static async Task Main()

foreach (var symbol in group)
{
var orderTypes = string.Join(",", symbol.OrderTypes.Select(_ => "OrderType." + _));
lines.Insert(index++, $" public static readonly Symbol {symbol.BaseAsset}_{symbol.QuoteAsset} = new Symbol(SymbolStatus.{symbol.Status}, Asset.{symbol.BaseAsset}, Asset.{symbol.QuoteAsset}, ({symbol.Quantity.Minimum}m, {symbol.Quantity.Maximum}m, {symbol.Quantity.Increment}m), ({symbol.Price.Minimum}m, {symbol.Price.Maximum}m, {symbol.Price.Increment}m), {symbol.NotionalMinimumValue}m, {symbol.IsIcebergAllowed.ToString().ToLowerInvariant()}, new List<OrderType> {{{orderTypes}}});");
lines.Insert(index++, $" public static Symbol {symbol.BaseAsset}_{symbol.QuoteAsset} => Cache.Get(\"{symbol.BaseAsset}_{symbol.QuoteAsset}\");");
}

lines.Insert(index++, string.Empty);
Expand All @@ -70,7 +69,8 @@ private static async Task Main()

foreach(var symbol in symbols)
{
lines.Insert(index++, $" {{ \"{symbol}\", {symbol.BaseAsset}_{symbol.QuoteAsset} }},");
var orderTypes = string.Join(",", symbol.OrderTypes.Select(_ => "OrderType." + _));
lines.Insert(index++, $" new Symbol(SymbolStatus.{symbol.Status}, Asset.{symbol.BaseAsset}, Asset.{symbol.QuoteAsset}, ({symbol.Quantity.Minimum}m, {symbol.Quantity.Maximum}m, {symbol.Quantity.Increment}m), ({symbol.Price.Minimum}m, {symbol.Price.Maximum}m, {symbol.Price.Increment}m), {symbol.NotionalMinimumValue}m, {symbol.IsIcebergAllowed.ToString().ToLowerInvariant()}, new List<OrderType> {{{orderTypes}}}),");
}

// Save the generated source code (replacing original).
Expand All @@ -96,15 +96,15 @@ private static async Task Main()
// Insert definition for each asset.
foreach (var asset in assets)
{
lines.Insert(index++, $" public static readonly Asset {asset} = new Asset(\"{asset}\", {asset.Precision});");
lines.Insert(index++, $" public static Asset {asset} => Cache.Get(\"{asset}\");");
}

index = lines.FindIndex(l => l.Contains("<<insert asset definitions>>"));
lines.RemoveAt(index);

foreach (var asset in assets)
{
lines.Insert(index++, $" {{ \"{asset}\", {asset} }},");
lines.Insert(index++, $" new Asset(\"{asset}\", {asset.Precision}),");
}

// Save the generated source code (replacing original).
Expand Down
101 changes: 26 additions & 75 deletions samples/BinanceCodeGenerator/Symbol.template.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// ReSharper disable InconsistentNaming
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -23,10 +22,10 @@ public sealed class Symbol : IComparable<Symbol>, IEquatable<Symbol>
// <<insert symbols>>

// Redirect (BCH) Bitcoin Cash (BCC = BitConnect)
public static readonly Symbol BCH_USDT;
public static readonly Symbol BCH_BNB;
public static readonly Symbol BCH_BTC;
public static readonly Symbol BCH_ETH;
public static Symbol BCH_USDT => BCC_USDT;
public static Symbol BCH_BNB => BCC_BNB;
public static Symbol BCH_BTC => BCC_BTC;
public static Symbol BCH_ETH => BCC_ETH;

#endregion Public Constants

Expand All @@ -45,7 +44,7 @@ public sealed class Symbol : IComparable<Symbol>, IEquatable<Symbol>
/// <summary>
/// Symbol cache.
/// </summary>
public static IDictionary<string, Symbol> Cache { get; }
public static ISymbolCache Cache { get; set; }

/// <summary>
/// Get the symbol status.
Expand Down Expand Up @@ -103,22 +102,18 @@ static Symbol()
{
try
{
Cache = new InMemorySymbolCache();

Cache.Load(
new[] {
// <<insert symbol definitions>>
});

// Redirect (BCH) Bitcoin Cash (BCC = BitConnect)
BCH_USDT = BCC_USDT;
BCH_BNB = BCC_BNB;
BCH_BTC = BCC_BTC;
BCH_ETH = BCC_ETH;

Cache = new Dictionary<string, Symbol>
{
// <<insert symbol definitions>>

// Redirect (BCH) Bitcoin Cash (BCC = BitConnect)
{ "BCHUSDT", BCC_USDT },
{ "BCHBNB", BCC_BNB },
{ "BCHBTC", BCC_BTC },
{ "BCHETH", BCC_ETH }
};
Cache.Set("BCH_USDT", Cache.Get("BCC_USDT"));
Cache.Set("BCH_BNB", Cache.Get("BCC_BNB"));
Cache.Set("BCH_BTC", Cache.Get("BCC_BTC"));
Cache.Set("BCH_ETH", Cache.Get("BCC_ETH"));
}
catch (Exception e)
{
Expand Down Expand Up @@ -164,22 +159,6 @@ public Symbol(SymbolStatus status, Asset baseAsset, Asset quoteAsset, InclusiveR

#region Public Methods

/// <summary>
/// Get a symbol from the cache using a string.
/// Update the cache with UpdateCacheAsync if new symbols are missing.
/// </summary>
/// <param name="s">The string to match.</param>
/// <returns>A <see cref="Symbol"/> or null.</returns>
public static Symbol Get(string s)
{
if (s == null) return null;
var _s = s.FormatSymbol();
lock (_sync)
{
return Cache.ContainsKey(_s) ? Cache[_s] : null;
}
}

/// <summary>
/// Verify that symbol is valid. If fails, but known to be valid,
/// call UpdateCacheAsync() to get the latest symbols.
Expand All @@ -193,11 +172,7 @@ public static bool IsValid(string symbol)

symbol = symbol.FormatSymbol();

lock (_sync)
{
return Cache.ContainsKey(symbol)
&& Cache[symbol].ToString() == symbol;
}
return Cache.Get(symbol) == symbol;
}

/// <summary>
Expand All @@ -211,44 +186,20 @@ public static async Task UpdateCacheAsync(IBinanceApi api, CancellationToken tok
var symbols = await api.GetSymbolsAsync(token)
.ConfigureAwait(false);

UpdateCache(symbols);
}
Cache.Load(symbols);

/// <summary>
/// Update the symbol cache and asset cache.
/// </summary>
/// <param name="symbols">The symbols.</param>
/// <returns></returns>
public static void UpdateCache(IEnumerable<Symbol> symbols)
{
Throw.IfNull(symbols, nameof(symbols));
var assets = new List<Asset>();

// ReSharper disable once PossibleMultipleEnumeration
if (!symbols.Any())
throw new ArgumentException("Enumerable must not be empty.", nameof(symbols));

lock (_sync)
foreach (var symbol in symbols)
{
// Remove any old symbols (preserves redirections).
// ReSharper disable once PossibleMultipleEnumeration
foreach (var symbol in Cache.Values.ToArray())
{
if (!symbols.Contains(symbol))
{
Cache.Remove(symbol);
}
}

// Update existing and add any new symbols.
// ReSharper disable once PossibleMultipleEnumeration
foreach (var symbol in symbols)
{
Cache[string.Intern(symbol)] = symbol;
}
if (!assets.Contains(symbol.BaseAsset))
assets.Add(symbol.BaseAsset);

if (!assets.Contains(symbol.QuoteAsset))
assets.Add(symbol.QuoteAsset);
}

// ReSharper disable once PossibleMultipleEnumeration
Asset.UpdateCache(symbols);
Asset.Cache.Load(assets);
}

public override string ToString()
Expand Down
2 changes: 1 addition & 1 deletion samples/BinanceConsoleApp/Controllers/GetAssets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public async Task<bool> HandleAsync(string command, CancellationToken token = de
await Symbol.UpdateCacheAsync(Program.Api, token);
}

var assets = Asset.Cache.Values.OrderBy(a => a.Symbol);
var assets = Asset.Cache.GetAll().OrderBy(a => a.Symbol);

lock (Program.ConsoleSync)
{
Expand Down
2 changes: 1 addition & 1 deletion samples/BinanceConsoleApp/Controllers/GetSymbols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public async Task<bool> HandleAsync(string command, CancellationToken token = de
await Symbol.UpdateCacheAsync(Program.Api, token);
}

var symbols = Symbol.Cache.Values.OrderBy(s => s.ToString());
var symbols = Symbol.Cache.GetAll().OrderBy(s => s.ToString());
//var symbols = await Program.Api.SymbolsAsync(token); // as string.

lock (Program.ConsoleSync)
Expand Down
2 changes: 1 addition & 1 deletion samples/BinanceConsoleApp/Examples/ReadMeExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public static async Task ExampleMain(string[] args)
webSocketCache.Subscribe(Symbol.BTC_USDT, evt =>
{
// Get symbol from cache (update cache if a symbol is missing).
var symbol = Symbol.Get(evt.OrderBook.Symbol);
var symbol = Symbol.Cache.Get(evt.OrderBook.Symbol);

var minBidPrice = evt.OrderBook.Bids.Last().Price;
var maxAskPrice = evt.OrderBook.Asks.Last().Price;
Expand Down
Loading

0 comments on commit 7b936e2

Please sign in to comment.