comp_fin_lab is a modular Python package for computational finance, derivative pricing, and stochastic modelling.
The project started from verified computational finance implementations and refactors them into a reusable Python package. The goal is to preserve mathematically checked course implementations while making them usable as importable modules in future research, modelling, and portfolio projects.
The package currently includes:
- Vanilla option payoffs
- Black-Scholes European call and put pricing
- Cox-Ross-Rubinstein binomial tree pricing
- CRR lattice construction for diagnostics and visualisation
- Down-and-out barrier call pricing
- Pricing by integration under the Black-Scholes model
- Delta calculations using Black-Scholes, integration, and CRR methods
- American option valuation in the CRR model
- Perpetual American put valuation using an ODE/free-boundary approach
- Test scripts and diagnostic plots for model validation
Once the repository is public, the package can be installed from GitHub using:
pip install git+https://github.com/Palit2308/computational_finance_lab.git
After installation, check that the package imports correctly:
import comp_fin_lab
print("Package imported successfully")You can also import individual modules:
from comp_fin_lab.payoffs import vcall, vput, vopt, pcall
from comp_fin_lab.bs import eu_bs
from comp_fin_lab.binom import eu_crr, eu_crr_lattice
from comp_fin_lab.barriers import down_and_out_crr
from comp_fin_lab.integration import eu_int_sn
from comp_fin_lab.greeks import eu_bs_delta, eu_crr_delta, delta_int_sn
from comp_fin_lab.american import perp_am_put, am_crrCurrent project structure:
computational_finance_lab/
│
├── src/
│ └── comp_fin_lab/
│ ├── __init__.py
│ ├── american.py
│ ├── barriers.py
│ ├── binom.py
│ ├── bs.py
│ ├── calibration.py
│ ├── fourier.py
│ ├── greeks.py
│ ├── heston.py
│ ├── integration.py
│ ├── laplace.py
│ ├── monte_carlo.py
│ ├── payoffs.py
│ └── rng.py
│
├── tests/
│ ├── plots/
│ │ ├── american_crr_convergence.png
│ │ ├── american_crr_early_exercise_premium.png
│ │ ├── american_crr_sigma_sensitivity.png
│ │ ├── bs_crr_anchored_error.png
│ │ ├── down_and_out_crr_barrier_sensitivity.png
│ │ ├── eu_crr_sigma_sensitivity_parity.png
│ │ ├── eu_crr_stock_and_call_structures_with_one_random_path.png
│ │ ├── integration_vs_crr_power_call_runtime.png
│ │ └── perpetual_american_put.png
│ │
│ ├── test_american.py
│ ├── test_barriers.py
│ ├── test_binom.py
│ ├── test_bs.py
│ ├── test_delta.py
│ ├── test_integration.py
│ ├── test_packaging_works.py
│ └── test_payoffs.py
│
├── pyproject.toml
├── README.md
└── .gitignoreThe source code lives inside:
src/comp_fin_lab/The tests and reproducible examples live inside:
tests/The generated diagnostic plots are saved in:
tests/plots/Contains basic option payoff functions.
from comp_fin_lab.payoffs import vcall, vput, vopt, pcall
vcall(S=120, K=100)
vput(S=80, K=100)
vopt(S=120, K=100, call=True)
pcall(S=120, K=100, alpha=1.2)Available functions:
vcall(S, K)
vput(S, K)
vopt(S, K, call=True)
pcall(S, K, alpha)Contains the Black-Scholes formula for European call and put options.
from comp_fin_lab.bs import eu_bs
price_call = eu_bs(
t=0,
St=120,
K=100,
T=1,
r=0.02,
sigma=0.3,
call=1,
)
price_put = eu_bs(
t=0,
St=120,
K=100,
T=1,
r=0.02,
sigma=0.3,
call=0,
)Available function:
eu_bs(t, St, K, T, r, sigma, call)where:
call = 1 -> European call
call = 0 -> European put
Contains the Cox-Ross-Rubinstein binomial tree implementation.
from comp_fin_lab.payoffs import vcall
from comp_fin_lab.binom import eu_crr
price = eu_crr(
g=vcall,
S0=120,
K=100,
T=1,
r=0.02,
sigma=0.3,
M=100,
c=1,
)Available functions:
eu_crr(g, S0, K, T, r, sigma, M, c=1)
eu_crr_lattice(g, S0, K, T, r, sigma, M, c=1)eu_crr returns only the time-zero option price.
eu_crr_lattice returns the full stock-price lattice, option-value lattice, and time step:
S, V, delta_t = eu_crr_lattice(
g=vcall,
S0=120,
K=100,
T=1,
r=0.02,
sigma=0.3,
M=50,
)Contains down-and-out barrier option pricing using the CRR binomial model.
from comp_fin_lab.payoffs import vcall
from comp_fin_lab.barriers import down_and_out_crr
price = down_and_out_crr(
g=vcall,
S0=120,
K=100,
T=1,
B=90,
r=0.02,
sigma=0.3,
M=500,
)Available function:
down_and_out_crr(g, S0, K, T, B, r, sigma, M, c=1)Contains pricing by integration under the Black-Scholes model.
The function evaluates the discounted risk-neutral expectation by integrating over a standard normal variable.
from comp_fin_lab.payoffs import vcall
from comp_fin_lab.integration import eu_int_sn
price = eu_int_sn(
t=0,
f=lambda S: vcall(S, 100),
St=120,
T=1,
r=0.02,
sigma=0.3,
a=-float("inf"),
b=float("inf"),
)Available function:
eu_int_sn(t, f, St, T, r, sigma, a, b)Here f should be a function of terminal stock price only. If the payoff requires a strike, wrap it using lambda.
Example:
f = lambda S: vcall(S, K=100)Contains delta calculations using three methods:
- Black-Scholes closed-form delta
- Pricing-by-integration delta
- CRR finite-difference delta using a binomial tree construction
from comp_fin_lab.payoffs import vcall
from comp_fin_lab.greeks import eu_bs_delta, eu_crr_delta, delta_int_snBlack-Scholes delta:
call_delta = eu_bs_delta(
t=0,
St=120,
K=100,
T=1,
r=0.02,
sigma=0.3,
call=1,
)
put_delta = eu_bs_delta(
t=0,
St=120,
K=100,
T=1,
r=0.02,
sigma=0.3,
call=0,
)CRR delta:
delta = eu_crr_delta(
g=vcall,
S0=120,
K=100,
T=1,
r=0.02,
sigma=0.3,
M=1000,
)Delta by integration:
def vcall_prime(S, K):
return 1.0 if S > K else 0.0
delta = delta_int_sn(
t=0,
f_prime=lambda S: vcall_prime(S, 100),
St=120,
T=1,
r=0.02,
sigma=0.3,
a=-float("inf"),
b=float("inf"),
)Contains valuation routines for American options.
Currently this module includes:
- A perpetual American put option in the Black-Scholes model
- A finite-maturity American call/put option using the CRR binomial tree
from comp_fin_lab.american import perp_am_put, am_crrPerpetual American put:
from comp_fin_lab.american import perp_am_put
S_grid, v_grid, x_star = perp_am_put(
K=100,
r=0.02,
sigma=0.3,
S_min=1e-6,
S_max=200,
n_grid=200,
)Available function:
perp_am_put(K, r, sigma, S_min=1e-6, S_max=200, n_grid=200, S_grid=None)The function returns:
S_grid -> stock-price grid
v_grid -> perpetual American put value on the grid
x_star -> optimal exercise boundary
Finite-maturity American option using CRR:
from comp_fin_lab.american import am_crr
price, C, S = am_crr(
S_ini=120,
K=100,
T=1,
r=0.02,
sigma=0.3,
N=500,
opttype="P",
c=1,
)Available function:
am_crr(S_ini, K, T, r, sigma, N, opttype, c=1)where:
opttype = "C" -> American call
opttype = "P" -> American put
The function returns:
price -> American option price at time 0
C -> option value tree
S -> underlying stock price tree
At each node of the tree, the function compares the continuation value with the immediate exercise value:
American value = max(continuation value, exercise payoff)
For a non-dividend-paying stock, the American call should be approximately equal to the corresponding European call. The American put can be more valuable than the European put because early exercise may be optimal.
The test files are written as executable Python scripts. They can be run directly.
From the project root:
cd tests
python test_packaging_works.py
python test_payoffs.py
python test_binom.py
python test_bs.py
python test_barriers.py
python test_integration.py
python test_delta.py
python test_american.pyThe tests generate diagnostic output and plots under:
tests/plots/
Checks whether the package can be imported:
import comp_fin_lab
print("Package imported successfully")This verifies that the project is correctly structured as an installable Python package.
Checks the basic payoff functions:
vcall(S, K)
vput(S, K)
vopt(S, K, call=True)
vopt(S, K, call=False)It tests both scalar inputs and vectorized array inputs.
The goal is to confirm that payoff functions behave correctly before they are used inside pricing models.
This script performs two main CRR diagnostics.
The script computes:
S, V, delta_t = eu_crr_lattice(...)and verifies that:
eu_crr(...) == V[0, 0]It then plots:
- The CRR stock-price lattice
- The CRR call-option value lattice
- One random binomial path through the tree
Generated plot:
tests/plots/eu_crr_stock_and_call_structures_with_one_random_path.png
This diagnostic checks that the stock-price tree and backward-inducted option-value tree are internally consistent.
The script varies volatility over a grid:
sigmas = np.linspace(0.01, 5, 500)For each volatility, it computes CRR call and put prices.
It checks:
- Call prices remain within no-arbitrage bounds
- Put prices remain within no-arbitrage bounds
- Put-call parity holds approximately
The put-call parity expression checked is:
P - C - K exp(-rT) + S0 = 0
Generated plot:
tests/plots/eu_crr_sigma_sensitivity_parity.png
Compares CRR prices against Black-Scholes prices across a range of initial stock prices.
The script varies:
S_arr = np.linspace(20, 300, 1000)For each stock price, it computes:
- Standard CRR call price
- Anchored CRR call price
- Black-Scholes call price
The anchored CRR tree uses:
c = (K / S0) ** (2 / M)The plotted quantities are:
CRR - Black-Scholes
Anchored CRR - Black-Scholes
Generated plot:
tests/plots/bs_crr_anchored_error.png
This test checks how well the binomial approximation matches the Black-Scholes benchmark and whether anchoring improves the approximation error.
Prices a down-and-out call for different barrier levels:
B_values = np.linspace(50, 150, 100)For each barrier, it computes:
down_and_out_crr(...)It checks:
- Barrier option values are non-negative
- A down-and-out call is never more expensive than the vanilla call
- The option value decreases as the barrier increases
- If the barrier is above the initial stock price, the option is knocked out immediately
Generated plot:
tests/plots/down_and_out_crr_barrier_sensitivity.png
This diagnostic verifies that the barrier option behaves consistently with financial intuition.
This script tests pricing by integration.
It computes a European call price using numerical integration and compares it with the Black-Scholes formula.
The relative absolute error is checked:
|Black-Scholes price - Integration price| / Black-Scholes price
This confirms that integration over the standard normal distribution correctly reproduces the Black-Scholes price.
The script prices a power call payoff:
pcall(S, K, alpha)using:
- Pricing by integration
- CRR binomial pricing
The test allows a tolerance because CRR is a discrete approximation.
It compares average runtime between integration and CRR for the power call.
Generated plot:
tests/plots/integration_vs_crr_power_call_runtime.png
This diagnostic shows that, for a one-dimensional Black-Scholes expectation, numerical integration can be much faster than a large binomial tree.
Compares option deltas from three methods:
- Black-Scholes closed-form delta
- Delta by numerical integration
- CRR delta approximation
For a call, the test checks:
0 <= Delta_call <= 1
For a put, the test checks:
-1 <= Delta_put <= 0
It also checks put-call delta parity:
Delta_call - Delta_put = 1
The integration delta and CRR delta are compared against Black-Scholes delta as the benchmark.
- Tests the perpetual American put implementation.
The function:
perp_am_put(...)returns:
S_grid
v_grid_am
x_star
The script compares the perpetual American put value with:
- Immediate exercise payoff
- A finite-maturity European put from Black-Scholes
It checks:
- Values are finite
- Values are non-negative
- The exercise boundary satisfies
0 < x_star < K - In the exercise region, value equals payoff
- The American put value is at least the intrinsic value
- The perpetual American put is at least as valuable as the finite-maturity European put
- Put value decreases as stock price increases
Generated plot:
tests/plots/perpetual_american_put.png
This diagnostic illustrates the exercise boundary and the value of early exercise.
- American CRR vs European CRR test
The function:
am_crr(...)returns:
price
C
S
where:
price -> American option price at time 0
C -> option value tree
S -> underlying stock price tree
The script compares American CRR prices with European CRR prices computed using:
eu_crr(...)It checks:
- American call values are finite
- American put values are finite
- American call price is at least the European call price
- American put price is at least the European put price
- For a non-dividend-paying stock, the American call is approximately equal to the European call
- The returned option-value and stock-price trees have the expected shape
The core financial identity being checked is:
American option value >= European option value
For a non-dividend-paying stock:
American call ≈ European call
because early exercise of a call is not optimal without dividends.
For puts:
American put >= European put
because early exercise may be valuable.
- Convergence over number of binomial steps
The script varies the number of CRR time steps:
N_values = np.array([5, 10, 25, 50, 100, 200, 500])For each N, it computes:
- American call price
- European call price
- American put price
- European put price
Generated plot:
tests/plots/american_crr_convergence.png
This diagnostic checks whether the American CRR prices stabilize as the binomial tree becomes finer.
- Volatility sensitivity test
The script varies volatility over a grid:
sigmas = np.linspace(0.05, 1.0, 100)For each volatility, it computes:
- American call price
- European call price
- American put price
- European put price
It checks:
- Prices are finite
- Prices are non-negative
- American prices dominate European prices
- American call prices are approximately equal to European call prices in the no-dividend case
- Option prices generally increase with volatility
Generated plot:
tests/plots/american_crr_sigma_sensitivity.png
This diagnostic shows how American and European option prices respond to changes in volatility.
- Early exercise premium test
The script computes the early exercise premium:
American price - European price
for calls and puts.
Generated plot:
tests/plots/american_crr_early_exercise_premium.png
This diagnostic illustrates that:
- The call early-exercise premium is approximately zero for a non-dividend-paying stock
- The put early-exercise premium can be positive because early exercise may be optimal
The current diagnostic plots are:
tests/plots/bs_crr_anchored_error.png
tests/plots/down_and_out_crr_barrier_sensitivity.png
tests/plots/eu_crr_sigma_sensitivity_parity.png
tests/plots/eu_crr_stock_and_call_structures_with_one_random_path.png
tests/plots/integration_vs_crr_power_call_runtime.png
tests/plots/perpetual_american_put.png
These plots are generated by the test scripts and document the numerical behaviour of the implemented models.
For a clean GitHub repository, there are two possible approaches:
Keep tests/plots/ in .gitignore.
Users can reproduce the plots by running the tests.
Move selected figures to:
docs/plots/
and reference them in the README.
Example:
Recommended approach:
- Keep
tests/plots/ignored - Copy only polished portfolio figures to
docs/plots/
A typical workflow is:
from comp_fin_lab.payoffs import vcall
from comp_fin_lab.bs import eu_bs
from comp_fin_lab.binom import eu_crr
from comp_fin_lab.greeks import eu_bs_delta
S0 = 120
K = 100
T = 1
r = 0.02
sigma = 0.3
M = 100
bs_price = eu_bs(
t=0,
St=S0,
K=K,
T=T,
r=r,
sigma=sigma,
call=1,
)
crr_price = eu_crr(
g=vcall,
S0=S0,
K=K,
T=T,
r=r,
sigma=sigma,
M=M,
)
delta = eu_bs_delta(
t=0,
St=S0,
K=K,
T=T,
r=r,
sigma=sigma,
call=1,
)
print("Black-Scholes call price:", bs_price)
print("CRR call price:", crr_price)
print("Black-Scholes call delta:", delta)This package currently focuses on the following modelling ideas:
Most functions use the idea that the option price is a discounted risk-neutral expectation:
V(t) = exp(-r(T-t)) E^Q[payoff]
The CRR model approximates the risk-neutral stock process using a recombining binomial tree.
The Black-Scholes formula is used as the closed-form benchmark for European call and put pricing.
In the Black-Scholes model, terminal stock prices can be written as a function of a standard normal random variable. Therefore, option prices can be computed by integrating over the standard normal density.
Delta is computed using:
- Closed-form Black-Scholes expressions
- Differentiation under the integral sign
- CRR finite-difference approximations
The perpetual American put is treated as a free-boundary problem. Below the exercise boundary, immediate exercise is optimal. Above the boundary, the value solves a stationary Black-Scholes ODE.
Planned extensions include:
- Monte Carlo option pricing
- Random number generation methods
- Inverse transform sampling
- Acceptance-rejection sampling
- Ornstein-Uhlenbeck process simulation
- CIR process simulation
- GBM simulation
- Heston stochastic volatility pricing
- Characteristic-function based pricing
- Fourier transform pricing
- Laplace transform pricing
- Implied volatility
- Calibration routines
- Backtesting and hedging examples
- Documentation pages and example notebooks
This repository is designed as a reusable computational finance library rather than a collection of isolated notebooks.
The aim is to preserve mathematically verified implementations while turning them into importable, testable, and extensible Python modules.
The package can be used as a foundation for:
- Derivative pricing projects
- Stochastic process simulation
- Model calibration
- Risk analysis
- Delta hedging backtests
- Quant developer portfolio projects