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/option.c +542 -46
- data/ext/rupee/rupee.h +18 -2
- data/ext/rupee/statistics.c +133 -0
- data/lib/rupee.rb +1 -0
- data/lib/rupee/calendar.rb +238 -0
- data/lib/rupee/calendar/japan.rb +54 -0
- data/lib/rupee/calendar/us.rb +59 -0
- data/lib/rupee/option.rb +8 -0
- data/lib/rupee/quote.rb +37 -8
- data/lib/rupee/version.rb +1 -1
- data/spec/native/option_spec.rb +118 -31
- data/spec/ruby/calendar_spec.rb +85 -0
- data/test_rubies +0 -0
- metadata +49 -49
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
|
30
|
-
double
|
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 */
|
data/ext/rupee/statistics.c
CHANGED
@@ -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
@@ -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
|