License GitHub Workflow Status Crates.io GitHub Repo stars Crates.io Codecov Dependencies

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!

Contact: rustquantcontact@gmail.com

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.

Table of Contents

  1. Automatic Differentiation
  2. Option Pricers
  3. Stochastic Processes and Short Rate Models
  4. Bonds
  5. Distributions
  6. Mathematics
  7. Machine Learning
  8. Helper Functions and Macros
  9. How-tos
  10. References

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

:moneywithwings: Option Pricers

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

:chartwithupwards_trend: Stochastic Processes and Short Rate Models

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

:chartwithdownwards_trend: Bonds

:bar_chart: Distributions

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

:triangular_ruler: Mathematics

:crystal_ball: Machine Learning

:handshake: Helper Functions and Macros

A collection of utility functions and macros.

:heavycheckmark: How-tos

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.

Download data from Yahoo! Finance

You can download data from Yahoo! Finance into a Polars DataFrame.

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

println!("Apple's quotes: {:?}", yfd.price_history)

} ```

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

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)

} ```

Compute gradients

```rust use RustQuant::autodiff::*;

fn main() { // Create a new Tape. let t = Tape::new();

// Assign variables.
let x = t.var(0.5);
let y = t.var(4.2);

// Define a function.
let z = x * y + x.sin();

// Accumulate the gradient.
let grad = z.accumulate();

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

} ```

Compute integrals

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

} ```

Gradient Descent

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

} ```

Price options

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

} ```

Generate stochastic processes

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

} ```

:book: References: