From b93aa71afaa434dbe399720b21403e03f9e3df57 Mon Sep 17 00:00:00 2001 From: sofusmortensen Date: Sun, 11 Sep 2016 11:56:28 +0200 Subject: [PATCH] Universal: checkpoint before refactor --- .../contracts/universal/Arrangement.kt | 3 +- .../contracts/universal/Perceivable.kt | 19 ++-- .../contracts/universal/UniversalContract.kt | 88 +++++++-------- .../r3corda/contracts/universal/literal.kt | 61 +++++++---- .../com/r3corda/contracts/universal/Cap.kt | 100 +++++++++++++++++- .../com/r3corda/contracts/universal/Caplet.kt | 14 +-- .../contracts/universal/RollOutTests.kt | 92 ++++++++++++++++ .../r3corda/contracts/universal/Swaption.kt | 6 +- 8 files changed, 292 insertions(+), 91 deletions(-) create mode 100644 experimental/src/test/kotlin/com/r3corda/contracts/universal/RollOutTests.kt diff --git a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Arrangement.kt b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Arrangement.kt index 1e7a76f4fa..542bee4db5 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Arrangement.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Arrangement.kt @@ -4,6 +4,7 @@ import com.r3corda.core.contracts.Amount import com.r3corda.core.contracts.Frequency import com.r3corda.core.crypto.Party import java.math.BigDecimal +import java.time.LocalDate import java.util.* /** @@ -49,7 +50,7 @@ data class Or(val actions: Set) : Arrangement // Roll out of arrangement -data class RollOut(val startDate: String, val endDate: String, val frequency: Frequency, val template: Arrangement) : Arrangement +data class RollOut(val startDate: LocalDate, val endDate: LocalDate, val frequency: Frequency, val template: Arrangement) : Arrangement // Continuation of roll out diff --git a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Perceivable.kt b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Perceivable.kt index 602a261c42..b004db7c83 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Perceivable.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Perceivable.kt @@ -1,5 +1,6 @@ package com.r3corda.contracts.universal +import com.r3corda.core.contracts.BusinessCalendar import com.r3corda.core.contracts.Tenor import java.math.BigDecimal import java.time.Instant @@ -44,14 +45,16 @@ class EndDate : Perceivable */ data class TimePerceivable(val cmp: Comparison, val instant: Perceivable) : Perceivable -fun parseDate(str: String) = Instant.parse(str+"T00:00:00Z")!! +fun parseDate(str: String) = BusinessCalendar.parseDateFromString(str) + + // Instant.parse(str+"T00:00:00Z")!! fun before(expiry: Perceivable) = TimePerceivable(Comparison.LTE, expiry) fun after(expiry: Perceivable) = TimePerceivable(Comparison.GTE, expiry) fun before(expiry: Instant) = TimePerceivable(Comparison.LTE, const(expiry)) fun after(expiry: Instant) = TimePerceivable(Comparison.GTE, const(expiry)) -fun before(expiry: String) = TimePerceivable(Comparison.LTE, const(parseDate(expiry))) -fun after(expiry: String) = TimePerceivable(Comparison.GTE, const(parseDate(expiry))) +fun before(expiry: String) = TimePerceivable(Comparison.LTE, const(parseDate(expiry).toInstant())) +fun after(expiry: String) = TimePerceivable(Comparison.GTE, const(parseDate(expiry).toInstant())) data class PerceivableAnd(val left: Perceivable, val right: Perceivable) : Perceivable infix fun Perceivable.and(obs: Perceivable) = PerceivableAnd(this, obs) @@ -102,23 +105,23 @@ class DummyPerceivable : Perceivable // todo: holidays data class Interest(val amount: Perceivable, val dayCountConvention: String, - val interest: Perceivable, val start: LocalDate, val end: LocalDate) : Perceivable + val interest: Perceivable, val start: Perceivable, val end: Perceivable) : Perceivable fun libor(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable = DummyPerceivable() fun libor(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") start: Perceivable, @Suppress("UNUSED_PARAMETER") end: Perceivable) : Perceivable = DummyPerceivable() fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: BigDecimal /* todo - appropriate type */, - @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable = Interest(Const(amount), dayCountConvention, Const(interest), parseDate(start).toLocalDate(), parseDate(end).toLocalDate()) + @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable = Interest(Const(amount), dayCountConvention, Const(interest), const(parseDate(start).toInstant()), const(parseDate(end).toInstant())) fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable /* todo - appropriate type */, @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable = - Interest(Const(amount), dayCountConvention, interest, parseDate(start).toLocalDate(), parseDate(end).toLocalDate()) + Interest(Const(amount), dayCountConvention, interest, const(parseDate(start).toInstant()), const(parseDate(end).toInstant())) fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: BigDecimal /* todo - appropriate type */, @Suppress("UNUSED_PARAMETER") start: Perceivable, @Suppress("UNUSED_PARAMETER") end: Perceivable) : Perceivable = DummyPerceivable() -fun interest(@Suppress("UNUSED_PARAMETER") rate: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable /* todo - appropriate type */, - @Suppress("UNUSED_PARAMETER") start: Perceivable, @Suppress("UNUSED_PARAMETER") end: Perceivable) : Perceivable = DummyPerceivable() +fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable /* todo - appropriate type */, + @Suppress("UNUSED_PARAMETER") start: Perceivable, @Suppress("UNUSED_PARAMETER") end: Perceivable) : Perceivable = Interest(const(amount), dayCountConvention, interest, start, end) class Fixing(val source: String, val date: Perceivable, val tenor: Tenor) : Perceivable diff --git a/experimental/src/main/kotlin/com/r3corda/contracts/universal/UniversalContract.kt b/experimental/src/main/kotlin/com/r3corda/contracts/universal/UniversalContract.kt index 830ec07012..c62c87f260 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/UniversalContract.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/UniversalContract.kt @@ -4,6 +4,7 @@ import com.r3corda.core.contracts.* import com.r3corda.core.crypto.Party import com.r3corda.core.crypto.SecureHash import com.r3corda.core.transactions.TransactionBuilder +import com.sun.tools.corba.se.idl.InvalidArgument import java.math.BigDecimal import java.security.PublicKey import java.time.Instant @@ -39,19 +40,11 @@ class UniversalContract : Contract { class Issue : TypeOnlyCommandData(), Commands } - fun eval(@Suppress("UNUSED_PARAMETER") tx: TransactionForContract, expr: Perceivable): T = when (expr) { - is Const -> expr.value - else -> throw Error("Unable to evaluate") - } - - fun eval(@Suppress("UNUSED_PARAMETER") tx: TransactionForContract, expr: Perceivable): LocalDate = when (expr) { - is Const -> expr.value - else -> throw Error("Unable to evaluate") - } fun eval(@Suppress("UNUSED_PARAMETER") tx: TransactionForContract, expr: Perceivable): Instant = when (expr) { is Const -> expr.value else -> throw Error("Unable to evaluate") } + fun eval(tx: TransactionForContract, expr: Perceivable): Boolean = when (expr) { is PerceivableAnd -> eval(tx, expr.left) && eval(tx, expr.right) is PerceivableOr -> eval(tx, expr.right) || eval(tx, expr.right) @@ -64,7 +57,6 @@ class UniversalContract : Contract { else -> throw NotImplementedError("eval - Boolean - " + expr.javaClass.name) } - fun eval(tx: TransactionForContract, expr: Perceivable): BigDecimal = when (expr) { is Const -> expr.value @@ -102,41 +94,6 @@ class UniversalContract : Contract { else -> throw NotImplementedError("eval - BigDecimal - " + expr.javaClass.name) } - fun reduce(tx: TransactionForContract, expr: Perceivable): Perceivable = when (expr) { - is PerceivableOperation -> { - val left = reduce(tx, expr.left) - val right = reduce(tx, expr.right) - if (left is Const && right is Const) - when (expr.op) { - //Operation.DIV -> Const( left.value / right.value ) - Operation.MINUS -> Const(left.value - right.value) - Operation.PLUS -> Const(left.value + right.value) - //Operation.TIMES -> Const( left.value * right.value ) - else -> throw NotImplementedError("reduce - " + expr.op.name) - } - else - PerceivableOperation(left, expr.op, right) - } - is UnaryPlus -> { - val amount = reduce(tx, expr.arg) - if (amount is Const) { - if (amount.value > BigDecimal.ZERO) - amount - else - Const(BigDecimal.ZERO) - } else - UnaryPlus(amount) - } - is Interest -> Interest(reduce(tx, expr.amount), expr.dayCountConvention, reduce(tx, expr.interest), expr.start, expr.end) - else -> expr - } - - fun checkAndReduce(tx: TransactionForContract, arrangement: Arrangement): Arrangement = when (arrangement) { - is Transfer -> Transfer(reduce(tx, arrangement.amount), arrangement.currency, arrangement.from, arrangement.to) - is And -> And(arrangement.arrangements.map { checkAndReduce(tx, it) }.toSet()) - else -> arrangement - } - fun validateImmediateTransfers(tx: TransactionForContract, arrangement: Arrangement): Arrangement = when (arrangement) { is Transfer -> { val amount = eval(tx, arrangement.amount) @@ -147,6 +104,32 @@ class UniversalContract : Contract { else -> arrangement } + // todo: think about multi layered rollouts + fun reduceRollOut(rollOut: RollOut) : Arrangement { + val start = rollOut.startDate + val end = rollOut.endDate + + // todo: calendar + rolling conventions + val schedule = BusinessCalendar.createGenericSchedule(start, rollOut.frequency, noOfAdditionalPeriods = 1, endDate = end) + + val next = schedule.first() // fail if no dates + + val newRollOut = RollOut(next, end, rollOut.frequency, rollOut.template) + + return replaceNext(rollOut.template, newRollOut ) + } + + fun replaceNext(arrangement: Arrangement, nextReplacement: RollOut) : Arrangement { + return when (arrangement) { + is Or -> Or(arrangement.actions.map { replaceNext(it, nextReplacement)!! as Action }.toSet()) + is And -> And(arrangement.arrangements.map { replaceNext(it, nextReplacement) }.toSet()) + is Action -> Action( arrangement.name, arrangement.condition, arrangement.actors, replaceNext(arrangement.arrangement, nextReplacement)) + is Transfer -> arrangement + is Zero -> arrangement + else -> throw NotImplementedError("replaceNext " + arrangement.javaClass.name) + } + } + override fun verify(tx: TransactionForContract) { requireThat { @@ -160,7 +143,14 @@ class UniversalContract : Contract { when (value) { is Commands.Action -> { val inState = tx.inputs.single() as State - val actions = actions(inState.details) + val arr = when (inState.details) { + is Or -> inState.details + is Action -> inState.details + is RollOut -> reduceRollOut(inState.details) + else -> throw InvalidArgument("Unexpected arrangement, " + tx.inputs.single()) + } + + val actions = actions(arr) val action = actions[value.name] ?: throw IllegalArgumentException("Failed requirement: action must be defined") @@ -229,8 +219,8 @@ class UniversalContract : Contract { } fun replaceFixing(tx: TransactionForContract, perceivable: Perceivable, - fixings: Map, unusedFixings: MutableSet): Perceivable = - when (perceivable) { + fixings: Map, unusedFixings: MutableSet): Perceivable { + return when (perceivable) { is Const -> perceivable is UnaryPlus -> UnaryPlus(replaceFixing(tx, perceivable.arg, fixings, unusedFixings)) is PerceivableOperation -> PerceivableOperation(replaceFixing(tx, perceivable.left, fixings, unusedFixings), @@ -244,6 +234,7 @@ class UniversalContract : Contract { } else perceivable else -> throw NotImplementedError("replaceFixing - " + perceivable.javaClass.name) } + } fun replaceFixing(tx: TransactionForContract, arr: Action, fixings: Map, unusedFixings: MutableSet) = @@ -256,6 +247,7 @@ class UniversalContract : Contract { is Zero -> arr is Transfer -> Transfer(replaceFixing(tx, arr.amount, fixings, unusedFixings), arr.currency, arr.from, arr.to) is Or -> Or(arr.actions.map { replaceFixing(tx, it, fixings, unusedFixings) }.toSet()) + is RollOut -> RollOut(arr.startDate, arr.endDate, arr.frequency, replaceFixing(tx, arr.template, fixings, unusedFixings)) else -> throw NotImplementedError("replaceFixing - " + arr.javaClass.name) } diff --git a/experimental/src/main/kotlin/com/r3corda/contracts/universal/literal.kt b/experimental/src/main/kotlin/com/r3corda/contracts/universal/literal.kt index 6a88a11aca..89901c4c09 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/literal.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/literal.kt @@ -1,8 +1,10 @@ package com.r3corda.contracts.universal +import com.r3corda.core.contracts.BusinessCalendar import com.r3corda.core.contracts.Frequency import com.r3corda.core.crypto.Party import java.math.BigDecimal +import java.time.LocalDate import java.util.* /** @@ -23,19 +25,19 @@ val zero = Zero() open class ContractBuilder { val contracts = mutableListOf() - fun Party.gives(beneficiary: Party, amount: BigDecimal, currency: Currency) : Transfer { + fun Party.gives(beneficiary: Party, amount: BigDecimal, currency: Currency): Transfer { val c = Transfer(const(amount), currency, this, beneficiary) - contracts.add( c ) + contracts.add(c) return c } - fun Party.gives(beneficiary: Party, amount: Perceivable, currency: Currency) : Transfer { + fun Party.gives(beneficiary: Party, amount: Perceivable, currency: Currency): Transfer { val c = Transfer(amount, currency, this, beneficiary) - contracts.add( c ) + contracts.add(c) return c } - fun Party.may(init: ActionBuilder.() -> Unit) : Or { + fun Party.may(init: ActionBuilder.() -> Unit): Or { val b = ActionBuilder(setOf(this)) b.init() val c = Or(b.actions.toSet()) @@ -43,7 +45,7 @@ open class ContractBuilder { return c } - fun Set.may(init: ActionBuilder.() -> Unit) : Or { + fun Set.may(init: ActionBuilder.() -> Unit): Or { val b = ActionBuilder(this) b.init() val c = Or(b.actions.toSet()) @@ -51,32 +53,48 @@ open class ContractBuilder { return c } + + infix fun Or.or(ors: Or): Or { + assert(ors.actions.size == 1) + assert(contracts[contracts.lastIndex-1] == this) + assert(contracts[contracts.lastIndex] == ors) + contracts.removeAt(contracts.lastIndex) + + val c = Or(this.actions + ors.actions.single()) + contracts[contracts.lastIndex] = c + return c + } + infix fun Party.or(party: Party) = setOf(this, party) infix fun Set.or(party: Party) = this.plus(party) @Deprecated(level = DeprecationLevel.ERROR, message = "Not allowed") fun Action(@Suppress("UNUSED_PARAMETER") name: String, @Suppress("UNUSED_PARAMETER") condition: Perceivable, - @Suppress("UNUSED_PARAMETER") actors: Set, @Suppress("UNUSED_PARAMETER") arrangement: Arrangement) {} + @Suppress("UNUSED_PARAMETER") actors: Set, @Suppress("UNUSED_PARAMETER") arrangement: Arrangement) { + } @Deprecated(level = DeprecationLevel.ERROR, message = "Not available") - fun String.anytime(@Suppress("UNUSED_PARAMETER") ignore: T ) {} + fun String.anytime(@Suppress("UNUSED_PARAMETER") ignore: T) { + } @Deprecated(level = DeprecationLevel.ERROR, message = "Not available") - fun String.givenThat(@Suppress("UNUSED_PARAMETER") ignore: T ) {} + fun String.givenThat(@Suppress("UNUSED_PARAMETER") ignore: T) { + } @Deprecated(level = DeprecationLevel.ERROR, message = "Not available") - fun String.givenThat(@Suppress("UNUSED_PARAMETER") ignore1: T, @Suppress("UNUSED_PARAMETER") ignore2: T ) {} + fun String.givenThat(@Suppress("UNUSED_PARAMETER") ignore1: T, @Suppress("UNUSED_PARAMETER") ignore2: T) { + } - /* fun Party.gives(beneficiary: Party, amount: Perceivable, currency: Currency) { - contracts.add( Transfer(amount, currency, this, beneficiary)) - }*/ + /* fun Party.gives(beneficiary: Party, amount: Perceivable, currency: Currency) { + contracts.add( Transfer(amount, currency, this, beneficiary)) + }*/ - infix fun Arrangement.and(arrangement: Arrangement) = And( setOf(this, arrangement) ) - infix fun Action.or(arrangement: Action) = Or( setOf(this, arrangement) ) - infix fun Or.or(arrangement: Action) = Or( this.actions.plusElement(arrangement) ) - infix fun Or.or(ors: Or) = Or( this.actions.plus(ors.actions) ) + infix fun Arrangement.and(arrangement: Arrangement) = And(setOf(this, arrangement)) + infix fun Action.or(arrangement: Action) = Or(setOf(this, arrangement)) +// infix fun Or.or(arrangement: Action) = Or( this.actions.plusElement(arrangement) ) +// infix fun Or.or(ors: Or) = Or( this.actions.plus(ors.actions) ) - fun rollOut(startDate: String, endDate: String, frequency: Frequency, init: RollOutBuilder.() -> Unit) : RollOut { + fun rollOut(startDate: LocalDate, endDate: LocalDate, frequency: Frequency, init: RollOutBuilder.() -> Unit): RollOut { val b = RollOutBuilder(startDate, endDate, frequency, Dummy()) b.init() val c = b.final() @@ -84,7 +102,7 @@ open class ContractBuilder { return c } - fun rollOut(startDate: String, endDate: String, frequency: Frequency, vars: T, init: RollOutBuilder.() -> Unit) : RollOut { + fun rollOut(startDate: LocalDate, endDate: LocalDate, frequency: Frequency, vars: T, init: RollOutBuilder.() -> Unit): RollOut { val b = RollOutBuilder(startDate, endDate, frequency, vars) b.init() val c = b.final() @@ -92,6 +110,8 @@ open class ContractBuilder { return c } + val String.ld: LocalDate get() = BusinessCalendar.parseDateFromString(this) + open fun final() = when (contracts.size) { 0 -> zero @@ -100,6 +120,7 @@ open class ContractBuilder { } } + interface GivenThatResolve { fun resolve(contract: Arrangement) } @@ -139,7 +160,7 @@ data class Parameter(val initialValue: T) : Perceivable fun variable(v: T) = Parameter(v) -class RollOutBuilder(val startDate: String, val endDate: String, val frequency: Frequency, val vars: T) : ContractBuilder() { +class RollOutBuilder(val startDate: LocalDate, val endDate: LocalDate, val frequency: Frequency, val vars: T) : ContractBuilder() { val start = StartDate() val end = EndDate() diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/Cap.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Cap.kt index e26410a93f..4b286fbad4 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/Cap.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Cap.kt @@ -1,11 +1,13 @@ package com.r3corda.contracts.universal +import com.r3corda.core.contracts.FixOf import com.r3corda.core.contracts.Frequency import com.r3corda.core.contracts.Tenor import com.r3corda.core.utilities.DUMMY_NOTARY import com.r3corda.testing.transaction import org.junit.Test import java.time.Instant +import java.time.LocalDate /** * Created by sofusmortensen on 05/09/16. @@ -18,16 +20,19 @@ class Cap { val notional = 50.M val currency = EUR + val tradeDate: LocalDate = LocalDate.of(2016, 9, 1) + val contract = arrange { - rollOut("2016-04-01", "2017-04-01", Frequency.Quarterly) { + rollOut("2016-09-01".ld, "2017-04-01".ld, Frequency.Quarterly) { (acmeCorp or highStreetBank).may { "exercise".anytime { val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("6M")), start, end) val fixed = interest(notional, "act/365", 0.5.bd, start, end) - highStreetBank.gives(acmeCorp, floating - fixed, currency) + highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency) next() } - } or acmeCorp.may { + } or + acmeCorp.may { "skip".anytime { next() } @@ -35,11 +40,53 @@ class Cap { } } + val contractFixed = arrange { + (acmeCorp or highStreetBank).may { + "exercise".anytime() { + val floating1 = interest(notional, "act/365", 1.0.bd, "2016-04-01", "2016-07-01") + val fixed1 = interest(notional, "act/365", 0.5.bd, "2016-04-01", "2016-07-01") + highStreetBank.gives(acmeCorp, (floating1 - fixed1).plus(), currency) + rollOut("2016-07-01".ld, "2017-04-01".ld, Frequency.Quarterly) { + (acmeCorp or highStreetBank).may { + "exercise".anytime { + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("6M")), start, end) + val fixed = interest(notional, "act/365", 0.5.bd, start, end) + highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency) + next() + } + } or acmeCorp.may { + "skip".anytime { + next() + } + } + } + } + } or acmeCorp.may { + "skip".anytime { + rollOut("2016-07-01".ld, "2017-04-01".ld, Frequency.Quarterly) { + (acmeCorp or highStreetBank).may { + "exercise".anytime { + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("6M")), start, end) + val fixed = interest(notional, "act/365", 0.5.bd, start, end) + highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency) + next() + } + } or acmeCorp.may { + "skip".anytime { + next() + } + } + } + } + } + } + val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract) + val stateFixed = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractFixed) val contractTARN = arrange { - rollOut("2016-04-01", "2017-04-01", Frequency.Quarterly, object { + rollOut("2016-04-01".ld, "2017-04-01".ld, Frequency.Quarterly, object { val limit = variable(150.K) }) { (acmeCorp or highStreetBank).may { @@ -77,4 +124,49 @@ class Cap { } } + @Test + fun `fixing`() { + transaction { + input { stateStart } + output { stateFixed } + timestamp(TEST_TX_TIME_1) + + tweak { + command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + this `fails with` "action must be defined" + } + + tweak { + // wrong source + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("6M")), 1.0.bd))) } + + this `fails with` "relevant fixing must be included" + } + + tweak { + // wrong date + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("6M")), 1.0.bd))) } + + this `fails with` "relevant fixing must be included" + } + + tweak { + // wrong tenor + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))) } + + this `fails with` "relevant fixing must be included" + } + + tweak { + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.5.bd))) } + + this `fails with` "output state does not reflect fix command" + } + + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.0.bd))) } + + this.verifies() + } + } + } \ No newline at end of file diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/Caplet.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Caplet.kt index f2639d7b32..998a262909 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/Caplet.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Caplet.kt @@ -18,7 +18,7 @@ class Caplet { val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z") - val dt: LocalDate = LocalDate.of(2016, 9, 1) + val tradeDate: LocalDate = LocalDate.of(2016, 9, 1) val notional = 50.M val currency = EUR @@ -26,7 +26,7 @@ class Caplet { val contract = arrange { (acmeCorp or highStreetBank).may { "exercise".anytime() { - val floating = interest(notional, "act/365", fix("LIBOR", dt, Tenor("6M")), "2016-04-01", "2016-10-01") + val floating = interest(notional, "act/365", fix("LIBOR", tradeDate, Tenor("6M")), "2016-04-01", "2016-10-01") val fixed = interest(notional, "act/365", 0.5.bd, "2016-04-01", "2016-10-01") highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency) } @@ -102,32 +102,32 @@ class Caplet { tweak { // wrong source - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBORx", dt, Tenor("6M")), 1.0.bd))) } + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("6M")), 1.0.bd))) } this `fails with` "relevant fixing must be included" } tweak { // wrong date - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt.plusYears(1), Tenor("6M")), 1.0.bd))) } + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("6M")), 1.0.bd))) } this `fails with` "relevant fixing must be included" } tweak { // wrong tenor - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt, Tenor("3M")), 1.0.bd))) } + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))) } this `fails with` "relevant fixing must be included" } tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt, Tenor("6M")), 1.5.bd))) } + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.5.bd))) } this `fails with` "output state does not reflect fix command" } - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt, Tenor("6M")), 1.0.bd))) } + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.0.bd))) } this.verifies() } diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/RollOutTests.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/RollOutTests.kt new file mode 100644 index 0000000000..ed2e7ee4aa --- /dev/null +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/RollOutTests.kt @@ -0,0 +1,92 @@ +package com.r3corda.contracts.universal + +import com.r3corda.core.contracts.BusinessCalendar +import com.r3corda.core.contracts.Frequency +import com.r3corda.core.utilities.DUMMY_NOTARY +import com.r3corda.testing.transaction +import org.junit.Test +import java.time.Instant + +/** + * Created by sofusmortensen on 08/09/16. + */ + +class RollOutTests { + + val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z") + + val contract = arrange { + rollOut("2016-09-01".ld, "2017-09-01".ld, Frequency.Monthly) { + (acmeCorp or highStreetBank).may { + "transfer".givenThat(after(end)) { + highStreetBank.gives(acmeCorp, 10.K, USD) + next() + } + } + } + } + val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract) + + val contractStep1a = arrange { + rollOut("2016-12-01".ld, "2017-09-01".ld, Frequency.Monthly) { + (acmeCorp or highStreetBank).may { + "transfer".givenThat(after(end)) { + highStreetBank.gives(acmeCorp, 10.K, USD) + next() + } + } + } + } + + val contractStep1b = arrange { + highStreetBank.gives(acmeCorp, 10.K, USD) + } + + val stateStep1a = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractStep1a) + val stateStep1b = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractStep1b) + + @Test + fun dateTests() { + + val d1 = BusinessCalendar.parseDateFromString("2016-09-10") + } + + @Test + fun issue() { + transaction { + output { stateStart } + timestamp(TEST_TX_TIME_1) + + this `fails with` "transaction has a single command" + + tweak { + command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } + this `fails with` "the transaction is signed by all liable parties" + } + + command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() } + + this.verifies() + } + } + + @Test + fun `execute`() { + transaction { + input { stateStart } + output { stateStep1a } + output { stateStep1b } + timestamp(TEST_TX_TIME_1) + + tweak { + command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + this `fails with` "action must be defined" + } + + command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") } + + this.verifies() + } + } + +} \ No newline at end of file diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/Swaption.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Swaption.kt index 6281c9caf2..5361dff783 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/Swaption.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Swaption.kt @@ -45,7 +45,7 @@ class Swaption { val elegant_contract = arrange { - rollOut("01/04/2015", "01/04/2025", Frequency.Quarterly) { + rollOut("01/04/2015".ld, "01/04/2025".ld, Frequency.Quarterly) { (highStreetBank or acmeCorp).may { "proceed".givenThat(after(start)) { highStreetBank.gives(acmeCorp, libor(notional, start, end), currency) @@ -63,7 +63,7 @@ class Swaption { val strike = 1.2 val tarf = arrange { - rollOut("01/04/2015", "01/04/2016", Frequency.Quarterly, object { + rollOut("01/04/2015".ld, "01/04/2016".ld, Frequency.Quarterly, object { val cap = variable(150.K) }) { acmeCorp.may { @@ -86,7 +86,7 @@ class Swaption { } val tarf2 = arrange { - rollOut("01/04/2015", "01/04/2016", Frequency.Quarterly, object { + rollOut("01/04/2015".ld, "01/04/2016".ld, Frequency.Quarterly, object { val uses = variable(4) }) { acmeCorp.may {