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); } }
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); }
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(); } }
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)); }
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); }
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); }
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); }
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); } } }
private string GetRowKey(ExternalTrade externalTrade) { return($"{externalTrade.OrderId}_{externalTrade.Exchange}"); }
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); }
public Task RegisterAsync(ExternalTrade externalTrade) { return(_externalTradeRepository.InsertAsync(externalTrade)); }