Hi Joe,
You didn't mention at which point you were in the process of learning / discovering F#, so i'll try to explain
everything step by step at the risk of stating the obvious.
A tree state is defined as a pair of spot price and option price, and the set of states at a given
time is represented as a sequence ordered by ascending spot price.
The algorithm consists of two parts,
(1) Initialization :
The terminal sequence is generated from the lowest possible spot price at time n (s0*down**n : n down moves), to the highest (s0*up**n : n up moves), by incrementally multiplying by up/down (undo a down move and do an up move instead).
This step is accomplished by Seq.unfold which is a very nice function allowing to build a sequence from a recurrence relation :
it takes as arguments a seed state and a function which, given a current state returns None to indicate termination or Some pair of a generated element and a new state. In order to obtain the terminal option prices as well, we just have to apply Seq.map to transform each spot to a pair of the spot and the associated payoff.
(2) Recursion :
Given the states at time k, compute the states at time (k-1) knowing that the new lowest state will be computed from the old lowest state and its successor and so on. Enter another nice function from the Seq module, pairwise, which produces the sequence of pairs (element, successor) we just need.
The remaining map simply implements the elementary backward computation step with a lambda whose argument is a pattern representing the pair of states ((spot down, option down), (spot up, option up)).
Note that the base sequence obtained at the end of the recursion is actually a kind of delayed computation which unfolds only when Seq.head is called.
Seq.unfold can be replaced with the more explicit :
[0 .. n]
|> Seq.map (fun i -> market.spot * (up ** float i) * (down ** float (n - i)))
Sequences can be replaced with lists altogether (change Seq to List and implement List.pairwise !!!).
Surprisingly enough the code performance is not too bad (?) when compared to the most imperative equivalent (one array + in place mutation + for loops) : american put with 500 steps => ~25ms (seq) ~20ms (list) ~5ms (horribly imperative).
Cheers, bubo**2.