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