use chrono::{Datelike, NaiveDate};
pub const NSE_RISK_FREE_RATE: f64 = 0.065;
pub fn dte_calendar(expiry_date: NaiveDate, as_of: NaiveDate) -> i32 {
let duration = expiry_date.signed_duration_since(as_of);
duration.num_days() as i32
}
pub fn t_years(expiry_date: NaiveDate, as_of: NaiveDate) -> f64 {
let days = dte_calendar(expiry_date, as_of);
if days <= 0 {
return 0.0;
}
days as f64 / 365.0
}
pub fn nse_lot_size_dated(symbol: &str, as_of: Option<NaiveDate>) -> Option<u32> {
match symbol.to_uppercase().as_str() {
"NIFTY" => {
if let Some(date) = as_of {
let d = (date.year(), date.month(), date.day());
if d < (2021, 7, 1) {
Some(75)
} else if d < (2024, 4, 26) {
Some(50)
} else if d < (2024, 11, 21) {
Some(25)
} else if d < (2025, 11, 26) {
Some(75)
} else {
Some(65)
}
} else {
Some(65)
}
}
"BANKNIFTY" => {
if let Some(date) = as_of {
let d = (date.year(), date.month(), date.day());
if d < (2023, 7, 1) {
Some(25)
} else {
Some(15)
}
} else {
Some(15)
}
}
"FINNIFTY" => Some(40),
"MIDCPNIFTY" => Some(75),
"SENSEX" => Some(10),
_ => None,
}
}
pub fn nse_lot_size(symbol: &str) -> Option<u32> {
nse_lot_size_dated(symbol, None)
}
pub fn moneyness(spot: f64, strike: f64) -> &'static str {
let lower_bound = spot * 0.998;
let upper_bound = spot * 1.002;
if strike < lower_bound {
"ITM"
} else if strike > upper_bound {
"OTM"
} else {
"ATM"
}
}