From 5a829620bf02a44202074084864fb510f48d4280 Mon Sep 17 00:00:00 2001 From: ialbert Date: Wed, 10 Sep 2025 15:38:57 +0300 Subject: [PATCH] http://jira.mfd.msk:8088/browse/CLS-889 --- .../UtilitarianDigitalRightPredicate.java | 24 +++++++ .../spcex/clearing/error/ClearingError.java | 1 + .../execution/ExecutionFondComponent.java | 43 ++++++++++-- .../service/validation/ValidationStored.java | 2 +- .../STradesSecurityDGCTValidationRule.java | 70 +++++++++++++++++++ .../platform/utils/number/BigDecimalUtil.java | 2 +- 6 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/component/predicate/cash/UtilitarianDigitalRightPredicate.java create mode 100644 clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/strades/STradesSecurityDGCTValidationRule.java diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/component/predicate/cash/UtilitarianDigitalRightPredicate.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/component/predicate/cash/UtilitarianDigitalRightPredicate.java new file mode 100644 index 000000000..0fb77a031 --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/component/predicate/cash/UtilitarianDigitalRightPredicate.java @@ -0,0 +1,24 @@ +package ru.spcex.clearing.component.predicate.cash; + +import ru.clearing.classes.statics.data.security.UtilitarianDigitalRight; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; +import ru.spcex.platform.imdg.iml.hazelcast.adapter.CashV2; + +public class UtilitarianDigitalRightPredicate { + + public static UtilitarianDigitalRightPredicate getPredicate(Long secId) { + return new UtilitarianDigitalRightPredicate(secId); + } + + private final Long secId; + + private UtilitarianDigitalRightPredicate(Long secId) { + this.secId = secId; + } + + public ImdgPredicate cashed(ImdgPredicateBuilder pb, CashV2 cash) { + ImdgPredicate prdct = pb.equals("securityId", secId); + return pb.cashed(prdct, cash, secId); + } +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/error/ClearingError.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/error/ClearingError.java index 84f5bfc38..d0ce0b9c6 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/error/ClearingError.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/error/ClearingError.java @@ -45,6 +45,7 @@ public enum ClearingError implements IErrorEnumId { MarketIsNotMkr(5217L), BalanceInsufficient(5222L), Sdf57UnexpectedBalance(5435L), + UtilitarianDigitalRightNotFound(5436L), TCRegistryNotFound(3022L), WrongField(5004L), GatewayTimeout(6003L), diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/execution/ExecutionFondComponent.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/execution/ExecutionFondComponent.java index 710bb425a..35af415c5 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/execution/ExecutionFondComponent.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/execution/ExecutionFondComponent.java @@ -1,6 +1,5 @@ package ru.spcex.clearing.service.execution; -import static java.lang.String.format; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.Instant; @@ -10,6 +9,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Optional; +import static java.lang.String.format; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.event.Level; @@ -33,6 +33,7 @@ import ru.clearing.classes.statics.data.security.CurrencyPairSecurity; import ru.clearing.classes.statics.data.security.DigitalCertificateSecurity; import ru.clearing.classes.statics.data.security.MoneyMarketSecurity; import ru.clearing.classes.statics.data.security.Security; +import ru.clearing.classes.statics.data.security.UtilitarianDigitalRight; import ru.spcex.clearing.config.element.ClearingServiceSettings; import ru.spcex.clearing.error.ClearingError; import ru.spcex.clearing.error.ClearingException; @@ -44,10 +45,12 @@ import ru.spcex.clearing.platform.messaging.domain.cud.registry.ExecutionType; import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender; import ru.spcex.clearing.service.validation.ValidationStored; import ru.spcex.clearing.service.validation.strades.CompanyByTradingCodeCashingValidationRule; +import ru.spcex.clearing.service.validation.strades.STradesSecurityDGCTValidationRule; import ru.spcex.clearing.service.validation.strades.STradesSecurityPresentValidationRule; import ru.spcex.clearing.service.validation.strades.TcrByCodeAndCmpIsActiveCashingValidationRule; import ru.spcex.platform.enumeration.CurrencyCode; import ru.spcex.platform.enumeration.InstrumentType; +import ru.spcex.platform.enumeration.NominalTypeCode; import ru.spcex.platform.enumeration.ObjectType; import ru.spcex.platform.enumeration.Priority; import ru.spcex.platform.enumeration.Section; @@ -58,6 +61,7 @@ import ru.spcex.platform.imdg.api.ImdgProvider; import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; import ru.spcex.platform.imdg.iml.hazelcast.adapter.CashCloser; +import ru.spcex.platform.imdg.iml.hazelcast.adapter.CashV2; import ru.spcex.platform.imdg.iml.hazelcast.adapter.CashV2ByIdAndString; import ru.spcex.platform.imdg.iml.hazelcast.adapter.CashV2ByString; import ru.spcex.platform.imdg.validation.ImdgValidationContext; @@ -67,6 +71,7 @@ import ru.spcex.platform.utils.enumeration.IEnumId; import ru.spcex.platform.utils.enumeration.IEnumKey; import ru.spcex.platform.utils.enumeration.IMessageResolver; import ru.spcex.platform.utils.log.ExceptionUtils; +import static ru.spcex.platform.utils.number.BigDecimalUtil.parse; import static ru.spcex.platform.utils.number.BigDecimalUtil.safeBD; import ru.spcex.platform.utils.text.TextUtil; import ru.spcex.platform.utils.time.TimeUtil; @@ -92,6 +97,7 @@ public class ExecutionFondComponent implements IExecutionUploadComponent { private final Imdg imdgCompany; private final Imdg imdgTradingClearingRegistry; private final Imdg imdgDigitCertSecurity; + private final Imdg udrImdg; private final ImdgId idGen; //fixme ждать ТЗ @@ -126,6 +132,7 @@ public class ExecutionFondComponent implements IExecutionUploadComponent { this.equitySecurityImdg = imdgProvider.getCashingImdg(IMDGDistributedNames.Map_EquitySecurity, EquitySecurity.class, null); this.imdgCompany = imdgProvider.getCashingImdg(IMDGDistributedNames.Map_Company, Company.class, null); this.imdgTradingClearingRegistry = imdgProvider.getCashingImdg(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class, null); + this.udrImdg = imdgProvider.getCashingImdg(IMDGDistributedNames.Map_UtilitarianDigitalRight, UtilitarianDigitalRight.class, null); this.idGen = imdgProvider.getImdgIdGenerator(); this.valuation = settings.getTrade().getValuation(); //fixme this.kafkaSender = kafkaSender; @@ -278,6 +285,7 @@ public class ExecutionFondComponent implements IExecutionUploadComponent { Company company = validator.getStored(ValidationStored.STradesCompany); Company counterCompany = validator.getStored(ValidationStored.STradesCounterCompany); TradingClearingRegistry rgstr = validator.getStored(ValidationStored.STradesTradingClearingRegistry); + UtilitarianDigitalRight udr = validator.getStored(ValidationStored.STradesUtilitarianDigitalRight); ClientCode clientCode = clientCodeImdg.getFirstObjectByFieldValues(Map.of("code", sTrades.getClientCode())); ExecutionFond eFond = new ExecutionFond(); @@ -293,7 +301,21 @@ public class ExecutionFondComponent implements IExecutionUploadComponent { Market market = marketImdg.getFirstObjectBySQL("code = '%s'".formatted(sTrades.getClassCode())); eFond.setMarket(sTrades.getClassCode()); - eFond.setPrice(sTrades.getPrice()); + BigDecimal price = sTrades.getPrice(); + if (udr != null) { + DigitalCertificateSecurity dcs = (DigitalCertificateSecurity) security; + BigDecimal baseUnitSize = BigDecimal.valueOf(Optional.ofNullable(dcs.getBaseUnitSize()).orElse(0L)); + if (NominalTypeCode.INDX.equalsByKey(dcs.getNominalTypeCode())) { + price = safeBD(sTrades.getPrice()) + .multiply(safeBD(dcs.getNominalValue())) + .multiply(baseUnitSize); + } else if (NominalTypeCode.INDX.equalsByKey(dcs.getNominalTypeCode())) { + price = safeBD(sTrades.getPrice()) + .multiply(safeBD(parse(udr.getRightUnit()))) + .multiply(baseUnitSize); + } + } + eFond.setPrice(price); eFond.setLots(sTrades.getQty()); eFond.setQuantity(sTrades.getQtyPcs()); { @@ -327,9 +349,10 @@ public class ExecutionFondComponent implements IExecutionUploadComponent { BigDecimal lotSize = listing != null ? safeBD(listing.getLotSize()) : BigDecimal.ONE; BigDecimal qty = safeBD(eFond.getLots()).multiply(lotSize); eFond.setQuantity(qty); - BigDecimal price = safeBD(sTrades.getPrice());// eFond.setPrice BigDecimal settlementAmount = BigDecimal.ZERO; - if (InstrumentType.BOND.equalsByKey(security.getInstrumentType())) { + if (udr != null) { + settlementAmount = qty.multiply(eFond.getPrice()); + } else if (InstrumentType.BOND.equalsByKey(security.getInstrumentType())) { FixedIncomeSecurity fixedIncome = (FixedIncomeSecurity) security; BigDecimal nominalValue; ImdgPredicateBuilder pb = fixedIncomeCashFlowImdg.predicateBuilder(); @@ -450,6 +473,8 @@ public class ExecutionFondComponent implements IExecutionUploadComponent { context.addImdg(IMDGDistributedNames.Map_Company, imdgCompany); context.addImdg(IMDGDistributedNames.Map_TradingClearingRegistry, imdgTradingClearingRegistry); context.addImdg(IMDGDistributedNames.Map_DigitalCertificateSecurity, imdgDigitCertSecurity); + context.addImdg(IMDGDistributedNames.Map_UtilitarianDigitalRight, udrImdg); + //fixme add map utilitarian context.setLogPrefix(LogPrefixId.INSTANCE); return new ValidatorImpl<>(context, new STradesSecurityPresentValidationRule(valCash.fixIncSecCash, @@ -463,7 +488,8 @@ public class ExecutionFondComponent implements IExecutionUploadComponent { new CompanyByTradingCodeCashingValidationRule<>( STrades::getCpFirmId, valCash.cmpCash, ValidationStored.STradesCounterCompany ), - new TcrByCodeAndCmpIsActiveCashingValidationRule(valCash.tcrCash) + new TcrByCodeAndCmpIsActiveCashingValidationRule(valCash.tcrCash), + new STradesSecurityDGCTValidationRule(valCash.udrCash) ); } @@ -474,6 +500,7 @@ public class ExecutionFondComponent implements IExecutionUploadComponent { private final CashV2ByString currPairSecCash; private final CashV2ByString digitCertSecCash; private final CashV2ByString cmpCash; + private final CashV2 udrCash; private final CashV2ByIdAndString tcrCash; public ExecFondCompCash() { @@ -485,6 +512,12 @@ public class ExecutionFondComponent implements IExecutionUploadComponent { this.cmpCash = add(new CashV2ByString<>("cmpCash", Company::getTradingCode)); this.tcrCash = add(new CashV2ByIdAndString<>("tcrCash", tcr -> new CashV2ByIdAndString.CustomKey(tcr.getCompanyId(), tcr.getCode()))); this.digitCertSecCash = add(new CashV2ByString<>("digitCertSecCash", Security::getSecuritySymbol)); + this.udrCash = add(new CashV2("udrCash") { + @Override + protected Long extractKey(UtilitarianDigitalRight obj) { + return obj.getSecurityId(); + } + }); } } } diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ValidationStored.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ValidationStored.java index 4def823d1..7ba69e2c6 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ValidationStored.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ValidationStored.java @@ -2,7 +2,7 @@ package ru.spcex.clearing.service.validation; public enum ValidationStored { STradesCompany, STradesCounterCompany, STradesSecurity, STradesTradingClearingRegistry, - Account, Company, + Account, Company, STradesUtilitarianDigitalRight, Sdf57CompanyDeb, Sdf57CompanyCred, Sdf57AccountDeb, Sdf57AccountCred, diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/strades/STradesSecurityDGCTValidationRule.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/strades/STradesSecurityDGCTValidationRule.java new file mode 100644 index 000000000..c84d5d555 --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/strades/STradesSecurityDGCTValidationRule.java @@ -0,0 +1,70 @@ +package ru.spcex.clearing.service.validation.strades; + +import java.util.Objects; +import java.util.Optional; +import ru.clearing.classes.statics.data.misc.STrades; +import ru.clearing.classes.statics.data.security.DigitalCertificateSecurity; +import ru.clearing.classes.statics.data.security.Security; +import ru.clearing.classes.statics.data.security.UtilitarianDigitalRight; +import ru.spcex.clearing.component.predicate.cash.UtilitarianDigitalRightPredicate; +import ru.spcex.clearing.error.ClearingError; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.service.validation.ValidationStored; +import ru.spcex.platform.enumeration.InstrumentType; +import ru.spcex.platform.enumeration.NominalTypeCode; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; +import ru.spcex.platform.imdg.iml.hazelcast.adapter.CashV2; +import ru.spcex.platform.imdg.validation.ImdgValidationContext; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.text.TextUtil; +import ru.spcex.platform.utils.validation.IValidationRule; + +public class STradesSecurityDGCTValidationRule implements IValidationRule> { + private final CashV2 udrCash; + + public STradesSecurityDGCTValidationRule(CashV2 udrCash) { + this.udrCash = udrCash; + } + + @Override + public String ruleName() { + return "STradesSecurityDGCTValidationRule"; + } + + @Override + public Optional validate(ImdgValidationContext ctx) { + STrades validatedObject = ctx.getValidatedObject(); + Security security = ctx.getStoredObject(ValidationStored.STradesSecurity); + if (security == null || !InstrumentType.DGCT.equalsByKey(security.getInstrumentType())) { + return empty(); + } + DigitalCertificateSecurity dcs = (DigitalCertificateSecurity) security; + Imdg udrImdg = ctx.obtainMap( + IMDGDistributedNames.Map_UtilitarianDigitalRight, UtilitarianDigitalRight.class + ); + ImdgPredicateBuilder pb = udrImdg.predicateBuilder(); + ImdgPredicate cashingPredicate = UtilitarianDigitalRightPredicate + .getPredicate(security.getId()) + .cashed(pb, udrCash); + UtilitarianDigitalRight udr = udrImdg.getFirstObjectByPredicate(cashingPredicate); + if (udr == null) { + return of(ClearingError.UtilitarianDigitalRightNotFound, security.getSecuritySymbol()); + } + if (NominalTypeCode.INDX.equalsByKey(dcs.getNominalTypeCode())) { + if (!Objects.equals(dcs.getNominalIndexationDate(), validatedObject.getTradeDate())) { + return of(ClearingError.UtilitarianDigitalRightNotFound, security.getSecuritySymbol()); + } + if (dcs.getNominalValue() == null) { + return of(ClearingError.UtilitarianDigitalRightNotFound, security.getSecuritySymbol()); + } + } else if (NominalTypeCode.FIXD.equalsByKey(dcs.getNominalTypeCode())) { + if (TextUtil.isEmpty(udr.getRightUnit())) { + return of(ClearingError.UtilitarianDigitalRightNotFound, security.getSecuritySymbol()); + } + } + ctx.storeObject(ValidationStored.STradesUtilitarianDigitalRight, udr); + return empty(); + } +} diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/number/BigDecimalUtil.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/number/BigDecimalUtil.java index 6e06c8800..ed4e446d6 100644 --- a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/number/BigDecimalUtil.java +++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/number/BigDecimalUtil.java @@ -8,7 +8,7 @@ public class BigDecimalUtil { // private final Pattern numberPattern = Pattern.compile("\\d+([.,]\\d+)?"); public static BigDecimal parse(String number) { - if (number == null) { + if (TextUtil.isEmpty(number)) { return null; } number = number.trim().replace(",", ".");