I have been working on a pricing engine [PE] (a piece of software that calculates bond prices using different models) for the last year, written in Java 6.
A PE is essentially a DAG (Directed Acyclic Graph), and there are many ways to implement the relationships between the pricing nodes: remote nodes if you fancy distributed processes, matrix for the parent-child edges, or a pure OO approach where a pricing node has a direct reference to its parents.
An example of such a naive implementation would look like this:
public abstract class AbsNode {
protected List parents = new LinkedList<>();
protected double price, yield;
public abstract void calculate();
}
class FutureNode extends AbsNode {
@Override public void calculate() {
}
}
class GrossBasisNode extends AbsNode {
@Override public void calculate() {
price = parents.get(0).price * 0.98 + 1.0;
}
}
class BenNode extends AbsNode {
@Override public void calculate() {
yield = parents.get(0).yield * 3.14;
}
}
This obviously is only there to demonstrate the following issues: (1) The code cannot guaranty that the child of a future node must only be a ben node, or that the child of a ben node can only be a ben node, (2) that price and yield should only exist for bonds, and price only for futures (3) you can do pre-check at runtime using
instanceof but it is not very nice, (4) there are potential class cast exceptions, (5) there is no compile-time check.
Learning Scala, I was wondering if the very much debated type system could come to the rescue.
I came up with:
trait MyNode {
type parentType
var parents:List[parentType] = List()
var calcPrice = Double.NaN
def addParent(parent: parentType) = parents = parent :: parents
}
trait MyBond extends MyNode {
var calcYield = Double.NaN
}
trait MyFuture extends MyNode
sealed class GenFuture extends MyFuture
sealed class GenBenBond extends MyBond {
type parentType = MyBond
}
sealed class GenGbnBond extends MyBond {
type parentType = MyFuture
}
Notice the override of the type
parentType: in
GenBenBond I enforce the type to be a bond, whereas for the
GenGbnBond bond I enforce the type to be
MyFuture.
This s quite neat.
It allows me to write this:
val fut = new GenFuture
val gbn = new GenGbnBond
val ben = new GenBenBond
gbn.addParent(fut)
ben.addParent(gbn)
but this
ben.addParent(fut)
does not compile... exactly what I want.
The following amended code shows another good side-effect, the
parent in
calculate are of the right type:
trait MyNode {
type parentType
var parents:List[parentType] = List()
var calcPrice = Double.NaN
def addParent(parent: parentType) = parents = parent :: parents
def calculate = {}
}
trait MyBond extends MyNode {
var calcYield = Double.NaN
}
trait MyFuture extends MyNode
sealed class GenFuture extends MyFuture
sealed class GenBenBond extends MyBond {
type parentType = MyBond
override def calculate = {
val bond = parents(0)
bond.calcYield
bond.calcPrice
}
}
sealed class GenGbnBond extends MyBond {
type parentType = MyFuture
override def calculate = {
val fut = parents(0)
// only calcPrice available
fut.calcPrice
}
}
No comments:
Post a Comment