rupee 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
- }