rupee 0.2.1 → 0.2.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/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
|