rupee 0.2.5 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/rupee.rb +13 -10
- data/lib/rupee/business_day.rb +49 -0
- data/lib/rupee/business_day/actual.rb +8 -0
- data/lib/rupee/business_day/following.rb +12 -0
- data/lib/rupee/business_day/modified_following.rb +23 -0
- data/lib/rupee/business_day/modified_previous.rb +23 -0
- data/lib/rupee/business_day/previous.rb +12 -0
- data/lib/rupee/calendar.rb +5 -2
- data/lib/rupee/calendar/japan.rb +11 -4
- data/lib/rupee/currency.rb +6 -0
- data/lib/rupee/custom.rb +65 -21
- data/lib/rupee/day_count.rb +74 -0
- data/lib/rupee/day_count/30_360.rb +20 -0
- data/lib/rupee/day_count/30e+_360.rb +17 -0
- data/lib/rupee/day_count/30e_360.rb +14 -0
- data/lib/rupee/day_count/30e_360_isda.rb +14 -0
- data/lib/rupee/day_count/act_360.rb +8 -0
- data/lib/rupee/day_count/act_365.rb +8 -0
- data/lib/rupee/day_count/act_act.rb +10 -0
- data/lib/rupee/mixins/find_instance.rb +23 -0
- data/lib/rupee/quote.rb +1 -5
- data/lib/rupee/source.rb +28 -0
- data/lib/rupee/source/bloomberg.rb +20 -0
- data/lib/rupee/source/google.rb +17 -0
- data/lib/rupee/source/yahoo.rb +18 -0
- data/lib/rupee/version.rb +1 -1
- data/lib/rupee/yield_curve.rb +2 -0
- data/spec/ruby/business_day_spec.rb +114 -0
- data/spec/ruby/calendar_spec.rb +18 -11
- data/spec/ruby/day_count_spec.rb +52 -0
- data/spec/ruby/generic_spec.rb +1 -1
- data/spec/ruby/quote_spec.rb +0 -4
- data/spec/ruby/source_spec.rb +18 -0
- data/tasks/benchmark.rake +26 -4
- metadata +32 -9
- data/lib/rupee/quote/source.rb +0 -83
data/lib/rupee.rb
CHANGED
@@ -16,15 +16,18 @@ require "rupee/version"
|
|
16
16
|
|
17
17
|
# This module contains all modules and classes associated with Rupee
|
18
18
|
module Rupee
|
19
|
-
autoload :Security,
|
19
|
+
autoload :Security, "rupee/security"
|
20
20
|
|
21
|
-
autoload :
|
22
|
-
autoload :
|
23
|
-
autoload :
|
24
|
-
autoload :
|
25
|
-
autoload :
|
26
|
-
autoload :
|
27
|
-
autoload :
|
28
|
-
autoload :
|
29
|
-
autoload :
|
21
|
+
autoload :Benchmark, "rupee/benchmark"
|
22
|
+
autoload :BusinessDay, "rupee/business_day"
|
23
|
+
autoload :Calendar, "rupee/calendar"
|
24
|
+
autoload :Custom, "rupee/custom"
|
25
|
+
autoload :Call, "rupee/option"
|
26
|
+
autoload :Currency, "rupee/currency"
|
27
|
+
autoload :DayCount, "rupee/day_count"
|
28
|
+
autoload :Option, "rupee/option"
|
29
|
+
autoload :Put, "rupee/option"
|
30
|
+
autoload :Quote, "rupee/quote"
|
31
|
+
autoload :Source, "rupee/source"
|
32
|
+
autoload :YieldCurve, "rupee/yield_curve"
|
30
33
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "rupee/mixins/find_instance"
|
2
|
+
|
3
|
+
module Rupee
|
4
|
+
# A business day convention, used to determine the next business day should a
|
5
|
+
# calculated payment date fall on a non-working day
|
6
|
+
class BusinessDay
|
7
|
+
autoload :ACTUAL, "rupee/business_day/actual"
|
8
|
+
autoload :FOLLOWING, "rupee/business_day/following"
|
9
|
+
autoload :MODIFIED_FOLLOWING, "rupee/business_day/modified_following"
|
10
|
+
autoload :MODIFIED_PREVIOUS, "rupee/business_day/modified_previous"
|
11
|
+
autoload :PREVIOUS, "rupee/business_day/previous"
|
12
|
+
|
13
|
+
# A description of the business day and where it's often found
|
14
|
+
attr :description
|
15
|
+
# The formula for calculated the next business day
|
16
|
+
attr :block
|
17
|
+
# The calendar used for calculating holidays and days off
|
18
|
+
attr :calendar
|
19
|
+
|
20
|
+
# Create a new business day object
|
21
|
+
#
|
22
|
+
# Configuration options
|
23
|
+
#
|
24
|
+
# * <tt>:calendar</tt> - The calendar to use for calculating holidays and
|
25
|
+
# days off
|
26
|
+
def initialize(description, opts = {}, &block)
|
27
|
+
opts = { :calendar => :us }.merge opts
|
28
|
+
|
29
|
+
@description = description
|
30
|
+
@block = block
|
31
|
+
|
32
|
+
self.calendar=(opts[:calendar])
|
33
|
+
end
|
34
|
+
|
35
|
+
def calendar=(calendar) # :nodoc:
|
36
|
+
@calendar = Calendar.find(calendar)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Calculates the next business day according to the calendar and the date
|
40
|
+
# given
|
41
|
+
def next_day(date)
|
42
|
+
block.call date, @calendar
|
43
|
+
end
|
44
|
+
|
45
|
+
class << self
|
46
|
+
include FindInstance
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rupee
|
2
|
+
class BusinessDay
|
3
|
+
# Modified following business day convention
|
4
|
+
MODIFIED_FOLLOWING = BusinessDay.new "Roll to following business day " +
|
5
|
+
"unless it's in the next month, then use previous business day" do |date, calendar|
|
6
|
+
month = date.month
|
7
|
+
|
8
|
+
while calendar.day_off?(date) && date.month == month
|
9
|
+
date += 86_400
|
10
|
+
end
|
11
|
+
|
12
|
+
if date.month != month
|
13
|
+
while calendar.day_off?(date)
|
14
|
+
date -= 86_400
|
15
|
+
end
|
16
|
+
else
|
17
|
+
date
|
18
|
+
end
|
19
|
+
|
20
|
+
date
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rupee
|
2
|
+
class BusinessDay
|
3
|
+
# Modified previous business day convention
|
4
|
+
MODIFIED_PREVIOUS = BusinessDay.new "Roll to previous business day " +
|
5
|
+
"unless it's in the previous month, then use following business day" do |date, calendar|
|
6
|
+
month = date.month
|
7
|
+
|
8
|
+
while calendar.day_off?(date) && date.month == month
|
9
|
+
date -= 86_400
|
10
|
+
end
|
11
|
+
|
12
|
+
if date.month != month
|
13
|
+
while calendar.day_off?(date)
|
14
|
+
date += 86_400
|
15
|
+
end
|
16
|
+
else
|
17
|
+
date
|
18
|
+
end
|
19
|
+
|
20
|
+
date
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/rupee/calendar.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "rupee/mixins/find_instance"
|
2
|
+
|
1
3
|
module Rupee
|
2
4
|
# An object representing a calendar, for use in determining the next payout
|
3
5
|
# date for a cash flow. Simple example:
|
@@ -171,6 +173,8 @@ module Rupee
|
|
171
173
|
end
|
172
174
|
|
173
175
|
class << self
|
176
|
+
include FindInstance
|
177
|
+
|
174
178
|
# Calculates the week of the month in which the given date falls
|
175
179
|
def week_of(date)
|
176
180
|
(date.day - 1) / 7 + 1
|
@@ -189,7 +193,7 @@ module Rupee
|
|
189
193
|
when 2
|
190
194
|
# Save February, with twenty-eight days clear
|
191
195
|
# And twenty-nine each leap year ;)
|
192
|
-
date.day > (date.year % 4 == 0 && date.year % 100 != 0) ? 22 : 21
|
196
|
+
date.day > (date.year % 4 == 0 && (date.year % 100 != 0 || date.year % 400 == 0)) ? 22 : 21
|
193
197
|
end
|
194
198
|
end
|
195
199
|
|
@@ -246,7 +250,6 @@ module Rupee
|
|
246
250
|
date.day == day
|
247
251
|
end
|
248
252
|
end
|
249
|
-
|
250
253
|
end
|
251
254
|
end
|
252
255
|
end
|
data/lib/rupee/calendar/japan.rb
CHANGED
@@ -29,7 +29,10 @@ module Rupee
|
|
29
29
|
|
30
30
|
# Vernal Equinox Day
|
31
31
|
Japan.has_day_off_on :shunbun do |date|
|
32
|
-
|
32
|
+
# Currently alternating between the 20th for two years, then the 21st for
|
33
|
+
# the next two years
|
34
|
+
date.month == MARCH &&
|
35
|
+
next_monday_if_sunday(date, date.year % 4 < 2 ? 20 : 21)
|
33
36
|
end
|
34
37
|
|
35
38
|
# Showa Day
|
@@ -37,7 +40,8 @@ module Rupee
|
|
37
40
|
date.month == APRIL && next_monday_if_sunday(date, 29)
|
38
41
|
end
|
39
42
|
|
40
|
-
# Golden Week: Constitution Memorial Day, Greenery Day and Children's
|
43
|
+
# Golden Week: Constitution Memorial Day, Greenery Day and Children's Day,
|
44
|
+
# respectively
|
41
45
|
Japan.has_day_off_on :golden_week do |date|
|
42
46
|
date.month == MAY && (date.day.between?(3, 5) ||
|
43
47
|
((date.monday? || date.tuesday?) && date.day == 6))
|
@@ -54,8 +58,11 @@ module Rupee
|
|
54
58
|
end
|
55
59
|
|
56
60
|
# Autumnal Equinox Day
|
57
|
-
Japan.has_day_off_on :
|
58
|
-
|
61
|
+
Japan.has_day_off_on :shubun do |date|
|
62
|
+
# Currently alternating between the 22nd for one years, then the 23rd for
|
63
|
+
# the next three years
|
64
|
+
date.month == SEPTEMBER &&
|
65
|
+
next_monday_if_sunday(date, date.year % 4 == 0 ? 22 : 23)
|
59
66
|
end
|
60
67
|
|
61
68
|
# Health and Sports Day
|
data/lib/rupee/currency.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
require "rupee/mixins/find_instance"
|
3
|
+
|
2
4
|
module Rupee
|
3
5
|
# A holder for currencies
|
4
6
|
class Currency
|
@@ -50,5 +52,9 @@ module Rupee
|
|
50
52
|
|
51
53
|
"#{@symbol}#{parts.join @separator}"
|
52
54
|
end
|
55
|
+
|
56
|
+
class << self
|
57
|
+
include FindInstance
|
58
|
+
end
|
53
59
|
end
|
54
60
|
end
|
data/lib/rupee/custom.rb
CHANGED
@@ -5,42 +5,86 @@ module Rupee
|
|
5
5
|
# curves, payout curves, calendars, currencies, daycounts, roll day
|
6
6
|
# conventions, etc.
|
7
7
|
class Custom < Security
|
8
|
-
# The
|
8
|
+
# The security's business day convention
|
9
|
+
attr :business_day
|
10
|
+
# The calendar used for determining holidays
|
9
11
|
attr :calendar
|
10
|
-
# The security's
|
12
|
+
# The security's currency
|
11
13
|
attr :currency
|
14
|
+
# The day count convention for determining payment and accrual dates
|
15
|
+
attr :day_count
|
12
16
|
|
13
17
|
# Build a custom security
|
18
|
+
#
|
19
|
+
# Configuration options
|
20
|
+
#
|
21
|
+
# * <tt>:business_day</tt> - The security's business day convention, used
|
22
|
+
# to determine when the next business day is relative to the calendar in
|
23
|
+
# use (default is :modified_following)
|
24
|
+
# * <tt>:actual</tt> - The actual day, regardless of whether it's a
|
25
|
+
# business day
|
26
|
+
# * <tt>:following</tt> - The following business day
|
27
|
+
# * <tt>:modified_following</tt> - The following business day unless it
|
28
|
+
# occurs in the following month, in which case use the previous
|
29
|
+
# business day
|
30
|
+
# * <tt>:modified_previous</tt> - The previous business day unless it
|
31
|
+
# occurs in the previous month, in which case use the following
|
32
|
+
# business day
|
33
|
+
# * <tt>:previous</tt> - The previous business day
|
34
|
+
# * <tt>:calendar</tt> - The calendar to use for determining holidays and
|
35
|
+
# days off (default is +:us+)
|
36
|
+
# * <tt>:us</tt> - The US Federal Reserve Calendar
|
37
|
+
# * <tt>:japan</tt> - The Japanese calendar
|
38
|
+
# * <tt>:currency</tt> - (default is +:usd+)
|
39
|
+
# * <tt>:eur</tt> or <tt>:euro</tt> - The euro
|
40
|
+
# * <tt>:jpy</tt> or <tt>:yen</tt> - The Japanese yen
|
41
|
+
# * <tt>:gbp</tt> or <tt>:pound</tt> - The British pound sterling
|
42
|
+
# * <tt>:usd</tt> or <tt>:dollar</tt> - The US dollar
|
43
|
+
# * <tt>:day_count</tt> - (default is +:thirty_360+)
|
44
|
+
# * <tt>:thirty_360</tt> - 30/360
|
45
|
+
# * <tt>:thirty_e_360</tt> - 30E/360
|
46
|
+
# * <tt>:thirty_e_360_isda</tt> - 30E/360 ISDA
|
47
|
+
# * <tt>:thirty_e_plus_360</tt> - 30E+/360
|
48
|
+
# * <tt>:act_360</tt> - Act/360
|
49
|
+
# * <tt>:act_365</tt> - Act/365
|
50
|
+
# * <tt>:act_act</tt> - Act/Act
|
51
|
+
#
|
52
|
+
# require "rupee"
|
53
|
+
#
|
54
|
+
# # A typical pay-fixed bond
|
55
|
+
# bond = Rupee::Custom.new
|
56
|
+
#
|
57
|
+
# # A typical yen LIBOR security
|
58
|
+
# bond = Rupee::Custom.new :calendar => :japan, :currency => :yen,
|
59
|
+
# :day_count => :act_365
|
14
60
|
def initialize(opts = {})
|
15
61
|
opts = {
|
16
|
-
:
|
17
|
-
:
|
62
|
+
:business_day => :modified_following,
|
63
|
+
:calendar => :us,
|
64
|
+
:currency => :usd,
|
65
|
+
:day_count => :thirty_360
|
18
66
|
}.merge opts
|
19
67
|
|
20
|
-
self.
|
21
|
-
self.
|
68
|
+
self.business_day = opts[:business_day]
|
69
|
+
self.calendar = opts[:calendar]
|
70
|
+
self.currency = opts[:currency]
|
71
|
+
self.day_count = opts[:day_count]
|
22
72
|
end
|
23
73
|
|
24
|
-
def
|
25
|
-
@
|
74
|
+
def business_day=(business_day) # :nodoc:
|
75
|
+
@business_day = BusinessDay.find(business_day)
|
26
76
|
end
|
27
77
|
|
28
|
-
def
|
29
|
-
@
|
78
|
+
def calendar=(calendar) # :nodoc:
|
79
|
+
@calendar = Calendar.find(calendar)
|
30
80
|
end
|
31
81
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# isn't already one)
|
36
|
-
def to_instance(x, constant)
|
37
|
-
x = x.upcase
|
82
|
+
def currency=(currency) # :nodoc:
|
83
|
+
@currency = Currency.find(currency)
|
84
|
+
end
|
38
85
|
|
39
|
-
|
40
|
-
|
41
|
-
else
|
42
|
-
constant.const_get(x)
|
43
|
-
end
|
86
|
+
def day_count=(day_count) # :nodoc:
|
87
|
+
@day_count = DayCount.find(day_count)
|
44
88
|
end
|
45
89
|
end
|
46
90
|
end
|
data/lib/rupee/day_count.rb
CHANGED
@@ -1,4 +1,78 @@
|
|
1
|
+
require "rupee/mixins/find_instance"
|
2
|
+
|
1
3
|
module Rupee
|
4
|
+
# A class representing a day count convention used to determine cash flow
|
5
|
+
# and accrual dates for fixed income products
|
2
6
|
class DayCount
|
7
|
+
include FindInstance
|
8
|
+
|
9
|
+
autoload :THIRTY_360, "rupee/day_count/30_360"
|
10
|
+
autoload :THIRTY_E_360, "rupee/day_count/30e_360"
|
11
|
+
autoload :THIRTY_E_360_ISDA, "rupee/day_count/30e_360_isda"
|
12
|
+
autoload :THIRTY_E_PLUS_360, "rupee/day_count/30e+_360"
|
13
|
+
autoload :ACT_360, "rupee/day_count/act_360"
|
14
|
+
autoload :ACT_365, "rupee/day_count/act_365"
|
15
|
+
autoload :ACT_ACT, "rupee/day_count/act_act"
|
16
|
+
|
17
|
+
# A description of the day count convention
|
18
|
+
attr :description
|
19
|
+
# The formula for determining a day count factor (days divided by years)
|
20
|
+
attr :block
|
21
|
+
|
22
|
+
# Create a new DayCount object
|
23
|
+
def initialize(description, &block)
|
24
|
+
@description = description
|
25
|
+
@block = block
|
26
|
+
end
|
27
|
+
|
28
|
+
def factor(from, to)
|
29
|
+
block.call from, to
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
# The number of seconds in a day (a difference of <tt>1</tt> between two
|
34
|
+
# dates in Ruby indicates a difference of one second)
|
35
|
+
SECONDS_PER_DAY = 86_400.0
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def days(from, to)
|
40
|
+
(to - from) / SECONDS_PER_DAY
|
41
|
+
end
|
42
|
+
|
43
|
+
def days_in_year(date)
|
44
|
+
if leap_year?(date)
|
45
|
+
366.0
|
46
|
+
else
|
47
|
+
365.0
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def end_of_month?(date)
|
52
|
+
case date.month
|
53
|
+
when 9, 4, 6, 11
|
54
|
+
# Thirty days hath September
|
55
|
+
# April, June and November
|
56
|
+
date.day == 30
|
57
|
+
when 1, 3, 5, 7, 8, 10, 12
|
58
|
+
# All the rest have thirty-one
|
59
|
+
date.day == 31
|
60
|
+
when 2
|
61
|
+
# Save February, with twenty-eight days clear
|
62
|
+
# And twenty-nine each leap year ;)
|
63
|
+
date.day == (date.year % 4 == 0 && (date.year % 100 != 0 || date.year % 400 == 0)) ? 29 : 28
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Determines whether a date falls during a leap year. Leap years include
|
68
|
+
# all years divisible by 4, with the exception of years divisible by 100
|
69
|
+
# but not divisible by 400 (got that?). That is, 2004 is a leap year, as is
|
70
|
+
# 1904. But while 2000 is a leap year (divisible by 400), 1900 is not
|
71
|
+
# (divisible by 100 but not 400).
|
72
|
+
def leap_year?(date) # :doc:
|
73
|
+
year = date.year
|
74
|
+
year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
|
75
|
+
end
|
76
|
+
end
|
3
77
|
end
|
4
78
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rupee
|
2
|
+
class DayCount
|
3
|
+
# Standard US 30/360
|
4
|
+
THIRTY_360 = DayCount.new "30/360, typical pay-fixed convention" do |from, to|
|
5
|
+
m1 = from.month
|
6
|
+
m2 = to.month
|
7
|
+
d1 = from.day
|
8
|
+
d2 = to.day
|
9
|
+
|
10
|
+
if end_of_month?(from)
|
11
|
+
d1 = 30
|
12
|
+
d2 = 30 if m1 == 2 && end_of_month?(to) && m2 == 2
|
13
|
+
end
|
14
|
+
|
15
|
+
d2 = 30 if d2 == 31 && d1 == 30
|
16
|
+
|
17
|
+
(360 * (to.year - from.year) + 30 * (m2 - m1) + (d2 - d1)) / 360.0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Rupee
|
2
|
+
class DayCount
|
3
|
+
# 30E+/360
|
4
|
+
THIRTY_E_PLUS_360 = DayCount.new "30E+/360" do |from, to|
|
5
|
+
m = to.month - from.month
|
6
|
+
d1 = from.day
|
7
|
+
d2 = to.day
|
8
|
+
d1 = 30 if d1 == 31
|
9
|
+
if d2 == 31
|
10
|
+
m += 1
|
11
|
+
d2 = 1
|
12
|
+
end
|
13
|
+
|
14
|
+
(360 * (to.year - from.year) + 30 * m + (d2 - d1)) / 360.0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Rupee
|
2
|
+
class DayCount
|
3
|
+
# Standard European 30/360
|
4
|
+
THIRTY_E_360 = DayCount.new "30E/360" do |from, to|
|
5
|
+
d1 = from.day
|
6
|
+
d2 = to.day
|
7
|
+
d1 = 30 if d1 == 31
|
8
|
+
d2 = 30 if d2 == 31
|
9
|
+
|
10
|
+
(360 * (to.year - from.year) + 30 * (to.month - from.month) +
|
11
|
+
(d2 - d1)) / 360.0
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Rupee
|
2
|
+
class DayCount
|
3
|
+
# 30E/360 ISDA, typical for Eurobonds
|
4
|
+
THIRTY_E_360_ISDA = DayCount.new "30E/360 ISDA" do |from, to|
|
5
|
+
d1 = from.day
|
6
|
+
d2 = to.day
|
7
|
+
d1 = 30 if end_of_month?(from)
|
8
|
+
d2 = 30 if end_of_month?(to) # && (d2 != maturity_date || to.month != 2)
|
9
|
+
|
10
|
+
(360 * (to.year - from.year) + 30 * (to.month - from.month) +
|
11
|
+
(d2 - d1)) / 360.0
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rupee
|
2
|
+
# For finding instances within a class
|
3
|
+
module FindInstance
|
4
|
+
# Converts the supplied value to an instance of the specified class (if it
|
5
|
+
# isn't already one). For example, within the Source object you could find
|
6
|
+
# the Bloomberg object one of two ways:
|
7
|
+
#
|
8
|
+
# Rupee::Source.find :bloomberg
|
9
|
+
# Rupee::Source.find Rupee::Source::BLOOMBERG
|
10
|
+
#
|
11
|
+
# This allows the user more flexibility, as specifying the full path to
|
12
|
+
# the <tt>BLOOMBERG</tt> constant is no longer necessary
|
13
|
+
def find(x)
|
14
|
+
if x.instance_of?(self)
|
15
|
+
# If x is an actual instance of the class, just return it
|
16
|
+
x
|
17
|
+
else
|
18
|
+
# Otherwise, search for constants after capitalizing x
|
19
|
+
self.const_get x.upcase
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/rupee/quote.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require "rupee/quote/source"
|
2
1
|
autoload :Net, "net/http"
|
3
2
|
autoload :URI, "uri"
|
4
3
|
|
@@ -23,13 +22,10 @@ module Rupee
|
|
23
22
|
class Quote
|
24
23
|
# A ticker symbol
|
25
24
|
attr_accessor :ticker
|
26
|
-
|
27
25
|
# The name of the quote source
|
28
26
|
attr_accessor :source
|
29
|
-
|
30
27
|
# The frequency in seconds that a quote's information should be updated
|
31
28
|
attr_accessor :frequency
|
32
|
-
|
33
29
|
# The time at which the next pull from the online quote source will occur
|
34
30
|
attr :next_pull
|
35
31
|
|
@@ -52,7 +48,7 @@ module Rupee
|
|
52
48
|
def initialize(ticker, opts = {})
|
53
49
|
opts = { :source => :bloomberg, :frequency => 15 }.merge opts
|
54
50
|
@ticker = ticker.upcase
|
55
|
-
@source =
|
51
|
+
@source = Source.find(opts[:source])
|
56
52
|
@frequency = opts[:frequency]
|
57
53
|
@next_pull = Time.now
|
58
54
|
end
|
data/lib/rupee/source.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "rupee/mixins/find_instance"
|
2
|
+
|
3
|
+
module Rupee
|
4
|
+
# A class to hold quote sources
|
5
|
+
class Source
|
6
|
+
# Autoload default sources
|
7
|
+
autoload :BLOOMBERG, "rupee/source/bloomberg"
|
8
|
+
autoload :GOOGLE, "rupee/source/google"
|
9
|
+
autoload :YAHOO, "rupee/source/yahoo"
|
10
|
+
|
11
|
+
# The name of the source
|
12
|
+
attr :name
|
13
|
+
# The full URL for where the security information is located, where
|
14
|
+
# <tt>%s</tt> is a query parameter
|
15
|
+
attr :url
|
16
|
+
# The parameters available
|
17
|
+
attr :params
|
18
|
+
|
19
|
+
# Creates a new quote service
|
20
|
+
def initialize(name, url, params = {})
|
21
|
+
@name, @url, @params = name, url, params
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
include FindInstance
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rupee
|
2
|
+
class Source
|
3
|
+
# Bloomberg
|
4
|
+
BLOOMBERG = Source.new(:bloomberg,
|
5
|
+
"http://www.bloomberg.com/apps/quote?ticker=%s",
|
6
|
+
:price => /(?:PRICE|VALUE): <span class="amount">([0-9.,NA-]{1,})/,
|
7
|
+
:change => /Change<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
8
|
+
:pct_chg => /Change<\/td>\n<td class="value[^>]+>[0-9.,NA-]{1,} \(([0-9NA.,-]{1,})\%/,
|
9
|
+
:date => /"date">(.*?)</,
|
10
|
+
:time => /"time">(.*?)</,
|
11
|
+
:bid => /Bid<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
12
|
+
:ask => /Ask<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
13
|
+
:open => /Open<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
14
|
+
:high => /High<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
15
|
+
:low => /Low<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
16
|
+
:volume => /Volume<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
17
|
+
:mkt_cap => /Market Cap[^<]+<\/td>\n<td class="value">([0-9.,NA-]{1,})/,
|
18
|
+
:p_e => /Price\/Earnings[^<]+<\/td>\n<td class="value">([0-9.,NA-]{1,})/)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Rupee
|
2
|
+
class Source
|
3
|
+
# Google Finance
|
4
|
+
GOOGLE = Source.new(:google,
|
5
|
+
"http://www.google.com/ig/api?stock=%s",
|
6
|
+
:price => /<last data="([0-9.-]*)"/,
|
7
|
+
:change => /<change data="([0-9.-]*)"/,
|
8
|
+
:pct_chg => /<perc_change data="([0-9.-]*)"/,
|
9
|
+
:date => /<trade_date_utc data="([0-9.-]*)"/,
|
10
|
+
:time => /<trade_date_utc data="([0-9.-]*)"/,
|
11
|
+
:open => /<open data="([0-9.-]*)"/,
|
12
|
+
:high => /<high data="([0-9.-]*)"/,
|
13
|
+
:low => /<low data="([0-9.-]*)"/,
|
14
|
+
:volume => /<volume data="([0-9.-]*)"/,
|
15
|
+
:mkt_cap => /<market_cap data="([0-9.-]*)"/)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Rupee
|
2
|
+
class Source
|
3
|
+
YAHOO = Source.new(:yahoo,
|
4
|
+
"http://finance.yahoo.com/q?s=%s",
|
5
|
+
:price => /(?:<\/a> |"yfnc_tabledata1"><big><b>)<span id="yfs_l[19]0_[^>]+>([0-9,.-]{1,})/,
|
6
|
+
:change => /><span id="yfs_c[16]0_.*?(?:\n[\s\w\-]*\n[\s">\(]*?)?([0-9.,-]{1,})/,
|
7
|
+
:pct_chg => /yfs_p[24]0_.*?(?:\n[\s\w\-]*\n[\s">\(]*?)?([0-9.,-]{1,})%\)(?:<\/span>|<\/b><\/span><\/td>)/,
|
8
|
+
:date => /<span id="yfs_market_time">.*?, (.*?20[0-9]{1,2})/,
|
9
|
+
:time => /(?:"time"|"yfnc_tabledata1")><span id="yfs_t[51]0_[^>]+>(.*?)</,
|
10
|
+
:bid => /yfs_b00_[^>]+>([0-9,.-]{1,})/,
|
11
|
+
:ask => /yfs_a00_[^>]+>([0-9,.-]{1,})/,
|
12
|
+
:open => /Open:<\/th><td class="yfnc_tabledata1">([0-9,.-]{1,})/,
|
13
|
+
:high => /yfs_h00_[^>]+>([0-9,.-]{1,})/,
|
14
|
+
:low => /yfs_g00_[^>]+>([0-9,.-]{1,})/,
|
15
|
+
:volume => /yfs_v00_[^>]+>([0-9,.-]{1,})/,
|
16
|
+
:mkt_cap => /yfs_j10_[^>]+>([0-9,.-]{1,}[KMBT]?)/)
|
17
|
+
end
|
18
|
+
end
|
data/lib/rupee/version.rb
CHANGED
data/lib/rupee/yield_curve.rb
CHANGED
@@ -0,0 +1,114 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe BusinessDay do
|
4
|
+
before :each do
|
5
|
+
@new_years = Time.new(2012, 1, 1)
|
6
|
+
@new_years_eve = Time.new(2011, 12, 31)
|
7
|
+
@workday = Time.new(2011, 11, 23)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "for the actual convention" do
|
11
|
+
before :each do
|
12
|
+
@business_day = BusinessDay.find(:actual)
|
13
|
+
@month_begin_rolled = Time.new(2012, 1, 1)
|
14
|
+
@month_end_rolled = Time.new(2011, 12, 31)
|
15
|
+
@workday_rolled = Time.new(2011, 11, 23)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be correct for the beginning of the month" do
|
19
|
+
@business_day.next_day(@new_years).should == @month_begin_rolled
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should be correct for the end of the month" do
|
23
|
+
@business_day.next_day(@new_years_eve).should == @month_end_rolled
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be correct for an actual workday" do
|
27
|
+
@business_day.next_day(@workday).should == @workday_rolled
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "for the following convention" do
|
32
|
+
before :each do
|
33
|
+
@business_day = BusinessDay.find(:following)
|
34
|
+
@month_begin_rolled = Time.new(2012, 1, 3)
|
35
|
+
@month_end_rolled = Time.new(2012, 1, 3)
|
36
|
+
@workday_rolled = Time.new(2011, 11, 23)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should be correct for the beginning of the month" do
|
40
|
+
@business_day.next_day(@new_years).should == @month_begin_rolled
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should be correct for the end of the month" do
|
44
|
+
@business_day.next_day(@new_years_eve).should == @month_end_rolled
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should be correct for an actual workday" do
|
48
|
+
@business_day.next_day(@workday).should == @workday_rolled
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "for the modified following convention" do
|
53
|
+
before :each do
|
54
|
+
@business_day = BusinessDay.find(:modified_following)
|
55
|
+
@month_begin_rolled = Time.new(2012, 1, 3)
|
56
|
+
@month_end_rolled = Time.new(2011, 12, 30)
|
57
|
+
@workday_rolled = Time.new(2011, 11, 23)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should be correct for the beginning of the month" do
|
61
|
+
@business_day.next_day(@new_years).should == @month_begin_rolled
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should be correct for the end of the month" do
|
65
|
+
@business_day.next_day(@new_years_eve).should == @month_end_rolled
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should be correct for an actual workday" do
|
69
|
+
@business_day.next_day(@workday).should == @workday_rolled
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "for the modified previous convention" do
|
74
|
+
before :each do
|
75
|
+
@business_day = BusinessDay.find(:modified_previous)
|
76
|
+
@month_begin_rolled = Time.new(2012, 1, 3)
|
77
|
+
@month_end_rolled = Time.new(2011, 12, 30)
|
78
|
+
@workday_rolled = Time.new(2011, 11, 23)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should be correct for the beginning of the month" do
|
82
|
+
@business_day.next_day(@new_years).should == @month_begin_rolled
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should be correct for the end of the month" do
|
86
|
+
@business_day.next_day(@new_years_eve).should == @month_end_rolled
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should be correct for an actual workday" do
|
90
|
+
@business_day.next_day(@workday).should == @workday_rolled
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "for the previous convention" do
|
95
|
+
before :each do
|
96
|
+
@business_day = BusinessDay.find(:previous)
|
97
|
+
@month_begin_rolled = Time.new(2011, 12, 30)
|
98
|
+
@month_end_rolled = Time.new(2011, 12, 30)
|
99
|
+
@workday_rolled = Time.new(2011, 11, 23)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should be correct for the beginning of the month" do
|
103
|
+
@business_day.next_day(@new_years).should == @month_begin_rolled
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should be correct for the end of the month" do
|
107
|
+
@business_day.next_day(@new_years_eve).should == @month_end_rolled
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should be correct for an actual workday" do
|
111
|
+
@business_day.next_day(@workday).should == @workday_rolled
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/spec/ruby/calendar_spec.rb
CHANGED
@@ -4,7 +4,9 @@ describe Calendar do
|
|
4
4
|
def test_calendar(calendar, holidays)
|
5
5
|
holidays.each do |holiday|
|
6
6
|
year, month, day = holiday
|
7
|
-
calendar.day_off?(Time.new(year, month, day))
|
7
|
+
if calendar.day_off?(Time.new(year, month, day)) == false
|
8
|
+
raise "#{year}/#{month}/#{day} is not a holiday"
|
9
|
+
end
|
8
10
|
end
|
9
11
|
end
|
10
12
|
|
@@ -68,10 +70,7 @@ describe Calendar do
|
|
68
70
|
describe "for Japanese holidays" do
|
69
71
|
before :each do
|
70
72
|
@holidays = {
|
71
|
-
:
|
72
|
-
[2012, 1, 1], [2012, 1, 2], [2012, 1, 3], [2012, 12, 31],
|
73
|
-
[2013, 1, 1], [2013, 1, 2], [2013, 1, 3], [2013, 12, 31],
|
74
|
-
[2014, 1, 1], [2014, 1, 2], [2014, 1, 3], [2014, 12, 31]],
|
73
|
+
:new_years => [[2011, 1, 1], [2012, 1, 1], [2013, 1, 1], [2014, 1, 1]],
|
75
74
|
:seijin_no_hi => [[2011, 1, 10], [2012, 1, 9], [2013, 1, 14], [2014, 1, 13]],
|
76
75
|
:kenkoku_kinen_no_hi => [[2011, 2, 11], [2012, 2, 11], [2013, 2, 11], [2014, 2, 11]],
|
77
76
|
:shunbun => [[2011, 3, 21], [2012, 3, 20], [2013, 3, 20], [2014, 3, 21]],
|
@@ -82,16 +81,20 @@ describe Calendar do
|
|
82
81
|
[2014, 5, 3], [2014, 5, 4], [2014, 5, 5], [2014, 5, 6]],
|
83
82
|
:umi_no_hi => [[2011, 7, 18], [2012, 7, 16], [2013, 7, 15], [2014, 7, 21]],
|
84
83
|
:keiro_no_hi => [[2011, 9, 19], [2012, 9, 17], [2013, 9, 16], [2014, 9, 15]],
|
85
|
-
:
|
84
|
+
:shubun => [[2011, 9, 23], [2012, 9, 22], [2013, 9, 23], [2014, 9, 23]],
|
86
85
|
:taiiku_no_hi => [[2011, 10, 10], [2012, 10, 8], [2013, 10, 14], [2014, 10, 13]],
|
87
86
|
:bunka_no_hi => [[2011, 11, 3], [2012, 11, 3], [2013, 11, 4], [2014, 11, 3]],
|
88
87
|
:kinro_kansha_no_hi => [[2011, 11, 23], [2012, 11, 23], [2013, 11, 23], [2014, 11, 24]],
|
89
|
-
:tenno_tanjobi => [[2011, 12, 23], [2012, 12, 23], [2013, 12, 23], [2014, 12, 23]]
|
88
|
+
:tenno_tanjobi => [[2011, 12, 23], [2012, 12, 23], [2013, 12, 23], [2014, 12, 23]],
|
89
|
+
:bank_holidays => [[2011, 1, 1], [2011, 1, 2], [2011, 1, 3], [2011, 12, 31],
|
90
|
+
[2012, 1, 1], [2012, 1, 2], [2012, 1, 3], [2012, 12, 31],
|
91
|
+
[2013, 1, 1], [2013, 1, 2], [2013, 1, 3], [2013, 12, 31],
|
92
|
+
[2014, 1, 1], [2014, 1, 2], [2014, 1, 3], [2014, 12, 31]]
|
90
93
|
}
|
91
94
|
end
|
92
95
|
|
93
|
-
it "should be accurate for
|
94
|
-
test_calendar Calendar::Japan, @holidays[:
|
96
|
+
it "should be accurate for New Year's Day" do
|
97
|
+
test_calendar Calendar::Japan, @holidays[:new_years]
|
95
98
|
end
|
96
99
|
|
97
100
|
it "should be accurate for Coming of Age Day" do
|
@@ -103,7 +106,7 @@ describe Calendar do
|
|
103
106
|
end
|
104
107
|
|
105
108
|
it "should be accurate for Vernal Equinox Day" do
|
106
|
-
|
109
|
+
test_calendar Calendar::Japan, @holidays[:shunbun]
|
107
110
|
end
|
108
111
|
|
109
112
|
it "should be accurate for Showa Day" do
|
@@ -123,7 +126,7 @@ describe Calendar do
|
|
123
126
|
end
|
124
127
|
|
125
128
|
it "should be accurate for Autumnal Equinox Day" do
|
126
|
-
|
129
|
+
test_calendar Calendar::Japan, @holidays[:shubun]
|
127
130
|
end
|
128
131
|
|
129
132
|
it "should be accurate for Health and Sports Day" do
|
@@ -141,5 +144,9 @@ describe Calendar do
|
|
141
144
|
it "should be accurate for the Emperor's Birthday" do
|
142
145
|
test_calendar Calendar::Japan, @holidays[:tenno_tanjobi]
|
143
146
|
end
|
147
|
+
|
148
|
+
it "should be accurate for unofficial year-end bank holidays" do
|
149
|
+
test_calendar Calendar::Japan, @holidays[:bank_holidays]
|
150
|
+
end
|
144
151
|
end
|
145
152
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe DayCount do
|
4
|
+
before :each do
|
5
|
+
@christmas = Time.new(2011, 12, 25)
|
6
|
+
@next_february = Time.new(2012, 2, 29)
|
7
|
+
@many_mays = Time.new(2015, 5, 31)
|
8
|
+
@tolerance = 0.0000001
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "30/360" do
|
12
|
+
it "should produce a correct day count factor" do
|
13
|
+
DayCount::THIRTY_360.factor(@christmas, @next_february).should be_within(@tolerance).of 64 / 360.0
|
14
|
+
DayCount::THIRTY_360.factor(@christmas, @many_mays).should be_within(@tolerance).of 1236 / 360.0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "30E/360" do
|
19
|
+
it "should produce a correct day count factor" do
|
20
|
+
DayCount::THIRTY_E_360.factor(@christmas, @next_february).should be_within(@tolerance).of 64 / 360.0
|
21
|
+
DayCount::THIRTY_E_360.factor(@christmas, @many_mays).should be_within(@tolerance).of 1235 / 360.0
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "30E+/360" do
|
26
|
+
it "should produce a correct day count factor" do
|
27
|
+
DayCount::THIRTY_E_PLUS_360.factor(@christmas, @next_february).should be_within(@tolerance).of 64 / 360.0
|
28
|
+
DayCount::THIRTY_E_PLUS_360.factor(@christmas, @many_mays).should be_within(@tolerance).of 1236 / 360.0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "Act/360" do
|
33
|
+
it "should produce a correct day count factor" do
|
34
|
+
DayCount::ACT_360.factor(@christmas, @next_february).should be_within(@tolerance).of 66 / 360.0
|
35
|
+
DayCount::ACT_360.factor(@christmas, @many_mays).should be_within(@tolerance).of 1253 / 360.0
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "Act/365" do
|
40
|
+
it "should produce a correct day count factor" do
|
41
|
+
DayCount::ACT_365.factor(@christmas, @next_february).should be_within(@tolerance).of 66 / 365.0
|
42
|
+
DayCount::ACT_365.factor(@christmas, @many_mays).should be_within(@tolerance).of 1253 / 365.0
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "Act/Act" do
|
47
|
+
it "should produce a correct day count factor" do
|
48
|
+
DayCount::ACT_ACT.factor(@christmas, @next_february).should be_within(@tolerance).of 6 / 365.0 + 60 / 366.0
|
49
|
+
DayCount::ACT_ACT.factor(@christmas, @many_mays).should be_within(@tolerance).of 887 / 365.0 + 1
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/spec/ruby/generic_spec.rb
CHANGED
data/spec/ruby/quote_spec.rb
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe Source do
|
4
|
+
it "should have a Bloomberg source" do
|
5
|
+
Source.find(:bloomberg).should_not be_nil
|
6
|
+
Source.find(Source::BLOOMBERG).should_not be_nil
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should have a Google source" do
|
10
|
+
Source.find(:google).should_not be_nil
|
11
|
+
Source.find(Source::GOOGLE).should_not be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have a Yahoo! source" do
|
15
|
+
Source.find(:yahoo).should_not be_nil
|
16
|
+
Source.find(Source::YAHOO).should_not be_nil
|
17
|
+
end
|
18
|
+
end
|
data/tasks/benchmark.rake
CHANGED
@@ -2,14 +2,34 @@ autoload :Benchmark, "benchmark"
|
|
2
2
|
|
3
3
|
desc "Run some tests comparing native C implementations vs pure Ruby"
|
4
4
|
task :benchmark do
|
5
|
-
require "rupee"
|
6
|
-
require "rupee/benchmark"
|
7
5
|
|
8
6
|
n = 100_000
|
7
|
+
n_str = n.to_s.gsub /(\d)(?=(\d\d\d)+(?!\d))/, "\\1,"
|
8
|
+
offset = 19
|
9
9
|
|
10
|
-
puts "
|
10
|
+
puts "\nTime to load all classes and defaults"
|
11
11
|
|
12
|
-
Benchmark.bm(
|
12
|
+
Benchmark.bm(offset) do |x|
|
13
|
+
x.report "Basic:" do
|
14
|
+
require "rupee"
|
15
|
+
end
|
16
|
+
|
17
|
+
x.report "All:" do
|
18
|
+
Rupee.constants.each do |c|
|
19
|
+
const = Rupee.const_get(c)
|
20
|
+
|
21
|
+
if const.respond_to?(:constants)
|
22
|
+
const.constants.each do |sc|
|
23
|
+
const.const_get sc
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
puts "\nBlack-Scholes (#{n_str} times)"
|
31
|
+
|
32
|
+
Benchmark.bm(offset) do |x|
|
13
33
|
x.report "Rupee (class):" do
|
14
34
|
n.times do
|
15
35
|
Rupee::Option.black_scholes "c", 60, 65, 0.25, 0.08, 0, 0.3
|
@@ -49,5 +69,7 @@ task :benchmark do
|
|
49
69
|
Rupee::Benchmark.black_scholes "c", 60, 65, 0.25, 0.08, 0.3
|
50
70
|
end
|
51
71
|
end
|
72
|
+
|
73
|
+
puts
|
52
74
|
end
|
53
75
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rupee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
|
-
requirement: &
|
16
|
+
requirement: &77429770 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '1.0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *77429770
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &77429500 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '2.0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *77429500
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: sdoc
|
38
|
-
requirement: &
|
38
|
+
requirement: &77429260 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0.3'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *77429260
|
47
47
|
description: ! " rupee aims to provide user-friendly tools for
|
48
48
|
use in\n financial gems and applications.\n"
|
49
49
|
email:
|
@@ -69,6 +69,12 @@ files:
|
|
69
69
|
- ext/rupee/util.c
|
70
70
|
- lib/rupee.rb
|
71
71
|
- lib/rupee/benchmark.rb
|
72
|
+
- lib/rupee/business_day.rb
|
73
|
+
- lib/rupee/business_day/actual.rb
|
74
|
+
- lib/rupee/business_day/following.rb
|
75
|
+
- lib/rupee/business_day/modified_following.rb
|
76
|
+
- lib/rupee/business_day/modified_previous.rb
|
77
|
+
- lib/rupee/business_day/previous.rb
|
72
78
|
- lib/rupee/calendar.rb
|
73
79
|
- lib/rupee/calendar/japan.rb
|
74
80
|
- lib/rupee/calendar/us.rb
|
@@ -79,10 +85,21 @@ files:
|
|
79
85
|
- lib/rupee/currency/usd.rb
|
80
86
|
- lib/rupee/custom.rb
|
81
87
|
- lib/rupee/day_count.rb
|
88
|
+
- lib/rupee/day_count/30_360.rb
|
89
|
+
- lib/rupee/day_count/30e+_360.rb
|
90
|
+
- lib/rupee/day_count/30e_360.rb
|
91
|
+
- lib/rupee/day_count/30e_360_isda.rb
|
92
|
+
- lib/rupee/day_count/act_360.rb
|
93
|
+
- lib/rupee/day_count/act_365.rb
|
94
|
+
- lib/rupee/day_count/act_act.rb
|
95
|
+
- lib/rupee/mixins/find_instance.rb
|
82
96
|
- lib/rupee/option.rb
|
83
97
|
- lib/rupee/quote.rb
|
84
|
-
- lib/rupee/quote/source.rb
|
85
98
|
- lib/rupee/security.rb
|
99
|
+
- lib/rupee/source.rb
|
100
|
+
- lib/rupee/source/bloomberg.rb
|
101
|
+
- lib/rupee/source/google.rb
|
102
|
+
- lib/rupee/source/yahoo.rb
|
86
103
|
- lib/rupee/version.rb
|
87
104
|
- lib/rupee/yield_curve.rb
|
88
105
|
- rupee.gemspec
|
@@ -90,10 +107,13 @@ files:
|
|
90
107
|
- spec/native/future_spec.rb
|
91
108
|
- spec/native/option_spec.rb
|
92
109
|
- spec/native/statistics_spec.rb
|
110
|
+
- spec/ruby/business_day_spec.rb
|
93
111
|
- spec/ruby/calendar_spec.rb
|
94
112
|
- spec/ruby/currency_spec.rb
|
113
|
+
- spec/ruby/day_count_spec.rb
|
95
114
|
- spec/ruby/generic_spec.rb
|
96
115
|
- spec/ruby/quote_spec.rb
|
116
|
+
- spec/ruby/source_spec.rb
|
97
117
|
- spec/spec_helper.rb
|
98
118
|
- tasks/benchmark.rake
|
99
119
|
- test_rubies
|
@@ -127,7 +147,10 @@ test_files:
|
|
127
147
|
- spec/native/future_spec.rb
|
128
148
|
- spec/native/option_spec.rb
|
129
149
|
- spec/native/statistics_spec.rb
|
150
|
+
- spec/ruby/business_day_spec.rb
|
130
151
|
- spec/ruby/calendar_spec.rb
|
131
152
|
- spec/ruby/currency_spec.rb
|
153
|
+
- spec/ruby/day_count_spec.rb
|
132
154
|
- spec/ruby/generic_spec.rb
|
133
155
|
- spec/ruby/quote_spec.rb
|
156
|
+
- spec/ruby/source_spec.rb
|
data/lib/rupee/quote/source.rb
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
module Rupee
|
2
|
-
class Quote
|
3
|
-
# A class to hold quote sources
|
4
|
-
class Source
|
5
|
-
# The name of the source
|
6
|
-
attr :name
|
7
|
-
# The full URL for where the security information is located, where
|
8
|
-
# <tt>%s</tt> is a query parameter
|
9
|
-
attr :url
|
10
|
-
# The parameters available
|
11
|
-
attr :params
|
12
|
-
|
13
|
-
def initialize(name, url, params = {})
|
14
|
-
@name, @url, @params = name, url, params
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
class << self
|
19
|
-
# A holder for the sources available
|
20
|
-
attr :sources
|
21
|
-
|
22
|
-
# The default source to use when making generic requests
|
23
|
-
attr :default_source
|
24
|
-
|
25
|
-
# Build the default sources that come with Rupee
|
26
|
-
def build_sources
|
27
|
-
@sources ||= {}
|
28
|
-
|
29
|
-
# Bloomberg
|
30
|
-
@sources[:bloomberg] = Source.new(:bloomberg,
|
31
|
-
"http://www.bloomberg.com/apps/quote?ticker=%s",
|
32
|
-
:price => /(?:PRICE|VALUE): <span class="amount">([0-9.,NA-]{1,})/,
|
33
|
-
:change => /Change<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
34
|
-
:pct_chg => /Change<\/td>\n<td class="value[^>]+>[0-9.,NA-]{1,} \(([0-9NA.,-]{1,})\%/,
|
35
|
-
:date => /"date">(.*?)</,
|
36
|
-
:time => /"time">(.*?)</,
|
37
|
-
:bid => /Bid<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
38
|
-
:ask => /Ask<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
39
|
-
:open => /Open<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
40
|
-
:high => /High<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
41
|
-
:low => /Low<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
42
|
-
:volume => /Volume<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
|
43
|
-
:mkt_cap => /Market Cap[^<]+<\/td>\n<td class="value">([0-9.,NA-]{1,})/,
|
44
|
-
:p_e => /Price\/Earnings[^<]+<\/td>\n<td class="value">([0-9.,NA-]{1,})/)
|
45
|
-
|
46
|
-
# Yahoo! Finance
|
47
|
-
@sources[:yahoo] = Source.new(:yahoo,
|
48
|
-
"http://finance.yahoo.com/q?s=%s",
|
49
|
-
:price => /(?:<\/a> |"yfnc_tabledata1"><big><b>)<span id="yfs_l[19]0_[^>]+>([0-9,.-]{1,})/,
|
50
|
-
:change => /><span id="yfs_c[16]0_.*?(?:\n[\s\w\-]*\n[\s">\(]*?)?([0-9.,-]{1,})/,
|
51
|
-
:pct_chg => /yfs_p[24]0_.*?(?:\n[\s\w\-]*\n[\s">\(]*?)?([0-9.,-]{1,})%\)(?:<\/span>|<\/b><\/span><\/td>)/,
|
52
|
-
:date => /<span id="yfs_market_time">.*?, (.*?20[0-9]{1,2})/,
|
53
|
-
:time => /(?:"time"|"yfnc_tabledata1")><span id="yfs_t[51]0_[^>]+>(.*?)</,
|
54
|
-
:bid => /yfs_b00_[^>]+>([0-9,.-]{1,})/,
|
55
|
-
:ask => /yfs_a00_[^>]+>([0-9,.-]{1,})/,
|
56
|
-
:open => /Open:<\/th><td class="yfnc_tabledata1">([0-9,.-]{1,})/,
|
57
|
-
:high => /yfs_h00_[^>]+>([0-9,.-]{1,})/,
|
58
|
-
:low => /yfs_g00_[^>]+>([0-9,.-]{1,})/,
|
59
|
-
:volume => /yfs_v00_[^>]+>([0-9,.-]{1,})/,
|
60
|
-
:mkt_cap => /yfs_j10_[^>]+>([0-9,.-]{1,}[KMBT]?)/)
|
61
|
-
|
62
|
-
# Google Finance
|
63
|
-
@sources[:google] = Source.new(:google,
|
64
|
-
"http://www.google.com/ig/api?stock=%s",
|
65
|
-
:price => /<last data="([0-9.-]*)"/,
|
66
|
-
:change => /<change data="([0-9.-]*)"/,
|
67
|
-
:pct_chg => /<perc_change data="([0-9.-]*)"/,
|
68
|
-
:date => /<trade_date_utc data="([0-9.-]*)"/,
|
69
|
-
:time => /<trade_date_utc data="([0-9.-]*)"/,
|
70
|
-
:open => /<open data="([0-9.-]*)"/,
|
71
|
-
:high => /<high data="([0-9.-]*)"/,
|
72
|
-
:low => /<low data="([0-9.-]*)"/,
|
73
|
-
:volume => /<volume data="([0-9.-]*)"/,
|
74
|
-
:mkt_cap => /<market_cap data="([0-9.-]*)"/)
|
75
|
-
|
76
|
-
@default_source = :bloomberg
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
# Initialize sources
|
81
|
-
Quote.build_sources
|
82
|
-
end
|
83
|
-
end
|