rupee 0.1.0 → 0.1.1

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.md CHANGED
@@ -56,8 +56,16 @@ this in the Ruby console (i.e. `irb` in a command prompt):
56
56
 
57
57
  require "rupee"
58
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
59
67
 
60
- which should return `2.1334`.
68
+ both of which should return `2.1334`.
61
69
 
62
70
  You should also be able to get the latest stock info for, for example, Wells
63
71
  Fargo using the following (note that you only need to `require` the `quote`
@@ -72,14 +80,30 @@ Performance
72
80
  -----------
73
81
 
74
82
  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:
83
+ call option with Black-Scholes 100,000 times. You can test the same on yours
84
+ with rake, but in any case it makes the point that for the mathematical side
85
+ of finance a native extension has substantial benefits:
78
86
 
79
87
  rake benchmark:black_scholes
80
88
 
81
89
  Results:
82
90
 
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)
91
+ user system total real
92
+ Rupee (class): 0.190000 0.000000 0.190000 ( 0.194001)
93
+ Rupee (one object): 0.180000 0.000000 0.180000 ( 0.183091)
94
+ Rupee (new object): 2.210000 0.000000 2.210000 ( 2.213351)
95
+ Pure Ruby: 2.320000 0.000000 2.320000 ( 2.324259)
96
+
97
+ In words, for math-intensive operations, using a C implementation is clearly
98
+ faster than the same thing in Ruby.
99
+
100
+ Also, if you're doing a valuation on a one-off set of examples (e.g. in a Monte
101
+ Carlo simulation), you probably don't want to create an object every time.
102
+ Something like `Rupee::Option.black_scholes ...` should work just fine.
103
+ Creating a `Rupee::Option` object takes roughly the same amount of time as
104
+ running `Rupee::Option.black_scholes` a dozen times.
105
+
106
+ However, if you're creating and reusing an object, I strongly recommend
107
+ preserving the object orientation of Ruby: the penalty for using a new instance
108
+ rather than calling the class method directly is almost entirely in the object
109
+ initialization itself.
data/ext/rupee/bond.c CHANGED
@@ -1,82 +1,274 @@
1
1
  #include "rupee.h"
2
2
 
3
+ #define ACCURACY 0.00001
4
+ #define MAX_ITERATIONS 200
5
+
3
6
  double
4
- bond_price(cf_times, cfs, r, len)
5
- double *cf_times, *cfs, r;
7
+ bond_conv(times, cflows, r, len, discrete)
8
+ double *times, *cflows, r;
6
9
  int len;
10
+ bool discrete;
7
11
  {
8
- double p, *cft;
12
+ double C, B;
9
13
  int i;
14
+ C = 0;
15
+
16
+ for (i = 0; i < len; i++) {
17
+ double time, time_sq;
18
+ time = times[i];
19
+
20
+ // I'm a little skeptical this is correct
21
+ if (discrete)
22
+ time_sq = time * (times[i] + 1);
23
+ else
24
+ time_sq = pow(time, 2);
25
+
26
+ C += cflows[i] * time_sq * simple_df(r, time, discrete);
27
+ }
10
28
 
29
+ B = bond_price(times, cflows, r, len, discrete);
30
+
31
+ // Same goes for this; I don't know why you'd discount only under discrete
32
+ // compounding. That doesn't seem like a market convention.
33
+ if (discrete)
34
+ return C / pow(1 + r, 2) / B;
35
+ else
36
+ return C / B;
37
+ };
38
+
39
+ double
40
+ bond_dur(times, cflows, r, len, discrete)
41
+ double *times, *cflows, r;
42
+ int len;
43
+ bool discrete;
44
+ {
45
+ double S, D1;
46
+ int i;
47
+ S = 0;
48
+ D1 = 0;
49
+
50
+ for (i = 0; i < len; i++) {
51
+ double time, dcflow;
52
+
53
+ time = times[i];
54
+ dcflow = cflows[i] * simple_df(r, time, discrete);
55
+
56
+ S += dcflow;
57
+ D1 += dcflow * time;
58
+ }
59
+
60
+ return D1 / S;
61
+ }
62
+
63
+ double
64
+ bond_price(times, cflows, r, len, discrete)
65
+ double *times, *cflows, r;
66
+ int len;
67
+ bool discrete;
68
+ {
69
+ double p, *cft;
70
+ int i;
11
71
  p = 0;
12
72
 
13
73
  for (i = 0; i < len; i++)
14
- p += exp(-r * cf_times[i]) * cfs[i];
74
+ p += cflows[i] * simple_df(r, times[i], discrete);
15
75
 
16
76
  return p;
17
77
  };
18
78
 
79
+ double
80
+ bond_ytm(times, cflows, price, len, discrete)
81
+ double *times, *cflows, price;
82
+ int len;
83
+ bool discrete;
84
+ {
85
+ double bot, top, r;
86
+ int i;
87
+ bot = 0;
88
+ top = 1;
89
+
90
+ while (bond_price(times, cflows, top, len, discrete) > price)
91
+ top *= 2;
92
+
93
+ r = avg(top, bot);
94
+
95
+ for (i = 0; i < MAX_ITERATIONS; i++) {
96
+ double diff;
97
+ diff = bond_price(times, cflows, r, len, discrete) - price;
98
+
99
+ if (fabs(diff) < ACCURACY)
100
+ return r;
101
+
102
+ if (diff > 0.0)
103
+ bot = r;
104
+ else
105
+ top = r;
106
+
107
+ r = avg(top, bot);
108
+ };
109
+
110
+ return r;
111
+ };
112
+
113
+ // Ruby singleton functions
114
+
115
+ static VALUE
116
+ convexity(self, _times, _cflows, _r, discrete)
117
+ VALUE self, _times, _cflows, _r;
118
+ bool discrete;
119
+ {
120
+ int len = RARRAY_LEN(_cflows);
121
+ double times[len], cflows[len], r;
122
+
123
+ rtofa(times, _times, len);
124
+ rtofa(cflows, _cflows, len);
125
+ r = NUM2DBL(_r);
126
+
127
+ return rb_float_new(bond_conv(times, cflows, r, len, discrete));
128
+ };
129
+
19
130
  static VALUE
20
- price(self, rcf_times, rcfs, rr)
21
- VALUE self, rcf_times, rcfs, rr;
131
+ duration(self, _times, _cflows, _r, discrete)
132
+ VALUE self, _times, _cflows, _r;
133
+ bool discrete;
22
134
  {
23
- int len = RARRAY_LEN(rcfs);
24
- double r, cf_times[len], cfs[len];
135
+ int len = RARRAY_LEN(_cflows);
136
+ double times[len], cflows[len], r;
25
137
 
26
- r = NUM2DBL(rr);
27
- rtofa(cf_times, rcf_times, len);
28
- rtofa(cfs, rcfs, len);
138
+ rtofa(times, _times, len);
139
+ rtofa(cflows, _cflows, len);
140
+ r = NUM2DBL(_r);
29
141
 
30
- return rb_float_new(bond_price(cf_times, cfs, r, len));
142
+ return rb_float_new(bond_dur(times, cflows, r, len, discrete));
31
143
  }
32
144
 
33
145
  static VALUE
34
- convexity(self, rcf_times, rcfs, rr)
35
- VALUE self, rcf_times, rcfs, rr;
146
+ macaulay(self, _times, _cflows, _price, discrete)
147
+ VALUE self, _times, _cflows, _price;
148
+ bool discrete;
36
149
  {
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;
150
+ int len = RARRAY_LEN(_cflows);
151
+ double times[len], cflows[len], price, ytm;
45
152
 
46
- for (i = 0; i < len; i++)
47
- C += cfs[i] * pow(cf_times[i], 2) * exp(-r * cf_times[i]);
153
+ rtofa(times, _times, len);
154
+ rtofa(cflows, _cflows, len);
155
+ price = NUM2DBL(price);
48
156
 
49
- B = bond_price(cf_times, cfs, r, len);
157
+ ytm = bond_ytm(times, cflows, price, len, discrete);
50
158
 
51
- return rb_float_new(C / B);
159
+ return rb_float_new(bond_dur(times, cflows, ytm, len, discrete));
52
160
  };
53
161
 
54
162
  static VALUE
55
- duration(self, rcf_times, rcfs, rr)
56
- VALUE self, rcf_times, rcfs, rr;
163
+ price(self, _times, _cflows, _r, discrete)
164
+ VALUE self, _times, _cflows, _r;
165
+ bool discrete;
57
166
  {
58
- double r, S, D1;
59
- int i, cfs_len;
167
+ int len = RARRAY_LEN(_cflows);
168
+ double times[len], cflows[len], r;
169
+
170
+ rtofa(times, _times, len);
171
+ rtofa(cflows, _cflows, len);
172
+ r = NUM2DBL(_r);
60
173
 
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;
174
+ return rb_float_new(bond_price(times, cflows, r, len, discrete));
175
+ }
67
176
 
68
- for (i = 0; i < cfs_len; i++) {
69
- double cfti, cfi, dcfi;
177
+ // Helper functions
70
178
 
71
- cfti = NUM2DBL(cf_times[i]);
72
- cfi = NUM2DBL(cfs[i]);
73
- dcfi = cfi * exp(-r * cfti);
179
+ // Convexity of a continuously compounded bond
180
+ static VALUE
181
+ convexity_continuous(self, _times, _cflows, _r)
182
+ VALUE self, _times, _cflows, _r;
183
+ {
184
+ return convexity(self, _times, _cflows, _r, false);
185
+ }
74
186
 
75
- S += dcfi;
76
- D1 += cfti * dcfi;
77
- }
187
+ // Convexity of a discretely compounded bond
188
+ static VALUE
189
+ convexity_discrete(self, _times, _cflows, _r)
190
+ VALUE self, _times, _cflows, _r;
191
+ {
192
+ return convexity(self, _times, _cflows, _r, true);
193
+ }
194
+
195
+ // Duration of a continuously compounded bond
196
+ static VALUE
197
+ duration_continuous(self, _times, _cflows, _r)
198
+ VALUE self, _times, _cflows, _r;
199
+ {
200
+ return duration(self, _times, _cflows, _r, false);
201
+ }
202
+
203
+ // Duration of a discretely compounded bond
204
+ static VALUE
205
+ duration_discrete(self, _times, _cflows, _r)
206
+ VALUE self, _times, _cflows, _r;
207
+ {
208
+ return duration(self, _times, _cflows, _r, true);
209
+ }
210
+
211
+ // Macaulay duration of a continuously compounded bond
212
+ static VALUE
213
+ macaulay_continuous(self, _times, _cflows, _price)
214
+ VALUE self, _times, _cflows, _price;
215
+ {
216
+ return macaulay(self, _times, _cflows, _price, false);
217
+ }
78
218
 
79
- return rb_float_new(D1 / S);
219
+ // Macaulay duration of a discretely compounded bond
220
+ static VALUE
221
+ macaulay_discrete(self, _times, _cflows, _price)
222
+ VALUE self, _times, _cflows, _price;
223
+ {
224
+ return macaulay(self, _times, _cflows, _price, true);
225
+ }
226
+
227
+ // Price of a continuously compounded bond
228
+ static VALUE
229
+ price_continuous(self, _times, _cflows, _r)
230
+ VALUE self, _times, _cflows, _r;
231
+ {
232
+ return price(self, _times, _cflows, _r, false);
233
+ }
234
+
235
+ // Price of a discretely compounded bond
236
+ static VALUE
237
+ price_discrete(self, _times, _cflows, _r)
238
+ VALUE self, _times, _cflows, _r;
239
+ {
240
+ return price(self, _times, _cflows, _r, true);
241
+ }
242
+
243
+ static VALUE
244
+ yield_to_maturity(self, _times, _cflows, _price, discrete)
245
+ VALUE self, _times, _cflows, _price;
246
+ bool discrete;
247
+ {
248
+ int len = RARRAY_LEN(_cflows);
249
+ double times[len], cflows[len], price;
250
+
251
+ rtofa(times, _times, len);
252
+ rtofa(cflows, _cflows, len);
253
+ price = NUM2DBL(_price);
254
+
255
+ return rb_float_new(bond_ytm(times, cflows, price, len, discrete));
256
+ };
257
+
258
+ // Yield to maturity of a continuously compounded bond
259
+ static VALUE
260
+ yield_to_maturity_continuous(self, _times, _cflows, _price)
261
+ VALUE self, _times, _cflows, _price;
262
+ {
263
+ return yield_to_maturity(self, _times, _cflows, _price, false);
264
+ }
265
+
266
+ // Yield to maturity of a discretely compounded bond
267
+ static VALUE
268
+ yield_to_maturity_discrete(self, _times, _cflows, _price)
269
+ VALUE self, _times, _cflows, _price;
270
+ {
271
+ return yield_to_maturity(self, _times, _cflows, _price, true);
80
272
  }
81
273
 
82
274
  void
@@ -85,13 +277,28 @@ init_bond()
85
277
  VALUE klass, singleton;
86
278
 
87
279
  #if 0
88
- value module = rb_define_module("rupee");
280
+ VALUE module = rb_define_module("Rupee");
281
+ VALUE superklass = rb_define_class_under(module, "Security", rb_cObject);
89
282
  #endif
90
283
 
91
- klass = rb_define_class_under(module, "Bond", rb_cObject);
284
+ klass = rb_define_class_under(module, "Bond", superklass);
92
285
  singleton = rb_singleton_class(klass);
93
286
 
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);
287
+ rb_define_singleton_method(klass, "convexity", convexity_discrete, 3);
288
+ rb_define_singleton_method(klass, "continuous_convexity", convexity_continuous, 3);
289
+ rb_define_singleton_method(klass, "continuous_duration", duration_continuous, 3);
290
+ rb_define_singleton_method(klass, "continuous_macaulay", macaulay_continuous, 3);
291
+ rb_define_singleton_method(klass, "continuous_price", price_continuous, 3);
292
+ rb_define_singleton_method(klass, "continuous_yield_to_maturity", yield_to_maturity_continuous, 3);
293
+ rb_define_alias(singleton, "continuous_yield", "continuous_yield_to_maturity");
294
+ rb_define_alias(singleton, "continuous_ytm", "continuous_yield_to_maturity");
295
+ rb_define_singleton_method(klass, "duration", duration_discrete, 3);
296
+ rb_define_alias(singleton, "dur", "duration");
297
+ rb_define_singleton_method(klass, "macaulay", macaulay_discrete, 3);
298
+ rb_define_alias(singleton, "macaulary_duration", "macaulay");
299
+ rb_define_singleton_method(klass, "price", price_discrete, 3);
300
+ rb_define_alias(singleton, "value", "price");
301
+ rb_define_singleton_method(klass, "yield_to_maturity", yield_to_maturity_discrete, 3);
302
+ rb_define_alias(singleton, "yield", "yield_to_maturity");
303
+ rb_define_alias(singleton, "ytm", "yield_to_maturity");
97
304
  }
@@ -0,0 +1,38 @@
1
+ #include "rupee.h"
2
+
3
+ double
4
+ future_price(S, r, ttm)
5
+ double S, r, ttm;
6
+ {
7
+ return S * exp(r * ttm);
8
+ }
9
+
10
+ static VALUE
11
+ price(self, _S, _r, _ttm)
12
+ VALUE self, _S, _r, _ttm;
13
+ {
14
+ double S, r, ttm;
15
+
16
+ S = NUM2DBL(_S);
17
+ r = NUM2DBL(_r);
18
+ ttm = NUM2DBL(_ttm);
19
+
20
+ return rb_float_new(future_price(S, r, ttm));
21
+ }
22
+
23
+ void
24
+ init_future()
25
+ {
26
+ VALUE klass, singleton;
27
+
28
+ #if 0
29
+ VALUE module = rb_define_module("Rupee");
30
+ VALUE superklass = rb_define_class_under(module, "Security", rb_cObject);
31
+ #endif
32
+
33
+ klass = rb_define_class_under(module, "Future", superklass);
34
+ singleton = rb_singleton_class(klass);
35
+
36
+ rb_define_singleton_method(klass, "price", price, 3);
37
+ rb_define_alias(singleton, "value", "price");
38
+ }
data/ext/rupee/option.c CHANGED
@@ -164,15 +164,16 @@ init_option()
164
164
 
165
165
  #if 0
166
166
  VALUE module = rb_define_module("Rupee");
167
+ VALUE superklass = rb_define_class_under(module, "Security", rb_cObject);
167
168
  #endif
168
169
 
169
- klass = rb_define_class_under(module, "Option", rb_cObject);
170
+ klass = rb_define_class_under(module, "Option", superklass);
170
171
  singleton = rb_singleton_class(klass);
171
172
 
173
+ rb_define_singleton_method(klass, "black76", rupee_black76, 6);
172
174
  rb_define_singleton_method(klass, "black_scholes", rupee_black_scholes, 7);
173
175
  rb_define_alias(singleton, "bs", "black_scholes");
174
176
  rb_define_singleton_method(klass, "generalized_black_scholes",
175
177
  rupee_generalized_black_scholes, 7);
176
178
  rb_define_alias(singleton, "gbs", "generalized_black_scholes");
177
- rb_define_singleton_method(klass, "black76", rupee_black76, 6);
178
179
  }
data/ext/rupee/rupee.c CHANGED
@@ -1,13 +1,15 @@
1
1
  #include "rupee.h"
2
2
 
3
- VALUE module;
3
+ VALUE module, superklass;
4
4
 
5
5
  void
6
6
  Init_rupee()
7
7
  {
8
8
  module = rb_define_module("Rupee");
9
+ superklass = rb_define_class_under(module, "Security", rb_cObject);
9
10
 
10
11
  init_distribution();
11
12
  init_option();
12
13
  init_bond();
14
+ init_future();
13
15
  }
data/ext/rupee/rupee.h CHANGED
@@ -7,9 +7,12 @@
7
7
  #include <stdbool.h>
8
8
 
9
9
  extern VALUE module;
10
+ extern VALUE superklass;
10
11
 
11
- /* Conversion */
12
+ /* Utilities */
12
13
  double *rtofa(double *dest, VALUE src, int len);
14
+ double avg(double x, double y);
15
+ double simple_df(double r, double time, bool discrete);
13
16
 
14
17
  /* Statistics */
15
18
  double cnd(double);
@@ -21,7 +24,13 @@ double gbs(const char *call_put_flag, double S, double X, double T, double r,
21
24
  void init_option();
22
25
 
23
26
  /* Bonds */
24
- double bond_price(double *cf_times, double *cfs, double r, int len);
27
+ double bond_dur(double *times, double *cflows, double r, int len, bool discrete);
28
+ double bond_price(double *times, double *cflows, double r, int len, bool discrete);
29
+ double bond_ytm(double *times, double *cflows, double r, int len, bool discrete);
25
30
  void init_bond();
26
31
 
32
+ /* Futures */
33
+ double future_price(double S, double r, double ttm);
34
+ void init_future();
35
+
27
36
  #endif
data/ext/rupee/util.c ADDED
@@ -0,0 +1,36 @@
1
+ #include "rupee.h"
2
+
3
+ double
4
+ simple_df(r, time, discrete)
5
+ double r, time;
6
+ bool discrete;
7
+ {
8
+ if (discrete)
9
+ return 1.0 / pow(1.0 + r, time);
10
+ else
11
+ return exp(-r * time);
12
+ }
13
+
14
+ double *
15
+ rtofa(dest, src, len)
16
+ double *dest;
17
+ VALUE src;
18
+ int len;
19
+ {
20
+ int i;
21
+ VALUE *ary;
22
+
23
+ ary = RARRAY_PTR(src);
24
+
25
+ for (i = 0; i < len; i++)
26
+ dest[i] = NUM2DBL(ary[i]);
27
+
28
+ return dest;
29
+ }
30
+
31
+ double
32
+ avg(x, y)
33
+ double x, y;
34
+ {
35
+ return 0.5 * (x + y);
36
+ }
data/lib/rupee.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require "rupee/rupee" # keep this as the first require
2
2
  require "rupee/version"
3
+ require "rupee/security"
4
+ require "rupee/option.rb"
3
5
 
4
6
  # Rupee aims to provide user-friendly tools for use in financial applications.
5
7
  # The gem is in its development stages, but it currently offers:
@@ -16,5 +18,6 @@ require "rupee/version"
16
18
 
17
19
  # This module contains all modules and classes associated with Rupee
18
20
  module Rupee
21
+ # autoload :Option, "rupee/option.rb"
19
22
  autoload :Quote, "rupee/quote"
20
23
  end
@@ -0,0 +1,31 @@
1
+ require "rupee/security"
2
+
3
+ module Rupee
4
+ class Option < Security
5
+ attr_accessor :type, :underlying, :strike, :time, :rate, :div_yield, :volatility, :price
6
+ alias :rfr :rate
7
+ alias :rfr= :rate=
8
+ alias :risk_free_rate :rate
9
+ alias :risk_free_rate= :rate=
10
+ alias :value :price
11
+ alias :value= :price=
12
+
13
+ def black_scholes
14
+ @value = self.class.black_scholes @type.to_s, @underlying, @strike, @time, @rate, @div_yield, @volatility
15
+ end
16
+ end
17
+
18
+ class Call < Option
19
+ def initialize(opts = {})
20
+ @type = "call"
21
+ super
22
+ end
23
+ end
24
+
25
+ class Put < Option
26
+ def initialize(opts = {})
27
+ @type = :put
28
+ super
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ module Rupee
2
+ # An abstract class from which all Rupee security types inherit
3
+ class Security
4
+ # Automatically sets all arguments passed to <tt>initialize</tt> to
5
+ # instance variables if they exist (think Rails mass assignment).
6
+ #
7
+ # require "rupee"
8
+ #
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
+ #
20
+ # You still have the option of avoiding the creation of an object (and the
21
+ # overhead it entails) by using the class methods directly:
22
+ #
23
+ # require "rupee"
24
+ #
25
+ # puts Rupee::Option.black_scholes "c", 60, 65, 0.25, 0.08, 0, 0.3
26
+ # #=> 2.1333718619275794
27
+ def initialize(opts = {})
28
+ opts.each do |key, value|
29
+ writer = key.to_s.+("=").to_sym
30
+ if respond_to?(writer)
31
+ send writer, value
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
data/lib/rupee/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Rupee
2
2
  # The current version
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.1"
4
4
  end
data/spec/c/bond_spec.rb CHANGED
@@ -1,17 +1,10 @@
1
1
  require File.dirname(__FILE__) + "/../spec_helper"
2
2
 
3
3
  # Discrete discounting
4
- # bonds price = 102.531
5
- # bond yield to maturity = 0.09
6
- # bond duration = 2.73895
7
4
  # bond duration modified = 2.5128
8
- # bond convexity =8.93248
9
5
  # new bond price = 100
10
6
  #Continous discounting
11
- # bonds price = 101.464
12
7
  # bond yield to maturity = 0.09
13
- # bond duration = 2.73753
14
- # bond convexity =7.86779
15
8
  # new bond price = 104.282
16
9
 
17
10
  TOLERANCE = 0.001
@@ -23,17 +16,41 @@ describe Rupee::Bond do
23
16
  @r = 0.09
24
17
  end
25
18
 
19
+ describe "with discrete discounting" do
20
+ it "should produce an accurate price" do
21
+ Rupee::Bond.price(@times, @cflows, @r).should be_within(TOLERANCE).of 102.531
22
+ end
23
+
24
+ it "should produce an accurate duration" do
25
+ Rupee::Bond.duration(@times, @cflows, @r).should be_within(TOLERANCE).of 2.73895
26
+ end
27
+
28
+ it "should produce an accurate convexity" do
29
+ Rupee::Bond.convexity(@times, @cflows, @r).should be_within(TOLERANCE).of 8.93248
30
+ end
31
+
32
+ it "should produce an accurate yield to maturity" do
33
+ @price = Rupee::Bond.price(@times, @cflows, @r)
34
+ Rupee::Bond.yield_to_maturity(@times, @cflows, @price).should be_within(TOLERANCE).of @r
35
+ end
36
+ end
37
+
26
38
  describe "with continuous discounting" do
27
39
  it "should produce an accurate price" do
28
- Rupee::Bond.price(@times, @cflows, @r).should be_within(TOLERANCE).of 101.464
40
+ Rupee::Bond.continuous_price(@times, @cflows, @r).should be_within(TOLERANCE).of 101.464
29
41
  end
30
42
 
31
43
  it "should produce an accurate duration" do
32
- Rupee::Bond.duration(@times, @cflows, @r).should be_within(TOLERANCE).of 2.73753
44
+ Rupee::Bond.continuous_duration(@times, @cflows, @r).should be_within(TOLERANCE).of 2.73753
33
45
  end
34
46
 
35
47
  it "should produce an accurate convexity" do
36
- Rupee::Bond.convexity(@times, @cflows, @r).should be_within(TOLERANCE).of 7.86779
48
+ Rupee::Bond.continuous_convexity(@times, @cflows, @r).should be_within(TOLERANCE).of 7.86779
49
+ end
50
+
51
+ it "should produce an accurate yield to maturity" do
52
+ @price = Rupee::Bond.continuous_price(@times, @cflows, @r)
53
+ Rupee::Bond.continuous_yield_to_maturity(@times, @cflows, @price).should be_within(TOLERANCE).of @r
37
54
  end
38
55
  end
39
56
  end
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe Rupee::Future do
4
+ before :each do
5
+ @underlying = 100
6
+ @rfr = 0.10
7
+ @ttm = 0.5
8
+ end
9
+
10
+ it "should produce an accurate price" do
11
+ Rupee::Future.price(@underlying, @rfr, @ttm).should be_within(TOLERANCE).of 105.127
12
+ end
13
+ end
data/tasks/benchmark.rake CHANGED
@@ -3,19 +3,48 @@ autoload :Benchmark, "benchmark"
3
3
  namespace :benchmark do
4
4
  task :default => :black_scholes
5
5
 
6
- desc "Run Black-Scholes one million times"
6
+ desc "Run Black-Scholes on a European call 100,000 times"
7
7
  task :black_scholes do
8
8
  require "rupee"
9
9
  require "rupee/benchmark"
10
10
 
11
- n = 1_000_000
12
- Benchmark.bm(11) do |x|
13
- x.report "rupee:" do
11
+ n = 100_000
12
+ Benchmark.bm(19) do |x|
13
+ x.report "Rupee (class):" do
14
14
  n.times do
15
15
  Rupee::Option.black_scholes "c", 60, 65, 0.25, 0.08, 0, 0.3
16
16
  end
17
17
  end
18
- x.report("pure ruby:") do
18
+
19
+ x.report "Rupee (one object):" do
20
+ call = Rupee::Call.new(
21
+ :underlying => 60,
22
+ :strike => 65,
23
+ :time => 0.25,
24
+ :rate => 0.08,
25
+ :div_yield => 0.00,
26
+ :volatility => 0.3
27
+ )
28
+
29
+ n.times do
30
+ call.black_scholes
31
+ end
32
+ end
33
+
34
+ x.report "Rupee (new object):" do
35
+ n.times do
36
+ Rupee::Call.new(
37
+ :underlying => 60,
38
+ :strike => 65,
39
+ :time => 0.25,
40
+ :rate => 0.08,
41
+ :div_yield => 0.00,
42
+ :volatility => 0.3
43
+ ).black_scholes
44
+ end
45
+ end
46
+
47
+ x.report("Pure Ruby:") do
19
48
  n.times do
20
49
  Rupee::Benchmark.black_scholes "c", 60, 65, 0.25, 0.08, 0.3
21
50
  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.0
4
+ version: 0.1.1
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: &84328270 !ruby/object:Gem::Requirement
16
+ requirement: &71003600 !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: *84328270
24
+ version_requirements: *71003600
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &84326450 !ruby/object:Gem::Requirement
27
+ requirement: &71002450 !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: *84326450
35
+ version_requirements: *71002450
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: autotest
38
- requirement: &84324740 !ruby/object:Gem::Requirement
38
+ requirement: &70999390 !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: *84324740
46
+ version_requirements: *70999390
47
47
  description: rupee aims to provide user-friendly tools for use in financial gems and
48
48
  applications.
49
49
  email:
@@ -61,20 +61,24 @@ files:
61
61
  - README.md
62
62
  - Rakefile
63
63
  - ext/rupee/bond.c
64
- - ext/rupee/conv.c
65
64
  - ext/rupee/distribution.c
66
65
  - ext/rupee/extconf.rb
66
+ - ext/rupee/future.c
67
67
  - ext/rupee/option.c
68
68
  - ext/rupee/rupee.c
69
69
  - ext/rupee/rupee.h
70
+ - ext/rupee/util.c
70
71
  - lib/rupee.rb
71
72
  - lib/rupee/benchmark.rb
73
+ - lib/rupee/option.rb
72
74
  - lib/rupee/quote.rb
73
75
  - lib/rupee/quote/source.rb
76
+ - lib/rupee/security.rb
74
77
  - lib/rupee/version.rb
75
78
  - rupee.gemspec
76
79
  - spec/c/bond_spec.rb
77
80
  - spec/c/distribution_spec.rb
81
+ - spec/c/future_spec.rb
78
82
  - spec/c/option_spec.rb
79
83
  - spec/import/quote_spec.rb
80
84
  - spec/spec_helper.rb
@@ -107,5 +111,6 @@ summary: Financial tools for Ruby
107
111
  test_files:
108
112
  - spec/c/bond_spec.rb
109
113
  - spec/c/distribution_spec.rb
114
+ - spec/c/future_spec.rb
110
115
  - spec/c/option_spec.rb
111
116
  - spec/import/quote_spec.rb
data/ext/rupee/conv.c DELETED
@@ -1,18 +0,0 @@
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
- }