rupee 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/rupee/option.c +542 -46
- data/ext/rupee/rupee.h +18 -2
- data/ext/rupee/statistics.c +133 -0
- data/lib/rupee.rb +1 -0
- data/lib/rupee/calendar.rb +238 -0
- data/lib/rupee/calendar/japan.rb +54 -0
- data/lib/rupee/calendar/us.rb +59 -0
- data/lib/rupee/option.rb +8 -0
- data/lib/rupee/quote.rb +37 -8
- data/lib/rupee/version.rb +1 -1
- data/spec/native/option_spec.rb +118 -31
- data/spec/ruby/calendar_spec.rb +85 -0
- data/test_rubies +0 -0
- metadata +49 -49
@@ -0,0 +1,54 @@
|
|
1
|
+
module Rupee
|
2
|
+
class Calendar
|
3
|
+
# The national Japanese calendar
|
4
|
+
Japan = Calendar.new("The national Japanese calendar")
|
5
|
+
|
6
|
+
# Weekends
|
7
|
+
Japan.has_weekends_off
|
8
|
+
|
9
|
+
# Fixed and solar holidays
|
10
|
+
Japan.has_day_off_when do |date|
|
11
|
+
case date.month
|
12
|
+
when JANUARY
|
13
|
+
# New Year's Day
|
14
|
+
date.day == 1
|
15
|
+
when FEBRUARY
|
16
|
+
# National Foundation Day
|
17
|
+
date.day == 11
|
18
|
+
when MARCH
|
19
|
+
# Vernal Equinox Day
|
20
|
+
date.day == 20
|
21
|
+
when APRIL
|
22
|
+
# Showa Day
|
23
|
+
date.day == 29
|
24
|
+
when MAY
|
25
|
+
# Golden Week: Constitution Memorial Day, Greenery Day and Children's
|
26
|
+
# Day, respectively
|
27
|
+
date.day.between?(3, 5)
|
28
|
+
when SEPTEMBER
|
29
|
+
# Autumnal Equinox Day
|
30
|
+
date.day == 23
|
31
|
+
when NOVEMBER
|
32
|
+
# Culture Day and Labour Thanksgiving Day
|
33
|
+
date.day == 3 || date.day == 23
|
34
|
+
when DECEMBER
|
35
|
+
# Emperor's Birthday
|
36
|
+
date.day == 23
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Mondays
|
41
|
+
Japan.has_day_off_when do |date|
|
42
|
+
if date.monday?
|
43
|
+
case date.month
|
44
|
+
when JANUARY, OCTOBER
|
45
|
+
# Coming of Age Day, Health and Sports Day
|
46
|
+
week_of(date) == 2
|
47
|
+
when JULY, SEPTEMBER
|
48
|
+
# Marine Day, Respect-for-the-Aged Day
|
49
|
+
week_of(date) == 3
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Rupee
|
2
|
+
class Calendar
|
3
|
+
# The US Federal Reserve calendar
|
4
|
+
US = Calendar.new("The Federal Reserve Calendar")
|
5
|
+
|
6
|
+
# Weekends
|
7
|
+
US.has_weekends_off
|
8
|
+
|
9
|
+
# New Year's Day (January 1)
|
10
|
+
US.has_day_off_on :new_years do |date|
|
11
|
+
date.month == JANUARY && next_weekday(date, 1)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Martin Luther King, Jr.'s Birthday (third Monday of January)
|
15
|
+
US.has_day_off_on :mlk_day do |date|
|
16
|
+
date.month == JANUARY && date.monday? && week_of(date) == 3
|
17
|
+
end
|
18
|
+
|
19
|
+
# Washington's Birthday (third Monday of February)
|
20
|
+
US.has_day_off_on :washingtons_day do |date|
|
21
|
+
date.month == FEBRUARY && date.monday? && week_of(date) == 3
|
22
|
+
end
|
23
|
+
|
24
|
+
# Memorial Day (last Monday of May)
|
25
|
+
US.has_day_off_on :memorial_day do |date|
|
26
|
+
date.month == MAY && date.monday? && last_week?(date)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Independence Day (July 4)
|
30
|
+
US.has_day_off_on :independence_day do |date|
|
31
|
+
date.month == JULY && nearest_weekday(date, 4)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Labor Day (first Monday of September)
|
35
|
+
US.has_day_off_on :labor_day do |date|
|
36
|
+
date.month == SEPTEMBER && date.monday? && week_of(date) == 1
|
37
|
+
end
|
38
|
+
|
39
|
+
# Columbus Day (second Monday of October)
|
40
|
+
US.has_day_off_on :columbus_day do |date|
|
41
|
+
date.month == OCTOBER && date.monday? && week_of(date) == 2
|
42
|
+
end
|
43
|
+
|
44
|
+
# Veterans Day (November 11)
|
45
|
+
US.has_day_off_on :veterans_day do |date|
|
46
|
+
date.month == NOVEMBER && nearest_weekday(date, 11)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Thanksgiving Day (fourth Thursday of November)
|
50
|
+
US.has_day_off_on :thanksgiving do |date|
|
51
|
+
date.month == NOVEMBER && date.thursday? && week_of(date) == 4
|
52
|
+
end
|
53
|
+
|
54
|
+
# Christmas Day (December 25)
|
55
|
+
US.has_day_off_on :christmas do |date|
|
56
|
+
date.month == DECEMBER && nearest_weekday(date, 25)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/rupee/option.rb
CHANGED
@@ -27,6 +27,14 @@ module Rupee
|
|
27
27
|
@value = self.class.black_scholes @type.to_s, @underlying, @strike,
|
28
28
|
@time, @rate, @div_yield, @volatility
|
29
29
|
end
|
30
|
+
|
31
|
+
[:charm, :color, :delta, :dual_delta, :dual_gamma, :dvega_dtime, :gamma,
|
32
|
+
:rho, :speed, :theta, :vanna, :vega, :vomma, :zomma].each do |method_name|
|
33
|
+
define_method method_name do
|
34
|
+
self.class.send method_name, @type, @underlying, @strike, @time,
|
35
|
+
@rate, @div_yield, @volatility
|
36
|
+
end
|
37
|
+
end
|
30
38
|
end
|
31
39
|
|
32
40
|
# The same thing as Rupee::Option, but with the <tt>:type</tt> flag set to
|
data/lib/rupee/quote.rb
CHANGED
@@ -44,6 +44,9 @@ module Rupee
|
|
44
44
|
# Configuration options
|
45
45
|
#
|
46
46
|
# * <tt>:source</tt> - The name of the source (default is +:bloomberg+).
|
47
|
+
# * <tt>:bloomberg</tt> - The Bloomberg quote service
|
48
|
+
# * <tt>:google</tt> - The Google Finance quote service
|
49
|
+
# * <tt>:yahoo</tt> - The Yahoo! quote service
|
47
50
|
# * <tt>:frequency</tt> - How often the quote will seek new values from the
|
48
51
|
# quote source, in seconds (default is +15+).
|
49
52
|
def initialize(ticker, opts = {})
|
@@ -54,7 +57,10 @@ module Rupee
|
|
54
57
|
@next_pull = Time.now
|
55
58
|
end
|
56
59
|
|
57
|
-
# Retrieves the current
|
60
|
+
# Retrieves the current information for a security
|
61
|
+
#
|
62
|
+
# Rupee::Quote.new("WFC").get :price, :change, :pct_chg
|
63
|
+
# #=> {:price=>24.96, :change=>0.17, :pct_chg =>0.686}
|
58
64
|
def get(*params)
|
59
65
|
now = Time.now
|
60
66
|
params = [:price] if params.empty?
|
@@ -83,6 +89,12 @@ module Rupee
|
|
83
89
|
end
|
84
90
|
end
|
85
91
|
|
92
|
+
# Retrieves the current information for a security
|
93
|
+
#
|
94
|
+
# Rupee::Quote.new("WFC")[:price, :change, :pct_chg]
|
95
|
+
# #=> {:price=>24.96, :change=>0.17, :pct_chg =>0.686}
|
96
|
+
alias :[] :get
|
97
|
+
|
86
98
|
# call-seq: #price
|
87
99
|
#
|
88
100
|
# Test
|
@@ -95,22 +107,39 @@ module Rupee
|
|
95
107
|
|
96
108
|
# The bid-ask spread
|
97
109
|
def bid_ask
|
98
|
-
|
110
|
+
diff bid, ask
|
111
|
+
end
|
99
112
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
(a - b).round precision(a, b)
|
104
|
-
end
|
113
|
+
# The daily trading range
|
114
|
+
def range
|
115
|
+
diff low, high
|
105
116
|
end
|
106
117
|
|
107
|
-
|
118
|
+
# The daily trading range
|
119
|
+
alias :trading_range :range
|
120
|
+
|
121
|
+
# The setter method for <tt>frequency</tt> also adjusts <tt>next_pull</tt>,
|
122
|
+
# such that if you change <tt>frequency</tt> from <tt>15</tt> to
|
123
|
+
# <tt>5</tt>, <tt>next_pull</tt> will move 10 seconds earlier.
|
124
|
+
def frequency=(x)
|
108
125
|
@next_pull += (x - @frequency)
|
109
126
|
@frequency = x
|
110
127
|
end
|
111
128
|
|
112
129
|
private
|
113
130
|
|
131
|
+
# Calculates the difference between <tt>x</tt> and <tt>y</tt>. This is
|
132
|
+
# different from merely subtracting <tt>x</tt> from <tt>y</tt> because of
|
133
|
+
# it forces the result to have the same number of significant digits as
|
134
|
+
# <tt>x</tt> or <tt>y</tt>, adjusted for whichever has more.
|
135
|
+
def diff(x, y)
|
136
|
+
if x.nil? || y.nil?
|
137
|
+
nil
|
138
|
+
else
|
139
|
+
(y - x).round precision(x, y)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
114
143
|
# Parses an object that might be a number
|
115
144
|
#
|
116
145
|
# parse "15" #=> 15
|
data/lib/rupee/version.rb
CHANGED
data/spec/native/option_spec.rb
CHANGED
@@ -4,53 +4,140 @@ describe Option do
|
|
4
4
|
describe "European option valuation" do
|
5
5
|
before :each do
|
6
6
|
@tolerance = 0.0001
|
7
|
-
@
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
@underlying = 60
|
8
|
+
@strike = 65
|
9
|
+
@time = 0.25
|
10
|
+
@rate = 0.08
|
11
|
+
@div_yield = 0.0
|
12
|
+
@volatility = 0.3
|
13
|
+
|
14
|
+
@call = Call.new(
|
15
|
+
:underlying => @underlying,
|
16
|
+
:strike => @strike,
|
17
|
+
:time => @time,
|
18
|
+
:rate => @rate,
|
19
|
+
:div_yield => @div_yield,
|
20
|
+
:volatility => @volatility
|
21
|
+
)
|
22
|
+
|
23
|
+
@put = Put.new(
|
24
|
+
:underlying => @underlying,
|
25
|
+
:strike => @strike,
|
26
|
+
:time => @time,
|
27
|
+
:rate => @rate,
|
28
|
+
:div_yield => @div_yield,
|
29
|
+
:volatility => @volatility
|
14
30
|
)
|
15
31
|
end
|
16
32
|
|
17
33
|
describe "using the Black-76 model" do
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
34
|
+
it "should return an accurate valuation for a call" do
|
35
|
+
Option.black76("c", @underlying, @strike, @time, @rate, @volatility).should be_within(@tolerance).of 1.7202
|
36
|
+
end
|
22
37
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
38
|
+
it "should return an accurate valuation for a put" do
|
39
|
+
Option.black76("p", @underlying, @strike, @time, @rate, @volatility).should be_within(@tolerance).of 6.6212
|
26
40
|
end
|
27
41
|
end
|
28
42
|
|
29
43
|
describe "using the generalized Black-Scholes model" do
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
44
|
+
it "should return an accurate valuation for a call" do
|
45
|
+
Option.generalized_black_scholes("c", @underlying, @strike, @time, @rate, @div_yield, @volatility).should be_within(@tolerance).of 1.7202
|
46
|
+
Option.gbs("c", @underlying, @strike, @time, @rate, @div_yield, @volatility).should be_within(@tolerance).of 1.7202
|
47
|
+
end
|
35
48
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
49
|
+
it "should return an accurate valuation for a put" do
|
50
|
+
Option.generalized_black_scholes("p", @underlying, @strike, @time, @rate, @div_yield, @volatility).should be_within(@tolerance).of 6.6212
|
51
|
+
Option.gbs("p", @underlying, @strike, @time, @rate, @div_yield, @volatility).should be_within(@tolerance).of 6.6212
|
40
52
|
end
|
41
53
|
end
|
42
54
|
|
43
55
|
describe "using the Black-Scholes model" do
|
44
|
-
describe "on a call option
|
45
|
-
it "should return
|
46
|
-
|
47
|
-
|
48
|
-
|
56
|
+
describe "on a call option" do
|
57
|
+
it "should return an accurate valuation" do
|
58
|
+
value = @call.black_scholes
|
59
|
+
value.should be_within(@tolerance).of 2.1334
|
60
|
+
Option.black_scholes("c", @underlying, @strike, @time, @rate, @div_yield, @volatility).should == value
|
61
|
+
Option.bs("c", @underlying, @strike, @time, @rate, @div_yield, @volatility).should == value
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should return an accurate charm" do
|
65
|
+
value = @call.charm
|
66
|
+
value.should be_a_kind_of Float
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should return an accurate color" do
|
70
|
+
value = @call.color
|
71
|
+
value.should be_a_kind_of Float
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should return an accurate delta" do
|
75
|
+
value = @call.delta
|
76
|
+
value.should be_a_kind_of Float
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should return an accurate dual_delta" do
|
80
|
+
value = @call.dual_delta
|
81
|
+
value.should be_a_kind_of Float
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should return an accurate dual_gamma" do
|
85
|
+
value = @call.dual_gamma
|
86
|
+
value.should be_a_kind_of Float
|
49
87
|
end
|
50
88
|
|
51
|
-
it "should return
|
52
|
-
|
53
|
-
|
89
|
+
it "should return an accurate dvega_dtime" do
|
90
|
+
value = @call.dvega_dtime
|
91
|
+
value.should be_a_kind_of Float
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should return an accurate gamma" do
|
95
|
+
value = @call.gamma
|
96
|
+
value.should be_a_kind_of Float
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should return an accurate rho" do
|
100
|
+
value = @call.rho
|
101
|
+
value.should be_a_kind_of Float
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should return an accurate speed" do
|
105
|
+
value = @call.speed
|
106
|
+
value.should be_a_kind_of Float
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should return an accurate theta" do
|
110
|
+
value = @call.theta
|
111
|
+
value.should be_a_kind_of Float
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should return an accurate vanna" do
|
115
|
+
value = @call.vanna
|
116
|
+
value.should be_a_kind_of Float
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should return an accurate vega" do
|
120
|
+
value = @call.vega
|
121
|
+
value.should be_a_kind_of Float
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should return an accurate vomma" do
|
125
|
+
value = @call.vomma
|
126
|
+
value.should be_a_kind_of Float
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should return an accurate zomma" do
|
130
|
+
value = @call.zomma
|
131
|
+
value.should be_a_kind_of Float
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "on a put option" do
|
136
|
+
it "should return an accurate valuation" do
|
137
|
+
value = @put.black_scholes
|
138
|
+
value.should be_within(@tolerance).of 5.8463
|
139
|
+
Option.black_scholes("p", @underlying, @strike, @time, @rate, @div_yield, @volatility).should == value
|
140
|
+
Option.bs("p", @underlying, @strike, @time, @rate, @div_yield, @volatility).should == value
|
54
141
|
end
|
55
142
|
end
|
56
143
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe Calendar do
|
4
|
+
describe "for Federal Reserve holidays" do
|
5
|
+
it "should be accurate for New Year's Day" do
|
6
|
+
Calendar::US.day_off?(Time.new(2011, 1, 1)).should_not be false
|
7
|
+
Calendar::US.day_off?(Time.new(2012, 1, 2)).should_not be false
|
8
|
+
Calendar::US.day_off?(Time.new(2013, 1, 1)).should_not be false
|
9
|
+
Calendar::US.day_off?(Time.new(2014, 1, 1)).should_not be false
|
10
|
+
Calendar::US.day_off?(Time.new(2015, 1, 1)).should_not be false
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should be accurate for Martin Luther King's Birthday" do
|
14
|
+
Calendar::US.day_off?(Time.new(2011, 1, 17)).should_not be false
|
15
|
+
Calendar::US.day_off?(Time.new(2012, 1, 16)).should_not be false
|
16
|
+
Calendar::US.day_off?(Time.new(2013, 1, 21)).should_not be false
|
17
|
+
Calendar::US.day_off?(Time.new(2014, 1, 20)).should_not be false
|
18
|
+
Calendar::US.day_off?(Time.new(2015, 1, 19)).should_not be false
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should be accurate for Washington's Birthday" do
|
22
|
+
Calendar::US.day_off?(Time.new(2011, 2, 21)).should_not be false
|
23
|
+
Calendar::US.day_off?(Time.new(2012, 2, 20)).should_not be false
|
24
|
+
Calendar::US.day_off?(Time.new(2013, 2, 18)).should_not be false
|
25
|
+
Calendar::US.day_off?(Time.new(2014, 2, 17)).should_not be false
|
26
|
+
Calendar::US.day_off?(Time.new(2015, 2, 16)).should_not be false
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should be accurate for Memorial Day" do
|
30
|
+
Calendar::US.day_off?(Time.new(2011, 5, 30)).should_not be false
|
31
|
+
Calendar::US.day_off?(Time.new(2012, 5, 28)).should_not be false
|
32
|
+
Calendar::US.day_off?(Time.new(2013, 5, 27)).should_not be false
|
33
|
+
Calendar::US.day_off?(Time.new(2014, 5, 26)).should_not be false
|
34
|
+
Calendar::US.day_off?(Time.new(2015, 5, 25)).should_not be false
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should be accurate for Independence Day" do
|
38
|
+
Calendar::US.day_off?(Time.new(2011, 7, 4)).should_not be false
|
39
|
+
Calendar::US.day_off?(Time.new(2012, 7, 4)).should_not be false
|
40
|
+
Calendar::US.day_off?(Time.new(2013, 7, 4)).should_not be false
|
41
|
+
Calendar::US.day_off?(Time.new(2014, 7, 4)).should_not be false
|
42
|
+
Calendar::US.day_off?(Time.new(2015, 7, 4)).should_not be false
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should be accurate for Labor Day" do
|
46
|
+
Calendar::US.day_off?(Time.new(2011, 9, 5)).should_not be false
|
47
|
+
Calendar::US.day_off?(Time.new(2012, 9, 3)).should_not be false
|
48
|
+
Calendar::US.day_off?(Time.new(2013, 9, 2)).should_not be false
|
49
|
+
Calendar::US.day_off?(Time.new(2014, 9, 1)).should_not be false
|
50
|
+
Calendar::US.day_off?(Time.new(2015, 9, 7)).should_not be false
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should be accurate for Columbus Day" do
|
54
|
+
Calendar::US.day_off?(Time.new(2011, 10, 10)).should_not be false
|
55
|
+
Calendar::US.day_off?(Time.new(2012, 10, 8)).should_not be false
|
56
|
+
Calendar::US.day_off?(Time.new(2013, 10, 14)).should_not be false
|
57
|
+
Calendar::US.day_off?(Time.new(2014, 10, 13)).should_not be false
|
58
|
+
Calendar::US.day_off?(Time.new(2015, 10, 12)).should_not be false
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should be accurate for Veterans Day" do
|
62
|
+
Calendar::US.day_off?(Time.new(2011, 11, 11)).should_not be false
|
63
|
+
Calendar::US.day_off?(Time.new(2012, 11, 12)).should_not be false
|
64
|
+
Calendar::US.day_off?(Time.new(2013, 11, 11)).should_not be false
|
65
|
+
Calendar::US.day_off?(Time.new(2014, 11, 11)).should_not be false
|
66
|
+
Calendar::US.day_off?(Time.new(2015, 11, 11)).should_not be false
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should be accurate for Thanksgiving Day" do
|
70
|
+
Calendar::US.day_off?(Time.new(2011, 11, 24)).should_not be false
|
71
|
+
Calendar::US.day_off?(Time.new(2012, 11, 22)).should_not be false
|
72
|
+
Calendar::US.day_off?(Time.new(2013, 11, 28)).should_not be false
|
73
|
+
Calendar::US.day_off?(Time.new(2014, 11, 27)).should_not be false
|
74
|
+
Calendar::US.day_off?(Time.new(2015, 11, 26)).should_not be false
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should be accurate for Christmas Day" do
|
78
|
+
Calendar::US.day_off?(Time.new(2011, 12, 26)).should_not be false
|
79
|
+
Calendar::US.day_off?(Time.new(2012, 12, 25)).should_not be false
|
80
|
+
Calendar::US.day_off?(Time.new(2013, 12, 25)).should_not be false
|
81
|
+
Calendar::US.day_off?(Time.new(2014, 12, 25)).should_not be false
|
82
|
+
Calendar::US.day_off?(Time.new(2015, 12, 25)).should_not be false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|