rupee 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|
-
[](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.
|