rupee 0.2.5 → 0.2.6
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/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
|