diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 31419f7..501d96e 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -19,8 +19,7 @@ jobs:
override: true
- name: Check
- run: cargo check --workspace --verbose
+ run: cargo check --workspace --verbose
- name: Run tests
run: cargo test --workspace --verbose
-
diff --git a/Cargo.lock b/Cargo.lock
index 970c946..c39b284 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -70,12 +70,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "anes"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
-
[[package]]
name = "anstream"
version = "0.6.14"
@@ -216,9 +210,6 @@ name = "bench"
version = "0.1.0"
dependencies = [
"assert_cmd",
- "criterion",
- "serde",
- "serde_json",
]
[[package]]
@@ -647,12 +638,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
-[[package]]
-name = "cast"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
-
[[package]]
name = "cc"
version = "1.1.6"
@@ -689,33 +674,6 @@ dependencies = [
"windows-targets 0.52.6",
]
-[[package]]
-name = "ciborium"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
-dependencies = [
- "ciborium-io",
- "ciborium-ll",
- "serde",
-]
-
-[[package]]
-name = "ciborium-io"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
-
-[[package]]
-name = "ciborium-ll"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
-dependencies = [
- "ciborium-io",
- "half",
-]
-
[[package]]
name = "cipher"
version = "0.4.4"
@@ -931,42 +889,6 @@ dependencies = [
"cfg-if",
]
-[[package]]
-name = "criterion"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
-dependencies = [
- "anes",
- "cast",
- "ciborium",
- "clap",
- "criterion-plot",
- "is-terminal",
- "itertools 0.10.5",
- "num-traits",
- "once_cell",
- "oorandom",
- "plotters",
- "rayon",
- "regex",
- "serde",
- "serde_derive",
- "serde_json",
- "tinytemplate",
- "walkdir",
-]
-
-[[package]]
-name = "criterion-plot"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
-dependencies = [
- "cast",
- "itertools 0.10.5",
-]
-
[[package]]
name = "crossbeam-channel"
version = "0.5.13"
@@ -2152,32 +2074,12 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
-[[package]]
-name = "is-terminal"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
-dependencies = [
- "hermit-abi",
- "libc",
- "windows-sys 0.52.0",
-]
-
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
-[[package]]
-name = "itertools"
-version = "0.10.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
-dependencies = [
- "either",
-]
-
[[package]]
name = "itertools"
version = "0.12.1"
@@ -2650,12 +2552,6 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
-[[package]]
-name = "oorandom"
-version = "11.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
-
[[package]]
name = "openssl"
version = "0.10.66"
@@ -3050,7 +2946,7 @@ dependencies = [
"cassowary",
"crossterm",
"indoc",
- "itertools 0.12.1",
+ "itertools",
"lru",
"paste",
"stability",
@@ -3907,16 +3803,6 @@ dependencies = [
"time-core",
]
-[[package]]
-name = "tinytemplate"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
-dependencies = [
- "serde",
- "serde_json",
-]
-
[[package]]
name = "tinyvec"
version = "1.8.0"
diff --git a/Cargo.toml b/Cargo.toml
index d8235dd..3448f3a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,15 @@ version = "0.1.0"
edition = "2021"
[workspace]
+resolver = "2"
+
members = ["core", "bench", "cli", "providers"]
+[workspace.package]
+edition = "2021"
+version = "0.14.0"
+readme = "README.md"
+license = "Apache-2.0"
[dependencies]
+
diff --git a/README.md b/README.md
index 7ba7d75..255745a 100644
--- a/README.md
+++ b/README.md
@@ -1,161 +1,12 @@
- The Certificate in Quantitative Finance | CQF For Rust Example
+
+[](https://releases.rs/docs/1.79.0)
+---
+**Custom Neuron Decision-Making and Visual Workflow Orchestration Quantitative**
-# Example -> Option Strategies
+
+
-## Single Leg Option
-
-**Documentation:**
-[Investopedia - Single Leg Option](https://www.investopedia.com/terms/s/single-option.aspx)
-
-**Example:**
-
-```rust
-let call_price = SingleLegOption::call(&model, s, k, r, sigma, t);
-let put_price = SingleLegOption::put(&model, s, k, r, sigma, t);
-```
-
-## Butterfly Spread
-
-**Documentation:**
-[Investopedia - Butterfly Spread](https://www.investopedia.com/terms/b/butterflyspread.asp)
-
-**Example:**
-
-```rust
-let butterfly_spread = ButterflySpread::new(&model, s, k1, k2, k3, r, sigma, t);
-let price = butterfly_spread.price();
-```
-
-## Vertical Spread
-
-**Documentation:**
-[Investopedia - Vertical Spread](https://www.investopedia.com/terms/v/verticalspread.asp)
-
-**Example:**
-
-```rust
-let bull_call_spread = VerticalSpread::new(&model, s, k1, k2, r, sigma, t, true);
-let bear_put_spread = VerticalSpread::new(&model, s, k1, k2, r, sigma, t, false);
-```
-## Covered Call
-
-**Documentation:**
-[Investopedia - Covered Call](https://www.investopedia.com/terms/c/coveredcall.asp)
-
-**Example:**
-
-```rust
-let covered_call = CoveredCall::new(&model, s, k, r, sigma, t);
-let price = covered_call.price();
-```
-## Collar
-
-**Documentation:**
-[Investopedia - Collar](https://www.investopedia.com/terms/c/collar.asp)
-
-**Example:**
-
-```rust
-let collar = Collar::new(&model, s, k1, k2, r, sigma, t);
-let price = collar.price();
-```
-
-## Straddle
-
-**Documentation:**
-[Investopedia - Straddle](https://www.investopedia.com/terms/s/straddle.asp)
-
-**Example:**
-
-```rust
-let straddle = Straddle::new(&model, s, k, r, sigma, t);
-let price = straddle.price();
-```
-
-## Strangle
-
-**Documentation:**
-[Investopedia - Strangle](https://www.investopedia.com/terms/s/strangle.asp)
-
-**Example:**
-
-```rust
-let strangle = Strangle::new(&model, s, k1, k2, r, sigma, t);
-let price = strangle.price();
-```
-
-## Calendar Spread
-
-**Documentation:**
-[Investopedia - Calendar Spread](https://www.investopedia.com/terms/c/calendarspread.asp)
-
-**Example:**
-
-```rust
-let calendar_spread = CalendarSpread::new(&model, s, k, r, sigma, t1, t2);
-let price = calendar_spread.price();
-```
-
-## Diagonal Spread
-
-**Documentation:**
-[Investopedia - Diagonal Spread](https://www.investopedia.com/terms/d/diagonalspread.asp)
-
-**Example:**
-
-```rust
-let diagonal_spread = DiagonalSpread::new(&model, s, k1, k2, r, sigma, t1, t2);
-let price = diagonal_spread.price();
-```
-
-## Condor
-
-**Documentation:**
-[Investopedia - Condor](https://www.investopedia.com/terms/c/condor.asp)
-
-**Example:**
-
-```rust
-let condor = Condor::new(&model, s, k1, k2, k3, k4, r, sigma, t);
-let price = condor.price();
-```
-
-## Iron Butterfly
-
-**Documentation:**
-[Investopedia - Iron Butterfly](https://www.investopedia.com/terms/i/ironbutterfly.asp)
-
-**Example:**
-
-```rust
-let iron_butterfly = IronButterfly::new(&model, s, k1, k2, k3, r, sigma, t);
-let price = iron_butterfly.price();
-```
-
-## Iron Condor
-
-**Documentation:**
-[Investopedia - Iron Condor](https://www.investopedia.com/terms/i/ironcondor.asp)
-
-**Example:**
-
-```rust
-let iron_condor = IronCondor::new(&model, s, k1, k2, k3, k4, r, sigma, t);
-let price = iron_condor.price();
-```
-
-## Dance
-
-**Documentation:**
-[Investopedia - Dance](https://www.investopedia.com/terms/d/dance.asp)
-
-**Example:**
-
-```rust
-let dance = Dance::new(&model, s, k1, k2, k3, r, sigma, t);
-let price = dance.price();
-```
# Contributing
Contributions are welcome! Please open an issue or submit a pull request for any improvements or new features.
diff --git a/core/src/models/binomial_tree.rs b/core/src/models/binomial_tree.rs
new file mode 100644
index 0000000..48752c7
--- /dev/null
+++ b/core/src/models/binomial_tree.rs
@@ -0,0 +1,236 @@
+use crate::models::{OptionParameters, OptionPricingModel};
+
+//
+//
+pub struct BinomialTreeModel {
+ /// Number of steps in the binomial tree model.
+ pub steps: usize,
+}
+
+enum OptionType {
+ Call,
+ Put,
+}
+
+impl BinomialTreeModel {
+ /// Creates a new `BinomialTreeModel` with a specified number of steps.
+ ///
+ /// # Arguments
+ ///
+ /// * `steps` - Number of steps in the binomial tree model.
+ pub fn new(steps: usize) -> Self {
+ Self { steps }
+ }
+
+ /// Initializes the prices vector for call or put options.
+ ///
+ /// # Arguments
+ ///
+ /// * `params` - A reference to `OptionParameters` containing the parameters for the option.
+ /// * `option_type` - A value indicating the type of option (`Call` or `Put`).
+ ///
+ /// # Returns
+ ///
+ /// A vector containing the prices of the option at each node.
+ fn initialize_prices(&self, params: &OptionParameters, option_type: OptionType) -> Vec {
+ let n = self.steps; // Number of steps in the binomial tree
+ let dt = params.t / (n as f64); // Time step size
+ let u = f64::exp(params.sigma * (dt as f64).sqrt()); // Up factor
+ let d = 1.0 / u; // Down factor
+
+ // Terminal prices
+ (0..=n)
+ .map(|i| {
+ let price = params.s * u.powi((n - i) as i32) * d.powi(i as i32);
+ match option_type {
+ OptionType::Call => (price - params.k).max(0.0),
+ OptionType::Put => (params.k - price).max(0.0),
+ }
+ })
+ .collect()
+ }
+ /// Performs backward induction to calculate option price.
+ ///
+ /// # Arguments
+ ///
+ /// * `prices` - A mutable vector containing the prices of the option at each node.
+ /// * `params` - A reference to `OptionParameters` containing the parameters for the option.
+ ///
+ /// # Returns
+ ///
+ /// The calculated option price.
+ fn backward_induction(&self, prices: &mut Vec, params: &OptionParameters) -> f64 {
+ let n = self.steps; // Number of steps in the binomial tree
+ let dt = params.t / (n as f64); // Time step size
+ let u = f64::exp(params.sigma * (dt as f64).sqrt()); // Up factor
+ let d = 1.0 / u; // Down factor
+ let q = (f64::exp(params.r * dt as f64) - d) / (u - d); // Risk-neutral probability
+
+ for j in (0..n).rev() {
+ for i in 0..=j {
+ prices[i] =
+ f64::exp(-params.r * dt as f64) * (q * prices[i] + (1.0 - q) * prices[i + 1]);
+ }
+ }
+ prices[0]
+ }
+}
+
+impl Default for BinomialTreeModel {
+ fn default() -> Self {
+ Self { steps: 100 } // Default number of steps is 100
+ }
+}
+
+impl OptionPricingModel for BinomialTreeModel {
+ /// Calculates the call option price using the binomial tree model.
+ ///
+ /// # Arguments
+ ///
+ /// * `params` - A reference to `OptionParameters` containing the parameters for the option.
+ ///
+ /// # Returns
+ ///
+ /// The calculated call option price.
+ fn call_price(&self, params: &OptionParameters) -> f64 {
+ let mut prices = self.initialize_prices(params, OptionType::Call);
+ self.backward_induction(&mut prices, params)
+ }
+
+ /// Calculates the put option price using the binomial tree model.
+ ///
+ /// # Arguments
+ ///
+ /// * `params` - A reference to `OptionParameters` containing the parameters for the option.
+ ///
+ /// # Returns
+ ///
+ /// The calculated put option price.
+ fn put_price(&self, params: &OptionParameters) -> f64 {
+ let mut prices = self.initialize_prices(params, OptionType::Put);
+ self.backward_induction(&mut prices, params)
+ }
+
+ /// Calculates the delta of the option using the binomial tree model.
+ ///
+ /// # Arguments
+ ///
+ /// * `params` - A reference to `OptionParameters` containing the parameters for the option.
+ ///
+ /// # Returns
+ ///
+ /// The calculated delta.
+ fn delta(&self, params: &OptionParameters) -> f64 {
+ let n = self.steps;
+ let dt = params.t / (n as f64);
+ let u = f64::exp(params.sigma * (dt as f64).sqrt());
+ let d = 1.0 / u;
+
+ let up_params = OptionParameters {
+ s: params.s * u,
+ ..params.clone()
+ };
+ let params = OptionParameters {
+ s: params.s * d,
+ ..params.clone()
+ };
+
+ let down_params = OptionParameters {
+ s: params.s * d,
+ ..params.clone()
+ };
+
+ let delta_up = self.call_price(&up_params);
+ let delta_down = self.call_price(&down_params);
+
+ (delta_up - delta_down) / (params.s * (u - d))
+ }
+
+ /// Calculates the gamma of the option using the binomial tree model.
+ ///
+ /// # Arguments
+ ///
+ /// * `params` - A reference to `OptionParameters` containing the parameters for the option.
+ ///
+ /// # Returns
+ ///
+ /// The calculated gamma.
+ fn gamma(&self, params: &OptionParameters) -> f64 {
+ let n = self.steps;
+ let dt = params.t / (n as f64);
+ let u = f64::exp(params.sigma * (dt as f64).sqrt());
+ let d = 1.0 / u;
+
+ let delta_up = self.delta(&OptionParameters {
+ s: params.s * u,
+ ..params.clone()
+ });
+ let delta_down = self.delta(&OptionParameters {
+ s: params.s * d,
+ ..params.clone()
+ });
+
+ (delta_up - delta_down) / (0.5 * params.s * (u - d))
+ }
+ /// Calculates the theta of the option using the binomial tree model.
+ ///
+ /// # Arguments
+ ///
+ /// * `params` - A reference to `OptionParameters` containing the parameters for the option.
+ ///
+ /// # Returns
+ ///
+ /// The calculated theta.
+ fn theta(&self, params: &OptionParameters) -> f64 {
+ let epsilon = 1e-5;
+ let new_params = OptionParameters {
+ t: params.t - epsilon,
+ ..params.clone()
+ };
+ let call_price_t1 = self.call_price(params);
+ let call_price_t2 = self.call_price(&new_params);
+
+ (call_price_t2 - call_price_t1) / epsilon
+ }
+ /// Calculates the vega of the option using the binomial tree model.
+ ///
+ /// # Arguments
+ ///
+ /// * `params` - A reference to `OptionParameters` containing the parameters for the option.
+ ///
+ /// # Returns
+ ///
+ /// The calcula ted vega.
+ fn vega(&self, params: &OptionParameters) -> f64 {
+ let epsilon = 1e-5;
+
+ let call_price_sigma1 = self.call_price(params);
+ let call_price_sigma2 = self.call_price(&OptionParameters {
+ sigma: params.sigma + epsilon,
+ ..params.clone()
+ });
+
+ (call_price_sigma2 - call_price_sigma1) / epsilon
+ }
+
+ /// Calculates the rho of the option using the binomial tree model.
+ ///
+ /// # Arguments
+ ///
+ /// * `params` - A reference to `OptionParameters` containing the parameters for the option.
+ ///
+ /// # Returns
+ ///
+ /// The calculated rho.
+ fn rho(&self, params: &OptionParameters) -> f64 {
+ let epsilon = 1e-5;
+ let new_params = OptionParameters {
+ r: params.r + epsilon,
+ ..params.clone()
+ };
+ let call_price_r1 = self.call_price(params);
+ let call_price_r2 = self.call_price(&new_params);
+
+ (call_price_r2 - call_price_r1) / epsilon
+ }
+}
diff --git a/core/src/models/black_scholes.rs b/core/src/models/black_scholes.rs
index 1467e76..8beea73 100644
--- a/core/src/models/black_scholes.rs
+++ b/core/src/models/black_scholes.rs
@@ -1,7 +1,7 @@
use crate::models::{OptionParameters, OptionPricingModel};
/// A Black-Scholes model for pricing European call and put options.
-/// ref: https://en.wikipedia.org/wiki/Black–Scholes_model
+/// ref:
pub struct BlackScholesModel;
impl OptionPricingModel for BlackScholesModel {
diff --git a/core/src/models/mod.rs b/core/src/models/mod.rs
index 07a39c5..0f216db 100644
--- a/core/src/models/mod.rs
+++ b/core/src/models/mod.rs
@@ -1,8 +1,11 @@
+pub mod binomial_tree;
pub mod black_scholes;
pub mod monte_carlo;
+
+pub use binomial_tree::BinomialTreeModel;
pub use black_scholes::BlackScholesModel;
pub use monte_carlo::MonteCarloModel;
-/// Parameters for option pricing models. ref: https://www.macroption.com/option-greeks-excel/
+/// Parameters for option pricing models
///
/// # Fields
///
diff --git a/core/tests/binmoia_tree_tests.rs b/core/tests/binmoia_tree_tests.rs
new file mode 100644
index 0000000..7e2e529
--- /dev/null
+++ b/core/tests/binmoia_tree_tests.rs
@@ -0,0 +1,104 @@
+extern crate core;
+use core::models::{BinomialTreeModel, OptionParameters, OptionPricingModel};
+
+#[test]
+fn test_call_price() {
+ let params = OptionParameters {
+ s: 100.0,
+ k: 100.0,
+ r: 0.05,
+ sigma: 0.2,
+ t: 1.0,
+ };
+ let model = BinomialTreeModel::default();
+ let price = model.call_price(¶ms);
+ println!("Call Price: {}", price);
+ assert!(price >= 0.0);
+}
+
+#[test]
+fn test_put_price() {
+ let params = OptionParameters {
+ s: 100.0,
+ k: 100.0,
+ r: 0.05,
+ sigma: 0.2,
+ t: 1.0,
+ };
+ let model = BinomialTreeModel::default();
+ let price = model.put_price(¶ms);
+ println!("Put Price: {}", price);
+ assert!(price >= 0.0);
+}
+
+#[test]
+fn test_delta() {
+ let params = OptionParameters {
+ s: 100.0,
+ k: 100.0,
+ r: 0.05,
+ sigma: 0.2,
+ t: 1.0,
+ };
+ let model = BinomialTreeModel::default();
+ let delta = model.delta(¶ms);
+ println!("Delta: {}", delta);
+ assert!(delta >= -1.0 && delta <= 1.0);
+}
+
+#[test]
+fn test_gamma() {
+ let params = OptionParameters {
+ s: 100.0,
+ k: 100.0,
+ r: 0.05,
+ sigma: 0.2,
+ t: 1.0,
+ };
+ let model = BinomialTreeModel::default();
+ let gamma = model.gamma(¶ms);
+ assert!(gamma >= 0.0);
+}
+
+#[test]
+fn test_theta() {
+ let params = OptionParameters {
+ s: 100.0,
+ k: 100.0,
+ r: 0.05,
+ sigma: 0.2,
+ t: 1.0,
+ };
+ let model = BinomialTreeModel::default();
+ let theta = model.theta(¶ms);
+ println!("Theta: {}", theta);
+ assert!(theta <= 0.0);
+}
+
+#[test]
+fn test_vega() {
+ let params = OptionParameters {
+ s: 100.0,
+ k: 100.0,
+ r: 0.05,
+ sigma: 0.2,
+ t: 1.0,
+ };
+ let model = BinomialTreeModel::default();
+ let vega = model.vega(¶ms);
+ assert!(vega >= 0.0);
+}
+
+#[test]
+fn test_rho() {
+ let params = OptionParameters {
+ s: 100.0,
+ k: 100.0,
+ r: 0.05,
+ sigma: 0.2,
+ t: 1.0,
+ };
+ let model = BinomialTreeModel::default();
+ let rho = model.rho(¶ms);
+ assert!(rho >= 0.0);
+}