From 1558c43e6554c6c7f4e2e272bc0e96d71609ac64 Mon Sep 17 00:00:00 2001 From: Julius Lauterbach Date: Wed, 9 Apr 2025 13:59:32 +0200 Subject: [PATCH 1/5] removed default values for product xml generation, update JAXBTests and PlainSwapEditorHandler to enhance logging and adjust XML file references --- .../product/xml/PlainSwapEditorHandler.java | 23 ++++++++--- .../smartderivativecontract.xml | 2 +- ...ntract_simulated_historical_marketdata.xml | 2 +- .../smartderivativecontract_with_rics.xml | 4 +- .../PlainSwapOperationRequest.yml | 8 ++-- .../smartcontract/product/xml/JAXBTests.java | 41 +++++++++---------- 6 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/main/java/net/finmath/smartcontract/product/xml/PlainSwapEditorHandler.java b/src/main/java/net/finmath/smartcontract/product/xml/PlainSwapEditorHandler.java index e9c9b145f..2c0c6c8de 100644 --- a/src/main/java/net/finmath/smartcontract/product/xml/PlainSwapEditorHandler.java +++ b/src/main/java/net/finmath/smartcontract/product/xml/PlainSwapEditorHandler.java @@ -187,15 +187,26 @@ public PlainSwapEditorHandler(final PlainSwapOperationRequest plainSwapOperation fixedLeg.receiverPartyReference.href = smartDerivativeContract.underlyings.underlying.dataDocument.party.get(1); } - - floatingLeg.resetDates.fixingDates.periodMultiplier = BigInteger.valueOf(plainSwapOperationRequest.getFloatingFixingDayOffset().longValue()); + if(plainSwapOperationRequest.getFloatingFixingDayOffset() != null){ + logger.info("floatingFixingDayOffSet manually set to: '{}'", plainSwapOperationRequest.getFloatingFixingDayOffset()); + floatingLeg.resetDates.fixingDates.periodMultiplier = BigInteger.valueOf(plainSwapOperationRequest.getFloatingFixingDayOffset().longValue()); + } logger.info("Reading back floating fixing date offset: {}", floatingLeg.resetDates.fixingDates.periodMultiplier); - floatingLeg.calculationPeriodAmount.calculation.dayCountFraction.value = plainSwapOperationRequest.getFloatingDayCountFraction(); + if(plainSwapOperationRequest.getFloatingDayCountFraction() != null && !plainSwapOperationRequest.getFloatingDayCountFraction().isEmpty()) { + logger.info("floatingDayCountFraction manually set to: '{}'", plainSwapOperationRequest.getFloatingDayCountFraction()); + floatingLeg.calculationPeriodAmount.calculation.dayCountFraction.value = plainSwapOperationRequest.getFloatingDayCountFraction(); + } logger.info("Reading back floating day count fraction: {}", floatingLeg.calculationPeriodAmount.calculation.dayCountFraction.value); - ((FloatingRateCalculation) floatingLeg.calculationPeriodAmount.calculation.getRateCalculation().getValue()).floatingRateIndex.value = plainSwapOperationRequest.getFloatingRateIndex(); + if(plainSwapOperationRequest.getFloatingRateIndex() != null && !plainSwapOperationRequest.getFloatingRateIndex().isEmpty()) { + logger.info("floatingRateIndex manually set to: '{}'", plainSwapOperationRequest.getFloatingRateIndex()); + ((FloatingRateCalculation) floatingLeg.calculationPeriodAmount.calculation.getRateCalculation().getValue()).floatingRateIndex.value = plainSwapOperationRequest.getFloatingRateIndex(); + } logger.info("Reading back floating rate index: {}", ((FloatingRateCalculation) floatingLeg.calculationPeriodAmount.calculation.getRateCalculation().getValue()).floatingRateIndex.value); - fixedLeg.calculationPeriodAmount.calculation.dayCountFraction.value = plainSwapOperationRequest.getFixedDayCountFraction(); + if(plainSwapOperationRequest.getFixedDayCountFraction() != null && !plainSwapOperationRequest.getFixedDayCountFraction().isEmpty()) { + logger.info("fixedDayCountFraction manually set to: '{}'", plainSwapOperationRequest.getFixedDayCountFraction()); + fixedLeg.calculationPeriodAmount.calculation.dayCountFraction.value = plainSwapOperationRequest.getFixedDayCountFraction(); + } logger.info("Reading back fixed day count fraction {}", fixedLeg.calculationPeriodAmount.calculation.dayCountFraction.value); fixedLeg.calculationPeriodAmount.calculation.fixedRateSchedule.initialValue = BigDecimal.valueOf(plainSwapOperationRequest.getFixedRate()).setScale(12, RoundingMode.HALF_EVEN).divide(BigDecimal.valueOf(100L).setScale(12, RoundingMode.HALF_EVEN), RoundingMode.HALF_EVEN); logger.info("Reading back fixed rate: {}", fixedLeg.calculationPeriodAmount.calculation.fixedRateSchedule.initialValue); @@ -295,6 +306,8 @@ private static void setSdcSettlementHeaderProvidedSymbols(final PlainSwapOperati } private static void setSdcSettlementHeaderTemplate(final PlainSwapOperationRequest plainSwapOperationRequest, final Smartderivativecontract sdc, Smartderivativecontract templateContract) { + logger.info("setSdcSettlementHeaderTemplate with template"); + Smartderivativecontract.Settlement settlementHeader = new Smartderivativecontract.Settlement(); settlementHeader.setSettlementDateInitial(plainSwapOperationRequest.getTradeDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "T12:00:00"); diff --git a/src/main/resources/net.finmath.smartcontract.product.xml/smartderivativecontract.xml b/src/main/resources/net.finmath.smartcontract.product.xml/smartderivativecontract.xml index 8fb83fdb8..680925e91 100644 --- a/src/main/resources/net.finmath.smartcontract.product.xml/smartderivativecontract.xml +++ b/src/main/resources/net.finmath.smartcontract.product.xml/smartderivativecontract.xml @@ -10,7 +10,7 @@ net.finmath finmath-smart-derivative-contract - 0.1.8 + 1.2.5-SNAPSHOT diff --git a/src/main/resources/net.finmath.smartcontract.product.xml/smartderivativecontract_simulated_historical_marketdata.xml b/src/main/resources/net.finmath.smartcontract.product.xml/smartderivativecontract_simulated_historical_marketdata.xml index ac136ab78..14e0ea29f 100644 --- a/src/main/resources/net.finmath.smartcontract.product.xml/smartderivativecontract_simulated_historical_marketdata.xml +++ b/src/main/resources/net.finmath.smartcontract.product.xml/smartderivativecontract_simulated_historical_marketdata.xml @@ -10,7 +10,7 @@ net.finmath finmath-smart-derivative-contract - 0.1.8 + 1.2.5-SNAPSHOT diff --git a/src/main/resources/net.finmath.smartcontract.product.xml/smartderivativecontract_with_rics.xml b/src/main/resources/net.finmath.smartcontract.product.xml/smartderivativecontract_with_rics.xml index f5c6be6b1..faf1f874b 100644 --- a/src/main/resources/net.finmath.smartcontract.product.xml/smartderivativecontract_with_rics.xml +++ b/src/main/resources/net.finmath.smartcontract.product.xml/smartderivativecontract_with_rics.xml @@ -10,7 +10,7 @@ net.finmath finmath-smart-derivative-contract - 0.1.8 + 1.2.5-SNAPSHOT @@ -585,7 +585,7 @@ - EUR-LIBOR-BBA + EURIBOR 6M 6 M diff --git a/src/main/resources/schemas/openapi-schemas/PlainSwapOperationRequest.yml b/src/main/resources/schemas/openapi-schemas/PlainSwapOperationRequest.yml index 0c3ee7bbb..59ecb0f04 100644 --- a/src/main/resources/schemas/openapi-schemas/PlainSwapOperationRequest.yml +++ b/src/main/resources/schemas/openapi-schemas/PlainSwapOperationRequest.yml @@ -65,23 +65,23 @@ properties: format: double fixedDayCountFraction: type: string - default: "30E/360" + #default: "30E/360" fixedPaymentFrequency: $ref: "PaymentFrequency.yml" floatingPayingParty: $ref: "Counterparty.yml" floatingRateIndex: type: string - default: "EURIBOR 6M" + #default: "EURIBOR 6M" floatingDayCountFraction: type: string - default: "ACT/360" + #default: "ACT/360" floatingFixingDayOffset: type: integer format: int32 minimum: -2 maximum: 2 - default: -2 + #default: -2 floatingPaymentFrequency: $ref: "PaymentFrequency.yml" valuationSymbols: diff --git a/src/test/java/net/finmath/smartcontract/product/xml/JAXBTests.java b/src/test/java/net/finmath/smartcontract/product/xml/JAXBTests.java index ee420e832..f263a1506 100644 --- a/src/test/java/net/finmath/smartcontract/product/xml/JAXBTests.java +++ b/src/test/java/net/finmath/smartcontract/product/xml/JAXBTests.java @@ -160,7 +160,7 @@ void jaxBPlainTest() throws java.lang.Exception { @Test void handlerTest() throws java.lang.Exception { - final String generatorPath = "net.finmath.smartcontract.product.xml/smartderivativecontract.xml"; + final String generatorPath = "net.finmath.smartcontract.product.xml/smartderivativecontract_with_rics.xml"; final String generatorFile = new String(JAXBTests.class.getClassLoader().getResourceAsStream(generatorPath).readAllBytes(), StandardCharsets.UTF_8); final String marketDataProvider = "refinitiv"; @@ -172,6 +172,8 @@ void handlerTest() throws java.lang.Exception { PlainSwapEditorHandler handler = new PlainSwapEditorHandler(request, generatorFile, schemaPath, projectVersion); String fpml = handler.getContractAsXmlString(); + System.out.println(fpml); + //final String marketData = new String(JAXBTests.class.getClassLoader().getResourceAsStream("net/finmath/smartcontract/valuation/client/legacy/md_testset_refinitiv.xml").readAllBytes(), StandardCharsets.UTF_8); final String marketData = new String(JAXBTests.class.getClassLoader().getResourceAsStream("net/finmath/smartcontract/valuation/client/md_testset_rics.xml").readAllBytes(), StandardCharsets.UTF_8); @@ -186,26 +188,29 @@ void handlerTest() throws java.lang.Exception { System.out.println(valuationResult); } + //commented out values are optional, if not set they are retrieved via the defined template private PlainSwapOperationRequest generateRequest(String marketDataProvider) throws java.lang.Exception { - + /* ObjectMapper objectMapper = JsonMapper.builder() .addModule(new JavaTimeModule()) .build(); final String fullSymbolListFromTemplate = new ClassPathResource( "references" + File.separator + "template2_symbolslist.json").getContentAsString( - StandardCharsets.UTF_8); + StandardCharsets.UTF_8);*/ final Counterparty firstCounterparty = new Counterparty().baseUrl("aaa").bicCode("ABCDXXXX") .fullName("PartyDoubleTest").dltAddress(PARTY1_DLT_ADDRESS); - final PaymentFrequency floatingPaymentFrequency = new PaymentFrequency().period("M").periodMultiplier(6) + /*final PaymentFrequency floatingPaymentFrequency = new PaymentFrequency().period("M").periodMultiplier(6) .fullName("Semiannual"); final PaymentFrequency fixedPaymentFrequency = new PaymentFrequency().period("Y").periodMultiplier(1) - .fullName("Annual"); + .fullName("Annual");*/ final Counterparty secondCounterparty = new Counterparty().baseUrl("bbb").bicCode("EFDGXXXX") .fullName("PartyTest").dltAddress(PARTY2_DLT_ADDRESS); - final PlainSwapOperationRequest plainSwapOperationRequest = new PlainSwapOperationRequest().firstCounterparty( - firstCounterparty).secondCounterparty(secondCounterparty).marginBufferAmount(30000.0) + final PlainSwapOperationRequest plainSwapOperationRequest = new PlainSwapOperationRequest() + .firstCounterparty(firstCounterparty) + .secondCounterparty(secondCounterparty) + .marginBufferAmount(30000.0) .terminationFeeAmount( 10000.0) .currency("EUR") @@ -244,25 +249,19 @@ private PlainSwapOperationRequest generateRequest(String marketDataProvider) thr .floatingPayingParty( firstCounterparty) .fixedRate(3.95) - .fixedDayCountFraction( - "30E/360") - .fixedPaymentFrequency( - fixedPaymentFrequency) - .floatingRateIndex( - "EURIBOR 6M") - .floatingDayCountFraction( - "ACT/360") - .floatingFixingDayOffset( - -2) - .floatingPaymentFrequency( - floatingPaymentFrequency) + //.fixedDayCountFraction("30E/360") + //.fixedPaymentFrequency(fixedPaymentFrequency) + //.floatingRateIndex("EURIBOR 6M") + //.floatingDayCountFraction("ACT/360") + //.floatingFixingDayOffset(-2) + //.floatingPaymentFrequency(floatingPaymentFrequency) .notionalAmount( 10000000.00) - .valuationSymbols( + /*.valuationSymbols( objectMapper.readerForListOf( FrontendItemSpec.class) .readValue( - fullSymbolListFromTemplate)) + fullSymbolListFromTemplate))*/ .marketDataProvider(marketDataProvider) .receiverPartyID("party2") .fixPayerPartyID("party2"); From 51fae2cccf37d691792ebf5d6405ea52236be7d1 Mon Sep 17 00:00:00 2001 From: Raphael Prandtl Date: Mon, 4 Aug 2025 11:05:52 +0200 Subject: [PATCH 2/5] Adding the possibility to obtain a swap's flow schedule and refining the handling of overnight rates. --- pom.xml | 5 +- .../smartcontract/model/ExceptionId.java | 1 + .../flowschedule/FlowScheduleSwap.java | 116 ++++++++ .../flowschedule/FlowScheduleSwapLeg.java | 47 +++ .../FlowScheduleSwapLegPeriod.java | 107 +++++++ .../product/xml/SDCXMLParser.java | 9 +- .../FlowScheduleCalculator.java | 269 ++++++++++++++++++ .../implementation/MarginCalculator.java | 2 +- .../curvecalibration/CalibrationDataset.java | 9 +- .../CalibrationParserDataItems.java | 2 + .../CalibrationSpecProviderON.java | 38 +++ .../curvecalibration/Calibrator.java | 13 +- .../ValuationOraclePlainSwap.java | 80 +++--- .../controllers/ValuationController.java | 19 ++ src/main/resources/api.yml | 27 ++ .../FlowScheduleSwapRequest.yml | 9 + .../FlowScheduleSwapResponse.yml | 6 + .../openapi-schemas/PaymentSchedule.yml | 3 - .../valuation/schedule/SwapScheduleTest.java | 47 +++ .../service/utils/SettlementServiceTest.java | 2 +- 20 files changed, 748 insertions(+), 63 deletions(-) create mode 100644 src/main/java/net/finmath/smartcontract/product/flowschedule/FlowScheduleSwap.java create mode 100644 src/main/java/net/finmath/smartcontract/product/flowschedule/FlowScheduleSwapLeg.java create mode 100644 src/main/java/net/finmath/smartcontract/product/flowschedule/FlowScheduleSwapLegPeriod.java create mode 100644 src/main/java/net/finmath/smartcontract/valuation/implementation/FlowScheduleCalculator.java create mode 100644 src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/CalibrationSpecProviderON.java create mode 100644 src/main/resources/schemas/openapi-schemas/FlowScheduleSwapRequest.yml create mode 100644 src/main/resources/schemas/openapi-schemas/FlowScheduleSwapResponse.yml delete mode 100644 src/main/resources/schemas/openapi-schemas/PaymentSchedule.yml create mode 100644 src/test/java/net/finmath/smartcontract/valuation/schedule/SwapScheduleTest.java diff --git a/pom.xml b/pom.xml index 5bd2ad4e5..0f100e78f 100644 --- a/pom.xml +++ b/pom.xml @@ -923,7 +923,9 @@ - + + org.sonatype.central central-publishing-maven-plugin diff --git a/src/main/java/net/finmath/smartcontract/model/ExceptionId.java b/src/main/java/net/finmath/smartcontract/model/ExceptionId.java index fe4089205..7cf6320b3 100644 --- a/src/main/java/net/finmath/smartcontract/model/ExceptionId.java +++ b/src/main/java/net/finmath/smartcontract/model/ExceptionId.java @@ -37,4 +37,5 @@ public enum ExceptionId { SDC_WEB3J_SEND_ERROR, SDC_NO_DATA_FOUND, SDC_WRONG_INPUT, + SDC_FLOW_SCHEDULE_ERROR, } \ No newline at end of file diff --git a/src/main/java/net/finmath/smartcontract/product/flowschedule/FlowScheduleSwap.java b/src/main/java/net/finmath/smartcontract/product/flowschedule/FlowScheduleSwap.java new file mode 100644 index 000000000..829757335 --- /dev/null +++ b/src/main/java/net/finmath/smartcontract/product/flowschedule/FlowScheduleSwap.java @@ -0,0 +1,116 @@ +package net.finmath.smartcontract.product.flowschedule; + + +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlElementWrapper; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; +import net.finmath.smartcontract.product.xml.Smartderivativecontract; + +import java.util.List; + + +/** + * Object for structuring the flow schedule of a swap, that is the underlying of a Smart Derivative Contract, as an XML string. + * Contains information from the parent SDC and a list of {@link FlowScheduleSwapLeg}, + * each representing the flow schedule of a swap leg. + * + * @author Raphael Prandtl + */ +@XmlRootElement(name = "flowScheduleSwap") +@XmlType(name = "", propOrder = { + "dltTradeId", + "dltAddress", + "uniqueTradeIdentifier", + "settlementCurrency", + "tradeType", + "parties", + "receiverPartyID", + "flowScheduleSwapLegs" +}) +public class FlowScheduleSwap { + String dltTradeId; + String dltAddress; + String uniqueTradeIdentifier; + String settlementCurrency; + String tradeType; + Smartderivativecontract.Parties parties; + String receiverPartyID; + List flowScheduleSwapLegs; + + + @XmlElement(name = "dltTradeId") + public String getDltTradeId() { + return dltTradeId; + } + + public void setDltTradeId(final String dltTradeId) { + this.dltTradeId = dltTradeId; + } + + @XmlElement(name = "dltAddress") + public String getDltAddress() { + return dltAddress; + } + + public void setDltAddress(final String dltAddress) { + this.dltAddress = dltAddress; + } + + + @XmlElement(name = "uniqueTradeIdentifier") + public String getUniqueTradeIdentifier() { + return uniqueTradeIdentifier; + } + + public void setUniqueTradeIdentifier(final String uniqueTradeIdentifier) { + this.uniqueTradeIdentifier = uniqueTradeIdentifier; + } + + @XmlElement(name = "settlementCurrency") + public String getSettlementCurrency() { + return settlementCurrency; + } + + public void setSettlementCurrency(final String settlementCurrency) { + this.settlementCurrency = settlementCurrency; + } + + @XmlElement(name = "tradeType") + public String getTradeType() { + return tradeType; + } + + public void setTradeType(final String tradeType) { + this.tradeType = tradeType; + } + + @XmlElement(name = "parties") + public Smartderivativecontract.Parties getParties() { + return parties; + } + + public void setParties(final Smartderivativecontract.Parties parties) { + this.parties = parties; + } + + @XmlElement(name = "receiverPartyID") + public String getReceiverPartyID() { + return receiverPartyID; + } + + public void setReceiverPartyID(final String receiverPartyID) { + this.receiverPartyID = receiverPartyID; + } + + @XmlElementWrapper(name = "flowScheduleSwapLegs") + @XmlElement(name = "flowScheduleSwapLeg") + public List getFlowScheduleSwapLegs() { + return flowScheduleSwapLegs; + } + + public void setFlowScheduleSwapLegs(List flowScheduleSwapLegs) { + this.flowScheduleSwapLegs = flowScheduleSwapLegs; + } + +} diff --git a/src/main/java/net/finmath/smartcontract/product/flowschedule/FlowScheduleSwapLeg.java b/src/main/java/net/finmath/smartcontract/product/flowschedule/FlowScheduleSwapLeg.java new file mode 100644 index 000000000..7330f1c73 --- /dev/null +++ b/src/main/java/net/finmath/smartcontract/product/flowschedule/FlowScheduleSwapLeg.java @@ -0,0 +1,47 @@ +package net.finmath.smartcontract.product.flowschedule; + + +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlElementWrapper; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; + +import java.util.List; + + +/** + * Object for structuring the flow schedule of a swap leg as an XML string. + * Contains the legType and a list of {@link FlowScheduleSwapLegPeriod}, + * each representing the flow schedule of a single period. + * + * @author Raphael Prandtl + */ +@XmlRootElement(name = "flowScheduleSwapLeg") +@XmlType(name = "", propOrder = { + "legType", + "flowScheduleSwapLegPeriods" +}) +public class FlowScheduleSwapLeg { + String legType; + List flowScheduleSwapLegPeriods; + + @XmlElement(name = "legType") + public String getLegType() { + return legType; + } + + public void setLegType(final String legType) { + this.legType = legType; + } + + @XmlElementWrapper(name = "flowScheduleSwapLegPeriods") + @XmlElement(name = "flowScheduleSwapLegPeriod") + public List getFlowScheduleSwapLegPeriods() { + return flowScheduleSwapLegPeriods; + } + + public void setFlowScheduleSwapLegPeriods(final List flowScheduleSwapLegPeriods) { + this.flowScheduleSwapLegPeriods = flowScheduleSwapLegPeriods; + } + +} diff --git a/src/main/java/net/finmath/smartcontract/product/flowschedule/FlowScheduleSwapLegPeriod.java b/src/main/java/net/finmath/smartcontract/product/flowschedule/FlowScheduleSwapLegPeriod.java new file mode 100644 index 000000000..56e0a2464 --- /dev/null +++ b/src/main/java/net/finmath/smartcontract/product/flowschedule/FlowScheduleSwapLegPeriod.java @@ -0,0 +1,107 @@ +package net.finmath.smartcontract.product.flowschedule; + + +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import net.finmath.smartcontract.valuation.marketdata.data.LocalDateTimeAdapter; + +import java.time.LocalDateTime; + + +/** + * Object for structuring the flow schedule of a swap leg period as an XML string. + * Contains the rolled-out schedule for the period and information on cash-flow. + * + * @author Raphael Prandtl + */ +@XmlRootElement(name = "flowScheduleSwapLegPeriod") +@XmlType(name = "", propOrder = { + "fixingDate", + "periodStartDate", + "periodEndDate", + "paymentDate", + "notional", + "rate", + "flowAmount" +}) +public class FlowScheduleSwapLegPeriod { + LocalDateTime fixingDate; + LocalDateTime periodStartDate; + LocalDateTime periodEndDate; + LocalDateTime paymentDate; + double notional; + double rate; + double flowAmount; + + @XmlElement(name = "fixingDate") + @XmlJavaTypeAdapter(LocalDateTimeAdapter.class) + public LocalDateTime getFixingDate() { + return fixingDate; + } + + public void setFixingDate(final LocalDateTime fixingDate) { + this.fixingDate = fixingDate; + } + + @XmlElement(name = "periodStartDate") + @XmlJavaTypeAdapter(LocalDateTimeAdapter.class) + public LocalDateTime getPeriodStartDate() { + return periodStartDate; + } + + public void setPeriodStartDate(final LocalDateTime periodStartDate) { + this.periodStartDate = periodStartDate; + } + + @XmlElement(name = "periodEndDate") + @XmlJavaTypeAdapter(LocalDateTimeAdapter.class) + public LocalDateTime getPeriodEndDate() { + return periodEndDate; + } + + public void setPeriodEndDate(final LocalDateTime periodEndDate) { + this.periodEndDate = periodEndDate; + } + + @XmlElement(name = "paymentDate") + @XmlJavaTypeAdapter(LocalDateTimeAdapter.class) + public LocalDateTime getPaymentDate() { + return paymentDate; + } + + public void setPaymentDate(final LocalDateTime paymentDate) { + this.paymentDate = paymentDate; + } + + @XmlElement(name = "notional") + public double getNotional() { + return notional; + } + + public void setNotional(final double notional) { + this.notional = notional; + } + + @XmlElement(name = "rate") + public double getRate() { + return rate; + } + + public void setRate(final double rate) { + this.rate = rate; + } + + @XmlElement(name = "flowAmount") + public double getFlowAmount() { + return flowAmount; + } + + public void setFlowAmount(final double flowAmount) { + this.flowAmount = flowAmount; + } + + + +} diff --git a/src/main/java/net/finmath/smartcontract/product/xml/SDCXMLParser.java b/src/main/java/net/finmath/smartcontract/product/xml/SDCXMLParser.java index 3982ee449..e14f6b8d8 100644 --- a/src/main/java/net/finmath/smartcontract/product/xml/SDCXMLParser.java +++ b/src/main/java/net/finmath/smartcontract/product/xml/SDCXMLParser.java @@ -8,6 +8,7 @@ import net.finmath.smartcontract.model.MarketDataList; import net.finmath.smartcontract.model.SDCException; import net.finmath.smartcontract.product.SmartDerivativeContractDescriptor; +import net.finmath.smartcontract.product.flowschedule.FlowScheduleSwap; import net.finmath.smartcontract.settlement.Settlement; import net.finmath.smartcontract.valuation.marketdata.curvecalibration.CalibrationDataItem; import org.slf4j.Logger; @@ -52,10 +53,10 @@ public class SDCXMLParser { new ConcurrentHashMap<>( Map.of( - Smartderivativecontract.class.getCanonicalName(), - createContext(Smartderivativecontract.class),// - MarketDataList.class.getCanonicalName(), createContext(MarketDataList.class),// - Settlement.class.getCanonicalName(), createContext(Settlement.class)// + Smartderivativecontract.class.getCanonicalName(), createContext(Smartderivativecontract.class), + MarketDataList.class.getCanonicalName(), createContext(MarketDataList.class), + Settlement.class.getCanonicalName(), createContext(Settlement.class), + FlowScheduleSwap.class.getCanonicalName(), createContext(FlowScheduleSwap.class) ) ); diff --git a/src/main/java/net/finmath/smartcontract/valuation/implementation/FlowScheduleCalculator.java b/src/main/java/net/finmath/smartcontract/valuation/implementation/FlowScheduleCalculator.java new file mode 100644 index 000000000..1aa74b772 --- /dev/null +++ b/src/main/java/net/finmath/smartcontract/valuation/implementation/FlowScheduleCalculator.java @@ -0,0 +1,269 @@ +package net.finmath.smartcontract.valuation.implementation; + +import net.finmath.marketdata.model.AnalyticModel; +import net.finmath.marketdata.model.curves.DiscountCurve; +import net.finmath.marketdata.products.AnalyticProduct; +import net.finmath.modelling.descriptor.InterestRateSwapLegProductDescriptor; +import net.finmath.modelling.descriptor.InterestRateSwapProductDescriptor; +import net.finmath.modelling.descriptor.ScheduleDescriptor; +import net.finmath.modelling.descriptor.xmlparser.FPMLParser; +import net.finmath.modelling.productfactory.InterestRateAnalyticProductFactory; +import net.finmath.smartcontract.model.ExceptionId; +import net.finmath.smartcontract.model.SDCException; +import net.finmath.smartcontract.product.SmartDerivativeContractDescriptor; +import net.finmath.smartcontract.product.flowschedule.FlowScheduleSwap; +import net.finmath.smartcontract.product.flowschedule.FlowScheduleSwapLeg; +import net.finmath.smartcontract.product.flowschedule.FlowScheduleSwapLegPeriod; +import net.finmath.smartcontract.product.xml.SDCXMLParser; +import net.finmath.smartcontract.product.xml.Smartderivativecontract; +import net.finmath.smartcontract.valuation.marketdata.curvecalibration.*; +import net.finmath.time.FloatingpointDate; +import net.finmath.time.Period; +import net.finmath.time.Schedule; +import net.finmath.time.ScheduleFromPeriods; +import net.finmath.time.daycount.DayCountConvention; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + + +/** + * Decomposes the underlying swap of a Smart Derivative Contract contained in a FMPL + * into individual periods and values them using historic market data. + * Stores the results as flow-schedule objects of type {@link FlowScheduleSwap} or {@link FlowScheduleSwapLeg}. + * + * @author Raphael Prandtl + */ +public class FlowScheduleCalculator { + + public enum LegType { + LEG_RECEIVER("legReceiver"), + LEG_PAYER("legPayer"); + + private final String value; + + LegType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + private static final String FIXING = "Fixing"; + private static final String DEPOSIT = "Deposit"; + + private ConcurrentHashMap productMap = new ConcurrentHashMap<>(); + private ConcurrentHashMap> modelMap = new ConcurrentHashMap<>(); + + public FlowScheduleCalculator() {} + + /** + * + * @param productData The FPML of the Smart Derivative Contract + * @param marketData The FPML of the historic market data + * @return The flow schedule of the underlying swap as an XML-string. + */ + public String getFlowScheduleSwapXml(String productData, String marketData) { + FlowScheduleSwap flowScheduleSwap = getFlowScheduleSwap(productData, marketData); + String flowScheduleSwapXml = SDCXMLParser.marshalClassToXMLString(flowScheduleSwap); + return flowScheduleSwapXml; + } + + /** + * + * @param productData The FPML of the Smart Derivative Contract + * @param marketData The FPML of the historic market data + * @return The flow schedule of the underlying swap as a {@link FlowScheduleSwap} + */ + public FlowScheduleSwap getFlowScheduleSwap(String productData, String marketData) { + FlowScheduleSwap flowScheduleSwap = createFlowScheduleSwap(productData, marketData); + return flowScheduleSwap; + } + + /* + Creates the flow schedule for each swap leg, currently only implemented for plain-vanilla-swaps with one receiver and one payer leg + and combines them to a FlowScheduleSwap object. + */ + private FlowScheduleSwap createFlowScheduleSwap(String productData, String marketData) { + SmartDerivativeContractDescriptor productDescriptor = getSmartderivativeContractDescriptor(productData); + Smartderivativecontract smartderivativecontract = SDCXMLParser.unmarshalXml(productData, Smartderivativecontract.class); + List flowScheduleSwapLegs = getFlowScheduleSwapLegs(productData, marketData); + + FlowScheduleSwap flowScheduleSwap = new FlowScheduleSwap(); + flowScheduleSwap.setDltTradeId(productDescriptor.getDltAddress()); + flowScheduleSwap.setDltAddress(productDescriptor.getDltAddress()); + flowScheduleSwap.setUniqueTradeIdentifier(productDescriptor.getUniqueTradeIdentifier()); + flowScheduleSwap.setSettlementCurrency(productDescriptor.getCurrency()); + flowScheduleSwap.setTradeType(productDescriptor.getTradeType()); + flowScheduleSwap.setParties(smartderivativecontract.getParties()); + flowScheduleSwap.setReceiverPartyID(productDescriptor.getUnderlyingReceiverPartyID()); + flowScheduleSwap.setFlowScheduleSwapLegs(flowScheduleSwapLegs); + + return flowScheduleSwap; + } + + private List getFlowScheduleSwapLegs(String productData, String marketData) { + List flowScheduleSwapLegs = new ArrayList<>(); + flowScheduleSwapLegs.add(getFlowScheduleSwapLeg(productData, marketData, LegType.LEG_RECEIVER)); + flowScheduleSwapLegs.add(getFlowScheduleSwapLeg(productData, marketData, LegType.LEG_PAYER)); + return flowScheduleSwapLegs; + } + + /** + * + * @param productData The FPML of the Smart Derivative Contract + * @param marketData The FPML of the historic market data + * @param legType The {@link FlowScheduleCalculator.LegType} of the swap, i.e. {@code LEG_RECEIVER} or {@code LEG_PAYER} + * @return The flow schedule of the underlying swap leg as an XML-string + */ + public FlowScheduleSwapLeg getFlowScheduleSwapLeg(String productData, String marketData, LegType legType) { + FlowScheduleSwapLeg flowScheduleSwapLeg = createFlowScheduleSwapLeg(productData, marketData, legType); + return flowScheduleSwapLeg; + } + + // Creates the flow schedule for each period of a swap leg and combines them to a FlowScheduleSwapLeg + private FlowScheduleSwapLeg createFlowScheduleSwapLeg(String productData, String marketData, LegType legType) { + SmartDerivativeContractDescriptor productDescriptor = getSmartderivativeContractDescriptor(productData); + AnalyticModel calibratedModel = getCalibratedModel(productData, marketData); + InterestRateSwapLegProductDescriptor swapLegProductDescriptor = getInterestRateSwapLegProductDescriptor(productData, legType); + + List flowScheduleSwapLegPeriods = getFlowScheduleSwapLegPeriods(swapLegProductDescriptor, legType, calibratedModel, productDescriptor.getTradeDate()); + FlowScheduleSwapLeg flowScheduleSwapLeg = new FlowScheduleSwapLeg(); + flowScheduleSwapLeg.setLegType(legType.getValue()); + flowScheduleSwapLeg.setFlowScheduleSwapLegPeriods(flowScheduleSwapLegPeriods); + return flowScheduleSwapLeg; + } + + /* + Decomposes a swap leg with N periods into N single period swaps and values them separately. + Stores the rolled-out period and flow amounts of each single period swap as a FlowScheduleSwapLegPeriod + */ + private List getFlowScheduleSwapLegPeriods(InterestRateSwapLegProductDescriptor swapLegDescriptor, LegType legType, AnalyticModel model, LocalDate productReferenceDate) { + ScheduleFromPeriods scheduleFromPeriods = (ScheduleFromPeriods) swapLegDescriptor.getLegScheduleDescriptor().getSchedule(productReferenceDate); + InterestRateAnalyticProductFactory productFactory = new InterestRateAnalyticProductFactory(productReferenceDate); + + final String forwardCurveName = swapLegDescriptor.getForwardCurveName(); + final String discountCurveName = swapLegDescriptor.getDiscountCurveName(); + final double[] notionals = swapLegDescriptor.getNotionals(); + final double[] spreads = swapLegDescriptor.getSpreads(); + final boolean isNotionalExchanged = swapLegDescriptor.isNotionalExchanged(); + // Required to adjust the retrieval time of the discount factors for the relative distance between trade and market date + final double productToModelTimeOffset = FloatingpointDate.getFloatingPointDateFromDate(model.getCurve(discountCurveName).getReferenceDate(), productReferenceDate); + final DayCountConvention dayCountConvention = scheduleFromPeriods.getDaycountconvention(); + + List flowScheduleSwapLegPeriods = new ArrayList<>(scheduleFromPeriods.getNumberOfPeriods()); + for (int i = 0; i < scheduleFromPeriods.getNumberOfPeriods(); i++) { + Period period = scheduleFromPeriods.getPeriod(i); + ScheduleDescriptor scheduleDescriptor = new ScheduleDescriptor(List.of(period), dayCountConvention); + Schedule schedule = scheduleDescriptor.getSchedule(productReferenceDate); + + InterestRateSwapLegProductDescriptor singlePeriodSwapLeg = new InterestRateSwapLegProductDescriptor(forwardCurveName, discountCurveName, scheduleDescriptor, notionals[i], spreads[i], isNotionalExchanged); + AnalyticProduct product = (AnalyticProduct) productFactory.getProductFromDescriptor(singlePeriodSwapLeg); + DiscountCurve discountCurve = model.getDiscountCurve(discountCurveName); + + double flowAmount = product.getValue(0.0, model); // Flow schedule is always calculated as of marketDataTime = 0.0 + double periodLength = schedule.getPeriodLength(0); // always periodIndex 0 for single period swap leg + double discountFactor = discountCurve.getDiscountFactor(scheduleFromPeriods.getPayment(i) + productToModelTimeOffset); // payment time relative to marketDataTime + double rate = flowAmount / discountFactor / periodLength / notionals[i]; // "reverse" swap leg getValue() for each period + if (legType.equals(LegType.LEG_PAYER)) { + flowAmount = (-1.0) * flowAmount; + } + flowScheduleSwapLegPeriods.add(createFlowScheduleSwapLegPeriod(period, notionals[i], flowAmount, rate)); + } + return flowScheduleSwapLegPeriods; + } + + private FlowScheduleSwapLegPeriod createFlowScheduleSwapLegPeriod(Period period, double notional, double flowAmount, double rate) { + FlowScheduleSwapLegPeriod flowScheduleSwapLegPeriod = new FlowScheduleSwapLegPeriod(); + // TODO Replace atStartOfDay with correct settlementTime + flowScheduleSwapLegPeriod.setFixingDate(period.getFixing().atStartOfDay()); + flowScheduleSwapLegPeriod.setPeriodStartDate(period.getPeriodStart().atStartOfDay()); + flowScheduleSwapLegPeriod.setPeriodEndDate(period.getPeriodEnd().atStartOfDay()); + flowScheduleSwapLegPeriod.setPaymentDate(period.getPayment().atStartOfDay()); + flowScheduleSwapLegPeriod.setNotional(notional); + flowScheduleSwapLegPeriod.setRate(rate); + flowScheduleSwapLegPeriod.setFlowAmount(flowAmount); + + return flowScheduleSwapLegPeriod; + } + + // On the first call or when the productData has changed, we need to reparse the productDescriptor. + public SmartDerivativeContractDescriptor getSmartderivativeContractDescriptor(String productData) { + return productMap.computeIfAbsent(productData, k -> { + try { + return SDCXMLParser.parse(productData); + } catch (Exception e) { + throw new SDCException(ExceptionId.SDC_XML_PARSE_ERROR, e.getMessage()); + } + }); + } + + // If the productData or marketData has changed we need to recalibrate the model + private AnalyticModel getCalibratedModel(String productData, String marketData) { + ConcurrentHashMap productDataToModelMap = modelMap.computeIfAbsent(marketData, k -> new ConcurrentHashMap<>()); + return productDataToModelMap.computeIfAbsent(productData, k -> calibrateModel(productData, marketData)); + } + + private AnalyticModel calibrateModel(String productData, String marketData) { + SmartDerivativeContractDescriptor productDescriptor = getSmartderivativeContractDescriptor(productData); + CalibrationDataset calibrationDataset = CalibrationParserDataItems.getCalibrationDataSetFromXML(marketData, productDescriptor.getMarketdataItemList()); + // Add most recent published overnight rate as proxy for discounting from t=0 to t+1 + addOvernightDepositRate(calibrationDataset); + LocalDateTime marketDataTime = calibrationDataset.getDate(); + final CalibrationParserDataItems parser = new CalibrationParserDataItems(); + try { + final Stream calibrationItems = calibrationDataset.getDataAsCalibrationDataPointStream(parser); + List fixings = calibrationDataset.getDataPoints().stream().filter( + cdi -> cdi.getSpec().getProductName().equals(FIXING) || cdi.getSpec().getProductName().equals(DEPOSIT)).toList(); + + Calibrator calibrator = new Calibrator(fixings, new CalibrationContextImpl(marketDataTime, 1E-9)); + final Optional optionalCalibrationResult = calibrator.calibrateModel(calibrationItems, new CalibrationContextImpl(marketDataTime, 1E-9)); + + AnalyticModel calibratedModel = optionalCalibrationResult.orElseThrow().getCalibratedModel(); + return calibratedModel; + } catch (final Exception e) { + throw new SDCException(ExceptionId.SDC_CALIBRATION_ERROR, e.getMessage()); + } + } + + // Search for the nearest ESTR fixing and add it to the calibration items as 1-day discount rate proxy + private void addOvernightDepositRate(CalibrationDataset calibrationDataset) { + List estrFixings = calibrationDataset.getFixingDataItems().stream().filter(fixingItem -> + fixingItem.getSpec().getCurveName().equals("ESTR") && + fixingItem.getSpec().getMaturity().equals("1D")).toList(); + if (!estrFixings.isEmpty()) { + CalibrationDataItem nearestFixing = estrFixings.stream() + .min((item1, item2) -> { + double diff1 = FloatingpointDate.getFloatingPointDateFromDate(calibrationDataset.getDate(), item1.getDateTime()); + double diff2 = FloatingpointDate.getFloatingPointDateFromDate(calibrationDataset.getDate(), item2.getDateTime()); + return Double.compare(Math.abs(diff1), Math.abs(diff2)); + }) + .orElse(null); + if (nearestFixing != null) { + CalibrationDataItem.Spec calibrationItemSpecON = new CalibrationDataItem.Spec("EUREST1D", "ESTR","Overnight-Rate","1D"); + CalibrationDataItem calibrationItemON = new CalibrationDataItem(calibrationItemSpecON, nearestFixing.getQuote(), nearestFixing.getDateTime()); + calibrationDataset.getCalibrationDataItems().add(calibrationItemON); + } + } + } + + private InterestRateSwapLegProductDescriptor getInterestRateSwapLegProductDescriptor(String productData, LegType legType) { + SmartDerivativeContractDescriptor productDescriptor = getSmartderivativeContractDescriptor(productData); + String ownerPartyID = productDescriptor.getUnderlyingReceiverPartyID(); + InterestRateSwapProductDescriptor underlying = (InterestRateSwapProductDescriptor) new FPMLParser(ownerPartyID, Calibrator.FORWARD_EUR_6M, Calibrator.DISCOUNT_EUR_OIS).getProductDescriptor(productDescriptor.getUnderlying()); + switch (legType) { + case LEG_RECEIVER: + return (InterestRateSwapLegProductDescriptor) underlying.getLegReceiver(); + case LEG_PAYER: + return (InterestRateSwapLegProductDescriptor) underlying.getLegPayer(); + default: + throw new IllegalArgumentException("Unknown leg type: " + legType.getValue()); + } + } + +} diff --git a/src/main/java/net/finmath/smartcontract/valuation/implementation/MarginCalculator.java b/src/main/java/net/finmath/smartcontract/valuation/implementation/MarginCalculator.java index 6ec80bda2..803d991b1 100644 --- a/src/main/java/net/finmath/smartcontract/valuation/implementation/MarginCalculator.java +++ b/src/main/java/net/finmath/smartcontract/valuation/implementation/MarginCalculator.java @@ -32,7 +32,7 @@ import java.util.function.DoubleUnaryOperator; /** - * Calculation of the settlement using Smart Derivative Contract with an Swap contained in a FPML, + * Calculation of the settlement using Smart Derivative Contract with a Swap contained in a FPML, * using a valuation oracle with historic market data. * For details see the corresponding white paper at SSRN. * diff --git a/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/CalibrationDataset.java b/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/CalibrationDataset.java index b6fb82ecc..2a3f85cb4 100644 --- a/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/CalibrationDataset.java +++ b/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/CalibrationDataset.java @@ -19,6 +19,7 @@ public class CalibrationDataset { private static final String FIXING = "Fixing"; + private static final String DEPOSIT = "Deposit"; LocalDateTime scenarioDate; Set calibrationDataItems; Set fixingDataItems; @@ -109,15 +110,15 @@ public String serializeToJson() { /** * Returns a Stream of CalibrationSpecs, curveData provided as calibration data points, will be converted to calibration specs - * Currently Swap-Rates, FRAS and Deposit Specs are used. + * Currently Swap-Rates, FRAs and Deposit Specs are used. * * @param parser Object implementing a CalibrationParser. * @return Stream of calibration spec providers. */ public Stream getDataAsCalibrationDataPointStream(final CalibrationParser parser) { - // TODO There are possilby items that have maturity 0D. These are filteres out here. TODO - check why items with maturity 0D occure. - /* Return only calibraiton specs EXCEPT Past Fixings and spot data */ - return parser.parse(calibrationDataItems.stream().filter(dataItem -> !dataItem.getProductName().equals(FIXING) && !dataItem.getProductName().equals("Deposit") && !dataItem.getSpec().getMaturity().equals("0D"))); + // TODO There are possibly items that have maturity 0D. These are filtered out here. TODO - check why items with maturity 0D occur. + /* Return only calibration specs EXCEPT Past Fixings and spot data */ + return parser.parse(calibrationDataItems.stream().filter(dataItem -> !dataItem.getProductName().equals(FIXING) && !dataItem.getProductName().equals(DEPOSIT) && !dataItem.getSpec().getMaturity().equals("0D"))); } public Set getDataPoints() { diff --git a/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/CalibrationParserDataItems.java b/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/CalibrationParserDataItems.java index a93a09522..b68466bdf 100644 --- a/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/CalibrationParserDataItems.java +++ b/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/CalibrationParserDataItems.java @@ -39,6 +39,8 @@ private Optional parseDatapointIfPresent(final Calibrat case "ESTR", "EONIA" -> { if (datapoint.getProductName().equals("Swap-Rate")) return Optional.of(new CalibrationSpecProviderOis(datapoint.getMaturity(), "annual", datapoint.getQuote())); + if (datapoint.getProductName().equals("Overnight-Rate")) + return Optional.of(new CalibrationSpecProviderON(datapoint.getMaturity(), "tenor", datapoint.getQuote())); else return Optional.empty(); } diff --git a/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/CalibrationSpecProviderON.java b/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/CalibrationSpecProviderON.java new file mode 100644 index 000000000..3c2f4f479 --- /dev/null +++ b/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/CalibrationSpecProviderON.java @@ -0,0 +1,38 @@ +package net.finmath.smartcontract.valuation.marketdata.curvecalibration; + +import net.finmath.marketdata.calibration.CalibratedCurves; +import net.finmath.time.Schedule; +import net.finmath.time.ScheduleGenerator; +import net.finmath.time.businessdaycalendar.BusinessdayCalendarExcludingTARGETHolidays; + +import static net.finmath.smartcontract.valuation.marketdata.curvecalibration.Calibrator.DISCOUNT_EUR_OIS; + +/** + * A calibration spec provider for Overnight (O/N) rates, e.g. €STR or SOFR + * This calibration spec should only be used for the overnight rates at which banks borrow and lend funds to each other for a single day + * The overnight rate is posted "ex post" by the central bank based on transactions observed on the previous day + * It is not an Overnight-index-swap or similar "trade-able" instrument + * Theoretically it is a Deposit instrument, but since the rate published today is the rate observed yesterday, + * the use of this rate as a 1-day calibration item serves only as a proxy of the overnight discount rate from today to tomorrow. + * + * @author Raphael Prandtl + */ +public class CalibrationSpecProviderON implements CalibrationSpecProvider { + private final String maturityLabel; + private final String frequency; + private final double overnightRate; + + public CalibrationSpecProviderON(final String maturityLabel, final String frequency, final double overnightRate) { + this.maturityLabel = maturityLabel; + this.frequency = frequency; + this.overnightRate = overnightRate; + } + + @Override + public CalibratedCurves.CalibrationSpec getCalibrationSpec(final CalibrationContext ctx) { + final Schedule scheduleInterfaceRec = ScheduleGenerator.createScheduleFromConventions(ctx.getReferenceDate(), 0, "0D", maturityLabel, frequency, "act/360", "first", "follow", new BusinessdayCalendarExcludingTARGETHolidays(), 0, 0); + final double calibrationTime = scheduleInterfaceRec.getPayment(scheduleInterfaceRec.getNumberOfPeriods() - 1); + + return new CalibratedCurves.CalibrationSpec(String.format("EUR-OIS-%1$s", maturityLabel), "Deposit", scheduleInterfaceRec, "forward-EUR-OIS", overnightRate, DISCOUNT_EUR_OIS, null, "", 0.0, DISCOUNT_EUR_OIS, DISCOUNT_EUR_OIS, calibrationTime); + } +} diff --git a/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/Calibrator.java b/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/Calibrator.java index 16aeffe3f..a4e0c6d4c 100644 --- a/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/Calibrator.java +++ b/src/main/java/net/finmath/smartcontract/valuation/marketdata/curvecalibration/Calibrator.java @@ -24,6 +24,7 @@ public class Calibrator { public static final String DISCOUNT_EUR_OIS = "discount-EUR-OIS"; + public static final String FORWARD_EUR_6M = "forward-EUR-6M"; private final List fixings; private final LocalDateTime referenceDateTime; @@ -64,14 +65,16 @@ private Curve[] getCalibrationCurves(final CalibrationContext ctx) { ctx), get6MForwardCurve(ctx)}; } + /* + We build the curve w.r.t to the reference Date AND Time, i.e. the referenceDateTime represents the time point 0.0 of the curve + Only the fixing dates of the historical fixings are relevant, i.e. we ignore the fixing time within a day and measure the previous fixings relative to the referenceDateTime in whole days (no day fractions) + The calibration items and the swap schedule use LocalDate exclusively, i.e. the curve points and schedule dates are also measured in whole days relative to the referenceDateTime / time 0.0 + */ private DiscountCurveInterpolation getOisDiscountCurve(final CalibrationContext ctx) { ArrayList fixingValuesList = new ArrayList<>(); ArrayList fixingTimesList = new ArrayList<>(); ArrayList dfList = new ArrayList<>(); ArrayList dfTimesList = new ArrayList<>(); - // We build the curve w.r.t to the reference Date AND Time, i.e. the referenceDateTime represents the time point 0.0 of the curve - // Only the fixing dates of the historical fixings are relevant, i.e. we ignore the fixing time within a day and measure the previous fixings relative to the referenceDateTime in whole days (no day fractions) - // The calibration items and the swap schedule use LocalDate exclusively, i.e. the curve points and schedule dates are also measured in whole days relative to the referenceDateTime / time 0.0 fixings.stream().filter(x -> x.getCurveName().equals("ESTR")) .sorted(Comparator.comparing(CalibrationDataItem::getDate).reversed()) .forEach(x -> { @@ -168,7 +171,7 @@ private ForwardCurve get6MForwardCurve(final CalibrationContext ctx) { .map(x -> FloatingpointDate.getFloatingPointDateFromDate(referenceDateTime, x)) .mapToDouble(Double::doubleValue).sorted().toArray(); if (fixingTimes.length == 0) { //if there are no fixings return empty curve - return new ForwardCurveInterpolation("forward-EUR-6M", + return new ForwardCurveInterpolation(FORWARD_EUR_6M, ctx.getReferenceDate(), "6M", new BusinessdayCalendarExcludingTARGETHolidays(), @@ -192,7 +195,7 @@ private ForwardCurve get6MForwardCurve(final CalibrationContext ctx) { CurveInterpolation.InterpolationEntity.VALUE, ForwardCurveInterpolation.InterpolationEntityForward.FORWARD, DISCOUNT_EUR_OIS, null, fixingTimes, fixingValues); - ForwardCurve forwardPart = new ForwardCurveInterpolation("forward-EUR-6M", + ForwardCurve forwardPart = new ForwardCurveInterpolation(FORWARD_EUR_6M, ctx.getReferenceDate(), "6M", new BusinessdayCalendarExcludingTARGETHolidays(), diff --git a/src/main/java/net/finmath/smartcontract/valuation/oracle/interestrates/ValuationOraclePlainSwap.java b/src/main/java/net/finmath/smartcontract/valuation/oracle/interestrates/ValuationOraclePlainSwap.java index 781296739..581a89528 100644 --- a/src/main/java/net/finmath/smartcontract/valuation/oracle/interestrates/ValuationOraclePlainSwap.java +++ b/src/main/java/net/finmath/smartcontract/valuation/oracle/interestrates/ValuationOraclePlainSwap.java @@ -38,6 +38,9 @@ public enum ValuationTypeSwap implements ValuationType { VALUE_PAYER_LEG } + private static final String FIXING = "Fixing"; + private static final String DEPOSIT = "Deposit"; + private final CurrencyUnit currency = Monetary.getCurrency("EUR"); private final List scenarioList; private final Map products; @@ -81,53 +84,42 @@ public Map getValues(final LocalDateTime evaluationDate, fin scenarioList.stream().filter(scenario -> scenario.getDate().equals(marketDataTime)).findAny(); if (optionalScenario.isPresent()) { final CalibrationDataset scenario = optionalScenario.get(); - // TODO this is only a hotfix, the ESTR OIS MUST be included in the calibration items!! - addMissingOverNightRate(scenario); - - final CalibrationParserDataItems parser = new CalibrationParserDataItems(); - - try { - - final Stream allCalibrationItems = - scenario.getDataAsCalibrationDataPointStream(parser); - List fixings = scenario.getDataPoints().stream().filter( - cdi -> cdi.getSpec().getProductName().equals("Fixing") || cdi.getSpec().getProductName().equals( - "Deposit")).toList(); - Calibrator calibrator = new Calibrator(fixings, new CalibrationContextImpl(marketDataTime, 1E-9)); - // TODO check conventions for 1-day OIS -> uses CalibrationSpecProviderOis - final Optional optionalCalibrationResult = - calibrator.calibrateModel(allCalibrationItems, new CalibrationContextImpl(marketDataTime, 1E-9)); - AnalyticModel calibratedModel = optionalCalibrationResult.orElseThrow().getCalibratedModel(); - - final double evaluationTime = FloatingpointDate.getFloatingPointDateFromDate(marketDataTime,evaluationDate); - //referenceDate.atStartOfDay(), - //marketDataTime); - - Map values = ( - products.entrySet().stream().collect(Collectors.toMap( - e -> e.getKey(), - e -> BigDecimal.valueOf(e.getValue().getValue(evaluationTime, calibratedModel)).setScale(scale,RoundingMode.HALF_UP)))); - -// final double valueWithCurves = product.getValue(evaluationTime, calibratedModel) * notionalAmount; - - return values; -// return rounding.applyAsDouble(valueWithCurves); - } catch (final Exception e) { - throw new SDCException(ExceptionId.SDC_CALIBRATION_ERROR, e.getMessage()); - } + // Add most recent published overnight rate as proxy for discounting from t=0 to t+1 + addOvernightDepositRate(scenario); + AnalyticModel calibratedModel = getCalibratedModel(scenario, marketDataTime); + final double evaluationTime = FloatingpointDate.getFloatingPointDateFromDate(marketDataTime, evaluationDate); + + Map values = ( + products.entrySet().stream().collect(Collectors.toMap( + e -> e.getKey(), + e -> BigDecimal.valueOf(e.getValue().getValue(evaluationTime, calibratedModel)).setScale(scale,RoundingMode.HALF_UP)))); + return values; } else { return null; } } - private void addMissingOverNightRate(CalibrationDataset calibrationDataset) { - // If overnight rate is present in calibration data items, do nothing - Optional oisRateOptional = calibrationDataset.getDataPoints().stream().filter( - cdi -> cdi.getSpec().getKey().equals("EURESTSD") || cdi.getSpec().getKey().equals("EUREST1D")).findAny(); // Correct key name for OIS instrument? - if (oisRateOptional.isPresent()) { - return; + + private AnalyticModel getCalibratedModel(CalibrationDataset calibrationDataset, LocalDateTime marketDataTime) { + final CalibrationParserDataItems parser = new CalibrationParserDataItems(); + try { + final Stream calibrationItems = calibrationDataset.getDataAsCalibrationDataPointStream(parser); + List fixings = calibrationDataset.getDataPoints().stream().filter( + cdi -> cdi.getSpec().getProductName().equals(FIXING) || cdi.getSpec().getProductName().equals(DEPOSIT)).toList(); + + Calibrator calibrator = new Calibrator(fixings, new CalibrationContextImpl(marketDataTime, 1E-9)); + final Optional optionalCalibrationResult = calibrator.calibrateModel(calibrationItems, new CalibrationContextImpl(marketDataTime, 1E-9)); + + AnalyticModel calibratedModel = optionalCalibrationResult.orElseThrow().getCalibratedModel(); + return calibratedModel; + } catch (final Exception e) { + throw new SDCException(ExceptionId.SDC_CALIBRATION_ERROR, e.getMessage()); } - // Search for nearest ESTR fixing + } + + + // Search for the nearest ESTR fixing and add it to the calibration items as 1-day discount rate proxy + private void addOvernightDepositRate(CalibrationDataset calibrationDataset) { List estrFixings = calibrationDataset.getFixingDataItems().stream().filter(fixingItem -> fixingItem.getSpec().getCurveName().equals("ESTR") && fixingItem.getSpec().getMaturity().equals("1D")).toList(); @@ -140,9 +132,9 @@ private void addMissingOverNightRate(CalibrationDataset calibrationDataset) { }) .orElse(null); if (nearestFixing != null) { - CalibrationDataItem.Spec oisCalibrationItemSpec = new CalibrationDataItem.Spec("EUREST1D", "ESTR","Swap-Rate","1D"); - CalibrationDataItem oisCalibrationItem = new CalibrationDataItem(oisCalibrationItemSpec, nearestFixing.getQuote(), nearestFixing.getDateTime()); - calibrationDataset.getCalibrationDataItems().add(oisCalibrationItem); + CalibrationDataItem.Spec calibrationItemSpecON = new CalibrationDataItem.Spec("EUREST1D", "ESTR","Overnight-Rate","1D"); + CalibrationDataItem calibrationItemON = new CalibrationDataItem(calibrationItemSpecON, nearestFixing.getQuote(), nearestFixing.getDateTime()); + calibrationDataset.getCalibrationDataItems().add(calibrationItemON); } } } diff --git a/src/main/java/net/finmath/smartcontract/valuation/service/controllers/ValuationController.java b/src/main/java/net/finmath/smartcontract/valuation/service/controllers/ValuationController.java index 7c290675e..663594eb2 100644 --- a/src/main/java/net/finmath/smartcontract/valuation/service/controllers/ValuationController.java +++ b/src/main/java/net/finmath/smartcontract/valuation/service/controllers/ValuationController.java @@ -10,6 +10,7 @@ import net.finmath.smartcontract.api.ValuationApi; import net.finmath.smartcontract.model.*; import net.finmath.smartcontract.valuation.client.ValuationClient; +import net.finmath.smartcontract.valuation.implementation.FlowScheduleCalculator; import net.finmath.smartcontract.valuation.implementation.MarginCalculator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -166,6 +167,24 @@ public ResponseEntity testProductValue(MultipartFile tradeData) { } } + @Override + public ResponseEntity generateFlowSchedule(SwapFlowScheduleRequest swapFlowScheduleRequest) { + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.add(RESPONDED, "swapSchedule"); + SwapFlowScheduleResponse swapScheduleResponse; + try { + FlowScheduleCalculator flowScheduleCalculator = new FlowScheduleCalculator(); + String flowScheduleSwapXml = flowScheduleCalculator.getFlowScheduleSwapXml(swapFlowScheduleRequest.getTradeData(), swapFlowScheduleRequest.getMarketData()); + swapScheduleResponse = new SwapFlowScheduleResponse(); + swapScheduleResponse.setGeneratedSwapSchedule(flowScheduleSwapXml); + return ResponseEntity.ok(swapScheduleResponse); + } catch (Exception e) { + logger.error(FAILED_CALCULATION, e); + throw new SDCException(ExceptionId.SDC_FLOW_SCHEDULE_ERROR, e.getMessage()); + } + } + + /** * Request mapping for test * diff --git a/src/main/resources/api.yml b/src/main/resources/api.yml index a13c78214..96f6df97b 100644 --- a/src/main/resources/api.yml +++ b/src/main/resources/api.yml @@ -207,6 +207,29 @@ paths: schema: $ref: "#/components/schemas/Error" + /valuation/generate-flow-schedule: + post: + summary: Generate a swap's flow schedule xml based on given marketdata and product xml + operationId: generate-flow-schedule + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/SwapFlowScheduleRequest" + responses: + "200": + description: swap flow schedule xml was generated + content: + application/json: + schema: + $ref: "#/components/schemas/SwapFlowScheduleResponse" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /plain-swap-editor/evaluate-from-editor: post: tags: @@ -582,3 +605,7 @@ components: $ref: "schemas/openapi-schemas/RefinitivMarketData.yml" MarketDataSet: $ref: "schemas/openapi-schemas/MarketDataSet.yml" + SwapFlowScheduleRequest: + $ref: "schemas/openapi-schemas/FlowScheduleSwapRequest.yml" + SwapFlowScheduleResponse: + $ref: "schemas/openapi-schemas/FlowScheduleSwapResponse.yml" diff --git a/src/main/resources/schemas/openapi-schemas/FlowScheduleSwapRequest.yml b/src/main/resources/schemas/openapi-schemas/FlowScheduleSwapRequest.yml new file mode 100644 index 000000000..c9422f01e --- /dev/null +++ b/src/main/resources/schemas/openapi-schemas/FlowScheduleSwapRequest.yml @@ -0,0 +1,9 @@ +type: object +required: + - tradeData + - marketData +properties: + tradeData: + type: string + marketData: + type: string \ No newline at end of file diff --git a/src/main/resources/schemas/openapi-schemas/FlowScheduleSwapResponse.yml b/src/main/resources/schemas/openapi-schemas/FlowScheduleSwapResponse.yml new file mode 100644 index 000000000..831220d52 --- /dev/null +++ b/src/main/resources/schemas/openapi-schemas/FlowScheduleSwapResponse.yml @@ -0,0 +1,6 @@ +type: object +required: + - generatedSwapSchedule +properties: + generatedSwapSchedule: + type: string \ No newline at end of file diff --git a/src/main/resources/schemas/openapi-schemas/PaymentSchedule.yml b/src/main/resources/schemas/openapi-schemas/PaymentSchedule.yml deleted file mode 100644 index 48da03334..000000000 --- a/src/main/resources/schemas/openapi-schemas/PaymentSchedule.yml +++ /dev/null @@ -1,3 +0,0 @@ -type: array - items: - $ref: "PaymentPeriod.yml" \ No newline at end of file diff --git a/src/test/java/net/finmath/smartcontract/valuation/schedule/SwapScheduleTest.java b/src/test/java/net/finmath/smartcontract/valuation/schedule/SwapScheduleTest.java new file mode 100644 index 000000000..0cae5a5f9 --- /dev/null +++ b/src/test/java/net/finmath/smartcontract/valuation/schedule/SwapScheduleTest.java @@ -0,0 +1,47 @@ +package net.finmath.smartcontract.valuation.schedule; + +import net.finmath.smartcontract.model.ValueResult; +import net.finmath.smartcontract.product.flowschedule.FlowScheduleSwap; +import net.finmath.smartcontract.product.flowschedule.FlowScheduleSwapLeg; +import net.finmath.smartcontract.product.flowschedule.FlowScheduleSwapLegPeriod; +import net.finmath.smartcontract.valuation.client.ValuationClient; +import net.finmath.smartcontract.valuation.implementation.MarginCalculator; +import net.finmath.smartcontract.valuation.implementation.FlowScheduleCalculator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +/** + * Test class to check if the sum of the decomposed swap, i.e. a swap with N periods + * decomposed into N single period swaps, has the same value as the original swap. + */ +public class SwapScheduleTest { + + @Test + void testSwapScheduleGeneration() throws Exception { + final String marketData = new String(ValuationClient.class.getClassLoader().getResourceAsStream("net/finmath/smartcontract/valuation/client/md_testset_with_fixings.xml").readAllBytes(), StandardCharsets.UTF_8); + final String productData = new String(ValuationClient.class.getClassLoader().getResourceAsStream("net.finmath.smartcontract.product.xml/smartderivativecontract.xml").readAllBytes(), StandardCharsets.UTF_8); + + FlowScheduleCalculator flowScheduleCalculator = new FlowScheduleCalculator(); + FlowScheduleSwap flowScheduleSwap = flowScheduleCalculator.getFlowScheduleSwap(productData, marketData); + List flowScheduleSwapLegs = flowScheduleSwap.getFlowScheduleSwapLegs(); + double valueFlowScheduleCalculator = 0.0; + for (FlowScheduleSwapLeg flowScheduleSwapLeg : flowScheduleSwapLegs) { + List flowScheduleSwapLegPeriods = flowScheduleSwapLeg.getFlowScheduleSwapLegPeriods(); + for (FlowScheduleSwapLegPeriod flowScheduleSwapLegPeriod : flowScheduleSwapLegPeriods) { + valueFlowScheduleCalculator += flowScheduleSwapLegPeriod.getFlowAmount(); + } + } + + MarginCalculator marginCalculator = new MarginCalculator(); + ValueResult valuationResult = marginCalculator.getValue(marketData, productData); + double valueMarginCalculator = valuationResult.getValue().doubleValue(); + Assertions.assertEquals(valueFlowScheduleCalculator, valueMarginCalculator,0.01, "Sum of single periods: " + valueFlowScheduleCalculator + "; Value margin calculator: " + valueMarginCalculator); + + } + + + +} diff --git a/src/test/java/net/finmath/smartcontract/valuation/service/utils/SettlementServiceTest.java b/src/test/java/net/finmath/smartcontract/valuation/service/utils/SettlementServiceTest.java index 6e5829e15..ff731a681 100644 --- a/src/test/java/net/finmath/smartcontract/valuation/service/utils/SettlementServiceTest.java +++ b/src/test/java/net/finmath/smartcontract/valuation/service/utils/SettlementServiceTest.java @@ -131,7 +131,7 @@ void generateRegularSettlement_multipleFixing() throws IOException { assertTrue(settlementString.contains("REGULAR")); assertFalse(settlementString.contains("INITIAL")); - assertTrue(settlementString.contains("1390792.42")); + assertTrue(settlementString.contains("1390791.61")); assertTrue(settlementString.contains("EUB6FIX6M")); assertTrue(settlementString.contains("ESTRFIX1D")); assertTrue(settlementString.contains("EUB6FIX6M0.052120080917-170000")); From 13497e924484e8baaa610caab551d262a26c331f Mon Sep 17 00:00:00 2001 From: Julius Lauterbach Date: Wed, 3 Sep 2025 15:14:18 +0200 Subject: [PATCH 3/5] NO_ISSUE, advanced unauthorized logging --- .../BasicAuthWebSecurityConfiguration.java | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/finmath/smartcontract/valuation/service/config/BasicAuthWebSecurityConfiguration.java b/src/main/java/net/finmath/smartcontract/valuation/service/config/BasicAuthWebSecurityConfiguration.java index 65d913620..eae4fff94 100644 --- a/src/main/java/net/finmath/smartcontract/valuation/service/config/BasicAuthWebSecurityConfiguration.java +++ b/src/main/java/net/finmath/smartcontract/valuation/service/config/BasicAuthWebSecurityConfiguration.java @@ -1,5 +1,7 @@ package net.finmath.smartcontract.valuation.service.config; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import net.finmath.smartcontract.model.ExceptionId; import net.finmath.smartcontract.model.SDCException; import net.finmath.smartcontract.valuation.service.utils.ApplicationProperties; @@ -8,6 +10,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; @@ -31,20 +34,55 @@ public class BasicAuthWebSecurityConfiguration { String serviceUrl; @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain filterChain(HttpSecurity http, InMemoryUserDetailsManager userDetailsManager) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(authz -> { try { - authz.anyRequest().authenticated().and().httpBasic(); + authz.anyRequest().authenticated(); } catch (Exception e) { throw new SDCException(ExceptionId.SDC_AUTH_ERROR, e.getMessage()); } }) - .cors(); + .httpBasic(Customizer.withDefaults()) + .userDetailsService(userDetailsManager) + .cors(Customizer.withDefaults()) + .exceptionHandling(exception -> exception + .authenticationEntryPoint(((request, response, authException) -> { + logger.warn("401 Unauthorized: {}, {}", extractBasicAuthUsername(request), getOriginalUri(request), authException); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "SDC: Unauthorized access request"); + })) + .accessDeniedHandler((request, response, accessDeniedException) -> { + logger.warn("403 Forbidden: {}, {}", extractBasicAuthUsername(request), getOriginalUri(request), accessDeniedException); + response.sendError(HttpServletResponse.SC_FORBIDDEN, "SDC: Forbidden"); + })); return http.build(); } + private static String extractBasicAuthUsername(HttpServletRequest request) { + String authHeader = request.getHeader("Authorization"); + if (authHeader != null && authHeader.startsWith("Basic ")) { + String base64Credentials = authHeader.substring("Basic ".length()); + String credentials = new String(java.util.Base64.getDecoder().decode(base64Credentials)); + int idx = credentials.indexOf(':'); + if (idx > 0) { + return credentials.substring(0, idx); + } + } + return "unknown"; + } + + private static String getOriginalUri(HttpServletRequest request) { + String originalUri = (String) request.getAttribute("jakarta.servlet.forward.request_uri"); + if (originalUri == null) { + originalUri = (String) request.getAttribute("jakarta.servlet.error.request_uri"); + } + if (originalUri == null) { + originalUri = request.getRequestURI(); + } + return originalUri; + } + // when using OpenAPI/Swagger class-level annotations are ignored, this is the global config to work around that @Bean public WebMvcConfigurer corsConfigurer() { From c4d68627c00d97c970454e5f4fa3af7ce7875ab4 Mon Sep 17 00:00:00 2001 From: Julius Lauterbach Date: Thu, 23 Oct 2025 13:37:19 +0200 Subject: [PATCH 4/5] refactor: filter calibration data to only include FIXING product names --- .../oracle/interestrates/ValuationOraclePlainSwap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/finmath/smartcontract/valuation/oracle/interestrates/ValuationOraclePlainSwap.java b/src/main/java/net/finmath/smartcontract/valuation/oracle/interestrates/ValuationOraclePlainSwap.java index 581a89528..01c8c3de8 100644 --- a/src/main/java/net/finmath/smartcontract/valuation/oracle/interestrates/ValuationOraclePlainSwap.java +++ b/src/main/java/net/finmath/smartcontract/valuation/oracle/interestrates/ValuationOraclePlainSwap.java @@ -105,7 +105,7 @@ private AnalyticModel getCalibratedModel(CalibrationDataset calibrationDataset, try { final Stream calibrationItems = calibrationDataset.getDataAsCalibrationDataPointStream(parser); List fixings = calibrationDataset.getDataPoints().stream().filter( - cdi -> cdi.getSpec().getProductName().equals(FIXING) || cdi.getSpec().getProductName().equals(DEPOSIT)).toList(); + cdi -> cdi.getSpec().getProductName().equals(FIXING)).toList(); Calibrator calibrator = new Calibrator(fixings, new CalibrationContextImpl(marketDataTime, 1E-9)); final Optional optionalCalibrationResult = calibrator.calibrateModel(calibrationItems, new CalibrationContextImpl(marketDataTime, 1E-9)); From 696f9b5315023ea62a2b1b87964b71adebe2388d Mon Sep 17 00:00:00 2001 From: Julius Lauterbach Date: Thu, 23 Oct 2025 14:29:08 +0200 Subject: [PATCH 5/5] refactor: update calibration data filtering and adjust test assertions for accuracy --- .../valuation/implementation/FlowScheduleCalculator.java | 2 +- .../java/net/finmath/smartcontract/product/xml/JAXBTests.java | 2 +- .../valuation/implementation/MarginCalculatorTest.java | 2 +- .../valuation/service/utils/SettlementServiceTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/finmath/smartcontract/valuation/implementation/FlowScheduleCalculator.java b/src/main/java/net/finmath/smartcontract/valuation/implementation/FlowScheduleCalculator.java index 1aa74b772..6f535dce2 100644 --- a/src/main/java/net/finmath/smartcontract/valuation/implementation/FlowScheduleCalculator.java +++ b/src/main/java/net/finmath/smartcontract/valuation/implementation/FlowScheduleCalculator.java @@ -219,7 +219,7 @@ private AnalyticModel calibrateModel(String productData, String marketData) { try { final Stream calibrationItems = calibrationDataset.getDataAsCalibrationDataPointStream(parser); List fixings = calibrationDataset.getDataPoints().stream().filter( - cdi -> cdi.getSpec().getProductName().equals(FIXING) || cdi.getSpec().getProductName().equals(DEPOSIT)).toList(); + cdi -> cdi.getSpec().getProductName().equals(FIXING)).toList(); Calibrator calibrator = new Calibrator(fixings, new CalibrationContextImpl(marketDataTime, 1E-9)); final Optional optionalCalibrationResult = calibrator.calibrateModel(calibrationItems, new CalibrationContextImpl(marketDataTime, 1E-9)); diff --git a/src/test/java/net/finmath/smartcontract/product/xml/JAXBTests.java b/src/test/java/net/finmath/smartcontract/product/xml/JAXBTests.java index 140af464f..53781e157 100644 --- a/src/test/java/net/finmath/smartcontract/product/xml/JAXBTests.java +++ b/src/test/java/net/finmath/smartcontract/product/xml/JAXBTests.java @@ -190,7 +190,7 @@ void handlerTest() throws java.lang.Exception { double value = valuationResult.getValue().doubleValue(); - assertEquals(-881044.86, value, 0.005, "Valuation"); + assertEquals(-890732.84, value, 0.005, "Valuation"); assertTrue(fpml.contains("
"+PARTY1_DLT_ADDRESS+"
")); assertTrue(fpml.contains("
"+PARTY2_DLT_ADDRESS+"
")); System.out.println(valuationResult); diff --git a/src/test/java/net/finmath/smartcontract/valuation/implementation/MarginCalculatorTest.java b/src/test/java/net/finmath/smartcontract/valuation/implementation/MarginCalculatorTest.java index 3051ed225..75e504cb7 100644 --- a/src/test/java/net/finmath/smartcontract/valuation/implementation/MarginCalculatorTest.java +++ b/src/test/java/net/finmath/smartcontract/valuation/implementation/MarginCalculatorTest.java @@ -30,7 +30,7 @@ void testMargin() throws Exception { double value = valuationResult.getValue().doubleValue(); - Assertions.assertEquals(9932.71, value, 0.005, "Margin"); + Assertions.assertEquals(14877.83, value, 0.005, "Margin"); System.out.println(valuationResult); } diff --git a/src/test/java/net/finmath/smartcontract/valuation/service/utils/SettlementServiceTest.java b/src/test/java/net/finmath/smartcontract/valuation/service/utils/SettlementServiceTest.java index ff731a681..969b3d4bf 100644 --- a/src/test/java/net/finmath/smartcontract/valuation/service/utils/SettlementServiceTest.java +++ b/src/test/java/net/finmath/smartcontract/valuation/service/utils/SettlementServiceTest.java @@ -131,7 +131,7 @@ void generateRegularSettlement_multipleFixing() throws IOException { assertTrue(settlementString.contains("REGULAR")); assertFalse(settlementString.contains("INITIAL")); - assertTrue(settlementString.contains("1390791.61")); + assertTrue(settlementString.contains("1393333.53")); assertTrue(settlementString.contains("EUB6FIX6M")); assertTrue(settlementString.contains("ESTRFIX1D")); assertTrue(settlementString.contains("EUB6FIX6M0.052120080917-170000"));