Trees are a great way to visualize different states of a process when measuring uncertainty and making predictions. While trees are harder to implement in SQL due to the relational nature of the language, they are powerful tools in finance, for instance, to value derivatives.

In this blog, we discuss how to set up a discrete binomial tree using the binomial options pricing model to value derivatives in Postgres SQL and apply this in an example of pricing a 3-period call option.

Setting Up A Binomial Tree

To create a tree in Postgres, we utilize the generate_series() function. Since the state of a process, which we denote here as a node, can be identified by the depth and width position of the node, we first generate a 4×7 lattice. We then truncate nodes that do not belong to the binomial tree.

  a as ( 
    select *
      generate_series(0, 3, 1)
  , b as (
    select *
      generate_series(-3, 3, 1)
  , tree as (
    select a.generate_series as t
      , b.generate_series as branch
    from a, b
      case when mod(a.generate_series, 2) = 1
        then @ b.generate_series <= a.generate_series
          and mod(@ b.generate_series, 2) = 1
        else @ b.generate_series <= a.generate_series
          and mod(@ b.generate_series, 2) = 0
Price chart

In the above CTEs, the tree table skims off unnecessary nodes that are outside of the symmetrical binomial tree through the where clause.

Binomial Options Pricing

An option is a derivative whose value is derived by the price of the underlying stock. A call option is a contract that gives the option holder the right to purchase the stock at an agreed strike price.

Since the value of the option can change depending on the state of the stock, its value is perfect for modeling out via the branches in a tree.

We will now walk through how to value a 3-period European call option whose underlying stock price is $83, has a strike price of $85, and in each period can move up by a factor of 1.1 or down by a factor of 0.9.

With the 3-period tree, we can build out the option pricing backwards iteratively. First, we need to find the option payout max[(S_3-K,0] at each of the final nodes (t = 3).

, final as (
    select *
      , case when t = 3 
            and 83 * 1.1^(t-(t-branch)/2)
              *0.9^((t-branch)/2) > 85
            then 83 * 1.1^(t-(t-branch)/2)
              *0.9^((t-branch)/2) - 85
          when t = 3
            then 0
          else null 
        end as final_val
    from tree
    where t = 3
Binomial tree

To work backwards iteratively for valuing options, we use the following formula for each step backward:


In order to calculate the risk-neutral value at t = 0, we have to take into account not only the expected value at each node, but also the probability of traversing through the node. Thus, to calculate the contribution of the final payout to the expected value of the option at each node, we use the following formula:

Formula 2

Encoding all of this into SQL, we get:

  , lt.branch
  , rt.t as final_t
  , rt.branch as final_branch
  , final_val
  , (1/exp(0.05)^(rt.t-lt.t))*(((rt.t-lt.t)!)/((((rt.t-lt.t)
      ^(((rt.t-lt.t)-(rt.branch-lt.branch))/2) as disc_val 
  tree lt, tree rt 
join final on rt.branch = final.branch 
  and rt.t = final.t
  lt.t <= rt.t
  and @(rt.branch - lt.branch) <= @(rt.t - lt.t)
  and rt.t = 3
order by branch desc
Call option tree

One handy tool that Sisense for Cloud Data Teams offers is the Cohort Grid, which aggregates these expected values by node. Here’s what it looks like in tree form:

Call option pricing

Now you can price your options through SQL!