mirror of
https://github.com/corda/corda.git
synced 2024-12-20 05:28:21 +00:00
Universal: refactoring, reduction/evaluation with unit test first steps
This commit is contained in:
parent
116bb7c1ba
commit
fffc049e12
@ -1,12 +1,9 @@
|
|||||||
package com.r3corda.contracts.universal
|
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.Amount
|
||||||
import com.r3corda.core.contracts.Frequency
|
import com.r3corda.core.contracts.Frequency
|
||||||
import com.r3corda.core.crypto.Party
|
import com.r3corda.core.crypto.Party
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,8 +27,9 @@ class Zero() : Arrangement {
|
|||||||
// X is an observable of type BigDecimal.
|
// X is an observable of type BigDecimal.
|
||||||
//
|
//
|
||||||
// todo: should be replaced with something that uses Corda assets and/or cash?
|
// todo: should be replaced with something that uses Corda assets and/or cash?
|
||||||
data class Transfer(val amount: Perceivable<Amount<Currency>>, val from: Party, val to: Party) : Arrangement {
|
data class Transfer(val amount: Perceivable<BigDecimal>, val currency: Currency, val from: Party, val to: Party) : Arrangement {
|
||||||
constructor(amount: Amount<Currency>, from: Party, to: Party ) : this(const(amount), from, to)
|
constructor(amount: BigDecimal, currency: Currency, from: Party, to: Party ) : this(const(amount), currency, from, to)
|
||||||
|
constructor(amount: Amount<Currency>, from: Party, to: Party ) : this(const(BigDecimal(amount.quantity)), amount.token, from, to)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package com.r3corda.contracts.universal
|
package com.r3corda.contracts.universal
|
||||||
|
|
||||||
import com.r3corda.core.contracts.Amount
|
|
||||||
import com.r3corda.core.contracts.Tenor
|
import com.r3corda.core.contracts.Tenor
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.text.DateFormat
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -21,7 +19,19 @@ enum class Comparison {
|
|||||||
/**
|
/**
|
||||||
* Constant perceivable
|
* Constant perceivable
|
||||||
*/
|
*/
|
||||||
data class Const<T>(val value: T) : Perceivable<T>
|
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 is Const<*>) {
|
||||||
|
if (value is BigDecimal && other.value is BigDecimal) {
|
||||||
|
return this.value.compareTo(other.value) == 0
|
||||||
|
}
|
||||||
|
return value.equals(other.value)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun<T> const(k: T) = Const(k)
|
fun<T> const(k: T) = Const(k)
|
||||||
|
|
||||||
@ -75,6 +85,7 @@ data class PerceivableOperation<T>(val left: Perceivable<T>, val op: Operation,
|
|||||||
|
|
||||||
operator fun Perceivable<BigDecimal>.plus(n: BigDecimal) = PerceivableOperation(this, Operation.PLUS, const(n))
|
operator fun Perceivable<BigDecimal>.plus(n: BigDecimal) = PerceivableOperation(this, Operation.PLUS, const(n))
|
||||||
fun<T> Perceivable<T>.plus() = UnaryPlus(this)
|
fun<T> Perceivable<T>.plus() = UnaryPlus(this)
|
||||||
|
operator fun Perceivable<BigDecimal>.minus(n: Perceivable<BigDecimal>) = PerceivableOperation(this, Operation.MINUS, n)
|
||||||
operator fun Perceivable<BigDecimal>.minus(n: BigDecimal) = PerceivableOperation(this, Operation.MINUS, const(n))
|
operator fun Perceivable<BigDecimal>.minus(n: BigDecimal) = PerceivableOperation(this, Operation.MINUS, const(n))
|
||||||
operator fun Perceivable<BigDecimal>.plus(n: Double) = PerceivableOperation(this, Operation.PLUS, const(BigDecimal(n)))
|
operator fun Perceivable<BigDecimal>.plus(n: Double) = PerceivableOperation(this, Operation.PLUS, const(BigDecimal(n)))
|
||||||
operator fun Perceivable<BigDecimal>.minus(n: Double) = PerceivableOperation(this, Operation.MINUS, const(BigDecimal(n)))
|
operator fun Perceivable<BigDecimal>.minus(n: Double) = PerceivableOperation(this, Operation.MINUS, const(BigDecimal(n)))
|
||||||
@ -86,41 +97,33 @@ operator fun Perceivable<BigDecimal>.div(n: Double) = PerceivableOperation(this,
|
|||||||
operator fun Perceivable<Int>.plus(n: Int) = PerceivableOperation(this, Operation.PLUS, const(n))
|
operator fun Perceivable<Int>.plus(n: Int) = PerceivableOperation(this, Operation.PLUS, const(n))
|
||||||
operator fun Perceivable<Int>.minus(n: Int) = PerceivableOperation(this, Operation.MINUS, const(n))
|
operator fun Perceivable<Int>.minus(n: Int) = PerceivableOperation(this, Operation.MINUS, const(n))
|
||||||
|
|
||||||
operator fun<T> Perceivable<Amount<T>>.plus(n: Perceivable<Amount<T>>) = PerceivableOperation(this, Operation.PLUS, n)
|
|
||||||
operator fun<T> Perceivable<Amount<T>>.minus(n: Perceivable<Amount<T>>) = PerceivableOperation(this, Operation.MINUS, n)
|
|
||||||
|
|
||||||
data class ScaleAmount<T>(val left: Perceivable<BigDecimal>, val right: Perceivable<Amount<T>>) : Perceivable<Amount<T>>
|
|
||||||
|
|
||||||
operator fun Perceivable<BigDecimal>.times(n: Amount<Currency>) = ScaleAmount(this, const(n))
|
|
||||||
//PerceivableOperation(this, Operation.TIMES, const(BigDecimal(n)))
|
|
||||||
|
|
||||||
|
|
||||||
class DummyPerceivable<T> : Perceivable<T>
|
class DummyPerceivable<T> : Perceivable<T>
|
||||||
|
|
||||||
data class Interest(val amount: Perceivable<Amount<Currency>>, val dayCountConvention: String,
|
|
||||||
val interest: Perceivable<BigDecimal>, val start: String, val end: String) : Perceivable<Amount<Currency>>
|
|
||||||
|
|
||||||
|
// todo: holidays
|
||||||
|
data class Interest(val amount: Perceivable<BigDecimal>, val dayCountConvention: String,
|
||||||
|
val interest: Perceivable<BigDecimal>, val start: String, val end: String) : Perceivable<BigDecimal>
|
||||||
|
|
||||||
// observable of type T
|
// observable of type T
|
||||||
// example:
|
// example:
|
||||||
val acmeCorporationHasDefaulted = DummyPerceivable<Boolean>()
|
val acmeCorporationHasDefaulted = DummyPerceivable<Boolean>()
|
||||||
|
|
||||||
|
|
||||||
fun libor(@Suppress("UNUSED_PARAMETER") amount: Amount<Currency>, @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable<Amount<Currency>> = DummyPerceivable()
|
fun libor(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable<BigDecimal> = DummyPerceivable()
|
||||||
fun libor(@Suppress("UNUSED_PARAMETER") amount: Amount<Currency>, @Suppress("UNUSED_PARAMETER") start: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>) : Perceivable<Amount<Currency>> = DummyPerceivable()
|
fun libor(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") start: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>) : Perceivable<BigDecimal> = DummyPerceivable()
|
||||||
|
|
||||||
fun interest(@Suppress("UNUSED_PARAMETER") amount: Amount<Currency>, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: BigDecimal /* todo - appropriate type */,
|
fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: BigDecimal /* todo - appropriate type */,
|
||||||
@Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable<Amount<Currency>> = Interest(Const(amount), dayCountConvention, Const(interest), start, end)
|
@Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable<BigDecimal> = Interest(Const(amount), dayCountConvention, Const(interest), start, end)
|
||||||
|
|
||||||
fun interest(@Suppress("UNUSED_PARAMETER") amount: Amount<Currency>, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable<BigDecimal> /* todo - appropriate type */,
|
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: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable<Amount<Currency>> =
|
@Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable<BigDecimal> =
|
||||||
Interest(Const(amount), dayCountConvention, interest, start, end)
|
Interest(Const(amount), dayCountConvention, interest, start, end)
|
||||||
|
|
||||||
fun interest(@Suppress("UNUSED_PARAMETER") amount: Amount<Currency>, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: BigDecimal /* todo - appropriate type */,
|
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<Amount<Currency>> = DummyPerceivable()
|
@Suppress("UNUSED_PARAMETER") start: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>) : Perceivable<BigDecimal> = DummyPerceivable()
|
||||||
|
|
||||||
fun interest(@Suppress("UNUSED_PARAMETER") rate: Amount<Currency>, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable<BigDecimal> /* todo - appropriate type */,
|
fun interest(@Suppress("UNUSED_PARAMETER") rate: 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<Amount<Currency>> = DummyPerceivable()
|
@Suppress("UNUSED_PARAMETER") start: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>) : Perceivable<BigDecimal> = DummyPerceivable()
|
||||||
|
|
||||||
class Fixing(val source: String, val date: LocalDate, val tenor: Tenor) : Perceivable<BigDecimal>
|
class Fixing(val source: String, val date: LocalDate, val tenor: Tenor) : Perceivable<BigDecimal>
|
||||||
|
|
||||||
|
@ -5,8 +5,6 @@ import com.r3corda.core.crypto.Party
|
|||||||
import com.r3corda.core.crypto.SecureHash
|
import com.r3corda.core.crypto.SecureHash
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Instant
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by sofusmortensen on 23/05/16.
|
* Created by sofusmortensen on 23/05/16.
|
||||||
@ -36,65 +34,106 @@ class UniversalContract : Contract {
|
|||||||
class Issue : TypeOnlyCommandData(), Commands
|
class Issue : TypeOnlyCommandData(), Commands
|
||||||
}
|
}
|
||||||
|
|
||||||
fun eval(tx: TransactionForContract, expr: Perceivable<Instant>) : Instant = when (expr) {
|
fun <T> eval(@Suppress("UNUSED_PARAMETER") tx: TransactionForContract, expr: Perceivable<T>): T = when (expr) {
|
||||||
is Const<Instant> -> expr.value
|
is Const -> expr.value
|
||||||
else -> throw NotImplementedError()
|
else -> throw Error("Unable to evaluate")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun eval(tx: TransactionForContract, expr: Perceivable<Boolean>) : Boolean = when (expr) {
|
fun eval(tx: TransactionForContract, expr: Perceivable<Boolean>): Boolean = when (expr) {
|
||||||
is PerceivableAnd -> eval(tx, expr.left) && eval(tx, expr.right)
|
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.right) || eval(tx, expr.right)
|
||||||
is Const<Boolean> -> expr.value
|
is Const<Boolean> -> expr.value
|
||||||
is TimePerceivable -> when (expr.cmp) {
|
is TimePerceivable -> when (expr.cmp) {
|
||||||
Comparison.LTE -> tx.timestamp!!.after!! <= eval(tx, expr.instant)
|
Comparison.LTE -> tx.timestamp!!.after!! <= eval(tx, expr.instant)
|
||||||
Comparison.GTE -> tx.timestamp!!.before!! >= eval(tx, expr.instant)
|
Comparison.GTE -> tx.timestamp!!.before!! >= eval(tx, expr.instant)
|
||||||
else -> throw NotImplementedError()
|
else -> throw NotImplementedError("eval special")
|
||||||
}
|
}
|
||||||
else -> throw NotImplementedError()
|
else -> throw NotImplementedError("eval - Boolean - " + expr.javaClass.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun eval(tx: TransactionForContract, expr: Perceivable<BigDecimal>) : BigDecimal = when (expr) {
|
|
||||||
is Const -> expr.value
|
|
||||||
is Fixing -> {
|
|
||||||
requireThat { "Fixing must be included" by false }
|
|
||||||
BigDecimal(0.0)
|
|
||||||
}
|
|
||||||
else -> throw Error("fook")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun eval(tx: TransactionForContract, expr: Perceivable<Amount<Currency>>) : Perceivable<Amount<Currency>> = when (expr) {
|
fun eval(tx: TransactionForContract, expr: Perceivable<BigDecimal>): BigDecimal =
|
||||||
is PerceivableOperation -> when (expr.op) {
|
when (expr) {
|
||||||
Operation.DIV -> throw NotImplementedError()
|
is Const<BigDecimal> -> expr.value
|
||||||
Operation.MINUS -> {
|
is UnaryPlus -> {
|
||||||
eval(tx, expr.left)
|
val x = eval(tx, expr.arg)
|
||||||
eval(tx, expr.right)
|
if (x > BigDecimal.ZERO)
|
||||||
throw NotImplementedError()
|
x
|
||||||
|
else
|
||||||
|
BigDecimal.ZERO
|
||||||
|
}
|
||||||
|
is PerceivableOperation -> {
|
||||||
|
val l = eval(tx, expr.left)
|
||||||
|
val r = eval(tx, expr.right)
|
||||||
|
|
||||||
|
when (expr.op) {
|
||||||
|
Operation.DIV -> l / r
|
||||||
|
Operation.MINUS -> l - r
|
||||||
|
Operation.PLUS -> l + r
|
||||||
|
Operation.TIMES -> l*r
|
||||||
|
else -> throw NotImplementedError("eval - amount - operation " + expr.op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Fixing -> {
|
||||||
|
requireThat { "Fixing must be included" by false }
|
||||||
|
BigDecimal(0.0)
|
||||||
|
}
|
||||||
|
is Interest -> {
|
||||||
|
val a = eval(tx, expr.amount)
|
||||||
|
val i = eval(tx, expr.interest)
|
||||||
|
|
||||||
|
//todo
|
||||||
|
|
||||||
|
a * i / BigDecimal(100)
|
||||||
|
}
|
||||||
|
else -> throw NotImplementedError("eval - BigDecimal - " + expr.javaClass.name)
|
||||||
}
|
}
|
||||||
Operation.PLUS -> throw NotImplementedError()
|
|
||||||
Operation.TIMES -> throw NotImplementedError()
|
fun reduce(tx: TransactionForContract, expr: Perceivable<BigDecimal>): Perceivable<BigDecimal> = when (expr) {
|
||||||
|
is PerceivableOperation -> {
|
||||||
|
val left = reduce(tx, expr.left)
|
||||||
|
val right = reduce(tx, expr.right)
|
||||||
|
if (left is Const && right is Const)
|
||||||
|
when (expr.op) {
|
||||||
|
//Operation.DIV -> Const( left.value / right.value )
|
||||||
|
Operation.MINUS -> Const(left.value - right.value)
|
||||||
|
Operation.PLUS -> Const(left.value + right.value)
|
||||||
|
//Operation.TIMES -> Const( left.value * right.value )
|
||||||
|
else -> throw NotImplementedError("reduce - " + expr.op.name)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
PerceivableOperation(left, expr.op, right)
|
||||||
}
|
}
|
||||||
is UnaryPlus -> {
|
is UnaryPlus -> {
|
||||||
eval(tx, expr.arg)
|
val amount = reduce(tx, expr.arg)
|
||||||
expr
|
if (amount is Const) {
|
||||||
}
|
if (amount.value > BigDecimal.ZERO)
|
||||||
is Interest -> {
|
amount
|
||||||
eval(tx, expr.interest)
|
else
|
||||||
expr
|
Const(BigDecimal.ZERO)
|
||||||
|
} else
|
||||||
|
UnaryPlus(amount)
|
||||||
}
|
}
|
||||||
|
is Interest -> Interest(reduce(tx, expr.amount), expr.dayCountConvention, reduce(tx, expr.interest), expr.start, expr.end)
|
||||||
else -> expr
|
else -> expr
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkAndReduce(tx: TransactionForContract, arrangement: Arrangement) : Arrangement = when (arrangement)
|
fun checkAndReduce(tx: TransactionForContract, arrangement: Arrangement): Arrangement = when (arrangement) {
|
||||||
{
|
is Transfer -> Transfer(reduce(tx, arrangement.amount), arrangement.currency, arrangement.from, arrangement.to)
|
||||||
is Transfer -> Transfer( eval(tx, arrangement.amount), arrangement.from, arrangement.to )
|
is And -> And(arrangement.arrangements.map { checkAndReduce(tx, it) }.toSet())
|
||||||
is And -> And( arrangement.arrangements.map { checkAndReduce(tx, it) }.toSet() )
|
else -> arrangement
|
||||||
|
}
|
||||||
|
|
||||||
|
fun validateImmediateTransfers(tx: TransactionForContract, arrangement: Arrangement): Arrangement = when (arrangement) {
|
||||||
|
is Transfer -> Transfer(eval(tx, arrangement.amount), arrangement.currency, arrangement.from, arrangement.to)
|
||||||
|
is And -> And(arrangement.arrangements.map { validateImmediateTransfers(tx, it) }.toSet())
|
||||||
else -> arrangement
|
else -> arrangement
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun verify(tx: TransactionForContract) {
|
override fun verify(tx: TransactionForContract) {
|
||||||
|
|
||||||
requireThat {
|
requireThat {
|
||||||
"transaction has a single command".by (tx.commands.size == 1 )
|
"transaction has a single command".by(tx.commands.size == 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
val cmd = tx.commands.requireSingleCommand<UniversalContract.Commands>()
|
val cmd = tx.commands.requireSingleCommand<UniversalContract.Commands>()
|
||||||
@ -109,12 +148,14 @@ class UniversalContract : Contract {
|
|||||||
val action = actions[value.name] ?: throw IllegalArgumentException("Failed requirement: action must be defined")
|
val action = actions[value.name] ?: throw IllegalArgumentException("Failed requirement: action must be defined")
|
||||||
|
|
||||||
requireThat {
|
requireThat {
|
||||||
"action must be timestamped" by ( tx.timestamp != null )
|
"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 } })
|
||||||
"condition must be met" by ( eval(tx, action.condition) )
|
"condition must be met" by (eval(tx, action.condition))
|
||||||
}
|
}
|
||||||
|
|
||||||
val arrangement = checkAndReduce(tx, action.arrangement)
|
// verify that any resulting transfers can be resolved
|
||||||
|
//val arrangement = checkAndReduce(tx, action.arrangement)
|
||||||
|
val arrangement = validateImmediateTransfers(tx, action.arrangement)
|
||||||
|
|
||||||
when (tx.outputs.size) {
|
when (tx.outputs.size) {
|
||||||
1 -> {
|
1 -> {
|
||||||
@ -126,7 +167,7 @@ class UniversalContract : Contract {
|
|||||||
0 -> throw IllegalArgumentException("must have at least one out state")
|
0 -> throw IllegalArgumentException("must have at least one out state")
|
||||||
else -> {
|
else -> {
|
||||||
|
|
||||||
var allContracts = And( tx.outputs.map { (it as State).details }.toSet() )
|
var allContracts = And(tx.outputs.map { (it as State).details }.toSet())
|
||||||
|
|
||||||
requireThat {
|
requireThat {
|
||||||
"output states must match action result state" by (arrangement.equals(allContracts))
|
"output states must match action result state" by (arrangement.equals(allContracts))
|
||||||
@ -138,7 +179,7 @@ class UniversalContract : Contract {
|
|||||||
is Commands.Issue -> {
|
is Commands.Issue -> {
|
||||||
val outState = tx.outputs.single() as State
|
val outState = tx.outputs.single() as State
|
||||||
requireThat {
|
requireThat {
|
||||||
"the transaction is signed by all liable parties" by ( liableParties(outState.details).all { it in cmd.signers } )
|
"the transaction is signed by all liable parties" by (liableParties(outState.details).all { it in cmd.signers })
|
||||||
"the transaction has no input states" by tx.inputs.isEmpty()
|
"the transaction has no input states" by tx.inputs.isEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,7 +188,7 @@ class UniversalContract : Contract {
|
|||||||
val outState = tx.outputs.single() as State
|
val outState = tx.outputs.single() as State
|
||||||
requireThat {
|
requireThat {
|
||||||
"the transaction is signed by all liable parties" by
|
"the transaction is signed by all liable parties" by
|
||||||
( liableParties(outState.details).all { it in cmd.signers } )
|
(liableParties(outState.details).all { it in cmd.signers })
|
||||||
"output state does not reflect move command" by
|
"output state does not reflect move command" by
|
||||||
(replaceParty(inState.details, value.from, value.to).equals(outState.details))
|
(replaceParty(inState.details, value.from, value.to).equals(outState.details))
|
||||||
}
|
}
|
||||||
@ -177,15 +218,14 @@ class UniversalContract : Contract {
|
|||||||
is UnaryPlus -> UnaryPlus(replaceFixing(tx, perceivable.arg, fixings, unusedFixings))
|
is UnaryPlus -> UnaryPlus(replaceFixing(tx, perceivable.arg, fixings, unusedFixings))
|
||||||
is PerceivableOperation -> PerceivableOperation(replaceFixing(tx, perceivable.left, fixings, unusedFixings),
|
is PerceivableOperation -> PerceivableOperation(replaceFixing(tx, perceivable.left, fixings, unusedFixings),
|
||||||
perceivable.op, replaceFixing(tx, perceivable.right, fixings, unusedFixings))
|
perceivable.op, replaceFixing(tx, perceivable.right, fixings, unusedFixings))
|
||||||
is Interest -> Interest( replaceFixing(tx, perceivable.amount, fixings, unusedFixings),
|
is Interest -> Interest(replaceFixing(tx, perceivable.amount, fixings, unusedFixings),
|
||||||
perceivable.dayCountConvention, replaceFixing(tx, perceivable.interest, fixings, unusedFixings),
|
perceivable.dayCountConvention, replaceFixing(tx, perceivable.interest, fixings, unusedFixings),
|
||||||
perceivable.start, perceivable.end) as Perceivable<T>
|
perceivable.start, perceivable.end) as Perceivable<T>
|
||||||
is Fixing -> if (fixings.containsKey(FixOf(perceivable.source, perceivable.date, perceivable.tenor))) {
|
is Fixing -> if (fixings.containsKey(FixOf(perceivable.source, perceivable.date, perceivable.tenor))) {
|
||||||
unusedFixings.remove(FixOf(perceivable.source, perceivable.date, perceivable.tenor))
|
unusedFixings.remove(FixOf(perceivable.source, perceivable.date, perceivable.tenor))
|
||||||
Const(fixings[FixOf(perceivable.source, perceivable.date, perceivable.tenor)]!!) as Perceivable<T>
|
Const(fixings[FixOf(perceivable.source, perceivable.date, perceivable.tenor)]!!) as Perceivable<T>
|
||||||
}
|
} else perceivable
|
||||||
else perceivable
|
else -> throw NotImplementedError("replaceFixing - " + perceivable.javaClass.name)
|
||||||
else -> throw NotImplementedError(perceivable.toString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun replaceFixing(tx: TransactionForContract, arr: Action,
|
fun replaceFixing(tx: TransactionForContract, arr: Action,
|
||||||
@ -194,12 +234,12 @@ class UniversalContract : Contract {
|
|||||||
arr.actors, replaceFixing(tx, arr.arrangement, fixings, unusedFixings))
|
arr.actors, replaceFixing(tx, arr.arrangement, fixings, unusedFixings))
|
||||||
|
|
||||||
fun replaceFixing(tx: TransactionForContract, arr: Arrangement,
|
fun replaceFixing(tx: TransactionForContract, arr: Arrangement,
|
||||||
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>) : Arrangement =
|
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Arrangement =
|
||||||
when (arr) {
|
when (arr) {
|
||||||
is Zero -> arr
|
is Zero -> arr
|
||||||
is Transfer -> Transfer(replaceFixing(tx, arr.amount, fixings, unusedFixings), arr.from, arr.to)
|
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 Or -> Or(arr.actions.map { replaceFixing(tx, it, fixings, unusedFixings) }.toSet())
|
||||||
else -> throw NotImplementedError( arr.toString() )
|
else -> throw NotImplementedError("replaceFixing - " + arr.javaClass.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val legalContractReference: SecureHash
|
override val legalContractReference: SecureHash
|
||||||
@ -207,7 +247,7 @@ class UniversalContract : Contract {
|
|||||||
|
|
||||||
fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: PublicKey) {
|
fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: PublicKey) {
|
||||||
check(tx.inputStates().isEmpty())
|
check(tx.inputStates().isEmpty())
|
||||||
tx.addOutputState( State(listOf(notary), arrangement) )
|
tx.addOutputState(State(listOf(notary), arrangement))
|
||||||
tx.addCommand(Commands.Issue(), at.party.owningKey)
|
tx.addCommand(Commands.Issue(), at.party.owningKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ fun replaceParty(action: Action, from: Party, to: Party) : Action {
|
|||||||
fun replaceParty(arrangement: Arrangement, from: Party, to: Party) : Arrangement {
|
fun replaceParty(arrangement: Arrangement, from: Party, to: Party) : Arrangement {
|
||||||
return when (arrangement) {
|
return when (arrangement) {
|
||||||
is Zero -> arrangement
|
is Zero -> arrangement
|
||||||
is Transfer -> Transfer( arrangement.amount,
|
is Transfer -> Transfer( arrangement.amount, arrangement.currency,
|
||||||
if (arrangement.from == from) to else arrangement.from,
|
if (arrangement.from == from) to else arrangement.from,
|
||||||
if (arrangement.to == from) to else arrangement.to )
|
if (arrangement.to == from) to else arrangement.to )
|
||||||
is Action -> replaceParty(arrangement, from, to)
|
is Action -> replaceParty(arrangement, from, to)
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
package com.r3corda.contracts.universal
|
package com.r3corda.contracts.universal
|
||||||
|
|
||||||
import com.r3corda.core.contracts.Amount
|
|
||||||
import com.r3corda.core.crypto.Party
|
import com.r3corda.core.crypto.Party
|
||||||
|
import java.math.BigDecimal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by sofusmortensen on 23/05/16.
|
* Created by sofusmortensen on 23/05/16.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fun swap(partyA: Party, amountA: Amount<Currency>, partyB: Party, amountB: Amount<Currency>) =
|
fun swap(partyA: Party, amountA: BigDecimal, currencyA: Currency, partyB: Party, amountB: BigDecimal, currencyB: Currency) =
|
||||||
arrange {
|
arrange {
|
||||||
partyA.gives(partyB, amountA)
|
partyA.gives(partyB, amountA, currencyA)
|
||||||
partyB.gives(partyA, amountB)
|
partyB.gives(partyA, amountB, currencyB)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fx_swap(expiry: String, notional: Long, strike: Double,
|
fun fx_swap(expiry: String, notional: BigDecimal, strike: BigDecimal,
|
||||||
foreignCurrency: Currency, domesticCurrency: Currency,
|
foreignCurrency: Currency, domesticCurrency: Currency,
|
||||||
partyA: Party, partyB: Party) =
|
partyA: Party, partyB: Party) =
|
||||||
|
|
||||||
(partyA or partyB).may {
|
(partyA or partyB).may {
|
||||||
"execute".givenThat( after(expiry) ) {
|
"execute".givenThat( after(expiry) ) {
|
||||||
swap(partyA, notional * strike * domesticCurrency, partyB, notional * foreignCurrency)
|
swap(partyA, notional * strike, domesticCurrency, partyB, notional, foreignCurrency)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,4 +29,4 @@ fun fx_swap2(expiry: String, notional: Long, strike: Double,
|
|||||||
foreignCurrency: Currency, domesticCurrency: Currency,
|
foreignCurrency: Currency, domesticCurrency: Currency,
|
||||||
partyA: Party, partyB: Party) =
|
partyA: Party, partyB: Party) =
|
||||||
Action("execute", after(expiry), setOf(partyA, partyB),
|
Action("execute", after(expiry), setOf(partyA, partyB),
|
||||||
swap(partyA, notional * strike * domesticCurrency, partyB, notional * foreignCurrency))
|
swap(partyA, BigDecimal(notional * strike), domesticCurrency, partyB, BigDecimal(notional), foreignCurrency))
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package com.r3corda.contracts.universal
|
package com.r3corda.contracts.universal
|
||||||
|
|
||||||
import com.r3corda.core.contracts.Amount
|
|
||||||
import com.r3corda.core.contracts.Frequency
|
import com.r3corda.core.contracts.Frequency
|
||||||
import com.r3corda.core.contracts.USD
|
|
||||||
import com.r3corda.core.crypto.Party
|
import com.r3corda.core.crypto.Party
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -17,23 +15,23 @@ 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(arrangement: Action) = Or( this.actions.plusElement(arrangement) )
|
||||||
infix fun Or.or(ors: Or) = Or( this.actions.plus(ors.actions) )
|
infix fun Or.or(ors: Or) = Or( this.actions.plus(ors.actions) )
|
||||||
|
|
||||||
operator fun Long.times(currency: Currency) = Amount(this.toLong(), currency)
|
// operator fun Long.times(currency: Currency) = Amount(this.toLong(), currency)
|
||||||
operator fun Double.times(currency: Currency) = Amount(BigDecimal(this.toDouble()), currency)
|
// operator fun Double.times(currency: Currency) = Amount(BigDecimal(this.toDouble()), currency)
|
||||||
|
|
||||||
val Int.M: Long get() = this.toLong() * 1000000
|
val Int.M: BigDecimal get() = BigDecimal(this) * BigDecimal(1000000)
|
||||||
val Int.K: Long get() = this.toLong() * 1000
|
val Int.K: BigDecimal get() = BigDecimal(this) * BigDecimal(1000)
|
||||||
|
|
||||||
val zero = Zero()
|
val zero = Zero()
|
||||||
|
|
||||||
class ContractBuilder {
|
class ContractBuilder {
|
||||||
val contracts = mutableListOf<Arrangement>()
|
val contracts = mutableListOf<Arrangement>()
|
||||||
|
|
||||||
fun Party.gives(beneficiary: Party, amount: Amount<Currency>) {
|
fun Party.gives(beneficiary: Party, amount: BigDecimal, currency: Currency) {
|
||||||
contracts.add( Transfer(amount, this, beneficiary))
|
contracts.add( Transfer(amount, currency, this, beneficiary))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Party.gives(beneficiary: Party, amount: Perceivable<Amount<Currency>>) {
|
fun Party.gives(beneficiary: Party, amount: Perceivable<BigDecimal>, currency: Currency) {
|
||||||
contracts.add( Transfer(amount, this, beneficiary))
|
contracts.add( Transfer(amount, currency, this, beneficiary))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated(level = DeprecationLevel.ERROR, message = "Not available")
|
@Deprecated(level = DeprecationLevel.ERROR, message = "Not available")
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.r3corda.contracts.universal
|
package com.r3corda.contracts.universal
|
||||||
|
|
||||||
import com.r3corda.core.contracts.Fix
|
|
||||||
import com.r3corda.core.contracts.FixOf
|
import com.r3corda.core.contracts.FixOf
|
||||||
import com.r3corda.core.contracts.Tenor
|
import com.r3corda.core.contracts.Tenor
|
||||||
import com.r3corda.testing.*
|
import com.r3corda.testing.*
|
||||||
@ -20,39 +19,39 @@ class Caplet {
|
|||||||
|
|
||||||
val dt = LocalDate.of(2016, 9, 1)
|
val dt = LocalDate.of(2016, 9, 1)
|
||||||
|
|
||||||
val fx = Fix(FixOf("LIBOR", dt, Tenor("6M")), BigDecimal.valueOf(0.31207))
|
val notional = 50.M
|
||||||
|
val currency = EUR
|
||||||
val notional = 50.M*EUR
|
|
||||||
|
|
||||||
val contract =
|
val contract =
|
||||||
(roadRunner or wileECoyote).may {
|
(roadRunner or wileECoyote).may {
|
||||||
"exercise".anytime() {
|
"exercise".anytime() {
|
||||||
val floating = interest(notional, "act/365", fix("LIBOR", dt, Tenor("6M")), "2016-04-01", "2016-10-01" )
|
val floating = interest(notional, "act/365", fix("LIBOR", dt, Tenor("6M")), "2016-04-01", "2016-10-01" )
|
||||||
val fixed = interest(notional, "act/365", BigDecimal.valueOf(1.0), "2016-04-01", "2016-10-01")
|
val fixed = interest(notional, "act/365", BigDecimal.valueOf(0.5), "2016-04-01", "2016-10-01")
|
||||||
wileECoyote.gives(roadRunner, (floating - fixed).plus() )
|
wileECoyote.gives(roadRunner, (floating - fixed).plus(), currency )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val contractFixed =
|
val contractFixed =
|
||||||
(roadRunner or wileECoyote).may {
|
(roadRunner or wileECoyote).may {
|
||||||
"exercise".anytime() {
|
"exercise".anytime() {
|
||||||
val floating = interest(notional, "act/365", BigDecimal.valueOf(.01), "2016-04-01", "2016-10-01" )
|
val floating = interest(notional, "act/365", BigDecimal.valueOf(1.0), "2016-04-01", "2016-10-01" )
|
||||||
val fixed = interest(notional, "act/365", BigDecimal.valueOf(1.0), "2016-04-01", "2016-10-01")
|
val fixed = interest(notional, "act/365", BigDecimal.valueOf(0.5), "2016-04-01", "2016-10-01")
|
||||||
wileECoyote.gives(roadRunner, (floating - fixed).plus() )
|
wileECoyote.gives(roadRunner, (floating - fixed).plus(), currency )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val inState = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contract)
|
val contractFinal = arrange { wileECoyote.gives(roadRunner, 250.K, EUR) }
|
||||||
|
|
||||||
val outStateFixed = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractFixed)
|
val stateStart = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contract)
|
||||||
|
|
||||||
val transfer = arrange { wileECoyote.gives(roadRunner, 100.K*EUR )}
|
val stateFixed = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractFixed)
|
||||||
val outState = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transfer )
|
|
||||||
|
val stateFinal = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractFinal )
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun issue() {
|
fun issue() {
|
||||||
transaction {
|
transaction {
|
||||||
output { inState }
|
output { stateStart }
|
||||||
timestamp(TEST_TX_TIME_1)
|
timestamp(TEST_TX_TIME_1)
|
||||||
|
|
||||||
this `fails with` "transaction has a single command"
|
this `fails with` "transaction has a single command"
|
||||||
@ -69,10 +68,10 @@ class Caplet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `execute - missing fixing `() {
|
fun `execute`() {
|
||||||
transaction {
|
transaction {
|
||||||
input { inState }
|
input { stateFixed }
|
||||||
output { outState }
|
output { stateFinal }
|
||||||
timestamp(TEST_TX_TIME_1)
|
timestamp(TEST_TX_TIME_1)
|
||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
@ -82,15 +81,15 @@ class Caplet {
|
|||||||
|
|
||||||
command(wileECoyote.owningKey) { UniversalContract.Commands.Action("exercise") }
|
command(wileECoyote.owningKey) { UniversalContract.Commands.Action("exercise") }
|
||||||
|
|
||||||
this `fails with` "fixing must be included"
|
this.verifies()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `fixing`() {
|
fun `fixing`() {
|
||||||
transaction {
|
transaction {
|
||||||
input { inState }
|
input { stateStart }
|
||||||
output { outStateFixed }
|
output { stateFixed }
|
||||||
timestamp(TEST_TX_TIME_1)
|
timestamp(TEST_TX_TIME_1)
|
||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
@ -100,26 +99,32 @@ class Caplet {
|
|||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
// wrong source
|
// wrong source
|
||||||
command(wileECoyote.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBORx", dt, Tenor("6M")), BigDecimal.valueOf(.01)))) }
|
command(wileECoyote.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBORx", dt, Tenor("6M")), BigDecimal(1.0)))) }
|
||||||
|
|
||||||
this `fails with` "relevant fixing must be included"
|
this `fails with` "relevant fixing must be included"
|
||||||
}
|
}
|
||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
// wrong date
|
// wrong date
|
||||||
command(wileECoyote.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt.plusYears(1), Tenor("6M")), BigDecimal.valueOf(.01)))) }
|
command(wileECoyote.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt.plusYears(1), Tenor("6M")), BigDecimal(1.0)))) }
|
||||||
|
|
||||||
this `fails with` "relevant fixing must be included"
|
this `fails with` "relevant fixing must be included"
|
||||||
}
|
}
|
||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
// wrong tenor
|
// wrong tenor
|
||||||
command(wileECoyote.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt, Tenor("3M")), BigDecimal.valueOf(.01)))) }
|
command(wileECoyote.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt, Tenor("3M")), BigDecimal.valueOf(1.0)))) }
|
||||||
|
|
||||||
this `fails with` "relevant fixing must be included"
|
this `fails with` "relevant fixing must be included"
|
||||||
}
|
}
|
||||||
|
|
||||||
command(wileECoyote.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt, Tenor("6M")), BigDecimal.valueOf(.01)))) }
|
tweak {
|
||||||
|
command(wileECoyote.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt, Tenor("6M")), BigDecimal.valueOf(1.5)))) }
|
||||||
|
|
||||||
|
this `fails with` "output state does not reflect fix command"
|
||||||
|
}
|
||||||
|
|
||||||
|
command(wileECoyote.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt, Tenor("6M")), BigDecimal.valueOf(1.0)))) }
|
||||||
|
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
package com.r3corda.contracts.universal
|
package com.r3corda.contracts.universal
|
||||||
|
|
||||||
import com.r3corda.core.contracts.Amount
|
|
||||||
import com.r3corda.core.contracts.Tenor
|
|
||||||
import com.r3corda.core.crypto.Party
|
import com.r3corda.core.crypto.Party
|
||||||
import com.r3corda.core.crypto.generateKeyPair
|
import com.r3corda.core.crypto.generateKeyPair
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.math.BigDecimal
|
|
||||||
import java.time.Instant
|
|
||||||
import java.time.LocalDate
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,7 +27,7 @@ class ContractDefinition {
|
|||||||
|
|
||||||
val cds_contract = roadRunner.may {
|
val cds_contract = roadRunner.may {
|
||||||
"payout".givenThat( acmeCorporationHasDefaulted and before("2017-09-01") ) {
|
"payout".givenThat( acmeCorporationHasDefaulted and before("2017-09-01") ) {
|
||||||
wileECoyote.gives(roadRunner, 1.M*USD)
|
wileECoyote.gives(roadRunner, 1.M, USD)
|
||||||
}
|
}
|
||||||
} or wileECoyote.may {
|
} or wileECoyote.may {
|
||||||
"expire".givenThat( after("2017-09-01") ) {}
|
"expire".givenThat( after("2017-09-01") ) {}
|
||||||
@ -41,8 +36,8 @@ class ContractDefinition {
|
|||||||
|
|
||||||
val american_fx_option = roadRunner.may {
|
val american_fx_option = roadRunner.may {
|
||||||
"exercise".anytime {
|
"exercise".anytime {
|
||||||
wileECoyote.gives(roadRunner, 1.M*EUR)
|
wileECoyote.gives(roadRunner, 1.M, EUR)
|
||||||
roadRunner.gives(wileECoyote, 1200.K*USD)
|
roadRunner.gives(wileECoyote, 1200.K, USD)
|
||||||
}
|
}
|
||||||
} or wileECoyote.may {
|
} or wileECoyote.may {
|
||||||
"expire".givenThat(after("2017-09-01")) {}
|
"expire".givenThat(after("2017-09-01")) {}
|
||||||
@ -53,8 +48,8 @@ class ContractDefinition {
|
|||||||
"exercise".anytime {
|
"exercise".anytime {
|
||||||
(roadRunner or wileECoyote).may {
|
(roadRunner or wileECoyote).may {
|
||||||
"execute".givenThat( after("2017-09-01") ) {
|
"execute".givenThat( after("2017-09-01") ) {
|
||||||
wileECoyote.gives( roadRunner, 1.M*EUR )
|
wileECoyote.gives( roadRunner, 1.M, EUR )
|
||||||
roadRunner.gives( wileECoyote, 1200.K*USD )
|
roadRunner.gives( wileECoyote, 1200.K, USD )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,20 +17,20 @@ class FXSwap {
|
|||||||
val contract =
|
val contract =
|
||||||
(roadRunner or wileECoyote).may {
|
(roadRunner or wileECoyote).may {
|
||||||
"execute".givenThat(after("2017-09-01")) {
|
"execute".givenThat(after("2017-09-01")) {
|
||||||
wileECoyote.gives(roadRunner, 1200.K*USD)
|
wileECoyote.gives(roadRunner, 1200.K, USD)
|
||||||
roadRunner.gives(wileECoyote, 1.M*EUR)
|
roadRunner.gives(wileECoyote, 1.M, EUR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val transfer1 = arrange { wileECoyote.gives(roadRunner, 1200.K*USD) }
|
val transfer1 = arrange { wileECoyote.gives(roadRunner, 1200.K, USD) }
|
||||||
val transfer2 = arrange { roadRunner.gives(wileECoyote, 1.M*EUR) }
|
val transfer2 = arrange { roadRunner.gives(wileECoyote, 1.M, EUR) }
|
||||||
|
|
||||||
val outState1 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transfer1 )
|
val outState1 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transfer1 )
|
||||||
val outState2 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transfer2 )
|
val outState2 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transfer2 )
|
||||||
|
|
||||||
val transferBad1 = arrange { wileECoyote.gives(roadRunner, 1200.K*GBP) } // wrong currency
|
val transferBad1 = arrange { wileECoyote.gives(roadRunner, 1200.K, GBP) } // wrong currency
|
||||||
val transferBad2 = arrange { roadRunner.gives(wileECoyote, 900.K*EUR) } // wrong amount
|
val transferBad2 = arrange { roadRunner.gives(wileECoyote, 900.K, EUR) } // wrong amount
|
||||||
val transferBad3 = arrange { wileECoyote.gives(wileECoyote, 1.M*EUR) } // wrong party
|
val transferBad3 = arrange { wileECoyote.gives(wileECoyote, 1.M, EUR) } // wrong party
|
||||||
|
|
||||||
val outStateBad1 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transferBad1 )
|
val outStateBad1 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transferBad1 )
|
||||||
val outStateBad2 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transferBad2 )
|
val outStateBad2 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transferBad2 )
|
||||||
|
@ -13,18 +13,19 @@ import java.math.BigDecimal
|
|||||||
|
|
||||||
class Swaption {
|
class Swaption {
|
||||||
|
|
||||||
val notional = 10.M * USD
|
val notional = 10.M
|
||||||
|
val currency = USD
|
||||||
val coupon = BigDecimal.valueOf(1.5)
|
val coupon = BigDecimal.valueOf(1.5)
|
||||||
|
|
||||||
val dreary_contract =
|
val dreary_contract =
|
||||||
(wileECoyote or roadRunner).may {
|
(wileECoyote or roadRunner).may {
|
||||||
"proceed".givenThat(after("01/07/2015")) {
|
"proceed".givenThat(after("01/07/2015")) {
|
||||||
wileECoyote.gives(roadRunner, libor( notional, "01/04/2015", "01/07/2015" ) )
|
wileECoyote.gives(roadRunner, libor( notional, "01/04/2015", "01/07/2015" ), currency )
|
||||||
roadRunner.gives(wileECoyote, interest( notional, "act/365", coupon, "01/04/2015", "01/07/2015" ) )
|
roadRunner.gives(wileECoyote, interest( notional, "act/365", coupon, "01/04/2015", "01/07/2015" ), currency )
|
||||||
(wileECoyote or roadRunner).may {
|
(wileECoyote or roadRunner).may {
|
||||||
"proceed".givenThat(after("01/10/2015")) {
|
"proceed".givenThat(after("01/10/2015")) {
|
||||||
wileECoyote.gives(roadRunner, libor( notional, "01/07/2015", "01/10/2015" ) )
|
wileECoyote.gives(roadRunner, libor( notional, "01/07/2015", "01/10/2015" ), currency )
|
||||||
roadRunner.gives(wileECoyote, interest( notional, "act/365", coupon, "01/07/2015", "01/10/2015" ) )
|
roadRunner.gives(wileECoyote, interest( notional, "act/365", coupon, "01/07/2015", "01/10/2015" ), currency )
|
||||||
|
|
||||||
(wileECoyote or roadRunner).may {
|
(wileECoyote or roadRunner).may {
|
||||||
// etc ...
|
// etc ...
|
||||||
@ -32,13 +33,13 @@ class Swaption {
|
|||||||
}
|
}
|
||||||
} or roadRunner.may {
|
} or roadRunner.may {
|
||||||
"cancel".anytime {
|
"cancel".anytime {
|
||||||
roadRunner.gives( wileECoyote, 10.K * USD )
|
roadRunner.gives( wileECoyote, 10.K, USD )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} or roadRunner.may {
|
} or roadRunner.may {
|
||||||
"cancel".anytime {
|
"cancel".anytime {
|
||||||
roadRunner.gives( wileECoyote, 10.K * USD )
|
roadRunner.gives( wileECoyote, 10.K, USD )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,13 +47,13 @@ class Swaption {
|
|||||||
val elegant_contract = rollOut( "01/04/2015", "01/04/2025", Frequency.Quarterly ) {
|
val elegant_contract = rollOut( "01/04/2015", "01/04/2025", Frequency.Quarterly ) {
|
||||||
(wileECoyote or roadRunner).may {
|
(wileECoyote or roadRunner).may {
|
||||||
"proceed".givenThat(after(start)) {
|
"proceed".givenThat(after(start)) {
|
||||||
wileECoyote.gives(roadRunner, libor( notional, start, end ) )
|
wileECoyote.gives(roadRunner, libor( notional, start, end ), currency )
|
||||||
roadRunner.gives(wileECoyote, interest( notional, "act/365", coupon, start, end ) )
|
roadRunner.gives(wileECoyote, interest( notional, "act/365", coupon, start, end ), currency )
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
} or roadRunner.may {
|
} or roadRunner.may {
|
||||||
"cancel".anytime {
|
"cancel".anytime {
|
||||||
roadRunner.gives( wileECoyote, 10.K * USD )
|
roadRunner.gives( wileECoyote, 10.K, currency )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,7 +62,7 @@ class Swaption {
|
|||||||
val strike = 1.2
|
val strike = 1.2
|
||||||
|
|
||||||
val tarf = rollOut( "01/04/2015", "01/04/2016", Frequency.Quarterly, object {
|
val tarf = rollOut( "01/04/2015", "01/04/2016", Frequency.Quarterly, object {
|
||||||
val cap = variable( 150.K*USD )
|
val cap = variable( 150.K )
|
||||||
}) {
|
}) {
|
||||||
roadRunner.may {
|
roadRunner.may {
|
||||||
"exercise".givenThat(before(end)) {
|
"exercise".givenThat(before(end)) {
|
||||||
@ -69,7 +70,7 @@ class Swaption {
|
|||||||
|
|
||||||
(roadRunner or wileECoyote).may {
|
(roadRunner or wileECoyote).may {
|
||||||
"proceed".givenThat(after(end)) {
|
"proceed".givenThat(after(end)) {
|
||||||
wileECoyote.gives(roadRunner, payout)
|
wileECoyote.gives(roadRunner, payout, USD)
|
||||||
next(vars.cap to vars.cap - payout)
|
next(vars.cap to vars.cap - payout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,7 +91,7 @@ class Swaption {
|
|||||||
|
|
||||||
(roadRunner or wileECoyote).may {
|
(roadRunner or wileECoyote).may {
|
||||||
"proceed".givenThat(after(end)) {
|
"proceed".givenThat(after(end)) {
|
||||||
wileECoyote.gives(roadRunner, payout)
|
wileECoyote.gives(roadRunner, payout, currency)
|
||||||
next(vars.uses to vars.uses - 1)
|
next(vars.uses to vars.uses - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class ZeroCouponBond {
|
|||||||
val contract =
|
val contract =
|
||||||
(roadRunner or wileECoyote).may {
|
(roadRunner or wileECoyote).may {
|
||||||
"execute".givenThat(after("2017-09-01")) {
|
"execute".givenThat(after("2017-09-01")) {
|
||||||
wileECoyote.gives(roadRunner, 100.K*GBP)
|
wileECoyote.gives(roadRunner, 100.K, GBP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,14 +22,14 @@ class ZeroCouponBond {
|
|||||||
val contractMove =
|
val contractMove =
|
||||||
(porkyPig or wileECoyote).may {
|
(porkyPig or wileECoyote).may {
|
||||||
"execute".givenThat(after("2017-09-01")) {
|
"execute".givenThat(after("2017-09-01")) {
|
||||||
wileECoyote.gives(porkyPig, 100.K*GBP)
|
wileECoyote.gives(porkyPig, 100.K, GBP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
||||||
|
|
||||||
val transfer = arrange { wileECoyote.gives(roadRunner, 100.K*GBP) }
|
val transfer = arrange { wileECoyote.gives(roadRunner, 100.K, GBP) }
|
||||||
val transferWrong = arrange { wileECoyote.gives(roadRunner, 80.K*GBP) }
|
val transferWrong = arrange { wileECoyote.gives(roadRunner, 80.K, GBP) }
|
||||||
|
|
||||||
val inState = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contract )
|
val inState = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contract )
|
||||||
|
|
||||||
|
@ -20,21 +20,21 @@ import java.util.*
|
|||||||
// fx swap
|
// fx swap
|
||||||
// both parties have the right to trigger the exchange of cash flows
|
// both parties have the right to trigger the exchange of cash flows
|
||||||
val an_fx_swap = Action("execute", after("2017-09-01"), setOf(roadRunner, wileECoyote),
|
val an_fx_swap = Action("execute", after("2017-09-01"), setOf(roadRunner, wileECoyote),
|
||||||
Transfer(1200.K * USD, wileECoyote, roadRunner)
|
Transfer(1200.K, USD, wileECoyote, roadRunner)
|
||||||
and Transfer(1.M * EUR, roadRunner, wileECoyote))
|
and Transfer(1.M, EUR, roadRunner, wileECoyote))
|
||||||
|
|
||||||
val american_fx_option = Action("exercise", before("2017-09-01"),
|
val american_fx_option = Action("exercise", before("2017-09-01"),
|
||||||
roadRunner,
|
roadRunner,
|
||||||
Transfer(1200.K * USD, wileECoyote, roadRunner)
|
Transfer(1200.K, USD, wileECoyote, roadRunner)
|
||||||
and Transfer(1.M * EUR, roadRunner, wileECoyote))
|
and Transfer(1.M, EUR, roadRunner, wileECoyote))
|
||||||
|
|
||||||
val european_fx_option = Action("exercise", before("2017-09-01"), roadRunner, fx_swap("2017-09-01", 1.M, 1.2, EUR, USD, roadRunner, wileECoyote)) or
|
val european_fx_option = Action("exercise", before("2017-09-01"), roadRunner, fx_swap("2017-09-01", 1.M, BigDecimal(1.2), EUR, USD, roadRunner, wileECoyote)) or
|
||||||
Action("expire", after("2017-09-01"), wileECoyote, zero)
|
Action("expire", after("2017-09-01"), wileECoyote, zero)
|
||||||
|
|
||||||
val zero_coupon_bond_1 = Action("execute", after("2017-09-01"), roadRunner, Transfer(1.M * USD, wileECoyote, roadRunner))
|
val zero_coupon_bond_1 = Action("execute", after("2017-09-01"), roadRunner, Transfer(1.M, USD, wileECoyote, roadRunner))
|
||||||
|
|
||||||
// maybe in the presence of negative interest rates you would want other side of contract to be able to take initiative as well
|
// 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 = Action("execute", after("2017-09-01"), setOf(roadRunner, wileECoyote), Transfer(1.M * USD, wileECoyote, roadRunner))
|
val zero_coupon_bond_2 = Action("execute", after("2017-09-01"), setOf(roadRunner, wileECoyote), Transfer(1.M, USD, wileECoyote, roadRunner))
|
||||||
|
|
||||||
// no touch
|
// no touch
|
||||||
// Party Receiver
|
// Party Receiver
|
||||||
@ -45,8 +45,8 @@ import java.util.*
|
|||||||
//
|
//
|
||||||
// Assume observable is using FX fixing
|
// Assume observable is using FX fixing
|
||||||
//
|
//
|
||||||
val no_touch = Action("execute", after("2017-09-01"), setOf(roadRunner, wileECoyote), Transfer(1.M * USD, wileECoyote, roadRunner)) or
|
val no_touch = Action("execute", after("2017-09-01"), setOf(roadRunner, wileECoyote), Transfer(1.M, USD, wileECoyote, roadRunner)) or
|
||||||
Action("knock out", EUR / USD gt 1.3, wileECoyote, zero)
|
Action("knock out", EUR / USD gt 1.3, wileECoyote, zero)
|
||||||
|
|
||||||
val one_touch = Action("expire", after("2017-09-01"), wileECoyote, zero) or
|
val one_touch = Action("expire", after("2017-09-01"), wileECoyote, zero) or
|
||||||
Action("knock in", EUR / USD gt 1.3, roadRunner, Transfer(1.M * USD, wileECoyote, roadRunner))
|
Action("knock in", EUR / USD gt 1.3, roadRunner, Transfer(1.M, USD, wileECoyote, roadRunner))
|
||||||
|
Loading…
Reference in New Issue
Block a user