rupee 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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