options_library 1.0.0 → 1.0.2
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.
- data/Manifest +2 -1
- data/README.md +57 -0
- data/Rakefile +1 -1
- data/lib/options_library/option_calculator.rb +96 -91
- data/options_library.gemspec +5 -5
- metadata +7 -3
data/Manifest
CHANGED
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
Initial Author
|
2
|
+
--------------
|
3
|
+
Dan Tylenda-Emmons
|
4
|
+
Twitter: @CoderTrader
|
5
|
+
Email: jrubyist@gmail.com
|
6
|
+
|
7
|
+
Options Library
|
8
|
+
===============
|
9
|
+
|
10
|
+
A pure Ruby implementation of the classic Black-Scholes option model for pricing Calls or Puts.
|
11
|
+
|
12
|
+
Goals
|
13
|
+
-------
|
14
|
+
|
15
|
+
* Provide the Open Source Ruby, JRuby and StockTwits communities with a publicly available model for computing option prices.
|
16
|
+
* Allow users of the library to compute price and greeks: delta, gamma, theta, vega and rho.
|
17
|
+
* To aid others in the understanding of how price and sensitivities to other factors are computed on a theoretical basis.
|
18
|
+
* To allo users of the library to extend or contribute back to the project so that it may be improved upon.
|
19
|
+
|
20
|
+
|
21
|
+
Installation
|
22
|
+
-----------
|
23
|
+
|
24
|
+
gem install options_library
|
25
|
+
|
26
|
+
|
27
|
+
Usage
|
28
|
+
-----
|
29
|
+
|
30
|
+
require 'rubygems'
|
31
|
+
require 'options_library'
|
32
|
+
|
33
|
+
# Option::Calculator.price_call( underlying, strike, time, interest, sigma, dividend )
|
34
|
+
call_price = Option::Calculator.price_call( 94.5, 90.5, 0.015, 0.01, 0.4875, 0.0 )
|
35
|
+
|
36
|
+
Testing
|
37
|
+
-------
|
38
|
+
|
39
|
+
To run the tests:
|
40
|
+
|
41
|
+
$ rake
|
42
|
+
|
43
|
+
To add tests see the `Commands` section earlier in this
|
44
|
+
README.
|
45
|
+
|
46
|
+
|
47
|
+
Contributing
|
48
|
+
------------
|
49
|
+
|
50
|
+
1. Fork it.
|
51
|
+
2. Create a branch (`git checkout -b my_options_library`)
|
52
|
+
3. Commit your changes (`git commit -am "Added Hull-White Model"`)
|
53
|
+
4. Push to the branch (`git push origin my_options_library`)
|
54
|
+
5. Create an [Issue][1] with a link to your branch
|
55
|
+
6. Enjoy a fresh cup of coffee and wait
|
56
|
+
|
57
|
+
[1]: http://github.com/github/markup/issues
|
data/Rakefile
CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
require 'echoe'
|
4
4
|
|
5
|
-
Echoe.new('options_library', '1.0.
|
5
|
+
Echoe.new('options_library', '1.0.2') do |p|
|
6
6
|
p.description = 'A gem used to calc the price of an option.'
|
7
7
|
p.url = 'http://github.com/codertrader/options_library'
|
8
8
|
p.author = 'Dan Tylenda-Emmons'
|
@@ -5,121 +5,126 @@
|
|
5
5
|
module Option
|
6
6
|
class Calculator
|
7
7
|
class << self
|
8
|
+
# used for finding implied vol based on a market (target) price
|
9
|
+
LOW_VOL, HIGH_VOL, VOL_TOLERANCE = 0.0, 5.0, 0.0001
|
10
|
+
|
11
|
+
# used for min/max normal distribution
|
12
|
+
MIN_Z_SCORE, MAX_Z_SCORE = -8.0, +8.0
|
8
13
|
|
9
|
-
|
14
|
+
include Math
|
10
15
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
# computes the call price sensitivity to a change in underlying price
|
17
|
+
def delta_call( underlying, strike, time, interest, sigma, dividend )
|
18
|
+
norm_sdist( d_one( underlying, strike, time, interest, sigma, dividend ) )
|
19
|
+
end
|
15
20
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
21
|
+
# computes the put price sensitivity to a change in underlying price
|
22
|
+
def delta_put( underlying, strike, time, interest, sigma, dividend )
|
23
|
+
delta_call( underlying, strike, time, interest, sigma, dividend ) - 1
|
24
|
+
end
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
26
|
+
# computes the option price sensitivity to a change in delta
|
27
|
+
def gamma( underlying, strike, time, interest, sigma, dividend )
|
28
|
+
phi( d_one( underlying, strike, time, interest, sigma, dividend ) ) / ( underlying * sigma * sqrt(time) )
|
29
|
+
end
|
25
30
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
31
|
+
# computes the option price sensitivity to a change in volatility
|
32
|
+
def vega( underlying, strike, time, interest, sigma, dividend )
|
33
|
+
0.01 * underlying * sqrt(time) * phi(d_one(underlying, strike, time, interest, sigma, dividend))
|
34
|
+
end
|
30
35
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
# computes the fair value of the call based on the knowns and assumed volatility (sigma)
|
37
|
+
def price_call( underlying, strike, time, interest, sigma, dividend )
|
38
|
+
d1 = d_one( underlying, strike, time, interest, sigma, dividend )
|
39
|
+
discounted_underlying = exp(-1.0 * dividend * time) * underlying
|
40
|
+
probability_weighted_value_of_being_exercised = discounted_underlying * norm_sdist( d1 )
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
42
|
+
d2 = d1 - ( sigma * sqrt(time) )
|
43
|
+
discounted_strike = exp(-1.0 * interest * time) * strike
|
44
|
+
probability_weighted_value_of_discounted_strike = discounted_strike * norm_sdist( d2 )
|
40
45
|
|
41
|
-
|
42
|
-
|
46
|
+
expected_value = probability_weighted_value_of_being_exercised - probability_weighted_value_of_discounted_strike
|
47
|
+
end
|
43
48
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
+
# computes the fair value of the put based on the knowns and assumed volatility (sigma)
|
50
|
+
def price_put( underlying, strike, time, interest, sigma, dividend )
|
51
|
+
d2 = d_two( underlying, strike, time, interest, sigma, dividend )
|
52
|
+
discounted_strike = strike * exp(-1.0 * interest * time)
|
53
|
+
probabiltity_weighted_value_of_discounted_strike = discounted_strike * norm_sdist( -1.0 * d2 )
|
49
54
|
|
50
|
-
|
51
|
-
|
52
|
-
|
55
|
+
d1 = d2 + ( sigma * sqrt(time) )
|
56
|
+
discounted_underlying = underlying * exp(-1.0 * dividend * time)
|
57
|
+
probability_weighted_value_of_being_exercised = discounted_underlying * norm_sdist( -1.0 * d1 )
|
53
58
|
|
54
|
-
|
55
|
-
|
59
|
+
expected_value = probabiltity_weighted_value_of_discounted_strike - probability_weighted_value_of_being_exercised
|
60
|
+
end
|
56
61
|
|
57
|
-
|
58
|
-
|
59
|
-
|
62
|
+
# finds the implied volatility based on the target_price passed in.
|
63
|
+
def implied_vol_call( underlying, strike, time, interest, target_price, dividend )
|
64
|
+
low, high = LOW_VOL, HIGH_VOL
|
60
65
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
66
|
+
while( high - low > VOL_TOLERANCE )
|
67
|
+
if( price_call( underlying, strike, time, interest, (high+low)/2.0, dividend ) > target_price )
|
68
|
+
high = (high + low) / 2.0
|
69
|
+
else
|
70
|
+
low = (high + low) / 2.0
|
71
|
+
end
|
72
|
+
end
|
68
73
|
|
69
|
-
|
74
|
+
(high + low) / 2.0
|
70
75
|
end
|
71
76
|
|
72
|
-
|
73
|
-
|
74
|
-
|
77
|
+
# finds the implied volatility based on the target_price passed in.
|
78
|
+
def implied_vol_put( underlying, strike, time, interest, target_price, dividend )
|
79
|
+
low, high = LOW_VOL, HIGH_VOL
|
75
80
|
|
76
|
-
while( high - low >
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
81
|
+
while( high - low > VOL_TOLERANCE )
|
82
|
+
if( price_put( underlying, strike, time, interest, (high+low)/2.0, dividend ) > target_price )
|
83
|
+
high = (high + low) / 2.0
|
84
|
+
else
|
85
|
+
low = (high + low) / 2.0
|
86
|
+
end
|
87
|
+
end
|
83
88
|
|
84
|
-
|
85
|
-
|
89
|
+
(high + low) / 2.0
|
90
|
+
end
|
86
91
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
92
|
+
# probability of being exercised at maturity (must be greater than d2 by (sigma*sqrt(time)) if exercised)
|
93
|
+
def d_one( underlying, strike, time, interest, sigma, dividend )
|
94
|
+
numerator = ( log(underlying / strike) + (interest - dividend + 0.5 * sigma ** 2.0 ) * time)
|
95
|
+
denominator = ( sigma * sqrt(time) )
|
96
|
+
numerator / denominator
|
97
|
+
end
|
93
98
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
99
|
+
# probability of underlying reaching the strike price (must be smaller than d1 by (sigma*sqrt(time)) if exercised.
|
100
|
+
def d_two( underlying, strike, time, interest, sigma, dividend )
|
101
|
+
d_one( underlying, strike, time, interest, sigma, dividend ) - ( sigma * sqrt(time) )
|
102
|
+
end
|
98
103
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
+
# Normal Standard Distribution
|
105
|
+
# using Taylor's approximation
|
106
|
+
def norm_sdist( z )
|
107
|
+
return 0.0 if z < MIN_Z_SCORE
|
108
|
+
return 1.0 if z > MAX_Z_SCORE
|
104
109
|
|
105
|
-
|
110
|
+
i, sum, term = 3.0, 0.0, z
|
106
111
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
+
while( sum + term != sum )
|
113
|
+
sum = sum + term
|
114
|
+
term = term * z * z / i
|
115
|
+
i += 2.0
|
116
|
+
end
|
112
117
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
# Standard Gaussian pdf
|
117
|
-
def phi(x)
|
118
|
-
numerator = exp(-1.0 * x*x / 2.0)
|
119
|
-
denominator = sqrt(2.0 * PI)
|
120
|
-
numerator / denominator
|
121
|
-
end
|
118
|
+
0.5 + sum * phi(z)
|
119
|
+
end
|
122
120
|
|
123
|
-
|
121
|
+
# Standard Gaussian pdf
|
122
|
+
def phi(x)
|
123
|
+
numerator = exp(-1.0 * x*x / 2.0)
|
124
|
+
denominator = sqrt(2.0 * PI)
|
125
|
+
numerator / denominator
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
124
129
|
end
|
125
130
|
end
|
data/options_library.gemspec
CHANGED
@@ -2,17 +2,17 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{options_library}
|
5
|
-
s.version = "1.0.
|
5
|
+
s.version = "1.0.2"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Dan Tylenda-Emmons"]
|
9
|
-
s.date = %q{2011-02-
|
9
|
+
s.date = %q{2011-02-16}
|
10
10
|
s.description = %q{A gem used to calc the price of an option.}
|
11
11
|
s.email = %q{jrubyist@gmail.com}
|
12
|
-
s.extra_rdoc_files = ["lib/options_library.rb", "lib/options_library/option_calculator.rb"]
|
13
|
-
s.files = ["Rakefile", "lib/options_library.rb", "lib/options_library/option_calculator.rb", "options_library.gemspec"
|
12
|
+
s.extra_rdoc_files = ["README.md", "lib/options_library.rb", "lib/options_library/option_calculator.rb"]
|
13
|
+
s.files = ["Manifest", "README.md", "Rakefile", "lib/options_library.rb", "lib/options_library/option_calculator.rb", "options_library.gemspec"]
|
14
14
|
s.homepage = %q{http://github.com/codertrader/options_library}
|
15
|
-
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Options_library"]
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Options_library", "--main", "README.md"]
|
16
16
|
s.require_paths = ["lib"]
|
17
17
|
s.rubyforge_project = %q{options_library}
|
18
18
|
s.rubygems_version = %q{1.5.2}
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: options_library
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 1.0.
|
5
|
+
version: 1.0.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Dan Tylenda-Emmons
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-02-
|
13
|
+
date: 2011-02-16 00:00:00 -06:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -21,14 +21,16 @@ executables: []
|
|
21
21
|
extensions: []
|
22
22
|
|
23
23
|
extra_rdoc_files:
|
24
|
+
- README.md
|
24
25
|
- lib/options_library.rb
|
25
26
|
- lib/options_library/option_calculator.rb
|
26
27
|
files:
|
28
|
+
- Manifest
|
29
|
+
- README.md
|
27
30
|
- Rakefile
|
28
31
|
- lib/options_library.rb
|
29
32
|
- lib/options_library/option_calculator.rb
|
30
33
|
- options_library.gemspec
|
31
|
-
- Manifest
|
32
34
|
has_rdoc: true
|
33
35
|
homepage: http://github.com/codertrader/options_library
|
34
36
|
licenses: []
|
@@ -39,6 +41,8 @@ rdoc_options:
|
|
39
41
|
- --inline-source
|
40
42
|
- --title
|
41
43
|
- Options_library
|
44
|
+
- --main
|
45
|
+
- README.md
|
42
46
|
require_paths:
|
43
47
|
- lib
|
44
48
|
required_ruby_version: !ruby/object:Gem::Requirement
|