diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d967f42..da953ff 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -105,7 +105,7 @@ jobs:
           toolchain: stable
 
       - name: Build Docs
-        run: cargo doc --no-deps --document-private-items
+        run: cargo doc --no-deps --document-private-items -F py,wasm,ffi
 
       - name: Add redirect
         run: echo '<meta http-equiv="refresh" content="0;url=finlib/index.html">' > target/doc/index.html
@@ -160,6 +160,7 @@ jobs:
         uses: actions/setup-node@v2
         with:
           node-version: 22
+          registry-url: 'https://registry.npmjs.org' # This is just the default registry URL
 
       - name: Publish
         working-directory: ./finlib-wasm/pkg
diff --git a/finlib/src/ffi/mod.rs b/finlib/src/ffi/mod.rs
index a4a5a25..b988d0c 100644
--- a/finlib/src/ffi/mod.rs
+++ b/finlib/src/ffi/mod.rs
@@ -1,3 +1,5 @@
+//! FFI specific functionality to define the struct function interfaces in Python and WASM
+
 #[cfg(feature = "py")]
 pub mod py;
 #[cfg(feature = "wasm")]
diff --git a/finlib/src/interest/mod.rs b/finlib/src/interest/mod.rs
index 56151fc..c42e2e6 100644
--- a/finlib/src/interest/mod.rs
+++ b/finlib/src/interest/mod.rs
@@ -1,3 +1,4 @@
+//! Compound interest etc
 
 pub fn compound_32(principal: f32, rate: f32, time: f32, n: f32) -> f32 {
     principal * f32::powf( 1f32 + (rate / n), time * n)
diff --git a/finlib/src/lib.rs b/finlib/src/lib.rs
index ec12a32..7dc8d0d 100644
--- a/finlib/src/lib.rs
+++ b/finlib/src/lib.rs
@@ -1,5 +1,33 @@
+//! # Quant finance functionality for Rust with FFIs to C/C++, C#, Python and WASM
+
 pub mod interest;
 pub mod stats;
 pub mod util;
 pub mod risk;
-pub mod ffi;
\ No newline at end of file
+pub mod ffi;
+
+#[cfg(feature = "parallel")]
+use rayon::prelude::*;
+
+#[macro_export]
+macro_rules! gated_iter {
+    ($x:expr) => {
+        {
+            $x.iter()
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! gated_iter_mut {
+    ($x:expr) => {
+        {
+            if cfg!(feature = "parallel") {
+                $x.par_iter_mut()
+            }
+            else {
+                $x.iter_mut()
+            }
+        }
+    };
+}
\ No newline at end of file
diff --git a/finlib/src/risk/mod.rs b/finlib/src/risk/mod.rs
index 1411c4b..9b7b8fc 100644
--- a/finlib/src/risk/mod.rs
+++ b/finlib/src/risk/mod.rs
@@ -1,3 +1,5 @@
+//! Calculating risk for a given asset or portfolio using Value at Risk, [`var`]
+
 pub mod var;
 pub mod portfolio;
 pub mod forecast;
diff --git a/finlib/src/risk/portfolio.rs b/finlib/src/risk/portfolio.rs
index cd79a01..5bb44f6 100644
--- a/finlib/src/risk/portfolio.rs
+++ b/finlib/src/risk/portfolio.rs
@@ -5,12 +5,15 @@ use ndarray_stats::CorrelationExt;
 use wasm_bindgen::prelude::*;
 #[cfg(feature = "py")]
 use pyo3::prelude::*;
+#[cfg(feature = "parallel")]
+use rayon::prelude::*;
 use statrs::distribution::{ContinuousCDF, Normal};
 use crate::risk::forecast::{mean_investment, std_dev_investment};
 use crate::risk::var::varcovar::{investment_value_at_risk};
-use crate::stats;
+use crate::{stats};
 use crate::util::roc::rates_of_change;
 
+/// Describes a Portfolio as a collection of [`PortfolioAsset`]s
 #[cfg_attr(feature = "wasm", wasm_bindgen)]
 #[cfg_attr(feature = "py", pyclass)]
 #[cfg_attr(feature = "ffi", repr(C))]
@@ -28,6 +31,7 @@ pub enum ValueType {
     RateOfChange
 }
 
+/// Describes a single instrument as a list of previous values with an associated portfolio proportion
 #[cfg_attr(feature = "wasm", wasm_bindgen)]
 #[cfg_attr(feature = "py", pyclass)]
 #[cfg_attr(feature = "ffi", repr(C))]
@@ -46,6 +50,7 @@ impl PortfolioAsset {
         }
     }
 
+    /// If the asset's values have been given as absolute values, convert those to a percentage change between each
     pub fn apply_rates_of_change(&mut self) {
         match self.value_type {
             ValueType::Absolute => {
@@ -56,6 +61,9 @@ impl PortfolioAsset {
         }
     }
 
+    /// Get the mean and standard deviation of the rates of change of an asset
+    ///
+    /// returns (mean, std_dev)
     pub fn get_mean_and_std(&self) -> Option<(f64, f64)> {
         match self.value_type {
             ValueType::Absolute => {
@@ -77,18 +85,32 @@ impl Portfolio {
         }
     }
 
+    /// Return the proportions of a portfolio's assets
+    ///
+    /// In a properly formed Portfolio these will add up to 1.0
     pub fn get_asset_weight(&self) -> impl Iterator<Item=f64> + use<'_> {
         self.assets
             .iter()
             .map(|x| x.portfolio_weight)
     }
 
+    /// Convert a portfolio of assets with absolute values to the percentage change in values
     pub fn apply_rates_of_change(&mut self) {
-        for asset in self.assets.iter_mut() {
-            asset.apply_rates_of_change();
+        #[cfg(feature = "parallel")]
+        {
+            self.assets.par_iter_mut().for_each(|asset| {
+                asset.apply_rates_of_change();
+            });
+        }
+        #[cfg(not(feature = "parallel"))]
+        {
+            self.assets.iter_mut().for_each(|asset| {
+                asset.apply_rates_of_change();
+            });
         }
     }
 
+    /// Do all the assets in the portfolio have the same number of values (required to perform matrix operations)
     pub fn valid_sizes(&self) -> bool {
         let mut last_value_length: Option<usize> = None;
 
@@ -109,6 +131,7 @@ impl Portfolio {
         true
     }
 
+    /// Do the proportions of the assets in the portfolio add up to 100%?
     pub fn valid_weights(&self) -> bool {
         let mut weight = 1f64;
 
@@ -123,6 +146,7 @@ impl Portfolio {
         self.valid_sizes() && self.valid_weights()
     }
 
+    /// Format the asset values in the portfolio as a matrix such that statistical operations can be applied to it
     pub fn get_matrix(&self) -> Option<Array2<f64>> {
         if self.assets.is_empty() || !self.valid_sizes() {
             return None;
@@ -131,17 +155,34 @@ impl Portfolio {
         let column_count = self.assets.len();
         let row_count = self.assets[0].values.len();
 
-        let matrix = Array2::from_shape_vec((column_count, row_count),
-            self.assets
-                .iter()
-                .map(|a| a.values.clone())
-                .flatten()
-                .collect::<Vec<f64>>()
-        ).unwrap();
+        #[cfg(feature = "parallel")]
+        {
+            let matrix = Array2::from_shape_vec((column_count, row_count),
+                                                self.assets
+                                                    .par_iter()
+                                                    .map(|a| a.values.clone())
+                                                    .flatten()
+                                                    .collect::<Vec<f64>>()
+            ).unwrap();
+            Some(matrix.into_owned())
+        }
+        #[cfg(not(feature = "parallel"))]
+        {
+            let matrix = Array2::from_shape_vec((column_count, row_count),
+                                                self.assets
+                                                    .iter()
+                                                    .map(|a| a.values.clone())
+                                                    .flatten()
+                                                    .collect::<Vec<f64>>()
+            ).unwrap();
+            Some(matrix.into_owned())
+        }
 
-        Some(matrix.into_owned())
     }
 
+    /// Calculate the mean and the standard deviation of a portfolio, taking into account the relative weights and covariance of the portfolio's assets
+    ///
+    /// returns (mean, std_dev)
     pub fn get_mean_and_std(&mut self) -> Option<(f64, f64)> {
         if !self.valid_sizes() {
             error!("Can't get portfolio mean and std dev because asset value counts arent't the same");
@@ -178,8 +219,9 @@ impl Portfolio {
         Some((porfolio_mean_return, portfolio_stddev))
     }
 
-    // https://www.interviewqs.com/blog/value-at-risk
-
+    /// For a given confidence rate (0.01, 0.05, 0.10) and initial investment value, calculate the parametric value at risk
+    ///
+    /// https://www.interviewqs.com/blog/value-at-risk
     pub fn value_at_risk(&mut self, confidence: f64, initial_investment: f64) -> Option<f64> {
         match self.get_mean_and_std() {
             None => None,
@@ -198,8 +240,9 @@ impl Portfolio {
         }
     }
 
-    // https://www.interviewqs.com/blog/value-at-risk
-
+    /// For a given confidence rate (0.01, 0.05, 0.10) calculate the percentage change in an investment
+    ///
+    /// https://www.interviewqs.com/blog/value-at-risk
     pub fn value_at_risk_percent(&mut self, confidence: f64) -> Option<f64> {
         match self.get_mean_and_std() {
             None => None,
diff --git a/finlib/src/risk/var/mod.rs b/finlib/src/risk/var/mod.rs
index 36a75d8..8863494 100644
--- a/finlib/src/risk/var/mod.rs
+++ b/finlib/src/risk/var/mod.rs
@@ -1,2 +1,4 @@
+//! Calculate Value at Risk using either the [`historical`] or parametric [`varcovar`] methods for an asset or portfolio
+
 pub mod historical;
 pub mod varcovar;
diff --git a/finlib/src/stats/mod.rs b/finlib/src/stats/mod.rs
index c2bb3ac..b19353a 100644
--- a/finlib/src/stats/mod.rs
+++ b/finlib/src/stats/mod.rs
@@ -1,13 +1,9 @@
 mod covariance;
 pub use covariance::*;
 
-#[cfg(feature = "parallel")]
-use rayon::prelude::*;
-
 pub fn mean(slice: &[f64]) -> f64
 {
     slice
-        // .par_iter()
         .iter()
         .sum::<f64>() / slice.len() as f64
 }
@@ -16,7 +12,6 @@ pub fn population_variance(slice: &[f64]) -> f64
 {
     let mean = mean(slice);
     slice
-        // .par_iter()
         .iter()
         .map(|x| f64::powi(x - mean, 2))
         .sum::<f64>()
@@ -27,7 +22,6 @@ pub fn sample_variance(slice: &[f64]) -> f64
 {
     let mean = mean(slice);
     slice
-        // .par_iter()
         .iter()
         .map(|x| f64::powi(x - mean, 2))
         .sum::<f64>()