ialbert 2023-11-15 16:46:36 +03:00
parent e8cba0467a
commit c86f1e907a
7 changed files with 125 additions and 52 deletions

View file

@ -15,6 +15,7 @@ public class ClearingServiceSettings {
private KafkaConsumerSettings kafkaConsumer;
private KafkaProducerSettings kafkaProducer;
private SessionStageSettings sessionStage;
private TradeSettings trade = new TradeSettings();
public HazelcastClientParams getHazelcast() {
return hazelcast;
@ -47,4 +48,12 @@ public class ClearingServiceSettings {
public void setSessionStage(SessionStageSettings sessionStage) {
this.sessionStage = sessionStage;
}
public TradeSettings getTrade() {
return trade;
}
public void setTrade(TradeSettings trade) {
this.trade = trade;
}
}

View file

@ -0,0 +1,14 @@
package ru.spcex.clearing.config.element;
public class TradeSettings {
private Boolean valuation = false;
public Boolean getValuation() {
return valuation;
}
public void setValuation(Boolean valuation) {
this.valuation = valuation;
}
}

View file

@ -1,42 +0,0 @@
package ru.spcex.clearing.service.builder;
import ru.clearing.classes.statics.data.payment.PaymentInstruction;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.spcex.platform.imdg.api.ImdgProvider;
import java.math.BigDecimal;
public class PaymentInstructionBuilderDepositReturn {
private ImdgProvider imdgProvider;
private Long sessionId;
private BigDecimal amount;
private Registry lm_tRgs;
public static PaymentInstructionBuilderDepositReturn builder(ImdgProvider imdgProvider) {
return new PaymentInstructionBuilderDepositReturn(imdgProvider);
}
private PaymentInstructionBuilderDepositReturn(ImdgProvider imdgProvider) {
this.imdgProvider = imdgProvider;
}
public PaymentInstructionBuilderDepositReturn sessionId(Long sessionId) {
this.sessionId = sessionId;
return this;
}
public PaymentInstructionBuilderDepositReturn amount(BigDecimal amount) {
this.amount = amount;
return this;
}
public PaymentInstructionBuilderDepositReturn lm_tRgs(Registry rgs) {
this.lm_tRgs = rgs;
return this;
}
public PaymentInstruction build() {
return null;
}
}

View file

@ -11,29 +11,35 @@ import org.springframework.stereotype.Component;
import ru.clearing.classes.statics.data.account.ClientCode;
import ru.clearing.classes.statics.data.company.Company;
import ru.clearing.classes.statics.data.execution.ExecutionFond;
import ru.clearing.classes.statics.data.instrument.issue.FixedIncomeCashFlow;
import ru.clearing.classes.statics.data.instrument.issue.FixedIncomeSecurity;
import ru.clearing.classes.statics.data.misc.Listing;
import ru.clearing.classes.statics.data.misc.STrades;
import ru.clearing.classes.statics.data.registry.TradingClearingRegistry;
import ru.clearing.classes.statics.data.security.Security;
import ru.spcex.clearing.config.element.ClearingServiceSettings;
import ru.spcex.clearing.error.ClearingError;
import ru.spcex.clearing.error.ClearingException;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.notification.NotificationSender;
import ru.spcex.clearing.platform.messaging.domain.Consts;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.DealRegisterNewRequest;
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.platform.enumeration.CurrencyCode;
import ru.spcex.platform.enumeration.Section;
import ru.spcex.platform.enumeration.Side;
import ru.spcex.platform.enumeration.*;
import ru.spcex.platform.imdg.api.Imdg;
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.utils.enumeration.*;
import ru.spcex.platform.utils.log.ExceptionUtils;
import ru.spcex.platform.utils.time.TimeUtil;
import ru.spcex.platform.utils.validation.IValidator;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.LocalDate;
import java.util.Collection;
@ -41,6 +47,8 @@ import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import static ru.spcex.platform.utils.number.BigDecimalUtil.safeBD;
@Component
@EnableScheduling
public class ExecutionFondComponent {
@ -50,6 +58,7 @@ public class ExecutionFondComponent {
private final Imdg<ExecutionFond> executionFondImdg;
private final Imdg<Listing> listingImdg;
private final Imdg<ClientCode> clientCodeImdg;
private final Imdg<FixedIncomeCashFlow> fixedIncomeCashFlowImdg;
private final Imdg<ru.clearing.classes.statics.data.misc.Market> marketImdg;
//fixme ждать ТЗ
@ -58,19 +67,27 @@ public class ExecutionFondComponent {
private final IMessageResolver msgResolver = new SimpleMessageResolver();
private final Function<STrades, IValidator> stradesValidator;
private final KafkaSender kafkaSender;
private final NotificationSender notifications;
private final boolean valuation;
private static MathContext prec2 = new MathContext(2, RoundingMode.HALF_UP);
@Autowired
public ExecutionFondComponent(ImdgProvider imdgProvider, Producer<String, Object> kafka,
public ExecutionFondComponent(ImdgProvider imdgProvider,
ClearingServiceSettings settings,
Producer<String, Object> kafka,
@Qualifier("sTradesValidatorFond") Function<STrades, IValidator> stradesValidator,
@Qualifier("kafkaSenderWithoutRequestInfo") KafkaSender kafkaSender) {
@Qualifier("kafkaSenderWithoutRequestInfo") KafkaSender kafkaSender, NotificationSender notifications) {
this.sTradeImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_STrades, STrades.class);
this.executionFondImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ExecutionFond, ExecutionFond.class);
this.listingImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Listing, Listing.class);
this.clientCodeImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClientCode, ClientCode.class);
this.marketImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Market, ru.clearing.classes.statics.data.misc.Market.class);
this.fixedIncomeCashFlowImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_FixedIncomeCashFlow, FixedIncomeCashFlow.class);
this.valuation = settings.getTrade().getValuation(); //fixme
this.stradesValidator = stradesValidator;
this.kafkaSender = kafkaSender;
this.notifications = notifications;
resetTradingDay();
}
@ -165,6 +182,7 @@ public class ExecutionFondComponent {
protected ExecutionFond createExecutionFond(STrades sTrades,
IValidator validator) throws ClearingException {
Security security = validator.getStored(ValidationStored.STradesSecurity);
// Listing listing = listingImdg.getFirstObjectByFieldValues(Map.of("securityId", security.getId()));
Company company = validator.getStored(ValidationStored.STradesCompany);
Company counterCompany = validator.getStored(ValidationStored.STradesCounterCompany);
TradingClearingRegistry rgstr = validator.getStored(ValidationStored.STradesTradingClearingRegistry);
@ -212,7 +230,64 @@ public class ExecutionFondComponent {
eFond.setSecurityId(security.getId());
eFond.setInterestAmount(sTrades.getAccruedint());
eFond.setExchangeOrderId(sTrades.getOrderNum());
eFond.setSettlementAmount(sTrades.getValue());
if (valuation) {
BigDecimal lots = safeBD(sTrades.getQty());// eFond.getLots
BigDecimal price = safeBD(sTrades.getPrice());// eFond.setPrice
BigDecimal settlementAmount = BigDecimal.ZERO;
if (InstrumentType.BOND.equalsByKey(security.getInstrumentType())) {
FixedIncomeSecurity fixedIncome = (FixedIncomeSecurity) security;
BigDecimal nominalValue;
Listing listing = listingImdg.getFirstObjectBySQL("securityId = %d and market = '%s'".formatted(fixedIncome.getId(), sTrades.getClassCode()));
BigDecimal lotSize = listing != null ? safeBD(listing.getLotSize()) : BigDecimal.ZERO;
BigDecimal ncd = safeBD(sTrades.getAccruedint());// eFond.interestAmount
if (BondType.I.equalsByKey(fixedIncome.getBondType())) {
ImdgPredicateBuilder pb = fixedIncomeCashFlowImdg.predicateBuilder();
ImdgPredicate prdct = pb.and(
pb.equals("securityId", fixedIncome.getId()),
sTrades.getSettleDate() != null ?
pb.equals("valueDate", sTrades.getSettleDate()) : pb.alwaysFalse()
//fixme в тз settlementDate
);
FixedIncomeCashFlow cashFlow = fixedIncomeCashFlowImdg.getFirstObjectByPredicate(prdct);
if (cashFlow != null) {
nominalValue = safeBD(cashFlow.getNominalValue());
} else {
nominalValue = BigDecimal.ZERO;
}
} else {
nominalValue = safeBD(fixedIncome.getNominalValue());
}
{
lotSize.multiply(lots);
settlementAmount = lotSize.multiply(lots)
.multiply(nominalValue)
.multiply(price)
.divide(new BigDecimal(100), prec2) //first rounding
.add(ncd.setScale(2, RoundingMode.HALF_UP)); //second rounding
}
eFond.setSettlementAmount(settlementAmount);
//ОКРУГЛЕНИЕ( NV* quantity * price/100) + ОКРУГЛЕНИЕ (executionFond.interestAmount) для каждой сделки
} else if (InstrumentType.EQTY.equalsByKey(security.getInstrumentType())) {
settlementAmount = lots.multiply(price, prec2);
}
eFond.setSettlementAmount(settlementAmount);
if (settlementAmount.compareTo(safeBD(sTrades.getValue())) != 0) {
log.warn("sTrades.TradeNum={} sTrades.value({}) != eFond({})",
sTrades.getTradeNum(),
safeBD(sTrades.getValue()),
settlementAmount);
notifications.sendNotification(
ObjectType.vfrs,
"Для сделки %s объем сделки в ТС %s не совпадает с расчитанным в КС %s"
.formatted(sTrades.getTradeNum(),
safeBD(sTrades.getValue()).toString(),
settlementAmount.toString()),
Priority.HIGH
);
}
} else {
eFond.setSettlementAmount(sTrades.getValue());
}
eFond.setCounterPartyId(counterCompany.getId());
{
ImdgPredicateBuilder strPb = sTradeImdg.predicateBuilder();

View file

@ -34,7 +34,10 @@ import ru.spcex.platform.utils.enumeration.IMessageResolver;
import java.time.Instant;
import java.time.LocalDate;
import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -269,15 +272,23 @@ public class FinishingSession implements ISessionStage {
protected Collection<ExecutionFond> findExecutionFondBySessionId(Set<Long> exchangeIds) {
ImdgPredicateBuilder pb = executionFondImdg.predicateBuilder();
ImdgPredicate prdct = pb.in("exchangeExecutionId", exchangeIds.toArray(new Long[0]));
ImdgPredicate prdct = pb.and(
pb.in("exchangeExecutionId", exchangeIds.toArray(new Long[0])),
pb.not(pb.equals("settlementDate", LocalDate.now()))
);
Collection<ExecutionFond> result = executionFondImdg.getCollectionObjectsByPredicate(prdct);
log.trace("found {} ExecutionFond from first day", result.size());
return result;
}
protected Collection<ExecutionDeposit> findExecutionDepositBySessionId(Long sessionId) {
Collection<ExecutionDeposit> result = executionDepositImdg.getCollectionObjectsByFieldValues(Map.of("sessionId", sessionId));
log.trace("found {} ExecutionDeposit by sessionId={}", result.size(), sessionId);
ImdgPredicateBuilder pb = executionDepositImdg.predicateBuilder();
ImdgPredicate prdct = pb.and(
pb.equals("sessionId", sessionId),
pb.equals("firstLegSettlementDate", LocalDate.now())
);
Collection<ExecutionDeposit> result = executionDepositImdg.getCollectionObjectsByPredicate(prdct);
log.trace("found {} ExecutionDeposit by {}", result.size(), prdct);
return result;
}

View file

@ -107,4 +107,9 @@ public class ImdgPredicateBuilderHazelcast implements ImdgPredicateBuilder {
public ImdgPredicate alwaysTrue() {
return new ImdgPredicateHazelcast(Predicates.alwaysTrue());
}
@Override
public ImdgPredicate alwaysFalse() {
return new ImdgPredicateHazelcast(Predicates.alwaysFalse());
}
}

View file

@ -21,4 +21,5 @@ public interface ImdgPredicateBuilder {
ImdgPredicate regex(String key, String regex);
ImdgPredicate alwaysTrue();
ImdgPredicate alwaysFalse();
}