public async Task BestPathConversion() { var marketProfile = new Core.Domain.MarketProfile(); var btcUsdBid = 8600; var slrUsdAsk = 0.4508; // USD wights are lower than CHF, so best par from BTC to SLR is through USD. var feedData = new List <FeedData> { new FeedData { Asset = "BTCUSD", Ask = 9000, Bid = btcUsdBid, DateTime = DateTime.UtcNow }, new FeedData { Asset = "SLRUSD", Ask = slrUsdAsk, Bid = 0.0003, DateTime = DateTime.UtcNow }, new FeedData { Asset = "BTCCHF", Ask = 0.98599, Bid = 0.97925, DateTime = DateTime.UtcNow }, new FeedData { Asset = "SLRCHF", Ask = 0.4466, Bid = 0.0005, DateTime = DateTime.UtcNow }, }; marketProfile.Profile = feedData; var amountInBase = await _service.GetAmountInBaseWithProfile("BTC", 1, "SLR", marketProfile); Assert.Equal((btcUsdBid / slrUsdAsk).TruncateDecimalPlaces(TestsUtils.CryptoAccuracy), amountInBase); }
public async Task <double> GetAmountInBaseWithProfile( string assetFrom, double amount, string assetTo, Core.Domain.MarketProfile marketProfile) { if (assetFrom == assetTo) { return(amount); } if (Math.Abs(amount) < double.Epsilon) { return(0); } var marketProfileData = marketProfile ?? await GetMarketProfileRemoteAsync(); var pricesData = _crossPairsCalculator.PrepareForConversion(marketProfileData); return(GetCrossPairsAmountInBase( assetFrom, amount, assetTo, pricesData)); }
internal Dictionary <string, NodeInfo> PrepareForConversion(Core.Domain.MarketProfile marketProfile) { var result = new Dictionary <string, NodeInfo>(); var assetIdsToBuild = new HashSet <string>(); foreach (var feedData in marketProfile.Profile) { var pair = _assetPairsReadModelRepository.TryGet(feedData.Asset); if (pair == null) { continue; } result[$"{pair.BaseAssetId}_{pair.QuotingAssetId}"] = new NodeInfo { Straight = true, Bid = feedData.Bid, Ask = feedData.Ask, }; result[$"{pair.QuotingAssetId}_{pair.BaseAssetId}"] = new NodeInfo { Straight = false, Bid = feedData.Bid, Ask = feedData.Ask, }; if (!_links.ContainsKey(pair.BaseAssetId)) { _links.TryAdd(pair.BaseAssetId, new ConcurrentDictionary <string, byte>()); } if (!_links.ContainsKey(pair.QuotingAssetId)) { _links.TryAdd(pair.QuotingAssetId, new ConcurrentDictionary <string, byte>()); } bool added = _links[pair.BaseAssetId].TryAdd(pair.QuotingAssetId, 0); if (added) { assetIdsToBuild.Add(pair.BaseAssetId); } added = _links[pair.QuotingAssetId].TryAdd(pair.BaseAssetId, 0); if (added) { assetIdsToBuild.Add(pair.QuotingAssetId); } } foreach (var assetId in assetIdsToBuild) { BuildBestAssetIdLinks(assetId); } return(result); }
public async Task Is_GetAmountInBase_Inverted_Correct() { var marketProfile = new Core.Domain.MarketProfile(); var feedData = new List <FeedData> { new FeedData { Asset = "USDCHF", Ask = 0.98599, Bid = 0.97925, DateTime = DateTime.UtcNow } }; marketProfile.Profile = feedData; var amountInBase = await _service.GetAmountInBaseWithProfile("CHF", 1, "USD", marketProfile); Assert.Equal(1.01, amountInBase); }
public async Task Is_GetAmountInBase_Correct() { var marketProfile = new Core.Domain.MarketProfile(); var feedData = new List <FeedData> { new FeedData { Asset = "BTCUSD", Ask = 2656.381, Bid = 2652, DateTime = DateTime.UtcNow } }; marketProfile.Profile = feedData; var amountInBase = await _service.GetAmountInBaseWithProfile("BTC", 1, "USD", marketProfile); Assert.Equal(2652, amountInBase); }
public async Task StraightFalseFalseConversion() { var marketProfile = new Core.Domain.MarketProfile(); var btcAsk = 9000; var chfAsk = 0.98599; var feedData = new List <FeedData> { new FeedData { Asset = "BTCUSD", Ask = btcAsk, Bid = 8600, DateTime = DateTime.UtcNow }, new FeedData { Asset = "USDCHF", Ask = chfAsk, Bid = 0.97925, DateTime = DateTime.UtcNow }, }; marketProfile.Profile = feedData; var amountInBase = await _service.GetAmountInBaseWithProfile("CHF", 1, "BTC", marketProfile); Assert.Equal((1 / chfAsk / btcAsk).TruncateDecimalPlaces(TestsUtils.CryptoAccuracy), amountInBase); }
public async Task StraightTrueFalseConversion() { var marketProfile = new Core.Domain.MarketProfile(); var btcBid = 8600; var slrAsk = 0.4508; var feedData = new List <FeedData> { new FeedData { Asset = "BTCUSD", Ask = 9000, Bid = btcBid, DateTime = DateTime.UtcNow }, new FeedData { Asset = "SLRUSD", Ask = slrAsk, Bid = 0.0003, DateTime = DateTime.UtcNow }, }; marketProfile.Profile = feedData; var amountInBase = await _service.GetAmountInBaseWithProfile("BTC", 1, "SLR", marketProfile); Assert.Equal((btcBid / slrAsk).TruncateDecimalPlaces(TestsUtils.CryptoAccuracy), amountInBase); }
public async Task StraightTrueTrueConversion() { var marketProfile = new Core.Domain.MarketProfile(); var btcBid = 8600; var chfBid = 0.97925; var feedData = new List <FeedData> { new FeedData { Asset = "BTCUSD", Ask = 9000, Bid = btcBid, DateTime = DateTime.UtcNow }, new FeedData { Asset = "USDCHF", Ask = 0.98599, Bid = chfBid, DateTime = DateTime.UtcNow }, }; marketProfile.Profile = feedData; var amountInBase = await _service.GetAmountInBaseWithProfile("BTC", 1, "CHF", marketProfile); Assert.Equal((btcBid * chfBid).TruncateDecimalPlaces(TestsUtils.FiatAccuracy), amountInBase); }
private ConversionResult GetMarketAmountInBase( OrderAction orderAction, IEnumerable <IOrderBook> orderBooks, AssetWithAmount from, string assetTo, IReadOnlyDictionary <string, Assets.Client.Models.v3.Asset> assetsDict, IEnumerable <Assets.Client.Models.v3.AssetPair> assetPairs, Core.Domain.MarketProfile marketProfile) { var result = new ConversionResult(); var assetPair = GetPairWithAssets(assetPairs, from.AssetId, assetTo); if (!IsInputValid(from, assetTo, assetsDict) || assetPair == null) { result.Result = OperationResult.InvalidInputParameters; return(result); } if (from.AssetId == assetTo) { result.From = result.To = from; result.Price = result.VolumePrice = 1; result.Result = OperationResult.Ok; return(result); } orderAction = IsInverted(assetPair, assetTo) ? orderAction.ViceVersa() : orderAction; var isBuy = orderAction == OrderAction.Buy; var orderBook = orderBooks.FirstOrDefault(x => x.AssetPair == assetPair.Id && x.IsBuy == isBuy); if (orderBook == null) { result.Result = OperationResult.NoLiquidity; return(result); } if (IsInverted(assetPair, assetTo)) { orderBook.Invert(); } orderBook.Order(); double sum = 0; double priceSum = 0; int n = 0; var neededSum = double.MaxValue; foreach (var line in orderBook.Prices) { if (n != 0 && sum >= neededSum) { break; } sum += Math.Abs(line.Volume); priceSum += line.Price; n++; neededSum = from.Amount * GetRate(assetTo, assetPair, priceSum / n); } if (n == 0) { result.Result = OperationResult.NoLiquidity; return(result); } var price = priceSum / n; result.From = from; var rate = GetRate(assetTo, assetPair, price); var displayRate = GetRate(from.AssetId, assetPair, price); result.To = new AssetWithAmount { AssetId = assetTo, Amount = (rate * from.Amount).TruncateDecimalPlaces(assetsDict[assetTo].Accuracy, orderAction == OrderAction.Buy) }; result.Result = sum < neededSum ? OperationResult.NoLiquidity : OperationResult.Ok; result.Price = GetRate( from.AssetId, assetPair, marketProfile.GetPrice(assetPair.Id, orderAction).GetValueOrDefault()); result.VolumePrice = displayRate; return(result); }
public async Task <double> GetAmountInBaseWithProfile(string assetFrom, string assetTo, double amount, [FromBody] Core.Domain.MarketProfile marketProfile) { return(await _rateCalculatorService.GetAmountInBaseWithProfile(assetFrom, amount, assetTo, marketProfile)); }