mirror of
https://github.com/corda/corda.git
synced 2024-12-22 22:32:26 +00:00
universal: simplied Actions squashing condition and actors together (suggested by Simon Peyton-Jones)
This commit is contained in:
parent
edc30717bb
commit
f19df50351
experimental/src
main/kotlin/net/corda/contracts/universal
test/kotlin/net/corda/contracts/universal
@ -30,8 +30,7 @@ data class Obligation(val amount: Perceivable<BigDecimal>, val currency: Currenc
|
||||
// The ``And`` combinator cannot be root in a arrangement.
|
||||
data class And(val arrangements: Set<Arrangement>) : Arrangement
|
||||
|
||||
data class Action(val name: String, val condition: Perceivable<Boolean>,
|
||||
val actors: Set<Party>, val arrangement: Arrangement)
|
||||
data class Action(val name: String, val condition: Perceivable<Boolean>, val arrangement: Arrangement)
|
||||
|
||||
// An action combinator. This declares a list of named action that can be taken by anyone of the actors given that
|
||||
// _condition_ is met. If the action is performed the arrangement state transitions into the specified arrangement.
|
||||
|
@ -27,5 +27,5 @@ fun fx_swap(expiry: String, notional: BigDecimal, strike: BigDecimal,
|
||||
fun fx_swap2(expiry: String, notional: Long, strike: Double,
|
||||
foreignCurrency: Currency, domesticCurrency: Currency,
|
||||
partyA: Party, partyB: Party) =
|
||||
Action("execute", after(expiry), setOf(partyA, partyB),
|
||||
Action("execute", after(expiry) and (signedBy(partyA) or signedBy(partyB)),
|
||||
swap(partyA, BigDecimal(notional * strike), domesticCurrency, partyB, BigDecimal(notional), foreignCurrency))
|
||||
|
@ -150,7 +150,7 @@ class ActionBuilder(val actors: Set<Party>) {
|
||||
fun String.givenThat(condition: Perceivable<Boolean>, init: ContractBuilder.() -> Arrangement): Action {
|
||||
val b = ContractBuilder()
|
||||
b.init()
|
||||
val a = Action(this, condition, actors, b.final())
|
||||
val a = Action(this, condition and signedByOneOf(actors), b.final())
|
||||
actions.add(a)
|
||||
return a
|
||||
}
|
||||
@ -159,7 +159,7 @@ class ActionBuilder(val actors: Set<Party>) {
|
||||
val This = this
|
||||
return object : GivenThatResolve {
|
||||
override fun resolve(contract: Arrangement) {
|
||||
actions.add(Action(This, condition, actors, contract))
|
||||
actions.add(Action(This, condition and signedByOneOf(actors), contract))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -167,7 +167,7 @@ class ActionBuilder(val actors: Set<Party>) {
|
||||
infix fun String.anytime(init: ContractBuilder.() -> Unit): Action {
|
||||
val b = ContractBuilder()
|
||||
b.init()
|
||||
val a = Action(this, const(true), actors, b.final())
|
||||
val a = Action(this, signedByOneOf(actors), b.final())
|
||||
actions.add(a)
|
||||
return a
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.corda.contracts.universal
|
||||
|
||||
import net.corda.core.contracts.BusinessCalendar
|
||||
import net.corda.core.contracts.Tenor
|
||||
import net.corda.core.crypto.Party
|
||||
import java.math.BigDecimal
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
@ -40,7 +41,9 @@ data class Const<T>(val value: T) : Perceivable<T> {
|
||||
value!!.hashCode()
|
||||
}
|
||||
|
||||
fun <T> const(k: T) = Const(k)
|
||||
fun <T> const(k: T) : Perceivable<T> = Const(k)
|
||||
|
||||
// fun const(b: Boolean) : Perceivable<Boolean> = Const(b)
|
||||
|
||||
data class Max(val args: Set<Perceivable<BigDecimal>>) : Perceivable<BigDecimal>
|
||||
|
||||
@ -70,6 +73,18 @@ class EndDate : Perceivable<Instant> {
|
||||
}
|
||||
}
|
||||
|
||||
data class ActorPerceivable(val actor: Party) : Perceivable<Boolean>
|
||||
fun signedBy(actor: Party) : Perceivable<Boolean> = ActorPerceivable(actor)
|
||||
|
||||
fun signedByOneOf(actors: Collection<Party>): Perceivable<Boolean> =
|
||||
if (actors.size == 0)
|
||||
const(true)
|
||||
else
|
||||
actors.drop(1).fold(signedBy(actors.first())) { total, next -> total or signedBy(next) }
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Perceivable based on time
|
||||
*/
|
||||
|
@ -47,11 +47,8 @@ A base contract representing debt of X amount of currency CCY from party A to pa
|
||||
#### ``And contract1 ... contractN``
|
||||
A combinator over a list of contracts. Each contract in list will create a separate independent contract state. The ``And`` combinator cannot be root in a contract.
|
||||
|
||||
#### ``Action name, condition, actors, contract``
|
||||
An action combinator. This declares a named action that can be taken by anyone of the actors given that _condition_ is met. If the action is performed the contract state transitions into the specificed contract.
|
||||
|
||||
#### ``Or action1 ... actionN``
|
||||
A combinator that can only be used on action contracts. This means only one of the action can be executed. Should any one action be executed, all other actions are discarded.
|
||||
#### ``Action [name, condition, contract]``
|
||||
An action combinator. This declares a list of named actions, only one can be taken and only if the condition is satisfied. If the action is performed the contract state transitions into the specificed contract.
|
||||
|
||||
#### ``RollOut startDate endDate frequency contractTemplate``
|
||||
A combinator for rolling out a date sequence using specified template
|
||||
|
@ -40,13 +40,14 @@ class UniversalContract : Contract {
|
||||
|
||||
fun eval(tx: TransactionForContract, expr: Perceivable<Boolean>): Boolean = when (expr) {
|
||||
is PerceivableAnd -> eval(tx, expr.left) && eval(tx, expr.right)
|
||||
is PerceivableOr -> eval(tx, expr.right) || eval(tx, expr.right)
|
||||
is PerceivableOr -> eval(tx, expr.left) || eval(tx, expr.right)
|
||||
is Const<Boolean> -> expr.value
|
||||
is TimePerceivable -> when (expr.cmp) {
|
||||
Comparison.LTE -> tx.timestamp!!.after!! <= eval(tx, expr.instant)
|
||||
Comparison.GTE -> tx.timestamp!!.before!! >= eval(tx, expr.instant)
|
||||
else -> throw NotImplementedError("eval special")
|
||||
}
|
||||
is ActorPerceivable -> tx.commands.single().signers.contains(expr.actor.owningKey)
|
||||
else -> throw NotImplementedError("eval - Boolean - " + expr.javaClass.name)
|
||||
}
|
||||
|
||||
@ -130,6 +131,9 @@ class UniversalContract : Contract {
|
||||
is PerceivableOperation -> PerceivableOperation<T>(replaceStartEnd(p.left, start, end), p.op, replaceStartEnd(p.right, start, end))
|
||||
is Interest -> Interest(replaceStartEnd(p.amount, start, end), p.dayCountConvention, replaceStartEnd(p.interest, start, end), replaceStartEnd(p.start, start, end), replaceStartEnd(p.end, start, end)) as Perceivable<T>
|
||||
is Fixing -> Fixing(p.source, replaceStartEnd(p.date, start, end), p.tenor) as Perceivable<T>
|
||||
is PerceivableAnd -> (replaceStartEnd(p.left, start, end) and replaceStartEnd(p.right, start, end)) as Perceivable<T>
|
||||
is PerceivableOr -> (replaceStartEnd(p.left, start, end) or replaceStartEnd(p.right, start, end)) as Perceivable<T>
|
||||
is ActorPerceivable -> p
|
||||
else -> throw NotImplementedError("replaceStartEnd " + p.javaClass.name)
|
||||
}
|
||||
|
||||
@ -138,14 +142,14 @@ class UniversalContract : Contract {
|
||||
is And -> And(arrangement.arrangements.map { replaceStartEnd(it, start, end) }.toSet())
|
||||
is Zero -> arrangement
|
||||
is Obligation -> Obligation(replaceStartEnd(arrangement.amount, start, end), arrangement.currency, arrangement.from, arrangement.to)
|
||||
is Actions -> Actions(arrangement.actions.map { Action(it.name, replaceStartEnd(it.condition, start, end), it.actors, replaceStartEnd(it.arrangement, start, end)) }.toSet())
|
||||
is Actions -> Actions(arrangement.actions.map { Action(it.name, replaceStartEnd(it.condition, start, end), replaceStartEnd(it.arrangement, start, end)) }.toSet())
|
||||
is Continuation -> arrangement
|
||||
else -> throw NotImplementedError("replaceStartEnd " + arrangement.javaClass.name)
|
||||
}
|
||||
|
||||
fun replaceNext(arrangement: Arrangement, nextReplacement: RollOut): Arrangement =
|
||||
when (arrangement) {
|
||||
is Actions -> Actions(arrangement.actions.map { Action(it.name, it.condition, it.actors, replaceNext(it.arrangement, nextReplacement)) }.toSet())
|
||||
is Actions -> Actions(arrangement.actions.map { Action(it.name, it.condition, replaceNext(it.arrangement, nextReplacement)) }.toSet())
|
||||
is And -> And(arrangement.arrangements.map { replaceNext(it, nextReplacement) }.toSet())
|
||||
is Obligation -> arrangement
|
||||
is Zero -> arrangement
|
||||
@ -155,7 +159,7 @@ class UniversalContract : Contract {
|
||||
|
||||
fun removeNext(arrangement: Arrangement): Arrangement =
|
||||
when (arrangement) {
|
||||
is Actions -> Actions(arrangement.actions.map { Action(it.name, it.condition, it.actors, removeNext(it.arrangement)) }.toSet())
|
||||
is Actions -> Actions(arrangement.actions.map { Action(it.name, it.condition, removeNext(it.arrangement)) }.toSet())
|
||||
is And -> {
|
||||
val a = arrangement.arrangements.map { removeNext(it) }.filter { it != zero }
|
||||
if (a.count() > 1)
|
||||
@ -200,7 +204,8 @@ class UniversalContract : Contract {
|
||||
|
||||
requireThat {
|
||||
"action must be timestamped" by (tx.timestamp != null)
|
||||
"action must be authorized" by (cmd.signers.any { action.actors.any { party -> party.owningKey == it } })
|
||||
// "action must be authorized" by (cmd.signers.any { action.actors.any { party -> party.owningKey == it } })
|
||||
// todo perhaps merge these two requirements?
|
||||
"condition must be met" by (eval(tx, action.condition))
|
||||
}
|
||||
|
||||
@ -290,8 +295,7 @@ class UniversalContract : Contract {
|
||||
|
||||
fun replaceFixing(tx: TransactionForContract, arr: Action,
|
||||
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>) =
|
||||
Action(arr.name, replaceFixing(tx, arr.condition, fixings, unusedFixings),
|
||||
arr.actors, replaceFixing(tx, arr.arrangement, fixings, unusedFixings))
|
||||
Action(arr.name, replaceFixing(tx, arr.condition, fixings, unusedFixings), replaceFixing(tx, arr.arrangement, fixings, unusedFixings))
|
||||
|
||||
fun replaceFixing(tx: TransactionForContract, arr: Arrangement,
|
||||
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Arrangement =
|
||||
@ -299,7 +303,7 @@ class UniversalContract : Contract {
|
||||
is Zero -> arr
|
||||
is And -> And(arr.arrangements.map { replaceFixing(tx, it, fixings, unusedFixings) }.toSet())
|
||||
is Obligation -> Obligation(replaceFixing(tx, arr.amount, fixings, unusedFixings), arr.currency, arr.from, arr.to)
|
||||
is Actions -> Actions(arr.actions.map { Action(it.name, it.condition, it.actors, replaceFixing(tx, it.arrangement, fixings, unusedFixings)) }.toSet())
|
||||
is Actions -> Actions(arr.actions.map { Action(it.name, it.condition, replaceFixing(tx, it.arrangement, fixings, unusedFixings)) }.toSet())
|
||||
is RollOut -> RollOut(arr.startDate, arr.endDate, arr.frequency, replaceFixing(tx, arr.template, fixings, unusedFixings))
|
||||
is Continuation -> arr
|
||||
else -> throw NotImplementedError("replaceFixing - " + arr.javaClass.name)
|
||||
|
@ -2,6 +2,7 @@ package net.corda.contracts.universal
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import com.google.common.collect.Sets
|
||||
import com.sun.tools.corba.se.idl.InvalidArgument
|
||||
import net.corda.core.contracts.Frequency
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.crypto.Party
|
||||
@ -12,6 +13,15 @@ fun Instant.toLocalDate(): LocalDate = LocalDate.ofEpochDay(this.epochSecond / 6
|
||||
|
||||
fun LocalDate.toInstant(): Instant = Instant.ofEpochSecond(this.toEpochDay() * 60 * 60 * 24)
|
||||
|
||||
private fun signingParties(perceivable: Perceivable<Boolean>) : ImmutableSet<CompositeKey> =
|
||||
when (perceivable) {
|
||||
is ActorPerceivable -> ImmutableSet.of( perceivable.actor.owningKey )
|
||||
is PerceivableAnd -> Sets.union( signingParties( perceivable.left ), signingParties(perceivable.right) ).immutableCopy()
|
||||
is PerceivableOr -> Sets.union( signingParties( perceivable.left ), signingParties(perceivable.right) ).immutableCopy()
|
||||
is TimePerceivable -> ImmutableSet.of<CompositeKey>()
|
||||
else -> throw IllegalArgumentException("signingParties " + perceivable)
|
||||
}
|
||||
|
||||
private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<CompositeKey> =
|
||||
when (arrangement) {
|
||||
is Zero -> ImmutableSet.of<CompositeKey>()
|
||||
@ -25,17 +35,19 @@ private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<Composi
|
||||
else -> throw IllegalArgumentException("liableParties " + arrangement)
|
||||
}
|
||||
|
||||
private fun liablePartiesVisitor(action: Action): ImmutableSet<CompositeKey> =
|
||||
if (action.actors.size != 1)
|
||||
liablePartiesVisitor(action.arrangement)
|
||||
else
|
||||
Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(action.actors.single())).immutableCopy()
|
||||
private fun liablePartiesVisitor(action: Action): ImmutableSet<CompositeKey> {
|
||||
val actors = signingParties(action.condition)
|
||||
return if (actors.size != 1)
|
||||
liablePartiesVisitor(action.arrangement)
|
||||
else
|
||||
Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(actors.single())).immutableCopy()
|
||||
}
|
||||
|
||||
/** Returns list of potentially liable parties for a given contract */
|
||||
fun liableParties(contract: Arrangement): Set<CompositeKey> = liablePartiesVisitor(contract)
|
||||
|
||||
private fun involvedPartiesVisitor(action: Action): Set<CompositeKey> =
|
||||
Sets.union(involvedPartiesVisitor(action.arrangement), action.actors.map { it.owningKey }.toSet()).immutableCopy()
|
||||
Sets.union(involvedPartiesVisitor(action.arrangement), signingParties(action.condition)).immutableCopy()
|
||||
|
||||
private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet<CompositeKey> =
|
||||
when (arrangement) {
|
||||
@ -51,11 +63,25 @@ private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet<Compo
|
||||
/** returns list of involved parties for a given contract */
|
||||
fun involvedParties(arrangement: Arrangement): Set<CompositeKey> = involvedPartiesVisitor(arrangement)
|
||||
|
||||
fun replaceParty(perceivable: Perceivable<Boolean>, from: Party, to: Party): Perceivable<Boolean> =
|
||||
when (perceivable) {
|
||||
is ActorPerceivable ->
|
||||
if (perceivable.actor == from)
|
||||
signedBy(to)
|
||||
else
|
||||
perceivable
|
||||
is PerceivableAnd -> replaceParty(perceivable.left, from, to) and replaceParty(perceivable.right, from, to)
|
||||
is PerceivableOr -> replaceParty(perceivable.left, from, to) or replaceParty(perceivable.right, from, to)
|
||||
is TimePerceivable -> perceivable
|
||||
else -> throw InvalidArgument("replaceParty " + perceivable)
|
||||
}
|
||||
|
||||
fun replaceParty(action: Action, from: Party, to: Party): Action =
|
||||
if (action.actors.contains(from)) {
|
||||
Action(action.name, action.condition, action.actors - from + to, replaceParty(action.arrangement, from, to))
|
||||
} else
|
||||
Action(action.name, action.condition, action.actors, replaceParty(action.arrangement, from, to))
|
||||
Action(action.name, replaceParty(action.condition, from, to), replaceParty(action.arrangement, from, to))
|
||||
//if (action.actors.contains(from)) {
|
||||
// Action(action.name, action.condition, action.actors - from + to, replaceParty(action.arrangement, from, to))
|
||||
//} else
|
||||
// Action(action.name, action.condition, replaceParty(action.arrangement, from, to))
|
||||
|
||||
fun replaceParty(arrangement: Arrangement, from: Party, to: Party): Arrangement = when (arrangement) {
|
||||
is Zero -> arrangement
|
||||
@ -184,7 +210,6 @@ fun debugCompare(arrLeft: Arrangement, arrRight: Arrangement) {
|
||||
arrLeft.actions.zip(arrRight.actions).forEach {
|
||||
debugCompare(it.first.arrangement, it.second.arrangement)
|
||||
debugCompare(it.first.condition, it.second.condition)
|
||||
debugCompare(it.first.actors, it.second.actors)
|
||||
debugCompare(it.first.name, it.second.name)
|
||||
return
|
||||
}
|
||||
|
@ -22,16 +22,16 @@ class Cap {
|
||||
val contractInitial = arrange {
|
||||
rollOut("2016-09-01".ld, "2017-09-01".ld, Frequency.SemiAnnual) {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
"exercise".anytime {
|
||||
(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.owes(acmeCorp, floating - fixed, currency)
|
||||
next()
|
||||
}
|
||||
}
|
||||
acmeCorp.may {
|
||||
"skip".anytime {
|
||||
acmeCorp may {
|
||||
"skip" anytime {
|
||||
next()
|
||||
}
|
||||
}
|
||||
@ -41,23 +41,23 @@ class Cap {
|
||||
|
||||
val contractAfterFixingFirst = arrange {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
"exercise".anytime() {
|
||||
(acmeCorp or highStreetBank) may {
|
||||
"exercise" anytime {
|
||||
val floating1 = interest(notional, "act/365", 1.0.bd, "2016-09-01", "2017-03-01")
|
||||
val fixed1 = interest(notional, "act/365", 0.5.bd, "2016-09-01", "2017-03-01")
|
||||
highStreetBank.owes(acmeCorp, floating1 - fixed1, currency)
|
||||
rollOut("2017-03-01".ld, "2017-09-01".ld, Frequency.SemiAnnual) {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
"exercise".anytime {
|
||||
(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.owes(acmeCorp, floating - fixed, currency)
|
||||
next()
|
||||
}
|
||||
}
|
||||
acmeCorp.may {
|
||||
"skip".anytime {
|
||||
acmeCorp may {
|
||||
"skip" anytime {
|
||||
next()
|
||||
}
|
||||
}
|
||||
@ -65,20 +65,20 @@ class Cap {
|
||||
}
|
||||
}
|
||||
}
|
||||
acmeCorp.may {
|
||||
"skip".anytime {
|
||||
acmeCorp may {
|
||||
"skip" anytime {
|
||||
rollOut("2017-03-01".ld, "2017-09-01".ld, Frequency.SemiAnnual) {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
"exercise".anytime {
|
||||
(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.owes(acmeCorp, floating - fixed, currency)
|
||||
next()
|
||||
}
|
||||
}
|
||||
acmeCorp.may {
|
||||
"skip".anytime {
|
||||
acmeCorp may {
|
||||
"skip" anytime {
|
||||
next()
|
||||
}
|
||||
}
|
||||
@ -91,15 +91,15 @@ class Cap {
|
||||
|
||||
val contractAfterFixingFinal = arrange {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
"exercise".anytime() {
|
||||
(acmeCorp or highStreetBank) may {
|
||||
"exercise" anytime {
|
||||
val floating1 = interest(notional, "act/365", 1.0.bd, "2017-03-01", "2017-09-01")
|
||||
val fixed1 = interest(notional, "act/365", 0.5.bd, "2017-03-01", "2017-09-01")
|
||||
highStreetBank.owes(acmeCorp, floating1 - fixed1, currency)
|
||||
}
|
||||
}
|
||||
acmeCorp.may {
|
||||
"skip".anytime {
|
||||
acmeCorp may {
|
||||
"skip" anytime {
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,16 +108,16 @@ class Cap {
|
||||
val contractAfterExecutionFirst = arrange {
|
||||
rollOut("2017-03-01".ld, "2017-09-01".ld, Frequency.SemiAnnual) {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
"exercise".anytime {
|
||||
(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.owes(acmeCorp, floating - fixed, currency)
|
||||
next()
|
||||
}
|
||||
}
|
||||
acmeCorp.may {
|
||||
"skip".anytime {
|
||||
acmeCorp may {
|
||||
"skip" anytime {
|
||||
next()
|
||||
}
|
||||
}
|
||||
@ -144,8 +144,8 @@ class Cap {
|
||||
val limit = variable(150.K)
|
||||
}) {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
"exercise".anytime {
|
||||
(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)
|
||||
@ -153,8 +153,8 @@ class Cap {
|
||||
next(vars.limit to vars.limit - payout)
|
||||
}
|
||||
}
|
||||
acmeCorp.may {
|
||||
"skip".anytime {
|
||||
acmeCorp may {
|
||||
"skip" anytime {
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ class Caplet {
|
||||
|
||||
val contract = arrange {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
"exercise".anytime() {
|
||||
(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.owes(acmeCorp, (floating - fixed).plus(), currency)
|
||||
@ -31,8 +31,8 @@ class Caplet {
|
||||
|
||||
val contractFixed = arrange {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
"exercise".anytime() {
|
||||
(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.owes(acmeCorp, (floating - fixed).plus(), currency)
|
||||
|
@ -25,12 +25,12 @@ class ContractDefinition {
|
||||
|
||||
val cds_contract = arrange {
|
||||
actions {
|
||||
acmeCorp.may {
|
||||
acmeCorp may {
|
||||
"payout".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) {
|
||||
highStreetBank.owes(acmeCorp, 1.M, USD)
|
||||
}
|
||||
}
|
||||
highStreetBank.may {
|
||||
highStreetBank may {
|
||||
"expire".givenThat(after("2017-09-01")) {
|
||||
zero
|
||||
}
|
||||
@ -41,13 +41,13 @@ class ContractDefinition {
|
||||
|
||||
val american_fx_option = arrange {
|
||||
actions {
|
||||
acmeCorp.may {
|
||||
"exercise".anytime {
|
||||
highStreetBank.owes(acmeCorp, 1.M, EUR)
|
||||
acmeCorp.owes(highStreetBank, 1200.K, USD)
|
||||
acmeCorp may {
|
||||
"exercise" anytime {
|
||||
highStreetBank.owes(acmeCorp, 1.M, USD)
|
||||
acmeCorp.owes(highStreetBank, 1070.K, EUR)
|
||||
}
|
||||
}
|
||||
highStreetBank.may {
|
||||
highStreetBank may {
|
||||
"expire".givenThat(after("2017-09-01")) {
|
||||
zero
|
||||
}
|
||||
@ -58,19 +58,19 @@ class ContractDefinition {
|
||||
|
||||
val european_fx_option = arrange {
|
||||
actions {
|
||||
acmeCorp.may {
|
||||
"exercise".anytime {
|
||||
acmeCorp may {
|
||||
"exercise" anytime {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
(acmeCorp or highStreetBank) may {
|
||||
"execute".givenThat(after("2017-09-01")) {
|
||||
highStreetBank.owes(acmeCorp, 1.M, EUR)
|
||||
acmeCorp.owes(highStreetBank, 1200.K, USD)
|
||||
highStreetBank.owes(acmeCorp, 1.M, USD)
|
||||
acmeCorp.owes(highStreetBank, 1070.K, EUR)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
highStreetBank.may {
|
||||
highStreetBank may {
|
||||
"expire".givenThat(after("2017-09-01")) {
|
||||
zero
|
||||
}
|
||||
@ -83,10 +83,10 @@ class ContractDefinition {
|
||||
fun `builder problem - should not compile`() {
|
||||
val arr = arrange {
|
||||
actions {
|
||||
acmeCorp.may {
|
||||
"execute".anytime {
|
||||
acmeCorp.may {
|
||||
"problem".anytime {
|
||||
acmeCorp may {
|
||||
"execute" anytime {
|
||||
acmeCorp may {
|
||||
"problem" anytime {
|
||||
highStreetBank.gives(acmeCorp, 1.M, USD)
|
||||
}
|
||||
}
|
||||
@ -106,11 +106,11 @@ class ContractDefinition {
|
||||
fun `builder problem - legal`() {
|
||||
val arr = arrange {
|
||||
actions {
|
||||
acmeCorp.may {
|
||||
"execute".anytime {
|
||||
acmeCorp may {
|
||||
"execute" anytime {
|
||||
actions {
|
||||
acmeCorp.may {
|
||||
"problem".anytime {
|
||||
acmeCorp may {
|
||||
"problem" anytime {
|
||||
highStreetBank.owes(acmeCorp, 1.M, USD)
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ class FXFwdTimeOption
|
||||
val initialContract = arrange {
|
||||
|
||||
val swap = arrange {
|
||||
highStreetBank.owes(acmeCorp, 1200.K, EUR)
|
||||
highStreetBank.owes(acmeCorp, 1070.K, EUR)
|
||||
acmeCorp.owes(highStreetBank, 1.M, USD)
|
||||
}
|
||||
val maturity = "2018-06-01".ld
|
||||
|
@ -14,22 +14,22 @@ class FXSwap {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank) may {
|
||||
"execute".givenThat(after("2017-09-01")) {
|
||||
highStreetBank.owes(acmeCorp, 1200.K, USD)
|
||||
acmeCorp.owes(highStreetBank, 1.M, EUR)
|
||||
highStreetBank.owes(acmeCorp, 1070.K, EUR)
|
||||
acmeCorp.owes(highStreetBank, 1.M, USD)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val transfer1 = arrange { highStreetBank.owes(acmeCorp, 1200.K, USD) }
|
||||
val transfer2 = arrange { acmeCorp.owes(highStreetBank, 1.M, EUR) }
|
||||
val transfer1 = arrange { highStreetBank.owes(acmeCorp, 1070.K, EUR) }
|
||||
val transfer2 = arrange { acmeCorp.owes(highStreetBank, 1.M, USD) }
|
||||
|
||||
val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer1)
|
||||
val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer2)
|
||||
|
||||
val transferBad1 = arrange { highStreetBank.owes(acmeCorp, 1200.K, GBP) } // wrong currency
|
||||
val transferBad2 = arrange { acmeCorp.owes(highStreetBank, 900.K, EUR) } // wrong amount
|
||||
val transferBad3 = arrange { highStreetBank.owes(highStreetBank, 1.M, EUR) } // wrong party
|
||||
val transferBad1 = arrange { highStreetBank.owes(acmeCorp, 1070.K, USD) } // wrong currency
|
||||
val transferBad2 = arrange { acmeCorp.owes(highStreetBank, 900.K, USD) } // wrong amount
|
||||
val transferBad3 = arrange { highStreetBank.owes(highStreetBank, 1070.K, EUR) } // wrong party
|
||||
|
||||
val outStateBad1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad1)
|
||||
val outStateBad2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad2)
|
||||
@ -108,7 +108,7 @@ class FXSwap {
|
||||
timestamp(TEST_TX_TIME_1)
|
||||
|
||||
command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
this `fails with` "action must be authorized"
|
||||
this `fails with` "condition must be met"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,15 +33,15 @@ class IRS {
|
||||
val contractInitial = arrange {
|
||||
rollOut("2016-09-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
(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 floating".anytime {
|
||||
"pay floating" anytime {
|
||||
highStreetBank.owes(acmeCorp, floating - fixed, currency)
|
||||
next()
|
||||
}
|
||||
"pay fixed".anytime {
|
||||
"pay fixed" anytime {
|
||||
highStreetBank.owes(acmeCorp, fixed - floating, currency)
|
||||
next()
|
||||
}
|
||||
@ -51,11 +51,11 @@ class IRS {
|
||||
}
|
||||
val contractAfterFixingFirst = arrange {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
(acmeCorp or highStreetBank) may {
|
||||
val floating = interest(notional, "act/365", 1.0.bd, "2016-09-01", "2016-12-01")
|
||||
val fixed = interest(notional, "act/365", 0.5.bd, "2016-09-01", "2016-12-01")
|
||||
|
||||
"pay floating".anytime {
|
||||
"pay floating" anytime {
|
||||
highStreetBank.owes(acmeCorp, floating - fixed, currency)
|
||||
rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
|
||||
actions {
|
||||
@ -63,11 +63,11 @@ class IRS {
|
||||
val nextFloating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end)
|
||||
val nextFixed = interest(notional, "act/365", 0.5.bd, start, end)
|
||||
|
||||
"pay floating".anytime {
|
||||
"pay floating" anytime {
|
||||
highStreetBank.owes(acmeCorp, nextFloating - nextFixed, currency)
|
||||
next()
|
||||
}
|
||||
"pay fixed".anytime {
|
||||
"pay fixed" anytime {
|
||||
highStreetBank.owes(acmeCorp, nextFixed - nextFloating, currency)
|
||||
next()
|
||||
}
|
||||
@ -75,7 +75,7 @@ class IRS {
|
||||
}
|
||||
}
|
||||
}
|
||||
"pay fixed".anytime {
|
||||
"pay fixed" anytime {
|
||||
highStreetBank.owes(acmeCorp, fixed - floating, currency)
|
||||
rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
|
||||
actions {
|
||||
@ -83,11 +83,11 @@ class IRS {
|
||||
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 floating".anytime {
|
||||
"pay floating" anytime {
|
||||
highStreetBank.owes(acmeCorp, floating - fixed, currency)
|
||||
next()
|
||||
}
|
||||
"pay fixed".anytime {
|
||||
"pay fixed" anytime {
|
||||
highStreetBank.owes(acmeCorp, fixed - floating, currency)
|
||||
next()
|
||||
}
|
||||
@ -102,15 +102,15 @@ class IRS {
|
||||
val contractAfterExecutionFirst = arrange {
|
||||
rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
(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 floating".anytime {
|
||||
"pay floating" anytime {
|
||||
highStreetBank.owes(acmeCorp, floating - fixed, currency)
|
||||
next()
|
||||
}
|
||||
"pay fixed".anytime {
|
||||
"pay fixed" anytime {
|
||||
highStreetBank.owes(acmeCorp, fixed - floating, currency)
|
||||
next()
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ class RollOutTests {
|
||||
val contract_action1 = arrange {
|
||||
actions {
|
||||
highStreetBank may {
|
||||
"do it".anytime {
|
||||
"do it" anytime {
|
||||
highStreetBank.owes(acmeCorp, 10.K, USD)
|
||||
}
|
||||
}
|
||||
@ -76,7 +76,7 @@ class RollOutTests {
|
||||
val contract_action2 = arrange {
|
||||
actions {
|
||||
highStreetBank may {
|
||||
"do it".anytime {
|
||||
"do it" anytime {
|
||||
highStreetBank.owes(acmeCorp, 10.K, USD)
|
||||
}
|
||||
}
|
||||
@ -85,14 +85,14 @@ class RollOutTests {
|
||||
val contract_and1 = arrange {
|
||||
actions {
|
||||
highStreetBank may {
|
||||
"do it".anytime {
|
||||
"do it" anytime {
|
||||
highStreetBank.owes(acmeCorp, 10.K, USD)
|
||||
}
|
||||
}
|
||||
}
|
||||
actions {
|
||||
acmeCorp may {
|
||||
"do it".anytime {
|
||||
"do it" anytime {
|
||||
acmeCorp.owes(momAndPop, 10.K, USD)
|
||||
}
|
||||
}
|
||||
@ -103,14 +103,14 @@ class RollOutTests {
|
||||
val contract_and2 = arrange {
|
||||
actions {
|
||||
highStreetBank may {
|
||||
"do it".anytime {
|
||||
"do it" anytime {
|
||||
highStreetBank.owes(acmeCorp, 10.K, USD)
|
||||
}
|
||||
}
|
||||
}
|
||||
actions {
|
||||
acmeCorp may {
|
||||
"do it".anytime {
|
||||
"do it" anytime {
|
||||
acmeCorp.owes(momAndPop, 10.K, USD)
|
||||
}
|
||||
}
|
||||
|
@ -9,19 +9,19 @@ class Swaption {
|
||||
|
||||
val dreary_contract = arrange {
|
||||
actions {
|
||||
(highStreetBank or acmeCorp).may {
|
||||
(highStreetBank or acmeCorp) may {
|
||||
"proceed".givenThat(after("01/07/2015")) {
|
||||
highStreetBank.owes(acmeCorp, libor(notional, "01/04/2015", "01/07/2015"), currency)
|
||||
acmeCorp.owes(highStreetBank, interest(notional, "act/365", coupon, "01/04/2015", "01/07/2015"), currency)
|
||||
actions {
|
||||
(highStreetBank or acmeCorp).may {
|
||||
(highStreetBank or acmeCorp) may {
|
||||
"proceed".givenThat(after("01/10/2015")) {
|
||||
highStreetBank.owes(acmeCorp, libor(notional, "01/07/2015", "01/10/2015"), currency)
|
||||
acmeCorp.owes(highStreetBank, interest(notional, "act/365", coupon, "01/07/2015", "01/10/2015"), currency)
|
||||
|
||||
actions {
|
||||
(highStreetBank or acmeCorp).may {
|
||||
"dummy".anytime { zero }
|
||||
(highStreetBank or acmeCorp) may {
|
||||
"dummy" anytime { zero }
|
||||
// etc ...
|
||||
}
|
||||
}
|
||||
@ -29,16 +29,16 @@ class Swaption {
|
||||
}
|
||||
}
|
||||
actions {
|
||||
acmeCorp.may {
|
||||
"cancel".anytime {
|
||||
acmeCorp may {
|
||||
"cancel" anytime {
|
||||
acmeCorp.owes(highStreetBank, 10.K, USD)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
acmeCorp.may {
|
||||
"cancel".anytime {
|
||||
acmeCorp may {
|
||||
"cancel" anytime {
|
||||
acmeCorp.owes(highStreetBank, 10.K, USD)
|
||||
}
|
||||
}
|
||||
@ -49,15 +49,15 @@ class Swaption {
|
||||
val elegant_contract = arrange {
|
||||
rollOut("01/04/2015".ld, "01/04/2025".ld, Frequency.Quarterly) {
|
||||
actions {
|
||||
(highStreetBank or acmeCorp).may {
|
||||
(highStreetBank or acmeCorp) may {
|
||||
"proceed".givenThat(after(start)) {
|
||||
highStreetBank.owes(acmeCorp, libor(notional, start, end), currency)
|
||||
acmeCorp.owes(highStreetBank, interest(notional, "act/365", coupon, start, end), currency)
|
||||
next()
|
||||
}
|
||||
}
|
||||
acmeCorp.may {
|
||||
"cancel".anytime {
|
||||
acmeCorp may {
|
||||
"cancel" anytime {
|
||||
acmeCorp.owes(highStreetBank, 10.K, currency)
|
||||
}
|
||||
}
|
||||
@ -72,12 +72,12 @@ class Swaption {
|
||||
val cap = variable(150.K)
|
||||
}) {
|
||||
actions {
|
||||
acmeCorp.may {
|
||||
acmeCorp may {
|
||||
"exercise".givenThat(before(end)) {
|
||||
val payout = (EUR / USD - strike).plus() * notional
|
||||
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
(acmeCorp or highStreetBank) may {
|
||||
"proceed".givenThat(after(end)) {
|
||||
highStreetBank.owes(acmeCorp, payout, USD)
|
||||
next(vars.cap to vars.cap - payout)
|
||||
@ -86,7 +86,7 @@ class Swaption {
|
||||
}
|
||||
}
|
||||
}
|
||||
(acmeCorp or highStreetBank).may {
|
||||
(acmeCorp or highStreetBank) may {
|
||||
"proceedWithoutExercise".givenThat(after(end)) {
|
||||
next()
|
||||
}
|
||||
@ -100,12 +100,12 @@ class Swaption {
|
||||
val uses = variable(4)
|
||||
}) {
|
||||
actions {
|
||||
acmeCorp.may {
|
||||
acmeCorp may {
|
||||
"exercise".givenThat(before(end)) {
|
||||
val payout = (EUR / USD - strike).plus() * notional
|
||||
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
(acmeCorp or highStreetBank) may {
|
||||
"proceed".givenThat(after(end)) {
|
||||
highStreetBank.owes(acmeCorp, payout, currency)
|
||||
next(vars.uses to vars.uses - 1)
|
||||
@ -114,7 +114,7 @@ class Swaption {
|
||||
}
|
||||
}
|
||||
}
|
||||
(acmeCorp or highStreetBank).may {
|
||||
(acmeCorp or highStreetBank) may {
|
||||
"proceedWithoutExercise".givenThat(after(end)) {
|
||||
next()
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ class ZeroCouponBond {
|
||||
|
||||
val contract = arrange {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank).may {
|
||||
(acmeCorp or highStreetBank) may {
|
||||
"execute".givenThat(after("2017-09-01")) {
|
||||
highStreetBank.owes(acmeCorp, 100.K, GBP)
|
||||
}
|
||||
@ -19,7 +19,7 @@ class ZeroCouponBond {
|
||||
|
||||
val contractMove = arrange {
|
||||
actions {
|
||||
(momAndPop or highStreetBank).may {
|
||||
(momAndPop or highStreetBank) may {
|
||||
"execute".givenThat(after("2017-09-01")) {
|
||||
highStreetBank.owes(momAndPop, 100.K, GBP)
|
||||
}
|
||||
@ -90,7 +90,7 @@ class ZeroCouponBond {
|
||||
timestamp(TEST_TX_TIME_1)
|
||||
|
||||
command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
this `fails with` "action must be authorized"
|
||||
this `fails with` "condition must be met"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,8 @@ val an_fx_swap = arrange {
|
||||
actions {
|
||||
(acmeCorp or highStreetBank) may {
|
||||
"execute".givenThat(after("2017-09-01")) {
|
||||
highStreetBank.owes(acmeCorp, 1200.K, USD)
|
||||
acmeCorp.owes(highStreetBank, 1.M, EUR)
|
||||
highStreetBank.owes(acmeCorp, 1070.K, EUR)
|
||||
acmeCorp.owes(highStreetBank, 1.M, USD)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,8 +31,8 @@ val american_fx_option = arrange {
|
||||
actions {
|
||||
acmeCorp may {
|
||||
"exercise".givenThat(before("2017-09-01")) {
|
||||
highStreetBank.owes(acmeCorp, 1200.K, USD)
|
||||
acmeCorp.owes(highStreetBank, 1.M, EUR)
|
||||
highStreetBank.owes(acmeCorp, 1070.K, EUR)
|
||||
acmeCorp.owes(highStreetBank, 1.M, USD)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -46,7 +46,7 @@ val european_fx_option = arrange {
|
||||
}
|
||||
}
|
||||
(acmeCorp or highStreetBank) may {
|
||||
"expire".anytime {
|
||||
"expire" anytime {
|
||||
zero
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user