Universal: checkpoint before refactor

This commit is contained in:
sofusmortensen 2016-09-11 11:56:28 +02:00
parent dab883dcba
commit b93aa71afa
8 changed files with 292 additions and 91 deletions

View File

@ -4,6 +4,7 @@ import com.r3corda.core.contracts.Amount
import com.r3corda.core.contracts.Frequency
import com.r3corda.core.crypto.Party
import java.math.BigDecimal
import java.time.LocalDate
import java.util.*
/**
@ -49,7 +50,7 @@ data class Or(val actions: Set<Action>) : Arrangement
// Roll out of arrangement
data class RollOut(val startDate: String, val endDate: String, val frequency: Frequency, val template: Arrangement) : Arrangement
data class RollOut(val startDate: LocalDate, val endDate: LocalDate, val frequency: Frequency, val template: Arrangement) : Arrangement
// Continuation of roll out

View File

@ -1,5 +1,6 @@
package com.r3corda.contracts.universal
import com.r3corda.core.contracts.BusinessCalendar
import com.r3corda.core.contracts.Tenor
import java.math.BigDecimal
import java.time.Instant
@ -44,14 +45,16 @@ class EndDate : Perceivable<Instant>
*/
data class TimePerceivable(val cmp: Comparison, val instant: Perceivable<Instant>) : Perceivable<Boolean>
fun parseDate(str: String) = Instant.parse(str+"T00:00:00Z")!!
fun parseDate(str: String) = BusinessCalendar.parseDateFromString(str)
// Instant.parse(str+"T00:00:00Z")!!
fun before(expiry: Perceivable<Instant>) = TimePerceivable(Comparison.LTE, expiry)
fun after(expiry: Perceivable<Instant>) = TimePerceivable(Comparison.GTE, expiry)
fun before(expiry: Instant) = TimePerceivable(Comparison.LTE, const(expiry))
fun after(expiry: Instant) = TimePerceivable(Comparison.GTE, const(expiry))
fun before(expiry: String) = TimePerceivable(Comparison.LTE, const(parseDate(expiry)))
fun after(expiry: String) = TimePerceivable(Comparison.GTE, const(parseDate(expiry)))
fun before(expiry: String) = TimePerceivable(Comparison.LTE, const(parseDate(expiry).toInstant()))
fun after(expiry: String) = TimePerceivable(Comparison.GTE, const(parseDate(expiry).toInstant()))
data class PerceivableAnd(val left: Perceivable<Boolean>, val right: Perceivable<Boolean>) : Perceivable<Boolean>
infix fun Perceivable<Boolean>.and(obs: Perceivable<Boolean>) = PerceivableAnd(this, obs)
@ -102,23 +105,23 @@ class DummyPerceivable<T> : Perceivable<T>
// todo: holidays
data class Interest(val amount: Perceivable<BigDecimal>, val dayCountConvention: String,
val interest: Perceivable<BigDecimal>, val start: LocalDate, val end: LocalDate) : Perceivable<BigDecimal>
val interest: Perceivable<BigDecimal>, val start: Perceivable<Instant>, val end: Perceivable<Instant>) : Perceivable<BigDecimal>
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: BigDecimal, @Suppress("UNUSED_PARAMETER") start: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>) : Perceivable<BigDecimal> = DummyPerceivable()
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<BigDecimal> = Interest(Const(amount), dayCountConvention, Const(interest), parseDate(start).toLocalDate(), parseDate(end).toLocalDate())
@Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable<BigDecimal> = Interest(Const(amount), dayCountConvention, Const(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: Perceivable<BigDecimal> /* todo - appropriate type */,
@Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable<BigDecimal> =
Interest(Const(amount), dayCountConvention, interest, parseDate(start).toLocalDate(), parseDate(end).toLocalDate())
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()
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<BigDecimal> = DummyPerceivable()
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>

View File

@ -4,6 +4,7 @@ import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.transactions.TransactionBuilder
import com.sun.tools.corba.se.idl.InvalidArgument
import java.math.BigDecimal
import java.security.PublicKey
import java.time.Instant
@ -39,19 +40,11 @@ class UniversalContract : Contract {
class Issue : TypeOnlyCommandData(), Commands
}
fun <T> eval(@Suppress("UNUSED_PARAMETER") tx: TransactionForContract, expr: Perceivable<T>): T = when (expr) {
is Const -> expr.value
else -> throw Error("Unable to evaluate")
}
fun eval(@Suppress("UNUSED_PARAMETER") tx: TransactionForContract, expr: Perceivable<LocalDate>): LocalDate = when (expr) {
is Const -> expr.value
else -> throw Error("Unable to evaluate")
}
fun eval(@Suppress("UNUSED_PARAMETER") tx: TransactionForContract, expr: Perceivable<Instant>): Instant = when (expr) {
is Const -> expr.value
else -> throw Error("Unable to evaluate")
}
fun eval(tx: TransactionForContract, expr: Perceivable<Boolean>): Boolean = when (expr) {
is PerceivableAnd -> eval(tx, expr.left) && eval(tx, expr.right)
is PerceivableOr -> eval(tx, expr.right) || eval(tx, expr.right)
@ -64,7 +57,6 @@ class UniversalContract : Contract {
else -> throw NotImplementedError("eval - Boolean - " + expr.javaClass.name)
}
fun eval(tx: TransactionForContract, expr: Perceivable<BigDecimal>): BigDecimal =
when (expr) {
is Const<BigDecimal> -> expr.value
@ -102,41 +94,6 @@ class UniversalContract : Contract {
else -> throw NotImplementedError("eval - BigDecimal - " + expr.javaClass.name)
}
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 -> {
val amount = reduce(tx, expr.arg)
if (amount is Const) {
if (amount.value > BigDecimal.ZERO)
amount
else
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
}
fun checkAndReduce(tx: TransactionForContract, arrangement: Arrangement): Arrangement = when (arrangement) {
is Transfer -> Transfer(reduce(tx, arrangement.amount), arrangement.currency, arrangement.from, arrangement.to)
is And -> And(arrangement.arrangements.map { checkAndReduce(tx, it) }.toSet())
else -> arrangement
}
fun validateImmediateTransfers(tx: TransactionForContract, arrangement: Arrangement): Arrangement = when (arrangement) {
is Transfer -> {
val amount = eval(tx, arrangement.amount)
@ -147,6 +104,32 @@ class UniversalContract : Contract {
else -> arrangement
}
// todo: think about multi layered rollouts
fun reduceRollOut(rollOut: RollOut) : Arrangement {
val start = rollOut.startDate
val end = rollOut.endDate
// todo: calendar + rolling conventions
val schedule = BusinessCalendar.createGenericSchedule(start, rollOut.frequency, noOfAdditionalPeriods = 1, endDate = end)
val next = schedule.first() // fail if no dates
val newRollOut = RollOut(next, end, rollOut.frequency, rollOut.template)
return replaceNext(rollOut.template, 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)
}
}
override fun verify(tx: TransactionForContract) {
requireThat {
@ -160,7 +143,14 @@ class UniversalContract : Contract {
when (value) {
is Commands.Action -> {
val inState = tx.inputs.single() as State
val actions = actions(inState.details)
val arr = when (inState.details) {
is Or -> inState.details
is Action -> inState.details
is RollOut -> reduceRollOut(inState.details)
else -> throw InvalidArgument("Unexpected arrangement, " + tx.inputs.single())
}
val actions = actions(arr)
val action = actions[value.name] ?: throw IllegalArgumentException("Failed requirement: action must be defined")
@ -229,8 +219,8 @@ class UniversalContract : Contract {
}
fun <T> replaceFixing(tx: TransactionForContract, perceivable: Perceivable<T>,
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Perceivable<T> =
when (perceivable) {
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Perceivable<T> {
return when (perceivable) {
is Const -> perceivable
is UnaryPlus -> UnaryPlus(replaceFixing(tx, perceivable.arg, fixings, unusedFixings))
is PerceivableOperation -> PerceivableOperation(replaceFixing(tx, perceivable.left, fixings, unusedFixings),
@ -244,6 +234,7 @@ class UniversalContract : Contract {
} else perceivable
else -> throw NotImplementedError("replaceFixing - " + perceivable.javaClass.name)
}
}
fun replaceFixing(tx: TransactionForContract, arr: Action,
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>) =
@ -256,6 +247,7 @@ class UniversalContract : Contract {
is Zero -> arr
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 RollOut -> RollOut(arr.startDate, arr.endDate, arr.frequency, replaceFixing(tx, arr.template, fixings, unusedFixings))
else -> throw NotImplementedError("replaceFixing - " + arr.javaClass.name)
}

View File

@ -1,8 +1,10 @@
package com.r3corda.contracts.universal
import com.r3corda.core.contracts.BusinessCalendar
import com.r3corda.core.contracts.Frequency
import com.r3corda.core.crypto.Party
import java.math.BigDecimal
import java.time.LocalDate
import java.util.*
/**
@ -23,19 +25,19 @@ val zero = Zero()
open class ContractBuilder {
val contracts = mutableListOf<Arrangement>()
fun Party.gives(beneficiary: Party, amount: BigDecimal, currency: Currency) : Transfer {
fun Party.gives(beneficiary: Party, amount: BigDecimal, currency: Currency): Transfer {
val c = Transfer(const(amount), currency, this, beneficiary)
contracts.add( c )
contracts.add(c)
return c
}
fun Party.gives(beneficiary: Party, amount: Perceivable<BigDecimal>, currency: Currency) : Transfer {
fun Party.gives(beneficiary: Party, amount: Perceivable<BigDecimal>, currency: Currency): Transfer {
val c = Transfer(amount, currency, this, beneficiary)
contracts.add( c )
contracts.add(c)
return c
}
fun Party.may(init: ActionBuilder.() -> Unit) : Or {
fun Party.may(init: ActionBuilder.() -> Unit): Or {
val b = ActionBuilder(setOf(this))
b.init()
val c = Or(b.actions.toSet())
@ -43,7 +45,7 @@ open class ContractBuilder {
return c
}
fun Set<Party>.may(init: ActionBuilder.() -> Unit) : Or {
fun Set<Party>.may(init: ActionBuilder.() -> Unit): Or {
val b = ActionBuilder(this)
b.init()
val c = Or(b.actions.toSet())
@ -51,32 +53,48 @@ open class ContractBuilder {
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)
@Deprecated(level = DeprecationLevel.ERROR, message = "Not allowed")
fun Action(@Suppress("UNUSED_PARAMETER") name: String, @Suppress("UNUSED_PARAMETER") condition: Perceivable<Boolean>,
@Suppress("UNUSED_PARAMETER") actors: Set<Party>, @Suppress("UNUSED_PARAMETER") arrangement: Arrangement) {}
@Suppress("UNUSED_PARAMETER") actors: Set<Party>, @Suppress("UNUSED_PARAMETER") arrangement: Arrangement) {
}
@Deprecated(level = DeprecationLevel.ERROR, message = "Not available")
fun<T> String.anytime(@Suppress("UNUSED_PARAMETER") ignore: T ) {}
fun <T> String.anytime(@Suppress("UNUSED_PARAMETER") ignore: T) {
}
@Deprecated(level = DeprecationLevel.ERROR, message = "Not available")
fun<T> String.givenThat(@Suppress("UNUSED_PARAMETER") ignore: T ) {}
fun <T> String.givenThat(@Suppress("UNUSED_PARAMETER") ignore: T) {
}
@Deprecated(level = DeprecationLevel.ERROR, message = "Not available")
fun<T> String.givenThat(@Suppress("UNUSED_PARAMETER") ignore1: T, @Suppress("UNUSED_PARAMETER") ignore2: T ) {}
fun <T> String.givenThat(@Suppress("UNUSED_PARAMETER") ignore1: T, @Suppress("UNUSED_PARAMETER") ignore2: T) {
}
/* fun Party.gives(beneficiary: Party, amount: Perceivable<Long>, currency: Currency) {
contracts.add( Transfer(amount, currency, this, beneficiary))
}*/
/* fun Party.gives(beneficiary: Party, amount: Perceivable<Long>, currency: Currency) {
contracts.add( Transfer(amount, currency, this, beneficiary))
}*/
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) )
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) )
fun rollOut(startDate: String, endDate: String, frequency: Frequency, init: RollOutBuilder<Dummy>.() -> Unit) : RollOut {
fun rollOut(startDate: LocalDate, endDate: LocalDate, frequency: Frequency, init: RollOutBuilder<Dummy>.() -> Unit): RollOut {
val b = RollOutBuilder(startDate, endDate, frequency, Dummy())
b.init()
val c = b.final()
@ -84,7 +102,7 @@ open class ContractBuilder {
return c
}
fun <T> rollOut(startDate: String, endDate: String, frequency: Frequency, vars: T, init: RollOutBuilder<T>.() -> Unit) : RollOut {
fun <T> rollOut(startDate: LocalDate, endDate: LocalDate, frequency: Frequency, vars: T, init: RollOutBuilder<T>.() -> Unit): RollOut {
val b = RollOutBuilder(startDate, endDate, frequency, vars)
b.init()
val c = b.final()
@ -92,6 +110,8 @@ open class ContractBuilder {
return c
}
val String.ld: LocalDate get() = BusinessCalendar.parseDateFromString(this)
open fun final() =
when (contracts.size) {
0 -> zero
@ -100,6 +120,7 @@ open class ContractBuilder {
}
}
interface GivenThatResolve {
fun resolve(contract: Arrangement)
}
@ -139,7 +160,7 @@ data class Parameter<T>(val initialValue: T) : Perceivable<T>
fun<T> variable(v: T) = Parameter<T>(v)
class RollOutBuilder<T>(val startDate: String, val endDate: String, val frequency: Frequency, val vars: T) : ContractBuilder() {
class RollOutBuilder<T>(val startDate: LocalDate, val endDate: LocalDate, val frequency: Frequency, val vars: T) : ContractBuilder() {
val start = StartDate()
val end = EndDate()

View File

@ -1,11 +1,13 @@
package com.r3corda.contracts.universal
import com.r3corda.core.contracts.FixOf
import com.r3corda.core.contracts.Frequency
import com.r3corda.core.contracts.Tenor
import com.r3corda.core.utilities.DUMMY_NOTARY
import com.r3corda.testing.transaction
import org.junit.Test
import java.time.Instant
import java.time.LocalDate
/**
* Created by sofusmortensen on 05/09/16.
@ -18,16 +20,19 @@ class Cap {
val notional = 50.M
val currency = EUR
val tradeDate: LocalDate = LocalDate.of(2016, 9, 1)
val contract = arrange {
rollOut("2016-04-01", "2017-04-01", Frequency.Quarterly) {
rollOut("2016-09-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)
val fixed = interest(notional, "act/365", 0.5.bd, start, end)
highStreetBank.gives(acmeCorp, floating - fixed, currency)
highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency)
next()
}
} or acmeCorp.may {
} or
acmeCorp.may {
"skip".anytime {
next()
}
@ -35,11 +40,53 @@ 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")
highStreetBank.gives(acmeCorp, (floating1 - fixed1).plus(), currency)
rollOut("2016-07-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)
val fixed = interest(notional, "act/365", 0.5.bd, start, end)
highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency)
next()
}
} or acmeCorp.may {
"skip".anytime {
next()
}
}
}
}
} or acmeCorp.may {
"skip".anytime {
rollOut("2016-07-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)
val fixed = interest(notional, "act/365", 0.5.bd, start, end)
highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency)
next()
}
} or acmeCorp.may {
"skip".anytime {
next()
}
}
}
}
}
}
val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract)
val stateFixed = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractFixed)
val contractTARN = arrange {
rollOut("2016-04-01", "2017-04-01", Frequency.Quarterly, object {
rollOut("2016-04-01".ld, "2017-04-01".ld, Frequency.Quarterly, object {
val limit = variable(150.K)
}) {
(acmeCorp or highStreetBank).may {
@ -77,4 +124,49 @@ class Cap {
}
}
@Test
fun `fixing`() {
transaction {
input { stateStart }
output { stateFixed }
timestamp(TEST_TX_TIME_1)
tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") }
this `fails with` "action must be defined"
}
tweak {
// wrong source
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("6M")), 1.0.bd))) }
this `fails with` "relevant fixing must be included"
}
tweak {
// wrong date
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("6M")), 1.0.bd))) }
this `fails with` "relevant fixing must be included"
}
tweak {
// wrong tenor
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))) }
this `fails with` "relevant fixing must be included"
}
tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", 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))) }
this.verifies()
}
}
}

View File

@ -18,7 +18,7 @@ class Caplet {
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
val dt: LocalDate = LocalDate.of(2016, 9, 1)
val tradeDate: LocalDate = LocalDate.of(2016, 9, 1)
val notional = 50.M
val currency = EUR
@ -26,7 +26,7 @@ class Caplet {
val contract = arrange {
(acmeCorp or highStreetBank).may {
"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", tradeDate, Tenor("6M")), "2016-04-01", "2016-10-01")
val fixed = interest(notional, "act/365", 0.5.bd, "2016-04-01", "2016-10-01")
highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency)
}
@ -102,32 +102,32 @@ class Caplet {
tweak {
// wrong source
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBORx", dt, Tenor("6M")), 1.0.bd))) }
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("6M")), 1.0.bd))) }
this `fails with` "relevant fixing must be included"
}
tweak {
// wrong date
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt.plusYears(1), Tenor("6M")), 1.0.bd))) }
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("6M")), 1.0.bd))) }
this `fails with` "relevant fixing must be included"
}
tweak {
// wrong tenor
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt, Tenor("3M")), 1.0.bd))) }
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))) }
this `fails with` "relevant fixing must be included"
}
tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", dt, Tenor("6M")), 1.5.bd))) }
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", dt, Tenor("6M")), 1.0.bd))) }
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.0.bd))) }
this.verifies()
}

View File

@ -0,0 +1,92 @@
package com.r3corda.contracts.universal
import com.r3corda.core.contracts.BusinessCalendar
import com.r3corda.core.contracts.Frequency
import com.r3corda.core.utilities.DUMMY_NOTARY
import com.r3corda.testing.transaction
import org.junit.Test
import java.time.Instant
/**
* Created by sofusmortensen on 08/09/16.
*/
class RollOutTests {
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
val contract = arrange {
rollOut("2016-09-01".ld, "2017-09-01".ld, Frequency.Monthly) {
(acmeCorp or highStreetBank).may {
"transfer".givenThat(after(end)) {
highStreetBank.gives(acmeCorp, 10.K, USD)
next()
}
}
}
}
val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract)
val contractStep1a = arrange {
rollOut("2016-12-01".ld, "2017-09-01".ld, Frequency.Monthly) {
(acmeCorp or highStreetBank).may {
"transfer".givenThat(after(end)) {
highStreetBank.gives(acmeCorp, 10.K, USD)
next()
}
}
}
}
val contractStep1b = arrange {
highStreetBank.gives(acmeCorp, 10.K, USD)
}
val stateStep1a = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractStep1a)
val stateStep1b = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractStep1b)
@Test
fun dateTests() {
val d1 = BusinessCalendar.parseDateFromString("2016-09-10")
}
@Test
fun issue() {
transaction {
output { stateStart }
timestamp(TEST_TX_TIME_1)
this `fails with` "transaction has a single command"
tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
this `fails with` "the transaction is signed by all liable parties"
}
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() }
this.verifies()
}
}
@Test
fun `execute`() {
transaction {
input { stateStart }
output { stateStep1a }
output { stateStep1b }
timestamp(TEST_TX_TIME_1)
tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") }
this `fails with` "action must be defined"
}
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") }
this.verifies()
}
}
}

View File

@ -45,7 +45,7 @@ class Swaption {
val elegant_contract = arrange {
rollOut("01/04/2015", "01/04/2025", Frequency.Quarterly) {
rollOut("01/04/2015".ld, "01/04/2025".ld, Frequency.Quarterly) {
(highStreetBank or acmeCorp).may {
"proceed".givenThat(after(start)) {
highStreetBank.gives(acmeCorp, libor(notional, start, end), currency)
@ -63,7 +63,7 @@ class Swaption {
val strike = 1.2
val tarf = arrange {
rollOut("01/04/2015", "01/04/2016", Frequency.Quarterly, object {
rollOut("01/04/2015".ld, "01/04/2016".ld, Frequency.Quarterly, object {
val cap = variable(150.K)
}) {
acmeCorp.may {
@ -86,7 +86,7 @@ class Swaption {
}
val tarf2 = arrange {
rollOut("01/04/2015", "01/04/2016", Frequency.Quarterly, object {
rollOut("01/04/2015".ld, "01/04/2016".ld, Frequency.Quarterly, object {
val uses = variable(4)
}) {
acmeCorp.may {