示例#1
0
 public LatestPriceResultMessage GetResult(AssetPair pair)
 {
     return(Results().FirstOrDefault(x => x.Pair.Equals(pair)));
 }
        public async Task <IActionResult> Update([FromBody] AssetPair assetPair)
        {
            await _assetPairService.UpdateAsync(assetPair);

            return(NoContent());
        }
 public IReadOnlyList <ICandle> FillGapUpTo(AssetPair assetPair, IFeedHistory feedHistory)
 {
     return(feedHistory.Candles
            .Select(item => item.ToCandle(feedHistory.AssetPair, feedHistory.PriceType, feedHistory.DateTime))
            .ToList());
 }
示例#4
0
        public object Parse(TypeNode typeNode, ArchiveBinaryReader reader)
        {
            object result = null;

            switch (typeNode.TypeFlag)
            {
            case "bool":
                result = reader.ReadBoolean();
                break;

            case "SInt8":
                result = reader.ReadSByte();
                break;

            case "char":
            case "UInt8":
                result = reader.ReadByte();
                break;

            case "short":
            case "SInt16":
                result = reader.ReadInt16();
                break;

            case "unsigned short":
            case "UInt16":
                result = reader.ReadUInt16();
                break;

            case "int":
            case "SInt32":
                result = reader.ReadInt32();
                break;

            case "unsigned int":
            case "UInt32":
            case "Type*":
                result = reader.ReadUInt32();
                break;

            case "long long":
            case "SInt64":
                result = reader.ReadInt64();
                break;

            case "unsigned long long":
            case "UInt64":
                result = reader.ReadUInt64();
                break;

            case "float":
                result = reader.ReadSingle();
                break;

            case "double":
                result = reader.ReadDouble();
                break;

            case "Quaternionf":
            {
                result = reader.ReadQuaternion();
                break;
            }

            case "float4":
            case "Vector4f":
            {
                result = reader.ReadVector4();
                break;
            }

            case "float3":
            case "Vector3f":
            {
                result = reader.ReadVector3();
                break;
            }

            case "float2":
            case "Vector2f":
            {
                result = reader.ReadVector2();
                break;
            }

            case "ColorRGBA":
            {
                if (typeNode.Version == 2)
                {
                    result = reader.ReadColor32();
                }
                else
                {
                    result = reader.ReadColor();
                }
                break;
            }

            case "Matrix4x4f":
            {
                result = reader.ReadMatrix4x4();
                break;
            }

            case "Hash128":
            {
                result = reader.ReadHash128();
                break;
            }

            case "string":
            {
                result = reader.ReadString();
                break;
            }

            case "vector":
            case "staticvector":
            case "set":
            {
                var valueTypeNode = typeNode.Children[0];
                result = this.Parse(valueTypeNode, reader);
                break;
            }

            case "map":
            {
                var pairTypeNode  = typeNode.Children[0].Children[1];
                var keyTypeNode   = pairTypeNode.Children[0];
                var valueTypeNode = pairTypeNode.Children[1];

                var size = reader.ReadInt32();
                Map map  = new Map(typeNode);
                for (int i = 0; i < size; i++)
                {
                    var key   = this.Parse(keyTypeNode, reader);
                    var value = this.Parse(valueTypeNode, reader);
                    map.Add(key, value);
                }
                result = map;
                break;
            }

            case "Array":
            {
                var valueTypeNode = typeNode.Children[1];
                var size          = reader.ReadInt32();
                result = this.ParseArray(valueTypeNode, size, reader);
                break;
            }

            case "PPtr":
            {
                var fileID = reader.ReadInt32();
                var pathID = reader.ReadInt64();
                result = new PPtr(fileID, pathID, typeNode.TypeName);
                break;
            }

            case "TypelessData":
            {
                var size = reader.ReadInt32();
                result = new TypelessData(reader.ReadBytes(size));
                break;
            }

            case "StreamedResource":
            {
                var source           = reader.ReadString();
                var offset           = reader.ReadUInt64();
                var size             = reader.ReadUInt64();
                var streamedResource = new StreamedResource(source, offset, size);
                result = streamedResource;
                break;
            }

            case "AssetBundle":
            {
                AssetBundle bundle = new AssetBundle(((TypeTree)typeNode).Archive);
                bundle.FullName = reader.ReadString();

                var         size         = reader.ReadInt32();
                List <PPtr> preloadTable = new List <PPtr>(size);
                for (int i = 0; i < size; i++)
                {
                    PPtr pptr = new PPtr(reader.ReadInt32(), reader.ReadInt64(), "PPtr<Object>");
                    preloadTable.Add(pptr);
                }

                bundle.Preloads.AddRange(preloadTable);

                size = reader.ReadInt32();
                List <AssetPair> container = new List <AssetPair>(size);
                for (int i = 0; i < size; i++)
                {
                    var first        = reader.ReadString();
                    var preloadIndex = reader.ReadInt32();
                    var preloadSize  = reader.ReadInt32();
                    var pptr         = new PPtr(reader.ReadInt32(), reader.ReadInt64(), "PPtr<Object>");
                    var pair         = new AssetPair(first, new Objects.AssetInfo(preloadIndex, preloadSize, pptr));
                    container.Add(pair);
                }
                bundle.Container.AddRange(container);

                bundle.MainAsset = new Objects.AssetInfo(reader.ReadInt32(), reader.ReadInt32(), new PPtr(reader.ReadInt32(), reader.ReadInt64(), "PPtr<Object>"));

                bundle.RuntimeCompatibility = reader.ReadUInt32();
                bundle.Name = reader.ReadString();

                size = reader.ReadInt32();
                List <string> dependencies = new List <string>(size);
                for (int i = 0; i < size; i++)
                {
                    dependencies.Add(reader.ReadString());
                }
                bundle.Dependencies.AddRange(dependencies);

                bundle.IsStreamed = reader.ReadBoolean();
                result            = bundle;
                break;
            }

            case "PreloadData":
            {
                PreloadData preloadData = new PreloadData(((TypeTree)typeNode).Archive);

                preloadData.Name = reader.ReadString();

                var         size         = reader.ReadInt32();
                List <PPtr> preloadTable = new List <PPtr>(size);
                for (int i = 0; i < size; i++)
                {
                    PPtr pptr = new PPtr(reader.ReadInt32(), reader.ReadInt64(), "PPtr<Object>");
                    preloadTable.Add(pptr);
                }

                preloadData.Preloads.AddRange(preloadTable);

                size = reader.ReadInt32();
                List <string> dependencies = new List <string>(size);
                for (int i = 0; i < size; i++)
                {
                    dependencies.Add(reader.ReadString());
                }
                preloadData.Dependencies.AddRange(dependencies);
                result = preloadData;
                break;
            }

            case "AssetBundleManifest":
            {
                Objects.AssetBundleManifest obj = new Objects.AssetBundleManifest((TypeTree)typeNode);
                foreach (TypeNode childNode in typeNode.Children)
                {
                    var key        = childNode.FieldName;
                    var childValue = this.Parse(childNode, reader);
                    obj[key] = childValue;
                }
                result = obj;
                break;
            }

            default:
            {
                DynamicObject obj = typeNode is TypeTree ? new UnityDynamicObject((TypeTree)typeNode) : new DynamicObject(typeNode);
                foreach (TypeNode childNode in typeNode.Children)
                {
                    var key        = childNode.FieldName;
                    var childValue = this.Parse(childNode, reader);
                    obj[key] = childValue;
                }
                result = obj;
                break;
            }
            }

            if (typeNode.IsAlign)
            {
                reader.Align(4);
            }

            return(result);
        }
示例#5
0
        private async Task <OrderBook> CalculateDirectOrderBookAsync(Instrument instrument, DateTime iterationDateTime)
        {
            Quote[] quotes = _b2C2OrderBookService.GetQuotes(instrument.AssetPairId);

            if (quotes == null || quotes.Length != 2)
            {
                _log.WarningWithDetails("No quotes for instrument", instrument.AssetPairId);
                return(null);
            }

            Balance  baseAssetBalance   = null;
            Balance  quoteAssetBalance  = null;
            TimeSpan timeSinceLastTrade = TimeSpan.Zero;

            if (instrument.AllowSmartMarkup)
            {
                AssetPairLink assetPairLink =
                    await _assetPairLinkService.GetByInternalAssetPairIdAsync(instrument.AssetPairId);

                if (assetPairLink != null && !assetPairLink.IsEmpty())
                {
                    baseAssetBalance =
                        await _balanceService.GetByAssetIdAsync(ExchangeNames.B2C2, assetPairLink.ExternalBaseAssetId);

                    quoteAssetBalance =
                        await _balanceService.GetByAssetIdAsync(ExchangeNames.B2C2, assetPairLink.ExternalQuoteAssetId);

                    timeSinceLastTrade =
                        DateTime.UtcNow - _tradeService.GetLastInternalTradeTime(instrument.AssetPairId);
                }
                else
                {
                    _log.WarningWithDetails("The asset pair link does not configured", new { instrument.AssetPairId });
                }
            }

            AssetPair assetPair = await _assetsServiceWithCache.TryGetAssetPairAsync(instrument.AssetPairId);

            Asset baseAsset = await _assetsServiceWithCache.TryGetAssetAsync(assetPair.BaseAssetId);

            MarketMakerSettings marketMakerSettings = await _marketMakerSettingsService.GetAsync();

            decimal globalMarkup = marketMakerSettings.LimitOrderPriceMarkup;

            decimal noQuotesMarkup = await _noFreshQuotesStopLossService.GetNoFreshQuotesMarkup(assetPair.Id);

            decimal pnLStopLossMarkup = await _pnLStopLossEngineService.GetTotalMarkupByAssetPairIdAsync(assetPair.Id);

            decimal fiatEquityStopLossMarkup = await _fiatEquityStopLossService.GetFiatEquityMarkup(assetPair.Id);

            _log.InfoWithDetails("Arguments for Calculator.CalculateLimitOrders(...).", new
            {
                instrument.AssetPairId,
                quotes,
                levels                                   = instrument.Levels.ToArray(),
                baseAmountBalance                        = baseAssetBalance?.Amount ?? 0,
                quoteAmountBalance                       = quoteAssetBalance?.Amount ?? 0,
                timeSinceLastTradeTotalSeconds           = (int)timeSinceLastTrade.TotalSeconds,
                instrumentHalfLifePeriod                 = instrument.HalfLifePeriod,
                instrumentAllowSmartMarkup               = instrument.AllowSmartMarkup,
                marketMakerSettingsLimitOrderPriceMarkup = globalMarkup,
                pnLStopLossMarkup,
                fiatEquityStopLossMarkup,
                noQuotesMarkup,
                assetPairAccuracy = assetPair.Accuracy,
                baseAssetAccuracy = baseAsset.Accuracy,
                instrument
            });

            OrderBookUpdateReport orderBookUpdateReport = null;

            if (_isOrderBooksUpdateReportEnabled)
            {
                orderBookUpdateReport                    = new OrderBookUpdateReport(iterationDateTime);
                orderBookUpdateReport.AssetPair          = instrument.AssetPairId;
                orderBookUpdateReport.FirstQuoteAsk      = quotes[0].Ask;
                orderBookUpdateReport.FirstQuoteBid      = quotes[0].Bid;
                orderBookUpdateReport.SecondQuoteAsk     = quotes[1].Ask;
                orderBookUpdateReport.SecondQuoteBid     = quotes[1].Bid;
                orderBookUpdateReport.QuoteDateTime      = quotes[0].Time;
                orderBookUpdateReport.GlobalMarkup       = globalMarkup;
                orderBookUpdateReport.NoFreshQuoteMarkup = noQuotesMarkup;
                orderBookUpdateReport.PnLStopLossMarkup  = pnLStopLossMarkup;
                orderBookUpdateReport.FiatEquityMarkup   = fiatEquityStopLossMarkup;
            }

            IReadOnlyCollection <LimitOrder> limitOrders = Calculator.CalculateLimitOrders(
                quotes[0],
                quotes[1],
                instrument.Levels.ToArray(),
                baseAssetBalance?.Amount ?? 0,
                quoteAssetBalance?.Amount ?? 0,
                (int)timeSinceLastTrade.TotalSeconds,
                instrument.HalfLifePeriod,
                instrument.AllowSmartMarkup,
                globalMarkup,
                pnLStopLossMarkup,
                fiatEquityStopLossMarkup,
                noQuotesMarkup,
                assetPair.Accuracy,
                baseAsset.Accuracy,
                orderBookUpdateReport);

            await ValidateQuoteTimeoutAsync(limitOrders, quotes[0]);

            await ValidateQuoteTimeoutAsync(limitOrders, quotes[1]);

            ValidateMinVolume(limitOrders, (decimal)assetPair.MinVolume);

            await ValidatePriceAsync(limitOrders);

            await ValidateBalanceAsync(limitOrders, assetPair);

            WriteInfoLog(instrument.AssetPairId, quotes, timeSinceLastTrade, limitOrders,
                         "Direct limit orders calculated");

            if (orderBookUpdateReport != null)
            {
                await _orderBooksUpdatesReportPublisher.PublishAsync(orderBookUpdateReport);
            }

            return(new OrderBook
            {
                AssetPairId = instrument.AssetPairId,
                Time = DateTime.UtcNow,
                LimitOrders = limitOrders,
                IsDirect = true
            });
        }
        //public static async Task<AssetPair> GetAsync(this IAssetPairsRepository assetPairsRepository, string assetId1, string assetId2)
        //{
        //    var assetPairs = await assetPairsRepository.GetAllAsync();
        //    return assetPairs.FirstOrDefault(itm =>
        //        (itm.BaseAssetId == assetId1 && itm.QuotingAssetId == assetId2) ||
        //        (itm.BaseAssetId == assetId2 && itm.QuotingAssetId == assetId1));
        //}

        public static bool IsInverted(this AssetPair assetPair, string targetAsset)
        {
            return(assetPair.QuotingAssetId == targetAsset);
        }
        public static string RateToString(this double src, AssetPair assetPair)
        {
            var mask = "0." + new string('#', assetPair.Accuracy);

            return(src.ToString(mask));
        }
示例#8
0
 public Request(AssetPair pair, Network network = null)
 {
     NetworkSuggested = network;
     Pair             = pair;
 }
        public async Task <decimal> GetRateAsync(
            string baseAssetId,
            string quotingAssetId,
            decimal markupPercent,
            int markupPips,
            IMarkup merchantMarkup)
        {
            decimal askPrice, bidPrice;

            AssetPair priceAssetPair = null, assetPair = null;

            if (!string.IsNullOrEmpty(merchantMarkup.PriceAssetPairId))
            {
                _log.Info($"Price asset pair will be used: {merchantMarkup.PriceAssetPairId}");

                priceAssetPair = await _assetsLocalCache.GetAssetPairByIdAsync(merchantMarkup.PriceAssetPairId);

                IAssetPairRate assetPairRate =
                    await _assetRatesService.GetCurrentRateAsync(priceAssetPair.BaseAssetId, priceAssetPair.QuotingAssetId);

                _log.Info($"Price method: {merchantMarkup.PriceMethod.ToString()}");

                switch (merchantMarkup.PriceMethod)
                {
                case PriceMethod.None:
                case PriceMethod.Direct:
                    askPrice = assetPairRate.AskPrice;
                    bidPrice = assetPairRate.BidPrice;
                    break;

                case PriceMethod.Reverse:
                    askPrice = Math.Abs(assetPairRate.AskPrice) > 0
                            ? 1 / assetPairRate.AskPrice
                            : throw new MarketPriceZeroException("ask");
                    bidPrice = Math.Abs(assetPairRate.BidPrice) > 0
                            ? 1 / assetPairRate.BidPrice
                            : throw new MarketPriceZeroException("bid");
                    break;

                default:
                    throw new UnexpectedAssetPairPriceMethodException(merchantMarkup.PriceMethod);
                }
            }
            else
            {
                assetPair = await _assetsLocalCache.GetAssetPairAsync(baseAssetId, quotingAssetId);

                try
                {
                    IAssetPairRate assetPairRate = await _assetRatesService.GetCurrentRateAsync(baseAssetId, quotingAssetId);

                    askPrice = assetPairRate.AskPrice;

                    bidPrice = assetPairRate.BidPrice;
                }
                catch (Exception)
                {
                    askPrice = bidPrice = 1;
                }
            }

            _log.Info($"Market rate that will be used for calculation, askPrice = {askPrice}, bidPrice = {bidPrice}");

            Asset baseAsset = await _assetsLocalCache.GetAssetByIdAsync(baseAssetId);

            int pairAccuracy = priceAssetPair?.Accuracy ?? assetPair?.Accuracy ?? baseAsset.Accuracy;

            return(CalculatePrice(askPrice, bidPrice, pairAccuracy, baseAsset.Accuracy, markupPercent,
                                  markupPips, PriceCalculationMethod.ByBid, merchantMarkup));
        }
 public static double CalcEffectivePrice(List <TradeQueueItem.TradeInfo> trades, AssetPair assetPair, bool isBuy)
 {
     return(CalcEffectivePrice(trades.Select(x => new CommonTrade
     {
         Asset = x.MarketAsset,
         OppositeAsset = x.LimitAsset,
         Volume = x.MarketVolume,
         OppositeVolume = x.LimitVolume,
         Price = x.Price.GetValueOrDefault()
     }).ToList(), assetPair, isBuy));
 }
示例#11
0
        private void GetTradeOrderStatus(IOrderLimitProvider provider, string remoteOrderId, AssetPair market = null)
        {
            var context = new RemoteIdContext(UserContext.Current, remoteOrderId);

            if (market != null)
            {
                context.Market = market;
            }

            var r = AsyncContext.Run(() => provider.GetOrderStatusAsync(context));

            Assert.IsTrue(remoteOrderId.Equals(r.RemoteOrderId, StringComparison.Ordinal), "Remote trade order ids don't match");
            Trace.WriteLine($"Remote trade order id: {r.RemoteOrderId}");

            if (market != null)
            {
                if (r.AmountInitial.HasValue)
                {
                    Assert.IsTrue(r.AmountInitial.Value.Asset.Equals(market.Asset2));
                }
                if (r.AmountFilled.HasValue)
                {
                    Assert.IsTrue(r.AmountFilled.Value.Asset.Equals(market.Asset2));
                }
                if (r.AmountRemaining.HasValue)
                {
                    Assert.IsTrue(r.AmountRemaining.Value.Asset.Equals(market.Asset2));
                }
            }

            if (r.IsOpen)
            {
                Trace.WriteLine("Order is open");
            }
            if (r.IsCancelRequested)
            {
                Trace.WriteLine("Order is requested to be canceled");
            }
            if (r.IsCanceled)
            {
                Trace.WriteLine("Order is canceled");
            }
            if (r.IsClosed)
            {
                Trace.WriteLine("Order is closed");
            }
            if (r.IsFound)
            {
                Trace.WriteLine("Order is found");
            }

            if (r.Rate.HasValue)
            {
                Trace.WriteLine($"The rate of order is {r.Rate.Value}");
            }
            if (r.AmountInitial.HasValue)
            {
                Trace.WriteLine($"Initial amount is {r.AmountInitial.Value.Display}");
            }
            if (r.AmountFilled.HasValue)
            {
                Trace.WriteLine($"Filled amount is {r.AmountFilled.Value.Display}");
            }
            if (r.AmountRemaining.HasValue)
            {
                Trace.WriteLine($"Remaining amount is {r.AmountRemaining.Value.Display}");
            }
        }
 public static double CalcEffectivePrice(List <LimitQueueItem.LimitTradeInfo> trades, AssetPair assetPair, bool isBuy)
 {
     return(CalcEffectivePrice(trades.Select(x => new CommonTrade
     {
         Asset = x.Asset,
         OppositeAsset = x.OppositeAsset,
         Volume = x.Volume,
         OppositeVolume = x.OppositeVolume,
         Price = x.Price
     }).ToList(), assetPair, isBuy));
 }
        public static IReadOnlyList <ClientTrade> ToDomain(this LimitQueueItem.LimitOrderWithTrades item, AssetPair assetPair)
        {
            var trade = item.Trades[0];

            var limitVolume         = item.Trades.Sum(x => x.Volume);
            var oppositeLimitVolume = item.Trades.Sum(x => x.OppositeVolume);

            var price = CalcEffectivePrice(item.Trades, assetPair, item.Order.Volume > 0);

            var clientId = trade.ClientId ?? item.Order.ClientId;

            var depositAssetRecord  = CreateCommonPartForTradeRecord(trade, item.Order, item.Order.Id, price);
            var withdrawAssetRecord = CreateCommonPartForTradeRecord(trade, item.Order, item.Order.Id, price);

            depositAssetRecord.ClientId = withdrawAssetRecord.ClientId = clientId;

            depositAssetRecord.Amount  = oppositeLimitVolume;
            depositAssetRecord.AssetId = trade.OppositeAsset;

            withdrawAssetRecord.Amount = limitVolume;
            if (Math.Sign(limitVolume) == Math.Sign(oppositeLimitVolume))
            {
                withdrawAssetRecord.Amount *= -1;
            }
            withdrawAssetRecord.AssetId = trade.Asset;

            foreach (var t in item.Trades)
            {
                var transfer = t.Fees?.FirstOrDefault()?.Transfer;
                if (transfer != null)
                {
                    if (depositAssetRecord.AssetId == transfer.Asset)
                    {
                        depositAssetRecord.FeeSize += (double)transfer.Volume;
                        depositAssetRecord.FeeType  =
                            Service.OperationsRepository.AutorestClient.Models.FeeType.Absolute;
                    }
                    else
                    {
                        withdrawAssetRecord.FeeSize += (double)transfer.Volume;
                        withdrawAssetRecord.FeeType  =
                            Service.OperationsRepository.AutorestClient.Models.FeeType.Absolute;
                    }
                }
            }

            depositAssetRecord.Id  = Core.Domain.CashOperations.Utils.GenerateRecordId(depositAssetRecord.DateTime);
            withdrawAssetRecord.Id = Core.Domain.CashOperations.Utils.GenerateRecordId(withdrawAssetRecord.DateTime);

            return(new[] { depositAssetRecord, withdrawAssetRecord });
        }
示例#14
0
        private async Task <OrderBook> CalculateDirectOrderBookAsync(Instrument instrument)
        {
            Quote[] quotes = _b2C2OrderBookService.GetQuotes(instrument.AssetPairId);

            if (quotes == null || quotes.Length != 2)
            {
                _log.WarningWithDetails("No quotes for instrument", instrument.AssetPairId);
                return(null);
            }

            Balance  baseAssetBalance   = null;
            Balance  quoteAssetBalance  = null;
            TimeSpan timeSinceLastTrade = TimeSpan.Zero;

            if (instrument.AllowSmartMarkup)
            {
                AssetPairLink assetPairLink =
                    await _assetPairLinkService.GetByInternalAssetPairIdAsync(instrument.AssetPairId);

                if (assetPairLink != null && !assetPairLink.IsEmpty())
                {
                    baseAssetBalance =
                        await _balanceService.GetByAssetIdAsync(ExchangeNames.B2C2, assetPairLink.ExternalBaseAssetId);

                    quoteAssetBalance =
                        await _balanceService.GetByAssetIdAsync(ExchangeNames.B2C2, assetPairLink.ExternalQuoteAssetId);

                    timeSinceLastTrade =
                        DateTime.UtcNow - _tradeService.GetLastInternalTradeTime(instrument.AssetPairId);
                }
                else
                {
                    _log.WarningWithDetails("The asset pair link does not configured", new { instrument.AssetPairId });
                }
            }

            AssetPair assetPair = await _assetsServiceWithCache.TryGetAssetPairAsync(instrument.AssetPairId);

            Asset baseAsset = await _assetsServiceWithCache.TryGetAssetAsync(assetPair.BaseAssetId);

            MarketMakerSettings marketMakerSettings = await _marketMakerSettingsService.GetAsync();

            decimal pnLStopLossMarkup = await _pnLStopLossEngineService.GetTotalMarkupByAssetPairIdAsync(assetPair.Id);

            decimal fiatEquityStopLossMarkup = await _fiatEquityStopLossService.GetFiatEquityMarkup(assetPair.Id);

            decimal stopLossMarkup = await _noFreshQuotesStopLossService.GetNoFreshQuotesMarkup(assetPair.Id);

            IReadOnlyCollection <LimitOrder> limitOrders = Calculator.CalculateLimitOrders(
                quotes[0],
                quotes[1],
                instrument.Levels.ToArray(),
                baseAssetBalance?.Amount ?? 0,
                quoteAssetBalance?.Amount ?? 0,
                (int)timeSinceLastTrade.TotalSeconds,
                instrument.HalfLifePeriod,
                instrument.AllowSmartMarkup,
                marketMakerSettings.LimitOrderPriceMarkup,
                pnLStopLossMarkup,
                fiatEquityStopLossMarkup,
                stopLossMarkup,
                assetPair.Accuracy,
                baseAsset.Accuracy);

            await ValidateQuoteTimeoutAsync(limitOrders, quotes[0]);

            await ValidateQuoteTimeoutAsync(limitOrders, quotes[1]);

            ValidateMinVolume(limitOrders, (decimal)assetPair.MinVolume);

            await ValidatePriceAsync(limitOrders);

            await ValidateBalanceAsync(limitOrders, assetPair);

            WriteInfoLog(instrument.AssetPairId, quotes, timeSinceLastTrade, limitOrders,
                         "Direct limit orders calculated");

            return(new OrderBook
            {
                AssetPairId = instrument.AssetPairId,
                Time = DateTime.UtcNow,
                LimitOrders = limitOrders,
                IsDirect = true
            });
        }
示例#15
0
        private async Task <MultiOrderItemModel> ToMultiOrderItemModel(string clientId, AssetPair assetPair, BulkOrderItemModel item)
        {
            var requestId = GetNextRequestId();
            var fees      = _calculateOrderFees
                ? await _feeCalculator.GetLimitOrderFees(clientId, assetPair, item.OrderAction)
                : Array.Empty <LimitOrderFeeModel>();

            var model = new MultiOrderItemModel
            {
                Id          = requestId.ToString(),
                Price       = (double)item.Price,
                Volume      = (double)item.Volume,
                OrderAction = item.OrderAction.ToMeOrderAction(),
                Fee         = fees.FirstOrDefault()
            };

            if (!string.IsNullOrWhiteSpace(item.OldId))
            {
                model.OldId = item.OldId;
            }

            return(model);
        }
示例#16
0
        private async Task ProcessLimitOrdersAsync(LimitOrdersMessage message)
        {
            if (message.Orders == null || !message.Orders.Any())
            {
                return;
            }

            HashSet <string> limitOrderIds = message.Orders
                                             .Select(o => o.Order.Id)
                                             .ToHashSet();

            foreach (var orderMessage in message.Orders)
            {
                if (orderMessage.Trades == null || !orderMessage.Trades.Any())
                {
                    continue;
                }

                string assetPairId = orderMessage.Order.AssetPairId;

                AssetPair assetPair = _assetPairsRepository.TryGet(assetPairId);

                if (assetPair == null)
                {
                    _log.Error($"Asset pair {assetPairId} not found");
                    continue;
                }

                List <LimitOrdersMessage.Trade> allTrades = message.Orders.SelectMany(x => x.Trades).ToList();

                string marketDataKey  = RedisService.GetMarketDataKey(assetPairId);
                string baseVolumeKey  = RedisService.GetMarketDataBaseVolumeKey(assetPairId);
                string quoteVolumeKey = RedisService.GetMarketDataQuoteVolumeKey(assetPairId);
                string priceKey       = RedisService.GetMarketDataPriceKey(assetPairId);

                foreach (var tradeMessage in orderMessage.Trades.OrderBy(t => t.Timestamp).ThenBy(t => t.Index))
                {
                    long maxIndex = allTrades
                                    .Where(x => x.OppositeOrderId == tradeMessage.OppositeOrderId)
                                    .Max(t => t.Index);

                    var    price        = (decimal)tradeMessage.Price;
                    string priceString  = price.ToString(CultureInfo.InvariantCulture);
                    var    nowDate      = tradeMessage.Timestamp;
                    var    nowTradeDate = nowDate.AddMilliseconds(tradeMessage.Index);

                    await Task.WhenAll(
                        _database.HashSetAsync(marketDataKey, nameof(MarketSlice.LastPrice), priceString),
                        _database.SortedSetAddAsync(priceKey, RedisExtensions.SerializeWithTimestamp(priceString, nowTradeDate), nowTradeDate.ToUnixTime())
                        );

                    var isOppositeOrderIsLimit = limitOrderIds.Contains(tradeMessage.OppositeOrderId);
                    // If opposite order is market order, then unconditionally takes the given limit order.
                    // But if both of orders are limit orders, we should take only one of them.
                    if (isOppositeOrderIsLimit)
                    {
                        var isBuyOrder = orderMessage.Order.Volume > 0;
                        // Takes trade only for the sell limit orders
                        if (isBuyOrder)
                        {
                            continue;
                        }
                    }

                    decimal baseVolume;
                    decimal quotingVolume;

                    if (tradeMessage.Asset == assetPair.BaseAssetId)
                    {
                        baseVolume    = (decimal)tradeMessage.Volume;
                        quotingVolume = (decimal)tradeMessage.OppositeVolume;
                    }
                    else
                    {
                        baseVolume    = (decimal)tradeMessage.OppositeVolume;
                        quotingVolume = (decimal)tradeMessage.Volume;
                    }

                    if (tradeMessage.Price > 0 && baseVolume > 0 && quotingVolume > 0)
                    {
                        double now  = nowDate.ToUnixTime();
                        double from = (nowDate - _marketDataInterval).ToUnixTime();

                        decimal baseVolumeSum  = baseVolume;
                        decimal quoteVolumeSum = quotingVolume;
                        decimal priceChange    = 0;
                        decimal highValue      = (decimal)tradeMessage.Price;
                        decimal lowValue       = (decimal)tradeMessage.Price;

                        var tasks = new List <Task>();

                        var baseVolumesDataTask  = _database.SortedSetRangeByScoreAsync(baseVolumeKey, from, now);
                        var quoteVolumesDataTask = _database.SortedSetRangeByScoreAsync(quoteVolumeKey, from, now);
                        var priceDataTask        = _database.SortedSetRangeByScoreAsync(priceKey, from, now);

                        await Task.WhenAll(baseVolumesDataTask, quoteVolumesDataTask, priceDataTask);

                        baseVolumeSum += baseVolumesDataTask.Result
                                         .Where(x => x.HasValue)
                                         .Sum(x => RedisExtensions.DeserializeTimestamped <decimal>(x));

                        quoteVolumeSum += quoteVolumesDataTask.Result
                                          .Where(x => x.HasValue)
                                          .Sum(x => RedisExtensions.DeserializeTimestamped <decimal>(x));

                        var currentHigh = priceDataTask.Result.Any(x => x.HasValue)
                            ? priceDataTask.Result
                                          .Where(x => x.HasValue)
                                          .Max(x => RedisExtensions.DeserializeTimestamped <decimal>(x))
                            : (decimal?)null;

                        if (currentHigh.HasValue && currentHigh.Value > highValue)
                        {
                            highValue = currentHigh.Value;
                        }

                        var currentLow = priceDataTask.Result.Any(x => x.HasValue)
                            ? priceDataTask.Result
                                         .Where(x => x.HasValue)
                                         .Min(x => RedisExtensions.DeserializeTimestamped <decimal>(x))
                            : (decimal?)null;

                        if (currentLow.HasValue && currentLow.Value < lowValue)
                        {
                            lowValue = currentLow.Value;
                        }

                        var pricesData = priceDataTask.Result;

                        if (pricesData.Any() && pricesData[0].HasValue)
                        {
                            decimal openPrice = RedisExtensions.DeserializeTimestamped <decimal>(pricesData[0]);

                            if (openPrice > 0)
                            {
                                priceChange = ((decimal)tradeMessage.Price - openPrice) / openPrice;
                            }
                        }

                        tasks.Add(_database.SortedSetAddAsync(baseVolumeKey, RedisExtensions.SerializeWithTimestamp(baseVolume, nowTradeDate), now));
                        tasks.Add(_database.SortedSetAddAsync(quoteVolumeKey, RedisExtensions.SerializeWithTimestamp(quotingVolume, nowTradeDate), now));

                        await Task.WhenAll(tasks);

                        //send event only for the last trade in the order
                        if (tradeMessage.Index == maxIndex)
                        {
                            var evt = new MarketDataChangedEvent
                            {
                                AssetPairId = assetPairId,
                                VolumeBase  = baseVolumeSum,
                                VolumeQuote = quoteVolumeSum,
                                PriceChange = priceChange,
                                LastPrice   = (decimal)tradeMessage.Price,
                                High        = highValue,
                                Low         = lowValue
                            };

                            _cqrsEngine.PublishEvent(evt, MarketDataBoundedContext.Name);

                            try
                            {
                                await _tickerWriter.InsertOrReplaceAsync(new Ticker(assetPairId)
                                {
                                    VolumeBase  = baseVolumeSum,
                                    VolumeQuote = quoteVolumeSum,
                                    PriceChange = priceChange,
                                    LastPrice   = (decimal)tradeMessage.Price,
                                    High        = highValue,
                                    Low         = lowValue,
                                    UpdatedDt   = nowTradeDate
                                });
                            }
                            catch (Exception ex)
                            {
                                _log.Error(ex, "Error sending ticker to MyNySqlServer", context: evt.ToJson());
                            }
                        }
                    }
                }
            }
        }
示例#17
0
        public async Task <ResponseModel <MarketOrderResponseModel> > PlaceMarketOrderAsync(string clientId, AssetPair assetPair, OrderAction orderAction, decimal volume,
                                                                                            bool straight, double?reservedLimitVolume = null)
        {
            var fees = _calculateOrderFees
                ? await _feeCalculator.GetMarketOrderFees(clientId, assetPair, orderAction)
                : Array.Empty <MarketOrderFeeModel>();

            var order = new MarketOrderModel
            {
                Id                  = GetNextRequestId().ToString(),
                AssetPairId         = assetPair.Id,
                ClientId            = clientId,
                ReservedLimitVolume = reservedLimitVolume,
                Straight            = straight,
                Volume              = (double)Math.Abs(volume),
                OrderAction         = orderAction.ToMeOrderAction(),
                Fees                = fees
            };

            var response = await _matchingEngineClient.HandleMarketOrderAsync(order);

            CheckResponseAndThrowIfNull(response);

            var result = new MarketOrderResponseModel
            {
                Price = response.Price
            };

            return(ConvertToApiModel(response.Status, result));
        }
示例#18
0
 public ExchangeRateSubscribe(ObjectId id, AssetPair pair, SubscriptionType type) : base(id, type)
 {
     Pair = pair;
 }
 public static int Multiplier(this AssetPair assetPair)
 {
     return((int)Math.Pow(10, assetPair.Accuracy));
 }
示例#20
0
 public ExchangeRateSubscribe(ObjectId id, AssetPair pair, Network network, SubscriptionType type) : this(id, pair, type)
 {
     Network = network;
 }
示例#21
0
        private IReadOnlyList <OrderBook> GetOrderBooks(AssetPair assetPair1, AssetPair assetPair2, AssetPair assetPair3)
        {
            var orderBook1 = new OrderBook("FE", assetPair1, new List <VolumePrice>(), new List <VolumePrice>(), DateTime.UtcNow);
            var orderBook2 = new OrderBook("FE", assetPair2, new List <VolumePrice>(), new List <VolumePrice>(), DateTime.UtcNow);
            var orderBook3 = new OrderBook("FE", assetPair3, new List <VolumePrice>(), new List <VolumePrice>(), DateTime.UtcNow);

            return(new List <OrderBook> {
                orderBook1, orderBook2, orderBook3
            });
        }
 public LatestPriceRequestSubscription(ObjectId subscriberId, AssetPair pair, Network network = null, SubscriptionType type = SubscriptionType.Subscribe) : base(subscriberId, type)
 {
     Pair    = pair;
     Network = network;
 }
示例#23
0
        public async Task <decimal> GetMultiplierAsync(AssetPair pair)
        {
            var rates = await GetRatesAsync().ConfigureAwait(false);

            return(rates.Get(pair, 0));
        }
示例#24
0
        protected override async Task <AssetPrice> Handle(IProjection <AssetPairsInfo> projection, AssetPriceQuery query)
        {
            var price  = 1.0;
            var fordom = AssetPair.Fordom(query.ForAsset, query.DomAsset);
            var info   = projection.State;

            info.Tree.Log = _log;
            var timestamp = query.Timestamp;

            if (query.Timeline != "")
            {
                timestamp = _branchManager.GetTime(query.Timeline);
            }
            else if (timestamp == default)
            {
                timestamp = _branchManager.GetTime(_branchManager.ActiveBranch);
            }

            if (info.Pairs.ToList().Contains(fordom))
            {
                var result = await _handler.Handle(new SingleAssetPriceQuery(fordom)
                {
                    Timeline  = query.Timeline,
                    Timestamp = query.Timestamp,
                });

                if (result == null || result.Timestamp.Minus(timestamp).Days > 0 || timestamp.Minus(result.Timestamp).Days > 0)
                {
                    throw new InvalidOperationException($"Stale pricing date for {fordom}");
                }
                price = result.Price;
            }
            else
            {
                // try to triangulate the price
                var path = info.Tree.GetPath(query.ForAsset, query.DomAsset);
                if (path == null)
                {
                    throw new InvalidOperationException($"No path found from {query.ForAsset?.Ticker} to {query.DomAsset?.Ticker}");
                }

                foreach (var(forAsset, domAsset) in path)
                {
                    var pathForDom = forAsset + domAsset;
                    var isInverse  = info.Pairs.Contains(domAsset + forAsset);
                    if (isInverse)
                    {
                        pathForDom = domAsset + forAsset;
                    }

                    var pathResult = await _handler.Handle(new SingleAssetPriceQuery(pathForDom)
                    {
                        Timeline  = query.Timeline,
                        Timestamp = query.Timestamp,
                    });

                    if (pathResult == null || pathResult.Timestamp.Minus(timestamp).Days > 0 || timestamp.Minus(pathResult.Timestamp).Days > 0)
                    {
                        throw new InvalidOperationException($"Stale pricing date for {pathForDom}");
                    }

                    if (isInverse)
                    {
                        pathResult.Price = 1.0 / pathResult.Price;
                    }

                    price *= pathResult.Price;
                }
            }

            return(new AssetPrice(price, timestamp));
        }
示例#25
0
        private async Task ValidateBalanceAsync(IReadOnlyCollection <LimitOrder> limitOrders, AssetPair assetPair)
        {
            Asset baseAsset = await _assetsServiceWithCache.TryGetAssetAsync(assetPair.BaseAssetId);

            Asset quoteAsset = await _assetsServiceWithCache.TryGetAssetAsync(assetPair.QuotingAssetId);

            List <LimitOrder> sellLimitOrders = limitOrders
                                                .Where(o => o.Error == LimitOrderError.None)
                                                .Where(o => o.Type == LimitOrderType.Sell)
                                                .OrderBy(o => o.Price)
                                                .ToList();

            List <LimitOrder> buyLimitOrders = limitOrders
                                               .Where(o => o.Error == LimitOrderError.None)
                                               .Where(o => o.Type == LimitOrderType.Buy)
                                               .OrderByDescending(o => o.Price)
                                               .ToList();

            if (sellLimitOrders.Any())
            {
                decimal balance = (await _balanceService.GetByAssetIdAsync(ExchangeNames.Lykke, baseAsset.Id)).Amount;

                foreach (LimitOrder limitOrder in sellLimitOrders)
                {
                    decimal amount = limitOrder.Volume.TruncateDecimalPlaces(baseAsset.Accuracy, true);

                    if (balance - amount < 0)
                    {
                        decimal volume = balance.TruncateDecimalPlaces(baseAsset.Accuracy);

                        if (volume < (decimal)assetPair.MinVolume)
                        {
                            limitOrder.Error = LimitOrderError.NotEnoughFunds;
                        }
                        else
                        {
                            limitOrder.UpdateVolume(volume);
                        }
                    }

                    balance -= amount;
                }
            }

            if (buyLimitOrders.Any())
            {
                decimal balance = (await _balanceService.GetByAssetIdAsync(ExchangeNames.Lykke, quoteAsset.Id)).Amount;

                foreach (LimitOrder limitOrder in buyLimitOrders)
                {
                    decimal amount = (limitOrder.Price * limitOrder.Volume)
                                     .TruncateDecimalPlaces(quoteAsset.Accuracy, true);

                    if (balance - amount < 0)
                    {
                        decimal volume = (balance / limitOrder.Price).TruncateDecimalPlaces(baseAsset.Accuracy);

                        if (volume < (decimal)assetPair.MinVolume)
                        {
                            limitOrder.Error = LimitOrderError.NotEnoughFunds;
                        }
                        else
                        {
                            limitOrder.UpdateVolume(volume);
                        }
                    }

                    balance -= amount;
                }
            }
        }
示例#26
0
        public async Task <ResponseModel <LimitOrderResponseModel> > PlaceLimitOrderAsync(string clientId, AssetPair assetPair, OrderAction orderAction, decimal volume,
                                                                                          decimal price, bool cancelPreviousOrders = false)
        {
            var requestId = GetNextRequestId();
            var fees      = _calculateOrderFees
                ? await _feeCalculator.GetLimitOrderFees(clientId, assetPair, orderAction)
                : Array.Empty <LimitOrderFeeModel>();

            var order = new LimitOrderModel
            {
                Id                   = requestId.ToString(),
                AssetPairId          = assetPair.Id,
                ClientId             = clientId,
                Price                = (double)price,
                CancelPreviousOrders = cancelPreviousOrders,
                Volume               = (double)Math.Abs(volume),
                OrderAction          = orderAction.ToMeOrderAction(),
                Fees                 = fees
            };

            var response = await _matchingEngineClient.PlaceLimitOrderAsync(order);

            CheckResponseAndThrowIfNull(response);

            var result = new LimitOrderResponseModel
            {
                Id = requestId
            };

            return(ConvertToApiModel(response.Status, result));
        }
        private BitcoinIndonesiaSchema.TickerEntryResponse CreateTickerObject(Dictionary <string, decimal> response, AssetPair pair)
        {
            response.TryGetValue("high", out decimal high);
            response.TryGetValue("low", out decimal low);
            response.TryGetValue("vol_" + pair.Asset1.ShortCode.ToLower(), out decimal volBase);
            response.TryGetValue("vol_" + pair.Asset2.ShortCode.ToLower(), out decimal volQuote);
            response.TryGetValue("last", out decimal last);
            response.TryGetValue("buy", out decimal buy);
            response.TryGetValue("sell", out decimal sell);
            response.TryGetValue("server_time", out decimal server_time);

            return(new BitcoinIndonesiaSchema.TickerEntryResponse()
            {
                high = high,
                low = low,
                vol_base = volBase,
                vol_quote = volQuote,
                last = last,
                buy = buy,
                sell = sell,
                server_time = (long)server_time
            });
        }
示例#28
0
        public async Task <ResponseModel <BulkOrderResponseModel> > PlaceBulkLimitOrderAsync(string clientId, AssetPair assetPair, IEnumerable <BulkOrderItemModel> items, bool cancelPrevious, CancelMode?cancelMode)
        {
            var requestId = GetNextRequestId();

            var orders = new ConcurrentBag <MultiOrderItemModel>();
            await items.ParallelForEachAsync(async item =>
            {
                var subOrder = await ToMultiOrderItemModel(clientId, assetPair, item);
                orders.Add(subOrder);
            });

            var order = new MultiLimitOrderModel
            {
                Id                   = requestId.ToString(),
                ClientId             = clientId,
                AssetPairId          = assetPair.Id,
                CancelPreviousOrders = cancelPrevious,
                Orders               = orders.ToArray()
            };

            if (cancelMode.HasValue)
            {
                order.CancelMode = cancelMode.Value.ToMeCancelModel();
            }

            var response = await _matchingEngineClient.PlaceMultiLimitOrderAsync(order);

            CheckResponseAndThrowIfNull(response);

            var result = new BulkOrderResponseModel
            {
                AssetPairId = assetPair.Id,
                Error       = response.Status != MeStatusCodes.Ok ? ErrorCodeType.Rejected : default(ErrorCodeType?),
                Statuses    = response.Statuses?.Select(ToBulkOrderItemStatusModel).ToArray()
            };

            return(ConvertToApiModel(response.Status, result));
        }
 public IReadOnlyList <ICandle> FillGapUpTo(AssetPair assetPair, CandlePriceType priceType, DateTime dateTime, ICandle endCandle)
 {
     return(Array.Empty <ICandle>());
 }
示例#30
0
 private void CheckResponseErrors <T>(BittrexSchema.BaseResponse <T> response, AssetPair pair = null)
 {
     if (response.success == false)
     {
         if (response.message.Equals("INVALID_MARKET") && pair != null)
         {
             throw new NoAssetPairException(pair, this);
         }
         throw new ApiResponseException($"API error: {response.message}", this);
     }
 }