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, }); }
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); } }
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); }
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); } }