public Dictionary <string, (PositionCloseResult, Order)> StartLiquidation(string accountId, OriginatorType originator, string additionalInfo, string operationId) { var result = new Dictionary <string, (PositionCloseResult, Order)>(); var command = new StartLiquidationInternalCommand { OperationId = operationId, CreationTime = _dateService.Now(), AccountId = accountId, LiquidationType = LiquidationType.Forced, OriginatorType = originator, AdditionalInfo = additionalInfo }; _cqrsSender.SendCommandToSelf(command); var positions = _ordersCache.Positions.GetPositionsByAccountIds(accountId); var openPositions = new List <Position>(); foreach (var position in positions) { switch (position.Status) { case PositionStatus.Active: openPositions.Add(position); break; case PositionStatus.Closing: result.Add(position.Id, (PositionCloseResult.ClosingIsInProgress, null)); break; case PositionStatus.Closed: result.Add(position.Id, (PositionCloseResult.Closed, null)); break; default: throw new InvalidOperationException($"Position state {position.Status.ToString()} is not handled"); } } foreach (var group in openPositions.GroupBy(p => p.AssetPairId)) { // if asset pair is not available for trading, we will not try to close these positions if (_assetPairDayOffService.IsAssetTradingDisabled(group.Key)) { continue; } var positionGroup = group.ToArray(); // if the net volume can be liquidated, we assume that positions will be closed without special liquidation if (CheckIfNetVolumeCanBeLiquidated(group.Key, positionGroup, out _)) { positionGroup.ForEach(p => result.Add(p.Id, (PositionCloseResult.Closed, null))); } else { positionGroup.ForEach(p => result.Add(p.Id, (PositionCloseResult.ClosingStarted, null))); } } return(result); }
public async Task Handle(StartLiquidationInternalCommand command, IEventPublisher publisher) { #region Private Methods void PublishFailedEvent(string reason) { publisher.PublishEvent(new LiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = reason, LiquidationType = command.LiquidationType.ToType <LiquidationTypeContract>(), AccountId = command.AccountId, AssetPairId = command.AssetPairId, Direction = command.Direction?.ToType <PositionDirectionContract>(), }); _liquidationEndEventChannel.SendEvent(this, new LiquidationEndEventArgs { OperationId = command.OperationId, CreationTime = _dateService.Now(), AccountId = command.AccountId, LiquidatedPositionIds = new List <string>(), FailReason = reason, }); } #endregion #region Validations if (string.IsNullOrEmpty(command.AccountId)) { PublishFailedEvent("AccountId must be specified"); return; } if (_accountsCache.TryGet(command.AccountId) == null) { PublishFailedEvent("Account does not exist"); return; } #endregion var(executionInfo, _) = await _operationExecutionInfoRepository.GetOrAddAsync( operationName : LiquidationSaga.OperationName, operationId : command.OperationId, factory : () => new OperationExecutionInfo <LiquidationOperationData>( operationName: LiquidationSaga.OperationName, id: command.OperationId, lastModified: _dateService.Now(), data: new LiquidationOperationData { State = LiquidationOperationState.Initiated, AccountId = command.AccountId, AssetPairId = command.AssetPairId, QuoteInfo = command.QuoteInfo, Direction = command.Direction, LiquidatedPositionIds = new List <string>(), ProcessedPositionIds = new List <string>(), LiquidationType = command.LiquidationType, OriginatorType = command.OriginatorType, AdditionalInfo = command.AdditionalInfo, StartedAt = command.CreationTime } )); if (executionInfo.Data.State == LiquidationOperationState.Initiated) { if (!_accountsCache.TryStartLiquidation(command.AccountId, command.OperationId, out var currentOperationId)) { if (currentOperationId != command.OperationId) { PublishFailedEvent( $"Liquidation is already in progress. Initiated by operation : {currentOperationId}"); return; } } _chaosKitty.Meow( $"{nameof(StartLiquidationInternalCommand)}:" + $"Publish_LiquidationStartedInternalEvent:" + $"{command.OperationId}"); publisher.PublishEvent(new LiquidationStartedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), AssetPairId = executionInfo.Data.AssetPairId, AccountId = executionInfo.Data.AccountId, LiquidationType = executionInfo.Data.LiquidationType.ToType <LiquidationTypeContract>() }); } }