From 664fa06b78945c64fc64177b83960a56918676ac Mon Sep 17 00:00:00 2001
From: Andy Pack <andy@sarsoo.xyz>
Date: Sun, 16 Feb 2025 23:36:55 +0000
Subject: [PATCH 1/6] working on black scholes options modelling

---
 finlib/src/lib.rs                      |   1 +
 finlib/src/options/blackscholes/mod.rs | 245 +++++++++++++++++++++++++
 finlib/src/options/mod.rs              |   1 +
 3 files changed, 247 insertions(+)
 create mode 100644 finlib/src/options/blackscholes/mod.rs
 create mode 100644 finlib/src/options/mod.rs

diff --git a/finlib/src/lib.rs b/finlib/src/lib.rs
index 7dc8d0d..5024e04 100644
--- a/finlib/src/lib.rs
+++ b/finlib/src/lib.rs
@@ -5,6 +5,7 @@ pub mod stats;
 pub mod util;
 pub mod risk;
 pub mod ffi;
+pub mod options;
 
 #[cfg(feature = "parallel")]
 use rayon::prelude::*;
diff --git a/finlib/src/options/blackscholes/mod.rs b/finlib/src/options/blackscholes/mod.rs
new file mode 100644
index 0000000..ec2af63
--- /dev/null
+++ b/finlib/src/options/blackscholes/mod.rs
@@ -0,0 +1,245 @@
+use statrs::distribution::{ContinuousCDF, Normal};
+
+#[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd)]
+pub struct OptionVariables {
+    underlying_price: f64,
+    strike_price: f64,
+    volatility: f64,
+    risk_free_interest_rate: f64,
+    dividend: f64,
+    time_to_expiration: f64
+}
+
+impl OptionVariables {
+
+    pub fn from(underlying_price: f64,
+                strike_price: f64,
+                volatility: f64,
+                risk_free_interest_rate: f64,
+                dividend: f64,
+                time_to_expiration: f64) -> Self {
+        Self {
+            underlying_price,
+            strike_price,
+            volatility,
+            risk_free_interest_rate,
+            dividend,
+            time_to_expiration,
+        }
+    }
+
+    pub fn call(self) -> CallOption {
+        let n = Normal::new(0., 1.0).unwrap();
+        let (d1, d2) = self.d1_d2();
+
+        let first = self.underlying_price * (-self.dividend * self.time_to_expiration).exp() * n.cdf(d1);
+
+        let second = self.strike_price * (-self.risk_free_interest_rate * self.time_to_expiration).exp() * n.cdf(d2);
+
+        CallOption::from(first - second, self)
+    }
+
+
+    pub fn put(self) -> PutOption {
+        let n = Normal::new(0., 1.0).unwrap();
+        let (d1, d2) = self.d1_d2();
+
+        let first = self.strike_price * (-self.risk_free_interest_rate * self.time_to_expiration).exp() * n.cdf(-d2);
+
+        let second = self.underlying_price * (-self.dividend * self.time_to_expiration).exp() * n.cdf(-d1);
+
+        PutOption::from(first - second, self)
+    }
+
+    pub fn d1_d2(&self) -> (f64, f64) {
+        let d1 = self.d1();
+
+        (d1, self.d2(d1))
+    }
+
+    pub fn d1(&self) -> f64 {
+
+        let first = (self.underlying_price / self.strike_price).log(std::f64::consts::E);
+
+        let second = self.time_to_expiration * (self.risk_free_interest_rate - self.dividend + (f64::powi(self.volatility, 2) / 2.));
+
+        let denominator = self.volatility * f64::sqrt(self.time_to_expiration);
+
+        (first + second) / denominator
+    }
+
+    pub fn d2(&self, d1: f64, ) -> f64 {
+        d1 - (self.volatility * f64::sqrt(self.time_to_expiration))
+    }
+}
+
+pub trait Option {
+    fn delta(&self) -> f64;
+    fn gamma(&self) -> f64;
+    fn vega(&self) -> f64;
+    fn theta(&self) -> f64;
+    fn rho(&self) -> f64;
+}
+
+#[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd)]
+pub struct CallOption {
+    pub price: f64,
+    pub variables: OptionVariables
+}
+
+impl CallOption {
+    pub fn from(price: f64, variables: OptionVariables) -> Self {
+        Self { price, variables }
+    }
+}
+
+impl Option for CallOption {
+    fn delta(&self) -> f64 {
+        let n = Normal::new(0., 1.0).unwrap();
+
+        (-self.variables.dividend * self.variables.time_to_expiration).exp() * n.cdf(self.variables.d1())
+    }
+
+    fn gamma(&self) -> f64 {
+        gamma(&self.variables)
+    }
+
+    fn vega(&self) -> f64 {
+        vega(&self.variables)
+    }
+
+    fn theta(&self) -> f64 {
+        todo!()
+    }
+
+    fn rho(&self) -> f64 {
+        todo!()
+    }
+}
+
+#[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd)]
+pub struct PutOption {
+    pub price: f64,
+    pub variables: OptionVariables
+}
+
+impl PutOption {
+    pub fn from(price: f64, variables: OptionVariables) -> Self {
+        Self { price, variables }
+    }
+}
+
+impl Option for PutOption {
+    fn delta(&self) -> f64 {
+        let n = Normal::new(0., 1.0).unwrap();
+
+        (-self.variables.dividend * self.variables.time_to_expiration).exp() * (n.cdf(self.variables.d1()) - 1.)
+    }
+
+    fn gamma(&self) -> f64 {
+        gamma(&self.variables)
+    }
+
+    fn vega(&self) -> f64 {
+        vega(&self.variables)
+    }
+
+    fn theta(&self) -> f64 {
+        todo!()
+    }
+
+    fn rho(&self) -> f64 {
+        todo!()
+    }
+}
+
+pub fn gamma(v: &OptionVariables) -> f64 {
+    let n = Normal::new(0., 1.0).unwrap();
+
+    let numerator = (-v.dividend * v.time_to_expiration).exp();
+    let denominator = v.underlying_price * v.volatility * f64::sqrt(v.time_to_expiration);
+
+    (numerator / denominator) * n.cdf(v.d1())
+}
+
+pub fn vega(v: &OptionVariables) -> f64 {
+    let n = Normal::new(0., 1.0).unwrap();
+
+    let numerator = (-v.dividend * v.time_to_expiration).exp();
+
+    v.underlying_price * numerator * f64::sqrt(v.time_to_expiration) * n.cdf(v.d1()) / 100.
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn call_test() {
+        let v = OptionVariables::from(100., 100., 0.25, 0.05, 0.01, 30./365.25);
+
+        let diff = (v.call().price - 3.019).abs();
+
+        assert!(diff < 0.01);
+    }
+
+    #[test]
+    fn put_test() {
+        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+
+        let diff = (v.put().price - 2.691).abs();
+        assert!(diff < 0.01);
+    }
+
+    #[test]
+    fn call_delta_test() {
+        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+
+        let diff = (v.call().delta() - 0.532).abs();
+        assert!(diff < 0.01);
+    }
+
+    #[test]
+    fn put_delta_test() {
+        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+
+        let delta = v.put().delta();
+        let diff = (delta - -0.467).abs();
+        assert!(diff < 0.01);
+    }
+
+    #[test]
+    fn gamma_test() {
+        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+
+        let gamma = v.put().gamma();
+        let diff = (gamma - 0.055).abs();
+        assert!(diff < 0.01);
+    }
+
+    #[test]
+    fn vega_test() {
+        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+
+        let vega = v.put().vega();
+        let diff = (vega - 11.390).abs();
+        assert!(diff < 0.01);
+    }
+
+    #[test]
+    fn call_rho_test() {
+        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+
+        let diff = (v.call().rho() - 4.126).abs();
+        assert!(diff < 0.01);
+    }
+
+    #[test]
+    fn put_rho_test() {
+        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+
+        let rho = v.put().rho();
+        let diff = (rho - -4.060).abs();
+        assert!(diff < 0.01);
+    }
+}
\ No newline at end of file
diff --git a/finlib/src/options/mod.rs b/finlib/src/options/mod.rs
new file mode 100644
index 0000000..56dedb3
--- /dev/null
+++ b/finlib/src/options/mod.rs
@@ -0,0 +1 @@
+pub mod blackscholes;
\ No newline at end of file

From a0a672cf0d8cf6f8c8090c707796c99359576b03 Mon Sep 17 00:00:00 2001
From: Andy Pack <andy@sarsoo.xyz>
Date: Mon, 17 Feb 2025 00:08:51 +0000
Subject: [PATCH 2/6] tweaking options variable handling

---
 finlib/src/options/blackscholes/mod.rs | 38 ++++++++++++++++----------
 1 file changed, 23 insertions(+), 15 deletions(-)

diff --git a/finlib/src/options/blackscholes/mod.rs b/finlib/src/options/blackscholes/mod.rs
index ec2af63..a45eba5 100644
--- a/finlib/src/options/blackscholes/mod.rs
+++ b/finlib/src/options/blackscholes/mod.rs
@@ -7,7 +7,8 @@ pub struct OptionVariables {
     volatility: f64,
     risk_free_interest_rate: f64,
     dividend: f64,
-    time_to_expiration: f64
+    time_to_expiration: f64,
+    d1: std::option::Option<f64>
 }
 
 impl OptionVariables {
@@ -25,12 +26,14 @@ impl OptionVariables {
             risk_free_interest_rate,
             dividend,
             time_to_expiration,
+            d1: None
         }
     }
 
-    pub fn call(self) -> CallOption {
+    pub fn call(mut self) -> CallOption {
         let n = Normal::new(0., 1.0).unwrap();
         let (d1, d2) = self.d1_d2();
+        self.d1 = Some(d1);
 
         let first = self.underlying_price * (-self.dividend * self.time_to_expiration).exp() * n.cdf(d1);
 
@@ -40,9 +43,10 @@ impl OptionVariables {
     }
 
 
-    pub fn put(self) -> PutOption {
+    pub fn put(mut self) -> PutOption {
         let n = Normal::new(0., 1.0).unwrap();
         let (d1, d2) = self.d1_d2();
+        self.d1 = Some(d1);
 
         let first = self.strike_price * (-self.risk_free_interest_rate * self.time_to_expiration).exp() * n.cdf(-d2);
 
@@ -97,7 +101,7 @@ impl Option for CallOption {
     fn delta(&self) -> f64 {
         let n = Normal::new(0., 1.0).unwrap();
 
-        (-self.variables.dividend * self.variables.time_to_expiration).exp() * n.cdf(self.variables.d1())
+        (-self.variables.dividend * self.variables.time_to_expiration).exp() * n.cdf(self.variables.d1.unwrap())
     }
 
     fn gamma(&self) -> f64 {
@@ -133,7 +137,7 @@ impl Option for PutOption {
     fn delta(&self) -> f64 {
         let n = Normal::new(0., 1.0).unwrap();
 
-        (-self.variables.dividend * self.variables.time_to_expiration).exp() * (n.cdf(self.variables.d1()) - 1.)
+        (-self.variables.dividend * self.variables.time_to_expiration).exp() * (n.cdf(self.variables.d1.unwrap()) - 1.)
     }
 
     fn gamma(&self) -> f64 {
@@ -159,7 +163,7 @@ pub fn gamma(v: &OptionVariables) -> f64 {
     let numerator = (-v.dividend * v.time_to_expiration).exp();
     let denominator = v.underlying_price * v.volatility * f64::sqrt(v.time_to_expiration);
 
-    (numerator / denominator) * n.cdf(v.d1())
+    (numerator / denominator) * n.cdf(v.d1.unwrap())
 }
 
 pub fn vega(v: &OptionVariables) -> f64 {
@@ -167,16 +171,20 @@ pub fn vega(v: &OptionVariables) -> f64 {
 
     let numerator = (-v.dividend * v.time_to_expiration).exp();
 
-    v.underlying_price * numerator * f64::sqrt(v.time_to_expiration) * n.cdf(v.d1()) / 100.
+    v.underlying_price * numerator * f64::sqrt(v.time_to_expiration) * n.cdf(v.d1.unwrap()) / 100.
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
 
+    fn get_example_option() -> OptionVariables {
+        OptionVariables::from(100., 100., 0.25, 0.05, 0.01, 30./365.25)
+    }
+
     #[test]
     fn call_test() {
-        let v = OptionVariables::from(100., 100., 0.25, 0.05, 0.01, 30./365.25);
+        let v = get_example_option();
 
         let diff = (v.call().price - 3.019).abs();
 
@@ -185,7 +193,7 @@ mod tests {
 
     #[test]
     fn put_test() {
-        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+        let v = get_example_option();
 
         let diff = (v.put().price - 2.691).abs();
         assert!(diff < 0.01);
@@ -193,7 +201,7 @@ mod tests {
 
     #[test]
     fn call_delta_test() {
-        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+        let v = get_example_option();
 
         let diff = (v.call().delta() - 0.532).abs();
         assert!(diff < 0.01);
@@ -201,7 +209,7 @@ mod tests {
 
     #[test]
     fn put_delta_test() {
-        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+        let v = get_example_option();
 
         let delta = v.put().delta();
         let diff = (delta - -0.467).abs();
@@ -210,7 +218,7 @@ mod tests {
 
     #[test]
     fn gamma_test() {
-        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+        let v = get_example_option();
 
         let gamma = v.put().gamma();
         let diff = (gamma - 0.055).abs();
@@ -219,7 +227,7 @@ mod tests {
 
     #[test]
     fn vega_test() {
-        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+        let v = get_example_option();
 
         let vega = v.put().vega();
         let diff = (vega - 11.390).abs();
@@ -228,7 +236,7 @@ mod tests {
 
     #[test]
     fn call_rho_test() {
-        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+        let v = get_example_option();
 
         let diff = (v.call().rho() - 4.126).abs();
         assert!(diff < 0.01);
@@ -236,7 +244,7 @@ mod tests {
 
     #[test]
     fn put_rho_test() {
-        let v = OptionVariables::from(100., 100.,0.25, 0.05, 0.01, 30./365.25);
+        let v = get_example_option();
 
         let rho = v.put().rho();
         let diff = (rho - -4.060).abs();

From 46617620a4a7ffd273b6982e91af04542e122e3b Mon Sep 17 00:00:00 2001
From: Andy Pack <andy@sarsoo.xyz>
Date: Mon, 17 Feb 2025 22:13:50 +0000
Subject: [PATCH 3/6] fixing and finishing greeks

---
 finlib/src/ffi/py/mod.rs                      |   3 +-
 finlib/src/ffi/py/options.rs                  |  21 +++
 .../src/options/blackscholes/OptionSurface.rs | 174 ++++++++++++++++++
 finlib/src/options/blackscholes/mod.rs        | 147 +++++++++++++--
 pyfinlib/src/lib.rs                           |  14 ++
 5 files changed, 344 insertions(+), 15 deletions(-)
 create mode 100644 finlib/src/ffi/py/options.rs
 create mode 100644 finlib/src/options/blackscholes/OptionSurface.rs

diff --git a/finlib/src/ffi/py/mod.rs b/finlib/src/ffi/py/mod.rs
index 20e1ebf..8f7a9c1 100644
--- a/finlib/src/ffi/py/mod.rs
+++ b/finlib/src/ffi/py/mod.rs
@@ -1 +1,2 @@
-pub mod portfolio;
\ No newline at end of file
+pub mod portfolio;
+pub mod options;
\ No newline at end of file
diff --git a/finlib/src/ffi/py/options.rs b/finlib/src/ffi/py/options.rs
new file mode 100644
index 0000000..aab186b
--- /dev/null
+++ b/finlib/src/ffi/py/options.rs
@@ -0,0 +1,21 @@
+use pyo3::prelude::*;
+use crate::options::blackscholes::{OptionVariables, OptionGreeks};
+use crate::risk::portfolio::{Portfolio, PortfolioAsset};
+
+#[pymethods]
+impl OptionVariables {
+    #[new]
+    pub fn init(underlying_price: f64,
+                strike_price: f64,
+                volatility: f64,
+                risk_free_interest_rate: f64,
+                dividend: f64,
+                time_to_expiration: f64) -> Self {
+        OptionVariables::from(underlying_price,
+                              strike_price,
+                              volatility,
+                              risk_free_interest_rate,
+                              dividend,
+                              time_to_expiration)
+    }
+}
\ No newline at end of file
diff --git a/finlib/src/options/blackscholes/OptionSurface.rs b/finlib/src/options/blackscholes/OptionSurface.rs
new file mode 100644
index 0000000..bcb5c3e
--- /dev/null
+++ b/finlib/src/options/blackscholes/OptionSurface.rs
@@ -0,0 +1,174 @@
+use core::ops::Range;
+use std::sync::{Arc, Mutex};
+#[cfg(feature = "parallel")]
+use rayon::prelude::*;
+use crate::options::blackscholes::OptionVariables;
+
+pub struct OptionSurface {
+    underlying_price: Range<isize>,
+    underlying_price_bounds: (f64, f64),
+    strike_price: Range<isize>,
+    strike_price_bounds: (f64, f64),
+    volatility: Range<isize>,
+    volatility_bounds: (f64, f64),
+    risk_free_interest_rate: Range<isize>,
+    risk_free_interest_rate_bounds: (f64, f64),
+    dividend: Range<isize>,
+    dividend_bounds: (f64, f64),
+    time_to_expiration: Range<isize>,
+    time_to_expiration_bounds: (f64, f64)
+}
+
+impl OptionSurface {
+    pub fn from(underlying_price: Range<isize>,
+                underlying_price_bounds: (f64, f64),
+                strike_price: Range<isize>,
+                strike_price_bounds: (f64, f64),
+                volatility: Range<isize>,
+                volatility_bounds: (f64, f64),
+                risk_free_interest_rate: Range<isize>,
+                risk_free_interest_rate_bounds: (f64, f64),
+                dividend: Range<isize>,
+                dividend_bounds: (f64, f64),
+                time_to_expiration: Range<isize>,
+                time_to_expiration_bounds: (f64, f64)) -> Self {
+        Self {
+            underlying_price,
+            underlying_price_bounds,
+            strike_price,
+            strike_price_bounds,
+            volatility,
+            volatility_bounds,
+            risk_free_interest_rate,
+            risk_free_interest_rate_bounds,
+            dividend,
+            dividend_bounds,
+            time_to_expiration,
+            time_to_expiration_bounds,
+        }
+    }
+
+    pub fn walk(self) -> Vec<OptionVariables> {
+
+        // #[cfg(feature = "parallel")]
+        // {
+        //     let vec: Arc<Mutex<Vec<OptionVariables>>> = Arc::new(Mutex::new(vec![]));
+        //     self.underlying_price
+        //         .into_par_iter()
+        //         .for_each(|p| {
+        //             self.strike_price
+        //                 .clone()
+        //                 .into_par_iter()
+        //                 .for_each(|s| {
+        //                     self.volatility
+        //                         .clone()
+        //                         .into_par_iter()
+        //                         .for_each(|v| {
+        //                             self.risk_free_interest_rate
+        //                                 .clone()
+        //                                 .into_par_iter()
+        //                                 .for_each(|i| {
+        //                                     self.dividend
+        //                                         .clone()
+        //                                         .into_par_iter()
+        //                                         .for_each(|d| {
+        //                                             self.time_to_expiration
+        //                                                 .clone()
+        //                                                 .into_par_iter()
+        //                                                 .for_each(|t| {
+        //                                                     let mut m = vec.clone();
+        //                                                     let mut guard = m.lock().unwrap();
+        //                                                     guard.push(OptionVariables::from(
+        //                                                         self.underlying_price_bounds.0 + (self.underlying_price_bounds.1 - self.underlying_price_bounds.0) * p as f64,
+        //                                                         self.strike_price_bounds.0 + (self.strike_price_bounds.1 - self.strike_price_bounds.0) * s as f64,
+        //                                                         self.volatility_bounds.0 + (self.volatility_bounds.1 - self.volatility_bounds.0) * v as f64,
+        //                                                         self.risk_free_interest_rate_bounds.0 + (self.risk_free_interest_rate_bounds.1 - self.risk_free_interest_rate_bounds.0) * i as f64,
+        //                                                         self.dividend_bounds.0 + (self.dividend_bounds.1 - self.dividend_bounds.0) * d as f64,
+        //                                                         self.time_to_expiration_bounds.0 + (self.time_to_expiration_bounds.1 - self.time_to_expiration_bounds.0) * t as f64
+        //                                                     ));
+        //                                                 })
+        //                                         })
+        //                                 })
+        //                         })
+        //                  })
+        //         });
+        //
+        //     Arc::try_unwrap(vec).unwrap().into_inner().unwrap()
+        // }
+        // #[cfg(not(feature = "parallel"))]
+        {
+            let mut vec: Vec<OptionVariables> = Vec::with_capacity(
+                self.underlying_price.len()
+                * self.strike_price.len()
+                * self.volatility.len()
+                * self.risk_free_interest_rate.len()
+                * self.dividend.len()
+                * self.time_to_expiration.len()
+            );
+            for p in self.underlying_price {
+                for s in self.strike_price.clone() {
+                    for v in self.volatility.clone() {
+                        for i in self.risk_free_interest_rate.clone() {
+                            for d in self.dividend.clone() {
+                                for t in self.time_to_expiration.clone() {
+                                    let v = OptionVariables::from(
+                                        self.underlying_price_bounds.0 + (self.underlying_price_bounds.1 - self.underlying_price_bounds.0) * p as f64,
+                                        self.strike_price_bounds.0 + (self.strike_price_bounds.1 - self.strike_price_bounds.0) * s as f64,
+                                        self.volatility_bounds.0 + (self.volatility_bounds.1 - self.volatility_bounds.0) * v as f64,
+                                        self.risk_free_interest_rate_bounds.0 + (self.risk_free_interest_rate_bounds.1 - self.risk_free_interest_rate_bounds.0) * i as f64,
+                                        self.dividend_bounds.0 + (self.dividend_bounds.1 - self.dividend_bounds.0) * d as f64,
+                                        self.time_to_expiration_bounds.0 + (self.time_to_expiration_bounds.1 - self.time_to_expiration_bounds.0) * t as f64
+                                    );
+                                    vec.push(v);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            vec
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::options::blackscholes::{CallOption, Option, PutOption};
+    use super::*;
+
+    #[test]
+    fn walk_test() {
+        let w = OptionSurface::from(
+            (0 .. 50),
+            (100., 200.),
+            (0 .. 50),
+            (100., 200.),
+            (0 .. 5),
+            (0.25, 0.50),
+            (0 .. 10),
+            (0.05, 0.08),
+            (0 .. 1),
+            (0.01, 0.02),
+            (0 .. 10),
+            (30./365.25, 30./365.25),
+        );
+
+        let a = w.walk();
+
+        let options = a
+            .par_iter()
+            .map(|v| {
+                let mut call = v.call();
+                let mut put = v.put();
+
+                call.calc_greeks();
+                put.calc_greeks();
+
+                (call, put)
+            })
+            .collect::<Vec<(CallOption, PutOption)>>();
+
+        let a1 = a.first();
+    }
+}
\ No newline at end of file
diff --git a/finlib/src/options/blackscholes/mod.rs b/finlib/src/options/blackscholes/mod.rs
index a45eba5..7235352 100644
--- a/finlib/src/options/blackscholes/mod.rs
+++ b/finlib/src/options/blackscholes/mod.rs
@@ -1,5 +1,14 @@
-use statrs::distribution::{ContinuousCDF, Normal};
+mod OptionSurface;
 
+use statrs::distribution::{Continuous, ContinuousCDF, Normal};
+#[cfg(feature = "wasm")]
+use wasm_bindgen::prelude::*;
+#[cfg(feature = "py")]
+use pyo3::prelude::*;
+
+#[cfg_attr(feature = "wasm", wasm_bindgen)]
+#[cfg_attr(feature = "py", pyclass)]
+#[cfg_attr(feature = "ffi", repr(C))]
 #[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd)]
 pub struct OptionVariables {
     underlying_price: f64,
@@ -8,7 +17,8 @@ pub struct OptionVariables {
     risk_free_interest_rate: f64,
     dividend: f64,
     time_to_expiration: f64,
-    d1: std::option::Option<f64>
+    d1: std::option::Option<f64>,
+    d2: std::option::Option<f64>
 }
 
 impl OptionVariables {
@@ -26,7 +36,8 @@ impl OptionVariables {
             risk_free_interest_rate,
             dividend,
             time_to_expiration,
-            d1: None
+            d1: None,
+            d2: None
         }
     }
 
@@ -34,6 +45,7 @@ impl OptionVariables {
         let n = Normal::new(0., 1.0).unwrap();
         let (d1, d2) = self.d1_d2();
         self.d1 = Some(d1);
+        self.d2 = Some(d2);
 
         let first = self.underlying_price * (-self.dividend * self.time_to_expiration).exp() * n.cdf(d1);
 
@@ -47,6 +59,7 @@ impl OptionVariables {
         let n = Normal::new(0., 1.0).unwrap();
         let (d1, d2) = self.d1_d2();
         self.d1 = Some(d1);
+        self.d2 = Some(d2);
 
         let first = self.strike_price * (-self.risk_free_interest_rate * self.time_to_expiration).exp() * n.cdf(-d2);
 
@@ -72,7 +85,7 @@ impl OptionVariables {
         (first + second) / denominator
     }
 
-    pub fn d2(&self, d1: f64, ) -> f64 {
+    pub fn d2(&self, d1: f64) -> f64 {
         d1 - (self.volatility * f64::sqrt(self.time_to_expiration))
     }
 }
@@ -83,17 +96,23 @@ pub trait Option {
     fn vega(&self) -> f64;
     fn theta(&self) -> f64;
     fn rho(&self) -> f64;
+    fn calc_greeks(&mut self);
+    fn has_greeks(&self) -> bool;
 }
 
+// #[cfg_attr(feature = "wasm", wasm_bindgen)]
+#[cfg_attr(feature = "py", pyclass)]
+#[cfg_attr(feature = "ffi", repr(C))]
 #[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd)]
 pub struct CallOption {
     pub price: f64,
-    pub variables: OptionVariables
+    pub variables: OptionVariables,
+    pub greeks: std::option::Option<OptionGreeks>
 }
 
 impl CallOption {
     pub fn from(price: f64, variables: OptionVariables) -> Self {
-        Self { price, variables }
+        Self { price, variables, greeks: None }
     }
 }
 
@@ -113,23 +132,50 @@ impl Option for CallOption {
     }
 
     fn theta(&self) -> f64 {
-        todo!()
+        let n = Normal::new(0., 1.0).unwrap();
+        let first = theta_first(&self.variables, &n);
+
+        let second = self.variables.risk_free_interest_rate
+            * self.variables.strike_price
+            * (-self.variables.risk_free_interest_rate * self.variables.time_to_expiration).exp()
+            * n.cdf(self.variables.d2.unwrap());
+
+        let third = self.variables.dividend
+            * self.variables.underlying_price
+            * (-self.variables.dividend * self.variables.time_to_expiration).exp()
+            * n.cdf(self.variables.d1.unwrap());
+
+        first - second + third
     }
 
     fn rho(&self) -> f64 {
-        todo!()
+        let n = Normal::new(0., 1.0).unwrap();
+
+        self.variables.strike_price * self.variables.time_to_expiration * (-self.variables.risk_free_interest_rate * self.variables.time_to_expiration).exp() * n.cdf(self.variables.d2.unwrap())
+    }
+
+    fn calc_greeks(&mut self) {
+        self.greeks = Some(OptionGreeks::from(self));
+    }
+
+    fn has_greeks(&self) -> bool {
+        self.greeks.is_some()
     }
 }
 
+// #[cfg_attr(feature = "wasm", wasm_bindgen)]
+#[cfg_attr(feature = "py", pyclass)]
+#[cfg_attr(feature = "ffi", repr(C))]
 #[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd)]
 pub struct PutOption {
     pub price: f64,
-    pub variables: OptionVariables
+    pub variables: OptionVariables,
+    pub greeks: std::option::Option<OptionGreeks>
 }
 
 impl PutOption {
     pub fn from(price: f64, variables: OptionVariables) -> Self {
-        Self { price, variables }
+        Self { price, variables, greeks: None }
     }
 }
 
@@ -149,12 +195,42 @@ impl Option for PutOption {
     }
 
     fn theta(&self) -> f64 {
-        todo!()
+        let n = Normal::new(0., 1.0).unwrap();
+        let first = theta_first(&self.variables, &n);
+
+        let second = self.variables.risk_free_interest_rate
+            * self.variables.strike_price
+            * (-self.variables.risk_free_interest_rate * self.variables.time_to_expiration).exp()
+            * n.cdf(-self.variables.d2.unwrap());
+
+        let third = self.variables.dividend
+            * self.variables.underlying_price
+            * (-self.variables.dividend * self.variables.time_to_expiration).exp()
+            * n.cdf(-self.variables.d1.unwrap());
+
+        first + second - third
     }
 
     fn rho(&self) -> f64 {
-        todo!()
+        let n = Normal::new(0., 1.0).unwrap();
+
+        - self.variables.strike_price * self.variables.time_to_expiration * (-self.variables.risk_free_interest_rate * self.variables.time_to_expiration).exp() * n.cdf(-self.variables.d2.unwrap())
     }
+
+    fn calc_greeks(&mut self) {
+        self.greeks = Some(OptionGreeks::from(self));
+    }
+
+    fn has_greeks(&self) -> bool {
+        self.greeks.is_some()
+    }
+}
+
+fn theta_first(v: &OptionVariables, n: &Normal) -> f64 {
+    let numerator = v.underlying_price * v.volatility * (-v.dividend * v.time_to_expiration).exp();
+    let denominator = 2. * f64::sqrt(v.time_to_expiration);
+
+    -(numerator / denominator) * n.pdf(v.d1.unwrap())
 }
 
 pub fn gamma(v: &OptionVariables) -> f64 {
@@ -163,7 +239,7 @@ pub fn gamma(v: &OptionVariables) -> f64 {
     let numerator = (-v.dividend * v.time_to_expiration).exp();
     let denominator = v.underlying_price * v.volatility * f64::sqrt(v.time_to_expiration);
 
-    (numerator / denominator) * n.cdf(v.d1.unwrap())
+    (numerator / denominator) * n.pdf(v.d1.unwrap())
 }
 
 pub fn vega(v: &OptionVariables) -> f64 {
@@ -171,13 +247,39 @@ pub fn vega(v: &OptionVariables) -> f64 {
 
     let numerator = (-v.dividend * v.time_to_expiration).exp();
 
-    v.underlying_price * numerator * f64::sqrt(v.time_to_expiration) * n.cdf(v.d1.unwrap()) / 100.
+    v.underlying_price * numerator * f64::sqrt(v.time_to_expiration) * n.pdf(v.d1.unwrap())
+}
+
+#[cfg_attr(feature = "wasm", wasm_bindgen)]
+#[cfg_attr(feature = "py", pyclass)]
+#[cfg_attr(feature = "ffi", repr(C))]
+#[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd)]
+pub struct OptionGreeks {
+    delta: f64,
+    gamma: f64,
+    vega: f64,
+    theta: f64,
+    rho: f64
+}
+
+impl OptionGreeks {
+    pub fn from(option: &impl Option) -> Self {
+        Self {
+            delta: option.delta(),
+            gamma: option.gamma(),
+            vega: option.vega(),
+            theta: option.theta(),
+            rho: option.rho()
+        }
+    }
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
 
+    // https://goodcalculators.com/black-scholes-calculator/
+
     fn get_example_option() -> OptionVariables {
         OptionVariables::from(100., 100., 0.25, 0.05, 0.01, 30./365.25)
     }
@@ -250,4 +352,21 @@ mod tests {
         let diff = (rho - -4.060).abs();
         assert!(diff < 0.01);
     }
+
+    #[test]
+    fn call_theta_test() {
+        let v = get_example_option();
+
+        let diff = (v.call().theta() - -19.300).abs();
+        assert!(diff < 0.01);
+    }
+
+    #[test]
+    fn put_theta_test() {
+        let v = get_example_option();
+
+        let theta = v.put().theta();
+        let diff = (theta - -15.319).abs();
+        assert!(diff < 0.01);
+    }
 }
\ No newline at end of file
diff --git a/pyfinlib/src/lib.rs b/pyfinlib/src/lib.rs
index 173cf4c..285e481 100644
--- a/pyfinlib/src/lib.rs
+++ b/pyfinlib/src/lib.rs
@@ -25,6 +25,20 @@ mod pyfinlib {
         }
     }
 
+    #[pymodule]
+    mod options {
+        use super::*;
+
+        #[pymodule_export]
+        use finlib::options::blackscholes::OptionVariables;
+        #[pymodule_export]
+        use finlib::options::blackscholes::CallOption;
+        #[pymodule_export]
+        use finlib::options::blackscholes::PutOption;
+        #[pymodule_export]
+        use finlib::options::blackscholes::OptionGreeks;
+    }
+
     #[pymodule]
     mod risk {
         use super::*;

From 30803bbee72b54ddbb720e532623f7c63c3bdcc2 Mon Sep 17 00:00:00 2001
From: Andy Pack <andy@sarsoo.xyz>
Date: Mon, 17 Feb 2025 22:15:08 +0000
Subject: [PATCH 4/6] adding dotnet building to ci, wildcard dotnet dll copying

---
 .gitea/workflows/build.yml      | 37 +++++++++++++++++++++++++++++++++
 .github/workflows/build.yml     | 35 +++++++++++++++++++++++++++++++
 FinLib.NET/FinLib/FinLib.csproj |  4 ++--
 3 files changed, 74 insertions(+), 2 deletions(-)

diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml
index b07b7b4..26516b9 100644
--- a/.gitea/workflows/build.yml
+++ b/.gitea/workflows/build.yml
@@ -56,6 +56,43 @@ jobs:
         working-directory: ./pyfinlib
         run: maturin build
 
+  buildNET:
+    name: Build .NET
+    runs-on: ubuntu-latest
+    needs: [ build ] # for ignoring bad builds
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          github-server-url: https://gitea.sheep-ghoul.ts.net
+
+      - name: Install Rust
+        uses: actions-rs/toolchain@v1
+        with:
+          toolchain: stable
+
+      - name: Cargo Build
+        uses: actions-rs/cargo@v1
+        with:
+          command: build
+
+      - name: Setup .NET Core SDK 9.0.x
+        uses: actions/setup-dotnet@v3.0.3
+        with:
+          dotnet-version: 9.0.x
+
+      - name: Install Dependencies
+        working-directory: ./FinLib.NET
+        run: dotnet restore
+
+      - name: Build
+        working-directory: ./FinLib.NET
+        run: dotnet build --configuration Debug --no-restore
+
+      - name: Test
+        working-directory: ./FinLib.NET
+        run: dotnet test --no-restore
+
   buildWASM:
     name: Build WASM
     runs-on: ubuntu-latest
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index da953ff..3709335 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -64,6 +64,41 @@ jobs:
         working-directory: ./pyfinlib
         run: maturin build
 
+  buildNET:
+    name: Build .NET
+    runs-on: ubuntu-latest
+    needs: [ build ] # for ignoring bad builds
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Install Rust
+        uses: actions-rs/toolchain@v1
+        with:
+          toolchain: stable
+
+      - name: Cargo Build
+        uses: actions-rs/cargo@v1
+        with:
+          command: build
+
+      - name: Setup .NET Core SDK 9.0.x
+        uses: actions/setup-dotnet@v3.0.3
+        with:
+          dotnet-version: 9.0.x
+
+      - name: Install Dependencies
+        working-directory: ./FinLib.NET
+        run: dotnet restore
+
+      - name: Build
+        working-directory: ./FinLib.NET
+        run: dotnet build --configuration Debug --no-restore
+
+      - name: Test
+        working-directory: ./FinLib.NET
+        run: dotnet test --no-restore
+
   buildWASM:
     name: Build WASM
     runs-on: ubuntu-latest
diff --git a/FinLib.NET/FinLib/FinLib.csproj b/FinLib.NET/FinLib/FinLib.csproj
index c7e30a4..28d7541 100644
--- a/FinLib.NET/FinLib/FinLib.csproj
+++ b/FinLib.NET/FinLib/FinLib.csproj
@@ -13,13 +13,13 @@
   </PropertyGroup>
 
   <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <Content Include="..\..\target\debug\libfinlib_ffi.dylib">
+    <Content Include="..\..\target\debug\libfinlib_ffi.*">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
   </ItemGroup>
 
   <ItemGroup Condition=" '$(Configuration)' == 'Release' ">
-    <Content Include="..\..\target\release\libfinlib_ffi.dylib">
+    <Content Include="..\..\target\release\libfinlib_ffi.*">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
   </ItemGroup>

From 10e429306b651bcd8a02d6f743a6a3067ca68171 Mon Sep 17 00:00:00 2001
From: Andy Pack <andy@sarsoo.xyz>
Date: Mon, 17 Feb 2025 22:32:33 +0000
Subject: [PATCH 5/6] publishing .net library

---
 .gitea/workflows/build.yml | 40 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml
index 26516b9..0aa4d26 100644
--- a/.gitea/workflows/build.yml
+++ b/.gitea/workflows/build.yml
@@ -93,6 +93,46 @@ jobs:
         working-directory: ./FinLib.NET
         run: dotnet test --no-restore
 
+  publishNET:
+    name: Build .NET
+    runs-on: ubuntu-latest
+    needs: [ buildNET ] # for ignoring bad builds
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          github-server-url: https://gitea.sheep-ghoul.ts.net
+
+      - name: Install Rust
+        uses: actions-rs/toolchain@v1
+        with:
+          toolchain: stable
+
+      - name: Cargo Build
+        uses: actions-rs/cargo@v1
+        with:
+          command: build
+
+      - name: Setup .NET Core SDK 9.0.x
+        uses: actions/setup-dotnet@v3.0.3
+        with:
+          dotnet-version: 9.0.x
+
+      - name: Add Gitea Repo
+        run: dotnet nuget add source --name gitea --api-key ${{ secrets.DOCKERHUB_TOKEN }} https://gitea.sheep-ghoul.ts.net/api/packages/sarsoo/nuget/index.json
+
+      - name: Install Dependencies
+        working-directory: ./FinLib.NET
+        run: dotnet restore
+
+      - name: Pack
+        working-directory: ./FinLib.NET/FinLib
+        run: dotnet pack
+
+      - name: Push
+        working-directory: ./FinLib.NET/FinLib
+        run: dotnet nuget push --source gitea ./bin/Release/FinLib.NET.0.0.3.nupkg
+
   buildWASM:
     name: Build WASM
     runs-on: ubuntu-latest

From e74c266a6eda30eff03ddc8b781c3b8d3cf4ae73 Mon Sep 17 00:00:00 2001
From: Andy Pack <andy@sarsoo.xyz>
Date: Mon, 17 Feb 2025 23:22:35 +0000
Subject: [PATCH 6/6] tweaking ffi bindings, bumping version

---
 .gitea/workflows/build.yml             |  2 +-
 Cargo.lock                             |  8 +++---
 Cargo.toml                             |  2 +-
 FinLib.NET/FinLib/FinLib.Interest.cs   |  5 +---
 FinLib.NET/FinLib/FinLib.Risk.cs       |  2 ++
 FinLib.NET/FinLib/FinLib.csproj        | 12 ++++----
 FinLib.NET/FinLib/NativeMethods.g.cs   | 39 ++++++++++++++++++++++++++
 finlib-cpp/include/finlib-native.h     |  2 ++
 finlib-ffi/build.rs                    | 11 ++++++++
 finlib-ffi/cbindgen.toml               |  8 +++---
 finlib-ffi/src/lib.rs                  |  6 ++++
 finlib/src/options/blackscholes/mod.rs | 10 +++----
 12 files changed, 82 insertions(+), 25 deletions(-)

diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml
index 0aa4d26..6913d7c 100644
--- a/.gitea/workflows/build.yml
+++ b/.gitea/workflows/build.yml
@@ -131,7 +131,7 @@ jobs:
 
       - name: Push
         working-directory: ./FinLib.NET/FinLib
-        run: dotnet nuget push --source gitea ./bin/Release/FinLib.NET.0.0.3.nupkg
+        run: dotnet nuget push --source gitea ./bin/Release/FinLib.NET.0.0.4.nupkg
 
   buildWASM:
     name: Build WASM
diff --git a/Cargo.lock b/Cargo.lock
index 08b4261..265f179 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -259,7 +259,7 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
 
 [[package]]
 name = "finlib"
-version = "0.0.3"
+version = "0.0.4"
 dependencies = [
  "getrandom 0.2.15",
  "log",
@@ -274,7 +274,7 @@ dependencies = [
 
 [[package]]
 name = "finlib-ffi"
-version = "0.0.3"
+version = "0.0.4"
 dependencies = [
  "cbindgen",
  "csbindgen",
@@ -283,7 +283,7 @@ dependencies = [
 
 [[package]]
 name = "finlib-wasm"
-version = "0.0.3"
+version = "0.0.4"
 dependencies = [
  "console_error_panic_hook",
  "console_log",
@@ -606,7 +606,7 @@ dependencies = [
 
 [[package]]
 name = "pyfinlib"
-version = "0.0.3"
+version = "0.0.4"
 dependencies = [
  "finlib",
  "log",
diff --git a/Cargo.toml b/Cargo.toml
index 5c81626..2383a13 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,7 +14,7 @@ default-members = [
 ]
 
 [workspace.package]
-version = "0.0.3"
+version = "0.0.4"
 authors = ["sarsoo <andy@sarsoo.xyz>"]
 description = "Quant finance functions implemented in Rust"
 edition = "2021"
diff --git a/FinLib.NET/FinLib/FinLib.Interest.cs b/FinLib.NET/FinLib/FinLib.Interest.cs
index a6bba3f..efa04ef 100644
--- a/FinLib.NET/FinLib/FinLib.Interest.cs
+++ b/FinLib.NET/FinLib/FinLib.Interest.cs
@@ -7,8 +7,5 @@ namespace FinLib.Interest;
 
 public static class Interest
 {
-    public static double Compound(double principal, double rate, double time, double n)
-    {
-        return NativeMethods.interest_compound(principal, rate, time, n);
-    }
+    public static double Compound(double principal, double rate, double time, double n) => NativeMethods.interest_compound(principal, rate, time, n);
 }
\ No newline at end of file
diff --git a/FinLib.NET/FinLib/FinLib.Risk.cs b/FinLib.NET/FinLib/FinLib.Risk.cs
index 7813bdb..bba9bee 100644
--- a/FinLib.NET/FinLib/FinLib.Risk.cs
+++ b/FinLib.NET/FinLib/FinLib.Risk.cs
@@ -30,4 +30,6 @@ public static class ValueAtRisk
             }
         }
     }
+
+    public static double ScaleValueAtRisk(double initialValue, nint timeCycles) => NativeMethods.scale_value_at_risk(initialValue, timeCycles);
 }
\ No newline at end of file
diff --git a/FinLib.NET/FinLib/FinLib.csproj b/FinLib.NET/FinLib/FinLib.csproj
index 28d7541..f2b69d9 100644
--- a/FinLib.NET/FinLib/FinLib.csproj
+++ b/FinLib.NET/FinLib/FinLib.csproj
@@ -1,17 +1,17 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
+  <PropertyGroup>
+    <PackageId>FinLib.NET</PackageId>
+    <Version>0.0.4</Version>
+    <Authors>sarsoo</Authors>
+  </PropertyGroup>
+
   <PropertyGroup>
     <TargetFrameworks>netstandard2.0</TargetFrameworks>
     <LangVersion>latest</LangVersion>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
 
-  <PropertyGroup>
-    <PackageId>FinLib.NET</PackageId>
-    <Version>0.0.1</Version>
-    <Authors>sarsoo</Authors>
-  </PropertyGroup>
-
   <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
     <Content Include="..\..\target\debug\libfinlib_ffi.*">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
diff --git a/FinLib.NET/FinLib/NativeMethods.g.cs b/FinLib.NET/FinLib/NativeMethods.g.cs
index 1d1065f..fcbb16b 100644
--- a/FinLib.NET/FinLib/NativeMethods.g.cs
+++ b/FinLib.NET/FinLib/NativeMethods.g.cs
@@ -28,9 +28,48 @@ namespace FinLib
         [DllImport(__DllName, EntryPoint = "varcovar_value_at_risk", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
         internal static extern double* varcovar_value_at_risk(double* arr, nuint len, double confidence);
 
+        [DllImport(__DllName, EntryPoint = "scale_value_at_risk", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+        internal static extern double scale_value_at_risk(double initial_value, nint time_cycles);
+
 
     }
 
+    [StructLayout(LayoutKind.Sequential)]
+    internal unsafe partial struct Portfolio
+    {
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    internal unsafe partial struct PortfolioAsset
+    {
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    internal unsafe partial struct OptionVariables
+    {
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    internal unsafe partial struct CallOption
+    {
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    internal unsafe partial struct PutOption
+    {
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    internal unsafe partial struct OptionGreeks
+    {
+    }
+
+
+    internal enum ValueType : byte
+    {
+        Absolute,
+        RateOfChange,
+    }
 
 
 }
diff --git a/finlib-cpp/include/finlib-native.h b/finlib-cpp/include/finlib-native.h
index 1dd2773..dea90d7 100644
--- a/finlib-cpp/include/finlib-native.h
+++ b/finlib-cpp/include/finlib-native.h
@@ -23,6 +23,8 @@ const double *historical_value_at_risk(const double *arr, size_t len, double con
 
 double interest_compound(double principal, double rate, double time, double n);
 
+double scale_value_at_risk(double initial_value, ptrdiff_t time_cycles);
+
 const double *varcovar_value_at_risk(const double *arr, size_t len, double confidence);
 
 }  // extern "C"
diff --git a/finlib-ffi/build.rs b/finlib-ffi/build.rs
index 4f30ff9..08e65f4 100644
--- a/finlib-ffi/build.rs
+++ b/finlib-ffi/build.rs
@@ -19,7 +19,18 @@ fn main() {
     csbindgen::Builder::default()
         .input_extern_file("src/lib.rs")
         .input_extern_file("../finlib/src/lib.rs")
+        .input_extern_file("../finlib/src/risk/portfolio.rs")
+        .input_extern_file("../finlib/src/options/blackscholes/mod.rs")
         .csharp_dll_name("libfinlib_ffi")
+        .always_included_types([
+            "Portfolio",
+            "ValueType",
+            "PortfolioAsset",
+            "OptionVariables",
+            "CallOption",
+            "PutOption",
+            "OptionGreeks",
+        ])
         .csharp_namespace("FinLib")
         .generate_csharp_file("../FinLib.NET/FinLib/NativeMethods.g.cs")
         .unwrap();
diff --git a/finlib-ffi/cbindgen.toml b/finlib-ffi/cbindgen.toml
index cd8eb52..dc8e222 100644
--- a/finlib-ffi/cbindgen.toml
+++ b/finlib-ffi/cbindgen.toml
@@ -144,11 +144,11 @@ bitflags = false
 ############## Options for How Your Rust library Should Be Parsed ##############
 
 [parse]
-parse_deps = false
-# include = []
-exclude = []
+parse_deps = true
+include = ["finlib"]
+#exclude = []
 clean = false
-extra_bindings = []
+extra_bindings = ["finlib"]
 
 
 
diff --git a/finlib-ffi/src/lib.rs b/finlib-ffi/src/lib.rs
index ddf2a73..a52cbcf 100644
--- a/finlib-ffi/src/lib.rs
+++ b/finlib-ffi/src/lib.rs
@@ -44,3 +44,9 @@ pub unsafe extern "C" fn varcovar_value_at_risk(arr: *const f64, len: usize, con
 
     Box::into_raw(Box::new(finlib::risk::var::varcovar::value_at_risk_percent(input_array, confidence)))
 }
+
+#[no_mangle]
+pub unsafe extern "C" fn scale_value_at_risk(initial_value: f64, time_cycles: isize) -> f64 {
+
+    finlib::risk::var::varcovar::scale_value_at_risk(initial_value, time_cycles)
+}
diff --git a/finlib/src/options/blackscholes/mod.rs b/finlib/src/options/blackscholes/mod.rs
index 7235352..b162007 100644
--- a/finlib/src/options/blackscholes/mod.rs
+++ b/finlib/src/options/blackscholes/mod.rs
@@ -255,11 +255,11 @@ pub fn vega(v: &OptionVariables) -> f64 {
 #[cfg_attr(feature = "ffi", repr(C))]
 #[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd)]
 pub struct OptionGreeks {
-    delta: f64,
-    gamma: f64,
-    vega: f64,
-    theta: f64,
-    rho: f64
+    pub delta: f64,
+    pub gamma: f64,
+    pub vega: f64,
+    pub theta: f64,
+    pub rho: f64
 }
 
 impl OptionGreeks {