Sunday, March 2, 2008

Groovy : Currying closures

This is just a quick Groovy feature that I think is cool.
For my reporting page, I want to produce a whole bunch of stats based on the duration of my trades. What is my average net, %age, and R gains for my winners and losers? Am I making more money per day with my winners than with my losers (ie: am I cutting the crap short and holding my winners)? Each calculation is essentially the same, just with some minor permutations. The basic structure will be:

for each trade in a list
num days += trade.num days
currVal += trade.currVal
return currVal / num days

The problem is that I need to do this calculation for all of my winners, losers and breakeven trades, and for each list, I need to do several different calculations. This is hardly intractable, it just makes a messy loop with lots of temp variables.


Groovy lets you do a couple cool things. First, you can define a closure which is sort of like an anonymous inner class in Java, except without the class. It's really just a method pointer. So we can start by defining a closure to calculate the average dollar value:

def avgDollar = {ts -> ts?.sum{it.net()} / (ts ? ts.sum{it.duration()} : 1) }

The notation ts?.sum is groovy's way of checking for nulls so instead of writing:

double val = 0;
if( ts != null ) {
val = ts.sum();
}

you just say:

def val = ts?.sum()

Cool, no? Makes it easy to do the "right" thing and check for nulls without messing up your code. Anyway...


So now that we have our closure, we can say:

def avgDlrWins = avgDollar(winners)
def avgDlrBE = avgDollar(breakEvens)
def avgDlrLoss = avgDollar(losers)



That's a step in the right direction, but now we have to do the same mess when we try to average R's and %ages. So now we step up our game and do some meta-programming, defining a closure to pass to our closure!

def holdAvg = {clos, ts -> ts?.sum{clos.call(it)} / (ts ? ts?.sum{it.duration()} : 1) }

This closure takes two arguments, closure and a list of trades and sums the result of calling the closure and dividing that by the sum of the duration. That lets us create our sub closures as:

def avgDollar = holdAvg.curry({it.net()})
def avgR = holdAvg.curry({it.r()})
def avgPerc = holdAvg.curry({it.percent()})

The method "curry" binds the first argument of the holdAvg closure to the argument we pass in (in this case another closure). That means that the avgDollar() closure we just define is fully equivalent to the first one, only now we can quickly rattle off the avgR and avgPercent closure. Now that's code re-use!


If you haven't used closures before, this is probably a bit weird-looking and when I first read about curried closures, I couldn't see how they'd be used, but it's surprising how often they pop up when you know that the feature exists.


Good coding!

1 comments:

Vishal Sharma said...

you are blatantly copying my blog entries without my permission from entips.sharmvishl.com.
If you dont take them off i will have to soon send you an official notiiice.....