greeks 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0d3ca525eaf15c5be09d9cdba6adc4dfd619089d
4
+ data.tar.gz: e991c828fbdaaff0d99a23e3137115136bd00b5c
5
+ SHA512:
6
+ metadata.gz: c76eff692a4ee4d8710f39a7c3c121133227f67bbc50ee8d3126c22946a17f6b41509a3ca7c79294335b4799ea2f4e54afc50057c4ea6a4343bb925ac0429a49
7
+ data.tar.gz: 501bfefa7861518237aab7c7cb3187598264fec413c27ec7b345f08267d4e3f830ff3d394e1091553dbc8730ed8e4a6545d850e272b7f9609a51b91aa980b06f
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in *.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,29 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ greeks (1.0)
5
+ hash_plus
6
+ require_all
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ diff-lcs (1.2.4)
12
+ hash_plus (1.1)
13
+ require_all (1.2.1)
14
+ rspec (2.13.0)
15
+ rspec-core (~> 2.13.0)
16
+ rspec-expectations (~> 2.13.0)
17
+ rspec-mocks (~> 2.13.0)
18
+ rspec-core (2.13.1)
19
+ rspec-expectations (2.13.0)
20
+ diff-lcs (>= 1.1.3, < 2.0)
21
+ rspec-mocks (2.13.1)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ greeks!
28
+ rspec
29
+ rspec-expectations
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ greeks
2
+ ======
3
+
4
+ Calculate greeks for options trading (Implied Volatility, Delta, Gamma, Vega, Rho, and Theta)
data/greeks.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "greeks/version"
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "greeks"
7
+ gem.version = Math::Greeks::VERSION
8
+ gem.authors = ["Glenn Nagel"]
9
+ gem.email = ["glenn@mercury-wireless.com"]
10
+ gem.homepage = "https://github.com/gnagel/greeks"
11
+ gem.summary = %q{Calculate greeks for options trading (Implied Volatility, Delta, Gamma, Vega, Rho, and Theta)}
12
+ gem.description = %q{Calculate greeks (iv, delta, gamma, vega, rho, theta)}
13
+ gem.license = 'MIT'
14
+
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib", "tasks"]
20
+
21
+ # System
22
+ gem.add_dependency('require_all')
23
+ gem.add_dependency('hash_plus')
24
+
25
+ gem.add_development_dependency('rspec')
26
+ gem.add_development_dependency('rspec-expectations')
27
+ end
@@ -0,0 +1,29 @@
1
+ module Math
2
+ module GreekCalculations
3
+ # Delta
4
+ # A measurement of the change in the price of an option resulting from a change in the price of the underlying security.
5
+ # Delta is positive for calls and negative for puts. Delta can be calculated as the dollar change of the option that an
6
+ # investor can expect for a one-dollar change in the underlying security. For example, let's say an option on a stock
7
+ # trading at $50 costs $1 and has a delta of $0.50 per dollar of underlying stock price change. If the stock price rises
8
+ # to $52, the price of the option will increase by $1 (the $2 price change times the $0.50 delta). After the stock price
9
+ # movement, the option will be worth $2 ($1 initial cost plus $1 delta). Delta can also be calculated as a percentage
10
+ # change in the option price for a one-percent change in the underlying security; this method of viewing the delta value
11
+ # is also known as "leverage."
12
+ def delta(opts)
13
+ opts.requires_fields(:option_type, :rate_vs_expires, :d1_normal_distribution, :iv)
14
+
15
+ return nil if opts[:iv].nil?
16
+
17
+ multiplier = case opts[:option_type]
18
+ when :call
19
+ 1.0
20
+ when :put
21
+ -1.0
22
+ else
23
+ raise "Invalid option_type = #{opts[:option_type].inspect}"
24
+ end
25
+
26
+ multiplier * opts[:rate_vs_expires] * opts[:d1_normal_distribution]
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ module Math
2
+ module GreekCalculations
3
+ # Gamma
4
+ # A measurement of the change in delta as the price of the underlying stock changes. As the underlying stock price changes,
5
+ # the delta of the option changes, too. Gamma indicates how quickly your exposure to the price movement of the underlying
6
+ # security changes as the price of the underlying security varies. For example, if you have a call with a strike of $50
7
+ # and the stock price is $50, the delta likely will be approximately $0.50 for a one-dollar movement of the stock.
8
+ # At a stock price of $60, the delta will be greater, closer to $0.75. At a stock price of $40, the delta will be less,
9
+ # closer to $0.25. In this example, if the stock price changes from $50 to $60, then the delta will change from $0.50 to $0.75.
10
+ # The $10 change in stock price caused a $0.25 change in delta, so gamma is approximately $0.25/10, or $0.025, in this case.
11
+ def gamma(opts = {})
12
+ opts.requires_fields(:stock_price, :option_expires_pct_year_sqrt, :iv, :nd1, :rate_vs_expires)
13
+
14
+ return nil if opts[:iv].nil?
15
+
16
+ opts[:nd1] * opts[:rate_vs_expires] / (opts[:stock_price] * opts[:iv] * opts[:option_expires_pct_year_sqrt])
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,117 @@
1
+ module Math
2
+ module GreekCalculations
3
+ def iv(opts)
4
+ opts.requires_fields(:stock_price, :option_strike, :option_expires_pct_year, :option_expires_pct_year_sqrt, :federal_reserve_interest_rate_f, :stock_dividend_rate_f, :option_type, :option_price, :rate_vs_expires, :price_vs_rate_vs_expires, :strike_vs_fed_vs_expires, :price_ratio_log_less_rates)
5
+
6
+ return nil if opts[:option_price].nil? || opts[:option_price] < 0
7
+
8
+ iv_calc(
9
+ opts[:stock_price],
10
+ opts[:option_strike],
11
+ opts[:option_expires_pct_year],
12
+ opts[:option_expires_pct_year_sqrt],
13
+ opts[:federal_reserve_interest_rate_f],
14
+ opts[:stock_dividend_rate_f],
15
+ opts[:option_type],
16
+ opts[:option_price],
17
+ opts[:price_vs_rate_vs_expires],
18
+ opts[:price_ratio_log_less_rates],
19
+ opts[:strike_vs_fed_vs_expires]
20
+ )
21
+ end
22
+
23
+
24
+ def iv_vega(stock_price, option_strike, option_expires_pct_year, option_expires_pct_year_sqrt, volatility_guess, federal_reserve_interest_rate_f, stock_dividend_rate_f, price_ratio_log_less_rates, price_vs_rate_vs_expires)
25
+ var_d1 = (price_ratio_log_less_rates + volatility_guess * volatility_guess * option_expires_pct_year / 2) / (volatility_guess * option_expires_pct_year_sqrt)
26
+ var_nd = Math.exp(-var_d1 * var_d1 / 2) / Math::sqrt(2 * Math::PI)
27
+ return price_vs_rate_vs_expires * option_expires_pct_year_sqrt * var_nd
28
+ end
29
+
30
+
31
+ def iv_option_price(stock_price, option_strike, option_expires_pct_year, option_expires_pct_year_sqrt, volatility_guess, federal_reserve_interest_rate_f, stock_dividend_rate_f, option_type, price_ratio_log_less_rates, price_vs_rate_vs_expires, strike_vs_fed_vs_expires)
32
+ var_d1 = (price_ratio_log_less_rates + volatility_guess * volatility_guess * option_expires_pct_year / 2) / (volatility_guess * option_expires_pct_year_sqrt)
33
+ var_d2 = var_d1 - volatility_guess * option_expires_pct_year_sqrt
34
+
35
+ case option_type
36
+ when :call
37
+ return price_vs_rate_vs_expires * normal_distribution(var_d1) - strike_vs_fed_vs_expires * normal_distribution(var_d2)
38
+ when :put
39
+ return strike_vs_fed_vs_expires * normal_distribution(-var_d2) - price_vs_rate_vs_expires * normal_distribution(-var_d1)
40
+ else
41
+ raise "Invalid option_type = #{option_type.inspect}"
42
+ end
43
+ end
44
+
45
+
46
+ def iv_volatility_guess0(stock_price, option_strike, option_expires_pct_year, federal_reserve_interest_rate_f, stock_dividend_rate_f)
47
+ Math.sqrt(
48
+ (Math.log(stock_price / option_strike) + (federal_reserve_interest_rate_f - stock_dividend_rate_f) * option_expires_pct_year).abs * 2 / option_expires_pct_year)
49
+ end
50
+
51
+
52
+ def iv_calc(stock_price, option_strike, option_expires_pct_year, option_expires_pct_year_sqrt, federal_reserve_interest_rate_f, stock_dividend_rate_f, option_type, option_price, price_vs_rate_vs_expires, price_ratio_log_less_rates, strike_vs_fed_vs_expires)
53
+ # Contstant values for the calculations
54
+ price_limit = [0.005, 0.01 * option_price].min
55
+
56
+ # Lambda for short-hand calculations
57
+ calc_option_price = lambda { |volatility_guess| iv_option_price(stock_price, option_strike, option_expires_pct_year, option_expires_pct_year_sqrt, volatility_guess, federal_reserve_interest_rate_f, stock_dividend_rate_f, option_type, price_ratio_log_less_rates, price_vs_rate_vs_expires, strike_vs_fed_vs_expires) }
58
+
59
+ # Lambda for short-hand calculations
60
+ calc_vega = lambda { |volatility_guess| iv_vega(stock_price, option_strike, option_expires_pct_year, option_expires_pct_year_sqrt, volatility_guess, federal_reserve_interest_rate_f, stock_dividend_rate_f, price_ratio_log_less_rates, price_vs_rate_vs_expires) }
61
+
62
+ # Lambda for short-hand calculations
63
+ calc_volatility_guess1 = lambda { |var_volatility_guess, var_option_price, var_vega| var_volatility_guess - (var_option_price - option_price) / var_vega }
64
+
65
+ # Lambda for short-hand calculations
66
+ is_terminal_volatility_guess = lambda { |var_option_price| ((option_price - var_option_price).abs < price_limit) }
67
+
68
+ # Lambda for short-hand calculations
69
+ cleanup_volatility_guess = lambda { |volatility_guess| volatility_guess.nil? || volatility_guess <= 0 ? nil : volatility_guess.to_f }
70
+
71
+ var_volatility_guess = iv_volatility_guess0(stock_price, option_strike, option_expires_pct_year, federal_reserve_interest_rate_f, stock_dividend_rate_f)
72
+ var_volatility_guess = 0.1 if var_volatility_guess <= 0
73
+ var_option_price = calc_option_price.call(var_volatility_guess)
74
+
75
+ if is_terminal_volatility_guess.call(var_option_price)
76
+ return cleanup_volatility_guess.call(var_volatility_guess)
77
+ end
78
+
79
+ var_vega = calc_vega.call(var_volatility_guess)
80
+
81
+ var_volatility_guess1 = calc_volatility_guess1.call(var_volatility_guess, var_option_price, var_vega)
82
+
83
+ var_step = 1
84
+ max_steps = 13
85
+ while ((var_volatility_guess - var_volatility_guess1).abs > 0.0001 && var_step < max_steps)
86
+ var_volatility_guess = var_volatility_guess1
87
+ var_option_price = calc_option_price.call(var_volatility_guess)
88
+
89
+ if is_terminal_volatility_guess.call(var_option_price)
90
+ return cleanup_volatility_guess.call(var_volatility_guess)
91
+ end
92
+
93
+ var_vega = calc_vega.call(var_volatility_guess)
94
+
95
+ var_volatility_guess1 = calc_volatility_guess1.call(var_volatility_guess, var_option_price, var_vega)
96
+ if (var_volatility_guess1 < 0)
97
+ return cleanup_volatility_guess.call(var_volatility_guess1)
98
+ end
99
+
100
+ var_step += 1
101
+ end
102
+
103
+ if (var_step < max_steps)
104
+ return cleanup_volatility_guess.call(var_volatility_guess1)
105
+ end
106
+
107
+ var_option_price = calc_option_price.call(var_volatility_guess1)
108
+
109
+ if is_terminal_volatility_guess.call(var_option_price)
110
+ return cleanup_volatility_guess.call(var_volatility_guess1)
111
+ else
112
+ return nil
113
+ end
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,27 @@
1
+ module Math
2
+ module GreekCalculations
3
+ # Moddeled after the Excel NORMSDIST function
4
+ def normal_distribution(value)
5
+ p = 0.2316419
6
+ b1 = 0.319381530
7
+ b2 = -0.356563782
8
+ b3 = 1.781477937
9
+ b4 = -1.821255978
10
+ b5 = 1.330274429
11
+
12
+ y = value.abs
13
+ z = Math.exp(-y*y/2) / Math.sqrt(2 * Math::PI)
14
+ t = 1 / ( 1 + p * y)
15
+ cum = 1 - z * (b1*t + b2*t*t + b3*t*t*t + b4*t*t*t*t + b5*t*t*t*t*t)
16
+
17
+ cum = 1 - cum if (value < 0)
18
+ cum
19
+ end
20
+
21
+
22
+ # Normal distribution function (Gaussian bell curve)
23
+ def normal_distribution_gaussian(value)
24
+ Math.exp(-0.5 * value * value) / Math.sqrt(2 * Math::PI)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ module Math
2
+ module GreekCalculations
3
+ # Rho
4
+ # The change in the value of an option for a change in the prevailing interest rate that matches the duration of the option,
5
+ # all else held equal. Generally rho is not a big driver of price changes for options, as interest rates tend to be relatively stable.
6
+ def rho(opts = {})
7
+ opts.requires_fields(:option_type, :option_expires_pct_year, :strike_vs_fed_vs_expires, :d2_normal_distribution, :iv)
8
+
9
+ return nil if opts[:iv].nil?
10
+
11
+ multiplier = case opts[:option_type]
12
+ when :call
13
+ 1.0
14
+ when :put
15
+ -1.0
16
+ else
17
+ raise "Invalid option_type = #{opts[:option_type].inspect}"
18
+ end
19
+
20
+ multiplier * opts[:option_expires_pct_year] * opts[:strike_vs_fed_vs_expires] * opts[:d2_normal_distribution] / 100
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Math
2
+ module GreekCalculations
3
+ def theta(opts = {})
4
+ opts.requires_fields(:stock_dividend_rate_f, :federal_reserve_interest_rate_f, :option_type, :option_expires_pct_year_sqrt, :iv, :strike_vs_fed_vs_expires, :price_vs_rate_vs_expires, :nd1, :d1_normal_distribution, :d2_normal_distribution)
5
+
6
+ return nil if opts[:iv].nil?
7
+
8
+ part0 = opts[:price_vs_rate_vs_expires] * opts[:nd1] * opts[:iv]
9
+ part1 = 2 * opts[:option_expires_pct_year_sqrt]
10
+ part2 = opts[:stock_dividend_rate_f] * opts[:price_vs_rate_vs_expires] * opts[:d1_normal_distribution]
11
+ part3 = opts[:federal_reserve_interest_rate_f] * opts[:strike_vs_fed_vs_expires] * opts[:d2_normal_distribution]
12
+
13
+ case opts[:option_type]
14
+ when :call
15
+ return (-part0 / part1 + part2 - part3) / 365
16
+ when :put
17
+ return (-part0 / part1 - part2 + part3) / 365
18
+ else
19
+ raise "Invalid option_type = #{opts[:option_type].inspect}"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,181 @@
1
+ module Math
2
+ module GreekCalculations
3
+
4
+ def nil_or_gte0(value)
5
+ value.nil? || value.to_f < 0 ? nil : value
6
+ end
7
+
8
+
9
+ # Intrinsic Value
10
+ # The value that the option would pay if it were executed today. For example, if a stock is trading at $40,
11
+ # a call on that stock with a strike price of $35 would have $5 of intrinsic value ($40-$35) if it were
12
+ # exercised today. However, the call should actually be worth more than $5 to account for the value of the
13
+ # chance of any further appreciation until expiration, and the difference between the price and the
14
+ # intrinsic value would be the time value.
15
+ def premium_value(opts)
16
+ opts.requires_fields(:option_type, :option_strike, :stock_price)
17
+
18
+ case opts[:option_type]
19
+ when :call
20
+ return [opts[:stock_price] - opts[:option_strike], 0].max
21
+ when :put
22
+ return [opts[:option_strike] - opts[:stock_price], 0].max
23
+ else
24
+ raise ArgumentError, "Invalid option_type = #{opts[:option_type]}"
25
+ end
26
+ end
27
+
28
+
29
+ # Time Value
30
+ # The value of an option that captures the chance of further appreciation before expiration.
31
+ # The value of an option can be broken down into intrinsic value, or the value of the option
32
+ # if it were exercised today, and time value, or the added value of the option over and above
33
+ # the intrinsic value. For example, if a stock is trading at $40 and a call with a strike price
34
+ # of $35 were trading for $7, the call would have a $5 intrinsic value ($40-$35) and a $2 time value ($7-$5).
35
+ # Time value will decay by expiration assuming the underlying security stays at the same price.
36
+ def time_value(opts)
37
+ opts.requires_fields(:option_price, :premium_value)
38
+
39
+ return nil if opts[:option_price].nil?
40
+ return nil if opts[:option_price] < 0
41
+
42
+ nil_or_gte0(opts[:option_price] - opts[:premium_value])
43
+ end
44
+
45
+
46
+ # Annualized Premium
47
+ # The annualized premium is the value of the option divided by the strike price. You can use annualized
48
+ # premium to develop an intuitive understanding of how much the market is "paying" for a dollar of risk.
49
+ # For example, if a stock is trading at $50 and you sell a $50 strike 6 month call for $4, you are getting
50
+ # paid 8% in 6 months, or about 16% annualized, in exchange for being willing to buy at $50, the current price.
51
+ def annualized_premium_value
52
+ opts.requires_fields(:option_price, :option_strike, :option_expires_pct_year)
53
+
54
+ return nil if opts[:option_price].nil?
55
+ return nil if opts[:option_price] < 0
56
+
57
+ nil_or_gte0(100 * Math.log(1 + opts[:option_price] / opts[:option_strike]) / opts[:option_expires_pct_year])
58
+ end
59
+
60
+
61
+ # Annualized Time Value
62
+ # The time value of the option divided by the strike price, then annualized. You can use annualized
63
+ # time value to develop an intuitive understanding of how much value the option market is adding to
64
+ # an in-the-money option beyond the intrinsic value. For example, if a stock is trading at $40 and a
65
+ # six month call on that stock with a strike price of $35 has an intrinsic value of $5 and a total
66
+ # value of $7, the time value ($2) divided by the strike is ($2/$40) = 5%. Annualizing that time value
67
+ # to a one year horizon on a continuously compounded basis yields 9.76% (2 × ln(1 + 0.05)).
68
+ def annualized_time_value
69
+ opts.requires_fields(:option_strike, :option_expires_pct_year, :time_value)
70
+
71
+ return nil if opts[:time_value].nil?
72
+
73
+ nil_or_gte0(100 * Math.log(1.0 + opts[:time_value] / opts[:option_strike]) / opts[:option_expires_pct_year])
74
+ end
75
+
76
+
77
+ # Chance of Breakeven
78
+ # The probability that a stock will be trading beyond the breakeven price as implied by the option price.
79
+ # Chance of Breakeven can be used to get a sense for the valuation of the option by comparing the markets'
80
+ # estimate of Chance of Breakeven to estimates derived from your own fundamental research.
81
+ # If you believe the Chance of Breakeven is less than the probability that a stock will be beyond the
82
+ # breakeven price at option expiration, then you believe the option is undervalued, and visa versa.
83
+ def break_even(opts)
84
+ opts.requires_fields(:option_type, :option_price, :option_strike, :option_expires_pct_year, :option_expires_pct_year_sqrt, :stock_price, :stock_dividend_rate_f, :federal_reserve_interest_rate_f, :iv)
85
+
86
+ return nil if opts[:option_price].nil?
87
+ return nil if opts[:option_price] < 0
88
+ return nil if opts[:iv].nil?
89
+
90
+ part1 = (opts[:federal_reserve_interest_rate_f] - opts[:stock_dividend_rate_f] - opts[:iv] * opts[:iv] / 2) * opts[:option_expires_pct_year]
91
+ part2 = opts[:iv] * opts[:option_expires_pct_year_sqrt]
92
+
93
+ case opts[:option_type]
94
+ when :call
95
+ return normal_distribution((Math.log(opts[:stock_price] / (opts[:option_strike] + opts[:option_price])) + part1) / part2)
96
+ when :put
97
+ return normal_distribution(-(Math.log(opts[:stock_price] / (opts[:option_strike] - opts[:option_price])) + part1) / part2)
98
+ else
99
+ raise ArgumentError, "Invalid option_type = #{opts[:option_type]}"
100
+ end
101
+ end
102
+
103
+
104
+ #####
105
+ # Misc calculations
106
+ #####
107
+
108
+
109
+ def misc_price_ratio_log_less_rates(opts)
110
+ opts.requires_fields(:stock_price, :option_strike, :option_expires_pct_year, :federal_reserve_interest_rate_f, :stock_dividend_rate_f)
111
+
112
+ Math.log(opts[:stock_price] / opts[:option_strike]) + (opts[:federal_reserve_interest_rate_f] - opts[:stock_dividend_rate_f]) * opts[:option_expires_pct_year]
113
+ end
114
+
115
+
116
+ def misc_rate_vs_expires(opts)
117
+ opts.requires_fields(:option_expires_pct_year, :stock_dividend_rate_f)
118
+
119
+ Math.exp(opts[:option_expires_pct_year] * -opts[:stock_dividend_rate_f])
120
+ end
121
+
122
+
123
+ def misc_price_vs_rate_vs_expires(opts)
124
+ opts.requires_fields(:stock_price, :option_expires_pct_year, :stock_dividend_rate_f)
125
+
126
+ opts[:stock_price] * misc_rate_vs_expires(opts)
127
+ end
128
+
129
+
130
+ def misc_strike_vs_fed_vs_expires(opts)
131
+ opts.requires_fields(:option_strike, :option_expires_pct_year, :federal_reserve_interest_rate_f)
132
+
133
+ opts[:option_strike] * Math.exp(opts[:option_expires_pct_year] * -opts[:federal_reserve_interest_rate_f])
134
+ end
135
+
136
+
137
+ def misc_nd1(opts)
138
+ opts.requires_fields(:d1)
139
+
140
+ return nil if opts[:d1].nil?
141
+
142
+ Math.exp(-0.5 * opts[:d1] * opts[:d1]) / Math.sqrt(2 * Math::PI)
143
+ end
144
+
145
+
146
+ def misc_d1(opts)
147
+ opts.requires_fields(:price_ratio_log_less_rates, :iv, :option_expires_pct_year, :option_expires_pct_year_sqrt)
148
+
149
+ return nil if opts[:iv].nil?
150
+
151
+ (opts[:price_ratio_log_less_rates] + opts[:iv] * opts[:iv] * opts[:option_expires_pct_year] / 2) / (opts[:iv] * opts[:option_expires_pct_year_sqrt])
152
+ end
153
+
154
+
155
+ def misc_d2(opts)
156
+ opts.requires_fields(:d1, :iv, :option_expires_pct_year_sqrt)
157
+
158
+ return nil if opts[:iv].nil?
159
+
160
+ opts[:d1] - opts[:iv] * opts[:option_expires_pct_year_sqrt]
161
+ end
162
+
163
+ def misc_d_normal_distribution(opts)
164
+ opts.requires_fields(:option_type, :d_value)
165
+
166
+ return nil if opts[:d_value].nil?
167
+
168
+ multiplier = case opts[:option_type]
169
+ when :call
170
+ 1.0
171
+ when :put
172
+ -1.0
173
+ else
174
+ raise ArgumentError, "Invalid option_type = #{opts[:option_type]}"
175
+ end
176
+
177
+ normal_distribution(multiplier * opts[:d_value])
178
+ end
179
+
180
+ end
181
+ end
@@ -0,0 +1,17 @@
1
+ module Math
2
+ module GreekCalculations
3
+ # Vega
4
+ # The change in the price of an option for a change in the implied volatility of the option, all else held equal.
5
+ # In general, as the options market thinks it is more difficult to value a stock, implied volatility and therefore
6
+ # the price of the options will increase. For example, if an option is trading for $1, the implied volatility is 20%,
7
+ # and the vega is $0.05, then a one-percentage-point increase in implied volatility to 21% would correspond to an increase in
8
+ # the price of the option to $1.05. In percentage terms, the vega in this case would be ($0.05/$1.00)/(1 percentage point) = 5%.
9
+ def vega(opts = {})
10
+ opts.requires_fields(:price_vs_rate_vs_expires, :nd1, :option_expires_pct_year_sqrt, :iv)
11
+
12
+ return nil if opts[:iv].nil?
13
+
14
+ opts[:price_vs_rate_vs_expires] * opts[:option_expires_pct_year_sqrt] * opts[:nd1] / 100
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ module Math
2
+ module Greeks
3
+ VERSION = '1.0'
4
+ end
5
+ end