parent
a2ce71161e
commit
335f5dbc82
34
FinLib.NET/FinLib.Bench/Benchmarks/HistoricalVAR.cs
Normal file
34
FinLib.NET/FinLib.Bench/Benchmarks/HistoricalVAR.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System.Security.Cryptography;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using FinLib.Bench.Comparisons;
|
||||
|
||||
namespace FinLib.Bench.Benchmarks;
|
||||
|
||||
public class HistoricalVar
|
||||
{
|
||||
private const int N = 10000;
|
||||
|
||||
// [Params(0.05d, 0.1d)]
|
||||
public double confidence = 0.05;
|
||||
[Params(10, 1000, 10000)]
|
||||
public int length;
|
||||
private double[] data;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
data = new double[length];
|
||||
var random = new Random(42);
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
data[i] = random.NextDouble();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public double FinLibDotnet() => HistoricalVARDotnet.ValueAtRisk(data.ToList(), confidence);
|
||||
|
||||
[Benchmark]
|
||||
public double FinLibRust() => FinLib.Risk.ValueAtRisk.Historical(data, confidence);
|
||||
}
|
37
FinLib.NET/FinLib.Bench/Comparisons/HistoricalVARDotnet.cs
Normal file
37
FinLib.NET/FinLib.Bench/Comparisons/HistoricalVARDotnet.cs
Normal file
@ -0,0 +1,37 @@
|
||||
namespace FinLib.Bench.Comparisons;
|
||||
|
||||
public static class HistoricalVARDotnet
|
||||
{
|
||||
public static IEnumerable<(T, T)> Pairwise<T>(this IEnumerable<T> enumerable)
|
||||
{
|
||||
var previous = default(T);
|
||||
|
||||
using (var e = enumerable.GetEnumerator())
|
||||
{
|
||||
if (e.MoveNext())
|
||||
previous = e.Current;
|
||||
|
||||
while (e.MoveNext())
|
||||
{
|
||||
previous = e.Current;
|
||||
yield return (previous, e.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<double> RatesOfChange(this IEnumerable<double> values)
|
||||
{
|
||||
foreach (var value in values.Pairwise())
|
||||
{
|
||||
yield return (value.Item2 - value.Item1) / value.Item1;
|
||||
}
|
||||
}
|
||||
|
||||
public static double ValueAtRisk(List<double> values, double confidence)
|
||||
{
|
||||
var roc = values.RatesOfChange().Order().ToArray();
|
||||
var threshold = (int) Math.Floor(confidence * roc.Length);
|
||||
|
||||
return roc[threshold];
|
||||
}
|
||||
}
|
18
FinLib.NET/FinLib.Bench/FinLib.Bench.csproj
Normal file
18
FinLib.NET/FinLib.Bench/FinLib.Bench.csproj
Normal file
@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FinLib\FinLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
13
FinLib.NET/FinLib.Bench/Program.cs
Normal file
13
FinLib.NET/FinLib.Bench/Program.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using BenchmarkDotNet.Running;
|
||||
using FinLib.Bench.Benchmarks;
|
||||
|
||||
namespace FinLib.Bench
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var summary = BenchmarkRunner.Run<HistoricalVar>();
|
||||
}
|
||||
}
|
||||
}
|
@ -11,24 +11,24 @@ public class Tests
|
||||
[Test]
|
||||
public void TestCompoundInterest()
|
||||
{
|
||||
FinLib.CompoundInterest(100, 0.05, 1, 1).Should().Be(105);
|
||||
Interest.Interest.Compound(100, 0.05, 1, 1).Should().Be(105);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCompoundInterestMonthly()
|
||||
{
|
||||
Math.Round(FinLib.CompoundInterest(100, 0.05, 1, 12), 2).Should().Be(105.12);
|
||||
Math.Round(Interest.Interest.Compound(100, 0.05, 1, 12), 2).Should().Be(105.12);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Covariance()
|
||||
{
|
||||
FinLib.Covariance([1d, 2d, 3d, 4], [1d, 2, 3, 4]).Should().Be(1.6666666666666667);
|
||||
Stats.Stats.Covariance([1d, 2d, 3d, 4], [1d, 2, 3, 4]).Should().Be(1.6666666666666667);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CovarianceBreaking()
|
||||
{
|
||||
FinLib.Covariance([1d, 2d, 3d, 4], [1d]).Should().BeNull();
|
||||
Stats.Stats.Covariance([1d, 2d, 3d, 4], [1d]).Should().BeNull();
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinLib", "FinLib\FinLib.csp
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinLib.Test", "FinLib.Test\FinLib.Test.csproj", "{148E024C-07D0-4DC0-A3E2-A146E709897B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinLib.Bench", "FinLib.Bench\FinLib.Bench.csproj", "{F4171146-0A63-4611-972B-BAFB9A090D2B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -18,5 +20,9 @@ Global
|
||||
{148E024C-07D0-4DC0-A3E2-A146E709897B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{148E024C-07D0-4DC0-A3E2-A146E709897B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{148E024C-07D0-4DC0-A3E2-A146E709897B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F4171146-0A63-4611-972B-BAFB9A090D2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F4171146-0A63-4611-972B-BAFB9A090D2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F4171146-0A63-4611-972B-BAFB9A090D2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F4171146-0A63-4611-972B-BAFB9A090D2B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
14
FinLib.NET/FinLib/FinLib.Interest.cs
Normal file
14
FinLib.NET/FinLib/FinLib.Interest.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
21
FinLib.NET/FinLib/FinLib.Risk.cs
Normal file
21
FinLib.NET/FinLib/FinLib.Risk.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FinLib.Risk;
|
||||
|
||||
public static class ValueAtRisk
|
||||
{
|
||||
public static double Historical(IEnumerable<double> values, double confidence)
|
||||
{
|
||||
unsafe {
|
||||
var valueArr = values.ToArray();
|
||||
fixed (double* ptrOne = valueArr) {
|
||||
var ret = NativeMethods.value_at_risk(ptrOne, (UIntPtr)valueArr.Length, confidence);
|
||||
|
||||
return *ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,15 +3,10 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FinLib;
|
||||
namespace FinLib.Stats;
|
||||
|
||||
public static class FinLib
|
||||
public static class Stats
|
||||
{
|
||||
public static double CompoundInterest(double principal, double rate, double time, double n)
|
||||
{
|
||||
return NativeMethods.interest_compound(principal, rate, time, n);
|
||||
}
|
||||
|
||||
public static double? Covariance(IEnumerable<double> valuesOne, IEnumerable<double> valuesTwo)
|
||||
{
|
||||
unsafe {
|
||||
|
@ -22,6 +22,9 @@ namespace FinLib
|
||||
[DllImport(__DllName, EntryPoint = "covariance", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern double* covariance(double* arr, nuint len, double* arr_two, nuint len_two);
|
||||
|
||||
[DllImport(__DllName, EntryPoint = "value_at_risk", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern double* value_at_risk(double* arr, nuint len, double confidence);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,8 @@ const double *covariance(const double *arr, size_t len, const double *arr_two, s
|
||||
|
||||
double interest_compound(double principal, double rate, double time, double n);
|
||||
|
||||
const double *value_at_risk(const double *arr, size_t len, double confidence);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
} // namespace finlib
|
@ -14,7 +14,7 @@ fn main() {
|
||||
.with_config(config)
|
||||
.generate()
|
||||
.expect("Unable to generate bindings")
|
||||
.write_to_file("../finlib-cpp/finlib-native.h");
|
||||
.write_to_file("../finlib-cpp/include/finlib-native.h");
|
||||
|
||||
csbindgen::Builder::default()
|
||||
.input_extern_file("src/lib.rs")
|
||||
|
@ -23,4 +23,14 @@ pub unsafe extern "C" fn covariance(arr: *const f64, len: usize, arr_two: *const
|
||||
Some(v) => Box::into_raw(Box::new(v))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn value_at_risk(arr: *const f64, len: usize, confidence: f64) -> *const f64 {
|
||||
let input_array = unsafe {
|
||||
assert!(!arr.is_null());
|
||||
slice::from_raw_parts(arr, len)
|
||||
};
|
||||
|
||||
Box::into_raw(Box::new(finlib::risk::var::historical::value_at_risk(input_array, confidence)))
|
||||
}
|
@ -6,7 +6,8 @@ edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
pyo3 = { workspace = true, optional = true }
|
||||
rayon = { workspace = true }
|
||||
rayon = { workspace = true, optional = true }
|
||||
|
||||
[features]
|
||||
py = ["dep:pyo3"]
|
||||
py = ["dep:pyo3"]
|
||||
parallel = ["dep::rayon"]
|
@ -1,2 +1,4 @@
|
||||
pub mod interest;
|
||||
pub mod stats;
|
||||
pub mod stats;
|
||||
pub mod util;
|
||||
pub mod risk;
|
1
finlib/src/risk/mod.rs
Normal file
1
finlib/src/risk/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod var;
|
27
finlib/src/risk/var/historical.rs
Normal file
27
finlib/src/risk/var/historical.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use crate::util::roc::rates_of_change;
|
||||
|
||||
#[cfg(feature = "parallel")]
|
||||
use rayon::prelude::*;
|
||||
|
||||
// https://www.simtrade.fr/blog_simtrade/historical-method-var-calculation/
|
||||
|
||||
pub fn value_at_risk(values: &[f64], confidence: f64) -> f64 {
|
||||
let mut roc = rates_of_change(values).collect::<Vec<_>>();
|
||||
// roc.par_sort_by(|x, y| x.partial_cmp(y).unwrap());
|
||||
roc.sort_by(|x, y| x.partial_cmp(y).unwrap());
|
||||
|
||||
let threshold = (confidence * roc.len() as f64).floor() as usize;
|
||||
|
||||
roc[threshold]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn var_test() {
|
||||
let result = value_at_risk(&[1f64, 2f64, 4f64, 5f64], 0.01f64);
|
||||
assert_eq!(result, 0.25f64);
|
||||
}
|
||||
}
|
1
finlib/src/risk/var/mod.rs
Normal file
1
finlib/src/risk/var/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod historical;
|
@ -1,3 +1,4 @@
|
||||
#[cfg(feature = "parallel")]
|
||||
use rayon::prelude::*;
|
||||
use super::mean;
|
||||
|
||||
@ -8,7 +9,13 @@ pub fn covariance(slice: &[f64], slice_two: &[f64]) -> Option<f64>
|
||||
let mean_1 = mean(slice);
|
||||
let mean_2 = mean(slice_two);
|
||||
|
||||
Some(slice.par_iter().zip(slice_two.par_iter())
|
||||
Some(slice
|
||||
// .par_iter()
|
||||
.iter()
|
||||
.zip(slice_two
|
||||
// .par_iter()
|
||||
.iter()
|
||||
)
|
||||
.map(|(x, y)| (x - mean_1) * (y - mean_2))
|
||||
.sum::<f64>()
|
||||
/ ((slice.len() - 1) as f64))
|
||||
|
@ -1,29 +1,37 @@
|
||||
mod covariance;
|
||||
pub use covariance::*;
|
||||
|
||||
#[cfg(feature = "parallel")]
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn mean(slice: &[f64]) -> f64
|
||||
{
|
||||
slice.par_iter().sum::<f64>() / slice.len() as f64
|
||||
slice
|
||||
// .par_iter()
|
||||
.iter()
|
||||
.sum::<f64>() / slice.len() as f64
|
||||
}
|
||||
|
||||
pub fn population_variance(slice: &[f64]) -> f64
|
||||
{
|
||||
let mean = mean(slice);
|
||||
slice.par_iter()
|
||||
slice
|
||||
// .par_iter()
|
||||
.iter()
|
||||
.map(|x| f64::powi(x - mean, 2))
|
||||
.sum::<f64>()
|
||||
/ slice.len() as f64
|
||||
/ slice.len() as f64
|
||||
}
|
||||
|
||||
pub fn sample_variance(slice: &[f64]) -> f64
|
||||
{
|
||||
let mean = mean(slice);
|
||||
slice.par_iter()
|
||||
slice
|
||||
// .par_iter()
|
||||
.iter()
|
||||
.map(|x| f64::powi(x - mean, 2))
|
||||
.sum::<f64>()
|
||||
/ ((slice.len() - 1) as f64)
|
||||
/ ((slice.len() - 1) as f64)
|
||||
}
|
||||
|
||||
pub fn population_std_dev(slice: &[f64]) -> f64
|
||||
|
1
finlib/src/util/mod.rs
Normal file
1
finlib/src/util/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod roc;
|
33
finlib/src/util/roc.rs
Normal file
33
finlib/src/util/roc.rs
Normal file
@ -0,0 +1,33 @@
|
||||
#[cfg(feature = "parallel")]
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn changes(values: &[f64]) -> impl Iterator<Item = f64> + use<'_> {
|
||||
values
|
||||
.windows(2)
|
||||
// .par_windows(2)
|
||||
.map(|x| x[1] - x[0])
|
||||
}
|
||||
|
||||
pub fn rates_of_change(values: &[f64]) -> impl Iterator<Item = f64> + use<'_> {
|
||||
values
|
||||
.windows(2)
|
||||
// .par_windows(2)
|
||||
.map(|x| (x[1] - x[0])/x[0])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn change_test() {
|
||||
let result = changes(&[1f64, 2f64, 4f64, 5f64]).collect::<Vec<_>>();
|
||||
assert_eq!(result, vec![1f64, 2f64, 1f64]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roc_test() {
|
||||
let result = rates_of_change(&[1f64, 2f64, 4f64, 5f64]).collect::<Vec<_>>();
|
||||
assert_eq!(result, vec![1f64, 1f64, 0.25f64]);
|
||||
}
|
||||
}
|
@ -12,7 +12,19 @@ pub fn covariance(slice: Vec<f64>, slice_two: Vec<f64>) -> PyResult<Option<f64>>
|
||||
|
||||
#[pymodule]
|
||||
fn pyfinlib(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(compound, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(covariance, m)?)?;
|
||||
register_interest_module(m);
|
||||
register_stats_module(m);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_interest_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
let child_module = PyModule::new(parent_module.py(), "interest")?;
|
||||
child_module.add_function(wrap_pyfunction!(compound, &child_module)?)?;
|
||||
parent_module.add_submodule(&child_module)
|
||||
}
|
||||
|
||||
fn register_stats_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
let child_module = PyModule::new(parent_module.py(), "stats")?;
|
||||
child_module.add_function(wrap_pyfunction!(covariance, &child_module)?)?;
|
||||
parent_module.add_submodule(&child_module)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user