From 9c5ef6cdfe6a30f143cd38f9f55d06ccc31033cc Mon Sep 17 00:00:00 2001 From: sofusmortensen Date: Sat, 17 Sep 2016 16:16:08 +0200 Subject: [PATCH] Universal: more refactor + cap rollout unit test --- .../contracts/universal/Arrangement.kt | 1 - .../contracts/universal/Perceivable.kt | 9 +- .../contracts/universal/UniversalContract.kt | 2 +- .../contracts/universal/contractFunctions.kt | 9 +- .../r3corda/contracts/universal/literal.kt | 71 +++-- .../com/r3corda/contracts/universal/Cap.kt | 281 +++++++++++++----- .../com/r3corda/contracts/universal/Caplet.kt | 24 +- .../contracts/universal/ContractDefinition.kt | 56 ++-- .../com/r3corda/contracts/universal/FXSwap.kt | 10 +- .../com/r3corda/contracts/universal/IRS.kt | 22 +- .../contracts/universal/RollOutTests.kt | 80 +++-- .../r3corda/contracts/universal/Swaption.kt | 117 ++++---- .../contracts/universal/ZeroCouponBond.kt | 17 +- .../r3corda/contracts/universal/examples.kt | 92 +++--- 14 files changed, 515 insertions(+), 276 deletions(-) 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 82cd75b4ea..83fb0bd361 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Arrangement.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Arrangement.kt @@ -35,7 +35,6 @@ data class Transfer(val amount: Perceivable, val currency: Currency, // The ``And`` combinator cannot be root in a arrangement. data class And(val arrangements: Set) : Arrangement - data class Action(val name: String, val condition: Perceivable, val actors: Set, val arrangement: Arrangement) 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 a7e1a36019..c848747804 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Perceivable.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Perceivable.kt @@ -38,10 +38,11 @@ data class Const(val value: T) : Perceivable { return false } - override fun hashCode(): Int { - val h = value!!.hashCode() - return h - } + override fun hashCode(): Int = + if (value is BigDecimal) + value.toDouble().hashCode() + else + value!!.hashCode() } fun const(k: T) = Const(k) 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 963f325f6d..63ad0f1b38 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/UniversalContract.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/UniversalContract.kt @@ -245,7 +245,7 @@ class UniversalContract : Contract { val expectedArr = replaceFixing(tx, arr, value.fixes.associateBy({ it.of }, { it.value }), unusedFixes) - // debugCompare(expectedArr, outState.details) +// debugCompare(expectedArr, outState.details) requireThat { "relevant fixing must be included" by unusedFixes.isEmpty() diff --git a/experimental/src/main/kotlin/com/r3corda/contracts/universal/contractFunctions.kt b/experimental/src/main/kotlin/com/r3corda/contracts/universal/contractFunctions.kt index bb8f6dea58..373362ca74 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/contractFunctions.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/contractFunctions.kt @@ -17,10 +17,11 @@ fun swap(partyA: Party, amountA: BigDecimal, currencyA: Currency, partyB: Party, fun fx_swap(expiry: String, notional: BigDecimal, strike: BigDecimal, foreignCurrency: Currency, domesticCurrency: Currency, partyA: Party, partyB: Party) = arrange { - - (partyA or partyB).may { - "execute".givenThat(after(expiry)) { - swap(partyA, notional * strike, domesticCurrency, partyB, notional, foreignCurrency) + actions { + (partyA or partyB).may { + "execute".givenThat(after(expiry)) { + swap(partyA, notional * strike, domesticCurrency, partyB, notional, foreignCurrency) + } } } } 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 bd53c88623..e48595ec64 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/literal.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/literal.kt @@ -22,8 +22,44 @@ val Double.bd: BigDecimal get() = BigDecimal(this) val zero = Zero() +class ActionsBuilder { + private var actions = mutableSetOf() + + fun final() = + if (actions.isEmpty()) + zero + else + Actions(actions.toSet()) + + fun Party.may(init: ActionBuilder.() -> Action): Action { + val builder = ActionBuilder(setOf(this)) + builder.init() + actions.addAll( builder.actions ) + return builder.actions.first() + } + + fun Set.may(init: ActionBuilder.() -> Action): Action { + val builder = ActionBuilder(this) + builder.init() + actions.addAll( builder.actions ) + + return builder.actions.first() + } + + infix fun Party.or(party: Party) = setOf(this, party) + infix fun Set.or(party: Party) = this.plus(party) +} + open class ContractBuilder { - val contracts = mutableListOf() + private val contracts = mutableListOf() + + fun actions(init: ActionsBuilder.() -> Action ) : Arrangement { + val b = ActionsBuilder() + b.init() + val c = b.final() + contracts.add(c) + return c + } fun Party.gives(beneficiary: Party, amount: BigDecimal, currency: Currency): Transfer { val c = Transfer(const(amount), currency, this, beneficiary) @@ -37,25 +73,6 @@ open class ContractBuilder { return c } - fun Party.may(init: ActionBuilder.() -> Unit): Actions { - val b = ActionBuilder(setOf(this)) - b.init() - val c = Actions(b.actions.toSet()) - contracts.add(c) - return c - } - - fun Set.may(init: ActionBuilder.() -> Unit): Actions { - val b = ActionBuilder(this) - b.init() - val c = Actions(b.actions.toSet()) - contracts.add(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) { @@ -77,7 +94,7 @@ open class ContractBuilder { contracts.add( Transfer(amount, currency, this, beneficiary)) }*/ - infix fun Arrangement.and(arrangement: Arrangement) = And(setOf(this, arrangement)) +// infix fun Arrangement.and(arrangement: Arrangement) = And(setOf(this, arrangement)) val start = StartDate() val end = EndDate() @@ -130,10 +147,12 @@ interface GivenThatResolve { class ActionBuilder(val actors: Set) { val actions = mutableListOf() - fun String.givenThat(condition: Perceivable, init: ContractBuilder.() -> Arrangement ) { + fun String.givenThat(condition: Perceivable, init: ContractBuilder.() -> Arrangement ) : Action { val b = ContractBuilder() b.init() - actions.add( Action(this, condition, actors, b.final() ) ) + val a = Action(this, condition, actors, b.final() ) + actions.add( a ) + return a } fun String.givenThat(condition: Perceivable ) : GivenThatResolve { @@ -145,10 +164,12 @@ class ActionBuilder(val actors: Set) { } } - fun String.anytime(init: ContractBuilder.() -> Unit ) { + fun String.anytime(init: ContractBuilder.() -> Unit ) : Action { val b = ContractBuilder() b.init() - actions.add( Action(this, const(true), actors, b.final() ) ) + val a = Action(this, const(true), actors, b.final() ) + actions.add( a ) + return a } } 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 d23b555675..4b5a725543 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/Cap.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Cap.kt @@ -1,5 +1,6 @@ package com.r3corda.contracts.universal +import com.r3corda.core.contracts.BusinessCalendar import com.r3corda.core.contracts.FixOf import com.r3corda.core.contracts.Frequency import com.r3corda.core.contracts.Tenor @@ -23,89 +24,174 @@ class Cap { val tradeDate: LocalDate = LocalDate.of(2016, 9, 1) val contract = arrange { - rollOut("2016-09-01".ld, "2017-04-01".ld, Frequency.Quarterly) { + rollOut("2016-09-01".ld, "2017-09-01".ld, Frequency.Quarterly) { + actions { + (acmeCorp or highStreetBank).may { + "exercise".anytime { + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) + val fixed = interest(notional, "act/365", 0.5.bd, start, end) + highStreetBank.gives(acmeCorp, floating - fixed, currency) + next() + } + } + acmeCorp.may { + "skip".anytime { + next() + } + } + } + } + } + + val contractFixed = arrange { + actions { (acmeCorp or highStreetBank).may { - "exercise".anytime { - val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) - val fixed = interest(notional, "act/365", 0.5.bd, start, end) - highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency) - next() + "exercise".anytime() { + val floating1 = interest(notional, "act/365", 1.0.bd, "2016-09-01", "2016-12-01") + val fixed1 = interest(notional, "act/365", 0.5.bd, "2016-09-01", "2016-12-01") + highStreetBank.gives(acmeCorp, floating1 - fixed1, currency) + rollOut("2016-12-01".ld, "2017-09-01".ld, Frequency.Quarterly) { + actions { + (acmeCorp or highStreetBank).may { + "exercise".anytime { + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) + val fixed = interest(notional, "act/365", 0.5.bd, start, end) + highStreetBank.gives(acmeCorp, floating - fixed, currency) + next() + } + } + acmeCorp.may { + "skip".anytime { + next() + } + } + } + } } } acmeCorp.may { "skip".anytime { - next() - } - } - } - } - - - - val contractFixed = arrange { - (acmeCorp or highStreetBank).may { - "exercise".anytime() { - val floating1 = interest(notional, "act/365", 1.0.bd, "2016-09-01", "2016-12-01") - val fixed1 = interest(notional, "act/365", 0.5.bd, "2016-09-01", "2016-12-01") - highStreetBank.gives(acmeCorp, (floating1 - fixed1).plus(), currency) - rollOut("2016-12-01".ld, "2017-04-01".ld, Frequency.Quarterly) { - (acmeCorp or highStreetBank).may { - "exercise".anytime { - val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) - val fixed = interest(notional, "act/365", 0.5.bd, start, end) - highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency) - next() - } - } - acmeCorp.may { - "skip".anytime { - next() - } - } - } - } - } - acmeCorp.may { - "skip".anytime { - rollOut("2016-12-01".ld, "2017-04-01".ld, Frequency.Quarterly) { - (acmeCorp or highStreetBank).may { - "exercise".anytime { - val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) - val fixed = interest(notional, "act/365", 0.5.bd, start, end) - highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency) - next() - } - } - acmeCorp.may { - "skip".anytime { - next() + rollOut("2016-12-01".ld, "2017-09-01".ld, Frequency.Quarterly) { + actions { + (acmeCorp or highStreetBank).may { + "exercise".anytime { + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) + val fixed = interest(notional, "act/365", 0.5.bd, start, end) + highStreetBank.gives(acmeCorp, floating - fixed, currency) + next() + } + } + acmeCorp.may { + "skip".anytime { + next() + } + } } } } } } } + val contractFixed2 = arrange { + actions { + (acmeCorp or highStreetBank).may { + "exercise".anytime() { + val floating1 = interest(notional, "act/365", 1.0.bd, "2016-12-01", "2017-03-01") + val fixed1 = interest(notional, "act/365", 0.5.bd, "2016-12-01", "2017-03-01") + highStreetBank.gives(acmeCorp, floating1 - fixed1, currency) + rollOut("2017-03-01".ld, "2017-09-01".ld, Frequency.Quarterly) { + actions { + (acmeCorp or highStreetBank).may { + "exercise".anytime { + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) + val fixed = interest(notional, "act/365", 0.5.bd, start, end) + highStreetBank.gives(acmeCorp, floating - fixed, currency) + next() + } + } + acmeCorp.may { + "skip".anytime { + next() + } + } + } + } + } + } + acmeCorp.may { + "skip".anytime { + rollOut("2017-03-01".ld, "2017-09-01".ld, Frequency.Quarterly) { + actions { + (acmeCorp or highStreetBank).may { + "exercise".anytime { + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) + val fixed = interest(notional, "act/365", 0.5.bd, start, end) + highStreetBank.gives(acmeCorp, floating - fixed, currency) + next() + } + } + acmeCorp.may { + "skip".anytime { + next() + } + } + } + } + } + } + } + } + + val contractAfterExecute = arrange { + rollOut("2016-12-01".ld, "2017-09-01".ld, Frequency.Quarterly) { + actions { + (acmeCorp or highStreetBank).may { + "exercise".anytime { + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) + val fixed = interest(notional, "act/365", 0.5.bd, start, end) + highStreetBank.gives(acmeCorp, floating - fixed, currency) + next() + } + } + acmeCorp.may { + "skip".anytime { + next() + } + } + } + } + } + + val paymentFirst = arrange { highStreetBank.gives(acmeCorp, 250.K, EUR) } + val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract) val stateFixed = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractFixed) + val stateAfterExecute = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractAfterExecute) + val statePaymentFirst = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), paymentFirst) + + val stateFixed2 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractFixed2) + val contractLimitedCap = arrange { rollOut("2016-04-01".ld, "2017-04-01".ld, Frequency.Quarterly, object { val limit = variable(150.K) }) { - (acmeCorp or highStreetBank).may { - "exercise".anytime { - val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) - val fixed = interest(notional, "act/365", 0.5.bd, start, end) - val payout = min(floating - fixed) - highStreetBank.gives(acmeCorp, payout, currency) - next(vars.limit to vars.limit - payout) + actions { + (acmeCorp or highStreetBank).may { + "exercise".anytime { + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) + val fixed = interest(notional, "act/365", 0.5.bd, start, end) + val payout = min(floating - fixed) + highStreetBank.gives(acmeCorp, payout, currency) + next(vars.limit to vars.limit - payout) + } } - } - acmeCorp.may { - "skip".anytime { - next() + acmeCorp.may { + "skip".anytime { + next() + } } } } @@ -131,12 +217,7 @@ class Cap { } @Test - fun `print debugging`() { - // debugprint(contract) - } - - @Test - fun `fixing`() { + fun `first fixing`() { transaction { input { stateStart } output { stateFixed } @@ -180,4 +261,68 @@ class Cap { } } + @Test + fun `first execute`() { + transaction { + input { stateFixed } + output { stateAfterExecute } + output { statePaymentFirst } + + 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() + } + } + + @Test + fun `second fixing`() { + transaction { + input { stateAfterExecute } + output { stateFixed2 } + 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", BusinessCalendar.parseDateFromString("2016-12-01"), Tenor("3M")), 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", BusinessCalendar.parseDateFromString("2016-12-01").plusYears(1), Tenor("3M")), 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", BusinessCalendar.parseDateFromString("2016-12-01"), Tenor("9M")), 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", BusinessCalendar.parseDateFromString("2016-12-01"), Tenor("3M")), 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", BusinessCalendar.parseDateFromString("2016-12-01"), Tenor("3M")), 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 998a262909..68539df199 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/Caplet.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Caplet.kt @@ -24,21 +24,25 @@ class Caplet { val currency = EUR val contract = arrange { - (acmeCorp or highStreetBank).may { - "exercise".anytime() { - 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) + actions { + (acmeCorp or highStreetBank).may { + "exercise".anytime() { + 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) + } } } } val contractFixed = arrange { - (acmeCorp or highStreetBank).may { - "exercise".anytime() { - val floating = interest(notional, "act/365", 1.0.bd, "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) + actions { + (acmeCorp or highStreetBank).may { + "exercise".anytime() { + val floating = interest(notional, "act/365", 1.0.bd, "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) + } } } } diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/ContractDefinition.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/ContractDefinition.kt index b55b1a066e..b71967ab30 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/ContractDefinition.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/ContractDefinition.kt @@ -28,48 +28,54 @@ class ContractDefinition { val cds_contract = arrange { - acmeCorp.may { - "payout".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) { - highStreetBank.gives(acmeCorp, 1.M, USD) + actions { + acmeCorp.may { + "payout".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) { + highStreetBank.gives(acmeCorp, 1.M, USD) + } } - } - highStreetBank.may { - "expire".givenThat(after("2017-09-01")) { - zero + highStreetBank.may { + "expire".givenThat(after("2017-09-01")) { + zero + } } } } val american_fx_option = arrange { - acmeCorp.may { - "exercise".anytime { - highStreetBank.gives(acmeCorp, 1.M, EUR) - acmeCorp.gives(highStreetBank, 1200.K, USD) + actions { + acmeCorp.may { + "exercise".anytime { + highStreetBank.gives(acmeCorp, 1.M, EUR) + acmeCorp.gives(highStreetBank, 1200.K, USD) + } } - } - highStreetBank.may { - "expire".givenThat(after("2017-09-01")) { - zero + highStreetBank.may { + "expire".givenThat(after("2017-09-01")) { + zero + } } } } val european_fx_option = arrange { - acmeCorp.may { - "exercise".anytime { - (acmeCorp or highStreetBank).may { - "execute".givenThat(after("2017-09-01")) { - highStreetBank.gives(acmeCorp, 1.M, EUR) - acmeCorp.gives(highStreetBank, 1200.K, USD) + actions { + acmeCorp.may { + "exercise".anytime { + (acmeCorp or highStreetBank).may { + "execute".givenThat(after("2017-09-01")) { + highStreetBank.gives(acmeCorp, 1.M, EUR) + acmeCorp.gives(highStreetBank, 1200.K, USD) + } } } } - } - highStreetBank.may { - "expire".givenThat(after("2017-09-01")) { - zero + highStreetBank.may { + "expire".givenThat(after("2017-09-01")) { + zero + } } } } diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/FXSwap.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/FXSwap.kt index ca675e2795..7337106a84 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/FXSwap.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/FXSwap.kt @@ -15,10 +15,12 @@ class FXSwap { val TEST_TX_TIME_TOO_EARLY: Instant get() = Instant.parse("2017-08-31T12:00:00.00Z") val contract = arrange { - (acmeCorp or highStreetBank).may { - "execute".givenThat(after("2017-09-01")) { - highStreetBank.gives(acmeCorp, 1200.K, USD) - acmeCorp.gives(highStreetBank, 1.M, EUR) + actions { + (acmeCorp or highStreetBank).may { + "execute".givenThat(after("2017-09-01")) { + highStreetBank.gives(acmeCorp, 1200.K, USD) + acmeCorp.gives(highStreetBank, 1.M, EUR) + } } } } diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/IRS.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/IRS.kt index 5bd49a7073..9fb8ceb1b6 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/IRS.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/IRS.kt @@ -23,17 +23,19 @@ class IRS { val contract = arrange { rollOut("2016-09-01".ld, "2018-09-01".ld, Frequency.Quarterly) { - (acmeCorp or highStreetBank).may { - val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) - val fixed = interest(notional, "act/365", 0.5.bd, start, end) + actions { + (acmeCorp or highStreetBank).may { + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) + val fixed = interest(notional, "act/365", 0.5.bd, start, end) - "pay 1".anytime { - highStreetBank.gives(acmeCorp, floating - fixed, currency) - next() - } - "pay 2".anytime { - highStreetBank.gives(acmeCorp, fixed - floating, currency) - next() + "pay 1".anytime { + highStreetBank.gives(acmeCorp, floating - fixed, currency) + next() + } + "pay 2".anytime { + highStreetBank.gives(acmeCorp, fixed - floating, currency) + next() + } } } } diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/RollOutTests.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/RollOutTests.kt index 4973bc51eb..4a27710ba9 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/RollOutTests.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/RollOutTests.kt @@ -18,21 +18,26 @@ class RollOutTests { 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() + actions { + (acmeCorp or highStreetBank).may { + "transfer".givenThat(after(end)) { + highStreetBank.gives(acmeCorp, 10.K, USD) + next() + } } } } } + val contract2 = 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() + actions { + (acmeCorp or highStreetBank).may { + "transfer".givenThat(after(end)) { + highStreetBank.gives(acmeCorp, 10.K, USD) + next() + } } } } @@ -41,10 +46,12 @@ class RollOutTests { val contractStep1a = arrange { rollOut("2016-10-03".ld, "2017-09-01".ld, Frequency.Monthly) { - (acmeCorp or highStreetBank).may { - "transfer".givenThat(after(end)) { - highStreetBank.gives(acmeCorp, 10.K, USD) - next() + actions { + (acmeCorp or highStreetBank).may { + "transfer".givenThat(after(end)) { + highStreetBank.gives(acmeCorp, 10.K, USD) + next() + } } } } @@ -64,41 +71,54 @@ class RollOutTests { highStreetBank.gives(acmeCorp, 10.K, USD) } val contract_action1 = arrange { - highStreetBank.may { - "do it".anytime { - highStreetBank.gives(acmeCorp, 10.K, USD) + actions { + highStreetBank.may { + "do it".anytime { + highStreetBank.gives(acmeCorp, 10.K, USD) + } } } } val contract_action2 = arrange { - highStreetBank.may { - "do it".anytime { - highStreetBank.gives(acmeCorp, 10.K, USD) + actions { + highStreetBank.may { + "do it".anytime { + highStreetBank.gives(acmeCorp, 10.K, USD) + } } } } val contract_and1 = arrange { - highStreetBank.may { - "do it".anytime { - highStreetBank.gives(acmeCorp, 10.K, USD) + actions { + highStreetBank.may { + "do it".anytime { + highStreetBank.gives(acmeCorp, 10.K, USD) + } } } - acmeCorp.may { - "do it".anytime { - acmeCorp.gives(momAndPop, 10.K, USD) + actions { + acmeCorp.may { + "do it".anytime { + acmeCorp.gives(momAndPop, 10.K, USD) + } } } next() + } val contract_and2 = arrange { - highStreetBank.may { - "do it".anytime { - highStreetBank.gives(acmeCorp, 10.K, USD) + actions { + highStreetBank.may { + "do it".anytime { + highStreetBank.gives(acmeCorp, 10.K, USD) + } } } - acmeCorp.may { - "do it".anytime { - acmeCorp.gives(momAndPop, 10.K, USD) + actions { + acmeCorp.may { + "do it".anytime { + acmeCorp.gives(momAndPop, 10.K, USD) + } } } next() 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 d52623719e..76d0d7a69e 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/Swaption.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Swaption.kt @@ -17,30 +17,37 @@ class Swaption { val coupon = 1.5.bd val dreary_contract = arrange { - (highStreetBank or acmeCorp).may { - "proceed".givenThat(after("01/07/2015")) { - highStreetBank.gives(acmeCorp, libor(notional, "01/04/2015", "01/07/2015"), currency) - acmeCorp.gives(highStreetBank, interest(notional, "act/365", coupon, "01/04/2015", "01/07/2015"), currency) - (highStreetBank or acmeCorp).may { - "proceed".givenThat(after("01/10/2015")) { - highStreetBank.gives(acmeCorp, libor(notional, "01/07/2015", "01/10/2015"), currency) - acmeCorp.gives(highStreetBank, interest(notional, "act/365", coupon, "01/07/2015", "01/10/2015"), currency) + actions { + (highStreetBank or acmeCorp).may { + "proceed".givenThat(after("01/07/2015")) { + highStreetBank.gives(acmeCorp, libor(notional, "01/04/2015", "01/07/2015"), currency) + acmeCorp.gives(highStreetBank, interest(notional, "act/365", coupon, "01/04/2015", "01/07/2015"), currency) + (highStreetBank or acmeCorp).may { + "proceed".givenThat(after("01/10/2015")) { + highStreetBank.gives(acmeCorp, libor(notional, "01/07/2015", "01/10/2015"), currency) + acmeCorp.gives(highStreetBank, interest(notional, "act/365", coupon, "01/07/2015", "01/10/2015"), currency) - (highStreetBank or acmeCorp).may { - // etc ... + actions { + (highStreetBank or acmeCorp).may { + "dummy".anytime { zero } + // etc ... + } + } + } + } + actions { + acmeCorp.may { + "cancel".anytime { + acmeCorp.gives(highStreetBank, 10.K, USD) + } } } } - acmeCorp.may { - "cancel".anytime { - acmeCorp.gives(highStreetBank, 10.K, USD) - } - } } - } - acmeCorp.may { - "cancel".anytime { - acmeCorp.gives(highStreetBank, 10.K, USD) + acmeCorp.may { + "cancel".anytime { + acmeCorp.gives(highStreetBank, 10.K, USD) + } } } } @@ -48,16 +55,18 @@ class Swaption { val elegant_contract = arrange { 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) - acmeCorp.gives(highStreetBank, interest(notional, "act/365", coupon, start, end), currency) - next() + actions { + (highStreetBank or acmeCorp).may { + "proceed".givenThat(after(start)) { + highStreetBank.gives(acmeCorp, libor(notional, start, end), currency) + acmeCorp.gives(highStreetBank, interest(notional, "act/365", coupon, start, end), currency) + next() + } } - } - acmeCorp.may { - "cancel".anytime { - acmeCorp.gives(highStreetBank, 10.K, currency) + acmeCorp.may { + "cancel".anytime { + acmeCorp.gives(highStreetBank, 10.K, currency) + } } } } @@ -69,21 +78,25 @@ class Swaption { rollOut("01/04/2015".ld, "01/04/2016".ld, Frequency.Quarterly, object { val cap = variable(150.K) }) { - acmeCorp.may { - "exercise".givenThat(before(end)) { - val payout = (EUR / USD - strike).plus() * notional + actions { + acmeCorp.may { + "exercise".givenThat(before(end)) { + val payout = (EUR / USD - strike).plus() * notional - (acmeCorp or highStreetBank).may { - "proceed".givenThat(after(end)) { - highStreetBank.gives(acmeCorp, payout, USD) - next(vars.cap to vars.cap - payout) + actions { + (acmeCorp or highStreetBank).may { + "proceed".givenThat(after(end)) { + highStreetBank.gives(acmeCorp, payout, USD) + next(vars.cap to vars.cap - payout) + } + } } } } - } - (acmeCorp or highStreetBank).may { - "proceedWithoutExercise".givenThat(after(end)) { - next() + (acmeCorp or highStreetBank).may { + "proceedWithoutExercise".givenThat(after(end)) { + next() + } } } } @@ -93,21 +106,25 @@ class Swaption { rollOut("01/04/2015".ld, "01/04/2016".ld, Frequency.Quarterly, object { val uses = variable(4) }) { - acmeCorp.may { - "exercise".givenThat(before(end)) { - val payout = (EUR / USD - strike).plus() * notional + actions { + acmeCorp.may { + "exercise".givenThat(before(end)) { + val payout = (EUR / USD - strike).plus() * notional - (acmeCorp or highStreetBank).may { - "proceed".givenThat(after(end)) { - highStreetBank.gives(acmeCorp, payout, currency) - next(vars.uses to vars.uses - 1) + actions { + (acmeCorp or highStreetBank).may { + "proceed".givenThat(after(end)) { + highStreetBank.gives(acmeCorp, payout, currency) + next(vars.uses to vars.uses - 1) + } + } } } } - } - (acmeCorp or highStreetBank).may { - "proceedWithoutExercise".givenThat(after(end)) { - next() + (acmeCorp or highStreetBank).may { + "proceedWithoutExercise".givenThat(after(end)) { + next() + } } } } diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/ZeroCouponBond.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/ZeroCouponBond.kt index 89b5158627..3d82065cba 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/ZeroCouponBond.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/ZeroCouponBond.kt @@ -12,18 +12,21 @@ import java.time.Instant class ZeroCouponBond { val contract = arrange { - (acmeCorp or highStreetBank).may { - "execute".givenThat(after("2017-09-01")) { - highStreetBank.gives(acmeCorp, 100.K, GBP) + actions { + (acmeCorp or highStreetBank).may { + "execute".givenThat(after("2017-09-01")) { + highStreetBank.gives(acmeCorp, 100.K, GBP) + } } } } - val contractMove = arrange { - (momAndPop or highStreetBank).may { - "execute".givenThat(after("2017-09-01")) { - highStreetBank.gives(momAndPop, 100.K, GBP) + actions { + (momAndPop or highStreetBank).may { + "execute".givenThat(after("2017-09-01")) { + highStreetBank.gives(momAndPop, 100.K, GBP) + } } } } diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/examples.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/examples.kt index feb720b4c5..dedf9d4709 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/examples.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/examples.kt @@ -14,9 +14,11 @@ import java.util.* // various example arrangements using basic syntax val cds_contract = arrange { - acmeCorp.may { - "claim".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) { - highStreetBank.gives(acmeCorp, 1.M, USD) + actions { + acmeCorp.may { + "claim".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) { + highStreetBank.gives(acmeCorp, 1.M, USD) + } } } } @@ -24,49 +26,59 @@ val cds_contract = arrange { // fx swap // both parties have the right to trigger the exchange of cash flows val an_fx_swap = arrange { - (acmeCorp or highStreetBank).may { - "execute".givenThat(after("2017-09-01")) { - highStreetBank.gives(acmeCorp, 1200.K, USD) - acmeCorp.gives(highStreetBank, 1.M, EUR) + actions { + (acmeCorp or highStreetBank).may { + "execute".givenThat(after("2017-09-01")) { + highStreetBank.gives(acmeCorp, 1200.K, USD) + acmeCorp.gives(highStreetBank, 1.M, EUR) + } } } } val american_fx_option = arrange { - acmeCorp.may { - "exercise".givenThat(before("2017-09-01")) { - highStreetBank.gives(acmeCorp, 1200.K, USD) - acmeCorp.gives(highStreetBank, 1.M, EUR) + actions { + acmeCorp.may { + "exercise".givenThat(before("2017-09-01")) { + highStreetBank.gives(acmeCorp, 1200.K, USD) + acmeCorp.gives(highStreetBank, 1.M, EUR) + } } } } val european_fx_option = arrange { - acmeCorp.may { - "exercise".givenThat(before("2017-09-01")) { - fx_swap("2017-09-01", 1.M, 1.2.bd, EUR, USD, acmeCorp, highStreetBank) + actions { + acmeCorp.may { + "exercise".givenThat(before("2017-09-01")) { + fx_swap("2017-09-01", 1.M, 1.2.bd, EUR, USD, acmeCorp, highStreetBank) + } } - } - (acmeCorp or highStreetBank).may { - "expire".anytime { - zero + (acmeCorp or highStreetBank).may { + "expire".anytime { + zero + } } } } val contractZeroCouponBond = arrange { - acmeCorp.may { - "execute".givenThat(after("2017-11-01")) { - highStreetBank.gives(acmeCorp, 1.M, USD) + actions { + acmeCorp.may { + "execute".givenThat(after("2017-11-01")) { + highStreetBank.gives(acmeCorp, 1.M, USD) + } } } } // maybe in the presence of negative interest rates you would want other side of contract to be able to take initiative as well val zero_coupon_bond_2 = arrange { - (acmeCorp or highStreetBank).may { - "execute".givenThat(after("2017-09-01")) { - highStreetBank.gives(acmeCorp, 1.M, USD) + actions { + (acmeCorp or highStreetBank).may { + "execute".givenThat(after("2017-09-01")) { + highStreetBank.gives(acmeCorp, 1.M, USD) + } } } } @@ -81,25 +93,31 @@ val zero_coupon_bond_2 = arrange { // Assume observable is using FX fixing // val no_touch = arrange { - (acmeCorp or highStreetBank).may { - "execute".givenThat(after("2017-09-01")) { - highStreetBank.gives(acmeCorp, 1.M, USD) + actions { + (acmeCorp or highStreetBank).may { + "execute".givenThat(after("2017-09-01")) { + highStreetBank.gives(acmeCorp, 1.M, USD) + } + } + highStreetBank.may { + "knock out".givenThat(EUR / USD gt 1.3) { + zero + } } - } - highStreetBank.may { - "knock out".givenThat(EUR/USD gt 1.3) } } val one_touch = arrange { - highStreetBank.may { - "expire".givenThat(after("2017-09-01")) { - zero + actions { + highStreetBank.may { + "expire".givenThat(after("2017-09-01")) { + zero + } } - } - acmeCorp.may { - "knock in".givenThat(EUR / USD gt 1.3) { - highStreetBank.gives(acmeCorp, 1.M, USD) + acmeCorp.may { + "knock in".givenThat(EUR / USD gt 1.3) { + highStreetBank.gives(acmeCorp, 1.M, USD) + } } } }