mirror of
https://github.com/corda/corda.git
synced 2025-01-18 02:39:51 +00:00
Universal: Post refactor, rollout unit test passing
This commit is contained in:
parent
b93aa71afa
commit
9f6415efab
@ -35,18 +35,15 @@ data class Transfer(val amount: Perceivable<BigDecimal>, val currency: Currency,
|
||||
data class And(val arrangements: Set<Arrangement>) : Arrangement
|
||||
|
||||
|
||||
// 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 arrangement state transitions into the specified arrangement.
|
||||
data class Action(val name: String, val condition: Perceivable<Boolean>,
|
||||
val actors: Set<Party>, val arrangement: Arrangement) : Arrangement {
|
||||
constructor(name: String, condition: Perceivable<Boolean>,
|
||||
actor: Party, arrangement: Arrangement)
|
||||
: this(name, condition, setOf(actor), arrangement)
|
||||
}
|
||||
val actors: Set<Party>, 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.
|
||||
data class Actions(val actions: Set<Action>) : Arrangement
|
||||
|
||||
// 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<Action>) : Arrangement
|
||||
// constructor(name: String, condition: Perceivable<Boolean>,
|
||||
// actor: Party, arrangement: Arrangement)
|
||||
|
||||
|
||||
// Roll out of arrangement
|
||||
|
@ -22,23 +22,55 @@ enum class Comparison {
|
||||
*/
|
||||
data class Const<T>(val value: T) : Perceivable<T> {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other == null) return false
|
||||
if (value == null) return false
|
||||
if (other == null) {
|
||||
return false
|
||||
}
|
||||
if (value == null) {
|
||||
return false
|
||||
}
|
||||
if (other is Const<*>) {
|
||||
if (value is BigDecimal && other.value is BigDecimal) {
|
||||
return this.value.compareTo(other.value) == 0
|
||||
if (this.value.compareTo(other.value) == 0)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
}
|
||||
return value.equals(other.value)
|
||||
if(value.equals(other.value))
|
||||
return true
|
||||
else
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
val h = value!!.hashCode()
|
||||
return h
|
||||
}
|
||||
}
|
||||
|
||||
fun<T> const(k: T) = Const(k)
|
||||
|
||||
//
|
||||
class StartDate : Perceivable<Instant>
|
||||
class EndDate : Perceivable<Instant>
|
||||
class StartDate : Perceivable<Instant> {
|
||||
override fun hashCode(): Int {
|
||||
return 2
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is StartDate
|
||||
}
|
||||
}
|
||||
|
||||
class EndDate : Perceivable<Instant> {
|
||||
override fun hashCode(): Int {
|
||||
return 3
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is EndDate
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perceivable based on time
|
||||
@ -118,12 +150,12 @@ fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED
|
||||
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<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>) : Perceivable<BigDecimal> = DummyPerceivable()
|
||||
@Suppress("UNUSED_PARAMETER") start: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>) : Perceivable<BigDecimal> = Interest(const(amount), dayCountConvention, const(interest), start, end )
|
||||
|
||||
fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable<BigDecimal> /* todo - appropriate type */,
|
||||
@Suppress("UNUSED_PARAMETER") start: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>) : Perceivable<BigDecimal> = Interest(const(amount), dayCountConvention, interest, start, end)
|
||||
|
||||
class Fixing(val source: String, val date: Perceivable<Instant>, val tenor: Tenor) : Perceivable<BigDecimal>
|
||||
data class Fixing(val source: String, val date: Perceivable<Instant>, val tenor: Tenor) : Perceivable<BigDecimal>
|
||||
|
||||
fun fix(source: String, date: Perceivable<Instant>, tenor: Tenor): Perceivable<BigDecimal> = Fixing(source, date, tenor)
|
||||
fun fix(source: String, date: LocalDate, tenor: Tenor): Perceivable<BigDecimal> = Fixing(source, const(date.toInstant()), tenor)
|
@ -8,7 +8,6 @@ import com.sun.tools.corba.se.idl.InvalidArgument
|
||||
import java.math.BigDecimal
|
||||
import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
|
||||
/**
|
||||
* Created by sofusmortensen on 23/05/16.
|
||||
@ -40,8 +39,10 @@ class UniversalContract : Contract {
|
||||
class Issue : TypeOnlyCommandData(), Commands
|
||||
}
|
||||
|
||||
fun eval(@Suppress("UNUSED_PARAMETER") tx: TransactionForContract, expr: Perceivable<Instant>): Instant = when (expr) {
|
||||
fun eval(@Suppress("UNUSED_PARAMETER") tx: TransactionForContract, expr: Perceivable<Instant>): Instant? = when (expr) {
|
||||
is Const -> expr.value
|
||||
is StartDate -> null
|
||||
is EndDate -> null
|
||||
else -> throw Error("Unable to evaluate")
|
||||
}
|
||||
|
||||
@ -112,23 +113,50 @@ class UniversalContract : Contract {
|
||||
// todo: calendar + rolling conventions
|
||||
val schedule = BusinessCalendar.createGenericSchedule(start, rollOut.frequency, noOfAdditionalPeriods = 1, endDate = end)
|
||||
|
||||
val next = schedule.first() // fail if no dates
|
||||
val nextStart = schedule.first()
|
||||
// todo: look into schedule for final dates
|
||||
|
||||
val newRollOut = RollOut(next, end, rollOut.frequency, rollOut.template)
|
||||
// todo: we may have to save original start date in order to roll out correctly
|
||||
val newRollOut = RollOut(nextStart, end, rollOut.frequency, rollOut.template)
|
||||
|
||||
return replaceNext(rollOut.template, newRollOut )
|
||||
val arr = replaceStartEnd(rollOut.template, start.toInstant(), nextStart.toInstant())
|
||||
|
||||
return replaceNext(arr, 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)
|
||||
}
|
||||
}
|
||||
fun<T> replaceStartEnd(p: Perceivable<T>, start: Instant, end: Instant) : Perceivable<T> =
|
||||
when (p) {
|
||||
is Const -> p
|
||||
is TimePerceivable -> TimePerceivable( p.cmp, replaceStartEnd(p.instant, start, end) ) as Perceivable<T>
|
||||
is EndDate -> const(end) as Perceivable<T>
|
||||
is StartDate -> const(start) as Perceivable<T>
|
||||
is UnaryPlus -> UnaryPlus( replaceStartEnd(p.arg, start, end) )
|
||||
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>
|
||||
else -> throw NotImplementedError("replaceStartEnd " + p.javaClass.name)
|
||||
}
|
||||
|
||||
fun replaceStartEnd(arrangement: Arrangement, start: Instant, end: Instant) : Arrangement =
|
||||
when (arrangement) {
|
||||
is And -> And(arrangement.arrangements.map { replaceStartEnd(it, start, end) }.toSet())
|
||||
is Zero -> arrangement
|
||||
is Transfer -> Transfer( 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 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 And -> And(arrangement.arrangements.map { replaceNext(it, nextReplacement) }.toSet())
|
||||
is Transfer -> arrangement
|
||||
is Zero -> arrangement
|
||||
is Continuation -> nextReplacement
|
||||
else -> throw NotImplementedError("replaceNext " + arrangement.javaClass.name)
|
||||
}
|
||||
|
||||
|
||||
override fun verify(tx: TransactionForContract) {
|
||||
|
||||
@ -143,9 +171,8 @@ class UniversalContract : Contract {
|
||||
when (value) {
|
||||
is Commands.Action -> {
|
||||
val inState = tx.inputs.single() as State
|
||||
val arr = when (inState.details) {
|
||||
is Or -> inState.details
|
||||
is Action -> inState.details
|
||||
val arr = when (inState.details) {
|
||||
is Actions -> inState.details
|
||||
is RollOut -> reduceRollOut(inState.details)
|
||||
else -> throw InvalidArgument("Unexpected arrangement, " + tx.inputs.single())
|
||||
}
|
||||
@ -154,6 +181,12 @@ class UniversalContract : Contract {
|
||||
|
||||
val action = actions[value.name] ?: throw IllegalArgumentException("Failed requirement: action must be defined")
|
||||
|
||||
// todo: not sure this is necessary??
|
||||
val rest = extractRemainder(arr, action)
|
||||
|
||||
// for now - let's assume not
|
||||
assert(rest is Zero)
|
||||
|
||||
requireThat {
|
||||
"action must be timestamped" by (tx.timestamp != null)
|
||||
"action must be authorized" by (cmd.signers.any { action.actors.any { party -> party.owningKey == it } })
|
||||
@ -169,12 +202,12 @@ class UniversalContract : Contract {
|
||||
val outState = tx.outputs.single() as State
|
||||
requireThat {
|
||||
"output state must match action result state" by (arrangement.equals(outState.details))
|
||||
"output state must match action result state" by (rest == zero)
|
||||
}
|
||||
}
|
||||
0 -> throw IllegalArgumentException("must have at least one out state")
|
||||
else -> {
|
||||
|
||||
var allContracts = And(tx.outputs.map { (it as State).details }.toSet())
|
||||
val allContracts = And(tx.outputs.map { (it as State).details }.toSet())
|
||||
|
||||
requireThat {
|
||||
"output states must match action result state" by (arrangement.equals(allContracts))
|
||||
@ -202,16 +235,26 @@ class UniversalContract : Contract {
|
||||
}
|
||||
is Commands.Fix -> {
|
||||
val inState = tx.inputs.single() as State
|
||||
val arr = when (inState.details) {
|
||||
is Actions -> inState.details
|
||||
is RollOut -> reduceRollOut(inState.details)
|
||||
else -> throw InvalidArgument("Unexpected arrangement, " + tx.inputs.single())
|
||||
}
|
||||
val outState = tx.outputs.single() as State
|
||||
|
||||
val unusedFixes = value.fixes.map { it.of }.toMutableSet()
|
||||
val arr = replaceFixing(tx, inState.details,
|
||||
val expectedArr = replaceFixing(tx, arr,
|
||||
value.fixes.associateBy({ it.of }, { it.value }), unusedFixes)
|
||||
|
||||
println(expectedArr)
|
||||
println(outState.details)
|
||||
|
||||
// debugCompare(expectedArr, outState.details)
|
||||
|
||||
requireThat {
|
||||
"relevant fixing must be included" by unusedFixes.isEmpty()
|
||||
"output state does not reflect fix command" by
|
||||
(arr.equals(outState.details))
|
||||
(expectedArr.equals(outState.details))
|
||||
}
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unrecognised command")
|
||||
@ -219,8 +262,8 @@ class UniversalContract : Contract {
|
||||
}
|
||||
|
||||
fun <T> replaceFixing(tx: TransactionForContract, perceivable: Perceivable<T>,
|
||||
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Perceivable<T> {
|
||||
return when (perceivable) {
|
||||
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Perceivable<T> =
|
||||
when (perceivable) {
|
||||
is Const -> perceivable
|
||||
is UnaryPlus -> UnaryPlus(replaceFixing(tx, perceivable.arg, fixings, unusedFixings))
|
||||
is PerceivableOperation -> PerceivableOperation(replaceFixing(tx, perceivable.left, fixings, unusedFixings),
|
||||
@ -228,13 +271,15 @@ class UniversalContract : Contract {
|
||||
is Interest -> Interest(replaceFixing(tx, perceivable.amount, fixings, unusedFixings),
|
||||
perceivable.dayCountConvention, replaceFixing(tx, perceivable.interest, fixings, unusedFixings),
|
||||
perceivable.start, perceivable.end) as Perceivable<T>
|
||||
is Fixing -> if (fixings.containsKey(FixOf(perceivable.source, eval(tx, perceivable.date).toLocalDate(), perceivable.tenor))) {
|
||||
unusedFixings.remove(FixOf(perceivable.source, eval(tx, perceivable.date).toLocalDate(), perceivable.tenor))
|
||||
Const(fixings[FixOf(perceivable.source, eval(tx, perceivable.date).toLocalDate(), perceivable.tenor)]!!) as Perceivable<T>
|
||||
} else perceivable
|
||||
is Fixing -> {
|
||||
val dt = eval(tx, perceivable.date)
|
||||
if (dt != null && fixings.containsKey(FixOf(perceivable.source, dt.toLocalDate(), perceivable.tenor))) {
|
||||
unusedFixings.remove(FixOf(perceivable.source, dt.toLocalDate(), perceivable.tenor))
|
||||
Const(fixings[FixOf(perceivable.source, dt.toLocalDate(), perceivable.tenor)]!!) as Perceivable<T>
|
||||
} else perceivable
|
||||
}
|
||||
else -> throw NotImplementedError("replaceFixing - " + perceivable.javaClass.name)
|
||||
}
|
||||
}
|
||||
|
||||
fun replaceFixing(tx: TransactionForContract, arr: Action,
|
||||
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>) =
|
||||
@ -245,9 +290,11 @@ class UniversalContract : Contract {
|
||||
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Arrangement =
|
||||
when (arr) {
|
||||
is Zero -> arr
|
||||
is And -> And(arr.arrangements.map { replaceFixing(tx, it, fixings, unusedFixings) }.toSet())
|
||||
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 Actions -> Actions( arr.actions.map { Action(it.name, it.condition, it.actors, 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)
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,12 @@ 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 com.sun.javaws.exceptions.InvalidArgumentException
|
||||
import com.sun.org.apache.xpath.internal.operations.Bool
|
||||
import com.sun.tools.corba.se.idl.InvalidArgument
|
||||
import com.sun.tools.javadoc.Start
|
||||
import java.math.BigDecimal
|
||||
import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
@ -14,79 +19,193 @@ import java.util.*
|
||||
* Created by sofusmortensen on 23/05/16.
|
||||
*/
|
||||
|
||||
fun Instant.toLocalDate() : LocalDate = LocalDate.ofEpochDay( this.epochSecond / 60 / 60 / 24 )
|
||||
fun LocalDate.toInstant() : Instant = Instant.ofEpochSecond( this.toEpochDay() * 60 * 60 * 24 )
|
||||
fun Instant.toLocalDate(): LocalDate = LocalDate.ofEpochDay(this.epochSecond / 60 / 60 / 24)
|
||||
|
||||
/** returns list of potentially liable parties for a given contract */
|
||||
fun liableParties(contract: Arrangement) : Set<PublicKey> {
|
||||
fun LocalDate.toInstant(): Instant = Instant.ofEpochSecond(this.toEpochDay() * 60 * 60 * 24)
|
||||
|
||||
fun visit(arrangement: Arrangement) : ImmutableSet<PublicKey> =
|
||||
private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKey> =
|
||||
when (arrangement) {
|
||||
is Zero -> ImmutableSet.of<PublicKey>()
|
||||
is Transfer -> ImmutableSet.of(arrangement.from.owningKey)
|
||||
is Action ->
|
||||
if (arrangement.actors.size != 1)
|
||||
visit(arrangement.arrangement)
|
||||
else
|
||||
Sets.difference(visit(arrangement.arrangement), ImmutableSet.of(arrangement.actors.single())).immutableCopy()
|
||||
is And ->
|
||||
arrangement.arrangements.fold( ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll( visit(k)) } ).build()
|
||||
is Or ->
|
||||
arrangement.actions.fold( ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll( visit(k)) } ).build()
|
||||
is RollOut -> visit( arrangement.template )
|
||||
arrangement.arrangements.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
|
||||
is Actions ->
|
||||
arrangement.actions.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
|
||||
is RollOut -> liablePartiesVisitor(arrangement.template)
|
||||
is Continuation -> ImmutableSet.of<PublicKey>()
|
||||
else -> throw IllegalArgumentException("liableParties " + arrangement)
|
||||
}
|
||||
|
||||
return visit(contract)
|
||||
}
|
||||
private fun liablePartiesVisitor(action: Action): ImmutableSet<PublicKey> =
|
||||
if (action.actors.size != 1)
|
||||
liablePartiesVisitor(action.arrangement)
|
||||
else
|
||||
Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(action.actors.single())).immutableCopy()
|
||||
|
||||
/** returns list of involved parties for a given contract */
|
||||
fun involvedParties(arrangement: Arrangement) : Set<PublicKey> {
|
||||
/** returns list of potentially liable parties for a given contract */
|
||||
fun liableParties(contract: Arrangement): Set<PublicKey> = liablePartiesVisitor(contract)
|
||||
|
||||
fun visit(arrangement: Arrangement) : ImmutableSet<PublicKey> {
|
||||
return when (arrangement) {
|
||||
private fun involvedPartiesVisitor(action: Action): Set<PublicKey> =
|
||||
Sets.union(involvedPartiesVisitor(action.arrangement), action.actors.map { it.owningKey }.toSet()).immutableCopy()
|
||||
|
||||
private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKey> =
|
||||
when (arrangement) {
|
||||
is Zero -> ImmutableSet.of<PublicKey>()
|
||||
is Transfer -> ImmutableSet.of(arrangement.from.owningKey)
|
||||
is Action -> Sets.union( visit(arrangement.arrangement), arrangement.actors.map { it.owningKey }.toSet() ).immutableCopy()
|
||||
is And ->
|
||||
arrangement.arrangements.fold( ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll( visit(k)) } ).build()
|
||||
is Or ->
|
||||
arrangement.actions.fold( ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll( visit(k)) } ).build()
|
||||
arrangement.arrangements.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
|
||||
is Actions ->
|
||||
arrangement.actions.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
/** returns list of involved parties for a given contract */
|
||||
fun involvedParties(arrangement: Arrangement): Set<PublicKey> = involvedPartiesVisitor(arrangement)
|
||||
|
||||
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))
|
||||
|
||||
fun replaceParty(arrangement: Arrangement, from: Party, to: Party): Arrangement = when (arrangement) {
|
||||
is Zero -> arrangement
|
||||
is Transfer -> Transfer(arrangement.amount, arrangement.currency,
|
||||
if (arrangement.from == from) to else arrangement.from,
|
||||
if (arrangement.to == from) to else arrangement.to)
|
||||
is And -> And(arrangement.arrangements.map { replaceParty(it, from, to) }.toSet())
|
||||
is Actions -> Actions(arrangement.actions.map { replaceParty(it, from, to) }.toSet())
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
fun extractRemainder(arrangement: Arrangement, action: Action) : Arrangement = when (arrangement) {
|
||||
is Actions -> if (arrangement.actions.contains(action)) zero else arrangement
|
||||
is And -> {
|
||||
val a = arrangement.arrangements.map { extractRemainder(it, action) }.filter { it != zero }
|
||||
when (a.size) {
|
||||
0 -> zero
|
||||
1 -> a.single()
|
||||
else -> And( a.toSet() )
|
||||
}
|
||||
}
|
||||
else -> arrangement
|
||||
}
|
||||
|
||||
fun actions(arrangement: Arrangement): Map<String, Action> = when (arrangement) {
|
||||
is Zero -> mapOf()
|
||||
is Transfer -> mapOf()
|
||||
is Actions -> arrangement.actions.map { it.name to it }.toMap()
|
||||
is And -> arrangement.arrangements.map { actions(it) }.fold(mutableMapOf()) { m, x ->
|
||||
x.forEach { s, action -> m[s] = action }
|
||||
m
|
||||
}
|
||||
is RollOut -> mapOf()
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
fun debugCompare(left: String, right: String) {
|
||||
assert(left.equals(right))
|
||||
}
|
||||
|
||||
fun<T> debugCompare(perLeft: Perceivable<T>, perRight: Perceivable<T>) {
|
||||
if (perLeft.equals(perRight)) return
|
||||
|
||||
when (perLeft) {
|
||||
is UnaryPlus -> {
|
||||
if (perRight is UnaryPlus) {
|
||||
debugCompare(perLeft.arg, perRight.arg)
|
||||
return
|
||||
}
|
||||
}
|
||||
is PerceivableOperation -> {
|
||||
if (perRight is PerceivableOperation) {
|
||||
debugCompare(perLeft.left, perRight.left)
|
||||
debugCompare(perLeft.right, perRight.right)
|
||||
assert( perLeft.op.equals(perRight.op) )
|
||||
return
|
||||
}
|
||||
}
|
||||
is Interest -> {
|
||||
if (perRight is Interest) {
|
||||
debugCompare(perLeft.amount, perRight.amount)
|
||||
debugCompare(perLeft.interest, perRight.interest)
|
||||
debugCompare(perLeft.start, perRight.start)
|
||||
debugCompare(perLeft.end, perRight.end)
|
||||
assert(perLeft.dayCountConvention.equals(perRight.dayCountConvention))
|
||||
return
|
||||
}
|
||||
}
|
||||
is Fixing -> {
|
||||
if (perRight is Fixing) {
|
||||
debugCompare(perLeft.date, perRight.date)
|
||||
debugCompare(perLeft.source, perRight.source)
|
||||
debugCompare(perLeft.date, perRight.date)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return visit(arrangement)
|
||||
assert(false)
|
||||
}
|
||||
|
||||
fun replaceParty(action: Action, from: Party, to: Party) : Action {
|
||||
if (action.actors.contains(from)) {
|
||||
return Action( action.name, action.condition, action.actors - from + to, replaceParty(action.arrangement, from, to))
|
||||
}
|
||||
return Action( action.name, action.condition, action.actors, replaceParty(action.arrangement, from, to))
|
||||
fun debugCompare(parLeft: Party, parRight: Party) {
|
||||
assert( parLeft.equals(parRight) )
|
||||
}
|
||||
fun debugCompare(left: Frequency, right: Frequency) {
|
||||
assert( left.equals(right) )
|
||||
}
|
||||
fun debugCompare(left: LocalDate, right: LocalDate) {
|
||||
assert( left.equals(right) )
|
||||
}
|
||||
|
||||
fun replaceParty(arrangement: Arrangement, from: Party, to: Party) : Arrangement {
|
||||
return when (arrangement) {
|
||||
is Zero -> arrangement
|
||||
is Transfer -> Transfer( arrangement.amount, arrangement.currency,
|
||||
if (arrangement.from == from) to else arrangement.from,
|
||||
if (arrangement.to == from) to else arrangement.to )
|
||||
is Action -> replaceParty(arrangement, from, to)
|
||||
is And -> And( arrangement.arrangements.map { replaceParty(it, from, to) }.toSet() )
|
||||
is Or -> Or( arrangement.actions.map { replaceParty(it, from, to) }.toSet() )
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
fun debugCompare(parLeft: Set<Party>, parRight: Set<Party>) {
|
||||
if (parLeft.equals(parRight)) return
|
||||
|
||||
assert( parLeft.equals(parRight) )
|
||||
}
|
||||
|
||||
fun actions(arrangement: Arrangement) : Map<String, Action> {
|
||||
fun debugCompare(arrLeft: Arrangement, arrRight: Arrangement) {
|
||||
if (arrLeft.equals(arrRight)) return
|
||||
|
||||
when (arrangement) {
|
||||
is Zero -> return mapOf()
|
||||
is Transfer -> return mapOf()
|
||||
is Action -> return mapOf( arrangement.name to arrangement)
|
||||
is Or -> return arrangement.actions.map { it.name to it }.toMap()
|
||||
when (arrLeft) {
|
||||
is Transfer -> {
|
||||
if (arrRight is Transfer) {
|
||||
|
||||
debugCompare(arrLeft.amount, arrRight.amount)
|
||||
debugCompare(arrLeft.from, arrRight.from)
|
||||
debugCompare(arrLeft.to, arrRight.to)
|
||||
return
|
||||
}
|
||||
}
|
||||
is And -> {
|
||||
if (arrRight is And) {
|
||||
arrLeft.arrangements.zip( arrRight.arrangements).forEach {
|
||||
debugCompare(it.first, it.second)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
is Actions -> {
|
||||
if (arrRight is Actions) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
is RollOut -> {
|
||||
if (arrRight is RollOut) {
|
||||
debugCompare(arrLeft.template, arrRight.template)
|
||||
debugCompare(arrLeft.startDate, arrRight.startDate)
|
||||
debugCompare(arrLeft.endDate, arrRight.endDate)
|
||||
debugCompare(arrLeft.frequency, arrRight.frequency)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw IllegalArgumentException()
|
||||
assert( false)
|
||||
}
|
@ -37,34 +37,22 @@ open class ContractBuilder {
|
||||
return c
|
||||
}
|
||||
|
||||
fun Party.may(init: ActionBuilder.() -> Unit): Or {
|
||||
fun Party.may(init: ActionBuilder.() -> Unit): Actions {
|
||||
val b = ActionBuilder(setOf(this))
|
||||
b.init()
|
||||
val c = Or(b.actions.toSet())
|
||||
val c = Actions(b.actions.toSet())
|
||||
contracts.add(c)
|
||||
return c
|
||||
}
|
||||
|
||||
fun Set<Party>.may(init: ActionBuilder.() -> Unit): Or {
|
||||
fun Set<Party>.may(init: ActionBuilder.() -> Unit): Actions {
|
||||
val b = ActionBuilder(this)
|
||||
b.init()
|
||||
val c = Or(b.actions.toSet())
|
||||
val c = Actions(b.actions.toSet())
|
||||
contracts.add(c)
|
||||
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<Party>.or(party: Party) = this.plus(party)
|
||||
|
||||
@ -90,9 +78,23 @@ open class ContractBuilder {
|
||||
}*/
|
||||
|
||||
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) )
|
||||
|
||||
val start = StartDate()
|
||||
val end = EndDate()
|
||||
|
||||
fun next(): Continuation {
|
||||
val c = Continuation()
|
||||
contracts.add(c)
|
||||
return c
|
||||
}
|
||||
|
||||
fun <T1> next(@Suppress("UNUSED_PARAMETER") p1: kotlin.Pair<Parameter<T1>, Perceivable<T1>>) = Continuation()
|
||||
fun <T1, T2> next(@Suppress("UNUSED_PARAMETER") p1: kotlin.Pair<Parameter<T1>, Perceivable<T1>>,
|
||||
@Suppress("UNUSED_PARAMETER") p2: kotlin.Pair<Parameter<T2>, Perceivable<T2>>) = Continuation()
|
||||
|
||||
fun <T1, T2, T3> next(@Suppress("UNUSED_PARAMETER") p1: kotlin.Pair<Parameter<T1>, Perceivable<T1>>,
|
||||
@Suppress("UNUSED_PARAMETER") p2: kotlin.Pair<Parameter<T2>, Perceivable<T2>>,
|
||||
@Suppress("UNUSED_PARAMETER") p3: kotlin.Pair<Parameter<T3>, Perceivable<T3>>) = Continuation()
|
||||
|
||||
fun rollOut(startDate: LocalDate, endDate: LocalDate, frequency: Frequency, init: RollOutBuilder<Dummy>.() -> Unit): RollOut {
|
||||
val b = RollOutBuilder(startDate, endDate, frequency, Dummy())
|
||||
@ -161,22 +163,10 @@ data class Parameter<T>(val initialValue: T) : Perceivable<T>
|
||||
fun<T> variable(v: T) = Parameter<T>(v)
|
||||
|
||||
class RollOutBuilder<T>(val startDate: LocalDate, val endDate: LocalDate, val frequency: Frequency, val vars: T) : ContractBuilder() {
|
||||
|
||||
val start = StartDate()
|
||||
val end = EndDate()
|
||||
|
||||
fun next() = Continuation()
|
||||
|
||||
fun<T1> next( @Suppress("UNUSED_PARAMETER") p1: kotlin.Pair<Parameter<T1>, Perceivable<T1>>) = Continuation()
|
||||
fun<T1, T2> next(@Suppress("UNUSED_PARAMETER") p1: kotlin.Pair<Parameter<T1>, Perceivable<T1>>,
|
||||
@Suppress("UNUSED_PARAMETER") p2: kotlin.Pair<Parameter<T2>, Perceivable<T2>>) = Continuation()
|
||||
fun<T1, T2, T3> next(@Suppress("UNUSED_PARAMETER") p1: kotlin.Pair<Parameter<T1>, Perceivable<T1>>,
|
||||
@Suppress("UNUSED_PARAMETER") p2: kotlin.Pair<Parameter<T2>, Perceivable<T2>>,
|
||||
@Suppress("UNUSED_PARAMETER") p3: kotlin.Pair<Parameter<T3>, Perceivable<T3>>) = Continuation()
|
||||
|
||||
override fun final() =
|
||||
RollOut(startDate, endDate, frequency, super.final())
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Dummy {}
|
||||
|
@ -31,7 +31,7 @@ class Cap {
|
||||
highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency)
|
||||
next()
|
||||
}
|
||||
} or
|
||||
}
|
||||
acmeCorp.may {
|
||||
"skip".anytime {
|
||||
next()
|
||||
@ -40,13 +40,15 @@ 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")
|
||||
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-07-01".ld, "2017-04-01".ld, Frequency.Quarterly) {
|
||||
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("6M")), start, end)
|
||||
@ -54,16 +56,18 @@ class Cap {
|
||||
highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency)
|
||||
next()
|
||||
}
|
||||
} or acmeCorp.may {
|
||||
}
|
||||
acmeCorp.may {
|
||||
"skip".anytime {
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} or acmeCorp.may {
|
||||
}
|
||||
acmeCorp.may {
|
||||
"skip".anytime {
|
||||
rollOut("2016-07-01".ld, "2017-04-01".ld, Frequency.Quarterly) {
|
||||
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("6M")), start, end)
|
||||
@ -71,7 +75,8 @@ class Cap {
|
||||
highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency)
|
||||
next()
|
||||
}
|
||||
} or acmeCorp.may {
|
||||
}
|
||||
acmeCorp.may {
|
||||
"skip".anytime {
|
||||
next()
|
||||
}
|
||||
@ -97,7 +102,8 @@ class Cap {
|
||||
highStreetBank.gives(acmeCorp, payout, currency)
|
||||
next(vars.limit to vars.limit - payout)
|
||||
}
|
||||
} or acmeCorp.may {
|
||||
}
|
||||
acmeCorp.may {
|
||||
"skip".anytime {
|
||||
next()
|
||||
}
|
||||
@ -124,6 +130,11 @@ class Cap {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `print debugging`() {
|
||||
// debugprint(contract)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fixing`() {
|
||||
transaction {
|
||||
@ -131,7 +142,7 @@ class Cap {
|
||||
output { stateFixed }
|
||||
timestamp(TEST_TX_TIME_1)
|
||||
|
||||
tweak {
|
||||
/* tweak {
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") }
|
||||
this `fails with` "action must be defined"
|
||||
}
|
||||
@ -161,7 +172,7 @@ class Cap {
|
||||
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))) }
|
||||
|
||||
|
@ -32,7 +32,8 @@ class ContractDefinition {
|
||||
"payout".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) {
|
||||
highStreetBank.gives(acmeCorp, 1.M, USD)
|
||||
}
|
||||
} or highStreetBank.may {
|
||||
}
|
||||
highStreetBank.may {
|
||||
"expire".givenThat(after("2017-09-01")) {
|
||||
zero
|
||||
}
|
||||
@ -46,7 +47,8 @@ class ContractDefinition {
|
||||
highStreetBank.gives(acmeCorp, 1.M, EUR)
|
||||
acmeCorp.gives(highStreetBank, 1200.K, USD)
|
||||
}
|
||||
} or highStreetBank.may {
|
||||
}
|
||||
highStreetBank.may {
|
||||
"expire".givenThat(after("2017-09-01")) {
|
||||
zero
|
||||
}
|
||||
@ -64,7 +66,8 @@ class ContractDefinition {
|
||||
}
|
||||
}
|
||||
}
|
||||
} or highStreetBank.may {
|
||||
}
|
||||
highStreetBank.may {
|
||||
"expire".givenThat(after("2017-09-01")) {
|
||||
zero
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import com.r3corda.core.utilities.DUMMY_NOTARY
|
||||
import com.r3corda.testing.transaction
|
||||
import org.junit.Test
|
||||
import java.time.Instant
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
/**
|
||||
* Created by sofusmortensen on 08/09/16.
|
||||
@ -25,10 +26,21 @@ class RollOutTests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract)
|
||||
|
||||
val contractStep1a = arrange {
|
||||
rollOut("2016-12-01".ld, "2017-09-01".ld, Frequency.Monthly) {
|
||||
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)
|
||||
@ -45,9 +57,73 @@ class RollOutTests {
|
||||
val stateStep1a = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractStep1a)
|
||||
val stateStep1b = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractStep1b)
|
||||
|
||||
val contract_transfer1 = arrange {
|
||||
highStreetBank.gives(acmeCorp, 10.K, USD)
|
||||
}
|
||||
val contract_transfer2 = arrange {
|
||||
highStreetBank.gives(acmeCorp, 10.K, USD)
|
||||
}
|
||||
val contract_action1 = arrange {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
val contract_and1 = arrange {
|
||||
highStreetBank.may {
|
||||
"do it".anytime {
|
||||
highStreetBank.gives(acmeCorp, 10.K, USD)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
acmeCorp.may {
|
||||
"do it".anytime {
|
||||
acmeCorp.gives(momAndPop, 10.K, USD)
|
||||
}
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `arrangement equality transfer`() {
|
||||
assertEquals(contract_transfer1, contract_transfer2)
|
||||
}
|
||||
@Test
|
||||
fun `arrangement equality action`() {
|
||||
assertEquals(contract_action1, contract_action2)
|
||||
}
|
||||
@Test
|
||||
fun `arrangement equality and`() {
|
||||
assertEquals(contract_and1, contract_and2)
|
||||
}
|
||||
@Test
|
||||
fun `arrangement equality complex`() {
|
||||
assertEquals(contract, contract2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dateTests() {
|
||||
|
||||
val d1 = BusinessCalendar.parseDateFromString("2016-09-10")
|
||||
}
|
||||
|
||||
@ -78,12 +154,12 @@ class RollOutTests {
|
||||
output { stateStep1b }
|
||||
timestamp(TEST_TX_TIME_1)
|
||||
|
||||
tweak {
|
||||
/* tweak {
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") }
|
||||
this `fails with` "action must be defined"
|
||||
}
|
||||
}*/
|
||||
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") }
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("transfer") }
|
||||
|
||||
this.verifies()
|
||||
}
|
||||
|
@ -30,13 +30,15 @@ class Swaption {
|
||||
// etc ...
|
||||
}
|
||||
}
|
||||
} or acmeCorp.may {
|
||||
}
|
||||
acmeCorp.may {
|
||||
"cancel".anytime {
|
||||
acmeCorp.gives(highStreetBank, 10.K, USD)
|
||||
}
|
||||
}
|
||||
}
|
||||
} or acmeCorp.may {
|
||||
}
|
||||
acmeCorp.may {
|
||||
"cancel".anytime {
|
||||
acmeCorp.gives(highStreetBank, 10.K, USD)
|
||||
}
|
||||
@ -52,7 +54,8 @@ class Swaption {
|
||||
acmeCorp.gives(highStreetBank, interest(notional, "act/365", coupon, start, end), currency)
|
||||
next()
|
||||
}
|
||||
} or acmeCorp.may {
|
||||
}
|
||||
acmeCorp.may {
|
||||
"cancel".anytime {
|
||||
acmeCorp.gives(highStreetBank, 10.K, currency)
|
||||
}
|
||||
@ -77,7 +80,8 @@ class Swaption {
|
||||
}
|
||||
}
|
||||
}
|
||||
} or (acmeCorp or highStreetBank).may {
|
||||
}
|
||||
(acmeCorp or highStreetBank).may {
|
||||
"proceedWithoutExercise".givenThat(after(end)) {
|
||||
next()
|
||||
}
|
||||
@ -100,7 +104,8 @@ class Swaption {
|
||||
}
|
||||
}
|
||||
}
|
||||
} or (acmeCorp or highStreetBank).may {
|
||||
}
|
||||
(acmeCorp or highStreetBank).may {
|
||||
"proceedWithoutExercise".givenThat(after(end)) {
|
||||
next()
|
||||
}
|
||||
|
@ -46,7 +46,8 @@ val european_fx_option = arrange {
|
||||
"exercise".givenThat(before("2017-09-01")) {
|
||||
fx_swap("2017-09-01", 1.M, 1.2.bd, EUR, USD, acmeCorp, highStreetBank)
|
||||
}
|
||||
} or (acmeCorp or highStreetBank).may {
|
||||
}
|
||||
(acmeCorp or highStreetBank).may {
|
||||
"expire".anytime {
|
||||
zero
|
||||
}
|
||||
@ -84,7 +85,8 @@ val no_touch = arrange {
|
||||
"execute".givenThat(after("2017-09-01")) {
|
||||
highStreetBank.gives(acmeCorp, 1.M, USD)
|
||||
}
|
||||
} or highStreetBank.may {
|
||||
}
|
||||
highStreetBank.may {
|
||||
"knock out".givenThat(EUR/USD gt 1.3)
|
||||
}
|
||||
}
|
||||
@ -94,7 +96,8 @@ val one_touch = arrange {
|
||||
"expire".givenThat(after("2017-09-01")) {
|
||||
zero
|
||||
}
|
||||
} or acmeCorp.may {
|
||||
}
|
||||
acmeCorp.may {
|
||||
"knock in".givenThat(EUR / USD gt 1.3) {
|
||||
highStreetBank.gives(acmeCorp, 1.M, USD)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user