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.
@@ -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, "rupee/security"
19
+ autoload :Security, "rupee/security"
20
20
 
21
- autoload :Calendar, "rupee/calendar"
22
- autoload :Custom, "rupee/custom"
23
- autoload :Call, "rupee/option"
24
- autoload :Currency, "rupee/currency"
25
- autoload :DayCount, "rupee/day_count"
26
- autoload :Option, "rupee/option"
27
- autoload :Put, "rupee/option"
28
- autoload :Quote, "rupee/quote"
29
- autoload :YieldCurve, "rupee/yield_curve"
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,8 @@
1
+ module Rupee
2
+ class BusinessDay
3
+ # Actual business day convention
4
+ ACTUAL = BusinessDay.new "Use date even if it's a non-business day" do |date, calendar|
5
+ date
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ module Rupee
2
+ class BusinessDay
3
+ # Following business day convention
4
+ FOLLOWING = BusinessDay.new "Roll to following business day" do |date, calendar|
5
+ while calendar.day_off?(date)
6
+ date += 86_400
7
+ end
8
+
9
+ date
10
+ end
11
+ end
12
+ 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
@@ -0,0 +1,12 @@
1
+ module Rupee
2
+ class BusinessDay
3
+ # Previous business day convention
4
+ PREVIOUS = BusinessDay.new "Roll to previous business day" do |date, calendar|
5
+ while calendar.day_off?(date)
6
+ date -= 86_400
7
+ end
8
+
9
+ date
10
+ end
11
+ end
12
+ end
@@ -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
@@ -29,7 +29,10 @@ module Rupee
29
29
 
30
30
  # Vernal Equinox Day
31
31
  Japan.has_day_off_on :shunbun do |date|
32
- date.month == MARCH && date.day == 20
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 # Day, respectively
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 :setsubun do |date|
58
- date.month == SEPTEMBER && next_monday_if_sunday(date, 23)
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
@@ -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
@@ -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 Calendar object
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 Currency
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
- :calendar => :us,
17
- :currency => :usd
62
+ :business_day => :modified_following,
63
+ :calendar => :us,
64
+ :currency => :usd,
65
+ :day_count => :thirty_360
18
66
  }.merge opts
19
67
 
20
- self.calendar = opts[:calendar]
21
- self.currency = opts[:currency]
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 calendar=(x) # :nodoc:
25
- @calendar = to_instance(x, Calendar)
74
+ def business_day=(business_day) # :nodoc:
75
+ @business_day = BusinessDay.find(business_day)
26
76
  end
27
77
 
28
- def currency=(x) # :nodoc:
29
- @currency = to_instance(x, Currency)
78
+ def calendar=(calendar) # :nodoc:
79
+ @calendar = Calendar.find(calendar)
30
80
  end
31
81
 
32
- private
33
-
34
- # Converts the supplied value to an instance of the specified class (if it
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
- if x.instance_of?(constant)
40
- x
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
@@ -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,8 @@
1
+ module Rupee
2
+ class DayCount
3
+ # Actual/360
4
+ ACT_360 = DayCount.new "Actual/360, typical LIBOR convention" do |from, to|
5
+ days(from, to) / 360.0
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Rupee
2
+ class DayCount
3
+ # Actual/365
4
+ ACT_365 = DayCount.new "Actual/365" do |from, to|
5
+ days(from, to) / 365.0
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ module Rupee
2
+ class DayCount
3
+ # Actual/Actual
4
+ ACT_ACT = DayCount.new "Actual/actual" do |from, to|
5
+ 1 - from.yday / days_in_year(from) +
6
+ (to.year - from.year - 1) +
7
+ to.yday / days_in_year(to)
8
+ end
9
+ end
10
+ 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
@@ -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 = Quote.sources[opts[:source]]
51
+ @source = Source.find(opts[:source])
56
52
  @frequency = opts[:frequency]
57
53
  @next_pull = Time.now
58
54
  end
@@ -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
@@ -1,4 +1,4 @@
1
1
  module Rupee
2
2
  # The current version
3
- VERSION = "0.2.5"
3
+ VERSION = "0.2.6"
4
4
  end
@@ -1,5 +1,7 @@
1
1
  module Rupee
2
2
  class YieldCurve
3
+ include FindInstance
4
+
3
5
  def initialize(description = "", opts = {})
4
6
  opts = {
5
7
  :currency => :usd,
@@ -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
@@ -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)).should_not be false
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
- :bank_holidays => [[2011, 1, 1], [2011, 1, 2], [2011, 1, 3], [2011, 12, 31],
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
- :setsubun => [[2011, 9, 23], [2012, 9, 22], [2013, 9, 23], [2014, 9, 23]],
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 year-end bank holidays" do
94
- test_calendar Calendar::Japan, @holidays[:bank_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
- # test_calendar Calendar::Japan, @holidays[:shunbun]
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
- # test_calendar Calendar::Japan, @holidays[:setsubun]
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
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + "/../spec_helper"
2
2
 
3
- describe Rupee do
3
+ describe "Autoloading" do
4
4
  it "should produce no errors when loading all constants two levels deep" do
5
5
  Rupee.constants.each do |c|
6
6
  # Get classes and constants
@@ -12,10 +12,6 @@ describe Quote do
12
12
  end
13
13
  end
14
14
 
15
- it "should automatically have a Bloomberg source" do
16
- Quote.sources.should include :bloomberg
17
- end
18
-
19
15
  describe "without any parameters specified" do
20
16
  before :each do
21
17
  @wfc = Quote.new(:wfc)
@@ -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
@@ -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 "Black-Scholes (#{n.to_s.gsub /(\d)(?=(\d\d\d)+(?!\d))/, "\\1,"} times)"
10
+ puts "\nTime to load all classes and defaults"
11
11
 
12
- Benchmark.bm(19) do |x|
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.5
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-01 00:00:00.000000000 Z
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: &79191280 !ruby/object:Gem::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: *79191280
24
+ version_requirements: *77429770
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &79191010 !ruby/object:Gem::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: *79191010
35
+ version_requirements: *77429500
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: sdoc
38
- requirement: &79190780 !ruby/object:Gem::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: *79190780
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
@@ -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