Permalink
Browse files

Fixed issues with Quote, got modified duration working

  • Loading branch information...
1 parent b1d2201 commit 489193f743512663ae0cccf6ca6012d6f29ecbe9 @brymck committed Sep 29, 2011
Showing with 46 additions and 32 deletions.
  1. +20 −0 ext/rupee/bond.c
  2. +3 −19 lib/rupee/quote.rb
  3. +9 −8 lib/rupee/quote/source.rb
  4. +1 −1 lib/rupee/version.rb
  5. +9 −3 spec/c/bond_spec.rb
  6. +4 −1 spec/c/future_spec.rb
View
@@ -224,6 +224,24 @@ macaulay_discrete(self, _times, _cflows, _price)
return macaulay(self, _times, _cflows, _price, true);
}
+// Modified duration of a discretely compounded bond
+static VALUE
+modified_discrete(self, _times, _cflows, _price)
+ VALUE self, _times, _cflows, _price;
+{
+ int len = RARRAY_LEN(_cflows);
+ double y, D, times[len], cflows[len], price;
+
+ rtofa(times, _times, len);
+ rtofa(cflows, _cflows, len);
+ price = NUM2DBL(_price);
+
+ y = bond_ytm(times, cflows, price, len, true);
+ D = bond_dur(times, cflows, y, len, true);
+
+ return rb_float_new(D / (1 + y));
+};
+
// Price of a continuously compounded bond
static VALUE
price_continuous(self, _times, _cflows, _r)
@@ -296,6 +314,8 @@ init_bond()
rb_define_alias(singleton, "dur", "duration");
rb_define_singleton_method(klass, "macaulay", macaulay_discrete, 3);
rb_define_alias(singleton, "macaulay_duration", "macaulay");
+ rb_define_singleton_method(klass, "modified", modified_discrete, 3);
+ rb_define_alias(singleton, "modified_duration", "modified");
rb_define_singleton_method(klass, "price", price_discrete, 3);
rb_define_alias(singleton, "value", "price");
rb_define_singleton_method(klass, "yield_to_maturity", yield_to_maturity_discrete, 3);
View
@@ -49,7 +49,7 @@ class Quote
def initialize(ticker, opts = {})
opts = { :source => :bloomberg, :frequency => 15 }.merge opts
@ticker = ticker
- @source = shorten_source(Quote.sources[opts[:source]])
+ @source = Quote.sources[opts[:source]]
@frequency = opts[:frequency]
@next_pull = Time.now
end
@@ -61,10 +61,9 @@ def get(*params)
if now >= @next_pull
@next_pull = now + @frequency
- url = BLOOMBERG_URL % ticker
@results = {}
- url = URI.parse(url)
- @html = Net::HTTP.start(url.host, url.port) do |http|
+ url = URI.parse(@source.url % ticker)
+ html = Net::HTTP.start(url.host, url.port) do |http|
http.get url.request_uri
end.body
@@ -101,21 +100,6 @@ def frequency=(x) # :nodoc:
private
- # The URL for Bloomberg's quotes service
- BLOOMBERG_URL = "http://www.bloomberg.com/apps/quote?ticker=%s"
-
- # Returns an intepretation of an abbreviated source name
- def shorten_source(source)
- case source.downcase.to_sym
- when :"", :bloomberg, :bberg, :bb, :b
- :bloomberg
- when :google, :goog, :g
- :google
- when :yahoo!, :yahoo, :yhoo, :y!, :y
- :yahoo
- end
- end
-
# Parses an object that might be a number
#
# parse "15" #=> 15
View
@@ -4,14 +4,14 @@ class Quote
class Source
# The name of the source
attr :name
-
+ # The full URL for where the security information is located, where
+ # <tt>%s</tt> is a query parameter
+ attr :url
# The parameters available
attr :params
- def initialize(name, aliases = [], params = {})
- @name = name
- @aliases = aliases
- @params = params
+ def initialize(name, url, params = {})
+ @name, @url, @params = name, url, params
end
end
@@ -27,7 +27,8 @@ def build_sources
@sources ||= {}
# Bloomberg
- @sources[:bloomberg] = Source.new(:bloomberg, [:bberg, :bb, :b],
+ @sources[:bloomberg] = Source.new(:bloomberg,
+ "http://www.bloomberg.com/apps/quote?ticker=%s",
:price => /(?:PRICE|VALUE): <span class="amount">([0-9.,NA-]{1,})/,
:change => /Change<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
:pct_change => /Change<\/td>\n<td class="value[^>]+>[0-9.,NA-]{1,} \(([0-9NA.,-]{1,})\%/,
@@ -41,8 +42,8 @@ def build_sources
:volume => /Volume<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
:mkt_cap => /Market Cap[^<]+<\/td>\n<td class="value">([0-9.,NA-]{1,})/,
:p_e => /Price\/Earnings[^<]+<\/td>\n<td class="value">([0-9.,NA-]{1,})/)
- @sources[:yahoo] = Source.new(:yahoo)
- @sources[:google] = Source.new(:google)
+ @sources[:yahoo] = Source.new(:yahoo, "yahoo.com")
+ @sources[:google] = Source.new(:google, "google.com")
@default_source = :bloomberg
end
end
View
@@ -1,4 +1,4 @@
module Rupee
# The current version
- VERSION = "0.1.5"
+ VERSION = "0.1.6"
end
View
@@ -1,7 +1,6 @@
require File.dirname(__FILE__) + "/../spec_helper"
# Discrete discounting
-# bond duration modified = 2.5128
# new bond price = 100
#Continous discounting
# bond yield to maturity = 0.09
@@ -16,20 +15,27 @@
end
describe "with discrete discounting" do
+ before :each do
+ @price = Rupee::Bond.price(@times, @cflows, @r)
+ end
+
it "should produce an accurate price" do
- Rupee::Bond.price(@times, @cflows, @r).should be_within(@tolerance).of 102.531
+ @price.should be_within(@tolerance).of 102.531
end
it "should produce an accurate duration" do
Rupee::Bond.duration(@times, @cflows, @r).should be_within(@tolerance).of 2.73895
end
+ it "should produce an accurate modified duration" do
+ Rupee::Bond.modified(@times, @cflows, @price).should be_within(@tolerance).of 2.5128
+ end
+
it "should produce an accurate convexity" do
Rupee::Bond.convexity(@times, @cflows, @r).should be_within(@tolerance).of 8.93248
end
it "should produce an accurate yield to maturity" do
- @price = Rupee::Bond.price(@times, @cflows, @r)
Rupee::Bond.yield_to_maturity(@times, @cflows, @price).should be_within(@tolerance).of @r
end
end
View
@@ -2,12 +2,15 @@
describe Rupee::Future do
before :each do
+ @tolerance = 0.001
@underlying = 100
@rfr = 0.10
@ttm = 0.5
end
it "should produce an accurate price" do
- Rupee::Future.price(@underlying, @rfr, @ttm).should be_within(TOLERANCE).of 105.127
+ Rupee::Future.price(@underlying, @rfr, @ttm).should(
+ be_within(@tolerance).of 105.127
+ )
end
end

0 comments on commit 489193f

Please sign in to comment.