License Crates.io Crates.io GitHub Repo stars

GitHub Workflow Status Codecov Dependencies

Discord

A Rust library for quantitative finance tools.

:dart: I want to hit a stable and legitimate v1.0.0 by the end of 2023, so any and all feedback, suggestions, or contributions are strongly welcomed!

Email me at: RustQuantContact@gmail.com

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

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.

:newspaper: Latest features

See CHANGELOG.md for a full list of changes.

:link: 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.

```rust 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:

rust 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!

:bar_chart: Data


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

You can:

```rust 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);

} ```

bash 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 │ └────────────┴───────────┴───────────┴───────────┴───────────┴────────────┴───────────┘)

bash 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

```rust 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)

} ```

:bar_chart: Distributions


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

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

:chart_with_upwards_trend: Instruments


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

:chartwithdownwards_trend: Bonds

:moneywithwings: Option Pricing

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

```rust use RustQuant::options::*;

fn main() { let VanillaOption = EuropeanOption { initialprice: 100.0, strikeprice: 110.0, riskfreerate: 0.05, volatility: 0.2, dividendrate: 0.02, timeto_maturity: 0.5, };

let prices = VanillaOption.price();

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

} ```

:triangular_ruler: 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

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).

```rust 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

```rust 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

:crystal_ball: Machine Learning


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

Regression

:moneybag: Money


Implementations for Cashflows, Currencies, and Quotes, and similar objects.

:chart_with_upwards_trend: Stochastic Processes and Short Rate Models


Can generate Brownian Motion (standard, arithmetric 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.

```rust 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);

} ```

:calendar: Time and Date


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

:handshake: Miscellaneous Functions and Macros


Various helper functions and macros.

A collection of utility functions and macros.

:heavy_check_mark: How-tos


Guides for using RustQuant.

See /examples for more details. Run them with:

bash 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.

:pray: I would greatly appreciate contributions so it can get to the v1.0.0 mark ASAP.

:book: References


References and resources used for this project.