diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/listener/TaskListener.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/listener/TaskListener.java index a61cf9408..2ca80d7cd 100644 --- a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/listener/TaskListener.java +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/listener/TaskListener.java @@ -1,10 +1,5 @@ package ru.spcex.clearing.dflt.management.listener; -import java.math.BigDecimal; -import java.math.MathContext; -import java.math.RoundingMode; -import java.time.DayOfWeek; -import java.time.LocalDate; import java.util.Collection; import java.util.Optional; import org.apache.kafka.clients.consumer.Consumer; @@ -14,83 +9,41 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import ru.clearing.classes.statics.data.company.ClearingMemberCategory; -import ru.clearing.classes.statics.data.company.Company; -import ru.clearing.classes.statics.data.company.CompanyRoleSet; -import ru.clearing.classes.statics.data.misc.Listing; -import ru.clearing.classes.statics.data.misc.Market; -import ru.clearing.classes.statics.data.misc.OrderCurrency; import ru.clearing.classes.statics.data.misc.OvernightSettings; import ru.clearing.classes.statics.data.registry.Registry; -import ru.clearing.classes.statics.data.registry.TradingClearingRegistry; -import ru.clearing.classes.statics.data.scheduler.ClearingCalendar; -import ru.clearing.classes.statics.data.security.CurrencyPairSecurity; -import ru.clearing.classes.statics.data.security.Rates; -import ru.clearing.classes.statics.data.security.RiskParameter; -import ru.clearing.platform.dictionary.CurrencyPairDictionary; -import ru.spcex.clearing.dflt.management.config.settings.DefaultManagementSettings; -import ru.spcex.clearing.dflt.management.service.OrderCurrencyService; +import ru.spcex.clearing.dflt.management.service.ImdgQueryService; +import ru.spcex.clearing.dflt.management.service.SpotOvernightOrderService; +import ru.spcex.clearing.dflt.management.service.SwapOvernightOrderService; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.domain.BaseRequest; import ru.spcex.clearing.platform.messaging.domain.cud.misc.OvernightSettingsNewRequest; import ru.spcex.clearing.platform.messaging.service.QueueConsumer; -import ru.spcex.clearing.util.security.UserRoleVerification; -import ru.spcex.platform.enumeration.CompanyRole; -import ru.spcex.platform.enumeration.DayStatus; -import ru.spcex.platform.enumeration.MarketType; import ru.spcex.platform.enumeration.OvernightType; -import ru.spcex.platform.enumeration.Side; import ru.spcex.platform.enumeration.Task; -import ru.spcex.platform.enumeration.WorkflowStatus; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgProvider; -import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; import ru.spcex.platform.utils.enumeration.IEnumKey; @Service public class TaskListener extends QueueConsumer implements InitializingBean { protected final Logger log = LoggerFactory.getLogger(getClass()); - private final OrderCurrencyService orderCurrencyService; - private final ImdgProvider imdgProvider; + private final ImdgQueryService imdgQueryService; private final Imdg registryImdg; - private final Imdg categoryImdg; - private final Imdg overnightSettingsImdg; - private final Imdg companyRoleSetImdg; - private final Imdg companyImdg; - private final Imdg tradingClearingRegistryImdg; - private final Imdg currencyPairDictionaryImdg; - private final Imdg currencyPairSecurityImdg; - private final Imdg listingImdg; - private final Imdg marketImdg; - private final Imdg riskParameterImdg; - private final Imdg ratesImdg; - private final Imdg clearingCalendarImdg; - private final DefaultManagementSettings settings; + private final SwapOvernightOrderService swapOvernightOrderService; + private final SpotOvernightOrderService spotOvernightOrderService; @Autowired public TaskListener(Consumer kafkaQueue, Producer kafkaProducer, + ImdgQueryService imdgQueryService, ImdgProvider imdgProvider, - UserRoleVerification userRoleVerification, - OrderCurrencyService orderCurrencyService, - DefaultManagementSettings settings) { + SwapOvernightOrderService swapOvernightOrderService, + SpotOvernightOrderService spotOvernightOrderService) { super(kafkaQueue, kafkaProducer); - this.imdgProvider = imdgProvider; + this.imdgQueryService = imdgQueryService; this.registryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class); - this.overnightSettingsImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_OvernightSettings, OvernightSettings.class); - this.categoryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class); - this.companyRoleSetImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_CompanyRoleSet, CompanyRoleSet.class); - this.companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class); - this.tradingClearingRegistryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class); - this.currencyPairDictionaryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_CurrencyPairDictionary, CurrencyPairDictionary.class); - this.currencyPairSecurityImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_CurrencyPairSecurity, CurrencyPairSecurity.class); - this.listingImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Listing, Listing.class); - this.marketImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Market, Market.class); - this.riskParameterImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_RiskParameter, RiskParameter.class); - this.ratesImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Rates, Rates.class); - this.clearingCalendarImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingCalendar, ClearingCalendar.class); - this.orderCurrencyService = orderCurrencyService; - this.settings = settings; + this.swapOvernightOrderService = swapOvernightOrderService; + this.spotOvernightOrderService = spotOvernightOrderService; } @Override @@ -104,7 +57,7 @@ public class TaskListener extends QueueConsumer implements InitializingBean { private void createOrder(BaseRequest newReq) { log.info("CROR createOrder started, request={}", newReq); - Collection registryIds = orderCurrencyService.getCurrencyRegistry(); + Collection registryIds = imdgQueryService.getCurrencyRegistry(); if (registryIds == null || registryIds.isEmpty()) { log.info("No registries found for CROR processing"); return; @@ -119,13 +72,12 @@ public class TaskListener extends QueueConsumer implements InitializingBean { } OvernightType overnightType = resolveOvernightType(registry); - log.info("Processing registry id={}, companyId={}, overnightType={}", registry.getId(), registry.getCompanyId(), overnightType); switch (overnightType) { - case SWAP -> createSwapOrders(registry); - case SPOT -> createSpotOrders(registry); + case SWAP -> swapOvernightOrderService.process(registry); + case SPOT -> spotOvernightOrderService.process(registry); default -> log.warn("Unsupported overnightType={} for registry id={}", overnightType, registry.getId()); } @@ -139,558 +91,10 @@ public class TaskListener extends QueueConsumer implements InitializingBean { private OvernightType resolveOvernightType(Registry registry) { Optional settingsOpt = - orderCurrencyService.findOvernightSettingsByCompany(registry.getCompanyId()); + imdgQueryService.findOvernightSettingsByCompany(registry.getCompanyId()); return settingsOpt .map(settings -> IEnumKey.getEnumByKey(OvernightType.class, settings.getOvernightType())) .orElse(OvernightType.SWAP); } - - private void createSpotOrders(Registry registry) { - - } - - private void createSwapOrders(Registry registry) { - OrderCurrency primaryTransferOrder = new OrderCurrency(); - OrderCurrency counterTransferOrder = new OrderCurrency(); - OrderCurrency primaryBalancingOrder = new OrderCurrency(); - OrderCurrency counterBalancingOrder = new OrderCurrency(); - - ImdgPredicateBuilder predicateBuilder = categoryImdg.predicateBuilder(); - ClearingMemberCategory category = categoryImdg.getFirstObjectByPredicate( - predicateBuilder.equals("companyId", registry.getCompanyId()) - ); - String orderCategory = category.getClearingMemberCategory().equals("P") ? "PL" : "UK"; - Side primaryTransferSide = Side.SELL; - Side counterTransferSide = Side.BUY; - - Side primaryBalancingSide = Side.BUY; - Side counterBalancingSide = Side.SELL; - - if (registry.getSecuritySymbol().equals("RUB")) { - primaryTransferSide = primaryTransferSide.revers(); - counterTransferSide = counterTransferSide.revers(); - primaryBalancingSide = primaryBalancingSide.revers(); - counterBalancingSide = counterBalancingSide.revers(); - } - - Market market = marketImdg.getSingleObjectByPredicate(predicateBuilder.equals("marketType", MarketType.SCSW)); - Listing listing; - RiskParameter riskParameter; - if (registry.getSecuritySymbol().equals("RUB")) { - listing = findListingForRub(registry, market.getCode()); - riskParameter = findRiskParameterForRub(); - } else { - listing = findListingForOtherCurrency(registry, market.getCode()); - riskParameter = findRiskParameterForOtherCurrency(registry); - } - - if (orderCategory.equals("PL")) { - fillPrimaryTransferP(primaryTransferOrder, registry); - fillCounterTransferP(counterTransferOrder, registry); - fillPrimaryBalancingP(primaryBalancingOrder, registry); - fillCounterBalancingP(counterBalancingOrder, registry); - } else if (orderCategory.equals("UK")) { - fillPrimaryTransferUK(primaryTransferOrder, registry); - fillCounterTransferUK(counterTransferOrder, registry); - fillPrimaryBalancingUK(primaryBalancingOrder, registry); - fillCounterBalancingUK(counterBalancingOrder, registry); - } - - fillSwapOrder(primaryTransferOrder, registry, listing, market, riskParameter, primaryTransferSide, "overnight"); - fillSwapOrder(counterTransferOrder, registry, listing, market, riskParameter, counterTransferSide, "overnight"); - fillSwapOrder(primaryBalancingOrder, registry, listing, market, riskParameter, primaryBalancingSide, "balance"); - fillSwapOrder(counterBalancingOrder, registry, listing, market, riskParameter, counterBalancingSide, "balance"); - } - - private void fillPrimaryTransferUK(OrderCurrency orderCurrency, Registry registry) { - ImdgPredicateBuilder predicateBuilder = categoryImdg.predicateBuilder(); - CompanyRoleSet companyRoleSet = companyRoleSetImdg.getSingleObjectByPredicate( - predicateBuilder.equals("companyRole", CompanyRole.CLCC) - ); - Company company = companyImdg.getSingleObjectByID(companyRoleSet.getCompanyId()); - - orderCurrency.setAccount(registry.getTradingClearingRegistry()); - orderCurrency.setTradingClearingRegistryId(registry.getTradingClearingRegistryId()); - orderCurrency.setCompanyId(registry.getCompanyId()); - orderCurrency.setTradingCode(registry.getTradingCode()); - orderCurrency.setCounterPartyId(companyRoleSet.getCompanyId()); - orderCurrency.setCounterTradingCode(company.getTradingCode()); - orderCurrency.setType("CMOV"); - } - - private void fillCounterTransferUK(OrderCurrency orderCurrency, Registry registry) { - ImdgPredicateBuilder predicateBuilder = categoryImdg.predicateBuilder(); - CompanyRoleSet companyRoleSet = companyRoleSetImdg.getSingleObjectByPredicate( - predicateBuilder.equals("companyRole", CompanyRole.CLCC) - ); - Company company = companyImdg.getSingleObjectByID(companyRoleSet.getCompanyId()); - - TradingClearingRegistry tradingClearingRegistry = tradingClearingRegistryImdg.getFirstObjectByPredicate( - predicateBuilder.equals("companyId", company.getId()) - ); - - orderCurrency.setAccount(tradingClearingRegistry.getCode()); - orderCurrency.setTradingClearingRegistryId(tradingClearingRegistry.getId()); - orderCurrency.setCompanyId(company.getId()); - orderCurrency.setTradingCode(company.getTradingCode()); - orderCurrency.setCounterPartyId(registry.getCompanyId()); - orderCurrency.setCounterTradingCode(registry.getTradingCode()); - orderCurrency.setType("CMOV"); - } - - private void fillPrimaryBalancingUK(OrderCurrency orderCurrency, Registry registry) { - ImdgPredicateBuilder predicateBuilder = categoryImdg.predicateBuilder(); - ClearingMemberCategory category = categoryImdg.getFirstObjectByPredicate( - predicateBuilder.equals("clearingMemberCategory", "P") - ); - TradingClearingRegistry tradingClearingRegistry = tradingClearingRegistryImdg.getFirstObjectByPredicate( - predicateBuilder.equals("companyId", category.getCompanyId()) - ); - Company company = companyImdg.getSingleObjectByID(category.getCompanyId()); - - CompanyRoleSet clccCompanyRole = companyRoleSetImdg.getSingleObjectByPredicate( - predicateBuilder.equals("companyRole", CompanyRole.CLCC) - ); - Company clssCompany = companyImdg.getSingleObjectByID(category.getCompanyId()); - - orderCurrency.setAccount(tradingClearingRegistry.getCode()); - orderCurrency.setTradingClearingRegistryId(tradingClearingRegistry.getId()); - orderCurrency.setCompanyId(company.getId()); - orderCurrency.setTradingCode(company.getTradingCode()); - orderCurrency.setCounterPartyId(clccCompanyRole.getCompanyId()); - orderCurrency.setCounterTradingCode(clssCompany.getTradingCode()); - orderCurrency.setType("CMBL"); - } - - private void fillCounterBalancingUK(OrderCurrency orderCurrency, Registry registry) { - ImdgPredicateBuilder predicateBuilder = categoryImdg.predicateBuilder(); - CompanyRoleSet companyRoleSet = companyRoleSetImdg.getSingleObjectByPredicate( - predicateBuilder.equals("companyRole", CompanyRole.CLCC) - ); - Company company = companyImdg.getSingleObjectByID(companyRoleSet.getCompanyId()); - - TradingClearingRegistry tradingClearingRegistry = tradingClearingRegistryImdg.getFirstObjectByPredicate( - predicateBuilder.equals("companyId", company.getId()) - ); - - ClearingMemberCategory categoryP = categoryImdg.getFirstObjectByPredicate( - predicateBuilder.equals("clearingMemberCategory", "P") - ); - Company companyP = companyImdg.getSingleObjectByID(categoryP.getCompanyId()); - - orderCurrency.setAccount(tradingClearingRegistry.getCode()); - orderCurrency.setTradingClearingRegistryId(tradingClearingRegistry.getId()); - orderCurrency.setCompanyId(company.getId()); - orderCurrency.setTradingCode(company.getTradingCode()); - orderCurrency.setCounterPartyId(categoryP.getCompanyId()); - orderCurrency.setCounterTradingCode(companyP.getTradingCode()); - orderCurrency.setType("CMBL"); - } - - - private void fillPrimaryTransferP(OrderCurrency orderCurrency, Registry registry) { - ImdgPredicateBuilder predicateBuilder = categoryImdg.predicateBuilder(); - CompanyRoleSet companyRoleSet = companyRoleSetImdg.getSingleObjectByPredicate( - predicateBuilder.equals("companyRole", CompanyRole.CLCC) - ); - Company company = companyImdg.getSingleObjectByID(companyRoleSet.getCompanyId()); - - orderCurrency.setAccount(registry.getTradingClearingRegistry()); - orderCurrency.setTradingClearingRegistryId(registry.getTradingClearingRegistryId()); - orderCurrency.setCompanyId(registry.getCompanyId()); - orderCurrency.setTradingCode(registry.getTradingCode()); - orderCurrency.setCounterPartyId(companyRoleSet.getCompanyId()); - orderCurrency.setCounterTradingCode(company.getTradingCode()); - orderCurrency.setType("PLOV"); - } - - private void fillCounterTransferP(OrderCurrency orderCurrency, Registry registry) { - ImdgPredicateBuilder predicateBuilder = categoryImdg.predicateBuilder(); - CompanyRoleSet companyRoleSet = companyRoleSetImdg.getSingleObjectByPredicate( - predicateBuilder.equals("companyRole", CompanyRole.CLCC) - ); - Company company = companyImdg.getSingleObjectByID(companyRoleSet.getCompanyId()); - - TradingClearingRegistry tradingClearingRegistry = tradingClearingRegistryImdg.getFirstObjectByPredicate( - predicateBuilder.equals("companyId", company.getId()) - ); - - orderCurrency.setAccount(tradingClearingRegistry.getCode()); - orderCurrency.setTradingClearingRegistryId(tradingClearingRegistry.getId()); - orderCurrency.setCompanyId(company.getId()); - orderCurrency.setTradingCode(company.getTradingCode()); - orderCurrency.setCounterPartyId(registry.getCompanyId()); - orderCurrency.setCounterTradingCode(registry.getTradingCode()); - orderCurrency.setType("PLOV"); - } - - private void fillPrimaryBalancingP(OrderCurrency orderCurrency, Registry registry) { - ImdgPredicateBuilder predicateBuilder = categoryImdg.predicateBuilder(); - CompanyRoleSet companyRoleSet = companyRoleSetImdg.getSingleObjectByPredicate( - predicateBuilder.equals("companyRole", CompanyRole.RGCM) - ); - Company company = companyImdg.getSingleObjectByID(companyRoleSet.getCompanyId()); - - TradingClearingRegistry tradingClearingRegistry = tradingClearingRegistryImdg.getFirstObjectByPredicate( - predicateBuilder.equals("companyId", company.getId()) - ); - - CompanyRoleSet clccCompanyRole = companyRoleSetImdg.getSingleObjectByPredicate( - predicateBuilder.equals("companyRole", CompanyRole.CLCC) - ); - Company clssCompany = companyImdg.getSingleObjectByID(clccCompanyRole.getCompanyId()); - - orderCurrency.setAccount(tradingClearingRegistry.getCode()); - orderCurrency.setTradingClearingRegistryId(tradingClearingRegistry.getId()); - orderCurrency.setCompanyId(company.getId()); - orderCurrency.setTradingCode(company.getTradingCode()); - orderCurrency.setCounterPartyId(clccCompanyRole.getCompanyId()); - orderCurrency.setCounterTradingCode(clssCompany.getTradingCode()); - orderCurrency.setType("PLBL"); - } - - private void fillCounterBalancingP(OrderCurrency orderCurrency, Registry registry) { - ImdgPredicateBuilder predicateBuilder = categoryImdg.predicateBuilder(); - CompanyRoleSet companyRoleSet = companyRoleSetImdg.getSingleObjectByPredicate( - predicateBuilder.equals("companyRole", CompanyRole.CLCC) - ); - Company company = companyImdg.getSingleObjectByID(companyRoleSet.getCompanyId()); - - TradingClearingRegistry tradingClearingRegistry = tradingClearingRegistryImdg.getFirstObjectByPredicate( - predicateBuilder.equals("companyId", company.getId()) - ); - - CompanyRoleSet rgcmCompanyRole = companyRoleSetImdg.getSingleObjectByPredicate( - predicateBuilder.equals("companyRole", CompanyRole.RGCM) - ); - Company rgcmCompany = companyImdg.getSingleObjectByID(rgcmCompanyRole.getCompanyId()); - - orderCurrency.setAccount(tradingClearingRegistry.getCode()); - orderCurrency.setTradingClearingRegistryId(tradingClearingRegistry.getId()); - orderCurrency.setCompanyId(company.getId()); - orderCurrency.setTradingCode(company.getTradingCode()); - orderCurrency.setCounterPartyId(rgcmCompanyRole.getCompanyId()); - orderCurrency.setCounterTradingCode(rgcmCompany.getTradingCode()); - orderCurrency.setType("PLBL"); - - } - - private void fillSwapOrder(OrderCurrency orderCurrency, - Registry registry, - Listing listing, - Market market, - RiskParameter riskParameter, - Side side, - String comment) { - CurrencyPairSecurity currencyPairSecurity = currencyPairSecurityImdg.getSingleObjectByID(listing.getSecurityId()); - CurrencyPairDictionary currencyPairDictionary = currencyPairDictionaryImdg - .getSingleObjectByID(currencyPairSecurity.getCurrencyPairId()); - - BigDecimal s = defineTransferRate(currencyPairDictionary.getId(), registry.getSecuritySymbol()); - BigDecimal n = numberOfDaysOfTransfer(); - CalculationResultHolder resultHolder = calculationSwapSums(registry, listing.getLotSize().intValue(), s, n); - BigDecimal quantityLot = resultHolder.getSum3().abs(); - BigDecimal price = swapPriceCalc(currencyPairDictionary.getBaseCurrency(), s, n); - - orderCurrency.setOvernightType("SWAP"); - orderCurrency.setMarket(market.getCode()); - orderCurrency.setListingId(listing.getId()); - orderCurrency.setSecuritySymbol(listing.getSymbolCode()); - orderCurrency.setSecurityId(listing.getSecurityId()); - orderCurrency.setLotSize(listing.getLotSize()); - orderCurrency.setRate(riskParameter.getQuoteValue()); - orderCurrency.setSide(side.getKey()); - orderCurrency.setSettleCode("T1"); - orderCurrency.setStatus("CRET"); - orderCurrency.setComment(comment); - orderCurrency.setRegistryId(registry.getId()); - orderCurrency.setPrice(price); - orderCurrency.setQuantityLot(quantityLot); - } - - private Listing findListingForRub(Registry registry, String market) { - ImdgPredicateBuilder predicateBuilder = currencyPairDictionaryImdg.predicateBuilder(); - CurrencyPairDictionary currencyPairDictionary = currencyPairDictionaryImdg.getFirstObjectByPredicate( - predicateBuilder.and( - predicateBuilder.equals("baseCurrency", "CNY"), - predicateBuilder.equals("quoteCurrency", registry.getSecuritySymbol()) - )); - Collection securitiesIds = currencyPairSecurityImdg.getCollectionIdsByPredicate( - predicateBuilder.equals("currencyPairId", currencyPairDictionary.getId()) - ); - Listing listing = listingImdg.getSingleObjectByPredicate( - predicateBuilder.and( - predicateBuilder.in("securityId", securitiesIds.toArray(new Long[0])), - predicateBuilder.equals("market", market) - ) - ); - return listing; - } - - private Listing findListingForOtherCurrency(Registry registry, String market) { - ImdgPredicateBuilder predicateBuilder = currencyPairDictionaryImdg.predicateBuilder(); - CurrencyPairDictionary currencyPairDictionary = currencyPairDictionaryImdg.getFirstObjectByPredicate( - predicateBuilder.and( - predicateBuilder.equals("baseCurrency", registry.getSecuritySymbol()), - predicateBuilder.equals("quoteCurrency", "RUB") - )); - Collection securitiesIds = currencyPairSecurityImdg.getCollectionIdsByPredicate( - predicateBuilder.equals("currencyPairId", currencyPairDictionary.getId()) - ); - Listing listing = listingImdg.getSingleObjectByPredicate( - predicateBuilder.and( - predicateBuilder.in("securityId", securitiesIds.toArray(new Long[0])), - predicateBuilder.equals("market", market) - ) - ); - return listing; - } - - private RiskParameter findRiskParameterForRub() { - ImdgPredicateBuilder predicateBuilder = currencyPairDictionaryImdg.predicateBuilder(); - CurrencyPairDictionary currencyPairDictionary = currencyPairDictionaryImdg.getFirstObjectByPredicate( - predicateBuilder.and( - predicateBuilder.equals("baseCurrency", "CNY") - )); - return riskParameterImdg.getSingleObjectByPredicate( - predicateBuilder.and( - predicateBuilder.equals("currencyPairId", currencyPairDictionary.getId()), - predicateBuilder.equals("settlementType", "T0") - ) - ); - } - - private RiskParameter findRiskParameterForOtherCurrency(Registry registry) { - ImdgPredicateBuilder predicateBuilder = currencyPairDictionaryImdg.predicateBuilder(); - CurrencyPairDictionary currencyPairDictionary = currencyPairDictionaryImdg.getFirstObjectByPredicate( - predicateBuilder.and( - predicateBuilder.equals("baseCurrency", registry.getSecuritySymbol()) - )); - return riskParameterImdg.getSingleObjectByPredicate( - predicateBuilder.and( - predicateBuilder.equals("currencyPairId", currencyPairDictionary.getId()), - predicateBuilder.equals("settlementType", "T0") - ) - ); - } - - private static final MathContext MC = new MathContext(20, RoundingMode.HALF_UP); - - //registry.securitySymbol != 'RUB' - private CalculationResultHolder calculationSwapSums(Registry registry, Integer lotSize, BigDecimal s, BigDecimal n) { - BigDecimal planPosition = registry.getBalance(); - BigDecimal sum1 = planPosition.abs(); - - Registry amtRubRegistry = findAmtRegistry(registry, "RUB"); - BigDecimal planPositionRub = amtRubRegistry.getBalance(); - - Rates rates = findRates(registry.getSecuritySymbol()); - BigDecimal ratesValue = rates.getValue(); - - BigDecimal sum2 = planPositionRub.divide(ratesValue, lotSize, RoundingMode.HALF_DOWN); - - BigDecimal ceilSum1 = sum1.setScale(0, RoundingMode.CEILING); - BigDecimal sum3 = ceilSum1.min(sum2); - BigDecimal sum4 = sum3 - .multiply(ratesValue, MC) - .negate(); - BigDecimal sum5 = sum3.negate(); - - BigDecimal daysInYear = BigDecimal.valueOf(365); - // (S / 365) * n - BigDecimal carryPart = s.divide(daysInYear, MC).multiply(n, MC); - // 1 + S / 365 * n - BigDecimal multiplier = BigDecimal.ONE.add(carryPart, MC); - - BigDecimal sum6 = sum3 - .multiply(ratesValue, MC) - .multiply(multiplier, MC); - return new CalculationResultHolder(sum1, sum2, sum3, sum4, sum5, sum6); - } - - //registry.securitySymbol == 'RUB' - private CalculationResultHolder calculationSwapRubSums(Registry registry, Integer lotSize, BigDecimal s, BigDecimal n) { - BigDecimal planPosition = registry.getBalance(); - Rates cnyRates = findRates(registry.getSecuritySymbol()); - BigDecimal ratesValue = cnyRates.getValue(); - BigDecimal sum1 = planPosition.divide(ratesValue, MC); - - Registry amtRubRegistry = findAmtRegistry(registry, "CNY"); - BigDecimal planPositionCny = amtRubRegistry.getBalance(); - - BigDecimal sum2 = planPositionCny.divide(ratesValue, lotSize, RoundingMode.HALF_DOWN); - - BigDecimal ceilSum1 = sum1.setScale(0, RoundingMode.CEILING); - BigDecimal sum3 = ceilSum1.min(sum2).negate(); - BigDecimal sum4 = sum3 - .multiply(ratesValue, MC) - .negate(); - BigDecimal sum5 = sum3.negate(); - - BigDecimal daysInYear = BigDecimal.valueOf(365); - // (S / 365) * n - BigDecimal carryPart = s.divide(daysInYear, MC).multiply(n, MC); - // 1 + S / 365 * n - BigDecimal multiplier = BigDecimal.ONE.add(carryPart, MC); - - BigDecimal sum6 = sum3 - .multiply(ratesValue, MC) - .multiply(multiplier, MC); - return new CalculationResultHolder(sum1, sum2, sum3, sum4, sum5, sum6); - } - - private Registry findAmtRegistry(Registry registry, String securitySymbol) { - ImdgPredicateBuilder predicateBuilder = registryImdg.predicateBuilder(); - return registryImdg.getSingleObjectByPredicate( - predicateBuilder.and( - predicateBuilder.regex("registryCode", "^PM.*T"), - predicateBuilder.equals("securitySymbol", securitySymbol), - predicateBuilder.equals("tradingClearingRegistryId", registry.getId()), - predicateBuilder.equals("companyId", registry.getCompanyId()) - ) - ); - } - - private Rates findRates(String securitySymbol) { - ImdgPredicateBuilder predicateBuilder = registryImdg.predicateBuilder(); - return ratesImdg.getSingleObjectByPredicate( - predicateBuilder.and( - predicateBuilder.equals("currency", securitySymbol), - predicateBuilder.equals("valueDate", LocalDate.now()), - predicateBuilder.equals("workflowStatus", WorkflowStatus.Active.getKey()) - ) - ); - } - - private Rates findCnyRates(Registry registry) { - ImdgPredicateBuilder predicateBuilder = registryImdg.predicateBuilder(); - return ratesImdg.getSingleObjectByPredicate( - predicateBuilder.and( - predicateBuilder.equals("currency", "CNY"), - predicateBuilder.equals("valueDate", LocalDate.now()), - predicateBuilder.equals("workflowStatus", WorkflowStatus.Active.getKey()) - ) - ); - } - - private BigDecimal swapPriceCalc(String baseCurrency, BigDecimal s, BigDecimal n) { - LocalDate now = LocalDate.now(); - ImdgPredicateBuilder predicateBuilder = ratesImdg.predicateBuilder(); - BigDecimal daysInYear = BigDecimal.valueOf(365); - - Rates rates = ratesImdg.getSingleObjectByPredicate( - predicateBuilder.and( - predicateBuilder.equals("currency", baseCurrency), - predicateBuilder.equals("valueDate", now), - predicateBuilder.equals("workflowStatus", WorkflowStatus.Active.getKey()) - ) - ); - BigDecimal rateValue = rates.getValue(); - return rateValue.multiply(s, MC).multiply(n, MC).divide(daysInYear, MC); - } - - private BigDecimal defineTransferRate(Long currencyPairDictionaryId, String securitySymbol) { - ImdgPredicateBuilder predicateBuilder = riskParameterImdg.predicateBuilder(); - String securitySymbolCondition = securitySymbol.equals("RUB") ? "CNY" : securitySymbol; - RiskParameter riskParameter = riskParameterImdg.getSingleObjectByPredicate( - predicateBuilder.and( - predicateBuilder.equals("settlementType", "T0"), - predicateBuilder.equals("currencyPairId", currencyPairDictionaryId), - predicateBuilder.equals("baseCurrency", securitySymbolCondition) - ) - ); - return riskParameter.getQuoteValue(); - } - - private BigDecimal numberOfDaysOfTransfer() { - LocalDate nextWorkingDay = findNextWorkingDate(); - return BigDecimal.valueOf(nextWorkingDay.compareTo(LocalDate.now())); - } - public static BigDecimal ceilMath(BigDecimal value, BigDecimal significance) { - if (significance == null || significance.compareTo(BigDecimal.ZERO) <= 0) { - throw new IllegalArgumentException("significance must be > 0"); - } - if (value == null) { - throw new IllegalArgumentException("value must not be null"); - } - - BigDecimal divided = value.divide(significance, 0, RoundingMode.CEILING); - return divided.multiply(significance); - } - - private LocalDate findNextWorkingDate() { - LocalDate checkingDate = LocalDate.now().plusDays(1); - - ImdgPredicateBuilder predicateBuilder = clearingCalendarImdg.predicateBuilder(); - while (true) { - ClearingCalendar clearingCalendar = clearingCalendarImdg.getFirstObjectByPredicate( - predicateBuilder.equals("clearingDate", checkingDate) - ); - DayOfWeek dayOfWeek = checkingDate.getDayOfWeek(); - if (isWeekday(dayOfWeek)) { - if (clearingCalendar != null && DayStatus.Workday.equalsByKey(clearingCalendar.getDayStatus())) { - return checkingDate; - } - checkingDate = checkingDate.plusDays(1); - } else { - if (clearingCalendar != null && DayStatus.DayOff.equalsByKey(clearingCalendar.getDayStatus())) { - checkingDate = checkingDate.plusDays(1); - continue; - } - return checkingDate; - } - } - } - - private boolean isWeekday(DayOfWeek dayOfWeek) { - return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY; - } - - public static class CalculationResultHolder { - private BigDecimal sum1; - private BigDecimal sum2; - private BigDecimal sum3; - private BigDecimal sum4; - private BigDecimal sum5; - private BigDecimal sum6; - - public CalculationResultHolder(BigDecimal sum1, - BigDecimal sum2, - BigDecimal sum3, - BigDecimal sum4, - BigDecimal sum5, - BigDecimal sum6) { - this.sum1 = sum1; - this.sum2 = sum2; - this.sum3 = sum3; - this.sum4 = sum4; - this.sum5 = sum5; - this.sum6 = sum6; - } - - public BigDecimal getSum1() { - return sum1; - } - - public BigDecimal getSum2() { - return sum2; - } - - public BigDecimal getSum3() { - return sum3; - } - - public BigDecimal getSum4() { - return sum4; - } - - public BigDecimal getSum5() { - return sum5; - } - - public BigDecimal getSum6() { - return sum6; - } - } } diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/model/CalculationResult.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/model/CalculationResult.java new file mode 100644 index 000000000..84c7940b2 --- /dev/null +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/model/CalculationResult.java @@ -0,0 +1,13 @@ +package ru.spcex.clearing.dflt.management.model; + +import java.math.BigDecimal; + +public record CalculationResult( + BigDecimal sum1, + BigDecimal sum2, + BigDecimal sum3, + BigDecimal sum4, + BigDecimal sum5, + BigDecimal sum6 +) { +} diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/model/CounterpartyInfo.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/model/CounterpartyInfo.java new file mode 100644 index 000000000..57c775f36 --- /dev/null +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/model/CounterpartyInfo.java @@ -0,0 +1,8 @@ +package ru.spcex.clearing.dflt.management.model; + +public record CounterpartyInfo( + Long companyId, + String tradingCode, + Long registryId, + String registryCode +) {} diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/model/SwapContext.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/model/SwapContext.java new file mode 100644 index 000000000..55f7a0cc6 --- /dev/null +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/model/SwapContext.java @@ -0,0 +1,119 @@ +package ru.spcex.clearing.dflt.management.model; + +import ru.clearing.classes.statics.data.misc.Listing; +import ru.clearing.classes.statics.data.misc.Market; +import ru.clearing.classes.statics.data.registry.Registry; +import ru.clearing.classes.statics.data.security.RiskParameter; + +public class SwapContext { + private final Registry registry; + private final String orderCategory; + private final Market market; + private final Listing listing; + private final RiskParameter riskParameter; + private final CounterpartyInfo categoryP; + private final CounterpartyInfo clcc; + private final CounterpartyInfo rgcm; + + private SwapContext(Builder builder) { + this.registry = builder.registry; + this.orderCategory = builder.orderCategory; + this.market = builder.market; + this.listing = builder.listing; + this.riskParameter = builder.riskParameter; + this.categoryP = builder.categoryP; + this.clcc = builder.clcc; + this.rgcm = builder.rgcm; + } + + public Registry getRegistry() { + return registry; + } + + public String getOrderCategory() { + return orderCategory; + } + + public Market getMarket() { + return market; + } + + public Listing getListing() { + return listing; + } + + public RiskParameter getRiskParameter() { + return riskParameter; + } + + public CounterpartyInfo getCategoryP() { + return categoryP; + } + + public CounterpartyInfo getClcc() { + return clcc; + } + + public CounterpartyInfo getRgcm() { + return rgcm; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Registry registry; + private String orderCategory; + private Market market; + private Listing listing; + private RiskParameter riskParameter; + private CounterpartyInfo categoryP; + private CounterpartyInfo clcc; + private CounterpartyInfo rgcm; + + public Builder registry(Registry registry) { + this.registry = registry; + return this; + } + + public Builder orderCategory(String orderCategory) { + this.orderCategory = orderCategory; + return this; + } + + public Builder market(Market market) { + this.market = market; + return this; + } + + public Builder listing(Listing listing) { + this.listing = listing; + return this; + } + + public Builder riskParameter(RiskParameter riskParameter) { + this.riskParameter = riskParameter; + return this; + } + + public Builder categoryP(CounterpartyInfo categoryP) { + this.categoryP = categoryP; + return this; + } + + public Builder clcc(CounterpartyInfo clcc) { + this.clcc = clcc; + return this; + } + + public Builder rgcm(CounterpartyInfo rgcm) { + this.rgcm = rgcm; + return this; + } + + public SwapContext build() { + return new SwapContext(this); + } + } +} diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/ImdgQueryService.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/ImdgQueryService.java new file mode 100644 index 000000000..b161170bb --- /dev/null +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/ImdgQueryService.java @@ -0,0 +1,261 @@ +package ru.spcex.clearing.dflt.management.service; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.Collection; +import java.util.Optional; +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.company.ClearingMemberCategory; +import ru.clearing.classes.statics.data.company.Company; +import ru.clearing.classes.statics.data.company.CompanyRoleSet; +import ru.clearing.classes.statics.data.misc.Listing; +import ru.clearing.classes.statics.data.misc.Market; +import ru.clearing.classes.statics.data.misc.OvernightSettings; +import ru.clearing.classes.statics.data.registry.Registry; +import ru.clearing.classes.statics.data.registry.TradingClearingRegistry; +import ru.clearing.classes.statics.data.security.CurrencyPairSecurity; +import ru.clearing.classes.statics.data.security.Rates; +import ru.clearing.classes.statics.data.security.RiskParameter; +import ru.clearing.platform.dictionary.CurrencyPairDictionary; +import ru.spcex.clearing.dflt.management.model.CounterpartyInfo; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.platform.enumeration.CompanyRole; +import ru.spcex.platform.enumeration.MarketType; +import ru.spcex.platform.enumeration.RegistryStatus; +import ru.spcex.platform.enumeration.WorkflowStatus; +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; + +@Service +public class ImdgQueryService { + private final Imdg categoryImdg; + private final Imdg companyRoleSetImdg; + private final Imdg companyImdg; + private final Imdg tradingClearingRegistryImdg; + private final Imdg currencyPairDictionaryImdg; + private final Imdg currencyPairSecurityImdg; + private final Imdg listingImdg; + private final Imdg marketImdg; + private final Imdg riskParameterImdg; + private final Imdg ratesImdg; + private final Imdg registryImdg; + private final Imdg overnightSettingsImdg; + + + public ImdgQueryService(ImdgProvider imdgProvider) { + this.categoryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class); + this.companyRoleSetImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_CompanyRoleSet, CompanyRoleSet.class); + this.companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class); + this.tradingClearingRegistryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class); + this.currencyPairDictionaryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_CurrencyPairDictionary, CurrencyPairDictionary.class); + this.currencyPairSecurityImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_CurrencyPairSecurity, CurrencyPairSecurity.class); + this.listingImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Listing, Listing.class); + this.marketImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Market, Market.class); + this.riskParameterImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_RiskParameter, RiskParameter.class); + this.ratesImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Rates, Rates.class); + this.registryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class); + this.overnightSettingsImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_OvernightSettings, OvernightSettings.class); + } + public Collection getCurrencyRegistry() { + ImdgPredicateBuilder builder = registryImdg.predicateBuilder(); + ImdgPredicate registryPredicate = builder.and( + builder.regex("registryCode", "^PM.*T"), + builder.not(builder.equals("registryStatus", RegistryStatus.OK.getKey())) + ); + + return registryImdg.getCollectionIdsByPredicate(registryPredicate); + } + + public Optional findOvernightSettingsByCompany(Long companyId) { + ImdgPredicateBuilder builder = overnightSettingsImdg.predicateBuilder(); + ImdgPredicate predicate = builder.and( + builder.equals("market", ru.spcex.platform.enumeration.Market.curr), + builder.equals("companyId", companyId) + ); + OvernightSettings overnightSettings = overnightSettingsImdg.getFirstObjectByPredicate(predicate); + return Optional.ofNullable(overnightSettings); + } + + public String resolveOrderCategory(Long companyId) { + ImdgPredicateBuilder pb = categoryImdg.predicateBuilder(); + ClearingMemberCategory category = categoryImdg.getFirstObjectByPredicate( + pb.equals("companyId", companyId) + ); + return "P".equals(category.getClearingMemberCategory()) ? "PL" : "UK"; + } + + public CounterpartyInfo getCompanyByRole(CompanyRole role) { + ImdgPredicateBuilder pb = companyRoleSetImdg.predicateBuilder(); + CompanyRoleSet roleSet = companyRoleSetImdg.getSingleObjectByPredicate( + pb.equals("companyRole", role) + ); + + Company company = companyImdg.getSingleObjectByID(roleSet.getCompanyId()); + TradingClearingRegistry registry = tradingClearingRegistryImdg.getFirstObjectByPredicate( + pb.equals("companyId", company.getId()) + ); + + return new CounterpartyInfo( + company.getId(), + company.getTradingCode(), + registry != null ? registry.getId() : null, + registry != null ? registry.getCode() : null + ); + } + + public CounterpartyInfo getCompanyByClearingCategory(String clearingCategory) { + ImdgPredicateBuilder pb = categoryImdg.predicateBuilder(); + ClearingMemberCategory category = categoryImdg.getFirstObjectByPredicate( + pb.equals("clearingMemberCategory", clearingCategory) + ); + + Company company = companyImdg.getSingleObjectByID(category.getCompanyId()); + TradingClearingRegistry registry = tradingClearingRegistryImdg.getFirstObjectByPredicate( + pb.equals("companyId", company.getId()) + ); + + return new CounterpartyInfo( + company.getId(), + company.getTradingCode(), + registry != null ? registry.getId() : null, + registry != null ? registry.getCode() : null + ); + } + + public Market getSwapMarket() { + ImdgPredicateBuilder pb = marketImdg.predicateBuilder(); + return marketImdg.getSingleObjectByPredicate(pb.equals("marketType", MarketType.SCSW)); + } + + public Listing findSwapListing(Registry registry, String marketCode) { + return "RUB".equals(registry.getSecuritySymbol()) + ? findListingForRub(registry, marketCode) + : findListingForOtherCurrency(registry, marketCode); + } + + public RiskParameter findRiskParameter(Registry registry) { + return "RUB".equals(registry.getSecuritySymbol()) + ? findRiskParameterForRub() + : findRiskParameterForOtherCurrency(registry); + } + + public Rates getRate(String currency, LocalDate valueDate) { + ImdgPredicateBuilder pb = ratesImdg.predicateBuilder(); + return ratesImdg.getSingleObjectByPredicate( + pb.and( + pb.equals("currency", currency), + pb.equals("valueDate", valueDate), + pb.equals("workflowStatus", WorkflowStatus.Active.getKey()) + ) + ); + } + + public BigDecimal defineTransferRate(Long currencyPairDictionaryId, String securitySymbol) { + ImdgPredicateBuilder pb = riskParameterImdg.predicateBuilder(); + String baseCurrency = "RUB".equals(securitySymbol) ? "CNY" : securitySymbol; + + RiskParameter riskParameter = riskParameterImdg.getSingleObjectByPredicate( + pb.and( + pb.equals("settlementType", "T0"), + pb.equals("currencyPairId", currencyPairDictionaryId), + pb.equals("baseCurrency", baseCurrency) + ) + ); + return riskParameter.getQuoteValue(); + } + + public CurrencyPairDictionary getCurrencyPairDictionaryByListing(Listing listing) { + CurrencyPairSecurity security = currencyPairSecurityImdg.getSingleObjectByID(listing.getSecurityId()); + return currencyPairDictionaryImdg.getSingleObjectByID(security.getCurrencyPairId()); + } + + private Listing findListingForRub(Registry registry, String marketCode) { + ImdgPredicateBuilder pb = currencyPairDictionaryImdg.predicateBuilder(); + + CurrencyPairDictionary pair = currencyPairDictionaryImdg.getFirstObjectByPredicate( + pb.and( + pb.equals("baseCurrency", "CNY"), + pb.equals("quoteCurrency", registry.getSecuritySymbol()) + ) + ); + + Collection securityIds = currencyPairSecurityImdg.getCollectionIdsByPredicate( + pb.equals("currencyPairId", pair.getId()) + ); + + return listingImdg.getSingleObjectByPredicate( + pb.and( + pb.in("securityId", securityIds.toArray(new Long[0])), + pb.equals("market", marketCode) + ) + ); + } + + private Listing findListingForOtherCurrency(Registry registry, String marketCode) { + ImdgPredicateBuilder pb = currencyPairDictionaryImdg.predicateBuilder(); + + CurrencyPairDictionary pair = currencyPairDictionaryImdg.getFirstObjectByPredicate( + pb.and( + pb.equals("baseCurrency", registry.getSecuritySymbol()), + pb.equals("quoteCurrency", "RUB") + ) + ); + + Collection securityIds = currencyPairSecurityImdg.getCollectionIdsByPredicate( + pb.equals("currencyPairId", pair.getId()) + ); + + return listingImdg.getSingleObjectByPredicate( + pb.and( + pb.in("securityId", securityIds.toArray(new Long[0])), + pb.equals("market", marketCode) + ) + ); + } + + private RiskParameter findRiskParameterForRub() { + ImdgPredicateBuilder pb = currencyPairDictionaryImdg.predicateBuilder(); + CurrencyPairDictionary pair = currencyPairDictionaryImdg.getFirstObjectByPredicate( + pb.equals("baseCurrency", "CNY") + ); + + return riskParameterImdg.getSingleObjectByPredicate( + pb.and( + pb.equals("currencyPairId", pair.getId()), + pb.equals("settlementType", "T0") + ) + ); + } + + private RiskParameter findRiskParameterForOtherCurrency(Registry registry) { + ImdgPredicateBuilder pb = currencyPairDictionaryImdg.predicateBuilder(); + CurrencyPairDictionary pair = currencyPairDictionaryImdg.getFirstObjectByPredicate( + pb.equals("baseCurrency", registry.getSecuritySymbol()) + ); + + return riskParameterImdg.getSingleObjectByPredicate( + pb.and( + pb.equals("currencyPairId", pair.getId()), + pb.equals("settlementType", "T0") + ) + ); + } + + public Registry getRegistryById(Long id) { + return registryImdg.getSingleObjectByID(id); + } + + public Registry findAmtRegistry(Registry registry, String securitySymbol) { + ImdgPredicateBuilder pb = registryImdg.predicateBuilder(); + return registryImdg.getSingleObjectByPredicate( + pb.and( + pb.regex("registryCode", "^PM.*T"), + pb.equals("securitySymbol", securitySymbol), + pb.equals("tradingClearingRegistryId", registry.getId()), + pb.equals("companyId", registry.getCompanyId()) + ) + ); + } +} diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/SpotOvernightOrderService.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/SpotOvernightOrderService.java new file mode 100644 index 000000000..7dfdc7f33 --- /dev/null +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/SpotOvernightOrderService.java @@ -0,0 +1,76 @@ +package ru.spcex.clearing.dflt.management.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.misc.Listing; +import ru.clearing.classes.statics.data.misc.Market; +import ru.clearing.classes.statics.data.misc.OrderCurrency; +import ru.clearing.classes.statics.data.registry.Registry; +import ru.clearing.classes.statics.data.security.RiskParameter; +import ru.spcex.clearing.dflt.management.model.CounterpartyInfo; +import ru.spcex.clearing.dflt.management.model.SwapContext; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.platform.enumeration.CompanyRole; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgProvider; + +@Service +public class SpotOvernightOrderService { + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final ImdgQueryService imdgQueryService; + private final Imdg orderCurrencyImdg; + private final SwapOrderTemplateBuilder templateBuilder; + private final SwapCalculationService swapCalculationService; + + public SpotOvernightOrderService(ImdgProvider imdgProvider, + ImdgQueryService imdgQueryService, + SwapOrderTemplateBuilder templateBuilder, + SwapCalculationService swapCalculationService) { + this.imdgQueryService = imdgQueryService; + this.orderCurrencyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_OrderCurrency, OrderCurrency.class); + this.templateBuilder = templateBuilder; + this.swapCalculationService = swapCalculationService; + } + + public void process(Registry registry) { + String orderCategory = imdgQueryService.resolveOrderCategory(registry.getCompanyId()); + + Market market = imdgQueryService.getSwapMarket(); + Listing listing = imdgQueryService.findSwapListing(registry, market.getCode()); + RiskParameter riskParameter = imdgQueryService.findRiskParameter(registry); + CounterpartyInfo categoryP = imdgQueryService.getCompanyByClearingCategory("P"); + CounterpartyInfo clcc = imdgQueryService.getCompanyByRole(CompanyRole.CLCC); + CounterpartyInfo rgcm = imdgQueryService.getCompanyByRole(CompanyRole.RGCM); + + SwapContext context = SwapContext.builder() + .registry(registry) + .orderCategory(orderCategory) + .market(market) + .listing(listing) + .riskParameter(riskParameter) + .categoryP(categoryP) + .clcc(clcc) + .rgcm(rgcm) + .build(); + +// OrderCurrency primaryTransfer = templateBuilder.buildPrimaryTransfer(context); +// OrderCurrency counterTransfer = templateBuilder.buildCounterTransfer(context); +// OrderCurrency primaryBalancing = templateBuilder.buildPrimaryBalancing(context); +// OrderCurrency counterBalancing = templateBuilder.buildCounterBalancing(context); +// +// swapCalculationService.calcAndEnrich(primaryTransfer, context); +// swapCalculationService.calcAndEnrich(counterTransfer, context); +// swapCalculationService.calcAndEnrich(primaryBalancing, context); +// swapCalculationService.calcAndEnrich(counterBalancing, context); +// +// orderCurrencyImdg.insert(primaryTransfer); +// orderCurrencyImdg.insert(counterTransfer); +// orderCurrencyImdg.insert(primaryBalancing); +// orderCurrencyImdg.insert(counterBalancing); + + log.info("Spot orders created for registryId={}", registry.getId()); + } + +} diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/SwapCalculationService.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/SwapCalculationService.java new file mode 100644 index 000000000..a749d696e --- /dev/null +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/SwapCalculationService.java @@ -0,0 +1,110 @@ +package ru.spcex.clearing.dflt.management.service; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.time.LocalDate; +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.misc.Listing; +import ru.clearing.classes.statics.data.misc.OrderCurrency; +import ru.clearing.classes.statics.data.registry.Registry; +import ru.clearing.classes.statics.data.security.Rates; +import ru.clearing.platform.dictionary.CurrencyPairDictionary; +import ru.spcex.clearing.dflt.management.model.CalculationResult; +import ru.spcex.clearing.dflt.management.model.SwapContext; + +@Service +public class SwapCalculationService { + private static final MathContext MC = new MathContext(20, RoundingMode.HALF_UP); + + private final ImdgQueryService imdgQueryService; + private final TransferDateService transferDateService; + + public SwapCalculationService(ImdgQueryService imdgQueryService, + TransferDateService transferDateService) { + this.imdgQueryService = imdgQueryService; + this.transferDateService = transferDateService; + } + + public void calcAndEnrich(OrderCurrency order, + SwapContext context) { + Registry registry = context.getRegistry(); + Listing listing = context.getListing(); + + CurrencyPairDictionary pair = imdgQueryService.getCurrencyPairDictionaryByListing(listing); + BigDecimal s = imdgQueryService.defineTransferRate(pair.getId(), registry.getSecuritySymbol()); + BigDecimal n = transferDateService.numberOfDaysOfTransfer(); + + CalculationResult result = isRub(registry) + ? calculationSwapRubSums(registry, listing.getLotSize().intValue(), s, n) + : calculationSwapSums(registry, listing.getLotSize().intValue(), s, n); + + BigDecimal quantityLot = result.sum3().abs(); + BigDecimal price = swapPriceCalc(pair.getBaseCurrency(), s, n); + + order.setPrice(price); + order.setQuantityLot(quantityLot); + } + + private CalculationResult calculationSwapSums(Registry registry, Integer lotSize, BigDecimal s, BigDecimal n) { + BigDecimal planPosition = registry.getBalance(); + BigDecimal sum1 = planPosition.abs(); + + Registry rubRegistry = imdgQueryService.findAmtRegistry(registry, "RUB"); + BigDecimal planPositionRub = rubRegistry.getBalance(); + + Rates rates = imdgQueryService.getRate(registry.getSecuritySymbol(), LocalDate.now()); + BigDecimal rateValue = rates.getValue(); + + BigDecimal sum2 = planPositionRub.divide(rateValue, lotSize, RoundingMode.HALF_DOWN); + BigDecimal sum3 = sum1.setScale(0, RoundingMode.CEILING).min(sum2); + BigDecimal sum4 = sum3.multiply(rateValue, MC).negate(); + BigDecimal sum5 = sum3.negate(); + + BigDecimal multiplier = BigDecimal.ONE.add( + s.divide(BigDecimal.valueOf(365), MC).multiply(n, MC), + MC + ); + + BigDecimal sum6 = sum3.multiply(rateValue, MC).multiply(multiplier, MC); + + return new CalculationResult(sum1, sum2, sum3, sum4, sum5, sum6); + } + + private CalculationResult calculationSwapRubSums(Registry registry, Integer lotSize, BigDecimal s, BigDecimal n) { + BigDecimal planPosition = registry.getBalance(); + Rates cnyRate = imdgQueryService.getRate(registry.getSecuritySymbol(), LocalDate.now()); + BigDecimal rateValue = cnyRate.getValue(); + + BigDecimal sum1 = planPosition.divide(rateValue, MC); + + Registry cnyRegistry = imdgQueryService.findAmtRegistry(registry, "CNY"); + BigDecimal planPositionCny = cnyRegistry.getBalance(); + + BigDecimal sum2 = planPositionCny.divide(rateValue, lotSize, RoundingMode.HALF_DOWN); + BigDecimal sum3 = sum1.setScale(0, RoundingMode.CEILING).min(sum2).negate(); + BigDecimal sum4 = sum3.multiply(rateValue, MC).negate(); + BigDecimal sum5 = sum3.negate(); + + BigDecimal multiplier = BigDecimal.ONE.add( + s.divide(BigDecimal.valueOf(365), MC).multiply(n, MC), + MC + ); + + BigDecimal sum6 = sum3.multiply(rateValue, MC).multiply(multiplier, MC); + + return new CalculationResult(sum1, sum2, sum3, sum4, sum5, sum6); + } + + private BigDecimal swapPriceCalc(String baseCurrency, BigDecimal s, BigDecimal n) { + Rates rate = imdgQueryService.getRate(baseCurrency, LocalDate.now()); + return rate.getValue() + .multiply(s, MC) + .multiply(n, MC) + .divide(BigDecimal.valueOf(365), MC); + } + + private boolean isRub(Registry registry) { + return "RUB".equals(registry.getSecuritySymbol()); + } +} diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/SwapOrderTemplateBuilder.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/SwapOrderTemplateBuilder.java new file mode 100644 index 000000000..e903955d7 --- /dev/null +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/SwapOrderTemplateBuilder.java @@ -0,0 +1,231 @@ +package ru.spcex.clearing.dflt.management.service; + +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.misc.Listing; +import ru.clearing.classes.statics.data.misc.Market; +import ru.clearing.classes.statics.data.misc.OrderCurrency; +import ru.clearing.classes.statics.data.registry.Registry; +import ru.clearing.classes.statics.data.security.RiskParameter; +import ru.spcex.clearing.dflt.management.model.CounterpartyInfo; +import ru.spcex.clearing.dflt.management.model.SwapContext; +import ru.spcex.platform.enumeration.Side; + +@Service +public class SwapOrderTemplateBuilder { + private final ImdgQueryService imdgQueryService; + + public SwapOrderTemplateBuilder(ImdgQueryService imdgQueryService) { + this.imdgQueryService = imdgQueryService; + } + + public OrderCurrency buildPrimaryTransfer(SwapContext context) { + OrderCurrency orderCurrency; + switch (context.getOrderCategory()) { + case "PL" -> orderCurrency = buildPrimaryTransferPl(context); + case "UK" -> orderCurrency = buildPrimaryTransferUk(context); + default -> throw new IllegalArgumentException("Unsupported order category: " + context.getOrderCategory()); + } + Side side = resolvePrimaryTransferSide(context.getRegistry()); + enrichOrder(orderCurrency, context, side, "overnight"); + return orderCurrency; + } + + public OrderCurrency buildCounterTransfer(SwapContext context) { + OrderCurrency orderCurrency; + switch (context.getOrderCategory()) { + case "PL" -> orderCurrency = buildCounterTransferPl(context); + case "UK" -> orderCurrency = buildCounterTransferUk(context); + default -> throw new IllegalArgumentException("Unsupported order category: " + context.getOrderCategory()); + }; + Side side = resolveCounterTransferSide(context.getRegistry()); + enrichOrder(orderCurrency, context, side, "overnight"); + return orderCurrency; + } + + public OrderCurrency buildPrimaryBalancing(SwapContext context) { + OrderCurrency orderCurrency; + switch (context.getOrderCategory()) { + case "PL" -> orderCurrency = buildPrimaryBalancingPl(context); + case "UK" -> orderCurrency = buildPrimaryBalancingUk(context); + default -> throw new IllegalArgumentException("Unsupported order category: " + context.getOrderCategory()); + }; + Side side = resolvePrimaryBalancingSide(context.getRegistry()); + enrichOrder(orderCurrency, context, side, "balance"); + return orderCurrency; + } + + public OrderCurrency buildCounterBalancing(SwapContext context) { + OrderCurrency orderCurrency; + switch (context.getOrderCategory()) { + case "PL" -> orderCurrency = buildCounterBalancingPl(context); + case "UK" -> orderCurrency = buildCounterBalancingUk(context); + default -> throw new IllegalArgumentException("Unsupported order category: " + context.getOrderCategory()); + }; + Side side = resolveCounterBalancingSide(context.getRegistry()); + enrichOrder(orderCurrency, context, side, "balance"); + return orderCurrency; + } + + private OrderCurrency buildPrimaryTransferUk(SwapContext context) { + CounterpartyInfo clcc = context.getClcc(); + Registry registry = context.getRegistry(); + + OrderCurrency order = new OrderCurrency(); + order.setAccount(registry.getTradingClearingRegistry()); + order.setTradingClearingRegistryId(registry.getTradingClearingRegistryId()); + order.setCompanyId(registry.getCompanyId()); + order.setTradingCode(registry.getTradingCode()); + order.setCounterPartyId(clcc.companyId()); + order.setCounterTradingCode(clcc.tradingCode()); + order.setType("CMOV"); + return order; + } + + private OrderCurrency buildCounterTransferUk(SwapContext context) { + CounterpartyInfo clcc = context.getClcc(); + Registry registry = context.getRegistry(); + + OrderCurrency order = new OrderCurrency(); + order.setAccount(clcc.registryCode()); + order.setTradingClearingRegistryId(clcc.registryId()); + order.setCompanyId(clcc.companyId()); + order.setTradingCode(clcc.tradingCode()); + order.setCounterPartyId(registry.getCompanyId()); + order.setCounterTradingCode(registry.getTradingCode()); + order.setType("CMOV"); + return order; + } + + private OrderCurrency buildPrimaryBalancingUk(SwapContext context) { + CounterpartyInfo categoryP = context.getCategoryP(); + CounterpartyInfo clcc = context.getClcc(); + + OrderCurrency order = new OrderCurrency(); + order.setAccount(categoryP.registryCode()); + order.setTradingClearingRegistryId(categoryP.registryId()); + order.setCompanyId(categoryP.companyId()); + order.setTradingCode(categoryP.tradingCode()); + order.setCounterPartyId(clcc.companyId()); + order.setCounterTradingCode(clcc.tradingCode()); + order.setType("CMBL"); + return order; + } + + private OrderCurrency buildCounterBalancingUk(SwapContext context) { + CounterpartyInfo categoryP = context.getCategoryP(); + CounterpartyInfo clcc = context.getClcc(); + + OrderCurrency order = new OrderCurrency(); + order.setAccount(clcc.registryCode()); + order.setTradingClearingRegistryId(clcc.registryId()); + order.setCompanyId(clcc.companyId()); + order.setTradingCode(clcc.tradingCode()); + order.setCounterPartyId(categoryP.companyId()); + order.setCounterTradingCode(categoryP.tradingCode()); + order.setType("CMBL"); + return order; + } + + private OrderCurrency buildPrimaryTransferPl(SwapContext context) { + CounterpartyInfo clcc = context.getClcc(); + Registry registry = context.getRegistry(); + + OrderCurrency order = new OrderCurrency(); + order.setAccount(registry.getTradingClearingRegistry()); + order.setTradingClearingRegistryId(registry.getTradingClearingRegistryId()); + order.setCompanyId(registry.getCompanyId()); + order.setTradingCode(registry.getTradingCode()); + order.setCounterPartyId(clcc.companyId()); + order.setCounterTradingCode(clcc.tradingCode()); + order.setType("PLOV"); + return order; + } + + private OrderCurrency buildCounterTransferPl(SwapContext context) { + CounterpartyInfo clcc = context.getClcc(); + Registry registry = context.getRegistry(); + + OrderCurrency order = new OrderCurrency(); + order.setAccount(clcc.registryCode()); + order.setTradingClearingRegistryId(clcc.registryId()); + order.setCompanyId(clcc.companyId()); + order.setTradingCode(clcc.tradingCode()); + order.setCounterPartyId(registry.getCompanyId()); + order.setCounterTradingCode(registry.getTradingCode()); + order.setType("PLOV"); + return order; + } + + private OrderCurrency buildPrimaryBalancingPl(SwapContext context) { + CounterpartyInfo rgcm = context.getRgcm(); + CounterpartyInfo clcc = context.getClcc(); + + OrderCurrency order = new OrderCurrency(); + order.setAccount(rgcm.registryCode()); + order.setTradingClearingRegistryId(rgcm.registryId()); + order.setCompanyId(rgcm.companyId()); + order.setTradingCode(rgcm.tradingCode()); + order.setCounterPartyId(clcc.companyId()); + order.setCounterTradingCode(clcc.tradingCode()); + order.setType("PLBL"); + return order; + } + + private OrderCurrency buildCounterBalancingPl(SwapContext context) { + CounterpartyInfo clcc = context.getClcc(); + CounterpartyInfo rgcm = context.getRgcm(); + + OrderCurrency order = new OrderCurrency(); + order.setAccount(clcc.registryCode()); + order.setTradingClearingRegistryId(clcc.registryId()); + order.setCompanyId(clcc.companyId()); + order.setTradingCode(clcc.tradingCode()); + order.setCounterPartyId(rgcm.companyId()); + order.setCounterTradingCode(rgcm.tradingCode()); + order.setType("PLBL"); + return order; + } + + public void enrichOrder(OrderCurrency order, + SwapContext context, + Side side, + String comment) { + Registry registry = context.getRegistry(); + Listing listing = context.getListing(); + Market market = context.getMarket(); + RiskParameter riskParameter = context.getRiskParameter(); + + order.setOvernightType("SWAP"); + order.setMarket(market.getCode()); + order.setListingId(listing.getId()); + order.setSecuritySymbol(listing.getSymbolCode()); + order.setSecurityId(listing.getSecurityId()); + order.setLotSize(listing.getLotSize()); + order.setRate(riskParameter.getQuoteValue()); + order.setSide(side.getKey()); + order.setSettleCode("T1"); + order.setStatus("CRET"); + order.setComment(comment); + order.setRegistryId(registry.getId()); + } + + private Side resolvePrimaryTransferSide(Registry registry) { + return isRub(registry) ? Side.BUY : Side.SELL; + } + + private Side resolveCounterTransferSide(Registry registry) { + return isRub(registry) ? Side.SELL : Side.BUY; + } + + private Side resolvePrimaryBalancingSide(Registry registry) { + return isRub(registry) ? Side.SELL : Side.BUY; + } + + private Side resolveCounterBalancingSide(Registry registry) { + return isRub(registry) ? Side.BUY : Side.SELL; + } + + private boolean isRub(Registry registry) { + return "RUB".equals(registry.getSecuritySymbol()); + } +} diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/SwapOvernightOrderService.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/SwapOvernightOrderService.java new file mode 100644 index 000000000..381a84774 --- /dev/null +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/SwapOvernightOrderService.java @@ -0,0 +1,76 @@ +package ru.spcex.clearing.dflt.management.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.misc.Listing; +import ru.clearing.classes.statics.data.misc.Market; +import ru.clearing.classes.statics.data.misc.OrderCurrency; +import ru.clearing.classes.statics.data.registry.Registry; +import ru.clearing.classes.statics.data.security.RiskParameter; +import ru.spcex.clearing.dflt.management.model.CounterpartyInfo; +import ru.spcex.clearing.dflt.management.model.SwapContext; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.platform.enumeration.CompanyRole; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgProvider; + +@Service +public class SwapOvernightOrderService { + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final ImdgQueryService imdgQueryService; + private final Imdg orderCurrencyImdg; + private final SwapOrderTemplateBuilder templateBuilder; + private final SwapCalculationService swapCalculationService; + + public SwapOvernightOrderService(ImdgProvider imdgProvider, + ImdgQueryService imdgQueryService, + SwapOrderTemplateBuilder templateBuilder, + SwapCalculationService swapCalculationService) { + this.imdgQueryService = imdgQueryService; + this.orderCurrencyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_OrderCurrency, OrderCurrency.class); + this.templateBuilder = templateBuilder; + this.swapCalculationService = swapCalculationService; + } + + public void process(Registry registry) { + String orderCategory = imdgQueryService.resolveOrderCategory(registry.getCompanyId()); + + Market market = imdgQueryService.getSwapMarket(); + Listing listing = imdgQueryService.findSwapListing(registry, market.getCode()); + RiskParameter riskParameter = imdgQueryService.findRiskParameter(registry); + CounterpartyInfo categoryP = imdgQueryService.getCompanyByClearingCategory("P"); + CounterpartyInfo clcc = imdgQueryService.getCompanyByRole(CompanyRole.CLCC); + CounterpartyInfo rgcm = imdgQueryService.getCompanyByRole(CompanyRole.RGCM); + + SwapContext context = SwapContext.builder() + .registry(registry) + .orderCategory(orderCategory) + .market(market) + .listing(listing) + .riskParameter(riskParameter) + .categoryP(categoryP) + .clcc(clcc) + .rgcm(rgcm) + .build(); + + OrderCurrency primaryTransfer = templateBuilder.buildPrimaryTransfer(context); + OrderCurrency counterTransfer = templateBuilder.buildCounterTransfer(context); + OrderCurrency primaryBalancing = templateBuilder.buildPrimaryBalancing(context); + OrderCurrency counterBalancing = templateBuilder.buildCounterBalancing(context); + + swapCalculationService.calcAndEnrich(primaryTransfer, context); + swapCalculationService.calcAndEnrich(counterTransfer, context); + swapCalculationService.calcAndEnrich(primaryBalancing, context); + swapCalculationService.calcAndEnrich(counterBalancing, context); + + orderCurrencyImdg.insert(primaryTransfer); + orderCurrencyImdg.insert(counterTransfer); + orderCurrencyImdg.insert(primaryBalancing); + orderCurrencyImdg.insert(counterBalancing); + + log.info("Swap orders created for registryId={}", registry.getId()); + } + +} diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/TransferDateService.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/TransferDateService.java new file mode 100644 index 000000000..f76c242ee --- /dev/null +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/service/TransferDateService.java @@ -0,0 +1,56 @@ +package ru.spcex.clearing.dflt.management.service; + +import java.math.BigDecimal; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.scheduler.ClearingCalendar; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.platform.enumeration.DayStatus; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; + +@Service +public class TransferDateService { + private final Imdg clearingCalendarImdg; + + public TransferDateService(ImdgProvider imdgProvider) { + this.clearingCalendarImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingCalendar, ClearingCalendar.class); + } + + public LocalDate findNextWorkingDate() { + LocalDate checkingDate = LocalDate.now().plusDays(1); + ImdgPredicateBuilder pb = clearingCalendarImdg.predicateBuilder(); + + while (true) { + ClearingCalendar calendarDay = clearingCalendarImdg.getFirstObjectByPredicate( + pb.equals("clearingDate", checkingDate) + ); + + if (isWeekday(checkingDate.getDayOfWeek())) { + // Будний день рабочий, если НЕТ явной записи DOFF + if (calendarDay == null || !DayStatus.DayOff.equalsByKey(calendarDay.getDayStatus())) { + return checkingDate; + } + } else { + // Выходной рабочий, только если ЕСТЬ явная запись BDAY + if (calendarDay != null && DayStatus.Workday.equalsByKey(calendarDay.getDayStatus())) { + return checkingDate; + } + } + + checkingDate = checkingDate.plusDays(1); + } + } + + public BigDecimal numberOfDaysOfTransfer() { + long days = ChronoUnit.DAYS.between(LocalDate.now(), findNextWorkingDate()); + return BigDecimal.valueOf(days); + } + + private boolean isWeekday(DayOfWeek dayOfWeek) { + return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY; + } +}