ShabbirHasan1 / RustQuant

Rust library for quantitative finance.

Home Page:https://crates.io/crates/RustQuant

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

License Crates.io Crates.io GitHub Repo stars

GitHub Workflow Status Codecov Dependencies

Discord

A Rust library for quantitative finance tools. Also the largest option pricing library in Rust.

๐ŸŽฏ I want to hit a stable v0.1.0 by the end of 2023, so any feedback, suggestions, or contributions are strongly welcomed!

Email me at: RustQuantContact@gmail.com

Join the Discord server: https://discord.gg/tQcM77h8vr

๐Ÿ“ฐ Latest changes

See CHANGELOG.md for a full list of changes.

Features

๐Ÿ”— Automatic Differentiation


Reverse (Adjoint) Mode Automatic Differentiation.

Currently only gradients can be computed. Suggestions on how to extend the functionality to Hessian matrices are definitely welcome.

Additionally, only functions $f: \mathbb{R}^n \rightarrow \mathbb{R}$ (scalar output) are supported. However, you can manually apply the differentiation to multiple functions that could represent a vector output.

  • Reverse (Adjoint) Mode
    • Implementation via Operator and Function Overloading.
    • Useful when number of outputs is smaller than number of inputs.
      • i.e for functions $f:\mathbb{R}^n \rightarrow \mathbb{R}^m$, where $m \ll n$
  • Forward (Tangent) Mode
    • Implementation via Dual Numbers.
    • Useful when number of outputs is larger than number of inputs.
      • i.e. for functions $f:\mathbb{R}^n \rightarrow \mathbb{R}^m$, where $m \gg n$
use RustQuant::autodiff::*;

fn main() {
    // Create a new Graph to store the computations.
    let g = Graph::new();

    // Assign variables.
    let x = g.var(69.);
    let y = g.var(420.);

    // Define a function.
    let f = {
      let a = x.powi(2);
      let b = y.powi(2);

      a + b + (x * y).exp()
    };

    // Accumulate the gradient.
    let gradient = f.accumulate();

    println!("Function = {}", f);
    println!("Gradient = {:?}", gradient.wrt([x, y]));
}

You can also generate Graphviz (dot) code to visualize the computation graphs:

println!("{}", graphviz(&graph, &variables));

The computation graph from computing Black-Scholes Greeks is:

Black-Scholes Greeks tape.

It is clearly a work in progress, but gives a general idea of how the computation graph is structured.

If you want to improve the visualization, please feel free to submit a PR!

๐Ÿ“Š Data


Methods for reading and writing data from/to various sources (CSV, JSON, Parquet). Can also download data from Yahoo! Finance.

You can:

  • Download data from Yahoo! Finance into a Polars DataFrame.
  • Compute returns on the DataFrame you just downloaded.
use RustQuant::data::*;
use time::macros::date;

fn main() {
    // New YahooFinanceData instance.
    // By default, date range is: 1970-01-01 to present.
    let mut yfd = YahooFinanceData::new("AAPL".to_string());

    // Can specify custom dates (optional).
    yfd.set_start_date(time::macros::datetime!(2019 - 01 - 01 0:00 UTC));
    yfd.set_end_date(time::macros::datetime!(2020 - 01 - 01 0:00 UTC));

    // Download the historical data.
    yfd.get_price_history();

    // Compute the returns.
    // Specify the type of returns to compute (Simple, Logarithmic, Absolute)
    // You don't need to run .get_price_history() first, .compute_returns()
    // will do it for you if necessary.
    yfd.compute_returns(ReturnsType::Logarithmic);

    println!("Apple's quotes: {:?}", yfd.price_history);
    println!("Apple's returns: {:?}", yfd.returns);
}
Apple's quotes: Some(shape: (252, 7)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ date       โ”† open      โ”† high      โ”† low       โ”† close     โ”† volume     โ”† adjusted  โ”‚
โ”‚ ---        โ”† ---       โ”† ---       โ”† ---       โ”† ---       โ”† ---        โ”† ---       โ”‚
โ”‚ date       โ”† f64       โ”† f64       โ”† f64       โ”† f64       โ”† f64        โ”† f64       โ”‚
โ•žโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ก
โ”‚ 2019-01-02 โ”† 38.7225   โ”† 39.712502 โ”† 38.557499 โ”† 39.48     โ”† 1.481588e8 โ”† 37.994499 โ”‚
โ”‚ 2019-01-03 โ”† 35.994999 โ”† 36.43     โ”† 35.5      โ”† 35.547501 โ”† 3.652488e8 โ”† 34.209969 โ”‚
โ”‚ 2019-01-04 โ”† 36.1325   โ”† 37.137501 โ”† 35.950001 โ”† 37.064999 โ”† 2.344284e8 โ”† 35.670372 โ”‚
โ”‚ 2019-01-07 โ”† 37.174999 โ”† 37.2075   โ”† 36.474998 โ”† 36.982498 โ”† 2.191112e8 โ”† 35.590965 โ”‚
โ”‚ โ€ฆ          โ”† โ€ฆ         โ”† โ€ฆ         โ”† โ€ฆ         โ”† โ€ฆ         โ”† โ€ฆ          โ”† โ€ฆ         โ”‚
โ”‚ 2019-12-26 โ”† 71.205002 โ”† 72.495003 โ”† 71.175003 โ”† 72.477501 โ”† 9.31212e7  โ”† 70.798401 โ”‚
โ”‚ 2019-12-27 โ”† 72.779999 โ”† 73.4925   โ”† 72.029999 โ”† 72.449997 โ”† 1.46266e8  โ”† 70.771545 โ”‚
โ”‚ 2019-12-30 โ”† 72.364998 โ”† 73.172501 โ”† 71.305    โ”† 72.879997 โ”† 1.441144e8 โ”† 71.191582 โ”‚
โ”‚ 2019-12-31 โ”† 72.482498 โ”† 73.419998 โ”† 72.379997 โ”† 73.412498 โ”† 1.008056e8 โ”† 71.711739 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜)
Apple's returns: Some(shape: (252, 7)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ date       โ”† volume     โ”† open_logarith โ”† high_logarith โ”† low_logarithm โ”† close_logari โ”† adjusted_log โ”‚
โ”‚ ---        โ”† ---        โ”† mic           โ”† mic           โ”† ic            โ”† thmic        โ”† arithmic     โ”‚
โ”‚ date       โ”† f64        โ”† ---           โ”† ---           โ”† ---           โ”† ---          โ”† ---          โ”‚
โ”‚            โ”†            โ”† f64           โ”† f64           โ”† f64           โ”† f64          โ”† f64          โ”‚
โ•žโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ก
โ”‚ 2019-01-02 โ”† 1.481588e8 โ”† null          โ”† null          โ”† null          โ”† null         โ”† null         โ”‚
โ”‚ 2019-01-03 โ”† 3.652488e8 โ”† -0.073041     โ”† -0.086273     โ”† -0.082618     โ”† -0.104924    โ”† -0.104925    โ”‚
โ”‚ 2019-01-04 โ”† 2.344284e8 โ”† 0.003813      โ”† 0.019235      โ”† 0.012596      โ”† 0.041803     โ”† 0.041803     โ”‚
โ”‚ 2019-01-07 โ”† 2.191112e8 โ”† 0.028444      โ”† 0.001883      โ”† 0.014498      โ”† -0.002228    โ”† -0.002229    โ”‚
โ”‚ โ€ฆ          โ”† โ€ฆ          โ”† โ€ฆ             โ”† โ€ฆ             โ”† โ€ฆ             โ”† โ€ฆ            โ”† โ€ฆ            โ”‚
โ”‚ 2019-12-26 โ”† 9.31212e7  โ”† 0.000457      โ”† 0.017709      โ”† 0.006272      โ”† 0.019646     โ”† 0.019646     โ”‚
โ”‚ 2019-12-27 โ”† 1.46266e8  โ”† 0.021878      โ”† 0.013666      โ”† 0.011941      โ”† -0.00038     โ”† -0.00038     โ”‚
โ”‚ 2019-12-30 โ”† 1.441144e8 โ”† -0.005718     โ”† -0.004364     โ”† -0.010116     โ”† 0.005918     โ”† 0.005918     โ”‚
โ”‚ 2019-12-31 โ”† 1.008056e8 โ”† 0.001622      โ”† 0.003377      โ”† 0.014964      โ”† 0.00728      โ”† 0.00728      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜)

Read/write data

use RustQuant::data::*;

fn main() {
    // New `Data` instance.
    let mut data = Data::new(
        format: DataFormat::CSV, // Can also be JSON or PARQUET.
        path: String::from("./file/path/read.csv")
    )

    // Read from the given file. 
    data.read().unwrap();

    // New path to write the data to. 
    data.path = String::from("./file/path/write.csv")
    data.write().unwrap();

    println!("{:?}", data.data)
}

๐Ÿ“Š Distributions


PDFs, CDFs, MGFs, CFs, and other ditrubution related functions for common distributions.

Probability density/mass functions, distribution functions, characteristic functions, etc.

  • Gaussian
  • Bernoulli
  • Binomial
  • Poisson
  • Uniform (discrete & continuous)
  • Chi-Squared
  • Gamma
  • Exponential

๐Ÿ“ˆ Instruments


Various implementations for instruments like `Bonds` and `Options`, and the pricing of them. Others coming in the future (swaps, futures, CDSs, etc).

๐Ÿ“‰ Bonds

  • Prices:
    • The Vasicek Model
    • The Cox, Ingersoll, and Ross Model
    • The Hullโ€“White (One-Factor) Model
    • The Rendleman and Bartter Model
    • The Hoโ€“Lee Model
    • The Blackโ€“Dermanโ€“Toy Model
    • The Blackโ€“Karasinski Model
  • Duration
  • Convexity

๐Ÿ’ธ Option Pricing

  • Closed-form price solutions:

    • Heston Model
    • Barrier
    • European
    • Greeks/Sensitivities
    • Lookback
    • Asian: Continuous Geometric Average
    • Forward Start
    • Bachelier and Modified Bachelier
    • Generalised Black-Scholes-Merton
    • Basket
    • Rainbow
    • American
  • Lattice models:

    • Binomial Tree (Cox-Ross-Rubinstein)

The stochastic process generators can be used to price path-dependent options via Monte-Carlo.

  • Monte Carlo pricing:
    • Lookback
    • Asian
    • Chooser
    • Barrier
use RustQuant::options::*;

fn main() {
    let VanillaOption = EuropeanOption {
        initial_price: 100.0,
        strike_price: 110.0,
        risk_free_rate: 0.05,
        volatility: 0.2,
        dividend_rate: 0.02,
        time_to_maturity: 0.5,
    };

    let prices = VanillaOption.price();

    println!("Call price = {}", prices.0);
    println!("Put price = {}", prices.1);
}

๐Ÿ“ Mathematics


Fast Fourier Transform (FFT), numerical integration (double-exponential quadrature), optimisation/root-finding (gradient descent, Newton-Raphson), and risk-reward metrics.

Optimization and Root Finding

  • Gradient Descent
  • Newton-Raphson

Note: the reason you need to specify the lifetimes and use the type Variable is because the gradient descent optimiser uses the RustQuant::autodiff module to compute the gradients. This is a slight inconvenience, but the speed-up is enormous when working with functions with many inputs (when compared with using finite-difference quotients).

use RustQuant::optimisation::GradientDescent;

// Define the objective function.
fn himmelblau<'v>(variables: &[Variable<'v>]) -> Variable<'v> {
    let x = variables[0];
    let y = variables[1];

    ((x.powf(2.0) + y - 11.0).powf(2.0) + (x + y.powf(2.0) - 7.0).powf(2.0))
}

fn main() {
    // Create a new GradientDescent object with:
    //      - Step size: 0.005 
    //      - Iterations: 10000
    //      - Tolerance: sqrt(machine epsilon)
    let gd = GradientDescent::new(0.005, 10000, std::f64::EPSILON.sqrt() );

    // Perform the optimisation with:
    //      - Initial guess (10.0, 10.0),
    //      - Verbose output.
    let result = gd.optimize(&himmelblau, &vec![10.0, 10.0], true);
    
    // Print the result.
    println!("{:?}", result.minimizer);
}

Integration

  • Numerical Integration (needed for Heston model, for example):
    • Tanh-Sinh (double exponential) quadrature
    • Composite Midpoint Rule
    • Composite Trapezoidal Rule
    • Composite Simpson's 3/8 Rule
use RustQuant::math::*;

fn main() {
    // Define a function to integrate: e^(sin(x))
    fn f(x: f64) -> f64 {
        (x.sin()).exp()
    }

    // Integrate from 0 to 5.
    let integral = integrate(f, 0.0, 5.0);

    // ~ 7.18911925
    println!("Integral = {}", integral); 
}

Risk-Reward Metrics

  • Risk-Reward Measures (Sharpe, Treynor, Sortino, etc)

๐Ÿ”ฎ Machine Learning


Currently only linear regression is implemented (and working on logistic regression). More to come in the future.

Regression

  • Linear (using QR or SVD decomposition)
  • Logistic (via IRLS, adding MLE in the future).

๐Ÿ’ฐ Money


Implementations for `Cashflows`, `Currencies`, and `Quotes`, and similar objects.
  • Cashflow
  • Currency
  • Money
  • Quote
  • Leg

๐Ÿ“ˆ Stochastic Processes and Short Rate Models


Can generate Brownian Motion (standard, arithmetic and geometric) and various short-rate models (CIR, OU, Vasicek, Hull-White, etc).

The following is a list of stochastic processes that can be generated.

  • Brownian Motions:
    • Standard Brownian Motion
      • $dX(t) = dW(t)$
    • Arithmetic Brownian Motion
      • $dX(t) = \mu dt + \sigma dW(t)$
    • Geometric Brownian Motion
      • $dX(t) = \mu X(t) dt + \sigma X(t) dW(t)$
    • Fractional Brownian Motion
  • Cox-Ingersoll-Ross (1985)
    • $dX(t) = \left[ \theta - \alpha X(t) \right] dt + \sigma \sqrt{r_t} dW(t)$
  • Ornstein-Uhlenbeck process
    • $dX(t) = \theta \left[ \mu - X(t) \right] dt + \sigma dW(t)$
  • Ho-Lee (1986)
    • $dX(t) = \theta(t) dt + \sigma dW(t)$
  • Hull-White (1990)
    • $dX(t) = \left[ \theta(t) - \alpha X(t) \right]dt + \sigma dW(t)$
  • Extended Vasicek (1990)
    • $dX(t) = \left[ \theta(t) - \alpha(t) X(t) \right] dt + \sigma dW(t)$
  • Black-Derman-Toy (1990)
    • $d\ln[X(t)] = \left[ \theta(t) + \frac{\sigma'(t)}{\sigma(t)}\ln[X(t)] \right]dt + \sigma_t dW(t)$
use RustQuant::stochastics::*;

fn main() {
    // Create new GBM with mu and sigma.
    let gbm = GeometricBrownianMotion::new(0.05, 0.9);

    // Generate path using Euler-Maruyama scheme.
    // Parameters: x_0, t_0, t_n, n, sims, parallel.
    let output = (&gbm).euler_maruyama(10.0, 0.0, 0.5, 10, 1, false);

    println!("GBM = {:?}", output.paths);
}

๐Ÿ“† Time and Date


Time and date functionality. Mostly the `DayCounter` for pricing options and bonds.
  • DayCounter

๐Ÿค Miscellaneous Functions and Macros


Various helper functions and macros.

A collection of utility functions and macros.

  • Plot a vector.
  • Write vector to file.
  • Cumulative sum of vector.
  • Linearly spaced sequence.
  • assert_approx_equal!

โœ”๏ธ How-tos


Guides for using RustQuant.

See /examples for more details. Run them with:

cargo run --example automatic_differentiation

I would not recommend using RustQuant within any other libraries for some time, as it will most likely go through many breaking changes as I learn more Rust and settle on a decent structure for the library.

๐Ÿ™ I would greatly appreciate contributions so it can get to the v1.0.0 mark ASAP.

๐Ÿ“– References


References and resources used for this project.
  • John C. Hull - Options, Futures, and Other Derivatives
  • Damiano Brigo & Fabio Mercurio - Interest Rate Models - Theory and Practice (With Smile, Inflation and Credit)
  • Paul Glasserman - Monte Carlo Methods in Financial Engineering
  • Andreas Griewank & Andrea Walther - Evaluating Derivatives - Principles and Techniques of Algorithmic Differentiation
  • Steven E. Shreve - Stochastic Calculus for Finance II: Continuous-Time Models
  • Espen Gaarder Haug - Option Pricing Formulas
  • Antoine Savine - Modern Computational Finance: AAD and Parallel Simulations

Disclaimer

This is currently a free-time project and not a professional financial software library. Nothing in this library should be taken as financial advice, and I do not recommend you to use it for trading or making financial decisions.

About

Rust library for quantitative finance.

https://crates.io/crates/RustQuant

License:Apache License 2.0


Languages

Language:Rust 100.0%