public async Task <PositionCloseResponse> CloseAsync([CanBeNull][FromRoute] string positionId, [FromBody] PositionCloseRequest request = null, [FromQuery] string accountId = null) { if (!_ordersCache.Positions.TryGetPositionById(positionId, out var position)) { throw new InvalidOperationException("Position not found"); } ValidationHelper.ValidateAccountId(position, accountId); try { ValidateDayOff(position.AssetPairId); } catch (Exception e) { if (e.Message.Contains(CommonErrorCodes.InstrumentTradingDisabled)) { return(new PositionCloseResponse() { PositionId = positionId, Result = PositionCloseResultContract.FailedToClose, ErrorCode = CommonErrorCodes.InstrumentTradingDisabled, }); } throw; } var originator = GetOriginator(request?.Originator); var closeResult = await _tradingEngine.ClosePositionsAsync( new PositionsCloseData( position, position.AccountId, position.AssetPairId, position.OpenMatchingEngineId, position.ExternalProviderId, originator, request?.AdditionalInfo, position.EquivalentAsset), true); _operationsLogService.AddLog("action order.close", position.AccountId, request?.ToJson(), closeResult.ToJson()); return(new PositionCloseResponse { PositionId = positionId, OrderId = closeResult.order?.Id, Result = closeResult.result.ToType <PositionCloseResultContract>() }); }
public async Task CloseAsync([CanBeNull][FromRoute] string positionId, [FromBody] PositionCloseRequest request = null) { if (!_ordersCache.Positions.TryGetPositionById(positionId, out var position)) { throw new InvalidOperationException("Position not found"); } ValidateDayOff(position.AssetPairId); var originator = GetOriginator(request?.Originator); var correlationId = request?.CorrelationId ?? _identityGenerator.GenerateGuid(); var order = await _tradingEngine.ClosePositionsAsync( new PositionsCloseData( new List <Position> { position }, position.AccountId, position.AssetPairId, position.Volume, position.OpenMatchingEngineId, position.ExternalProviderId, originator, request?.AdditionalInfo, correlationId, position.EquivalentAsset)); if (order.Status != OrderStatus.Executed && order.Status != OrderStatus.ExecutionStarted) { throw new InvalidOperationException(order.RejectReasonText); } _operationsLogService.AddLog("action order.close", order.AccountId, request?.ToJson(), order.ToJson()); }
public async Task Handle(LiquidatePositionsInternalCommand command, IEventPublisher publisher) { var executionInfo = await _operationExecutionInfoRepository.GetAsync <LiquidationOperationData>( operationName : LiquidationSaga.OperationName, id : command.OperationId); if (executionInfo?.Data == null) { return; } await _log.WriteInfoAsync(nameof(LiquidationCommandsHandler), nameof(LiquidatePositionsInternalCommand), command.ToJson(), "Checking if position liquidation should be failed"); var account = _accountsCache.Get(executionInfo.Data.AccountId); if (ShouldFailExecution(account.GetAccountLevel(), executionInfo.Data.LiquidationType)) { await _log.WriteWarningAsync( nameof(LiquidationCommandsHandler), nameof(LiquidatePositionsInternalCommand), new { accountId = account.Id, accountLevel = account.GetAccountLevel().ToString() }.ToJson(), $"Unable to liquidate positions since account level is not {ValidAccountLevel.ToString()}."); await _failureExecutor.ExecuteAsync(publisher, account.Id, command.OperationId, $"Account level is not {ValidAccountLevel.ToString()}."); return; } var positions = _ordersCache.Positions .GetPositionsByAccountIds(executionInfo.Data.AccountId) .Where(p => command.PositionIds.Contains(p.Id)) .ToArray(); if (!positions.Any()) { publisher.PublishEvent(new PositionsLiquidationFinishedInternalEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), LiquidationInfos = command.PositionIds.Select(p => new LiquidationInfo { PositionId = p, IsLiquidated = false, Comment = "Opened position was not found" }).ToList() }); return; } if (!_liquidationHelper.CheckIfNetVolumeCanBeLiquidated(command.AssetPairId, positions, out var details)) { publisher.PublishEvent(new NotEnoughLiquidityInternalEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), PositionIds = command.PositionIds, Details = details }); return; } var liquidationInfos = new List <LiquidationInfo>(); var comment = string.Empty; switch (executionInfo.Data.LiquidationType) { case LiquidationType.Mco: comment = "MCO liquidation"; break; case LiquidationType.Normal: comment = "Liquidation"; break; case LiquidationType.Forced: comment = "Close positions group"; break; } var positionGroups = positions .GroupBy(p => (p.AssetPairId, p.AccountId, p.Direction, p .OpenMatchingEngineId, p.ExternalProviderId, p.EquivalentAsset)) .Select(gr => new PositionsCloseData( gr.FreezeOrder(), gr.Key.AccountId, gr.Key.AssetPairId, gr.Key.OpenMatchingEngineId, gr.Key.ExternalProviderId, executionInfo.Data.OriginatorType, executionInfo.Data.AdditionalInfo, gr.Key.EquivalentAsset, comment)); foreach (var positionGroup in positionGroups) { try { var(result, order) = await _tradingEngine.ClosePositionsAsync(positionGroup, false); foreach (var position in positionGroup.Positions) { liquidationInfos.Add(new LiquidationInfo { PositionId = position.Value.Id, IsLiquidated = true, Comment = order != null ? $"Order: {order.Id}" : result.ToString() }); } } catch (Exception ex) { await _log.WriteWarningAsync(nameof(LiquidationCommandsHandler), nameof(LiquidatePositionsInternalCommand), $"Failed to close positions {string.Join(",", positionGroup.Positions.Select(p => p.Value.Id))} on liquidation operation #{command.OperationId}", ex); foreach (var position in positionGroup.Positions) { liquidationInfos.Add(new LiquidationInfo { PositionId = position.Value.Id, IsLiquidated = false, Comment = $"Close position failed: {ex.Message}" }); } } } publisher.PublishEvent(new PositionsLiquidationFinishedInternalEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), LiquidationInfos = liquidationInfos }); }
public async Task Handle(LiquidatePositionsInternalCommand command, IEventPublisher publisher) { var executionInfo = await _operationExecutionInfoRepository.GetAsync <LiquidationOperationData>( operationName : LiquidationSaga.OperationName, id : command.OperationId); if (executionInfo?.Data == null) { return; } var positions = _ordersCache.Positions .GetPositionsByAccountIds(executionInfo.Data.AccountId) .Where(p => command.PositionIds.Contains(p.Id)) .ToArray(); if (!positions.Any()) { publisher.PublishEvent(new PositionsLiquidationFinishedInternalEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), LiquidationInfos = command.PositionIds.Select(p => new LiquidationInfo { PositionId = p, IsLiquidated = false, Comment = "Opened position was not found" }).ToList() }); return; } if (!CheckIfNetVolumeCanBeLiquidated(executionInfo.Data.AccountId, command.AssetPairId, positions, out var details)) { publisher.PublishEvent(new NotEnoughLiquidityInternalEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), PositionIds = command.PositionIds, Details = details }); return; } var liquidationInfos = new List <LiquidationInfo>(); var comment = string.Empty; switch (executionInfo.Data.LiquidationType) { case LiquidationType.Mco: comment = "MCO liquidation"; break; case LiquidationType.Normal: comment = "Liquidation"; break; case LiquidationType.Forced: comment = "Close positions group"; break; } var positionGroups = positions .GroupBy(p => (p.AssetPairId, p.AccountId, p.Direction, p .OpenMatchingEngineId, p.ExternalProviderId, p.EquivalentAsset)) .Where(gr => gr.Any()) .Select(gr => new PositionsCloseData( gr.ToList(), gr.Key.AccountId, gr.Key.AssetPairId, gr.Sum(x => x.Volume), gr.Key.OpenMatchingEngineId, gr.Key.ExternalProviderId, executionInfo.Data.OriginatorType, executionInfo.Data.AdditionalInfo, command.OperationId, gr.Key.EquivalentAsset, comment)); foreach (var positionGroup in positionGroups) { try { var order = await _tradingEngine.ClosePositionsAsync(positionGroup); if (order.Status != OrderStatus.Executed && order.Status != OrderStatus.ExecutionStarted) { throw new Exception(order.RejectReasonText); } foreach (var position in positionGroup.Positions) { liquidationInfos.Add(new LiquidationInfo { PositionId = position.Id, IsLiquidated = true, Comment = $"Order: {order.Id}" }); } } catch (Exception ex) { await _log.WriteWarningAsync(nameof(LiquidationCommandsHandler), nameof(LiquidatePositionsInternalCommand), $"Failed to close positions {string.Join(",", positionGroup.Positions.Select(p => p.Id))} on liquidation operation #{command.OperationId}", ex); foreach (var position in positionGroup.Positions) { liquidationInfos.Add(new LiquidationInfo { PositionId = position.Id, IsLiquidated = false, Comment = $"Close position failed: {ex.Message}" }); } } } publisher.PublishEvent(new PositionsLiquidationFinishedInternalEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), LiquidationInfos = liquidationInfos }); }