rupee 0.2.1 → 0.2.2

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