In the previous post, we created a binomial probability mass function (pmf). We can use that to easily evaluate European-style options:

EBM:{[P;S;K;T;r;b;v;n] / European Binomial Model (CRR) t:T%n; / time interval u:exp v*sqrt t; / up d:1%u; / down p:(exp[b*t]-d)%(u-d); / probability of up ns:til n+1; / 0, 1, 2, ..., n us:u xexp ns; / u**0, u**1, ... ds:d xexp ns; / d**0, d**1, ... Ss:S*ds*reverse us; / prices at tree leaves ps:pmf[n;p]; / probabilities at tree leaves exp[neg r*T]*sum P[Ss;K]*ps }

Note that P is the payoff, S is the current price, K is the strike price, T is the time to maturity, r is the risk-free rate, v is the volatility, b is the cost of carry and n is the depth of the binomial tree. The Python version using NumPy and SciPy actually looks quite similar:

def EuropeanBinomialModel(P, S, K, T, r, b, v, n): n = int(n) t = float(T)/n # time interval u = np.exp(v * np.sqrt(t)) # up d = 1/u # down p = (np.exp(b*t)-d)/(u-d) # probability of up ns = np.arange(0, n+1, 1) # 0, 1, 2, ..., n us = u**ns # u**0, u**1, ... ds = d**ns # d**0, d**1, ... Ss = S*us*ds[::-1] # prices at leaves ps = binom_pmf(ns, n, p) # probabilities at leaves return np.exp(-r*T) * np.sum(P(Ss,K) * ps)

As we can see, both code has no explicit loops. This is possible in Python as NumPy and SciPy are array-oriented. NumPy and SciPy’s idea of “broadcasting” has some similarity with k/q’s concept of “atomic functions” (definition: *a function f of any number of arguments is atomic if f is identical to f’*).