rupee 0.1.3 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +103 -0
- data/ext/rupee/bond.c +1 -1
- data/ext/rupee/distribution.c +1 -1
- data/lib/rupee/benchmark.rb +9 -5
- data/lib/rupee/option.rb +26 -9
- data/lib/rupee/quote.rb +39 -6
- data/lib/rupee/security.rb +31 -14
- data/lib/rupee/version.rb +1 -1
- data/spec/c/bond_spec.rb +9 -10
- metadata +8 -8
- data/README.md +0 -122
data/README.rdoc
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
= Rupee - financial tools for Ruby
|
2
|
+
|
3
|
+
Homepage:: http://www.brymck.com
|
4
|
+
Author:: Bryan McKelvey
|
5
|
+
Copyright:: (c) 2011 Bryan McKelvey
|
6
|
+
License:: MIT
|
7
|
+
|
8
|
+
/|\
|
9
|
+
/ | \
|
10
|
+
/ | \
|
11
|
+
/_ / \ _\ RU PE E
|
12
|
+
| | | | _ _ _
|
13
|
+
| | | | | | | | |__o ____
|
14
|
+
| | | | | | | _ | __| |____|
|
15
|
+
| _| |_ | | | |/ / | |___
|
16
|
+
\ \ / / /_/|___/ \____|
|
17
|
+
\ | /
|
18
|
+
\ | /
|
19
|
+
\|/
|
20
|
+
|
21
|
+
== Installing
|
22
|
+
|
23
|
+
Note that you must have Ruby 1.8.7+ installed and the ability to compile native
|
24
|
+
extensions (standard on most platforms and available on Windows via
|
25
|
+
{DevKit}[http://rubyinstaller.org/downloads/]).
|
26
|
+
|
27
|
+
gem install rupee
|
28
|
+
|
29
|
+
You can also do things the hard way if you want to keep track of the repo:
|
30
|
+
|
31
|
+
git clone git://github.com/brymck/rupee.git
|
32
|
+
cd rupee
|
33
|
+
bundle update
|
34
|
+
rake install
|
35
|
+
|
36
|
+
After all that hard work, you can take a test drive by running something like
|
37
|
+
this in the Ruby console (i.e. <tt>irb</tt> in a command prompt):
|
38
|
+
|
39
|
+
require "rupee"
|
40
|
+
Rupee::Option.black_scholes "c", 60, 65, 0.25, 0.08, 0, 0.3
|
41
|
+
Rupee::Call.new(
|
42
|
+
:underlying => 60,
|
43
|
+
:strike => 65,
|
44
|
+
:time => 0.25,
|
45
|
+
:rate => 0.08,
|
46
|
+
:div_yield => 0.00,
|
47
|
+
:volatility => 0.3
|
48
|
+
).black_scholes
|
49
|
+
|
50
|
+
both of which should return <tt>2.1334</tt>.
|
51
|
+
|
52
|
+
You should also be able to get the latest stock info for, for example, Wells
|
53
|
+
Fargo using the following (note that you only need to <tt>require</tt> the
|
54
|
+
<tt>quote</tt> module):
|
55
|
+
|
56
|
+
require "rupee/quote"
|
57
|
+
wfc = Rupee::Quote.new("WFC")
|
58
|
+
|
59
|
+
wfc.get :price, :change, :pct_change
|
60
|
+
#=> {:price=>24.96, :change=>0.17, :pct_change =>0.686}
|
61
|
+
|
62
|
+
wfc.price
|
63
|
+
#=> 24.96
|
64
|
+
|
65
|
+
wfc.change
|
66
|
+
#=> 0.17
|
67
|
+
|
68
|
+
<tt>wfc.get</tt> will return a hash of the requested information for the security.
|
69
|
+
Each valid parameter will also have its own utility method. The results will
|
70
|
+
update every <tt>wfc.frequency</tt> seconds (defaults to 15).
|
71
|
+
|
72
|
+
Got it? Good. This will surely help you collect some rupees in real life.
|
73
|
+
|
74
|
+
== Performance
|
75
|
+
|
76
|
+
This is just a simple benchmark I ran on my own laptop, where I value a simple
|
77
|
+
call option with Black-Scholes 100,000 times. You can test the same on yours
|
78
|
+
with rake, but in any case it makes the point that for the mathematical side
|
79
|
+
of finance a native extension has substantial benefits:
|
80
|
+
|
81
|
+
rake benchmark:black_scholes
|
82
|
+
|
83
|
+
Results:
|
84
|
+
|
85
|
+
user system total real
|
86
|
+
Rupee (class): 0.190000 0.000000 0.190000 ( 0.194001)
|
87
|
+
Rupee (one object): 0.180000 0.000000 0.180000 ( 0.183091)
|
88
|
+
Rupee (new object): 2.210000 0.000000 2.210000 ( 2.213351)
|
89
|
+
Pure Ruby: 2.320000 0.000000 2.320000 ( 2.324259)
|
90
|
+
|
91
|
+
In words, for math-intensive operations, using a C implementation is clearly
|
92
|
+
faster than the same thing in Ruby.
|
93
|
+
|
94
|
+
Also, if you're doing a valuation on a one-off set of examples (e.g. in a Monte
|
95
|
+
Carlo simulation), you probably don't want to create an object every time.
|
96
|
+
Something like <tt>Rupee::Option.black_scholes ...</tt> should work just fine.
|
97
|
+
Creating a <tt>Rupee::Option</tt> object takes roughly the same amount of time as
|
98
|
+
running <tt>Rupee::Option.black_scholes</tt> a dozen times.
|
99
|
+
|
100
|
+
However, if you're creating and reusing an object, I strongly recommend
|
101
|
+
preserving the object orientation of Ruby: the penalty for using a new instance
|
102
|
+
rather than calling the class method directly is almost entirely in the object
|
103
|
+
initialization itself.
|
data/ext/rupee/bond.c
CHANGED
@@ -295,7 +295,7 @@ init_bond()
|
|
295
295
|
rb_define_singleton_method(klass, "duration", duration_discrete, 3);
|
296
296
|
rb_define_alias(singleton, "dur", "duration");
|
297
297
|
rb_define_singleton_method(klass, "macaulay", macaulay_discrete, 3);
|
298
|
-
rb_define_alias(singleton, "
|
298
|
+
rb_define_alias(singleton, "macaulay_duration", "macaulay");
|
299
299
|
rb_define_singleton_method(klass, "price", price_discrete, 3);
|
300
300
|
rb_define_alias(singleton, "value", "price");
|
301
301
|
rb_define_singleton_method(klass, "yield_to_maturity", yield_to_maturity_discrete, 3);
|
data/ext/rupee/distribution.c
CHANGED
data/lib/rupee/benchmark.rb
CHANGED
@@ -3,7 +3,9 @@ module Rupee
|
|
3
3
|
class Benchmark
|
4
4
|
class << self
|
5
5
|
# Black-Scholes option price valuation
|
6
|
+
#
|
6
7
|
# by Michael Neumann (with some revisions)
|
8
|
+
#
|
7
9
|
# http://www.espenhaug.com/black_scholes.html
|
8
10
|
def black_scholes(callPutFlag, s, x, t, r, v)
|
9
11
|
d1 = (Math.log(s / x) + (r + v * v / 2.0) * t) / (v * Math.sqrt(t))
|
@@ -17,14 +19,16 @@ module Rupee
|
|
17
19
|
|
18
20
|
private
|
19
21
|
|
20
|
-
A1 = 0.31938153
|
21
|
-
A2 = -0.356563782
|
22
|
-
A3 = 1.781477937
|
23
|
-
A4 = -1.821255978
|
24
|
-
A5 = 1.330274429
|
22
|
+
A1 = 0.31938153 # :nodoc:
|
23
|
+
A2 = -0.356563782 # :nodoc:
|
24
|
+
A3 = 1.781477937 # :nodoc:
|
25
|
+
A4 = -1.821255978 # :nodoc:
|
26
|
+
A5 = 1.330274429 # :nodoc:
|
25
27
|
|
26
28
|
# Cumulative normal distribution
|
29
|
+
#
|
27
30
|
# by Michael Neumann (with some revisions)
|
31
|
+
#
|
28
32
|
# http://www.espenhaug.com/black_scholes.html
|
29
33
|
def cnd(x)
|
30
34
|
l = x.abs
|
data/lib/rupee/option.rb
CHANGED
@@ -2,28 +2,45 @@ require "rupee/security"
|
|
2
2
|
|
3
3
|
module Rupee
|
4
4
|
class Option < Security
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
# The type of the option. Accepts <tt>"call"</tt> and <tt>"put"</tt>.
|
6
|
+
attr_accessor :type
|
7
|
+
# The price of the underlying security
|
8
|
+
attr_accessor :underlying
|
9
|
+
# The strike price of the underlying security
|
10
|
+
attr_accessor :strike
|
11
|
+
# The remaining time to maturity
|
12
|
+
attr_accessor :time
|
13
|
+
# The risk-free rate
|
14
|
+
attr_accessor :rate
|
15
|
+
# The dividend yield
|
16
|
+
attr_accessor :div_yield
|
17
|
+
# The annualized price volatility
|
18
|
+
attr_accessor :volatility
|
19
|
+
# The price of the option
|
20
|
+
attr_accessor :price
|
21
|
+
|
22
|
+
attr_alias :rfr, :rate
|
23
|
+
attr_alias :risk_free_rate, :rate
|
24
|
+
attr_alias :value, :price
|
12
25
|
|
13
26
|
def black_scholes
|
14
27
|
@value = self.class.black_scholes @type.to_s, @underlying, @strike, @time, @rate, @div_yield, @volatility
|
15
28
|
end
|
16
29
|
end
|
17
30
|
|
31
|
+
# The same thing as Rupee::Option, but with the <tt>:type</tt> flag set to
|
32
|
+
# <tt>"call"</tt>
|
18
33
|
class Call < Option
|
19
|
-
def initialize(opts = {})
|
34
|
+
def initialize(opts = {}) # :nodoc:
|
20
35
|
@type = "call"
|
21
36
|
super
|
22
37
|
end
|
23
38
|
end
|
24
39
|
|
40
|
+
# The same thing as Rupee::Option, but with the <tt>:type</tt> flag set to
|
41
|
+
# <tt>"put"</tt>
|
25
42
|
class Put < Option
|
26
|
-
def initialize(opts = {})
|
43
|
+
def initialize(opts = {}) # :nodoc:
|
27
44
|
@type = :put
|
28
45
|
super
|
29
46
|
end
|
data/lib/rupee/quote.rb
CHANGED
@@ -3,7 +3,23 @@ autoload :Net, "net/http"
|
|
3
3
|
autoload :URI, "uri"
|
4
4
|
|
5
5
|
module Rupee
|
6
|
-
#
|
6
|
+
# An object representing a security quote from an online source. With a
|
7
|
+
# Rupee::Quote object, you can retrieve the most recent information on a
|
8
|
+
# particular security using either the <tt>get</tt> method or the helper
|
9
|
+
# methods for each parameter (e.g. <tt>price</tt>, <tt>change</tt>):
|
10
|
+
#
|
11
|
+
# require "rupee/quote"
|
12
|
+
#
|
13
|
+
# wfc = Rupee::Quote.new("WFC")
|
14
|
+
#
|
15
|
+
# wfc.get :price, :change, :pct_change
|
16
|
+
# #=> {:price=>24.96, :change=>0.17, :pct_change =>0.686}
|
17
|
+
#
|
18
|
+
# wfc.price
|
19
|
+
# #=> 24.96
|
20
|
+
#
|
21
|
+
# wfc.change
|
22
|
+
# #=> 0.17
|
7
23
|
class Quote
|
8
24
|
# A ticker symbol
|
9
25
|
attr_accessor :ticker
|
@@ -11,19 +27,29 @@ module Rupee
|
|
11
27
|
# The name of the quote source
|
12
28
|
attr_accessor :source
|
13
29
|
|
14
|
-
# The cached HTML
|
15
|
-
attr :html
|
16
|
-
|
17
30
|
# The frequency in seconds that a quote's information should be updated
|
18
31
|
attr_accessor :frequency
|
19
32
|
|
20
33
|
# The time at which the next pull from the online quote source will occur
|
21
34
|
attr :next_pull
|
22
35
|
|
36
|
+
# Creates a new Rupee::Quote object.
|
37
|
+
#
|
38
|
+
# wfc = Rupee::Quote.new("WFC")
|
39
|
+
#
|
40
|
+
# is equivalent to
|
41
|
+
#
|
42
|
+
# wfc = Rupee::Quote.new("WFC", :source => :bloomberg, :frequency => 15)
|
43
|
+
#
|
44
|
+
# Configuration options
|
45
|
+
#
|
46
|
+
# * <tt>:source</tt> - The name of the source (default is +:bloomberg+).
|
47
|
+
# * <tt>:frequency</tt> - How often the quote will seek new values from the
|
48
|
+
# quote source, in seconds (default is +15+).
|
23
49
|
def initialize(ticker, opts = {})
|
24
50
|
opts = { :source => :bloomberg, :frequency => 15 }.merge opts
|
25
51
|
@ticker = ticker
|
26
|
-
@source = Quote.sources[opts[:source]]
|
52
|
+
@source = shorten_source(Quote.sources[opts[:source]])
|
27
53
|
@frequency = opts[:frequency]
|
28
54
|
@next_pull = Time.now
|
29
55
|
end
|
@@ -58,6 +84,9 @@ module Rupee
|
|
58
84
|
end
|
59
85
|
end
|
60
86
|
|
87
|
+
# call-seq: Rupee#price
|
88
|
+
#
|
89
|
+
# Blah
|
61
90
|
[:price, :change, :pct_change, :date, :time, :bid, :ask, :open, :high,
|
62
91
|
:low, :volume, :mkt_cap, :p_e].each do |method_name|
|
63
92
|
define_method method_name do
|
@@ -65,7 +94,7 @@ module Rupee
|
|
65
94
|
end
|
66
95
|
end
|
67
96
|
|
68
|
-
def frequency=(x)
|
97
|
+
def frequency=(x) # :nodoc:
|
69
98
|
@next_pull += (x - @frequency)
|
70
99
|
@frequency = x
|
71
100
|
end
|
@@ -87,6 +116,10 @@ module Rupee
|
|
87
116
|
end
|
88
117
|
end
|
89
118
|
|
119
|
+
# Parses an object that might be a number
|
120
|
+
#
|
121
|
+
# parse "15" #=> 15
|
122
|
+
# parse "abc" #=> "abc"
|
90
123
|
def parse(result)
|
91
124
|
begin
|
92
125
|
Float(result)
|
data/lib/rupee/security.rb
CHANGED
@@ -4,26 +4,26 @@ module Rupee
|
|
4
4
|
# Automatically sets all arguments passed to <tt>initialize</tt> to
|
5
5
|
# instance variables if they exist (think Rails mass assignment).
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# require "rupee"
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
9
|
+
# call = Rupee::Call.new(
|
10
|
+
# :underlying => 60,
|
11
|
+
# :strike => 65,
|
12
|
+
# :time => 0.25,
|
13
|
+
# :rate => 0.08,
|
14
|
+
# :div_yield => 0.00,
|
15
|
+
# :volatility => 0.3
|
16
|
+
# )
|
17
|
+
# puts call.black_scholes
|
18
|
+
# #=> 2.1333718619275794
|
19
19
|
#
|
20
20
|
# You still have the option of avoiding the creation of an object (and the
|
21
21
|
# overhead it entails) by using the class methods directly:
|
22
22
|
#
|
23
|
-
#
|
23
|
+
# require "rupee"
|
24
24
|
#
|
25
|
-
#
|
26
|
-
#
|
25
|
+
# puts Rupee::Option.black_scholes "c", 60, 65, 0.25, 0.08, 0, 0.3
|
26
|
+
# #=> 2.1333718619275794
|
27
27
|
def initialize(opts = {})
|
28
28
|
opts.each do |key, value|
|
29
29
|
writer = key.to_s.+("=").to_sym
|
@@ -32,5 +32,22 @@ module Rupee
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
class << self
|
37
|
+
# Aliases an attribute, taking into account both its getter and setter
|
38
|
+
# methods. For example,
|
39
|
+
#
|
40
|
+
# attr_accessor :price
|
41
|
+
# attr_alias :value, :price
|
42
|
+
#
|
43
|
+
# would add both a <tt>value</tt> and a <tt>value=</tt> that are equivalent
|
44
|
+
# to their <tt>price</tt> counterparts.
|
45
|
+
def attr_alias(new_read, old_read)
|
46
|
+
alias_method(new_read, old_read) if method_defined?(old_read)
|
47
|
+
new_write = "#{new_read}="
|
48
|
+
old_write = "#{old_read}="
|
49
|
+
alias_method(new_write, old_write) if method_defined?(old_write)
|
50
|
+
end
|
51
|
+
end
|
35
52
|
end
|
36
53
|
end
|
data/lib/rupee/version.rb
CHANGED
data/spec/c/bond_spec.rb
CHANGED
@@ -7,10 +7,9 @@ require File.dirname(__FILE__) + "/../spec_helper"
|
|
7
7
|
# bond yield to maturity = 0.09
|
8
8
|
# new bond price = 104.282
|
9
9
|
|
10
|
-
TOLERANCE = 0.001
|
11
|
-
|
12
10
|
describe Rupee::Bond do
|
13
11
|
before :each do
|
12
|
+
@tolerance = 0.001
|
14
13
|
@times = [1, 2, 3]
|
15
14
|
@cflows = [10, 10, 110]
|
16
15
|
@r = 0.09
|
@@ -18,39 +17,39 @@ describe Rupee::Bond do
|
|
18
17
|
|
19
18
|
describe "with discrete discounting" do
|
20
19
|
it "should produce an accurate price" do
|
21
|
-
Rupee::Bond.price(@times, @cflows, @r).should be_within(
|
20
|
+
Rupee::Bond.price(@times, @cflows, @r).should be_within(@tolerance).of 102.531
|
22
21
|
end
|
23
22
|
|
24
23
|
it "should produce an accurate duration" do
|
25
|
-
Rupee::Bond.duration(@times, @cflows, @r).should be_within(
|
24
|
+
Rupee::Bond.duration(@times, @cflows, @r).should be_within(@tolerance).of 2.73895
|
26
25
|
end
|
27
26
|
|
28
27
|
it "should produce an accurate convexity" do
|
29
|
-
Rupee::Bond.convexity(@times, @cflows, @r).should be_within(
|
28
|
+
Rupee::Bond.convexity(@times, @cflows, @r).should be_within(@tolerance).of 8.93248
|
30
29
|
end
|
31
30
|
|
32
31
|
it "should produce an accurate yield to maturity" do
|
33
32
|
@price = Rupee::Bond.price(@times, @cflows, @r)
|
34
|
-
Rupee::Bond.yield_to_maturity(@times, @cflows, @price).should be_within(
|
33
|
+
Rupee::Bond.yield_to_maturity(@times, @cflows, @price).should be_within(@tolerance).of @r
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
37
|
describe "with continuous discounting" do
|
39
38
|
it "should produce an accurate price" do
|
40
|
-
Rupee::Bond.continuous_price(@times, @cflows, @r).should be_within(
|
39
|
+
Rupee::Bond.continuous_price(@times, @cflows, @r).should be_within(@tolerance).of 101.464
|
41
40
|
end
|
42
41
|
|
43
42
|
it "should produce an accurate duration" do
|
44
|
-
Rupee::Bond.continuous_duration(@times, @cflows, @r).should be_within(
|
43
|
+
Rupee::Bond.continuous_duration(@times, @cflows, @r).should be_within(@tolerance).of 2.73753
|
45
44
|
end
|
46
45
|
|
47
46
|
it "should produce an accurate convexity" do
|
48
|
-
Rupee::Bond.continuous_convexity(@times, @cflows, @r).should be_within(
|
47
|
+
Rupee::Bond.continuous_convexity(@times, @cflows, @r).should be_within(@tolerance).of 7.86779
|
49
48
|
end
|
50
49
|
|
51
50
|
it "should produce an accurate yield to maturity" do
|
52
51
|
@price = Rupee::Bond.continuous_price(@times, @cflows, @r)
|
53
|
-
Rupee::Bond.continuous_yield_to_maturity(@times, @cflows, @price).should be_within(
|
52
|
+
Rupee::Bond.continuous_yield_to_maturity(@times, @cflows, @price).should be_within(@tolerance).of @r
|
54
53
|
end
|
55
54
|
end
|
56
55
|
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.1.
|
4
|
+
version: 0.1.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2011-09-28 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
|
-
requirement: &
|
16
|
+
requirement: &84129210 !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: *84129210
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &84128900 !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: *84128900
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: autotest
|
38
|
-
requirement: &
|
38
|
+
requirement: &84127610 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '4.0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *84127610
|
47
47
|
description: rupee aims to provide user-friendly tools for use in financial gems and
|
48
48
|
applications.
|
49
49
|
email:
|
@@ -58,7 +58,7 @@ files:
|
|
58
58
|
- .rspec
|
59
59
|
- COPYING
|
60
60
|
- Gemfile
|
61
|
-
- README.
|
61
|
+
- README.rdoc
|
62
62
|
- Rakefile
|
63
63
|
- ext/rupee/bond.c
|
64
64
|
- ext/rupee/distribution.c
|
data/README.md
DELETED
@@ -1,122 +0,0 @@
|
|
1
|
-
Rupee - financial tools for Ruby
|
2
|
-
================================
|
3
|
-
|
4
|
-
<table>
|
5
|
-
<tr>
|
6
|
-
<td>Homepage</td>
|
7
|
-
<td><a href="http://www.brymck.com">www.brymck.com</a></td>
|
8
|
-
</tr>
|
9
|
-
<tr>
|
10
|
-
<td>Author</td>
|
11
|
-
<td>Bryan McKelvey</td>
|
12
|
-
</tr>
|
13
|
-
<tr>
|
14
|
-
<td>Copyright</td>
|
15
|
-
<td>(c) 2011 Bryan McKelvey</td>
|
16
|
-
</tr>
|
17
|
-
<tr>
|
18
|
-
<td>License</td>
|
19
|
-
<td>MIT</td>
|
20
|
-
</tr>
|
21
|
-
</table>
|
22
|
-
|
23
|
-
[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=brymck&url=https://github.com/brymck/rupee&title=rupee&language=en_GB&tags=github&category=software)
|
24
|
-
|
25
|
-
/|\
|
26
|
-
/ | \
|
27
|
-
/ | \
|
28
|
-
/_ / \ _\ RU PE E
|
29
|
-
| | | | _ _ _
|
30
|
-
| | | | | | | | |__o ____
|
31
|
-
| | | | | | | _ | __| |____|
|
32
|
-
| _| |_ | | | |/ / | |___
|
33
|
-
\ \ / / /_/|___/ \____|
|
34
|
-
\ | /
|
35
|
-
\ | /
|
36
|
-
\|/ brymck
|
37
|
-
|
38
|
-
Installing
|
39
|
-
----------
|
40
|
-
|
41
|
-
Note that you must have Ruby 1.8.7+ installed and the ability to compile native
|
42
|
-
extensions (standard on most platforms and available on Windows via
|
43
|
-
[DevKit](http://rubyinstaller.org/downloads/)).
|
44
|
-
|
45
|
-
gem install rupee
|
46
|
-
|
47
|
-
You can also do things the hard way if you want to keep track of the repo:
|
48
|
-
|
49
|
-
git clone git://github.com/brymck/rupee.git
|
50
|
-
cd rupee
|
51
|
-
bundle update
|
52
|
-
rake install
|
53
|
-
|
54
|
-
After all that hard work, you can take a test drive by running something like
|
55
|
-
this in the Ruby console (i.e. `irb` in a command prompt):
|
56
|
-
|
57
|
-
require "rupee"
|
58
|
-
Rupee::Option.black_scholes "c", 60, 65, 0.25, 0.08, 0, 0.3
|
59
|
-
Rupee::Call.new(
|
60
|
-
:underlying => 60,
|
61
|
-
:strike => 65,
|
62
|
-
:time => 0.25,
|
63
|
-
:rate => 0.08,
|
64
|
-
:div_yield => 0.00,
|
65
|
-
:volatility => 0.3
|
66
|
-
).black_scholes
|
67
|
-
|
68
|
-
both of which should return `2.1334`.
|
69
|
-
|
70
|
-
You should also be able to get the latest stock info for, for example, Wells
|
71
|
-
Fargo using the following (note that you only need to `require` the `quote`
|
72
|
-
module):
|
73
|
-
|
74
|
-
require "rupee/quote"
|
75
|
-
wfc = Rupee::Quote.new("WFC")
|
76
|
-
|
77
|
-
wfc.get :price, :change, :pct_change
|
78
|
-
#=> {:price=>24.96, :change=>0.17, :pct_change =>0.686}
|
79
|
-
|
80
|
-
wfc.price
|
81
|
-
#=> 24.96
|
82
|
-
|
83
|
-
wfc.change
|
84
|
-
#=> 0.17
|
85
|
-
|
86
|
-
`wfc.get` will return a hash of the requested information for the security.
|
87
|
-
Each valid parameter will also have its own utility method. The results will
|
88
|
-
update every `wfc.frequency` seconds (defaults to 15).
|
89
|
-
|
90
|
-
Got it? Good. This will surely help you collect some rupees in real life.
|
91
|
-
|
92
|
-
Performance
|
93
|
-
-----------
|
94
|
-
|
95
|
-
This is just a simple benchmark I ran on my own laptop, where I value a simple
|
96
|
-
call option with Black-Scholes 100,000 times. You can test the same on yours
|
97
|
-
with rake, but in any case it makes the point that for the mathematical side
|
98
|
-
of finance a native extension has substantial benefits:
|
99
|
-
|
100
|
-
rake benchmark:black_scholes
|
101
|
-
|
102
|
-
Results:
|
103
|
-
|
104
|
-
user system total real
|
105
|
-
Rupee (class): 0.190000 0.000000 0.190000 ( 0.194001)
|
106
|
-
Rupee (one object): 0.180000 0.000000 0.180000 ( 0.183091)
|
107
|
-
Rupee (new object): 2.210000 0.000000 2.210000 ( 2.213351)
|
108
|
-
Pure Ruby: 2.320000 0.000000 2.320000 ( 2.324259)
|
109
|
-
|
110
|
-
In words, for math-intensive operations, using a C implementation is clearly
|
111
|
-
faster than the same thing in Ruby.
|
112
|
-
|
113
|
-
Also, if you're doing a valuation on a one-off set of examples (e.g. in a Monte
|
114
|
-
Carlo simulation), you probably don't want to create an object every time.
|
115
|
-
Something like `Rupee::Option.black_scholes ...` should work just fine.
|
116
|
-
Creating a `Rupee::Option` object takes roughly the same amount of time as
|
117
|
-
running `Rupee::Option.black_scholes` a dozen times.
|
118
|
-
|
119
|
-
However, if you're creating and reusing an object, I strongly recommend
|
120
|
-
preserving the object orientation of Ruby: the penalty for using a new instance
|
121
|
-
rather than calling the class method directly is almost entirely in the object
|
122
|
-
initialization itself.
|