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.