options_library 1.0.0 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|