ialbert 2025-09-10 15:38:57 +03:00
parent b0b4f294ec
commit 5a829620bf
6 changed files with 135 additions and 7 deletions

View file

@ -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<Long, UtilitarianDigitalRight> cash) {
ImdgPredicate prdct = pb.equals("securityId", secId);
return pb.cashed(prdct, cash, secId);
}
}

View file

@ -45,6 +45,7 @@ public enum ClearingError implements IErrorEnumId {
MarketIsNotMkr(5217L),
BalanceInsufficient(5222L),
Sdf57UnexpectedBalance(5435L),
UtilitarianDigitalRightNotFound(5436L),
TCRegistryNotFound(3022L),
WrongField(5004L),
GatewayTimeout(6003L),

View file

@ -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<Company> imdgCompany;
private final Imdg<TradingClearingRegistry> imdgTradingClearingRegistry;
private final Imdg<DigitalCertificateSecurity> imdgDigitCertSecurity;
private final Imdg<UtilitarianDigitalRight> 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<Security> currPairSecCash;
private final CashV2ByString<Security> digitCertSecCash;
private final CashV2ByString<Company> cmpCash;
private final CashV2<Long, UtilitarianDigitalRight> udrCash;
private final CashV2ByIdAndString<TradingClearingRegistry> 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<Long, UtilitarianDigitalRight>("udrCash") {
@Override
protected Long extractKey(UtilitarianDigitalRight obj) {
return obj.getSecurityId();
}
});
}
}
}

View file

@ -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,

View file

@ -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<ImdgValidationContext<STrades>> {
private final CashV2<Long, UtilitarianDigitalRight> udrCash;
public STradesSecurityDGCTValidationRule(CashV2<Long, UtilitarianDigitalRight> udrCash) {
this.udrCash = udrCash;
}
@Override
public String ruleName() {
return "STradesSecurityDGCTValidationRule";
}
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<STrades> 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<UtilitarianDigitalRight> 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();
}
}

View file

@ -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(",", ".");