public Task <bool> IsInstrumentEnabled(string assetPairId) { _assetPairsCache.GetAssetPairById(assetPairId); var isEnabled = !_assetPairDayOffService.IsAssetTradingDisabled(assetPairId); return(Task.FromResult(isEnabled)); }
private (string AssetPairId, string[] Positions)? GetLiquidationData( LiquidationOperationData data) { var positionsOnAccount = _ordersCache.Positions.GetPositionsByAccountIds(data.AccountId); if (data.LiquidationType == LiquidationType.Forced) { positionsOnAccount = positionsOnAccount.Where(p => p.OpenDate < data.StartedAt).ToList(); //group positions by asset and pnl sign, take only not processed, filtered and with open market //groups are ordered by positive pnl first var targetPositionsByPnlSign = positionsOnAccount .Where(p => !data.ProcessedPositionIds.Contains(p.Id)) .GroupBy(p => (p.AssetPairId, p.GetUnrealisedFpl() >= 0)) .Where(gr => !_assetPairDayOffService.IsAssetTradingDisabled(gr.Key.AssetPairId)) .OrderByDescending(gr => gr.Key.Item2) .FirstOrDefault(); if (targetPositionsByPnlSign == null) { return(null); } return(targetPositionsByPnlSign.Key.AssetPairId, targetPositionsByPnlSign.Select(p => p.Id).ToArray()); } //group positions and take only not processed, filtered and with open market var positionGroups = positionsOnAccount .Where(p => !data.ProcessedPositionIds.Contains(p.Id) && (string.IsNullOrEmpty(data.AssetPairId) || p.AssetPairId == data.AssetPairId) && (data.Direction == null || p.Direction == data.Direction)) .GroupBy(p => p.AssetPairId) .Where(gr => !_assetPairDayOffService.IsAssetTradingDisabled(gr.Key)) .ToArray(); IGrouping <string, Position> targetPositions = null; //take positions from group with max margin used or max initially used margin targetPositions = positionGroups .OrderByDescending(gr => gr.Sum(p => Math.Max(p.GetMarginMaintenance(), p.GetInitialMargin()))) .FirstOrDefault(); if (targetPositions == null) { return(null); } return(targetPositions.Key, targetPositions.Select(p => p.Id).ToArray()); }
public void ProcessOrderCommands(MarketMakerOrderCommandsBatchMessage batch) { batch.AssetPairId.RequiredNotNullOrWhiteSpace(nameof(batch.AssetPairId)); batch.Commands.RequiredNotNull(nameof(batch.Commands)); if (_maintenanceModeService.CheckIsEnabled()) { return; } if (_assetPairDayOffService.IsAssetTradingDisabled(batch.AssetPairId)) { return; } var model = new SetOrderModel { MarketMakerId = batch.MarketMakerId }; ConvertCommandsToOrders(batch, model); if (model.OrdersToAdd?.Count > 0 || model.DeleteByInstrumentsBuy?.Count > 0 || model.DeleteByInstrumentsSell?.Count > 0) { _matchingEngine.SetOrders(model); } }
private void ValidateDayOff(params string[] assetPairIds) { foreach (var instrument in assetPairIds) { var instrumentTradingStatus = _assetDayOffService.IsAssetTradingDisabled(instrument); if (!instrumentTradingStatus.TradingEnabled) { if (instrumentTradingStatus.Reason == InstrumentTradingDisabledReason.InstrumentTradingDisabled) { throw new InvalidOperationException($"Trades for {instrument} are disabled. Error code: {CommonErrorCodes.InstrumentTradingDisabled}"); } throw new InvalidOperationException($"Trades for {instrument} are not available"); } } }
public void SetOrderbook(ExternalOrderBook orderbook) { var isEodOrderbook = orderbook.ExchangeName == ExternalOrderbookService.EodExternalExchange; var instrumentTradingStatus = _assetPairDayOffService.IsAssetTradingDisabled(orderbook.AssetPairId); if (!isEodOrderbook && !instrumentTradingStatus.TradingEnabled && instrumentTradingStatus.Reason == InstrumentTradingDisabledReason.InstrumentTradingDisabled) { return; } if (_orderbookValidation.ValidateInstrumentStatusForEodQuotes && isEodOrderbook || _orderbookValidation.ValidateInstrumentStatusForTradingQuotes && !isEodOrderbook) { // we should process normal orderbook only if instrument is currently tradable if (_orderbookValidation.ValidateInstrumentStatusForTradingQuotes && instrumentTradingStatus && !isEodOrderbook) { return; } // and process EOD orderbook only if instrument is currently not tradable if (_orderbookValidation.ValidateInstrumentStatusForEodQuotes && !instrumentTradingStatus && isEodOrderbook) { //log current schedule for the instrument var schedule = _scheduleSettingsCache.GetMarketTradingScheduleByAssetPair(orderbook.AssetPairId); _log.WriteWarning("EOD quotes processing", $"Current schedule for the instrument's market: {schedule.ToJson()}", $"EOD quote for {orderbook.AssetPairId} is skipped, because instrument is within trading hours"); return; } } if (!CheckZeroQuote(orderbook, isEodOrderbook)) { return; } orderbook.ApplyExchangeIdFromSettings(_defaultExternalExchangeId); var bba = orderbook.GetBestPrice(); _orderbooks.AddOrUpdate(orderbook.AssetPairId, a => orderbook, (s, book) => orderbook); _bestPriceChangeEventChannel.SendEvent(this, new BestPriceChangeEventArgs(bba, isEodOrderbook)); }
public Task SetQuote(ExternalExchangeOrderbookMessage orderBookMessage) { var isEodOrderbook = orderBookMessage.ExchangeName == ExternalOrderbookService.EodExternalExchange; if (_marginTradingSettings.OrderbookValidation.ValidateInstrumentStatusForEodFx && isEodOrderbook || _marginTradingSettings.OrderbookValidation.ValidateInstrumentStatusForTradingFx && !isEodOrderbook) { var isAssetTradingDisabled = _assetPairDayOffService.IsAssetTradingDisabled(orderBookMessage.AssetPairId); // we should process normal orderbook only if asset is currently tradable if (_marginTradingSettings.OrderbookValidation.ValidateInstrumentStatusForTradingFx && isAssetTradingDisabled && !isEodOrderbook) { return(Task.CompletedTask); } // and process EOD orderbook only if asset is currently not tradable if (_marginTradingSettings.OrderbookValidation.ValidateInstrumentStatusForEodFx && !isAssetTradingDisabled && isEodOrderbook) { _log.WriteWarning("EOD FX quotes processing", "", $"EOD FX quote for {orderBookMessage.AssetPairId} is skipped, because instrument is within trading hours"); return(Task.CompletedTask); } } var bidAskPair = CreatePair(orderBookMessage); if (bidAskPair == null) { return(Task.CompletedTask); } SetQuote(bidAskPair); _fxBestPriceChangeEventChannel.SendEvent(this, new FxBestPriceChangeEventArgs(bidAskPair)); return(Task.CompletedTask); }
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 IAssetPair GetAssetPairIfAvailableForTrading(string assetPairId, OrderType orderType, bool shouldOpenNewPosition, bool isPreTradeValidation, bool validateForEdit = false) { if (isPreTradeValidation || orderType == OrderType.Market) { var tradingStatus = _assetDayOffService.IsAssetTradingDisabled(assetPairId); if (tradingStatus) { if (tradingStatus.Reason == InstrumentTradingDisabledReason.InstrumentTradingDisabled) { throw new ValidateOrderException(OrderRejectReason.InvalidInstrument, $"Trading for the instrument {assetPairId} is disabled. Error code: {CommonErrorCodes.InstrumentTradingDisabled}"); } throw new ValidateOrderException(OrderRejectReason.InvalidInstrument, $"Trades for instrument {assetPairId} are not available due to trading is closed"); } } else if (_assetDayOffService.ArePendingOrdersDisabled(assetPairId)) { throw new ValidateOrderException(OrderRejectReason.InvalidInstrument, $"Pending orders for instrument {assetPairId} are not available"); } var assetPair = _assetPairsCache.GetAssetPairByIdOrDefault(assetPairId); if (assetPair == null) { throw new ValidateOrderException(OrderRejectReason.InvalidInstrument, $"Instrument {assetPairId} not found"); } if (assetPair.IsDiscontinued) { throw new ValidateOrderException(OrderRejectReason.InvalidInstrument, $"Trading for the instrument {assetPairId} is discontinued"); } if (assetPair.IsTradingDisabled && !validateForEdit) { throw new ValidateOrderException(OrderRejectReason.InvalidInstrument, $"Trading for the instrument {assetPairId} is disabled. Error code: {CommonErrorCodes.InstrumentTradingDisabled}"); } if (assetPair.IsSuspended && shouldOpenNewPosition) { if (isPreTradeValidation) { throw new ValidateOrderException(OrderRejectReason.InvalidInstrument, $"Orders execution for instrument {assetPairId} is temporarily unavailable (instrument is suspended)"); } if (orderType == OrderType.Market) { throw new ValidateOrderException(OrderRejectReason.InvalidInstrument, $"Market orders for instrument {assetPairId} are temporarily unavailable (instrument is suspended)"); } } if (assetPair.IsFrozen && shouldOpenNewPosition) { throw new ValidateOrderException(OrderRejectReason.InvalidInstrument, $"Opening new positions for instrument {assetPairId} is temporarily unavailable (instrument is frozen)"); } return(assetPair); }
private async Task Handle(StartSpecialLiquidationCommand command, IEventPublisher publisher) { if (!_marginTradingSettings.SpecialLiquidation.Enabled) { publisher.PublishEvent(new SpecialLiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = "Special liquidation is disabled in settings", }); return; } //Validate that market is closed for instrument .. only in ExchangeConnector == Real mode if (_marginTradingSettings.ExchangeConnector == ExchangeConnectorType.RealExchangeConnector && !_assetPairDayOffService.IsAssetTradingDisabled(command.Instrument)) { publisher.PublishEvent(new SpecialLiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = $"Asset pair {command.Instrument} market must be disabled to start Special Liquidation", }); return; } var openedPositions = _ordersCache.GetPositions().Where(x => x.AssetPairId == command.Instrument).ToList(); if (!openedPositions.Any()) { publisher.PublishEvent(new SpecialLiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = "No positions to liquidate", }); return; } if (_assetPairsCache.GetAssetPairById(command.Instrument).IsDiscontinued) { await _log.WriteWarningAsync( nameof(StartSpecialLiquidationCommand), nameof(SpecialLiquidationCommandsHandler), $"Position(s) {string.Join(", ", openedPositions.Select(x => x.Id))} should be closed for discontinued instrument {command.Instrument}."); } if (!TryGetExchangeNameFromPositions(openedPositions, out var externalProviderId)) { publisher.PublishEvent(new SpecialLiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = "All requested positions must be open on the same external exchange", }); return; } var(executionInfo, _) = await _operationExecutionInfoRepository.GetOrAddAsync( operationName : SpecialLiquidationSaga.OperationName, operationId : command.OperationId, factory : () => new OperationExecutionInfo <SpecialLiquidationOperationData>( operationName: SpecialLiquidationSaga.OperationName, id: command.OperationId, lastModified: _dateService.Now(), data: new SpecialLiquidationOperationData { State = SpecialLiquidationOperationState.Initiated, Instrument = command.Instrument, PositionIds = openedPositions.Select(x => x.Id).ToList(), ExternalProviderId = externalProviderId, OriginatorType = OriginatorType.System, AdditionalInfo = LykkeConstants.LiquidationByCaAdditionalInfo, RequestedFromCorporateActions = command.IsTriggeredByCa, } )); if (executionInfo.Data.SwitchState(SpecialLiquidationOperationState.Initiated, SpecialLiquidationOperationState.Started)) { publisher.PublishEvent(new SpecialLiquidationStartedInternalEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Instrument = command.Instrument, }); _chaosKitty.Meow(command.OperationId); await _operationExecutionInfoRepository.Save(executionInfo); } }