rustalib 1.0.9

A library of technical indicators for financial analysis, similar to TA-Lib
Documentation
//! # Option Greeks Indicators
//!
//! This module provides functions for calculating and analyzing option Greeks
//! to generate trading signals and risk metrics.

use polars::prelude::*;
use std::collections::HashMap;

/// Calculator for Option Greeks and related indicators
pub struct GreeksCalculator {
    /// Risk-free interest rate used in pricing models
    pub risk_free_rate: f64,

    /// Dividend yield of the underlying asset
    pub _dividend_yield: f64,

    /// Model to use for pricing (e.g., "black_scholes", "binomial", "monte_carlo")
    pub pricing_model: String,
}

impl Default for GreeksCalculator {
    fn default() -> Self {
        Self {
            risk_free_rate: 0.02, // 2%
            _dividend_yield: 0.0, // 0%
            pricing_model: "black_scholes".to_string(),
        }
    }
}

/// Calculate basic option Greeks for a single option
///
/// Calculates Delta, Gamma, Theta, Vega, and Rho for a given option
/// based on its parameters.
///
/// # Arguments
///
/// * `spot_price` - Current price of the underlying asset
/// * `strike_price` - Strike price of the option
/// * `time_to_expiry` - Time to expiration in years
/// * `volatility` - Implied volatility as a decimal (e.g., 0.20 for 20%)
/// * `is_call` - Whether the option is a call (true) or put (false)
/// * `risk_free_rate` - Risk-free interest rate as a decimal
/// * `dividend_yield` - Dividend yield as a decimal
///
/// # Returns
///
/// * `HashMap<String, f64>` - Map of Greek names to their values
pub fn calculate_option_greeks(
    spot_price: f64,
    strike_price: f64,
    time_to_expiry: f64,
    volatility: f64,
    is_call: bool,
    risk_free_rate: f64,
    _dividend_yield: f64,
) -> HashMap<String, f64> {
    // For a real implementation, we would calculate these using Black-Scholes
    // or another option pricing model. This is a simplified placeholder.

    let mut greeks = HashMap::new();

    // Simplified calculations (not accurate but reasonable approximations for demo)
    let time_sqrt = time_to_expiry.sqrt();
    let moneyness = spot_price / strike_price;

    // Delta: simplified approximation based on moneyness and time
    let delta = if is_call {
        0.5 + 0.5 * (moneyness - 1.0) / (volatility * time_sqrt)
    } else {
        0.5 - 0.5 * (moneyness - 1.0) / (volatility * time_sqrt)
    };
    greeks.insert("delta".to_string(), delta.clamp(0.0, 1.0));

    // Gamma: highest at-the-money
    let gamma = (1.0 / (spot_price * volatility * time_sqrt * 2.5066))
        * (-((spot_price.ln() - strike_price.ln()).powi(2))
            / (2.0 * volatility.powi(2) * time_to_expiry))
            .exp();
    greeks.insert("gamma".to_string(), gamma);

    // Theta: time decay, higher for options near expiration
    let theta = -spot_price
        * volatility
        * (-((spot_price.ln() - strike_price.ln()).powi(2))
            / (2.0 * volatility.powi(2) * time_to_expiry))
            .exp()
        / (2.0 * time_sqrt * 2.5066)
        / 365.0;
    greeks.insert("theta".to_string(), theta);

    // Vega: sensitivity to volatility changes
    let vega = spot_price
        * time_sqrt
        * (-((spot_price.ln() - strike_price.ln()).powi(2))
            / (2.0 * volatility.powi(2) * time_to_expiry))
            .exp()
        / 2.5066
        / 100.0;
    greeks.insert("vega".to_string(), vega);

    // Rho: sensitivity to interest rate changes
    let rho = if is_call {
        strike_price * time_to_expiry * (-risk_free_rate * time_to_expiry).exp() / 100.0
    } else {
        -strike_price * time_to_expiry * (-risk_free_rate * time_to_expiry).exp() / 100.0
    };
    greeks.insert("rho".to_string(), rho);

    greeks
}

/// Calculate delta-based trading signals
///
/// Generates trading signals based on the delta of options, which
/// represents the expected probability of the option expiring in-the-money.
///
/// # Arguments
///
/// * `options_df` - DataFrame with options data
/// * `delta_thresholds` - Tuple of (lower_delta, upper_delta) for signal generation
///
/// # Returns
///
/// * `Result<Series, PolarsError>` - Series with signal values (-1 = bearish, 0 = neutral, 1 = bullish)
pub fn delta_based_signals(
    options_df: &DataFrame,
    _delta_thresholds: (f64, f64),
) -> Result<Series, PolarsError> {
    // In a real implementation, we would analyze the deltas across different
    // strikes and expirations to determine bullish/bearish sentiment

    // Placeholder implementation
    let signals = vec![0i32; options_df.height()];
    Ok(Series::new("delta_signals".into(), signals))
}

/// Calculate gamma exposure
///
/// Calculates the total gamma exposure at different price levels,
/// which can be used to identify potential market instability points.
///
/// # Arguments
///
/// * `options_df` - DataFrame with options data
/// * `price_range` - Tuple of (min_price, max_price) to calculate gamma exposure
/// * `price_steps` - Number of price steps to calculate gamma exposure for
///
/// # Returns
///
/// * `Result<DataFrame, PolarsError>` - DataFrame with price levels and gamma exposure
pub fn calculate_gamma_exposure(
    _options_df: &DataFrame,
    price_range: (f64, f64),
    price_steps: usize,
) -> Result<DataFrame, PolarsError> {
    // Placeholder implementation
    let (min_price, max_price) = price_range;
    let step_size = (max_price - min_price) / (price_steps as f64);

    let mut price_levels = Vec::with_capacity(price_steps);
    let mut gamma_values = Vec::with_capacity(price_steps);

    for i in 0..price_steps {
        let price = min_price + step_size * (i as f64);
        price_levels.push(price);

        // Placeholder gamma calculation
        let gamma = (-(price - ((min_price + max_price) / 2.0)).powi(2)
            / (max_price - min_price).powi(2)
            * 10.0)
            .exp();
        gamma_values.push(gamma);
    }

    // Create DataFrame with price levels and gamma exposure using df! macro
    df! {
        "price_level" => price_levels,
        "gamma_exposure" => gamma_values
    }
}

/// Find highest theta decay options
///
/// Identifies options with the highest theta decay for potential
/// theta-positive trading strategies.
///
/// # Arguments
///
/// * `options_df` - DataFrame with options data
/// * `min_days_to_expiry` - Minimum days to expiration to consider
/// * `max_days_to_expiry` - Maximum days to expiration to consider
///
/// # Returns
///
/// * `Result<DataFrame, PolarsError>` - Sorted DataFrame with high theta options
pub fn find_highest_theta_options(
    options_df: &DataFrame,
    _min_days_to_expiry: usize,
    _max_days_to_expiry: usize,
) -> Result<DataFrame, PolarsError> {
    // Placeholder implementation - in reality we would filter and sort the options DataFrame
    Ok(options_df.clone())
}

/// Calculate the historical volatility for use in options pricing
///
/// This function calculates the historical volatility of an asset over a specified period
///
/// # Arguments
///
/// * `price_df` - DataFrame with price data
/// * `close_col` - Column name for close prices
/// * `window` - Window size for volatility calculation
/// * `dividend_yield` - Annual dividend yield
pub fn calculate_historical_volatility(
    price_df: &DataFrame,
    _close_col: &str,
    _window: usize,
    _dividend_yield: f64,
) -> Result<Series, PolarsError> {
    // Placeholder implementation
    Ok(Series::new(
        "historical_volatility".into(),
        vec![0.0; price_df.height()],
    ))
}

/// Find options strikes with specific delta values
///
/// This is useful for options strategies that target specific delta values
///
/// # Arguments
///
/// * `options_df` - DataFrame with options data
/// * `delta_thresholds` - Tuple of (call_delta, put_delta) thresholds
pub fn find_strikes_by_delta(
    price_df: &DataFrame,
    _delta_thresholds: (f64, f64),
) -> Result<Series, PolarsError> {
    // Placeholder implementation
    Ok(Series::new(
        "strikes_by_delta".into(),
        vec![0.0; price_df.height()],
    ))
}

/// Screen for options with certain characteristics
///
/// This function identifies options that meet specific criteria
///
/// # Arguments
///
/// * `price_df` - DataFrame with price data
/// * `options_df` - DataFrame with options data
pub fn options_screening(
    price_df: &DataFrame,
    _options_df: &DataFrame,
) -> Result<Series, PolarsError> {
    // Placeholder implementation
    Ok(Series::new(
        "options_screening".into(),
        vec![0.0; price_df.height()],
    ))
}

/// Identify high IV - high premium opportunities
///
/// This function finds options with high implied volatility and premium
///
/// # Arguments
///
/// * `price_df` - DataFrame with price data
/// * `options_df` - DataFrame with options data
/// * `min_days_to_expiry` - Minimum days to expiry
/// * `max_days_to_expiry` - Maximum days to expiry
pub fn high_iv_premium_options(
    price_df: &DataFrame,
    _options_df: &DataFrame,
    _min_days_to_expiry: usize,
    _max_days_to_expiry: usize,
) -> Result<Series, PolarsError> {
    // Placeholder implementation
    Ok(Series::new(
        "high_iv_premium_options".into(),
        vec![0.0; price_df.height()],
    ))
}