rupee 0.1.0

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.
@@ -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