Skip to content

Commit 4b2f203

Browse files
Fix USDC binance crypto future and stable pairs (#9246)
- Minor fix for stable pairs handling without a pair, adding unit tests - Fix for USDC it's a USD-M crypto future not coin
1 parent 7eefdeb commit 4b2f203

4 files changed

Lines changed: 46 additions & 14 deletions

File tree

Common/Currencies.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,38 @@ public static class Currencies
270270
{ Market.DYDX , _stableCoinsWithoutPairsdYdX}
271271
};
272272

273+
private static readonly HashSet<string> _dollarStablePairs = ["USDT", "USDC", USD];
274+
275+
/// <summary>
276+
/// Checks whether or not certain symbol is a StableCoin without pair in a given market
277+
/// </summary>
278+
/// <param name="accountCurrency">The account currency</param>
279+
/// <param name="cashSymbol">The target cash symbol</param>
280+
/// <param name="market">The market in which we want to search for that StableCoin</param>
281+
/// <returns>True if the given symbol is a StableCoin without pair in the given market</returns>
282+
public static bool IsStableCoinWithoutPair(string accountCurrency, string cashSymbol, string market)
283+
{
284+
IEnumerable<string> _targets;
285+
if (_dollarStablePairs.Contains(accountCurrency))
286+
{
287+
// let's be polite and handle USDT/USDC/USD, this is internal
288+
_targets = _dollarStablePairs.Where(x => x != cashSymbol).SelectMany(x => new[] { x + cashSymbol, cashSymbol + x }).ToArray();
289+
}
290+
else
291+
{
292+
_targets = [accountCurrency + cashSymbol, cashSymbol + accountCurrency];
293+
}
294+
295+
foreach (var target in _targets)
296+
{
297+
if (IsStableCoinWithoutPair(target, market))
298+
{
299+
return true;
300+
}
301+
}
302+
return false;
303+
}
304+
273305
/// <summary>
274306
/// Checks whether or not certain symbol is a StableCoin without pair in a given market
275307
/// </summary>

Common/Securities/Cash.cs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,15 @@ public List<SubscriptionDataConfig> EnsureCurrencyDataFeed(SecurityManager secur
275275
.Concat(cryptoEntries)
276276
.ToList();
277277

278+
// Special case for crypto markets without direct pairs (They wont be found by the above)
279+
// This allows us to add cash for "StableCoins" that are 1-1 with our account currency without needing a conversion security.
280+
// Check out the StableCoinsWithoutPairs static var for those that are missing their 1-1 conversion pairs
281+
if (marketMap.TryGetValue(SecurityType.Crypto, out var market) && Currencies.IsStableCoinWithoutPair(accountCurrency, Symbol, market))
282+
{
283+
CurrencyConversion = ConstantCurrencyConversion.Identity(accountCurrency, Symbol);
284+
return null;
285+
}
286+
278287
if (!potentialEntries.Any(x =>
279288
Symbol == x.Key.Symbol.Substring(0, x.Key.Symbol.Length - x.Value.QuoteCurrency.Length) ||
280289
Symbol == x.Value.QuoteCurrency))
@@ -285,18 +294,6 @@ public List<SubscriptionDataConfig> EnsureCurrencyDataFeed(SecurityManager secur
285294
return null;
286295
}
287296

288-
// Special case for crypto markets without direct pairs (They wont be found by the above)
289-
// This allows us to add cash for "StableCoins" that are 1-1 with our account currency without needing a conversion security.
290-
// Check out the StableCoinsWithoutPairs static var for those that are missing their 1-1 conversion pairs
291-
if (marketMap.TryGetValue(SecurityType.Crypto, out var market)
292-
&&
293-
(Currencies.IsStableCoinWithoutPair(Symbol + accountCurrency, market)
294-
|| Currencies.IsStableCoinWithoutPair(accountCurrency + Symbol, market)))
295-
{
296-
CurrencyConversion = ConstantCurrencyConversion.Identity(accountCurrency, Symbol);
297-
return null;
298-
}
299-
300297
var requiredSecurities = new List<SubscriptionDataConfig>();
301298

302299
var potentials = potentialEntries

Common/Securities/CryptoFuture/CryptoFuture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public bool IsCryptoCoinFuture()
9393
/// <returns>True if the security is a crypto coin future</returns>
9494
private static bool IsCryptoCoinFuture(string quoteCurrency)
9595
{
96-
return quoteCurrency != "USDT" && quoteCurrency != "BUSD";
96+
return quoteCurrency != "USDT" && quoteCurrency != "BUSD" && quoteCurrency != "USDC";
9797
}
9898

9999
/// <summary>

Tests/Common/Securities/CashTests.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ public void EnsureCurrencyDataFeedDoesNothingWithUnsupportedCurrency()
629629
public void CryptoStableCoinMappingIsCorrect(IBrokerageModel brokerageModel, string accountCurrency, string stableCoin, bool shouldThrow, Symbol[] expectedConversionSymbols)
630630
{
631631
var cashBook = new CashBook() {AccountCurrency = accountCurrency};
632-
var cash = new Cash(stableCoin, 10m, 1m);
632+
var cash = new Cash(stableCoin, 10m, 0);
633633
cashBook.Add(cash.Symbol, cash);
634634

635635
var subscriptions = new SubscriptionManager(NullTimeKeeper.Instance);
@@ -716,6 +716,9 @@ private static TimeKeeper TimeKeeper
716716

717717
// *** Binance ***
718718
// USDC Cases
719+
new object[] { new BinanceBrokerageModel(), "USDT", "BNFCR", false, null },
720+
new object[] { new BinanceBrokerageModel(), "USDC", "BNFCR", false, null },
721+
new object[] { new BinanceBrokerageModel(), Currencies.USD, "BNFCR", false, null },
719722
new object[] { new BinanceBrokerageModel(), Currencies.USD, "USDC", false, null }, // No USDCUSD, but does not throw! Conversion 1-1
720723
new object[] { new BinanceBrokerageModel(), Currencies.EUR, "USDC", false, new[] { Symbol.Create("EURUSDC", SecurityType.Crypto, Market.Binance) } },
721724
new object[] { new BinanceBrokerageModel(), Currencies.GBP, "USDC", false, new[] { Symbol.Create("ADAUSDC", SecurityType.Crypto, Market.Binance), Symbol.Create("ADAGBP", SecurityType.Crypto, Market.Binance) } }, // No USDCGBP, but indirect conversion exists

0 commit comments

Comments
 (0)