Compare commits

...

2 Commits
master ... dev

Author SHA1 Message Date
8d529ffacc
adding rsi indicator 2025-02-21 21:51:19 +00:00
a730104083
adding .NET and C++ receiver code 2025-02-21 00:58:32 +00:00
35 changed files with 772 additions and 118 deletions

1
.gitignore vendored

@ -1,6 +1,7 @@
/target
bin
obj
cmake-build-debug
.venv
.idea
/finlib-wasm/pkg

8
Cargo.lock generated

@ -340,7 +340,7 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "finlib"
version = "0.0.6"
version = "0.0.7"
dependencies = [
"criterion",
"getrandom 0.2.15",
@ -357,7 +357,7 @@ dependencies = [
[[package]]
name = "finlib-ffi"
version = "0.0.6"
version = "0.0.7"
dependencies = [
"cbindgen",
"csbindgen",
@ -366,7 +366,7 @@ dependencies = [
[[package]]
name = "finlib-wasm"
version = "0.0.6"
version = "0.0.7"
dependencies = [
"console_error_panic_hook",
"console_log",
@ -759,7 +759,7 @@ dependencies = [
[[package]]
name = "pyfinlib"
version = "0.0.6"
version = "0.0.7"
dependencies = [
"finlib",
"log",

@ -14,7 +14,7 @@ default-members = [
]
[workspace.package]
version = "0.0.6"
version = "0.0.7"
authors = ["sarsoo <andy@sarsoo.xyz>"]
description = "Quant finance functions implemented in Rust"
edition = "2021"

@ -30,5 +30,5 @@ public class HistoricalVar
public double FinLibDotnet() => HistoricalVARDotnet.ValueAtRisk(data.ToList(), confidence);
[Benchmark]
public double FinLibRust() => FinLib.Risk.ValueAtRisk.Historical(data, confidence);
public double FinLibRust() => FinLib.Risk.ValueAtRisk.Historical(data, confidence)!.Value;
}

@ -0,0 +1,34 @@
using FluentAssertions;
namespace FinLib.Test;
public class PortfolioTest
{
[Test]
public void TestPortfolioCreation()
{
using var portfolio = new Portfolio();
portfolio.AddAsset(0.5, "first", [0.5, 0.5, 0.5, 0.5]);
portfolio.AddAsset(0.5, "second", [0.5, 0.5, 0.5, 0.5]);
var (mean, std) = portfolio.GetMeanAndStdDev()!.Value;
mean.Should().Be(0);
std.Should().Be(0);
}
[Test]
public void TestPortfolioValid()
{
var portfolio = new Portfolio();
portfolio.AddAsset(0.5, "first", [0.5, 0.5, 0.5, 0.5]);
portfolio.AddAsset(0.5, "second", [0.5, 0.5, 0.5]);
portfolio.IsValid().Should().BeFalse();
var portfolio2 = new Portfolio();
portfolio2.AddAsset(0.5, "first", [0.5, 0.5, 0.5, 0.5]);
portfolio2.AddAsset(0.5, "second", [0.5, 0.5, 0.5, 0.5]);
portfolio2.IsValid().Should().BeTrue();
}
}

@ -0,0 +1,7 @@
namespace FinLib;
public static class Indicators
{
public static double RelativeStrengthIndicator(double timePeriod, double averageGain, double averageLoss) => NativeMethods.relative_strength_indicator(timePeriod, averageGain, averageLoss);
public static double RelativeStrengthIndicatorSmoothed(double timePeriod, double previousAverageGain, double currentGain, double previousAverageLoss, double currentLoss) => NativeMethods.relative_strength_indicator_smoothed(timePeriod, previousAverageGain, currentGain, previousAverageLoss, currentLoss);
}

@ -7,26 +7,36 @@ namespace FinLib.Risk;
public static class ValueAtRisk
{
public static double Historical(IEnumerable<double> values, double confidence)
public static double? Historical(IEnumerable<double> values, double confidence)
{
unsafe {
var valueArr = values.ToArray();
fixed (double* ptrOne = valueArr) {
var ret = NativeMethods.historical_value_at_risk(ptrOne, (UIntPtr)valueArr.Length, confidence);
return *ret;
if (ret.is_valid)
{
return ret.val;
}
return null;
}
}
}
public static double VarCovar(IEnumerable<double> values, double confidence)
public static double? VarCovar(IEnumerable<double> values, double confidence)
{
unsafe {
var valueArr = values.ToArray();
fixed (double* ptrOne = valueArr) {
var ret = NativeMethods.varcovar_value_at_risk(ptrOne, (UIntPtr)valueArr.Length, confidence);
return *ret;
if (ret.is_valid)
{
return ret.val;
}
return null;
}
}
}

@ -16,14 +16,12 @@ public static class Stats
fixed (double* ptrTwo = valuesTwoArr) {
var ret = NativeMethods.covariance(ptrOne, (UIntPtr)valuesOneArr.Length, ptrTwo, (UIntPtr) valuesTwoArr.Length);
if (ret == null)
if (ret.is_valid)
{
return null;
}
else
{
return *ret;
return ret.val;
}
return null;
}
}
}

@ -1,9 +1,7 @@
using GroupedNativeMethodsGenerator;
namespace FinLib;
[GroupedNativeMethods]
internal static unsafe partial class NativeMethods
internal static partial class NativeMethods
{
}

@ -20,56 +20,96 @@ namespace FinLib
internal static extern double interest_compound(double principal, double rate, double time, double n);
[DllImport(__DllName, EntryPoint = "covariance", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern double* covariance(double* arr, nuint len, double* arr_two, nuint len_two);
internal static extern NullableFloat covariance(double* arr, nuint len, double* arr_two, nuint len_two);
[DllImport(__DllName, EntryPoint = "historical_value_at_risk", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern double* historical_value_at_risk(double* arr, nuint len, double confidence);
internal static extern NullableFloat historical_value_at_risk(double* arr, nuint len, double confidence);
[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);
internal static extern NullableFloat 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);
[DllImport(__DllName, EntryPoint = "portfolio_asset_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern PortfolioAsset_native* portfolio_asset_new(double portfolio_weight, byte* name, int name_len, double* values, nuint len);
[DllImport(__DllName, EntryPoint = "portfolio_asset_destroy", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void portfolio_asset_destroy(PortfolioAsset_native* asset);
[DllImport(__DllName, EntryPoint = "portfolio_asset_apply_rates_of_change", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void portfolio_asset_apply_rates_of_change(PortfolioAsset_native* asset);
[DllImport(__DllName, EntryPoint = "portfolio_asset_get_mean_and_std", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern Tuple portfolio_asset_get_mean_and_std(PortfolioAsset_native* asset);
[DllImport(__DllName, EntryPoint = "portfolio_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern Portfolio_native* portfolio_new();
[DllImport(__DllName, EntryPoint = "portfolio_destroy", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void portfolio_destroy(Portfolio_native* portfolio);
[DllImport(__DllName, EntryPoint = "portfolio_add_asset", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void portfolio_add_asset(Portfolio_native* portfolio, PortfolioAsset_native* asset);
[DllImport(__DllName, EntryPoint = "portfolio_apply_rates_of_change", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void portfolio_apply_rates_of_change(Portfolio_native* portfolio);
[DllImport(__DllName, EntryPoint = "portfolio_valid_sizes", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.U1)]
internal static extern bool portfolio_valid_sizes(Portfolio_native* portfolio);
[DllImport(__DllName, EntryPoint = "portfolio_valid_weights", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.U1)]
internal static extern bool portfolio_valid_weights(Portfolio_native* portfolio);
[DllImport(__DllName, EntryPoint = "portfolio_is_valid", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.U1)]
internal static extern bool portfolio_is_valid(Portfolio_native* portfolio);
[DllImport(__DllName, EntryPoint = "portfolio_get_mean_and_std", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern Tuple portfolio_get_mean_and_std(Portfolio_native* portfolio);
[DllImport(__DllName, EntryPoint = "portfolio_value_at_risk", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern NullableFloat portfolio_value_at_risk(Portfolio_native* portfolio, double confidence, double initial_investment);
[DllImport(__DllName, EntryPoint = "portfolio_value_at_risk_percent", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern NullableFloat portfolio_value_at_risk_percent(Portfolio_native* portfolio, double confidence);
[DllImport(__DllName, EntryPoint = "relative_strength_indicator", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern double relative_strength_indicator(double time_period, double average_gain, double average_loss);
[DllImport(__DllName, EntryPoint = "relative_strength_indicator_smoothed", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern double relative_strength_indicator_smoothed(double time_period, double previous_average_gain, double current_gain, double previous_average_loss, double current_loss);
}
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct Portfolio
internal unsafe partial struct Tuple
{
public double one;
public double two;
[MarshalAs(UnmanagedType.U1)] public bool is_valid;
}
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct NullableFloat
{
public double val;
[MarshalAs(UnmanagedType.U1)] public bool is_valid;
}
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct Portfolio_native
{
}
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct PortfolioAsset
internal unsafe partial struct PortfolioAsset_native
{
}
[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,
}
}

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace FinLib;
public class Portfolio: IDisposable
{
private readonly unsafe Portfolio_native* _portfolio;
internal unsafe Portfolio_native* GetPtr() => _portfolio;
public Portfolio()
{
unsafe
{
_portfolio = NativeMethods.portfolio_new();
}
}
public void AddAsset(double portfolioWeight, string assetName, IEnumerable<double> values)
{
unsafe
{
var n = Encoding.UTF8.GetBytes(assetName);
var v = values.ToArray();
fixed (byte* namePtr = n)
fixed (double* valuesPtr = v){
NativeMethods.portfolio_add_asset(_portfolio, NativeMethods.portfolio_asset_new(portfolioWeight, namePtr, assetName.Length, valuesPtr, (UIntPtr)v.Length));
}
}
}
public bool ValidSize()
{
unsafe
{
return NativeMethods.portfolio_valid_sizes(_portfolio);
}
}
public bool ValidWeights()
{
unsafe
{
return NativeMethods.portfolio_valid_weights(_portfolio);
}
}
public bool IsValid()
{
unsafe
{
return NativeMethods.portfolio_is_valid(_portfolio);
}
}
public void ApplyRatesOfChange()
{
unsafe
{
NativeMethods.portfolio_apply_rates_of_change(_portfolio);
}
}
public (double, double)? GetMeanAndStdDev()
{
unsafe
{
var ret = NativeMethods.portfolio_get_mean_and_std(_portfolio);
if (ret.is_valid)
{
return (ret.one, ret.two);
}
return null;
}
}
public double? ValueAtRisk(double confidence, double initialInvestment)
{
unsafe
{
var ret = NativeMethods.portfolio_value_at_risk(_portfolio, confidence, initialInvestment);
if (ret.is_valid)
{
return ret.val;
}
return null;
}
}
public double? ValueAtRiskPercent(double confidence)
{
unsafe
{
var ret = NativeMethods.portfolio_value_at_risk_percent(_portfolio, confidence);
if (ret.is_valid)
{
return ret.val;
}
return null;
}
}
private void ReleaseUnmanagedResources()
{
unsafe
{
NativeMethods.portfolio_destroy(_portfolio);
}
}
public void Dispose()
{
ReleaseUnmanagedResources();
GC.SuppressFinalize(this);
}
~Portfolio()
{
ReleaseUnmanagedResources();
}
}

27
finlib-cpp/CMakeLists.txt Normal file

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.19)
if(${CMAKE_VERSION} VERSION_LESS 3.19)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
endif()
include(FetchContent)
project(finlib-cpp)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR})
set(FINLIB_INSTALL_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
set(FINLIB_INSTALL_BIN_DIR ${PROJECT_SOURCE_DIR}/bin)
set(FINLIB_INSTALL_LIB_DIR ${PROJECT_SOURCE_DIR}/lib)
set(FINLIB_HEADERS_DIR ${PROJECT_SOURCE_DIR}/src/finlib)
include_directories(${FINLIB_INSTALL_INCLUDE_DIR})
include_directories(${FINLIB_HEADERS_DIR})
add_subdirectory(src)
add_subdirectory(test)

3
finlib-cpp/README.md Normal file

@ -0,0 +1,3 @@
# finlib-cpp
[StackOverflow - How can I build Rust code with a C++/Qt/CMake project?](https://stackoverflow.com/questions/31162438/how-can-i-build-rust-code-with-a-c-qt-cmake-project)

@ -1,32 +0,0 @@
#pragma once
/* Generated with cbindgen:0.28.0 */
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
namespace finlib {
extern "C" {
const double *covariance(const double *arr, size_t len, const double *arr_two, size_t len_two);
const double *historical_value_at_risk(const double *arr, size_t len, double confidence);
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"
} // namespace finlib

@ -0,0 +1,93 @@
#pragma once
/* Generated with cbindgen:0.28.0 */
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
namespace finlib {
/// Describes a Portfolio as a collection of [`PortfolioAsset`]s
struct Portfolio;
/// Describes a single instrument as a list of previous values with an associated portfolio proportion
struct PortfolioAsset;
struct NullableFloat
{
double Val;
bool IsValid;
};
struct Tuple
{
double One;
double Two;
bool IsValid;
};
extern "C" {
NullableFloat covariance(const double *arr, size_t len, const double *arr_two, size_t len_two);
NullableFloat historical_value_at_risk(const double *arr, size_t len, double confidence);
double interest_compound(double principal, double rate, double time, double n);
void portfolio_add_asset(Portfolio *portfolio, PortfolioAsset *asset);
void portfolio_apply_rates_of_change(Portfolio *portfolio);
void portfolio_asset_apply_rates_of_change(PortfolioAsset *asset);
void portfolio_asset_destroy(PortfolioAsset *asset);
Tuple portfolio_asset_get_mean_and_std(PortfolioAsset *asset);
PortfolioAsset *portfolio_asset_new(double portfolio_weight,
const uint8_t *name,
int32_t name_len,
const double *values,
size_t len);
void portfolio_destroy(Portfolio *portfolio);
Tuple portfolio_get_mean_and_std(Portfolio *portfolio);
bool portfolio_is_valid(Portfolio *portfolio);
Portfolio *portfolio_new();
bool portfolio_valid_sizes(Portfolio *portfolio);
bool portfolio_valid_weights(Portfolio *portfolio);
NullableFloat portfolio_value_at_risk(Portfolio *portfolio,
double confidence,
double initial_investment);
NullableFloat portfolio_value_at_risk_percent(Portfolio *portfolio, double confidence);
double relative_strength_indicator(double time_period, double average_gain, double average_loss);
double relative_strength_indicator_smoothed(double time_period,
double previous_average_gain,
double current_gain,
double previous_average_loss,
double current_loss);
double scale_value_at_risk(double initial_value, ptrdiff_t time_cycles);
NullableFloat varcovar_value_at_risk(const double *arr, size_t len, double confidence);
} // extern "C"
} // namespace finlib

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.19)
project(finexe)
add_subdirectory(finlib)
set(SOURCE_FILES
main.cpp
)
add_executable(finexe ${SOURCE_FILES})
target_link_libraries(finexe finlib)
install(TARGETS finexe DESTINATION ${FINLIB_INSTALL_BIN_DIR})

@ -0,0 +1,29 @@
include(ExternalProject)
project(finlib C CXX)
set(SOURCE_FILES
finlib.cpp
)
add_library(finlib SHARED STATIC ${SOURCE_FILES})
add_library(finlib::finlib ALIAS finlib)
ExternalProject_Add(
finlibrs
DOWNLOAD_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND cargo build
COMMAND cargo build --release
BINARY_DIR "${CMAKE_SOURCE_DIR}/../"
INSTALL_COMMAND ""
LOG_BUILD ON)
add_dependencies(finlib finlibrs)
target_link_libraries(finlib
debug "${CMAKE_SOURCE_DIR}/../target/debug/libfinlib_ffi.a"
optimized "${CMAKE_SOURCE_DIR}/../target/release/libfinlib_ffi.a"
)
install(TARGETS finlib DESTINATION ${FINLIB_INSTALL_LIB_DIR})
install(FILES finlib.hpp DESTINATION ${FINLIB_INSTALL_INCLUDE_DIR})

@ -0,0 +1,17 @@
//
// Created by Andy Pack on 21/02/2025.
//
#include "finlib.hpp"
#include <iostream>
namespace finlib {
void init() {
std::cout << "Initializing..." << std::endl;
auto ret = interest_compound(100, .05, 1, 1.0);
std::cout << "Answer: " << ret << std::endl;
}
}

@ -0,0 +1,7 @@
#pragma once
#include <finlib/finlib-native.h>
namespace finlib {
void init();
}

11
finlib-cpp/src/main.cpp Normal file

@ -0,0 +1,11 @@
#include <iostream>
#include <finlib.hpp>
using namespace std;
int main(int argc, const char *argv[]) {
finlib::init();
return 0;
}

@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.19)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(finlib_tests)
include_directories(${FINLIB_HEADERS_DIR})
set(SOURCE_FILES
src/basic_test.cpp
)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest
GIT_TAG v1.16.0
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
add_executable(finlib_tests ${SOURCE_FILES})
target_link_libraries(finlib_tests finlib GTest::gtest_main)
install(TARGETS finlib_tests DESTINATION bin)
include(GoogleTest)
gtest_discover_tests(finlib_tests)

@ -0,0 +1,28 @@
#include <gtest/gtest.h>
using namespace std;
// Demonstrate some basic assertions.
TEST(HelloTest, BasicAssertions) {
// Expect two strings not to be equal.
EXPECT_STRNE("hello", "world");
// Expect equality.
EXPECT_EQ(7 * 6, 42);
}
// Demonstrate some basic assertions.
TEST(HelloTest, BasicAssertions2) {
// Expect two strings not to be equal.
EXPECT_STRNE("hello", "world");
// Expect equality.
EXPECT_EQ(7 * 6, 42);
}
// Demonstrate some basic assertions.
TEST(HelloTest, BasicAssertions3) {
// Expect two strings not to be equal.
EXPECT_STRNE("hello", "world");
// Expect equality.
EXPECT_EQ(7 * 6, 42);
}

@ -11,7 +11,7 @@ readme.workspace = true
license.workspace = true
[lib]
crate-type = ["cdylib"]
crate-type = ["cdylib", "staticlib"]
[dependencies]
finlib = { path = "../finlib", features = ["ffi"] }

@ -14,24 +14,20 @@ fn main() {
.with_config(config)
.generate()
.expect("Unable to generate bindings")
.write_to_file("../finlib-cpp/include/finlib-native.h");
.write_to_file("../finlib-cpp/include/finlib/finlib-native.h");
csbindgen::Builder::default()
.input_extern_file("src/lib.rs")
.input_extern_file("../finlib/src/lib.rs")
.input_extern_file("src/portfolio.rs")
.input_extern_file("src/indicators.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")
.csharp_type_rename(|rust_type_name| match rust_type_name.as_str() { // optional, default: `|x| x`
"Portfolio" => "Portfolio_native".into(),
"PortfolioAsset" => "PortfolioAsset_native".into(),
_ => rust_type_name,
})
.generate_csharp_file("../FinLib.NET/FinLib/NativeMethods.g.cs")
.unwrap();
}

@ -0,0 +1,10 @@
#[no_mangle]
pub extern "C" fn relative_strength_indicator(time_period: f64, average_gain: f64, average_loss: f64) -> f64 {
finlib::indicators::rsi::relative_strength_indicator(time_period, average_gain, average_loss)
}
#[no_mangle]
pub extern "C" fn relative_strength_indicator_smoothed(time_period: f64, previous_average_gain: f64, current_gain: f64, previous_average_loss: f64, current_loss: f64) -> f64 {
finlib::indicators::rsi::relative_strength_indicator_smoothed(time_period, previous_average_gain, current_gain, previous_average_loss, current_loss)
}

@ -1,13 +1,30 @@
use std::ptr::null;
pub mod portfolio;
pub mod indicators;
use std::slice;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Tuple {
one: f64,
two: f64,
is_valid: bool
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct NullableFloat {
val: f64,
is_valid: bool
}
#[no_mangle]
pub extern "C" fn interest_compound(principal: f64, rate: f64, time: f64, n: f64) -> f64 {
finlib::interest::compound(principal, rate, time, n)
}
#[no_mangle]
pub unsafe extern "C" fn covariance(arr: *const f64, len: usize, arr_two: *const f64, len_two: usize) -> *const f64 {
pub unsafe extern "C" fn covariance(arr: *const f64, len: usize, arr_two: *const f64, len_two: usize) -> NullableFloat {
let input_array = unsafe {
assert!(!arr.is_null());
slice::from_raw_parts(arr, len)
@ -19,30 +36,36 @@ pub unsafe extern "C" fn covariance(arr: *const f64, len: usize, arr_two: *const
};
match finlib::stats::covariance(input_array, input_array_two) {
None => null(),
Some(v) => Box::into_raw(Box::new(v))
None => NullableFloat { val: 0.0, is_valid: false },
Some(v) => NullableFloat { val: v, is_valid: true }
}
}
#[no_mangle]
pub unsafe extern "C" fn historical_value_at_risk(arr: *const f64, len: usize, confidence: f64) -> *const f64 {
pub unsafe extern "C" fn historical_value_at_risk(arr: *const f64, len: usize, confidence: f64) -> NullableFloat {
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)))
NullableFloat {
val: finlib::risk::var::historical::value_at_risk(input_array, confidence),
is_valid: true
}
}
#[no_mangle]
pub unsafe extern "C" fn varcovar_value_at_risk(arr: *const f64, len: usize, confidence: f64) -> *const f64 {
pub unsafe extern "C" fn varcovar_value_at_risk(arr: *const f64, len: usize, confidence: f64) -> NullableFloat {
let input_array = unsafe {
assert!(!arr.is_null());
slice::from_raw_parts(arr, len)
};
Box::into_raw(Box::new(finlib::risk::var::varcovar::value_at_risk_percent(input_array, confidence)))
NullableFloat {
val: finlib::risk::var::varcovar::value_at_risk_percent(input_array, confidence),
is_valid: true
}
}
#[no_mangle]

112
finlib-ffi/src/portfolio.rs Normal file

@ -0,0 +1,112 @@
use std::{ptr, slice};
use finlib::risk::portfolio::{Portfolio, PortfolioAsset};
use crate::{NullableFloat, Tuple};
#[no_mangle]
pub unsafe extern "C" fn portfolio_asset_new(portfolio_weight: f64, name: *const u8, name_len: i32, values: *const f64, len: usize) -> *mut PortfolioAsset {
if name.is_null() {
return ptr::null_mut();
}
let slice = slice::from_raw_parts(name, name_len as usize);
let name = String::from_utf8_unchecked(slice.to_vec());
let input_array = unsafe {
assert!(!values.is_null());
slice::from_raw_parts(values, len)
};
Box::into_raw(Box::new(PortfolioAsset::new(portfolio_weight, name, input_array.to_vec())))
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_asset_destroy(asset: *mut PortfolioAsset) {
if !asset.is_null() {
drop(Box::from_raw(asset));
}
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_asset_apply_rates_of_change(asset: *mut PortfolioAsset) {
(&mut *asset).apply_rates_of_change();
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_asset_get_mean_and_std(asset: *mut PortfolioAsset) -> Tuple {
match (&mut *asset).get_mean_and_std() {
None => Tuple { one: 0.0, two: 0.0, is_valid: false },
Some((one, two)) => {
Tuple {
one, two, is_valid: true
}
}
}
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_new() -> *mut Portfolio {
Box::into_raw(Box::new(Portfolio::from(vec![])))
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_destroy(portfolio: *mut Portfolio) {
if !portfolio.is_null() {
drop(Box::from_raw(portfolio));
}
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_add_asset(portfolio: *mut Portfolio, asset: *mut PortfolioAsset) {
(&mut *portfolio).add_asset((*asset).clone());
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_apply_rates_of_change(portfolio: *mut Portfolio) {
(&mut *portfolio).apply_rates_of_change()
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_valid_sizes(portfolio: *mut Portfolio) -> bool {
(&mut *portfolio).valid_sizes()
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_valid_weights(portfolio: *mut Portfolio) -> bool {
(&mut *portfolio).valid_weights()
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_is_valid(portfolio: *mut Portfolio) -> bool {
(&mut *portfolio).is_valid()
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_get_mean_and_std(portfolio: *mut Portfolio) -> Tuple {
match (&mut *portfolio).get_mean_and_std() {
None => Tuple { one: 0.0, two: 0.0, is_valid: false },
Some((one, two)) => {
Tuple {
one, two, is_valid: true
}
}
}
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_value_at_risk(portfolio: *mut Portfolio, confidence: f64, initial_investment: f64) -> NullableFloat {
match (&mut *portfolio).value_at_risk(confidence, initial_investment) {
None => NullableFloat { val: 0.0, is_valid: false },
Some(v) => NullableFloat { val: v, is_valid: true }
}
}
#[no_mangle]
pub unsafe extern "C" fn portfolio_value_at_risk_percent(portfolio: *mut Portfolio, confidence: f64) -> NullableFloat {
match (&mut *portfolio).value_at_risk_percent(confidence) {
None => NullableFloat { val: 0.0, is_valid: false },
Some(v) => NullableFloat { val: v, is_valid: true }
}
}

@ -14,6 +14,21 @@ pub fn init_logging() {
}
}
#[wasm_bindgen]
pub struct RelativeStrengthIndicator { }
#[wasm_bindgen]
impl RelativeStrengthIndicator {
pub fn relative_strength_indicator(time_period: f64, average_gain: f64, average_loss: f64) -> f64 {
finlib::indicators::rsi::relative_strength_indicator(time_period, average_gain, average_loss)
}
pub fn relative_strength_indicator_smoothed(time_period: f64, previous_average_gain: f64, current_gain: f64, previous_average_loss: f64, current_loss: f64) -> f64 {
finlib::indicators::rsi::relative_strength_indicator_smoothed(time_period, previous_average_gain, current_gain, previous_average_loss, current_loss)
}
}
#[wasm_bindgen]
pub struct Interest { }

@ -0,0 +1 @@
pub mod rsi;

@ -0,0 +1,38 @@
pub fn relative_strength_indicator(time_period: f64, average_gain: f64, average_loss: f64) -> f64 {
100. - (
100. /
(1. + (
(average_gain / time_period)
/
(average_loss / time_period)
)
)
)
}
pub fn relative_strength_indicator_smoothed(time_period: f64, previous_average_gain: f64, current_gain: f64, previous_average_loss: f64, current_loss: f64) -> f64 {
100. - (100. /
(1. +
(
((previous_average_gain * time_period) + current_gain)
/
((previous_average_loss * time_period) + current_loss)
)
)
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn relative_strength_indicator_test() {
let result = relative_strength_indicator(14., 1., 0.8);
assert_eq!(f64::floor(result * 100.) / 100., 55.55);
}
}

@ -5,4 +5,5 @@ pub mod stats;
pub mod util;
pub mod risk;
pub mod ffi;
pub mod options;
pub mod options;
pub mod indicators;

@ -1,6 +1,5 @@
use crate::options::blackscholes::{OptionVariables};
use core::ops::Range;
use std::sync::{Arc, Mutex};
use crate::options::blackscholes::{CallOption, Option, OptionVariables, PutOption};
pub struct OptionSurface {
underlying_price: Range<isize>,
@ -84,30 +83,30 @@ impl OptionSurface {
#[cfg(test)]
mod tests {
use crate::options::blackscholes::{generate_options, CallOption, Option, PutOption};
use super::*;
use crate::options::blackscholes::generate_options;
#[test]
fn walk_test() {
let w = OptionSurface::from(
(0 .. 50),
0 .. 50,
(100., 200.),
(0 .. 50),
0 .. 50,
(100., 200.),
(0 .. 5),
0 .. 5,
(0.25, 0.50),
(0 .. 10),
0 .. 10,
(0.05, 0.08),
(0 .. 1),
0 .. 1,
(0.01, 0.02),
(0 .. 10),
0 .. 10,
(30./365.25, 30./365.25),
);
let a = w.walk();
let options = generate_options(&a);
let _ = generate_options(&a);
let a1 = a.first();
let _ = a.first();
}
}

@ -36,10 +36,10 @@ pub enum ValueType {
#[cfg_attr(feature = "ffi", repr(C))]
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct PortfolioAsset {
portfolio_weight: f64,
pub portfolio_weight: f64,
name: String,
values: Vec<f64>,
value_type: ValueType
pub value_type: ValueType
}
impl PortfolioAsset {
@ -84,6 +84,10 @@ impl Portfolio {
}
}
pub fn add_asset(&mut self, asset: PortfolioAsset) {
self.assets.push(asset);
}
/// Return the proportions of a portfolio's assets
///
/// In a properly formed Portfolio these will add up to 1.0

@ -1,7 +1,6 @@
use crate::stats;
use crate::util::roc::rates_of_change;
use rayon::prelude::*;
use statrs::distribution::{ContinuousCDF, Normal};
// https://medium.com/@serdarilarslan/value-at-risk-var-and-its-implementation-in-python-5c9150f73b0e

@ -15,6 +15,26 @@ mod pyfinlib {
Ok(())
}
#[pymodule]
mod indicators {
use super::*;
#[pymodule]
mod rsi {
use super::*;
#[pyfunction]
pub fn relative_strength_indicator(time_period: f64, average_gain: f64, average_loss: f64) -> PyResult<f64> {
Ok(finlib::indicators::rsi::relative_strength_indicator(time_period, average_gain, average_loss))
}
#[pyfunction]
pub fn relative_strength_indicator_smoothed(time_period: f64, previous_average_gain: f64, current_gain: f64, previous_average_loss: f64, current_loss: f64) -> PyResult<f64> {
Ok(finlib::indicators::rsi::relative_strength_indicator_smoothed(time_period, previous_average_gain, current_gain, previous_average_loss, current_loss))
}
}
}
#[pymodule]
mod interest {
use super::*;