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 ede692ce92..39828b8c2e 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Arrangement.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Arrangement.kt @@ -3,7 +3,9 @@ package com.r3corda.contracts.universal import com.google.common.collect.ImmutableSet import com.google.common.collect.Sets import com.r3corda.core.contracts.Amount +import com.r3corda.core.contracts.Frequency import com.r3corda.core.crypto.Party +import java.math.BigDecimal import java.security.PublicKey import java.util.* @@ -15,15 +17,21 @@ interface Arrangement // A base arrangement with no rights and no obligations. Contract cancellation/termination is a transition to ``Zero``. -data class Zero(val dummy: Int = 0) : Arrangement +class Zero() : Arrangement { + override fun hashCode(): Int { + return 0; + } + override fun equals(other: Any?): Boolean { + return other is Zero + } +} - -// A base arrangement representing immediate transfer of Cash - X amount of currency CCY from party A to party B. +// A basic arrangement representing immediate transfer of Cash - X amount of currency CCY from party A to party B. // X is an observable of type BigDecimal. // // todo: should be replaced with something that uses Corda assets and/or cash? -data class Transfer(val amount: Perceivable, val currency: Currency, val from: Party, val to: Party) : Arrangement { - constructor(amount: Amount, from: Party, to: Party ) : this(const(amount.quantity), amount.token, from, to) +data class Transfer(val amount: Perceivable>, val from: Party, val to: Party) : Arrangement { + constructor(amount: Amount, from: Party, to: Party ) : this(const(amount), from, to) } @@ -42,3 +50,6 @@ data class Action(val name: String, val condition: Perceivable, // only actions can be or'ed togetherA combinator that can only be used on action arrangements. This means only one of the action can be executed. Should any one action be executed, all other actions are discarded. data class Or(val actions: Set) : Arrangement + + +data class RollOut(val startDate: String, val endDate: String, val frequency: Frequency, val arrangement: Arrangement) : Arrangement \ No newline at end of file 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 a665b57160..5368e372d7 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.Amount import java.math.BigDecimal import java.text.DateFormat import java.time.Instant @@ -62,7 +63,16 @@ enum class Operation { data class PerceivableOperation(val left: Perceivable, val op: Operation, val right: Perceivable) : Perceivable -infix fun Perceivable.plus(n: BigDecimal) = PerceivableOperation(this, Operation.PLUS, const(n)) -infix fun Perceivable.minus(n: BigDecimal) = PerceivableOperation(this, Operation.MINUS, const(n)) -infix fun Perceivable.times(n: BigDecimal) = PerceivableOperation(this, Operation.TIMES, const(n)) -infix fun Perceivable.div(n: BigDecimal) = PerceivableOperation(this, Operation.DIV, const(n)) \ No newline at end of file +operator fun Perceivable.plus(n: BigDecimal) = PerceivableOperation(this, Operation.PLUS, const(n)) +operator fun Perceivable.minus(n: BigDecimal) = PerceivableOperation(this, Operation.MINUS, const(n)) +operator fun Perceivable.plus(n: Double) = PerceivableOperation(this, Operation.PLUS, const(BigDecimal(n))) +operator fun Perceivable.minus(n: Double) = PerceivableOperation(this, Operation.MINUS, const(BigDecimal(n))) +operator fun Perceivable.times(n: BigDecimal) = PerceivableOperation(this, Operation.TIMES, const(n)) +operator fun Perceivable.div(n: BigDecimal) = PerceivableOperation(this, Operation.DIV, const(n)) +operator fun Perceivable.times(n: Double) = PerceivableOperation(this, Operation.TIMES, const(BigDecimal(n))) +operator fun Perceivable.div(n: Double) = PerceivableOperation(this, Operation.DIV, const(BigDecimal(n))) + +data class ScaleAmount(val left: Perceivable, val right: Perceivable>) : Perceivable> + +operator fun Perceivable.times(n: Amount) = ScaleAmount(this, const(n)) + //PerceivableOperation(this, Operation.TIMES, const(BigDecimal(n))) 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 f1d5be805e..a134d53fdb 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/UniversalContract.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/UniversalContract.kt @@ -27,7 +27,7 @@ class UniversalContract : Contract { // must be signed by all parties present in contract before and after command class Move(val from: Party, val to: Party) : TypeOnlyCommandData(), Commands - // must be signed by all parties present in contract + // must be signed by all liable parties present in contract class Issue : TypeOnlyCommandData(), Commands } diff --git a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Util.kt b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Util.kt index 1bdf37b94a..ef8ad8ed38 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Util.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Util.kt @@ -66,7 +66,7 @@ fun replaceParty(action: Action, from: Party, to: Party) : Action { fun replaceParty(arrangement: Arrangement, from: Party, to: Party) : Arrangement { return when (arrangement) { is Zero -> arrangement - is Transfer -> Transfer( arrangement.amount, arrangement.currency, + is Transfer -> Transfer( arrangement.amount, if (arrangement.from == from) to else arrangement.from, if (arrangement.to == from) to else arrangement.to ) is Action -> replaceParty(arrangement, from, to) 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 fbe4dd6952..eb3866c02c 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/literal.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/literal.kt @@ -1,6 +1,7 @@ package com.r3corda.contracts.universal import com.r3corda.core.contracts.Amount +import com.r3corda.core.contracts.Frequency import com.r3corda.core.crypto.Party import java.math.BigDecimal import java.util.* @@ -30,10 +31,14 @@ class ContractBuilder { contracts.add( Transfer(amount, this, beneficiary)) } - fun Party.gives(beneficiary: Party, amount: Perceivable, currency: Currency) { - contracts.add( Transfer(amount, currency, this, beneficiary)) + fun Party.gives(beneficiary: Party, amount: Perceivable>) { + contracts.add( Transfer(amount, this, beneficiary)) } + /* fun Party.gives(beneficiary: Party, amount: Perceivable, currency: Currency) { + contracts.add( Transfer(amount, currency, this, beneficiary)) + }*/ + fun final() = when (contracts.size) { 0 -> zero @@ -89,87 +94,21 @@ infix fun Set.or(party: Party) = this.plus(party) fun arrange(init: ContractBuilder.() -> Unit ) : Arrangement { val b = ContractBuilder() b.init() - return b.final(); + return b.final() } +class RolloutBuilder(val startDate: String, val endDate: String, val frequency: Frequency) { + val start = "start date" + val end = "end date" + fun recurse() = zero -/* -val my_cds_contract = + fun final() = + RollOut(startDate, endDate, frequency, zero) +} - roadRunner.may { - "exercise".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) { - wileECoyote.gives(roadRunner, 1.M*USD) - } - } or (roadRunner or wileECoyote).may { - "expire".givenThat(after("2017-09-01")) {} - } - -val my_fx_swap = - - (roadRunner or wileECoyote).may { - "execute".givenThat(after("2017-09-01")) { - wileECoyote.gives(roadRunner, 1200.K*USD) - roadRunner.gives(wileECoyote, 1.M*EUR) - } - } - -val my_fx_option = - - roadRunner.may { - "exercise".anytime { - (roadRunner or wileECoyote).may { - "execute".givenThat(after("2017-09-01")) { - wileECoyote.gives(roadRunner, 1200.K*USD) - roadRunner.gives(wileECoyote, 1.M*EUR) - } - } - } - } or wileECoyote.may { - "expire".givenThat(after("2017-09-01")) {} - } - -val my_fx_knock_out_barrier_option = - - roadRunner.may { - "exercise".anytime { - (roadRunner or wileECoyote).may { - "execute".givenThat(after("2017-09-01")) { - wileECoyote.gives(roadRunner, 1200.K*USD) - roadRunner.gives(wileECoyote, 1.M*EUR) - } - } - } - } or wileECoyote.may { - "expire".givenThat(after("2017-09-01")) {} - "knock out".givenThat( EUR / USD gt 1.3 ) {} - } - -val my_fx_knock_in_barrier_option = - - roadRunner.may { - "knock in".givenThat(EUR / USD gt 1.3) { - roadRunner.may { - "exercise".anytime { - (roadRunner or wileECoyote).may { - "execute".givenThat(after("2017-09-01")) { - wileECoyote.gives(roadRunner, 1200.K*USD) - roadRunner.gives(wileECoyote, 1.M*EUR) - } - } - } - } or wileECoyote.may { - "expire".givenThat(after("2017-09-01")) {} - } - } - } or wileECoyote.may { - "expire".givenThat(after("2017-09-01")) {} - } - -//// - -fun fwd(partyA: Party, partyB: Party, maturity: String, contract: Kontract) = - (partyA or partyB).may { - "execute".givenThat(after(maturity)).resolve(contract) - } -*/ \ No newline at end of file +fun rollout(startDate: String, endDate: String, frequency: Frequency, init: RolloutBuilder.() -> Unit) : Arrangement { + val b = RolloutBuilder(startDate, endDate, frequency) + b.init() + return b.final() +} 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 0d8dcf8ba7..7d3c9a6bf9 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/ContractDefinition.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/ContractDefinition.kt @@ -1,5 +1,6 @@ package com.r3corda.contracts.universal +import com.r3corda.core.contracts.Amount import com.r3corda.core.crypto.Party import com.r3corda.core.crypto.generateKeyPair import org.junit.Test @@ -18,8 +19,11 @@ class DummyPerceivable : Perceivable // example: val acmeCorporationHasDefaulted = DummyPerceivable() -// example: -val euribor3M = DummyPerceivable() + +fun libor(amount: Amount, start: String, end: String) : Perceivable> = DummyPerceivable() + +fun interest(rate: Amount, dayCountConvention: String, interest: Double /* todo - appropriate type */, + start: String, end: String) : Perceivable> = DummyPerceivable() // Test parties val roadRunner = Party("Road Runner", generateKeyPair().public) diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/Swaption.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Swaption.kt new file mode 100644 index 0000000000..5fbec4b92d --- /dev/null +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Swaption.kt @@ -0,0 +1,74 @@ +package com.r3corda.contracts.universal + +import com.r3corda.core.contracts.Frequency + +/** + * Created by sofusmortensen on 28/06/16. + */ + +// Swaption + + + +class Swaption { + + val notional = 10.M * USD + val coupon = 1.5 + + val contract = + (wileECoyote or roadRunner).may { + "proceed".givenThat(after("01/07/2015")) { + wileECoyote.gives(roadRunner, libor( notional, "01/04/2015", "01/07/2015" ) ) + roadRunner.gives(wileECoyote, interest( notional, "act/365", coupon, "01/04/2015", "01/07/2015" ) ) + (wileECoyote or roadRunner).may { + "proceed".givenThat(after("01/10/2015")) { + wileECoyote.gives(roadRunner, libor( notional, "01/07/2015", "01/10/2015" ) ) + roadRunner.gives(wileECoyote, interest( notional, "act/365", coupon, "01/07/2015", "01/10/2015" ) ) + + (wileECoyote or roadRunner).may { + // etc ... + } + } + } or roadRunner.may { + "cancel".anytime { + roadRunner.gives( wileECoyote, 10.K * USD ) + } + } + } + } or roadRunner.may { + "cancel".anytime { + roadRunner.gives( wileECoyote, 10.K * USD ) + } + } + + + val contract2 = rollout( "01/04/2015", "01/04/2025", Frequency.Quarterly ) { + (wileECoyote or roadRunner).may { + "proceed".givenThat(after(start)) { + wileECoyote.gives(roadRunner, libor( notional, start, end ) ) + roadRunner.gives(wileECoyote, interest( notional, "act/365", coupon, start, end ) ) + recurse() + } + } or roadRunner.may { + "cancel".anytime { + roadRunner.gives( wileECoyote, 10.K * USD ) + } + } + } + + + val strike = 1.2 + val tarf = rollout( "01/04/2015", "01/04/2016", Frequency.Quarterly ) { + roadRunner.may { + "exercise".givenThat(before(start)) { + wileECoyote.gives(roadRunner, (EUR / USD - strike) * notional ) + recurse() + } + } or (roadRunner or wileECoyote).may { + "proceed".givenThat(after(start)) { + recurse() + } + } + } + +} \ No newline at end of file diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/ZCB.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/ZeroCouponBond.kt similarity index 99% rename from experimental/src/test/kotlin/com/r3corda/contracts/universal/ZCB.kt rename to experimental/src/test/kotlin/com/r3corda/contracts/universal/ZeroCouponBond.kt index 2c7d64b197..bfb830e03a 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/ZCB.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/ZeroCouponBond.kt @@ -8,7 +8,7 @@ import org.junit.Test * Created by sofusmortensen on 01/06/16. */ -class ZCB { +class ZeroCouponBond { val contract = (roadRunner or wileECoyote).may { 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 8df7b22a8e..c2a418202b 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/examples.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/examples.kt @@ -11,6 +11,7 @@ import java.util.* * Created by sofusmortensen on 23/05/16. */ +// various example arrangements using basic syntax val cds_contract = Action("payout", acmeCorporationHasDefaulted and before("2017-09-01"), roadRunner,