Appendix A — OCaml Quick Reference for Finance

A compact reference for OCaml syntax and idioms used throughout this book. All examples assume open Core unless otherwise noted.


A.1 Basic Types and Values

(* Primitive types *)
let x : int    = 42
let y : float  = 3.14
let s : string = "hello"
let b : bool   = true

(* Float arithmetic — note the dot suffix *)
let sum = 1.0 +. 2.0    (* not +  *)
let prd = 2.0 *. 3.0    (* not *  *)
let quo = 10.0 /. 3.0   (* not /  *)
let neg = Float.neg 1.0

(* Int to float *)
let n = Float.of_int 5
let m = Int.of_float 3.7  (* truncates to 3 *)

(* Option type *)
let maybe_price : float option = Some 100.0
let no_price    : float option = None

(* Result type *)
let ok_val  : (float, string) result = Ok 42.0
let err_val : (float, string) result = Error "Invalid input"

A.2 Functions

(* Basic function *)
let square x = x *. x

(* Multiple arguments *)
let present_value rate periods fv =
  fv /. (1.0 +. rate) ** periods

(* Labelled arguments *)
let black_scholes_call ~spot ~strike ~rate ~vol ~time =
  ignore (spot, strike, rate, vol, time); 0.0 (* placeholder *)

(* Calling with labels (order independent) *)
let price = black_scholes_call ~vol:0.2 ~spot:100.0
              ~strike:100.0 ~rate:0.05 ~time:1.0

(* Optional arguments with defaults *)
let round_trip ?(decimals = 2) x =
  let factor = 10.0 ** Float.of_int decimals in
  Float.round (x *. factor) /. factor

(* Anonymous functions *)
let double = fun x -> x *. 2.0
let npv rates = List.map rates ~f:(fun r -> 1.0 /. (1.0 +. r))

(* Pipe operator *)
let result =
  [1.0; 2.0; 3.0]
  |> List.map ~f:(fun x -> x *. 2.0)
  |> List.fold ~init:0.0 ~f:(+.)

A.3 Pattern Matching

(* Match on variants *)
type instrument =
  | Equity of { ticker: string; shares: float }
  | Bond   of { face: float; coupon: float; maturity: float }
  | Option of { kind: [`Call | `Put]; strike: float }

let describe = function
  | Equity { ticker; _ } -> Printf.sprintf "Equity: %s" ticker
  | Bond   { coupon; maturity; _ } ->
      Printf.sprintf "Bond %.1f%% %.1fY" (coupon *. 100.0) maturity
  | Option { kind = `Call; strike } ->
      Printf.sprintf "Call K=%.2f" strike
  | Option { kind = `Put; strike } ->
      Printf.sprintf "Put K=%.2f" strike

(* Matching on option *)
let safe_div a b =
  match b with
  | 0.0 -> None
  | _   -> Some (a /. b)

(* Matching on result *)
let handle_result = function
  | Ok price  -> Printf.printf "Price: %.4f\n" price
  | Error msg -> Printf.eprintf "Error: %s\n" msg

(* Guard clauses *)
let classify_moneyness spot strike =
  match spot /. strike with
  | r when r > 1.02 -> "in-the-money"
  | r when r < 0.98 -> "out-of-the-money"
  | _               -> "at-the-money"

A.4 Records

(* Define *)
type market_data = {
  spot     : float;
  vol      : float;
  rate     : float;
  dividend : float;
}

(* Create *)
let mkt = { spot = 100.0; vol = 0.20; rate = 0.05; dividend = 0.02 }

(* Access *)
let s = mkt.spot

(* Update (functional — creates new record) *)
let stressed_mkt = { mkt with vol = mkt.vol *. 1.5 }

(* Destructuring in function arguments *)
let log_forward { spot; rate; dividend; _ } time =
  spot *. exp ((rate -. dividend) *. time)

A.5 Lists and Arrays

(* Lists — immutable, linked *)
let rates  = [0.01; 0.02; 0.03; 0.04; 0.05]
let prices = 100.0 :: 101.0 :: []      (* same as [100.0; 101.0] *)

(* Common list operations *)
let n     = List.length rates
let total = List.fold rates ~init:0.0 ~f:(+.)
let mean  = total /. Float.of_int n
let above = List.filter rates ~f:(fun r -> r > 0.02)
let dfs   = List.map rates ~f:(fun r -> 1.0 /. (1.0 +. r))

(* Head/tail pattern *)
let rec sum_list = function
  | []      -> 0.0
  | x :: xs -> x +. sum_list xs

(* Arrays — mutable, O(1) indexed *)
let arr   = Array.make 100 0.0
let arr2  = Array.init 10 (fun i -> Float.of_int i *. 0.1)
arr.(0) <- 42.0                         (* mutation *)
let v    = arr.(0)                       (* access     *)
let len  = Array.length arr

(* Array mapping/folding *)
let log_returns prices =
  Array.init (Array.length prices - 1) (fun i ->
    log (prices.(i + 1) /. prices.(i)))

A.6 Modules

(* Define a module *)
module Black_scholes = struct
  type inputs = { spot: float; strike: float; rate: float;
                  vol: float; time: float }

  let norm_cdf x = 0.5 *. (1.0 +. Float.erf (x /. sqrt 2.0))

  let d1 { spot; strike; rate; vol; time } =
    (log (spot /. strike) +. (rate +. 0.5 *. vol *. vol) *. time)
    /. (vol *. sqrt time)

  let d2 inp = d1 inp -. inp.vol *. sqrt inp.time

  let call inp =
    let d1v = d1 inp and d2v = d2 inp in
    let pv_k = inp.strike *. exp (-. inp.rate *. inp.time) in
    inp.spot *. norm_cdf d1v -. pv_k *. norm_cdf d2v
end

(* Open locally *)
let price =
  let open Black_scholes in
  call { spot = 100.0; strike = 100.0; rate = 0.05; vol = 0.20; time = 1.0 }

(* Module signatures *)
module type PRICER = sig
  type t
  val price : t -> float
  val delta : t -> float
end

(* Functor: module parameterised by another module *)
module Make_portfolio (P : PRICER) = struct
  let portfolio_delta positions =
    List.fold positions ~init:0.0
      ~f:(fun acc p -> acc +. P.delta p)
end

A.7 Error Handling Patterns

(* Using Result *)
let safe_log x =
  if x <= 0.0 then Error (Printf.sprintf "log of non-positive: %f" x)
  else Ok (log x)

(* Chaining with bind *)
let price_log_contract spot strike =
  Result.bind (safe_log spot)   ~f:(fun ls ->
  Result.bind (safe_log strike) ~f:(fun lk ->
  Ok (ls -. lk)))

(* Converting to exception when appropriate *)
let price_exn spot strike =
  match price_log_contract spot strike with
  | Ok v    -> v
  | Error e -> failwith e

(* Option handling *)
let price_or_zero book ticker =
  Option.value (Map.find book ticker) ~default:0.0

A.8 Common Numerical Idioms

(* Normal CDF approximation *)
let norm_cdf x =
  0.5 *. (1.0 +. Float.erf (x /. sqrt 2.0))

(* Normal PDF *)
let norm_pdf x =
  exp (-0.5 *. x *. x) /. sqrt (2.0 *. Float.pi)

(* Newton-Raphson root-finding *)
let newton_raphson ~f ~f' ~x0 ?(tol = 1e-8) ?(max_iter = 100) () =
  let rec go x n =
    if n >= max_iter then Error "Newton: max iterations exceeded"
    else
      let fx = f x in
      if Float.abs fx < tol then Ok x
      else
        let fpx = f' x in
        if Float.abs fpx < 1e-15 then Error "Newton: zero derivative"
        else go (x -. fx /. fpx) (n + 1)
  in
  go x0 0

(* Kahan summation for numerical stability *)
let kahan_sum arr =
  let sum = ref 0.0 and c = ref 0.0 in
  Array.iter arr ~f:(fun x ->
    let y = x -. !c in
    let t = !sum +. y in
    c := (t -. !sum) -. y;
    sum := t);
  !sum

(* Linspace *)
let linspace a b n =
  Array.init n (fun i ->
    a +. (b -. a) *. Float.of_int i /. Float.of_int (n - 1))

A.9 Owl Quick Reference

open Owl

(* Matrices *)
let a  = Mat.of_array [| 1.0; 2.0; 3.0; 4.0 |] 2 2
let b  = Mat.eye 3
let c  = Mat.dot a (Mat.transpose a)      (* A * A^T *)
let l  = Linalg.D.chol a                  (* lower-triangular Cholesky *)

(* Statistical functions *)
let mu   = Arr.mean arr
let sd   = Arr.std  arr ~ntype:STD_D
let corr = Arr.pearsonr x y

(* Normal distribution *)
let cdf_val = Stats.gaussian_cdf ~mu:0.0 ~sigma:1.0 1.645
let ppf_val = Stats.gaussian_ppf ~mu:0.0 ~sigma:1.0 0.99
let pdf_val = Stats.gaussian_pdf ~mu:0.0 ~sigma:1.0 0.0

(* Sampling *)
let z  = Stats.gaussian_rvs ~mu:0.0 ~sigma:1.0 ()
let zs = Array.init 10000 (fun _ -> Stats.gaussian_rvs ~mu:0.0 ~sigma:1.0 ())

A.10 Core Standard Library

open Core

(* Date handling *)
let today = Date.today ~zone:Time.Zone.utc
let date  = Date.of_string "2025-01-15"
let diff  = Date.diff date today           (* days, signed *)
let next  = Date.add_months date 1

(* Maps *)
let m  = Map.Poly.empty
let m' = Map.set m ~key:"AAPL" ~data:150.0
let v  = Map.find m' "AAPL"               (* float option *)
let v2 = Map.find_exn m' "AAPL"          (* raises if absent *)

(* String operations *)
let parts  = String.split "AAPL,100.0,0.5" ~on:','
let joined = String.concat ~sep:"," ["a"; "b"; "c"]

A.11 Finance-Domain Type Aliases

(* Self-documenting type aliases *)
type price_t    = float
type rate_t     = float   (* annualised *)
type vol_t      = float   (* annualised *)
type time_t     = float   (* in years *)
type notional_t = float

(* Phantom currency types for compile-time safety *)
type usd
type eur

type 'ccy amount = Amount of float

let usd_val : usd amount = Amount 1_000_000.0
let eur_val : eur amount = Amount 850_000.0

(* add_amounts : 'a amount -> 'a amount -> 'a amount
   Prevents adding USD to EUR at compile time *)
let add_amounts (Amount a) (Amount b) = Amount (a +. b)

See Chapter 2 for full coverage of the OCaml type system, module system, and functional programming patterns.