From 6c0e6961078cb5816c83f4cbfc1f02571af288c9 Mon Sep 17 00:00:00 2001 From: Richard Green Date: Tue, 22 Mar 2016 11:45:08 +0000 Subject: [PATCH] Updated with new Tenor class that verifies string during construction. Now uses new Tenor class for Interest Rate Oracle --- core/src/main/kotlin/core/FinanceTypes.kt | 11 ++++++--- core/src/test/kotlin/core/FinanceTypesTest.kt | 14 +++++++++++ scripts/example.rates.txt | 8 +++---- .../core/node/services/NodeInterestRates.kt | 8 +++---- src/main/kotlin/demos/RateFixDemo.kt | 2 +- .../node/services/NodeInterestRatesTest.kt | 24 +++++++++++-------- 6 files changed, 45 insertions(+), 22 deletions(-) diff --git a/core/src/main/kotlin/core/FinanceTypes.kt b/core/src/main/kotlin/core/FinanceTypes.kt index d0fbeb7da6..f987f0a4bc 100644 --- a/core/src/main/kotlin/core/FinanceTypes.kt +++ b/core/src/main/kotlin/core/FinanceTypes.kt @@ -10,7 +10,6 @@ package core import java.math.BigDecimal import java.time.DayOfWeek -import java.time.Duration import java.time.LocalDate import java.time.format.DateTimeFormatter import java.util.* @@ -79,7 +78,7 @@ fun Iterable.sumOrZero(currency: Currency) = if (iterator().hasNext()) s // /** A [FixOf] identifies the question side of a fix: what day, tenor and type of fix ("LIBOR", "EURIBOR" etc) */ -data class FixOf(val name: String, val forDay: LocalDate, val ofTenor: Duration) +data class FixOf(val name: String, val forDay: LocalDate, val ofTenor: Tenor) /** A [Fix] represents a named interest rate, on a given day, for a given duration. It can be embedded in a tx. */ data class Fix(val of: FixOf, val value: BigDecimal) : CommandData @@ -87,7 +86,13 @@ data class Fix(val of: FixOf, val value: BigDecimal) : CommandData /** * Placeholder class for the Tenor datatype - which is a standardised duration of time until maturity */ -data class Tenor(var name:String) { +data class Tenor(val name:String) { + init { + val verifier = Regex("([0-9])+([DMYW])") // Only doing Overnight, Day, Week, Month, Year for now. + if (!(name == "ON" || verifier.containsMatchIn(name))) { + throw IllegalArgumentException("Unrecognized tenor : $name") + } + } override fun toString(): String = "$name" } diff --git a/core/src/test/kotlin/core/FinanceTypesTest.kt b/core/src/test/kotlin/core/FinanceTypesTest.kt index 3ea7c2b82e..323f4d4e73 100644 --- a/core/src/test/kotlin/core/FinanceTypesTest.kt +++ b/core/src/test/kotlin/core/FinanceTypesTest.kt @@ -12,6 +12,7 @@ import org.junit.Test import java.time.LocalDate import java.util.* import kotlin.test.assertEquals +import kotlin.test.assertFailsWith class FinanceTypesTest { @@ -21,6 +22,19 @@ class FinanceTypesTest { assert("0.01" in x.toString()) } + @Test + fun `valid tenor tests`() { + val exampleTenors = ("ON,1D,2D,3D,4D,5D,6D,7D,1W,2W,3W,1M,3M,6M,1Y,2Y,3Y,5Y,10Y,12Y,20Y").split(",") + exampleTenors.all { Tenor(it).name.length > 0 } // Slightly obtuse way of ensuring no exception thrown in construction. + } + + @Test + fun `invalid tenor tests`() { + val exampleTenors = ("W,M,D,Z,2Q,p0,W1").split(",") + for (t in exampleTenors) { + assertFailsWith { Tenor(t) } + } + } @Test fun `schedule generator 1`() { diff --git a/scripts/example.rates.txt b/scripts/example.rates.txt index 8e4448f902..a744e72352 100644 --- a/scripts/example.rates.txt +++ b/scripts/example.rates.txt @@ -1,6 +1,6 @@ # Some pretend noddy rate fixes, for the interest rate oracles. -LIBOR 2016-03-16 30 = 0.678 -LIBOR 2016-03-16 60 = 0.655 -EURIBOR 2016-03-15 30 = 0.123 -EURIBOR 2016-03-15 60 = 0.111 \ No newline at end of file +LIBOR 2016-03-16 1M = 0.678 +LIBOR 2016-03-16 2M = 0.655 +EURIBOR 2016-03-15 1M = 0.123 +EURIBOR 2016-03-15 2M = 0.111 \ No newline at end of file diff --git a/src/main/kotlin/core/node/services/NodeInterestRates.kt b/src/main/kotlin/core/node/services/NodeInterestRates.kt index 2ce6dacd90..422a99bba2 100644 --- a/src/main/kotlin/core/node/services/NodeInterestRates.kt +++ b/src/main/kotlin/core/node/services/NodeInterestRates.kt @@ -33,7 +33,7 @@ import javax.annotation.concurrent.ThreadSafe * for signing. */ object NodeInterestRates { - /** Parses a string of the form "LIBOR 16-March-2016 30 = 0.678" into a [FixOf] and [Fix] */ + /** Parses a string of the form "LIBOR 16-March-2016 1M = 0.678" into a [FixOf] and [Fix] */ fun parseOneRate(s: String): Pair { val (key, value) = s.split('=').map { it.trim() } val of = parseFixOf(key) @@ -41,10 +41,10 @@ object NodeInterestRates { return of to Fix(of, rate) } - /** Parses a string of the form "LIBOR 16-March-2016 30" into a [FixOf] */ + /** Parses a string of the form "LIBOR 16-March-2016 1M" into a [FixOf] */ fun parseFixOf(key: String): FixOf { - val (name, date, days) = key.split(' ') - return FixOf(name, LocalDate.parse(date), days.toInt().days) + val (name, date, tenorString) = key.split(' ') + return FixOf(name, LocalDate.parse(date), Tenor(tenorString)) } /** Parses lines containing fixes */ diff --git a/src/main/kotlin/demos/RateFixDemo.kt b/src/main/kotlin/demos/RateFixDemo.kt index 07199d1750..a80f46c917 100644 --- a/src/main/kotlin/demos/RateFixDemo.kt +++ b/src/main/kotlin/demos/RateFixDemo.kt @@ -37,7 +37,7 @@ fun main(args: Array) { val oracleAddrArg = parser.accepts("oracle").withRequiredArg().required() val oracleIdentityArg = parser.accepts("oracle-identity-file").withRequiredArg().required() - val fixOfArg = parser.accepts("fix-of").withRequiredArg().defaultsTo("LIBOR 2016-03-16 30") + val fixOfArg = parser.accepts("fix-of").withRequiredArg().defaultsTo("LIBOR 2016-03-16 1M") val expectedRateArg = parser.accepts("expected-rate").withRequiredArg().defaultsTo("0.67") val rateToleranceArg = parser.accepts("rate-tolerance").withRequiredArg().defaultsTo("0.1") diff --git a/src/test/kotlin/core/node/services/NodeInterestRatesTest.kt b/src/test/kotlin/core/node/services/NodeInterestRatesTest.kt index 550efaf879..8007ee31b1 100644 --- a/src/test/kotlin/core/node/services/NodeInterestRatesTest.kt +++ b/src/test/kotlin/core/node/services/NodeInterestRatesTest.kt @@ -11,6 +11,10 @@ package core.node.services import contracts.Cash import core.* import core.testing.MockNetwork +import core.DOLLARS +import core.Fix +import core.TransactionBuilder +import core.bd import core.testutils.* import core.utilities.BriefLogFormatter import org.junit.Test @@ -20,16 +24,16 @@ import kotlin.test.assertFailsWith class NodeInterestRatesTest { val TEST_DATA = NodeInterestRates.parseFile(""" - LIBOR 2016-03-16 30 = 0.678 - LIBOR 2016-03-16 60 = 0.655 - EURIBOR 2016-03-15 30 = 0.123 - EURIBOR 2016-03-15 60 = 0.111 + LIBOR 2016-03-16 1M = 0.678 + LIBOR 2016-03-16 2M = 0.655 + EURIBOR 2016-03-15 1M = 0.123 + EURIBOR 2016-03-15 2M = 0.111 """.trimIndent()) val service = NodeInterestRates.Oracle(MEGA_CORP, MEGA_CORP_KEY).apply { knownFixes = TEST_DATA } @Test fun `query successfully`() { - val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 30") + val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") val res = service.query(listOf(q)) assertEquals(1, res.size) assertEquals("0.678".bd, res[0].value) @@ -37,8 +41,8 @@ class NodeInterestRatesTest { } @Test fun `query with one success and one missing`() { - val q1 = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 30") - val q2 = NodeInterestRates.parseFixOf("LIBOR 2016-03-19 30") + val q1 = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") + val q2 = NodeInterestRates.parseFixOf("LIBOR 2016-03-19 1M") val e = assertFailsWith { service.query(listOf(q1, q2)) } assertEquals(e.fix, q2) } @@ -56,7 +60,7 @@ class NodeInterestRatesTest { @Test fun `sign successfully`() { val tx = makeTX() - val fix = service.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 30"))).first() + val fix = service.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M"))).first() tx.addCommand(fix, service.identity.owningKey) // Sign successfully. @@ -66,7 +70,7 @@ class NodeInterestRatesTest { @Test fun `do not sign with unknown fix`() { val tx = makeTX() - val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 30") + val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") val badFix = Fix(fixOf, "0.6789".bd) tx.addCommand(badFix, service.identity.owningKey) @@ -81,7 +85,7 @@ class NodeInterestRatesTest { NodeInterestRates.Service(n2).oracle.knownFixes = TEST_DATA val tx = TransactionBuilder() - val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 30") + val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") val protocol = RatesFixProtocol(tx, n2.legallyIdentifableAddress, fixOf, "0.675".bd, "0.1".bd) BriefLogFormatter.initVerbose("rates") val future = n1.smm.add("rates", protocol)