Example #1
0
        public async Task CloseAsync(IReadOnlyCollection <Position> positions, ExternalTrade externalTrade)
        {
            await _tradeService.RegisterAsync(externalTrade);

            foreach (Position position in positions)
            {
                await _openPositionRepository.DeleteAsync(position.AssetPairId, position.Id);

                position.Close(externalTrade);

                await _positionRepository.UpdateAsync(position);

                try
                {
                    await _positionRepositoryPostgres.UpdateAsync(position);
                }
                catch (Exception exception)
                {
                    _log.ErrorWithDetails(exception, "An error occurred while updating position in the postgres DB",
                                          position);
                }

                await _summaryReportService.RegisterClosePositionAsync(position);

                foreach (var closedPositionHandler in _closedPositionHandlers)
                {
                    await closedPositionHandler.HandleClosedPositionAsync(position);
                }

                _log.InfoWithDetails("Position closed", position);
            }
        }
Example #2
0
        private async Task ProcessMessageAsync(ExternalTrade message)
        {
            var sw = new Stopwatch();

            try
            {
                var model = Mapper.Map <Core.Domain.Trades.ExternalTrade>(message);

                await Task.WhenAll(
                    _externalTradeService.HandleAsync(model),
                    _realisedPnLService.CalculateAsync(model));
            }
            catch (Exception exception)
            {
                _log.ErrorWithDetails(exception, "An error occurred during processing external trade", message);
            }
            finally
            {
                sw.Stop();

                _log.InfoWithDetails("External trade was received",
                                     new
                {
                    message,
                    sw.ElapsedMilliseconds
                });
            }
        }
        public async Task InsertAsync(ExternalTrade externalTrade)
        {
            var entity = new ExternalTradeEntity(GetPartitionKey(externalTrade.Timestamp), GetRowKey(externalTrade.Id));

            Mapper.Map(externalTrade, entity);

            await _storage.InsertOrReplaceAsync(entity);
        }
Example #4
0
        private async Task <ExternalTrade> ExecuteLimitOrderAsync(string assetPairId, decimal volume,
                                                                  PositionType positionType)
        {
            ExternalTrade externalTrade = null;

            if (!_attempts.TryGetValue(assetPairId, out Tuple <int, int> attempt))
            {
                attempt = new Tuple <int, int>(0, 0);
            }

            try
            {
                if (attempt.Item2 <= 0)
                {
                    if (positionType == PositionType.Long)
                    {
                        externalTrade = await _externalExchangeService.ExecuteSellLimitOrderAsync(assetPairId, volume);
                    }
                    else
                    {
                        externalTrade = await _externalExchangeService.ExecuteBuyLimitOrderAsync(assetPairId, volume);
                    }

                    _attempts.Remove(assetPairId);
                }
                else
                {
                    _attempts[assetPairId] = new Tuple <int, int>(attempt.Item1, attempt.Item2 - 1);

                    _log.InfoWithDetails("Execution of hedge limit order is skipped", new
                    {
                        assetPairId,
                        volume,
                        positionType,
                        attempt   = attempt.Item1,
                        iteration = attempt.Item2
                    });
                }
            }
            catch (Exception exception)
            {
                _attempts[assetPairId] =
                    new Tuple <int, int>(attempt.Item1 + 1, Math.Min(attempt.Item1 + 1, MaxIterations));

                _log.WarningWithDetails("Can not close positions.", exception,
                                        new
                {
                    assetPairId,
                    volume,
                    positionType,
                    attempt = attempt.Item1
                });
            }

            return(externalTrade);
        }
        private async Task ClosePositionsAsync(IEnumerable <Position> positions, PositionType positionType)
        {
            foreach (IGrouping <string, Position> group in positions.GroupBy(o => o.AssetPairId))
            {
                var startedAt = DateTime.UtcNow;

                MarketMakerState marketMakerState = await _marketMakerStateService.GetStateAsync();

                if (marketMakerState.Status != MarketMakerStatus.Active)
                {
                    continue;
                }

                Instrument instrument = await _instrumentService.GetByAssetPairIdAsync(group.Key);

                decimal originalVolume = group.Sum(o => o.Volume);

                decimal volume = Math.Round(originalVolume, instrument.VolumeAccuracy);

                if (volume < instrument.MinVolume)
                {
                    _log.InfoWithDetails("The volume of open positions is less than min volume", new
                    {
                        instrument.AssetPairId,
                        positionType,
                        volume,
                        instrument.MinVolume
                    });
                    continue;
                }

                ExternalTrade externalTrade = await ExecuteLimitOrderAsync(group.Key, volume, positionType);

                if (externalTrade != null)
                {
                    await _positionService.CloseAsync(group.ToArray(), externalTrade);

                    await _remainingVolumeService.RegisterVolumeAsync(group.Key,
                                                                      (originalVolume - externalTrade.Volume) *GetSign(positionType));
                }

                var finishedAt = DateTime.UtcNow;

                _log.Info("HedgeService.ClosePositionsAsync() completed.", new
                {
                    AssetPairId = instrument.AssetPairId,
                    TradeIds    = positions.Select(x => x.TradeId).ToList(),
                    StartedAt   = startedAt,
                    FinishedAt  = finishedAt,
                    Latency     = (finishedAt - startedAt).TotalMilliseconds
                });

                PrometheusMetrics.HedgeAssetPairLatency.Inc((finishedAt - startedAt).TotalMilliseconds);
            }
        }
        public async Task InsertAsync(ExternalTrade externalTrade)
        {
            using (DataContext context = _connectionFactory.CreateDataContext())
            {
                ExternalTradeEntity entity = Mapper.Map <ExternalTradeEntity>(externalTrade);

                context.Add(entity);

                await context.SaveChangesAsync();
            }
        }
Example #7
0
        public async Task <ExternalTradeModel> GetExternalTradeByIdAsync(string tradeId)
        {
            ExternalTrade externalTrade = await _tradeService.GetExternalTradeByIdAsync(tradeId);

            if (externalTrade == null)
            {
                throw new ValidationApiException(HttpStatusCode.NotFound, "External trade does not exist.");
            }

            return(Mapper.Map <ExternalTradeModel>(externalTrade));
        }
Example #8
0
        public async Task InsertAsync(ExternalTrade externalTrade)
        {
            var entity = new ExternalTradeEntity(GetPartitionKey(externalTrade.Time), GetRowKey(externalTrade.Id));

            Mapper.Map(externalTrade, entity);

            await _storage.InsertOrReplaceAsync(entity);

            AzureIndex index = new AzureIndex(GetIndexPartitionKey(externalTrade.Id), GetRowKey(externalTrade.Id),
                                              entity);

            await _indicesStorage.InsertOrReplaceAsync(index);
        }
Example #9
0
        public async Task RegisterAsync(ExternalTrade externalTrade)
        {
            await TraceWrapper.TraceExecutionTimeAsync("Inserting external trade to the Azure storage",
                                                       () => _externalTradeRepository.InsertAsync(externalTrade), _log);

            try
            {
                await TraceWrapper.TraceExecutionTimeAsync("Inserting external trade to the Postgres storage",
                                                           () => _externalTradeRepositoryPostgres.InsertAsync(externalTrade), _log);
            }
            catch (Exception exception)
            {
                _log.ErrorWithDetails(exception,
                                      "An error occurred while inserting external trade to the Postgres DB", externalTrade);
            }
        }
        public async Task CalculateAsync(ExternalTrade externalTrade)
        {
            IReadOnlyCollection <WalletSettings> walletsSettings = await _walletSettingsService.GetWalletsAsync();

            walletsSettings = walletsSettings.Where(o => o.Enabled && o.HandleExternalTrades).ToArray();

            if (!walletsSettings.Any())
            {
                return;
            }

            var tradeData = new TradeData
            {
                Id                   = externalTrade.OrderId,
                Exchange             = externalTrade.Exchange,
                AssetPair            = externalTrade.AssetPairId,
                BaseAsset            = externalTrade.BaseAssetId,
                QuoteAsset           = externalTrade.QuoteAssetId,
                Price                = externalTrade.Price,
                Volume               = externalTrade.Volume,
                Type                 = externalTrade.Type,
                Time                 = externalTrade.Time,
                LimitOrderId         = externalTrade.OrderId,
                OppositeClientId     = null,
                OppositeLimitOrderId = null
            };

            foreach (WalletSettings walletSettings in walletsSettings)
            {
                string[] assets = walletSettings.Assets
                                  .Intersect(new[] { externalTrade.BaseAssetId, externalTrade.QuoteAssetId })
                                  .ToArray();

                foreach (string assetId in assets)
                {
                    AssetRealisedPnL assetRealisedPnL =
                        await CalculateAsync(tradeData, walletSettings.Id, assetId);

                    await _assetRealisedPnLRepository.InsertAsync(assetRealisedPnL);

                    _cache.Set(assetRealisedPnL);
                }
            }

            _log.InfoWithDetails("External trade handled", tradeData);
        }
        private async Task <bool> ExecuteAsync(InternalOrder internalOrder)
        {
            ExternalTrade externalTrade = null;

            string error = null;

            try
            {
                externalTrade = await _externalExchangeService.ExecuteLimitOrderAsync(internalOrder.AssetPairId,
                                                                                      internalOrder.Volume, internalOrder.Price, internalOrder.Type);
            }
            catch (NotEnoughLiquidityException)
            {
                error = Errors.NotEnoughLiquidity;
            }
            catch (Exception)
            {
                error = "An unexpected error occurred while executing order";
            }

            if (!string.IsNullOrEmpty(error))
            {
                internalOrder.Status       = InternalOrderStatus.Failed;
                internalOrder.RejectReason = error;

                await _internalOrderRepository.UpdateAsync(internalOrder);

                return(false);
            }

            // ReSharper disable once PossibleNullReferenceException
            internalOrder.ExecutedPrice  = externalTrade.Price;
            internalOrder.ExecutedVolume = externalTrade.Volume;
            internalOrder.TradeId        = externalTrade.Id;
            internalOrder.Status         = InternalOrderStatus.Executed;

            await _internalOrderRepository.UpdateAsync(internalOrder);

            await _positionService.CloseAsync(internalOrder, externalTrade);

            return(true);
        }
        public async Task CloseRemainingVolumeAsync(string assetPairId, ExternalTrade externalTrade)
        {
            await _tradeService.RegisterAsync(externalTrade);

            Position position = Position.Create(assetPairId, externalTrade);

            await _positionRepository.InsertAsync(position);

            try
            {
                await _positionRepositoryPostgres.InsertAsync(position);
            }
            catch (Exception exception)
            {
                _log.ErrorWithDetails(exception,
                                      "An error occurred while inserting remaining volume position to the postgres DB", position);
            }

            _log.InfoWithDetails("Position with remaining volume closed", position);
        }
Example #13
0
        private async Task ClosePositionsAsync(IEnumerable <Position> positions, PositionType positionType)
        {
            foreach (IGrouping <string, Position> group in positions.GroupBy(o => o.AssetPairId))
            {
                MarketMakerState marketMakerState = await _marketMakerStateService.GetStateAsync();

                if (marketMakerState.Status != MarketMakerStatus.Active)
                {
                    continue;
                }

                Instrument instrument = await _instrumentService.GetByAssetPairIdAsync(group.Key);

                decimal originalVolume = group.Sum(o => o.Volume);

                decimal volume = Math.Round(originalVolume, instrument.VolumeAccuracy);

                if (volume < instrument.MinVolume)
                {
                    _log.InfoWithDetails("The volume of open positions is less than min volume", new
                    {
                        instrument.AssetPairId,
                        positionType,
                        volume,
                        instrument.MinVolume
                    });
                    continue;
                }

                ExternalTrade externalTrade = await ExecuteLimitOrderAsync(group.Key, volume, positionType);

                if (externalTrade != null)
                {
                    await _positionService.CloseAsync(group.ToArray(), externalTrade);

                    await _remainingVolumeService.RegisterVolumeAsync(group.Key,
                                                                      (originalVolume - volume) *GetSign(positionType));
                }
            }
        }
        public async Task CloseAsync(InternalOrder internalOrder, ExternalTrade externalTrade)
        {
            await _tradeService.RegisterAsync(externalTrade);

            Position position = Position.Create(internalOrder, externalTrade);

            await _positionRepository.InsertAsync(position);

            try
            {
                await _positionRepositoryPostgres.InsertAsync(position);
            }
            catch (Exception exception)
            {
                _log.ErrorWithDetails(exception, "An error occurred while updating position in the postgres DB",
                                      position);
            }

            await _summaryReportService.RegisterClosePositionAsync(position);

            _log.InfoWithDetails("Position closed", position);
        }
Example #15
0
        private async Task CloseRemainingVolumeAsync()
        {
            IReadOnlyCollection <RemainingVolume> remainingVolumes = await _remainingVolumeService.GetAllAsync();

            foreach (RemainingVolume remainingVolume in remainingVolumes)
            {
                MarketMakerState marketMakerState = await _marketMakerStateService.GetStateAsync();

                if (marketMakerState.Status != MarketMakerStatus.Active)
                {
                    continue;
                }

                Instrument instrument = await _instrumentService.GetByAssetPairIdAsync(remainingVolume.AssetPairId);

                if (Math.Abs(remainingVolume.Volume) < instrument.MinVolume)
                {
                    continue;
                }

                decimal volume = Math.Round(Math.Abs(remainingVolume.Volume), instrument.VolumeAccuracy);

                PositionType positionType = remainingVolume.Volume > 0
                    ? PositionType.Long
                    : PositionType.Short;

                ExternalTrade externalTrade =
                    await ExecuteLimitOrderAsync(instrument.AssetPairId, volume, positionType);

                if (externalTrade != null)
                {
                    await _positionService.CloseRemainingVolumeAsync(instrument.AssetPairId, externalTrade);

                    await _remainingVolumeService.RegisterVolumeAsync(instrument.AssetPairId,
                                                                      volume *GetSign(positionType) * -1);
                }
            }
        }
Example #16
0
 private string GetRowKey(ExternalTrade externalTrade)
 {
     return($"{externalTrade.OrderId}_{externalTrade.Exchange}");
 }
Example #17
0
 public Task HandleAsync(ExternalTrade externalTrade)
 {
     return(_externalTradeRepository.InsertAsync(externalTrade));
 }
        private async Task CloseRemainingVolumeAsync()
        {
            var startedAt = DateTime.UtcNow;

            // close remaining volumes once per 1 min
            lock (_remainingSync)
            {
                if ((startedAt - _lastCloseRemaining).TotalSeconds > 60)
                {
                    _lastCloseRemaining = startedAt;
                }
                else
                {
                    return;
                }
            }

            IReadOnlyCollection <RemainingVolume> remainingVolumes = await _remainingVolumeService.GetAllAsync();

            foreach (RemainingVolume remainingVolume in remainingVolumes)
            {
                MarketMakerState marketMakerState = await _marketMakerStateService.GetStateAsync();

                if (marketMakerState.Status != MarketMakerStatus.Active)
                {
                    continue;
                }

                Instrument instrument = await _instrumentService.GetByAssetPairIdAsync(remainingVolume.AssetPairId);

                if (Math.Abs(remainingVolume.Volume) < instrument.MinVolume)
                {
                    continue;
                }

                decimal volume = Math.Round(Math.Abs(remainingVolume.Volume), instrument.VolumeAccuracy);

                PositionType positionType = remainingVolume.Volume > 0
                    ? PositionType.Long
                    : PositionType.Short;

                ExternalTrade externalTrade =
                    await ExecuteLimitOrderAsync(instrument.AssetPairId, volume, positionType);

                if (externalTrade != null)
                {
                    await _positionService.CloseRemainingVolumeAsync(instrument.AssetPairId, externalTrade);

                    await _remainingVolumeService.RegisterVolumeAsync(instrument.AssetPairId,
                                                                      externalTrade.Volume *GetSign(positionType) * -1);
                }
            }

            var finishedAt = DateTime.UtcNow;

            _log.Info("HedgeService.CloseRemainingVolumeAsync() completed.", new
            {
                StartedAt  = startedAt,
                FinishedAt = finishedAt,
                Latency    = (finishedAt - startedAt).TotalMilliseconds
            });
        }
        private async Task <ExternalTrade> ExecuteLimitOrderAsync(string assetPairId, decimal volume,
                                                                  PositionType positionType)
        {
            var startedAt = DateTime.UtcNow;

            ExternalTrade externalTrade = null;

            if (!_attempts.TryGetValue(assetPairId, out Tuple <int, int> attempt))
            {
                attempt = new Tuple <int, int>(0, 0);
            }

            try
            {
                if (attempt.Item2 <= 0)
                {
                    if (positionType == PositionType.Long)
                    {
                        externalTrade = await _externalExchangeService.ExecuteSellLimitOrderAsync(assetPairId, volume);
                    }
                    else
                    {
                        externalTrade = await _externalExchangeService.ExecuteBuyLimitOrderAsync(assetPairId, volume);
                    }

                    _attempts.Remove(assetPairId);
                }
                else
                {
                    _attempts[assetPairId] = new Tuple <int, int>(attempt.Item1, attempt.Item2 - 1);

                    _log.InfoWithDetails("Execution of hedge limit order is skipped", new
                    {
                        assetPairId,
                        volume,
                        positionType,
                        attempt   = attempt.Item1,
                        iteration = attempt.Item2
                    });
                }
            }
            catch (Exception exception)
            {
                _attempts[assetPairId] =
                    new Tuple <int, int>(attempt.Item1 + 1, Math.Min(attempt.Item1 + 1, MaxIterations));

                _log.WarningWithDetails("Can not close positions.", exception,
                                        new
                {
                    assetPairId,
                    volume,
                    positionType,
                    attempt = attempt.Item1
                });
            }

            var finishedAt = DateTime.UtcNow;

            _log.Info("HedgeService.ExecuteLimitOrderAsync() completed.", new
            {
                AssetPairId = assetPairId,
                StartedAt   = startedAt,
                FinishedAt  = finishedAt,
                Latency     = (finishedAt - startedAt).TotalMilliseconds
            });

            return(externalTrade);
        }
Example #20
0
 public Task RegisterAsync(ExternalTrade externalTrade)
 {
     return(_externalTradeRepository.InsertAsync(externalTrade));
 }