Hi Dave,
the ref keyword and "!w" are used for creating mutable variables, because by default in F# you can't assign a new value to already declared variable (that's why in F# variables are usually called values and not variables).

In the example you posted, the ref keyword is used when assigning value to the 'w' variable, which means that the type of 'w' isn't float but float ref, which is a mutable value and therefore it is possible to assign it a new value using "w := 1.0 ...". For reading value of mutable variables you need the '!' operaor, so "!w" just reads value of 'w' ('!' applied to variable of type 'a ref returns value of type 'a).

In this example the mutable state isn't necessary, so I'd recommend rewriting it like this:

1
2
3
4
5
6
7
8
9
10
11
12
 
let pow x n = exp ( (float n) * log(x) ) ;;
    
let cnd x =
    let a1 = 0.31938153 and a2= -0.356563782 and a3=1.781477937 and a4= -1.821255978 and a5=1.330274429 in
    let pi = 4.0 * atan 1.0 in
    let l  = abs_float(x) in
    let k  = 1.0 / (1.0 + 0.2316419 * l) in
    let w  = (1.0 - 1.0/sqrt(2.0*pi)*exp(-l*l/2.0)*(a1*k+a2*k*k+a3*(pow k 3)+a4*(pow k 4)+a5*(pow k 5))) in

    // Declare new value 'w' and assign it 1.0 - original w or original w depending on the 'x' value
    let w = if (x < 0.0) then 1.0 - w else w;;

PS: You might want to check out drafts of a few chapters from Don Syme's book "Expert F#" here. Mutable state is explained in chapter 4.

By on 4/29/2007 6:22 AM ()

Hi Tomas

Thanks, I follow it now.

Regards

Dave

By on 4/30/2007 3:15 AM ()

Hi Again,

I'm having some trouble implementing cnd(-d2), F# appears to interpret -d2 as float -> int when I use

cnd -d2

How do i negate a float?

Dave

By on 5/6/2007 12:00 AM ()

In your code, is your call to cnd(-d2) written as <cnd(-d2)> or <cnd -d2>?

If it's the latter, try with the former.

Did you face the same problem when pricing the call (and using cnd d2 instead of cnd(-d2))?

julien

By on 5/6/2007 2:30 AM ()

Hi Julien,

Thanks, the brackets seem to have done the trick, the version below works. Could have sworn I tried it that way [:)].

1
2
3
4
5
6
7
8
9
let black76 f x t r v option_type =
  let denom =  v * sqrt(t) 
  let d1 = (log(f / x) + (0.5 * (v * v) * t)) / denom
  let d2 = d1 - denom in

  match option_type with
       | "call" -> exp(-r*t) * (f * cnd d1 - x * cnd d2)
       | "put" ->  exp(-r*t) * (x * cnd(-d2) - f * cnd(-d1))
       | _ -> failwith "BS is only for calls and puts!" ;;

Cheers

Dave

By on 5/7/2007 12:47 AM ()

Hi again,

Now that I've got the basic working, I'm wondering if there's some way of avoiding re-evaluating expressions like d1 and d2 above, i.e. there are a series of associated equations which represent the derivatives of the above formula wrt various inputs arguments which are called "greeks" ([link:www.riskglossary.com]), i.e :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let black76 f x t r v option_type =
  let denom =  v * sqrt(t) 
  let d1 = (log(f / x) + (0.5 * (v * v) * t)) / denom
  let d2 = d1 - denom in

  match option_type with
       | "call" -> exp(-r*t) * (f * cnd d1 - x * cnd d2)
       | "put" ->  exp(-r*t) * (x * cnd(-d2) - f * cnd(-d1))
       | _ -> failwith "BS is only for calls and puts!" ;;

let black76delta f x t r v option_type =
  let denom =  v * sqrt(t) 
  let d1 = (log(f / x) + (0.5 * (v * v) * t)) / denom
  let d2 = d1 - denom in

  match option_type with
       | "call" -> exp(-r*t) * (cnd d1)
       | "put" ->  exp(-r*t) * (cnd d1 - 1)
       | _ -> failwith "BS is only for calls and puts!" ;;

Is there any way I can share the common d1/d2 etc so it only gets evaluated once for any given set of arguments? I've read about function memoization but cannot find any specific references to F#? Even better, I'd love to be able to extend this to a portfolio valuation problem where the portfolio could contain multiples of the same option, i.e. avoid repetitive calls to the black76 equation itself?

Regards

Dave

By on 5/9/2007 12:48 AM ()

Hi Dave,

Here's one solution which creates a closure and returns a pair of specialized functions to capture the common parts of the calculation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
type Black76OptionType = BlackCall | BlackPut

let generateBlack76 f x t v =
  let denom =  v * sqrt(t)
  let d1 = (log(f / x) + (0.5 * (v * v) * t)) / denom
  let d2 = d1 - denom in

  let black76 r option_type =
    match option_type with
    | BlackCall -> exp(-r*t) * (f * cnd d1 - x * cnd d2)
    | BlackPut ->  exp(-r*t) * (x * cnd(-d2) - f * cnd(-d1))
  in

  let black76delta r option_type =
    match option_type with
    | BlackCall -> exp(-r*t) * (cnd d1)
    | BlackPut ->  exp(-r*t) * (cnd d1 - 1)
  in
  
  black76, black76delta

let black76, black76delta = generateBlack76 1.0 1.0 1.0 1.0 in
let a = black76 1.0 BlackCall in
let b = black76delta 1.0 BlackPut in

...

Cheers,

Andy

By on 5/9/2007 1:58 AM ()

Here's a post from Don Syme on a Fibonacci programming thread with the same programming issue . It was posted on the OCaml weekly news mailing list (please delete this post if there's any kind of problem with that).

==================

Don Syme said:
You may be interested in the approach to this kind of problem discussed
in [link:dx.doi.org] (see also tech report

at [link:research.microsoft.com]).

Under that approach you get to write the code in a natural way as shown
below: fib_mem is defined recursively, but the "cache" function has the
natural "(a -> b) -> (a -> b)" type and is abstract and reusable (no
details as to the nature of the internal table are revealed).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let cache f = 
  let table = ref [] in 
  fun n -> 
     try 
       List.assoc n !table 
     with Not_found -> 
       let f_n = f n in 
          table := (n, f_n) :: !table; 
          f_n 

let rec fib_mem = 
   cache (function 
            | 0 -> 0 
            | 1 -> 1 
            | n -> fib_mem (n - 1) + fib_mem (n - 2)) 

The use of a computation on the right of a "let rec" is allowed by
systematically introducing initialization holes using lazy values and
forces. There are disadvantages to this approach, as it introduces a
potential for initialization unsoundness somewhat similar to those in
most designs and implementations of recursive modules. However the
paper argues that in the balance it is not unreasonable for a strict
language to accept this in order to gain modularity and localize the
potential for unsoundness. It is even more compelling when often
working with abstract APIs such as Java and .NET GUI libraries.
While this isn't OCaml, and may not ever be the right design for OCaml,
I've found it a useful technique to know even when doing C#, C++ and
OCaml programming, as a broad range of recursion puzzles can be
addressed by modelling the problem the "natural" way (e.g. more like
Haskell) and then using a translation that introduces initialization
holes systematically. The translation of your sample into OCaml using
"lazy" initialization holes is shown below (for single recursion you can
also just use a "option ref"). Note "cache" does not change,
maintaining the property that the caching function is abstract and
reusable.

1
2
3
4
5
6
7
8
9
let (!!) x = Lazy.force x 

let rec fib_mem' = lazy 
   cache (function 
            | 0 -> 0 
            | 1 -> 1 
            | n -> !!fib_mem' (n - 1) + !!fib_mem' (n - 2)) 

let fib_mem = !!fib_mem' 

FWIW it is well known that laziness can be used in essentially this way,
e.g. see Michel Mauny's early papers on laziness in OCaml. However I've
not seen a paper that argues the case for making this the default
interpretation of "let rec" in a strict language.

By on 5/9/2007 5:59 AM ()

Thanks for all the help everyone, I've got over the initial frustration of learning a new language \ way of thinking and am really enjoying this way of coding. As a quantative developer I'm find this particularly rewarding

Cheers

Dave

By on 5/10/2007 6:08 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper