rupee 0.2.1 → 0.2.2

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/ext/rupee/rupee.h CHANGED
@@ -15,10 +15,14 @@ double avg(double x, double y);
15
15
  double simple_df(double r, double time, bool discrete);
16
16
 
17
17
  /* Statistics */
18
+ double bnd(double, double, double);
18
19
  double cnd(double);
20
+ double cndev(double);
19
21
  double corr(double *, double *, int);
20
22
  double cov(double *, double *, int);
21
23
  double mean(double *, int);
24
+ double nd(double);
25
+ double pdf(double);
22
26
  double std(double *, int);
23
27
  double sum(double *, int);
24
28
  double sum_prod(double *, double *, int);
@@ -26,8 +30,20 @@ double var(double *, int);
26
30
  void init_distribution();
27
31
 
28
32
  /* Options */
29
- double gbs(const char *call_put_flag, double S, double X, double T, double r,
30
- double b, double v);
33
+ double bs(const char *call_put_flag, double S, double K, double T, double r,
34
+ double q, double v);
35
+ double bs_gamma(const char *call_put_flag, double S, double K, double T, double r,
36
+ double q, double v);
37
+ double delta(const char *call_put_flag, double S, double K, double T, double r,
38
+ double q, double v);
39
+ double gbs(const char *call_put_flag, double S, double K, double T, double r,
40
+ double q, double v);
41
+ double rho(const char *call_put_flag, double S, double K, double T, double r,
42
+ double q, double v);
43
+ double theta(const char *call_put_flag, double S, double K, double T, double r,
44
+ double q, double v);
45
+ double vega(const char *call_put_flag, double S, double K, double T, double r,
46
+ double q, double v);
31
47
  void init_option();
32
48
 
33
49
  /* Bonds */
@@ -2,6 +2,16 @@
2
2
 
3
3
  #define PI 3.1415926536
4
4
 
5
+ // DISTRIBUTIONS
6
+
7
+ double
8
+ bnd(x, y, rho)
9
+ double x, y, rho;
10
+ {
11
+ return exp(-1.0 / (2.0 * (1.0 - rho * rho)) * (x * x + y * y - 2.0 * x * y * rho)) /
12
+ (2.0 * PI * sqrt(1.0 - rho * rho));
13
+ }
14
+
5
15
  double
6
16
  cnd(z)
7
17
  double z;
@@ -31,6 +41,71 @@ cnd(z)
31
41
  return dCND;
32
42
  }
33
43
 
44
+ double
45
+ cndev(U)
46
+ double U;
47
+ {
48
+ double x;
49
+ double r;
50
+
51
+ static const double A0 = 2.50662823884;
52
+ static const double A1 = -18.61500062529;
53
+ static const double A2 = 41.39119773534;
54
+ static const double A3 = -25.44106049637;
55
+
56
+ static const double B0 = -8.4735109309;
57
+ static const double B1 = 23.08336743743;
58
+ static const double B2 = -21.06224101826;
59
+ static const double B3 = 3.1308290983;
60
+
61
+ static const double C0 = 0.337475482272615;
62
+ static const double C1 = 0.976169019091719;
63
+ static const double C2 = 0.160797971491821;
64
+ static const double C3 = 2.76438810333863e-02;
65
+ static const double C4 = 3.8405729373609e-03;
66
+ static const double C5 = 3.951896511919e-04;
67
+ static const double C6 = 3.21767881767818e-05;
68
+ static const double C7 = 2.888167364e-07;
69
+ static const double C8 = 3.960315187e-07;
70
+
71
+ x = U - .5;
72
+ if (abs(x) < .92) {
73
+ r = x * x;
74
+ r = x * (((A3 * r + A2) * r + A1) * r + A0) /
75
+ ((((B3 * r + B2) * r + B1) * r + B0) * r + 1.0);
76
+ return r;
77
+ }
78
+ else {
79
+ if (x >= 0.0)
80
+ r = 1.0 - U;
81
+ else
82
+ r = U;
83
+
84
+ r = log(-log(r));
85
+ r = C0 + r * (C1 + r * (C2 + r * (C3 + r + (C4 +
86
+ r * (C5 + r * (C6 + r * (C7 + r * C8)))))));
87
+
88
+ if (x < 0.0)
89
+ return -r;
90
+ else
91
+ return r;
92
+ }
93
+ }
94
+
95
+ double
96
+ nd(x)
97
+ double x;
98
+ {
99
+ return exp(-x * x / 2.0) / sqrt(2.0 * PI);
100
+ }
101
+
102
+ double
103
+ pdf(z)
104
+ double z;
105
+ {
106
+ return exp(-(z * z) / 2) / sqrt(2 * PI);
107
+ }
108
+
34
109
  double
35
110
  sum_prod(xvals, yvals, len)
36
111
  double *xvals, *yvals;
@@ -121,6 +196,23 @@ std(values, len)
121
196
  return sqrt(var(values, len));
122
197
  }
123
198
 
199
+ /* call-seq: bnd(x, y, rho)
200
+ *
201
+ * Returns the bivariate normal distribution function
202
+ */
203
+ static VALUE
204
+ rupee_bnd(self, _x, _y, _rho)
205
+ VALUE self, _x, _y, _rho;
206
+ {
207
+ double x, y, rho;
208
+
209
+ x = NUM2DBL(_x);
210
+ y = NUM2DBL(_y);
211
+ rho = NUM2DBL(_rho);
212
+
213
+ return rb_float_new(bnd(x, y, rho));
214
+ }
215
+
124
216
  /* call-seq: cnd(z)
125
217
  *
126
218
  * Returns the standard normal cumulative distribution (has a mean of zero and
@@ -133,6 +225,17 @@ rupee_cnd(self, _z)
133
225
  return rb_float_new(cnd(NUM2DBL(_z)));
134
226
  }
135
227
 
228
+ /* call-seq: cndev(U)
229
+ *
230
+ * Returns the inverse cumulative normal distribution function
231
+ */
232
+ static VALUE
233
+ rupee_cndev(self, _U)
234
+ VALUE self, _U;
235
+ {
236
+ return rb_float_new(cndev(NUM2DBL(_U)));
237
+ }
238
+
136
239
  /* call-seq: correlation(xvalues, yvalues)
137
240
  *
138
241
  * Returns the correlation of the supplied x- and y-values.
@@ -183,6 +286,28 @@ rupee_mean(self, _values)
183
286
  return rb_float_new(mean(values, len));
184
287
  }
185
288
 
289
+ /* call-seq: nd(x)
290
+ *
291
+ * Returns the normal distribution function
292
+ */
293
+ static VALUE
294
+ rupee_nd(self, _x)
295
+ VALUE self, _x;
296
+ {
297
+ return rb_float_new(nd(NUM2DBL(_x)));
298
+ }
299
+
300
+ /* call-seq: pdf(z)
301
+ *
302
+ * Returns the probability density function
303
+ */
304
+ static VALUE
305
+ rupee_pdf(self, _z)
306
+ VALUE self, _z;
307
+ {
308
+ return rb_float_new(pdf(NUM2DBL(_z)));
309
+ }
310
+
186
311
  /* call-seq: standard_deviation(values)
187
312
  *
188
313
  * Finds the standard deviation of the specified values.
@@ -243,6 +368,8 @@ init_distribution()
243
368
  klass = rb_define_class_under(module, "Statistics", rb_cObject);
244
369
  singleton = rb_singleton_class(klass);
245
370
 
371
+ rb_define_singleton_method(klass, "bivariate_normal_distribution", rupee_bnd, 3);
372
+ rb_define_alias(singleton, "bnd", "bivariate_normal_distribution");
246
373
  rb_define_singleton_method(klass, "correlation", rupee_corr, 2);
247
374
  rb_define_alias(singleton, "corr", "correlation");
248
375
  rb_define_alias(singleton, "correl", "correlation");
@@ -251,9 +378,15 @@ init_distribution()
251
378
  rb_define_alias(singleton, "covar", "covariance");
252
379
  rb_define_singleton_method(klass, "cumulative_normal_distribution", rupee_cnd, 1);
253
380
  rb_define_alias(singleton, "cnd", "cumulative_normal_distribution");
381
+ rb_define_singleton_method(klass, "inverse_cumulative_normal_distribution", rupee_cndev, 1);
382
+ rb_define_alias(singleton, "cndev", "inverse_cumulative_normal_distribution");
254
383
  rb_define_singleton_method(klass, "mean", rupee_mean, 1);
255
384
  rb_define_alias(singleton, "average", "mean");
256
385
  rb_define_alias(singleton, "avg", "mean");
386
+ rb_define_singleton_method(klass, "normal_distribution", rupee_cnd, 1);
387
+ rb_define_alias(singleton, "nd", "normal_distribution");
388
+ rb_define_singleton_method(klass, "probability_density_function", rupee_pdf, 1);
389
+ rb_define_alias(singleton, "pdf", "probability_density_function");
257
390
  rb_define_singleton_method(klass, "standard_deviation", rupee_std, 1);
258
391
  rb_define_alias(singleton, "std", "standard_deviation");
259
392
  rb_define_alias(singleton, "stdev", "standard_deviation");
data/lib/rupee.rb CHANGED
@@ -19,5 +19,6 @@ require "rupee/option.rb"
19
19
  # This module contains all modules and classes associated with Rupee
20
20
  module Rupee
21
21
  # autoload :Option, "rupee/option.rb"
22
+ autoload :Calendar, "rupee/calendar"
22
23
  autoload :Quote, "rupee/quote"
23
24
  end
@@ -0,0 +1,238 @@
1
+ module Rupee
2
+ # An object representing a calendar, for use in determining the next payout
3
+ # date for a cash flow. Simple example:
4
+ #
5
+ # require "rupee/calendar"
6
+ #
7
+ # module Rupee
8
+ # class Calendar
9
+ # # It's recommended that you store your calendar inside the Calendar
10
+ # # class, as that way you'll have access to them as "constants." Also,
11
+ # # it gives you access to the month constants (JANUARY, FEBRUARY,
12
+ # # MARCH, etc.)
13
+ # MyCalendar = Rupee::Calendar.new("Sample calendar")
14
+ #
15
+ # # Weekends
16
+ # MyCalendar.has_weekends_off
17
+ #
18
+ # # Thanksgiving (fourth Thursday of November
19
+ # MyCalendar.has_day_off_on :thanksgiving do |date|
20
+ # date.month == NOVEMBER && date.thursday? && week_of(date) == 4
21
+ # end
22
+ #
23
+ # # Christmas (December 25 or nearest weekday)
24
+ # MyCalendar.has_day_off_on :christmas do |date|
25
+ # date.month == DECEMBER && nearest_weekday(date, 25)
26
+ # end
27
+ #
28
+ # # New Year's Day (January 1 or next weekday)
29
+ # MyCalendar.has_day_off_on :new_years do |date|
30
+ # date.month == JANUARY && next_weekday(date, 1)
31
+ # end
32
+ # end
33
+ # end
34
+ #
35
+ # # Christmas falls on a Sunday in 2011...
36
+ # Rupee::Calendar::MyCalendar.day_off?(Time.new(2011, 12, 25))
37
+ # # => true
38
+ #
39
+ # # ... so we have the following Monday off...
40
+ # Rupee::Calendar::MyCalendar.day_off?(Time.new(2011, 12, 26))
41
+ # # => true
42
+ #
43
+ # # ...then it's back to work
44
+ # Rupee::Calendar::MyCalendar.day_off?(Time.new(2011, 12, 27))
45
+ # # => false
46
+ #
47
+ # You can also inherit from other calendars easily:
48
+ #
49
+ # require "rupee/calendar"
50
+ #
51
+ # class Rupee::Calendar
52
+ # # Pirates generally observe the Federal Reserve holiday schedule
53
+ # Blackbeard = US.copy
54
+ #
55
+ # # But they do observer Talk Like a Pirate Day
56
+ # Blackbeard.has_day_off_on :talk_like_a_pirate_day do |date|
57
+ # date.month == SEPTEMBER && date.day == 19
58
+ # end
59
+ #
60
+ # # And curse the flag Columbus flew under
61
+ # Blackbeard.remove_day_off_for :columbus_day
62
+ # end
63
+ #
64
+ # # Talk Like a Pirate Day
65
+ # Rupee::Calendar::Blackbeard.day_off?(Time.new(2011, 9, 19))
66
+ # # => true
67
+ # Rupee::Calendar::US.day_off?(Time.new(2011, 9, 19))
68
+ # # => false
69
+ #
70
+ # # Columbus Day
71
+ # Rupee::Calendar::Blackbeard.day_off?(Time.new(2011, 10, 10))
72
+ # # => false
73
+ # Rupee::Calendar::US.day_off?(Time.new(2011, 10, 10))
74
+ # # => true
75
+ class Calendar
76
+ # A constant representing the month of January
77
+ JANUARY = 1
78
+ # A constant representing the month of February
79
+ FEBRUARY = 2
80
+ # A constant representing the month of March
81
+ MARCH = 3
82
+ # A constant representing the month of April
83
+ APRIL = 4
84
+ # A constant representing the month of May
85
+ MAY = 5
86
+ # A constant representing the month of June
87
+ JUNE = 6
88
+ # A constant representing the month of July
89
+ JULY = 7
90
+ # A constant representing the month of August
91
+ AUGUST = 8
92
+ # A constant representing the month of September
93
+ SEPTEMBER = 9
94
+ # A constant representing the month of October
95
+ OCTOBER = 10
96
+ # A constant representing the month of November
97
+ NOVEMBER = 11
98
+ # A constant representing the month of December
99
+ DECEMBER = 12
100
+
101
+ autoload :Japan, "rupee/calendar/japan"
102
+ autoload :US, "rupee/calendar/us"
103
+
104
+ # A description of the calendar
105
+ attr :description
106
+ # Functions used to determine whether a day is off
107
+ attr :days_off
108
+
109
+ # Builds a calendar
110
+ def initialize(description)
111
+ @description = description
112
+ @days_off = {}
113
+ end
114
+
115
+ # Makes a copy of the calendar
116
+ def copy
117
+ new_cal = Calendar.new @description.dup
118
+
119
+ @days_off.each_pair do |key, day_off|
120
+ new_cal.days_off[key] = day_off
121
+ end
122
+
123
+ new_cal
124
+ end
125
+
126
+ # Provides a function telling that calendar how to evaluate whether a
127
+ # particular day is off. Note that within this block you can use the
128
+ # helper methods week_of, nearest_weekday, next_weekday and
129
+ # previous_weekday:
130
+ #
131
+ # # Thanksgiving (fourth Thursday of November
132
+ # MyCalendar.has_day_off_on :thanksgiving do |date|
133
+ # date.month == NOVEMBER && date.thursday? && week_of(date) == 4
134
+ # end
135
+ #
136
+ # # Christmas (December 25 or nearest weekday)
137
+ # MyCalendar.has_day_off_on :christmas do |date|
138
+ # date.month == DECEMBER && nearest_weekday(date, 25)
139
+ # end
140
+ #
141
+ # # New Year's Day (January 1 or next weekday)
142
+ # MyCalendar.has_day_off_on :new_years do |date|
143
+ # date.month == JANUARY && next_weekday(date, 1)
144
+ # end
145
+ def has_day_off_on(key, &block)
146
+ @days_off[key] = block
147
+ end
148
+
149
+ # Removes the day off for the specified key
150
+ def remove_day_off_for(key)
151
+ @days_off.delete key
152
+ end
153
+
154
+ # A simple helper method for the commonality among most countries that
155
+ # weekends are not workdays or trading days:
156
+ #
157
+ # MyCalendar.has_weekends_off
158
+ def has_weekends_off
159
+ @days_off[:weekends] = Proc.new do |date|
160
+ date.saturday? || date.sunday?
161
+ end
162
+ end
163
+
164
+ # Returns true if the specified date is a holiday or day off
165
+ def day_off?(date)
166
+ @days_off.each_value do |day_off|
167
+ return true if day_off.call(date)
168
+ end
169
+
170
+ false
171
+ end
172
+
173
+ class << self
174
+ # Calculates the week of the month in which the given date falls
175
+ def week_of(date)
176
+ (date.day - 1) / 7 + 1
177
+ end
178
+
179
+ # Whether the provided date falls in the last week of the month
180
+ def last_week?(date)
181
+ case date.month
182
+ when 9, 4, 6, 11
183
+ # Thirty days hath September
184
+ # April, June and November
185
+ date.day > 23
186
+ when 1, 3, 5, 7, 8, 10, 12
187
+ # All the rest have thirty-one
188
+ date.day > 24
189
+ when 2
190
+ # Save February, with twenty-eight days clear
191
+ # And twenty-nine each leap year ;)
192
+ date.day > (date.year % 4 == 0 && date.year % 100 != 0) ? 22 : 21
193
+ end
194
+ end
195
+
196
+ # Calculates whether the provided date is the nearest weekday relative
197
+ # to the provided day of the month
198
+ def nearest_weekday(date, day)
199
+ case date.wday
200
+ when 1 # Monday
201
+ date.day.between?(day, day + 1)
202
+ when 5 # Friday
203
+ date.day.between?(day - 1, day)
204
+ when 0, 6 # Weekends
205
+ false
206
+ else # Tuesday - Thursday
207
+ date.day == day
208
+ end
209
+ end
210
+
211
+ # Calculates whether the provided date is the nearest weekday relative
212
+ # to the provided day of the month
213
+ def next_weekday(date, day)
214
+ case date.wday
215
+ when 1 # Monday
216
+ date.day.between?(day, day + 2)
217
+ when 0, 6 # Weekends
218
+ false
219
+ else # Tuesday - Friday
220
+ date.day == day
221
+ end
222
+ end
223
+
224
+ # Calculates whether the provided date is the nearest weekday relative
225
+ # to the provided day of the month
226
+ def previous_weekday(date, day)
227
+ case date.wday
228
+ when 5 # Friday
229
+ date.day.between?(day - 2, day)
230
+ when 0, 6 # Weekends
231
+ false
232
+ else # Monday - Thursday
233
+ date.day == day
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end