Class: DhanHQ::OptionAnalytics::BlackScholes

Inherits:
Object
  • Object
show all
Defined in:
lib/DhanHQ/option_analytics/black_scholes.rb

Overview

Black-Scholes option pricing model for calculating theoretical option prices and Greeks.

Examples:

Calculate option price

price = DhanHQ::OptionAnalytics::BlackScholes.price(
  spot: 24000,
  strike: 24200,
  time_to_expiry: 0.038, # ~10 days
  risk_free_rate: 0.065,
  volatility: 0.15,
  option_type: :call
)

Class Method Summary collapse

Class Method Details

.greeks(spot:, strike:, time_to_expiry:, risk_free_rate:, volatility:, option_type:) ⇒ Hash

Calculate option Greeks (Delta, Gamma, Theta, Vega, Rho).

Parameters:

  • spot (Float) —

    Current spot price

  • strike (Float) —

    Strike price

  • time_to_expiry (Float) —

    Time to expiry in years

  • risk_free_rate (Float) —

    Risk-free interest rate (annualized)

  • volatility (Float) —

    Implied volatility (annualized)

  • option_type (Symbol) —

    :call or :put

Returns:

  • (Hash) —

    Hash with :delta, :gamma, :theta, :vega, :rho



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/DhanHQ/option_analytics/black_scholes.rb', line 50

def self.greeks(spot:, strike:, time_to_expiry:, risk_free_rate:, volatility:, option_type:)
  return empty_greeks if time_to_expiry <= 0

  d1 = calculate_d1(spot, strike, time_to_expiry, risk_free_rate, volatility)
  d2 = calculate_d2(d1, time_to_expiry, volatility)

  gamma = normal_pdf(d1) / (spot * volatility * Math.sqrt(time_to_expiry))

  theta = if option_type == :call
            calculate_call_theta(spot, strike, time_to_expiry, risk_free_rate, volatility, d1, d2)
          else
            calculate_put_theta(spot, strike, time_to_expiry, risk_free_rate, volatility, d1, d2)
          end

  vega = spot * normal_pdf(d1) * Math.sqrt(time_to_expiry) / 100

  rho = if option_type == :call
          calculate_call_rho(spot, strike, time_to_expiry, risk_free_rate, d2)
        else
          calculate_put_rho(spot, strike, time_to_expiry, risk_free_rate, d2)
        end

  {
    delta: calculate_delta(spot, strike, time_to_expiry, risk_free_rate, volatility, option_type),
    gamma: gamma,
    theta: theta / 365.0, # Daily theta
    vega: vega,
    rho: rho / 100.0
  }
end

.implied_volatility(market_price:, spot:, strike:, time_to_expiry:, risk_free_rate:, option_type:, tolerance: 0.0001, max_iterations: 100) ⇒ Float

Calculate implied volatility using Newton-Raphson method.

rubocop:disable Metrics/ParameterLists

Parameters:

  • market_price (Float) —

    Observed market price of the option

  • spot (Float) —

    Current spot price

  • strike (Float) —

    Strike price

  • time_to_expiry (Float) —

    Time to expiry in years

  • risk_free_rate (Float) —

    Risk-free interest rate (annualized)

  • option_type (Symbol) —

    :call or :put

  • tolerance (Float) (defaults to: 0.0001) —

    Convergence tolerance (default: 0.0001)

  • max_iterations (Integer) (defaults to: 100) —

    Maximum iterations (default: 100)

Returns:

  • (Float) —

    Implied volatility



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/DhanHQ/option_analytics/black_scholes.rb', line 93

def self.implied_volatility(market_price:, spot:, strike:, time_to_expiry:, risk_free_rate:, option_type:,
                            tolerance: 0.0001, max_iterations: 100)
  # rubocop:enable Metrics/ParameterLists
  return 0.0 if time_to_expiry <= 0 || market_price <= 0

  # Initial guess
  iv = 0.2
  max_iterations.times do
    theoretical_price = price(
      spot: spot, strike: strike, time_to_expiry: time_to_expiry,
      risk_free_rate: risk_free_rate, volatility: iv, option_type: option_type
    )

    diff = theoretical_price - market_price
    return iv if diff.abs < tolerance

    # Vega for Newton-Raphson
    vega = spot * normal_pdf(calculate_d1(spot, strike, time_to_expiry, risk_free_rate, iv)) *
           Math.sqrt(time_to_expiry)

    return iv if vega < 1e-10

    iv -= diff / vega
    iv = [iv, 0.001].max # Prevent negative volatility
  end

  iv
end

.price(spot:, strike:, time_to_expiry:, risk_free_rate:, volatility:, option_type:) ⇒ Float

Calculate theoretical option price using Black-Scholes model.

Parameters:

  • spot (Float) —

    Current spot price

  • strike (Float) —

    Strike price

  • time_to_expiry (Float) —

    Time to expiry in years

  • risk_free_rate (Float) —

    Risk-free interest rate (annualized)

  • volatility (Float) —

    Implied volatility (annualized)

  • option_type (Symbol) —

    :call or :put

Returns:

  • (Float) —

    Theoretical option price



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/DhanHQ/option_analytics/black_scholes.rb', line 28

def self.price(spot:, strike:, time_to_expiry:, risk_free_rate:, volatility:, option_type:)
  return 0.0 if time_to_expiry <= 0

  d1 = calculate_d1(spot, strike, time_to_expiry, risk_free_rate, volatility)
  d2 = calculate_d2(d1, time_to_expiry, volatility)

  if option_type == :call
    (spot * normal_cdf(d1)) - (strike * Math.exp(-risk_free_rate * time_to_expiry) * normal_cdf(d2))
  else
    (strike * Math.exp(-risk_free_rate * time_to_expiry) * normal_cdf(-d2)) - (spot * normal_cdf(-d1))
  end
end