rupee 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ # ~.autotest
2
+ require 'rubygems'
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ *.log
3
+ .bundle
4
+ .DS_Store
5
+ Gemfile.lock
6
+ doc
7
+ pkg
8
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/COPYING ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Bryan McKelvey
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in gold_rupee.gemspec
4
+ gemspec
@@ -0,0 +1,85 @@
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
+
60
+ which should return `2.1334`.
61
+
62
+ You should also be able to get the latest stock info for, for example, Wells
63
+ Fargo using the following (note that you only need to `require` the `quote`
64
+ module):
65
+
66
+ require "rupee/quote"
67
+ Rupee::Quote.bloomberg "WFC", :price, :change, :pct_change
68
+
69
+ Got it? Good. This will surely help you collect some rupees in real life.
70
+
71
+ Performance
72
+ -----------
73
+
74
+ This is just a simple benchmark I ran on my own laptop, where I value a simple
75
+ call option with Black-Scholes one million times. You can test the same on
76
+ yours with rake, but in any case it makes the point that for the mathematical
77
+ side of finance a native extension has substantial benefits:
78
+
79
+ rake benchmark:black_scholes
80
+
81
+ Results:
82
+
83
+ user system total real
84
+ rupee: 1.388000 0.000000 1.388000 ( 1.440000)
85
+ pure ruby: 18.580000 0.000000 18.580000 ( 18.817000)
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ Dir["tasks/**/*.rake"].each do |rake|
4
+ load File.expand_path(rake)
5
+ end
@@ -0,0 +1,97 @@
1
+ #include "rupee.h"
2
+
3
+ double
4
+ bond_price(cf_times, cfs, r, len)
5
+ double *cf_times, *cfs, r;
6
+ int len;
7
+ {
8
+ double p, *cft;
9
+ int i;
10
+
11
+ p = 0;
12
+
13
+ for (i = 0; i < len; i++)
14
+ p += exp(-r * cf_times[i]) * cfs[i];
15
+
16
+ return p;
17
+ };
18
+
19
+ static VALUE
20
+ price(self, rcf_times, rcfs, rr)
21
+ VALUE self, rcf_times, rcfs, rr;
22
+ {
23
+ int len = RARRAY_LEN(rcfs);
24
+ double r, cf_times[len], cfs[len];
25
+
26
+ r = NUM2DBL(rr);
27
+ rtofa(cf_times, rcf_times, len);
28
+ rtofa(cfs, rcfs, len);
29
+
30
+ return rb_float_new(bond_price(cf_times, cfs, r, len));
31
+ }
32
+
33
+ static VALUE
34
+ convexity(self, rcf_times, rcfs, rr)
35
+ VALUE self, rcf_times, rcfs, rr;
36
+ {
37
+ int len = RARRAY_LEN(rcfs);
38
+ double r, C, B, cf_times[len], cfs[len];
39
+ int i;
40
+
41
+ rtofa(cf_times, rcf_times, len);
42
+ rtofa(cfs, rcfs, len);
43
+ r = NUM2DBL(rr);
44
+ C = 0;
45
+
46
+ for (i = 0; i < len; i++)
47
+ C += cfs[i] * pow(cf_times[i], 2) * exp(-r * cf_times[i]);
48
+
49
+ B = bond_price(cf_times, cfs, r, len);
50
+
51
+ return rb_float_new(C / B);
52
+ };
53
+
54
+ static VALUE
55
+ duration(self, rcf_times, rcfs, rr)
56
+ VALUE self, rcf_times, rcfs, rr;
57
+ {
58
+ double r, S, D1;
59
+ int i, cfs_len;
60
+
61
+ VALUE *cf_times = RARRAY_PTR(rcf_times);
62
+ VALUE *cfs = RARRAY_PTR(rcfs);
63
+ cfs_len = RARRAY_LEN(rcfs);
64
+ r = NUM2DBL(rr);
65
+ S = 0;
66
+ D1 = 0;
67
+
68
+ for (i = 0; i < cfs_len; i++) {
69
+ double cfti, cfi, dcfi;
70
+
71
+ cfti = NUM2DBL(cf_times[i]);
72
+ cfi = NUM2DBL(cfs[i]);
73
+ dcfi = cfi * exp(-r * cfti);
74
+
75
+ S += dcfi;
76
+ D1 += cfti * dcfi;
77
+ }
78
+
79
+ return rb_float_new(D1 / S);
80
+ }
81
+
82
+ void
83
+ init_bond()
84
+ {
85
+ VALUE klass, singleton;
86
+
87
+ #if 0
88
+ value module = rb_define_module("rupee");
89
+ #endif
90
+
91
+ klass = rb_define_class_under(module, "Bond", rb_cObject);
92
+ singleton = rb_singleton_class(klass);
93
+
94
+ rb_define_singleton_method(klass, "convexity", convexity, 3);
95
+ rb_define_singleton_method(klass, "duration", duration, 3);
96
+ rb_define_singleton_method(klass, "price", price, 3);
97
+ }
@@ -0,0 +1,18 @@
1
+ #include "rupee.h"
2
+
3
+ double *
4
+ rtofa(dest, src, len)
5
+ double *dest;
6
+ VALUE src;
7
+ int len;
8
+ {
9
+ int i;
10
+ VALUE *ary;
11
+
12
+ ary = RARRAY_PTR(src);
13
+
14
+ for (i = 0; i < len; i++)
15
+ dest[i] = NUM2DBL(ary[i]);
16
+
17
+ return dest;
18
+ }
@@ -0,0 +1,63 @@
1
+ #include "rupee.h"
2
+
3
+ #define PI 3.1415926536
4
+
5
+ double
6
+ cnd(z)
7
+ double z;
8
+ {
9
+ double L, K, dCND;
10
+ static const double b = 0.2316419;
11
+ static const double a1 = 0.31938153;
12
+ static const double a2 = 0.356563782;
13
+ static const double a3 = 1.781477937;
14
+ static const double a4 = 1.821255978;
15
+ static const double a5 = 1.330274429;
16
+
17
+ L = fabs(z);
18
+ K = 1.0 / (1.0 + b * L);
19
+
20
+ dCND = 1.0 - 1.0 / sqrt(2.0 * PI) *
21
+ exp(-L * L / 2.0) *
22
+ (a1 * K -
23
+ a2 * K * K +
24
+ a3 * K * K * K -
25
+ a4 * K * K * K * K +
26
+ a5 * K * K * K * K * K);
27
+
28
+ if (z < 0.0)
29
+ return 1.0 - dCND;
30
+ else
31
+ return dCND;
32
+ }
33
+
34
+ /* call-seq: cnd(z)
35
+ *
36
+ * Returns the standard normal cumulative distribution (has a mean of zero and
37
+ * a standard deviation of one).
38
+ *
39
+ * ==== Arguments
40
+ *
41
+ * * +z+ - The value for which you want the distribution
42
+ */
43
+ static VALUE
44
+ rupee_cnd(self, rz)
45
+ VALUE self, rz;
46
+ {
47
+ return rb_float_new(cnd(NUM2DBL(rz)));
48
+ }
49
+
50
+ void
51
+ init_distribution()
52
+ {
53
+ VALUE klass, singleton;
54
+
55
+ #if 0
56
+ value module = rb_define_module("rupee");
57
+ #endif
58
+
59
+ klass = rb_define_class_under(module, "Distribution", rb_cObject);
60
+ singleton = rb_singleton_class(klass);
61
+
62
+ rb_define_singleton_method(klass, "cnd", rupee_cnd, 1);
63
+ }
@@ -0,0 +1,3 @@
1
+ require "mkmf"
2
+
3
+ create_makefile "rupee/rupee"
@@ -0,0 +1,178 @@
1
+ #include "rupee.h"
2
+
3
+ /* Whether a particular string refers to a call option (false for put) */
4
+ static bool
5
+ is_call(call_put_flag)
6
+ const char *call_put_flag;
7
+ {
8
+ /* Returns true for everything unless it starts with a 'p' */
9
+ return (call_put_flag[0] != 'p');
10
+ }
11
+
12
+ static double
13
+ bs(call_put_flag, S, X, T, r, q, v)
14
+ const char *call_put_flag;
15
+ double S, X, T, r, q, v;
16
+ {
17
+ double d1, d2;
18
+
19
+ d1 = (log(S / X) + (r - q + v * v / 2) * T) / (v * sqrt(T));
20
+ d2 = d1 - v * sqrt(T);
21
+
22
+ if (is_call(call_put_flag))
23
+ return S * exp(-q * T) * cnd(d1) - X * exp(-r * T) * cnd(d2);
24
+ else
25
+ return X * exp(-r * T) * cnd(-d2) - S * exp(-q * T) * cnd(-d1);
26
+ }
27
+
28
+ /* call-seq:
29
+ * black_scholes(call_put_flag, forward, strike_price, time_to_expiry,
30
+ * risk_free_rate, volatility)
31
+ *
32
+ * The Black-Scholes European call/put valuation
33
+ *
34
+ * ==== Arguments
35
+ *
36
+ * * +call_put_flag+ - Whether the instrument is a call (c) or a put (p)
37
+ * * +forward+ - The current forward value
38
+ * * +strike_price+ - The option's strike price
39
+ * * +time_to_expiry+ - The time to maturity in years
40
+ * * +risk_free_rate+ - The risk-free rate through expiry
41
+ * * +dividend_yield+ - The annual dividend yield
42
+ * * +volatility+ - The implied volatility at expiry
43
+ */
44
+ static VALUE
45
+ rupee_black_scholes(self, rcall_put_flag, rF, rX, rT, rr, rq, rv)
46
+ VALUE self, rcall_put_flag, rF, rX, rT, rr, rq, rv;
47
+ {
48
+ const char *call_put_flag;
49
+ double F, X, T, r, q, v;
50
+
51
+ call_put_flag = StringValuePtr(rcall_put_flag);
52
+ F = NUM2DBL(rF);
53
+ X = NUM2DBL(rX);
54
+ T = NUM2DBL(rT);
55
+ r = NUM2DBL(rr);
56
+ q = NUM2DBL(rq);
57
+ v = NUM2DBL(rv);
58
+
59
+ return rb_float_new(bs(call_put_flag, F, X, T, r, q, v));
60
+ }
61
+
62
+ double
63
+ gbs(call_put_flag, S, X, T, r, b, v)
64
+ const char *call_put_flag;
65
+ double S, X, T, r, b, v;
66
+ {
67
+ double d1, d2;
68
+
69
+ d1 = (log(S / X) + (b + v * v / 2) * T) / (v * sqrt(T));
70
+ d2 = d1 - v * sqrt(T);
71
+
72
+ if (is_call(call_put_flag))
73
+ return S * exp((b - r) * T) * cnd(d1) - X * exp(-r * T) * cnd(d2);
74
+ else
75
+ return X * exp(-r * T) * cnd(-d2) - S * exp((b - r) * T) * cnd(-d1);
76
+ }
77
+
78
+ /* call-seq:
79
+ * generalized_black_scholes(call_put_flag, forward, strike_price,
80
+ * time_to_expiry, risk_free_rate, volatility)
81
+ *
82
+ * The generalized Black-Scholes European call/put valuation
83
+ *
84
+ * ==== Arguments
85
+ *
86
+ * * +call_put_flag+ - Whether the instrument is a call (c) or a put (p)
87
+ * * +forward+ - The current forward value
88
+ * * +strike_price+ - The option's strike price
89
+ * * +time_to_expiry+ - The time to maturity in years
90
+ * * +risk_free_rate+ - The risk-free rate through expiry
91
+ * * +cost_of_carry+ - The annualized cost of carry
92
+ * * +volatility+ - The implied volatility at expiry
93
+ */
94
+ static VALUE
95
+ rupee_generalized_black_scholes(self, rcall_put_flag, rF, rX, rT, rr, rb, rv)
96
+ VALUE self, rcall_put_flag, rF, rX, rT, rr, rb, rv;
97
+ {
98
+ const char *call_put_flag;
99
+ double F, X, T, r, b, v;
100
+
101
+ call_put_flag = StringValuePtr(rcall_put_flag);
102
+ F = NUM2DBL(rF);
103
+ X = NUM2DBL(rX);
104
+ T = NUM2DBL(rT);
105
+ r = NUM2DBL(rr);
106
+ b = NUM2DBL(rb);
107
+ v = NUM2DBL(rv);
108
+
109
+ return rb_float_new(gbs(call_put_flag, F, X, T, r, b, v));
110
+ }
111
+
112
+ static double
113
+ black76(call_put_flag, F, X, T, r, v)
114
+ const char *call_put_flag;
115
+ double F, X, T, r, v;
116
+ {
117
+ double d1, d2;
118
+
119
+ d1 = (log(F / X) + (v * v / 2.0) * T) / (v * sqrt(T));
120
+ d2 = d1 - v * sqrt(T);
121
+
122
+ if (is_call(call_put_flag))
123
+ return exp(-r * T) * (F * cnd(d1) - X * cnd(d2));
124
+ else
125
+ return exp(-r * T) * (X * cnd(-d2) - F * cnd(-d1));
126
+ }
127
+
128
+ /* call-seq:
129
+ * black76(call_put_flag, forward, strike_price, time_to_expiry,
130
+ * risk_free_rate, volatility)
131
+ *
132
+ * The Black-76 valuation for options on futures and forwards
133
+ *
134
+ * ==== Arguments
135
+ *
136
+ * * +call_put_flag+ - Whether the instrument is a call (c) or a put (p)
137
+ * * +forward+ - The current forward value
138
+ * * +strike_price+ - The option's strike price
139
+ * * +time_to_expiry+ - The time to maturity in years
140
+ * * +risk_free_rate+ - The risk-free rate through expiry
141
+ * * +volatility+ - The implied volatility at expiry
142
+ */
143
+ static VALUE
144
+ rupee_black76(self, rcall_put_flag, rF, rX, rT, rr, rv)
145
+ VALUE self, rcall_put_flag, rF, rX, rT, rr, rv;
146
+ {
147
+ const char *call_put_flag;
148
+ double F, X, T, r, v;
149
+
150
+ call_put_flag = StringValuePtr(rcall_put_flag);
151
+ F = NUM2DBL(rF);
152
+ X = NUM2DBL(rX);
153
+ T = NUM2DBL(rT);
154
+ r = NUM2DBL(rr);
155
+ v = NUM2DBL(rv);
156
+
157
+ return rb_float_new(black76(call_put_flag, F, X, T, r, v));
158
+ }
159
+
160
+ void
161
+ init_option()
162
+ {
163
+ VALUE klass, singleton;
164
+
165
+ #if 0
166
+ VALUE module = rb_define_module("Rupee");
167
+ #endif
168
+
169
+ klass = rb_define_class_under(module, "Option", rb_cObject);
170
+ singleton = rb_singleton_class(klass);
171
+
172
+ rb_define_singleton_method(klass, "black_scholes", rupee_black_scholes, 7);
173
+ rb_define_alias(singleton, "bs", "black_scholes");
174
+ rb_define_singleton_method(klass, "generalized_black_scholes",
175
+ rupee_generalized_black_scholes, 7);
176
+ rb_define_alias(singleton, "gbs", "generalized_black_scholes");
177
+ rb_define_singleton_method(klass, "black76", rupee_black76, 6);
178
+ }
@@ -0,0 +1,13 @@
1
+ #include "rupee.h"
2
+
3
+ VALUE module;
4
+
5
+ void
6
+ Init_rupee()
7
+ {
8
+ module = rb_define_module("Rupee");
9
+
10
+ init_distribution();
11
+ init_option();
12
+ init_bond();
13
+ }
@@ -0,0 +1,27 @@
1
+ #ifndef RUPEE
2
+ #define RUPEE
3
+
4
+ #include <ruby.h>
5
+ #include <math.h>
6
+ #include <string.h>
7
+ #include <stdbool.h>
8
+
9
+ extern VALUE module;
10
+
11
+ /* Conversion */
12
+ double *rtofa(double *dest, VALUE src, int len);
13
+
14
+ /* Statistics */
15
+ double cnd(double);
16
+ void init_distribution();
17
+
18
+ /* Options */
19
+ double gbs(const char *call_put_flag, double S, double X, double T, double r,
20
+ double b, double v);
21
+ void init_option();
22
+
23
+ /* Bonds */
24
+ double bond_price(double *cf_times, double *cfs, double r, int len);
25
+ void init_bond();
26
+
27
+ #endif
@@ -0,0 +1,20 @@
1
+ require "rupee/rupee" # keep this as the first require
2
+ require "rupee/version"
3
+
4
+ # Rupee aims to provide user-friendly tools for use in financial applications.
5
+ # The gem is in its development stages, but it currently offers:
6
+ #
7
+ # * Live, streaming web import capabilities (Excel 2002+), including custom
8
+ # functions for easily importing security prices from Bloomberg.com, Google
9
+ # Finance and Yahoo! Finance
10
+ # * Basic options pricing, including Black-Scholes, the options Greeks and a
11
+ # few more complex options models
12
+ #
13
+ # Author:: Bryan McKelvey (mailto:bryan.mckelvey@gmail.com)
14
+ # Copyright:: Copyright (c) 2011 Bryan McKelvey
15
+ # License:: MIT
16
+
17
+ # This module contains all modules and classes associated with Rupee
18
+ module Rupee
19
+ autoload :Quote, "rupee/quote"
20
+ end
@@ -0,0 +1,39 @@
1
+ module Rupee
2
+ # Just for use in benchmarking the performance of Rupee code
3
+ class Benchmark
4
+ class << self
5
+ # Black-Scholes option price valuation
6
+ # by Michael Neumann (with some revisions)
7
+ # http://www.espenhaug.com/black_scholes.html
8
+ def black_scholes(callPutFlag, s, x, t, r, v)
9
+ d1 = (Math.log(s / x) + (r + v * v / 2.0) * t) / (v * Math.sqrt(t))
10
+ d2 = d1 - v * Math.sqrt(t)
11
+ if callPutFlag == 'c'
12
+ s * cnd(d1) - x * Math.exp(-r * t) * cnd(d2)
13
+ else
14
+ x * Math.exp(-r * t) * cnd(-d2) - s * cnd(-d1)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ A1 = 0.31938153
21
+ A2 = -0.356563782
22
+ A3 = 1.781477937
23
+ A4 = -1.821255978
24
+ A5 = 1.330274429
25
+
26
+ # Cumulative normal distribution
27
+ # by Michael Neumann (with some revisions)
28
+ # http://www.espenhaug.com/black_scholes.html
29
+ def cnd(x)
30
+ l = x.abs
31
+ k = 1.0 / (1.0 + 0.2316419 * l)
32
+ w = 1.0 - 1.0 / Math.sqrt(2 * Math::PI) * Math.exp(-l * l / 2.0) *
33
+ (A1 * k + A2 * k * k + A3 * (k ** 3) + A4 * (k ** 4) + A5 * (k ** 5))
34
+ w = 1.0 - w if x < 0
35
+ return w
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,48 @@
1
+ require "rupee/quote/source"
2
+ autoload :Net, "net/http"
3
+ autoload :URI, "uri"
4
+
5
+ module Rupee
6
+ # The quote and data import functionality in Rupee
7
+ class Quote
8
+ class << self
9
+ # Retrieves the current price of a security
10
+ def get(url, *params)
11
+ results = {}
12
+ params = [:price] if params.empty?
13
+ url = URI.parse(url)
14
+ html = Net::HTTP.start(url.host, url.port) do |http|
15
+ http.get url.request_uri
16
+ end.body
17
+
18
+ params.each do |p|
19
+ results[p] = @sources[0].params[p].match(html)[1]
20
+ end
21
+
22
+ results
23
+ end
24
+
25
+ # Retrieves the current price of a security from Bloomberg
26
+ def bloomberg(ticker, *params)
27
+ get BLOOMBERG_URL % ticker, *params
28
+ end
29
+
30
+ private
31
+
32
+ # The URL for Bloomberg's quotes service
33
+ BLOOMBERG_URL = "http://www.bloomberg.com/apps/quote?ticker=%s"
34
+
35
+ # Returns an intepretation of an abbreviated source name
36
+ def shorten_source(source)
37
+ case source.downcase.to_sym
38
+ when :"", :bloomberg, :bberg, :bb, :b
39
+ :bloomberg
40
+ when :google, :goog, :g
41
+ :google
42
+ when :yahoo!, :yahoo, :yhoo, :y!, :y
43
+ :yahoo
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,53 @@
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
+
8
+ # The parameters available
9
+ attr :params
10
+
11
+ def initialize(name, aliases = [], params = {})
12
+ @name = name
13
+ @aliases = aliases
14
+ @params = 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 << Source.new(:bloomberg, [:bberg, :bb, :b],
31
+ :price => /(?:PRICE|VALUE): <span class="amount">([0-9.,NA-]{1,})/,
32
+ :change => /Change<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
33
+ :pct_change => /Change<\/td>\n<td class="value[^>]+>[0-9.,NA-]{1,} \(([0-9NA.,-]{1,})\%/,
34
+ :date => /"date">(.*?)</,
35
+ :time => /"time">(.*?)</,
36
+ :bid => /Bid<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
37
+ :ask => /Ask<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
38
+ :open => /Open<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
39
+ :high => /High<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
40
+ :low => /Low<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
41
+ :volume => /Volume<\/td>\n<td class="value[^>]+>([0-9.,NA-]{1,})/,
42
+ :mkt_cap => /Market Cap[^<]+<\/td>\n<td class="value">([0-9.,NA-]{1,})/,
43
+ :p_e => /Price\/Earnings[^<]+<\/td>\n<td class="value">([0-9.,NA-]{1,})/)
44
+ @sources << Source.new(:yahoo)
45
+ @sources << Source.new(:google)
46
+ @default_source = :bloomberg
47
+ end
48
+ end
49
+
50
+ # Initialize sources
51
+ Quote.build_sources
52
+ end
53
+ end
@@ -0,0 +1,4 @@
1
+ module Rupee
2
+ # The current version
3
+ VERSION = "0.1.0"
4
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rupee/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rupee"
7
+ s.version = Rupee::VERSION
8
+ s.authors = ["Bryan McKelvey"]
9
+ s.email = ["bryan.mckelvey@gmail.com"]
10
+ s.homepage = "https://github.com/brymck/rupee"
11
+ s.summary = "Financial tools for Ruby"
12
+ s.description = "rupee aims to provide user-friendly tools for use in financial gems and applications."
13
+
14
+ s.rubyforge_project = "rupee"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- spec/**/*_spec.rb`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.extensions = "ext/rupee/extconf.rb"
20
+ s.require_paths = ["lib", "ext"]
21
+
22
+ # specify any dependencies here; for example:
23
+ s.add_development_dependency "bundler", "~> 1.0"
24
+ s.add_development_dependency "rspec", "~> 2.0"
25
+ s.add_development_dependency "autotest", "~> 4.0"
26
+ # s.add_runtime_dependency "rest-client"
27
+ end
@@ -0,0 +1,39 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ # Discrete discounting
4
+ # bonds price = 102.531
5
+ # bond yield to maturity = 0.09
6
+ # bond duration = 2.73895
7
+ # bond duration modified = 2.5128
8
+ # bond convexity =8.93248
9
+ # new bond price = 100
10
+ #Continous discounting
11
+ # bonds price = 101.464
12
+ # bond yield to maturity = 0.09
13
+ # bond duration = 2.73753
14
+ # bond convexity =7.86779
15
+ # new bond price = 104.282
16
+
17
+ TOLERANCE = 0.001
18
+
19
+ describe Rupee::Bond do
20
+ before :each do
21
+ @times = [1, 2, 3]
22
+ @cflows = [10, 10, 110]
23
+ @r = 0.09
24
+ end
25
+
26
+ describe "with continuous discounting" do
27
+ it "should produce an accurate price" do
28
+ Rupee::Bond.price(@times, @cflows, @r).should be_within(TOLERANCE).of 101.464
29
+ end
30
+
31
+ it "should produce an accurate duration" do
32
+ Rupee::Bond.duration(@times, @cflows, @r).should be_within(TOLERANCE).of 2.73753
33
+ end
34
+
35
+ it "should produce an accurate convexity" do
36
+ Rupee::Bond.convexity(@times, @cflows, @r).should be_within(TOLERANCE).of 7.86779
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,7 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe "Cumulative normal distribution" do
4
+ it "should return a probability of 50% for 0" do
5
+ Rupee::Distribution.cnd(0).round(4).should == 0.5
6
+ end
7
+ end
@@ -0,0 +1,45 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe Rupee::Option do
4
+ describe "European option valuation" do
5
+ describe "using the Black-76 model" do
6
+ describe "on a call option of price $60, strike $65, time to expiry 0.25, risk-free rate 8%, and volatility 30%" do
7
+ it "should return $1.7202 for a call" do
8
+ Rupee::Option.black76("c", 60, 65, 0.25, 0.08, 0.3).round(4).should == 1.7202
9
+ end
10
+
11
+ it "should return $6.6212 for a put" do
12
+ Rupee::Option.black76("p", 60, 65, 0.25, 0.08, 0.3).round(4).should == 6.6212
13
+ end
14
+ end
15
+ end
16
+
17
+ describe "using the generalized Black-Scholes model" do
18
+ describe "on a call option of price $60, strike $65, time to expiry 0.25, risk-free rate 8%, and volatility 30%" do
19
+ it "should return $1.7202 for a call" do
20
+ Rupee::Option.generalized_black_scholes("c", 60, 65, 0.25, 0.08, 0, 0.3).round(4).should == 1.7202
21
+ Rupee::Option.gbs("c", 60, 65, 0.25, 0.08, 0, 0.3).round(4).should == 1.7202
22
+ end
23
+
24
+ it "should return $6.6212 for a put" do
25
+ Rupee::Option.generalized_black_scholes("p", 60, 65, 0.25, 0.08, 0, 0.3).round(4).should == 6.6212
26
+ Rupee::Option.gbs("p", 60, 65, 0.25, 0.08, 0, 0.3).round(4).should == 6.6212
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "using the Black-Scholes model" do
32
+ describe "on a call option of price $60, strike $65, time to expiry 0.25, risk-free rate 8%, and volatility 30%" do
33
+ it "should return $1.7202 for a call" do
34
+ Rupee::Option.black_scholes("c", 60, 65, 0.25, 0.08, 0, 0.3).round(4).should == 2.1334
35
+ Rupee::Option.bs("c", 60, 65, 0.25, 0.08, 0, 0.3).round(4).should == 2.1334
36
+ end
37
+
38
+ it "should return $6.6212 for a put" do
39
+ Rupee::Option.black_scholes("p", 60, 65, 0.25, 0.08, 0, 0.3).round(4).should == 5.8463
40
+ Rupee::Option.bs("p", 60, 65, 0.25, 0.08, 0, 0.3).round(4).should == 5.8463
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,23 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe Rupee::Quote do
4
+ it "should automatically have a Bloomberg source" do
5
+ Rupee::Quote.sources[0].name.should == :bloomberg
6
+ end
7
+
8
+ describe "when pulling quotes" do
9
+ describe "without any parameters specified" do
10
+ before :each do
11
+ @wfc = Rupee::Quote.bloomberg("WFC")
12
+ end
13
+
14
+ it "should default to pulling the price" do
15
+ @wfc.should include :price
16
+ end
17
+
18
+ it "should return a price" do
19
+ @wfc[:price].to_f.should be_a_kind_of Float
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,2 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
2
+ require "rupee"
@@ -0,0 +1,25 @@
1
+ autoload :Benchmark, "benchmark"
2
+
3
+ namespace :benchmark do
4
+ task :default => :black_scholes
5
+
6
+ desc "Run Black-Scholes one million times"
7
+ task :black_scholes do
8
+ require "rupee"
9
+ require "rupee/benchmark"
10
+
11
+ n = 1_000_000
12
+ Benchmark.bm(11) do |x|
13
+ x.report "rupee:" do
14
+ n.times do
15
+ Rupee::Option.black_scholes "c", 60, 65, 0.25, 0.08, 0, 0.3
16
+ end
17
+ end
18
+ x.report("pure ruby:") do
19
+ n.times do
20
+ Rupee::Benchmark.black_scholes "c", 60, 65, 0.25, 0.08, 0.3
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rupee
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Bryan McKelvey
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: &84328270 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *84328270
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &84326450 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *84326450
36
+ - !ruby/object:Gem::Dependency
37
+ name: autotest
38
+ requirement: &84324740 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '4.0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *84324740
47
+ description: rupee aims to provide user-friendly tools for use in financial gems and
48
+ applications.
49
+ email:
50
+ - bryan.mckelvey@gmail.com
51
+ executables: []
52
+ extensions:
53
+ - ext/rupee/extconf.rb
54
+ extra_rdoc_files: []
55
+ files:
56
+ - .autotest
57
+ - .gitignore
58
+ - .rspec
59
+ - COPYING
60
+ - Gemfile
61
+ - README.md
62
+ - Rakefile
63
+ - ext/rupee/bond.c
64
+ - ext/rupee/conv.c
65
+ - ext/rupee/distribution.c
66
+ - ext/rupee/extconf.rb
67
+ - ext/rupee/option.c
68
+ - ext/rupee/rupee.c
69
+ - ext/rupee/rupee.h
70
+ - lib/rupee.rb
71
+ - lib/rupee/benchmark.rb
72
+ - lib/rupee/quote.rb
73
+ - lib/rupee/quote/source.rb
74
+ - lib/rupee/version.rb
75
+ - rupee.gemspec
76
+ - spec/c/bond_spec.rb
77
+ - spec/c/distribution_spec.rb
78
+ - spec/c/option_spec.rb
79
+ - spec/import/quote_spec.rb
80
+ - spec/spec_helper.rb
81
+ - tasks/benchmark.rake
82
+ homepage: https://github.com/brymck/rupee
83
+ licenses: []
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ - ext
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project: rupee
103
+ rubygems_version: 1.8.10
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: Financial tools for Ruby
107
+ test_files:
108
+ - spec/c/bond_spec.rb
109
+ - spec/c/distribution_spec.rb
110
+ - spec/c/option_spec.rb
111
+ - spec/import/quote_spec.rb