Пример #1
0
        private async Task <OrderInitialParameters> GetOrderInitialParameters(string assetPairId, string legalEntity,
                                                                              ReportingEquivalentPricesSettings equivalentSettings, string accountAssetId)
        {
            var fxAssetPairIdAndDirection = _cfdCalculatorService.GetFxAssetPairIdAndDirection(accountAssetId,
                                                                                               assetPairId, legalEntity);

            return(new OrderInitialParameters
            {
                Id = _identityGenerator.GenerateAlphanumericId(),
                Code = await _identityGenerator.GenerateIdAsync(nameof(Order)),
                Now = _dateService.Now(),
                EquivalentPrice = _cfdCalculatorService.GetQuoteRateForQuoteAsset(equivalentSettings.EquivalentAsset,
                                                                                  assetPairId, legalEntity),
                FxPrice = _cfdCalculatorService.GetQuoteRateForQuoteAsset(accountAssetId,
                                                                          assetPairId, legalEntity),
                FxAssetPairId = fxAssetPairIdAndDirection.id,
                FxToAssetPairDirection = fxAssetPairIdAndDirection.direction,
            });
        }
Пример #2
0
        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;
            }

            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(),
                    });
                }
                else
                {
                    var order = new OrderModel(
                        tradeType: command.Volume > 0 ? TradeType.Buy : TradeType.Sell,
                        orderType: OrderType.Market.ToType <Lykke.Service.ExchangeConnector.Client.Models.OrderType>(),
                        timeInForce: TimeInForce.FillOrKill,
                        volume: (double)Math.Abs(command.Volume),
                        dateTime: _dateService.Now(),
                        exchangeName: executionInfo.Data.ExternalProviderId,
                        instrument: command.Instrument,
                        price: (double?)command.Price,
                        orderId: _identityGenerator.GenerateAlphanumericId());

                    try
                    {
                        var executionResult = await _exchangeConnectorService.CreateOrderAsync(order);

                        publisher.PublishEvent(new SpecialLiquidationOrderExecutedEvent
                        {
                            OperationId   = command.OperationId,
                            CreationTime  = _dateService.Now(),
                            MarketMakerId = executionInfo.Data.ExternalProviderId,
                            ExecutionTime = executionResult.Time,
                            OrderId       = executionResult.ExchangeOrderId,
                        });
                    }
                    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);
            }
        }
Пример #3
0
        private void SendPositionHistoryEvent(Position position, PositionHistoryTypeContract historyType,
                                              decimal chargedPnl, string orderAdditionalInfo, Order dealOrder = null, decimal?dealVolume = null,
                                              PositionOpenMetadata metadata = null)
        {
            DealContract deal = null;

            if (dealOrder != null && dealVolume != null)
            {
                var sign = position.Volume > 0 ? 1 : -1;

                var accountBaseAssetAccuracy = AssetsConstants.DefaultAssetAccuracy;

                var fpl = Math.Round((dealOrder.ExecutionPrice.Value - position.OpenPrice) *
                                     dealOrder.FxRate * dealVolume.Value * sign, accountBaseAssetAccuracy);
                var balanceDelta = fpl - Math.Round(chargedPnl, accountBaseAssetAccuracy);

                var dealId = historyType == PositionHistoryTypeContract.Close
                    ? position.Id
                    : _identityGenerator.GenerateAlphanumericId();

                deal = new DealContract
                {
                    DealId                  = dealId,
                    PositionId              = position.Id,
                    Volume                  = dealVolume.Value,
                    Created                 = dealOrder.Executed.Value,
                    OpenTradeId             = position.OpenTradeId,
                    OpenOrderType           = position.OpenOrderType.ToType <OrderTypeContract>(),
                    OpenOrderVolume         = position.OpenOrderVolume,
                    OpenOrderExpectedPrice  = position.ExpectedOpenPrice,
                    CloseTradeId            = dealOrder.Id,
                    CloseOrderType          = dealOrder.OrderType.ToType <OrderTypeContract>(),
                    CloseOrderVolume        = dealOrder.Volume,
                    CloseOrderExpectedPrice = dealOrder.Price,
                    OpenPrice               = position.OpenPrice,
                    OpenFxPrice             = position.OpenFxPrice,
                    ClosePrice              = dealOrder.ExecutionPrice.Value,
                    CloseFxPrice            = dealOrder.FxRate,
                    Fpl             = fpl,
                    PnlOfTheLastDay = balanceDelta,
                    AdditionalInfo  = dealOrder.AdditionalInfo,
                    Originator      = dealOrder.Originator.ToType <OriginatorTypeContract>()
                };

                var account = _accountsCacheService.Get(position.AccountId);

                _cqrsSender.PublishEvent(new PositionClosedEvent(account.Id, account.ClientId,
                                                                 deal.DealId, position.AssetPairId, balanceDelta));

                _accountUpdateService.FreezeUnconfirmedMargin(position.AccountId, deal.DealId, balanceDelta)
                .GetAwaiter().GetResult();    //todo consider making this async or pass to broker
            }

            var positionContract = _convertService.Convert <Position, PositionContract>(position,
                                                                                        o => o.ConfigureMap(MemberList.Destination).ForMember(x => x.TotalPnL, c => c.Ignore()));

            positionContract.TotalPnL = position.GetFpl();

            var historyEvent = new PositionHistoryEvent
            {
                PositionSnapshot    = positionContract,
                Deal                = deal,
                EventType           = historyType,
                Timestamp           = _dateService.Now(),
                ActivitiesMetadata  = metadata?.ToJson(),
                OrderAdditionalInfo = orderAdditionalInfo,
            };

            _rabbitMqNotifyService.PositionHistory(historyEvent);
        }
Пример #4
0
        private async Task PlacePendingOrder(Order order)
        {
            if (order.IsBasicPendingOrder() || !string.IsNullOrEmpty(order.ParentPositionId))
            {
                Position parentPosition = null;

                if (!string.IsNullOrEmpty(order.ParentPositionId))
                {
                    parentPosition = _ordersCache.Positions.GetPositionById(order.ParentPositionId);
                    parentPosition.AddRelatedOrder(order);
                }

                order.Activate(_dateService.Now(), false, parentPosition?.ClosePrice);
                _ordersCache.Active.Add(order);
                _orderActivatedEventChannel.SendEvent(this, new OrderActivatedEventArgs(order));
            }
            else if (!string.IsNullOrEmpty(order.ParentOrderId))
            {
                if (_ordersCache.TryGetOrderById(order.ParentOrderId, out var parentOrder))
                {
                    parentOrder.AddRelatedOrder(order);
                    order.MakeInactive(_dateService.Now());
                    _ordersCache.Inactive.Add(order);
                    return;
                }

                //may be it was market and now it is position
                if (_ordersCache.Positions.TryGetPositionById(order.ParentOrderId, out var parentPosition))
                {
                    parentPosition.AddRelatedOrder(order);
                    if (parentPosition.Volume != -order.Volume)
                    {
                        order.ChangeVolume(-parentPosition.Volume, _dateService.Now(), OriginatorType.System);
                    }

                    order.Activate(_dateService.Now(), true, parentPosition.ClosePrice);
                    _ordersCache.Active.Add(order);
                    _orderActivatedEventChannel.SendEvent(this, new OrderActivatedEventArgs(order));
                }
                else
                {
                    order.MakeInactive(_dateService.Now());
                    _ordersCache.Inactive.Add(order);
                    CancelPendingOrder(order.Id, order.AdditionalInfo,
                                       _identityGenerator.GenerateAlphanumericId(),
                                       $"Parent order closed the position, so {order.OrderType.ToString()} order is cancelled");
                }
            }
            else
            {
                throw new ValidateOrderException(OrderRejectReason.InvalidParent, "Order parent is not valid");
            }

            if (order.Status == OrderStatus.Active &&
                _quoteCacheService.TryGetQuoteById(order.AssetPairId, out var pair))
            {
                var price = pair.GetPriceForOrderDirection(order.Direction);

                if (!_assetPairDayOffService.IsDayOff(order.AssetPairId) && //!_assetPairDayOffService.ArePendingOrdersDisabled(order.AssetPairId))
                    order.IsSuitablePriceForPendingOrder(price))
                {
                    _ordersCache.Active.Remove(order);
                    await PlaceOrderByMarketPrice(order);
                }
            }
        }
        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);
            }
        }