fixing var bug, rearranging
This commit is contained in:
parent
c830eb3ac7
commit
80ebde1e0b
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
notebooks/** -linguist-detectable
|
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -259,7 +259,7 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "finlib"
|
name = "finlib"
|
||||||
version = "0.0.2"
|
version = "0.0.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.15",
|
"getrandom 0.2.15",
|
||||||
"log",
|
"log",
|
||||||
@ -274,7 +274,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "finlib-ffi"
|
name = "finlib-ffi"
|
||||||
version = "0.0.2"
|
version = "0.0.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cbindgen",
|
"cbindgen",
|
||||||
"csbindgen",
|
"csbindgen",
|
||||||
@ -283,7 +283,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "finlib-wasm"
|
name = "finlib-wasm"
|
||||||
version = "0.0.2"
|
version = "0.0.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"console_log",
|
"console_log",
|
||||||
@ -606,7 +606,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyfinlib"
|
name = "pyfinlib"
|
||||||
version = "0.0.2"
|
version = "0.0.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"finlib",
|
"finlib",
|
||||||
"log",
|
"log",
|
||||||
|
@ -14,7 +14,7 @@ default-members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.0.2"
|
version = "0.0.3"
|
||||||
authors = ["sarsoo <andy@sarsoo.xyz>"]
|
authors = ["sarsoo <andy@sarsoo.xyz>"]
|
||||||
description = "Quant finance functions implemented in Rust"
|
description = "Quant finance functions implemented in Rust"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { ValueAtRisk, Portfolio, PortfolioAsset } from "finlib";
|
import { ValueAtRisk, Portfolio, PortfolioAsset, init_logging } from "finlib";
|
||||||
|
|
||||||
|
init_logging();
|
||||||
|
|
||||||
console.log(ValueAtRisk.varcovar([1, 2, 3, 4], 0.1));
|
console.log(ValueAtRisk.varcovar([1, 2, 3, 4], 0.1));
|
||||||
console.log(ValueAtRisk.varcovar([1, 2, 3, 4], 0.05));
|
console.log(ValueAtRisk.varcovar([1, 2, 3, 4], 0.05));
|
||||||
|
|
||||||
let portfolio = new Portfolio([new PortfolioAsset(1.0, "test", [1.0, 2.0, 3.0])]);
|
let portfolio = new Portfolio([new PortfolioAsset(1.0, "test", [1.0, 2.0, 3.0])]);
|
||||||
console.log(portfolio.isValid());
|
console.log(portfolio.isValid());
|
||||||
console.log(portfolio.valueAtRisk(0.1));
|
console.log(portfolio.valueAtRisk(0.1, 1000000));
|
||||||
|
@ -2,8 +2,13 @@ use wasm_bindgen::prelude::wasm_bindgen;
|
|||||||
use console_log;
|
use console_log;
|
||||||
use log::Level;
|
use log::Level;
|
||||||
|
|
||||||
#[wasm_bindgen(start)]
|
// #[wasm_bindgen(start)]
|
||||||
fn start() {
|
// fn start() {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn init_logging() {
|
||||||
if let Err(_) = console_log::init_with_level(Level::Debug) {
|
if let Err(_) = console_log::init_with_level(Level::Debug) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
use statrs::distribution::{ContinuousCDF, Normal};
|
|
||||||
|
|
||||||
pub fn mean_investment(portfolio_mean_change: f64, initial_investment: f64) -> f64 {
|
pub fn mean_investment(portfolio_mean_change: f64, initial_investment: f64) -> f64 {
|
||||||
(1. + portfolio_mean_change) * initial_investment
|
(1. + portfolio_mean_change) * initial_investment
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
|
use log::{debug, error, info};
|
||||||
use ndarray::prelude::*;
|
use ndarray::prelude::*;
|
||||||
use ndarray_stats::CorrelationExt;
|
use ndarray_stats::CorrelationExt;
|
||||||
#[cfg(feature = "wasm")]
|
#[cfg(feature = "wasm")]
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
#[cfg(feature = "py")]
|
#[cfg(feature = "py")]
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use crate::risk::var::varcovar::{portfolio_value_at_risk, portfolio_value_at_risk_percent};
|
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;
|
use crate::util::roc::rates_of_change;
|
||||||
|
|
||||||
#[cfg_attr(feature = "wasm", wasm_bindgen)]
|
#[cfg_attr(feature = "wasm", wasm_bindgen)]
|
||||||
#[cfg_attr(feature = "py", pyclass)]
|
#[cfg_attr(feature = "py", pyclass)]
|
||||||
#[cfg_attr(feature = "ffi", repr(C))]
|
#[cfg_attr(feature = "ffi", repr(C))]
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
pub struct Portfolio {
|
pub struct Portfolio {
|
||||||
assets: Vec<PortfolioAsset>
|
assets: Vec<PortfolioAsset>
|
||||||
}
|
}
|
||||||
@ -28,7 +31,7 @@ pub enum ValueType {
|
|||||||
#[cfg_attr(feature = "wasm", wasm_bindgen)]
|
#[cfg_attr(feature = "wasm", wasm_bindgen)]
|
||||||
#[cfg_attr(feature = "py", pyclass)]
|
#[cfg_attr(feature = "py", pyclass)]
|
||||||
#[cfg_attr(feature = "ffi", repr(C))]
|
#[cfg_attr(feature = "ffi", repr(C))]
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
pub struct PortfolioAsset {
|
pub struct PortfolioAsset {
|
||||||
portfolio_weight: f64,
|
portfolio_weight: f64,
|
||||||
name: String,
|
name: String,
|
||||||
@ -56,6 +59,7 @@ impl PortfolioAsset {
|
|||||||
pub fn get_mean_and_std(&self) -> Option<(f64, f64)> {
|
pub fn get_mean_and_std(&self) -> Option<(f64, f64)> {
|
||||||
match self.value_type {
|
match self.value_type {
|
||||||
ValueType::Absolute => {
|
ValueType::Absolute => {
|
||||||
|
info!("[{}] Asset's values are currently absolute, calculating rates of change first", self.name);
|
||||||
let roc = rates_of_change(&self.values).collect::<Vec<f64>>();
|
let roc = rates_of_change(&self.values).collect::<Vec<f64>>();
|
||||||
Some((stats::mean(&roc), stats::sample_std_dev(&roc)))
|
Some((stats::mean(&roc), stats::sample_std_dev(&roc)))
|
||||||
}
|
}
|
||||||
@ -106,7 +110,7 @@ impl Portfolio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn valid_weights(&self) -> bool {
|
pub fn valid_weights(&self) -> bool {
|
||||||
let mut weight = 1 as f64;
|
let mut weight = 1f64;
|
||||||
|
|
||||||
for asset in &self.assets {
|
for asset in &self.assets {
|
||||||
weight -= asset.portfolio_weight;
|
weight -= asset.portfolio_weight;
|
||||||
@ -140,23 +144,27 @@ impl Portfolio {
|
|||||||
|
|
||||||
pub fn get_mean_and_std(&mut self) -> Option<(f64, f64)> {
|
pub fn get_mean_and_std(&mut self) -> Option<(f64, f64)> {
|
||||||
if !self.valid_sizes() {
|
if !self.valid_sizes() {
|
||||||
|
error!("Can't get portfolio mean and std dev because asset value counts arent't the same");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.apply_rates_of_change();
|
self.apply_rates_of_change();
|
||||||
let m = self.get_matrix();
|
let m = self.get_matrix();
|
||||||
if m.is_none() {
|
if m.is_none() {
|
||||||
|
error!("Couldn't format portfolio as matrix");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let m = m.unwrap();
|
let m = m.unwrap();
|
||||||
|
|
||||||
let cov = m.cov(1.);
|
let cov = m.cov(1.);
|
||||||
if cov.is_err() {
|
if cov.is_err() {
|
||||||
|
error!("Failed to calculate portfolio covariance");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let cov = cov.unwrap();
|
let cov = cov.unwrap();
|
||||||
let mean_return = m.mean_axis(Axis(1));
|
let mean_return = m.mean_axis(Axis(1));
|
||||||
if mean_return.is_none() {
|
if mean_return.is_none() {
|
||||||
|
error!("Failed to calculate portfolio mean");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mean_return = mean_return.unwrap();
|
let mean_return = mean_return.unwrap();
|
||||||
@ -170,12 +178,36 @@ impl Portfolio {
|
|||||||
Some((porfolio_mean_return, portfolio_stddev))
|
Some((porfolio_mean_return, portfolio_stddev))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.interviewqs.com/blog/value-at-risk
|
||||||
|
|
||||||
pub fn value_at_risk(&mut self, confidence: f64, initial_investment: f64) -> Option<f64> {
|
pub fn value_at_risk(&mut self, confidence: f64, initial_investment: f64) -> Option<f64> {
|
||||||
portfolio_value_at_risk(self, confidence, initial_investment)
|
match self.get_mean_and_std() {
|
||||||
|
None => None,
|
||||||
|
Some((mean, std_dev)) => {
|
||||||
|
debug!("Portfolio percent movement mean[{}], std dev[{}]", mean, std_dev);
|
||||||
|
let investment_mean = mean_investment(mean, initial_investment);
|
||||||
|
let investment_std_dev = std_dev_investment(std_dev, initial_investment);
|
||||||
|
debug!("Investment[{}] mean[{}], std dev[{}]", initial_investment, mean, std_dev);
|
||||||
|
|
||||||
|
let investment_var = investment_value_at_risk(confidence, investment_mean, investment_std_dev);
|
||||||
|
|
||||||
|
debug!("Investment[{}] value at risk [{}]", initial_investment, investment_var);
|
||||||
|
|
||||||
|
Some(initial_investment - investment_var)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.interviewqs.com/blog/value-at-risk
|
||||||
|
|
||||||
pub fn value_at_risk_percent(&mut self, confidence: f64) -> Option<f64> {
|
pub fn value_at_risk_percent(&mut self, confidence: f64) -> Option<f64> {
|
||||||
portfolio_value_at_risk_percent(self, confidence)
|
match self.get_mean_and_std() {
|
||||||
|
None => None,
|
||||||
|
Some((mean, std_dev)) => {
|
||||||
|
let n = Normal::new(mean, std_dev).unwrap();
|
||||||
|
Some(n.inverse_cdf(confidence))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
use log::info;
|
|
||||||
use crate::stats;
|
use crate::stats;
|
||||||
use crate::util::roc::rates_of_change;
|
use crate::util::roc::rates_of_change;
|
||||||
|
|
||||||
use crate::risk::portfolio::Portfolio;
|
|
||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use statrs::distribution::{ContinuousCDF, Normal};
|
use statrs::distribution::{ContinuousCDF, Normal};
|
||||||
use crate::risk::forecast::{mean_investment, std_dev_investment};
|
|
||||||
// https://medium.com/@serdarilarslan/value-at-risk-var-and-its-implementation-in-python-5c9150f73b0e
|
// https://medium.com/@serdarilarslan/value-at-risk-var-and-its-implementation-in-python-5c9150f73b0e
|
||||||
|
|
||||||
pub fn value_at_risk_percent(values: &[f64], confidence: f64) -> f64 {
|
pub fn value_at_risk_percent(values: &[f64], confidence: f64) -> f64 {
|
||||||
@ -20,34 +17,6 @@ pub fn value_at_risk_percent(values: &[f64], confidence: f64) -> f64 {
|
|||||||
n.inverse_cdf(confidence)
|
n.inverse_cdf(confidence)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn portfolio_value_at_risk_percent(portfolio: &mut Portfolio, confidence: f64) -> Option<f64> {
|
|
||||||
match portfolio.get_mean_and_std() {
|
|
||||||
None => None,
|
|
||||||
Some((mean, std_dev)) => {
|
|
||||||
let n = Normal::new(mean, std_dev).unwrap();
|
|
||||||
Some(n.inverse_cdf(confidence))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn portfolio_value_at_risk(portfolio: &mut Portfolio, confidence: f64, initial_investment: f64) -> Option<f64> {
|
|
||||||
match portfolio.get_mean_and_std() {
|
|
||||||
None => None,
|
|
||||||
Some((mean, std_dev)) => {
|
|
||||||
let investment_mean = mean_investment(mean, initial_investment);
|
|
||||||
let investment_std_dev = std_dev_investment(std_dev, std_dev);
|
|
||||||
|
|
||||||
info!("{:?}, {:?}", investment_mean, investment_std_dev);
|
|
||||||
|
|
||||||
let investment_var = investment_value_at_risk(confidence, investment_mean, investment_std_dev);
|
|
||||||
|
|
||||||
println!("{:?}", investment_var);
|
|
||||||
|
|
||||||
Some(initial_investment - investment_var)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn investment_value_at_risk(confidence: f64, investment_mean: f64, investment_std_dev: f64) -> f64 {
|
pub fn investment_value_at_risk(confidence: f64, investment_mean: f64, investment_std_dev: f64) -> f64 {
|
||||||
let n = Normal::new(investment_mean, investment_std_dev).unwrap();
|
let n = Normal::new(investment_mean, investment_std_dev).unwrap();
|
||||||
|
|
||||||
@ -61,6 +30,7 @@ pub fn scale_value_at_risk(initial_value: f64, time_cycles: isize) -> f64 {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::risk::portfolio::Portfolio;
|
||||||
use crate::risk::portfolio::PortfolioAsset;
|
use crate::risk::portfolio::PortfolioAsset;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -72,7 +42,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut portfolio = Portfolio::from(assets);
|
let mut portfolio = Portfolio::from(assets);
|
||||||
|
|
||||||
portfolio_value_at_risk_percent(&mut portfolio, 0.1);
|
portfolio.value_at_risk_percent(0.1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +54,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut portfolio = Portfolio::from(assets);
|
let mut portfolio = Portfolio::from(assets);
|
||||||
|
|
||||||
portfolio_value_at_risk_percent(&mut portfolio, 0.1);
|
portfolio.value_at_risk_percent(0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -96,8 +66,8 @@ mod tests {
|
|||||||
|
|
||||||
let mut portfolio = Portfolio::from(assets);
|
let mut portfolio = Portfolio::from(assets);
|
||||||
|
|
||||||
println!("{:?}", portfolio_value_at_risk(&mut portfolio, 0.01, 1_000_000.));
|
println!("{:?}", portfolio.value_at_risk(0.01, 1_000_000.));
|
||||||
println!("{:?}", portfolio_value_at_risk(&mut portfolio, 0.1, 1_000_000.));
|
println!("{:?}", portfolio.value_at_risk(0.1, 1_000_000.));
|
||||||
println!("{:?}", portfolio_value_at_risk(&mut portfolio, 0.5, 1_000_000.));
|
println!("{:?}", portfolio.value_at_risk(0.5, 1_000_000.));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,9 @@
|
|||||||
|
use log::error;
|
||||||
|
|
||||||
pub fn dot_product(a: &[f64], b: &[f64]) -> f64 {
|
pub fn dot_product(a: &[f64], b: &[f64]) -> f64 {
|
||||||
|
if a.len() != b.len() {
|
||||||
|
error!("Can't dot product two vectors of different lengths, a = {}, b = {}", a.len(), b.len());
|
||||||
|
}
|
||||||
assert_eq!(a.len(), b.len());
|
assert_eq!(a.len(), b.len());
|
||||||
|
|
||||||
a.iter()
|
a.iter()
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -10,7 +10,7 @@ mod pyfinlib {
|
|||||||
use finlib::risk::portfolio::PortfolioAsset;
|
use finlib::risk::portfolio::PortfolioAsset;
|
||||||
|
|
||||||
#[pymodule_init]
|
#[pymodule_init]
|
||||||
fn init(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
fn init(_m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
pyo3_log::init();
|
pyo3_log::init();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ mod pyfinlib {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
mod var {
|
mod value_at_risk {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
@ -42,6 +42,11 @@ mod pyfinlib {
|
|||||||
fn varcovar(values: Vec<f64>, confidence: f64) -> PyResult<f64> {
|
fn varcovar(values: Vec<f64>, confidence: f64) -> PyResult<f64> {
|
||||||
Ok(finlib::risk::var::varcovar::value_at_risk_percent(&values, confidence))
|
Ok(finlib::risk::var::varcovar::value_at_risk_percent(&values, confidence))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pyfunction]
|
||||||
|
fn scale_value_at_risk(initial_value: f64, time_cycles: isize) -> PyResult<f64> {
|
||||||
|
Ok(finlib::risk::var::varcovar::scale_value_at_risk(initial_value, time_cycles))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user