Skip to main content

quantwave_core/options_india/
india.rs

1use chrono::NaiveDate;
2
3/// NSE risk-free rate (91-day T-bill, updated quarterly).
4/// Current value as of May 2026.
5pub const NSE_RISK_FREE_RATE: f64 = 0.065;
6
7/// Days to expiry from today to expiry date (calendar days).
8/// NSE options expire at 15:30 IST on expiry day.
9pub fn dte_calendar(expiry_date: NaiveDate, as_of: NaiveDate) -> i32 {
10    let duration = expiry_date.signed_duration_since(as_of);
11    duration.num_days() as i32
12}
13
14/// Time to expiry in years for Black-Scholes.
15/// Uses calendar days / 365.0 as per NSE convention.
16pub fn t_years(expiry_date: NaiveDate, as_of: NaiveDate) -> f64 {
17    let days = dte_calendar(expiry_date, as_of);
18    if days <= 0 {
19        return 0.0;
20    }
21    days as f64 / 365.0
22}
23
24/// NSE standard lot sizes.
25/// Hardcoded current values (Revised: April 2026).
26pub fn nse_lot_size(symbol: &str) -> Option<u32> {
27    match symbol.to_uppercase().as_str() {
28        "NIFTY" => Some(50),
29        "BANKNIFTY" => Some(15),
30        "FINNIFTY" => Some(40),
31        "MIDCPNIFTY" => Some(75),
32        "SENSEX" => Some(10),
33        _ => None,
34    }
35}
36
37/// Moneyness classification (from Call option perspective).
38/// Returns "ITM", "ATM", or "OTM".
39pub fn moneyness(spot: f64, strike: f64) -> &'static str {
40    let lower_bound = spot * 0.998;
41    let upper_bound = spot * 1.002;
42
43    if strike < lower_bound {
44        "ITM"
45    } else if strike > upper_bound {
46        "OTM"
47    } else {
48        "ATM"
49    }
50}