How to price options, FX and other financial contracts with Haskell
Table of Contents
- What is this post about
- Why do we need functional languages to describe financial contracts
- Pricing financial contracts
- Simple option example
- Pricing model
- More realistic example
- Summary and future work
What is this post about
Some time ago I heard that several banks and hedge-funds use functional languages such as Haskell, OCaml, and F# in their projects. I had a raw idea of benefits such languages can provide:
- Strong static types;
- An easy way to create DSL for the financial domain;
- Pure functions (no side effects);
Recently, I found exciting papers of Simon Peyton Jones (an outstanding person in Haskell-community and software development industry in general), J-M. Eber and Julian Seward — Composing Contracts: An Adventure in Financial Engineering (later I refer to the paper as CC) and it’s 2nd version “How to write a financial contract”. Both articles discuss the pricing of different financial derivatives using a common framework implemented in Haskell. These works are quite famous and have a lot of citations by papers related to financial contracts (e.g. Using the functional language Idris to describe smart contracts on the Ethereum platform, Pricing composable contracts on the GP-GPU).
Once I started to read the paper, I was curious how accurate results can it provide? Even though the article contains some parts of source code (and often cited), I didn’t find any examples of pricing financial contracts by using this framework (apart from vanilla bond pricing examples). It motivated me to implement the code missing in the paper and to check the proposed approach on realistic and non-trivial financial instruments.
Therefore in this post, I first briefly describe the approach from CC paper and then show the possible implementation of pricing of equity options.
Why do we need functional languages to describe financial contracts
The main idea of CC paper is to create DSL for describing and pricing financial contracts, and this DSL consists of a small number of combinators (around 10), which can be used to describe any contract.
For instance, we have a contract to receive 100$ on 1st Jan 2024:
The code is written in Haskell and quite easy to understand. zcb stands for “zero coupon bond” and means one-time payment in the future. zcb is an example of high-level combinator which is implemented using low-level combinators.
At the first glance zcb implementation looked more complicated, but we can mentally split it into two parts:
- when? - cWhen (at t); #t is future date
- how much? - (scale (konst x) (one k)); #k is currency and x is amount
Below is a list of low-level combinators:
Observations
Most non-trivial contract payoffs depend on some events (e.g. stock price, LIBOR rate, FX rate, etc.). Let’s say we need to price an option to buy stock in 1 year for 100$. If we knew the price of the stock in 1 year, it would be a trivial task. Of course, we can’t see the future. However, we can model the price. First, all unknown events, on which our contract depends, we call as observations. Next, we can define new combinators to work with observations. These combinators complete our list of contract combinators:
Below is an example of the contract which payoff depends on the difference between LIBOR and a fixed rate of 3%:
scale (lift2 (*) (lift2 (-) libor (konst 3)) (konst 1000)) (one USD)
The lift operation applies numeric operations to observations. As we see later Haskell allows us to get rid of explicit lift2 calls.
Simple option
Option contracts could be constructed with next low-level or high-level combinators:
Example of European option for fixed coupon bond:
Pricing financial contracts
So far we used combinators/DSL to describe contracts. Describing is a very important use-case as it is quite easy to build graphic representation of complex contracts with many payoffs for example. However, we are interested in pricing contracts or, in other words, we want to understand how much we are willing to pay for it today.
There are lots of different models which are used to price different financial instruments. But only three groups of numerical methods are widely used:
- Partial differential equations;
- Monte Carlo;
- Lattice methods;
Semantics, which we used to define contracts, can be valued with any model. Technically, we can use different models for different parts of the contract. In our examples, we will use the lattice method as it is quite easy to visualize and understand.
Value processes
If contract payoff depends on a particular stock price one year from now, we can model the future price as a random variable. Our goal is to translate the contract into the value process. In this process, we work with random variables. These variables can take a finite number of values.
A value process, p, over type a, is a (total) function of time to a random variable of type a. The random variable p(t) describes the possible values for p at time t.
PR a = Date -> RV a
Both contracts and observations are modeled as processes.
Simple option example
To get a better understanding let’s discuss an example of European option price for a stock. Option strike price is 32$ and expires in 3 months, the current stock price is 30$, the interest rate is 0%, and every month price can move only in two directions: either increase by 10% or decrease by 5%.
I noticed that the assumption that stock can move only in two directions looks oversimplified. But this approach is used by all binomial pricing models and is theoretically proved. The critical moment is how we can calculate up move and down move? We discuss this below using expected price and volatility.
First, let’s calculate a binomial tree of possible stock prices for 3 months.
Stock price we calculate from the left (today) to the right (3 months from now).
As can be seen, we got four possible outcomes for the stock price in 3 months.
Next, we can calculate option value for each of 4 outcomes using next formula:
max(StockPrice-Strike,0)
E.g. max(39.93–32,0)=7.93.
Finally, we can go to the left keeping in mind that the probability of each path is 50%. So, we can calculate the option price for today (1.923125).
In this example we set the interest rate equals 0. In the real world, this is usually not true, and we need to discount the value at every node as we move from right to left.
Pricing model
In the example above we saw how to calculate the price of the option. We can use a similar tree to model interest rates or fx-rates and price massive variety of contracts.
But first, we should define an abstract model. Below are model primitives:
More realistic example
When I first saw examples in the CC article I thought that they are oversimplified. It motivated me to find other articles discussing this paper, but samples were still very basic (e.g. vanilla bond pricing). Therefore, I decided to check the model on more realistic examples of options pricing. One possible way to make sure that my calculations are right is to take an example from well-known books such as Paul Wilmott on quantitative finance, Paul Wilmott introduces quantitative finance and Options, Futures, and Other Derivatives by John Hull.
Here is a stock option pricing example from Paul Wilmott introduces quantitative finance (page 80):
S=100, dT=1/12, r=0.1, Sigma=0.2; using continuous compounding
params given in the book u=1.0604,v=0.9431, p'=0.5567
First, I generated an expected stock price tree (lattice) as described in the book. After running the code first time, my result was looked realistic, but quite far from what was described in the book (book 6.13 vs my 4.62).
In his book, Paul Wilmott describes options pricing lattice similar to lattice in CC paper. However, there is a big difference. Both Paul Wilmott and John Hull use risk-neutral probability to find required expected up move (u) and expected down move (v). In the book’s example probability of up move is p`=0.5567 (and the probability of down move is 1-p`). On the other hand, CC paper ignores the risk-neutral probability (both up and down move probability is 0.5). As a result, the book’s price at period n is :
P(n)=P(n-1) * u * p`+ P(n-1) * v * (1-p`);
As mentioned, in CC paper p` is always 0.5, but, in general, it is not the case in most risk-neutral binomial models (e.g. Cox-Ross-Rubinstein, Leisen-Reimer).
One possible solution is to embed information about p` in the lattice (observation) as an extra parameter and use it inside modified pricer/model when we discount option’s value from future back to today. However, this adds product-specific logic(options) to our pricer/model.
Another solution is to generate an expected stock price tree in the way which works with CC paper implementation (when p` is 0.5). For this purpose the Jarrow-Rudd model can be used:
p = 1/2u = exp(r−σ²/2)Δt+σΔ√tv = exp(r−σ²/2)Δt−σΔ√t
Once p` was set up to 0.5, we need to adjust u (expected up move) and v (expected down move).
Some people may wonder, why are we able to select the probability and how are p`, u and v related to each other? The answer is that in all models, we discussed, asset price is modelled using the log-normal distribution. We have two equations: the expected price value and the price variance with the log-normal distribution, but with three variables. Since we have more variables than equations, we have a set of possible relations between these variables. Here is a short video about the binomial pricing model.
New implementation gives a better result which is close to the “book result”, but not the same (book 6.13 vs J-R 6.45). The reason is that both lattice pricing algorithms are approximations. Therefore, when we use more common in the finance industry Black Scholes model (BSM) to price our option, the price is 6.30. It is clear that BSM result is in the middle between book’s and J-R results. Therefore, BSM algorithm represents a lattice algorithm but the number of periods is increasing to infinity.
Once we increase the number of periods to 100 for both algorithms (book and J-R), we observe results which are closer to BSM (6.35 vs 6.36).
Summary and future work
Composing Contracts paper is a must-read for those who use functional languages to price financial instruments. It may also be useful for people who develop pricing applications in general. Although the paper doesn’t describe a lot of details required for full implementation and makes some oversimplified assumptions, it allows to get a correct price for simple instruments (e.g. some types of bonds) and get a price close to BSM price for options.
According to authors, the DSL can be elegantly extended with other numerical methods (Monte Carlo and partial differential equations). Checking if this is simple to do is a topic for a separate post.
Source code: https://github.com/AlexeyPirogov/PricingFinancialContractsWithHaskell
References:
- S.L. Peyton Jones, J-M. Eber and Julian Seward 2000. Composing Contracts: An Adventure in Financial Engineering
https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/contracts-icfp.pdf; - S.L. Peyton Jones and J-M. Eber. How to write a financial contract
http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=E0599E40BCD89C591594C5FD678E275F?doi=10.1.1.14.7885&rep=rep1&type=pdf - Joakim Ahnfelt-Rønne and Michael Flænø Werk. Pricing composable contracts on the GP-GPU
https://di.ku.dk/forskning/Publikationer/specialer/2011/report.pdf