hayden4r4 / blackscholes-rust

A Black-Scholes pricing model built in Rust
MIT License
11 stars 6 forks source link

Increase test coverage #8

Open hayden4r4 opened 3 months ago

hayden4r4 commented 3 months ago

We have pretty limited test coverage at the moment, only covering the pricing api.

Would love to expand test coverage to the IV calcs and greeks. Some of this is difficult as we have some little known greeks included, that could make it difficult to find a reliable library to test against.

Would appreciate any help in either recommending a trusted library to test against, or in actually expanding out the test coverage.

day01 commented 3 months ago

I agree, it is important. I've started working on wasm compilation (mving cpp to rust). and and and... i cannot finish it without tests... so I can add some tests to it.

i recommend test it on rust lvl. it is important for my next PR x]

day01 commented 3 months ago

I have concern fn calc_delta(&self) -> Result<f32, String> -> pub fn calc_nd1nd2(inputs: &Inputs) -> Result<(f32, f32), String> { -> pub fn calc_d1d2(inputs: &Inputs) -> Result<(f32, f32), String> { to be precise: let numd1 = (inputs.s / inputs.k).ln() + (inputs.r - inputs.q + (sigma.powi(2)) / 2.0) * inputs.t;

for strike price inputs.k = 0.0 we get result from calc_nd1nd2 equals +inf, +inf. Implementation of normal.cdf correctly returns 1. So in my opinion it shadows error for us.

links:

In my opinion we should return Err for these case. how do you think?


UPDATE: These error is not only here, bcs it is common function ...

day01 commented 3 months ago

In stochastic tests I get an issue


        #[test]
        fn test_implied_volatility_random(f in 50.0_f64..150.0_f64, k in 50.0_f64..150.0_f64, price in 0.01_f64..100.0_f64, t in 0.1_f64..2.0_f64, q in any::<u8>().prop_map(|x| if x % 2 == 0 { OptionType::Call } else { OptionType::Put })) {
            let iv = implied_volatility_from_a_transformed_rational_guess(price, f, k, t, q);

            let a = f64::total_cmp(&iv, &0.0);
            println!("Comparison result: {:?}", a);
            println!("Random Test - Implied Volatility: {:?}, f {:?}, k {:?} price {:?} t {:?} q {:?}", iv, f, k, price, t, q);
            assert!(!iv.is_sign_negative(), "Implied Volatility is negative");
            assert!(iv >= 0.0, "Implied Volatility is {}", iv);
        }

with parameters like:

Comparison result: Less
Random Test - Implied Volatility: -1.7976931348623157e308, f 145.23533118406826, k 50.0 price 0.01 t 0.1 q Call
thread 'lets_be_rational::tests::test_implied_volatility_random' panicked at src/lets_be_rational.rs:290:13:
Implied Volatility is negative

Based on my knowledge IV cannot be negative. Probably algorithm in cpp has a bug in numeric calculation, somewhere we should round to zero or we got underflow unchecked...

Suggestions?

hayden4r4 commented 2 months ago

In my opinion we should return Err for these case. how do you think? Thanks for spotting this, I agree that strike of 0 is undefined in BS model, we should return an error for that case, this is an easy one to fix I can make this change when I get a minute to work on it

hayden4r4 commented 2 months ago

Based on my knowledge IV cannot be negative. Probably algorithm in cpp has a bug in numeric calculation, somewhere we should round to zero or we got underflow unchecked...

Suggestions? I agree that this is most likely a bug in Jackel's implementation but I haven't had a chance to run a debugger on it, this is something we could look to fix or at least check for and handle in a port of the cpp code to rust.