/// <summary>
        /// Выполнить операцию BUY/SELL
        /// </summary>
        /// <param name="tradePosition"></param>
        /// <returns></returns>
        public override async Task PerformBuyOrSell(TradePosition tradePosition)
        {
            var options = tradePosition.Options as EasyTradeOptions;

            tradePosition.IsStateChanged = false;
            MarketOrder marketOrder = null;

            if (tradePosition.PrevOperationType == OperationType.Buy)
            {
                //текущая цена превысила ProfitThreshold (цена подскочила, продадим)
                //или упала на StopLossThreshold (продаем пока ещё больше не упало)
                if (options != null && (tradePosition.LastPrice >= options.ProfitThreshold ||
                                        tradePosition.LastPrice <= options.StopLossThreshold))
                {
                    marketOrder = new MarketOrder(tradePosition.PortfolioPosition.Figi,
                                                  tradePosition.PortfolioPosition.Lots,
                                                  OperationType.Sell,
                                                  AccountId);

                    tradePosition.IsStateChanged = true;
                }
            }
            else if (tradePosition.PrevOperationType == OperationType.Sell)
            {
                //хорошая цена , надо покупать (докупать)
                if (options != null && tradePosition.LastPrice <= options.DipThreshold
                    /*|| tradePosition.LastPrice >= options.UpwardTrendThreshold*/)
                {
                    //TODO: здесь надо вычислять сколько лотов можем купить

                    marketOrder = new MarketOrder(tradePosition.PortfolioPosition.Figi,
                                                  1,
                                                  OperationType.Buy,
                                                  AccountId);

                    tradePosition.IsStateChanged = true;
                }
            }

            if (tradePosition.IsStateChanged && marketOrder != null)
            {
                var placedMarketOrder = await Context.PlaceMarketOrderAsync(marketOrder);

                TradeOperationInfo tradeOperationInfo = new TradeOperationInfo();
                tradeOperationInfo.MarketOrder       = marketOrder;
                tradeOperationInfo.PlacedMarkedOrder = placedMarketOrder;

                ActionOperation?.BeginInvoke(tradeOperationInfo, null, null);
            }
        }
        private async Task Handle(ExecuteSpecialLiquidationOrderCommand command, IEventPublisher publisher)
        {
            var executionInfo = await _operationExecutionInfoRepository.GetAsync <SpecialLiquidationOperationData>(
                operationName : SpecialLiquidationSaga.OperationName,
                id : command.OperationId);

            if (executionInfo?.Data == null)
            {
                return;
            }

            await _log.WriteInfoAsync(nameof(SpecialLiquidationCommandsHandler),
                                      nameof(ExecuteSpecialLiquidationOrderCommand),
                                      command.ToJson(),
                                      "Checking if position special liquidation should be failed");

            if (!string.IsNullOrEmpty(executionInfo.Data.CausationOperationId))
            {
                await _log.WriteInfoAsync(nameof(SpecialLiquidationCommandsHandler),
                                          nameof(ExecuteSpecialLiquidationOrderCommand),
                                          command.ToJson(),
                                          "Special liquidation is caused by regular liquidation, checking liquidation type.");

                var liquidationInfo = await _operationExecutionInfoRepository.GetAsync <LiquidationOperationData>(
                    operationName : LiquidationSaga.OperationName,
                    id : executionInfo.Data.CausationOperationId);

                if (liquidationInfo == null)
                {
                    await _log.WriteInfoAsync(nameof(SpecialLiquidationCommandsHandler),
                                              nameof(ExecuteSpecialLiquidationOrderCommand),
                                              command.ToJson(),
                                              "Regular liquidation does not exist, position close will not be failed.");
                }
                else
                {
                    if (liquidationInfo.Data.LiquidationType == LiquidationType.Forced)
                    {
                        await _log.WriteInfoAsync(nameof(SpecialLiquidationCommandsHandler),
                                                  nameof(ExecuteSpecialLiquidationOrderCommand),
                                                  command.ToJson(),
                                                  "Regular liquidation type is Forced (Close All), position close will not be failed.");
                    }
                    else
                    {
                        var account = _accountsCacheService.Get(executionInfo.Data.AccountId);
                        var level   = account.GetAccountLevel();

                        await _log.WriteInfoAsync(nameof(SpecialLiquidationCommandsHandler),
                                                  nameof(ExecuteSpecialLiquidationOrderCommand),
                                                  command.ToJson(),
                                                  $"Current account state: {account.ToJson()}, level: {level}");

                        if (level != ValidAccountLevel)
                        {
                            await _log.WriteWarningAsync(
                                nameof(SpecialLiquidationCommandsHandler),
                                nameof(ExecuteSpecialLiquidationOrderCommand),
                                new { accountId = account.Id, accountLevel = level.ToString() }.ToJson(),
                                $"Unable to execute special liquidation since account level is not {ValidAccountLevel.ToString()}.");

                            publisher.PublishEvent(new SpecialLiquidationCancelledEvent()
                            {
                                OperationId  = command.OperationId,
                                CreationTime = _dateService.Now(),
                                Reason       = $"Account level is not {ValidAccountLevel.ToString()}.",
                            });

                            return;
                        }
                    }
                }
            }

            if (executionInfo.Data.SwitchState(SpecialLiquidationOperationState.PriceReceived,
                                               SpecialLiquidationOperationState.ExternalOrderExecuted))
            {
                if (command.Volume == 0)
                {
                    publisher.PublishEvent(new SpecialLiquidationOrderExecutedEvent
                    {
                        OperationId    = command.OperationId,
                        CreationTime   = _dateService.Now(),
                        MarketMakerId  = "ZeroNetVolume",
                        ExecutionTime  = _dateService.Now(),
                        OrderId        = _identityGenerator.GenerateGuid(),
                        ExecutionPrice = command.Price
                    });
                }
                else
                {
                    var operationInfo = new TradeOperationInfo
                    {
                        OperationId   = executionInfo.Id,
                        RequestNumber = executionInfo.Data.RequestNumber
                    };

                    var order = new OrderModel(
                        tradeType: command.Volume > 0 ? TradeType.Buy : TradeType.Sell,
                        orderType: OrderType.Market.ToType <Contracts.ExchangeConnector.OrderType>(),
                        timeInForce: TimeInForce.FillOrKill,
                        volume: (double)Math.Abs(command.Volume),
                        dateTime: _dateService.Now(),
                        exchangeName: operationInfo.ToJson(), //hack, but ExchangeName is not used and we need this info
                                                              // TODO: create a separate field and remove hack (?)
                        instrument: command.Instrument,
                        price: (double?)command.Price,
                        orderId: _identityGenerator.GenerateAlphanumericId(),
                        modality: executionInfo.Data.RequestedFromCorporateActions ? TradeRequestModality.Liquidation_CorporateAction : TradeRequestModality.Liquidation_MarginCall);

                    try
                    {
                        var executionResult = await _exchangeConnectorClient.ExecuteOrder(order);

                        if (!executionResult.Success)
                        {
                            throw new Exception(
                                      $"External order was not executed. Status: {executionResult.ExecutionStatus}. " +
                                      $"Failure: {executionResult.FailureType}");
                        }

                        var executionPrice = (decimal)executionResult.Price == default
                            ? command.Price
                            : (decimal)executionResult.Price;

                        if (executionPrice.EqualsZero())
                        {
                            throw new Exception("Execution price is equal to 0.");
                        }

                        publisher.PublishEvent(new SpecialLiquidationOrderExecutedEvent
                        {
                            OperationId    = command.OperationId,
                            CreationTime   = _dateService.Now(),
                            MarketMakerId  = executionInfo.Data.ExternalProviderId,
                            ExecutionTime  = executionResult.Time,
                            OrderId        = executionResult.ExchangeOrderId,
                            ExecutionPrice = executionPrice
                        });
                    }
                    catch (Exception exception)
                    {
                        publisher.PublishEvent(new SpecialLiquidationOrderExecutionFailedEvent
                        {
                            OperationId  = command.OperationId,
                            CreationTime = _dateService.Now(),
                            Reason       = exception.Message
                        });
                        await _log.WriteWarningAsync(nameof(SpecialLiquidationCommandsHandler),
                                                     nameof(ExecuteSpecialLiquidationOrderCommand),
                                                     $"Failed to execute the order: {order.ToJson()}",
                                                     exception);
                    }
                }

                await _operationExecutionInfoRepository.Save(executionInfo);
            }
        }