calc_profit 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/Install +4 -0
- data/README.rdoc +23 -0
- data/Rakefile +13 -0
- data/bin/calc_profit +75 -0
- data/bin/do-demo.sh +7 -0
- data/bin/easters.rb +17 -0
- data/bin/tm-profit.rb +60 -0
- data/calc_profit.gemspec +39 -0
- data/examples/Trans.csv +92 -0
- data/examples/driver.tex +16 -0
- data/examples/lopez.csv +34 -0
- data/examples/profit.lp +93 -0
- data/examples/solution.out +814 -0
- data/lib/calc_profit.rb +14 -0
- data/lib/calc_profit/core_ext.rb +2 -0
- data/lib/calc_profit/core_ext/array.rb +10 -0
- data/lib/calc_profit/core_ext/string.rb +32 -0
- data/lib/calc_profit/latex_table_builder.rb +182 -0
- data/lib/calc_profit/match.rb +57 -0
- data/lib/calc_profit/table.rb +151 -0
- data/lib/calc_profit/transaction.rb +50 -0
- data/lib/calc_profit/transaction_group.rb +181 -0
- data/lib/calc_profit/version.rb +7 -0
- data/test/TestAddress.rb +23 -0
- data/test/TestDBase.rb +47 -0
- data/test/TestDate.rb +267 -0
- data/test/TestLog.rb +19 -0
- data/test/TestName.rb +110 -0
- data/test/TestRetriever.rb +38 -0
- data/test/TestString.rb +12 -0
- data/test/TestTeXString.rb +12 -0
- data/test/TestTransaction.rb +56 -0
- data/test/Trans.csv +92 -0
- data/test/tabdemo-tm.tex +37 -0
- data/test/tabdemo.tex +37 -0
- data/test/tables-tm.tex +235 -0
- data/test/tables.tex +235 -0
- data/test/test_helper.rb +38 -0
- metadata +222 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module CalcProfit
|
4
|
+
class Transaction
|
5
|
+
attr_reader :code, :date, :price, :ref, :info, :raw_shares
|
6
|
+
attr_accessor :shares
|
7
|
+
|
8
|
+
def initialize(trans_hash)
|
9
|
+
if trans_hash[:code] =~ /^\s*[pP]/
|
10
|
+
@code = 'P'
|
11
|
+
elsif trans_hash[:code] =~ /^\s*[sS]/
|
12
|
+
@code = 'S'
|
13
|
+
else
|
14
|
+
raise "Bad code (#{code}) supplied to Transaction constructor."
|
15
|
+
end
|
16
|
+
@price = trans_hash[:price].to_f
|
17
|
+
@shares = trans_hash[:shares].to_f.round
|
18
|
+
@raw_shares = trans_hash[:raw_shares].to_f.round
|
19
|
+
case trans_hash[:date]
|
20
|
+
when String
|
21
|
+
@date = Date.parse(trans_hash[:date])
|
22
|
+
when Date
|
23
|
+
@date = trans_hash[:date]
|
24
|
+
else
|
25
|
+
raise "Bad date (#{date}) supplied to Transaction constructor."
|
26
|
+
end
|
27
|
+
@ref = trans_hash[:ref]
|
28
|
+
@info = trans_hash[:info]
|
29
|
+
end
|
30
|
+
|
31
|
+
def table_row
|
32
|
+
hash = {}
|
33
|
+
hash[:ref] = ref
|
34
|
+
hash[:date] = date.iso
|
35
|
+
hash[:code] = code
|
36
|
+
hash[:shares] = shares.to_s
|
37
|
+
hash[:price] = price.to_s
|
38
|
+
hash[:info] = info
|
39
|
+
hash
|
40
|
+
end
|
41
|
+
|
42
|
+
def purchase?
|
43
|
+
code == 'P'
|
44
|
+
end
|
45
|
+
|
46
|
+
def sale?
|
47
|
+
code == 'S'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
module CalcProfit
|
2
|
+
class TransactionGroup
|
3
|
+
attr_reader :profit, :matches
|
4
|
+
attr_reader :purchase_shares, :sale_shares
|
5
|
+
|
6
|
+
def initialize(table = nil)
|
7
|
+
@purchases = []
|
8
|
+
@sales = []
|
9
|
+
@trans_by_ref = {}
|
10
|
+
@matches = []
|
11
|
+
@profit = 0
|
12
|
+
@purchase_shares = 0
|
13
|
+
@sale_shares = 0
|
14
|
+
if table
|
15
|
+
add_table_transactions(table)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_table_transactions(table)
|
20
|
+
table.rows.each do |row|
|
21
|
+
self << Transaction.new(row)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def <<(t)
|
26
|
+
# Add a new transaction
|
27
|
+
if t.purchase?
|
28
|
+
@purchases << t
|
29
|
+
@trans_by_ref[t.ref.to_s] = t
|
30
|
+
elsif t.sale?
|
31
|
+
@sales << t
|
32
|
+
@trans_by_ref[t.ref.to_s] = t
|
33
|
+
else
|
34
|
+
raise ArgumentError,
|
35
|
+
"Can only add purchase or sale transactions to TransactionGroup."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def match
|
40
|
+
pp = @purchases.dup2
|
41
|
+
ss = @sales.dup2
|
42
|
+
until pp.empty? or ss.empty? do
|
43
|
+
# For each sale remaining, find the remaining purchase
|
44
|
+
# with 6 months having the lowest price and match
|
45
|
+
# to the extent of the lower number of shares
|
46
|
+
break if ss.empty?
|
47
|
+
best_sale = ss.max { |a, b| a.price <=> b.price }
|
48
|
+
eligible_purchases = pp.partition { |p|
|
49
|
+
p.date.within_6mos_of?(best_sale.date) && p.price < best_sale.price
|
50
|
+
}
|
51
|
+
eligible_purchases = eligible_purchases[0]
|
52
|
+
if eligible_purchases.empty?
|
53
|
+
ss.delete(best_sale)
|
54
|
+
next
|
55
|
+
end
|
56
|
+
best_prch = eligible_purchases.min { |a, b| a.price <=> b.price }
|
57
|
+
# Take number of shares as the smaller of the sale or buy
|
58
|
+
if best_sale.shares >= best_prch.shares
|
59
|
+
s = Transaction.new(code: 'S', date: best_sale.date, price: best_sale.price,
|
60
|
+
shares: best_prch.shares, ref: best_sale.ref,
|
61
|
+
info: best_sale.info)
|
62
|
+
p = Transaction.new(code: 'P', date: best_prch.date, price: best_prch.price,
|
63
|
+
shares: best_prch.shares, ref: best_prch.ref,
|
64
|
+
info: best_prch.info)
|
65
|
+
best_sale.shares -= best_prch.shares
|
66
|
+
pp.delete(best_prch)
|
67
|
+
else
|
68
|
+
s = Transaction.new(code: 'S', date: best_sale.date, price: best_sale.price,
|
69
|
+
shares: best_sale.shares, ref: best_sale.ref,
|
70
|
+
info: best_sale.info)
|
71
|
+
p = Transaction.new(code: 'P', date: best_prch.date, price: best_prch.price,
|
72
|
+
shares: best_sale.shares, ref: best_prch.ref,
|
73
|
+
info: best_prch.info)
|
74
|
+
best_prch.shares -= best_sale.shares
|
75
|
+
ss.delete(best_sale)
|
76
|
+
end
|
77
|
+
m = Match.new(p, s)
|
78
|
+
unless m.profit < 0.01
|
79
|
+
@matches << m
|
80
|
+
@profit += m.profit
|
81
|
+
@purchase_shares += p.shares
|
82
|
+
@sale_shares += s.shares
|
83
|
+
end
|
84
|
+
end
|
85
|
+
@matches.empty? ? nil : match_table
|
86
|
+
end
|
87
|
+
|
88
|
+
def match_table
|
89
|
+
table = Table.new
|
90
|
+
@matches.each do |m|
|
91
|
+
table << m.table_row
|
92
|
+
end
|
93
|
+
table
|
94
|
+
end
|
95
|
+
|
96
|
+
def transaction_table
|
97
|
+
trans = @purchases + @sales
|
98
|
+
table = Table.new
|
99
|
+
trans.each do |tr|
|
100
|
+
table << tr.table_row
|
101
|
+
end
|
102
|
+
table
|
103
|
+
end
|
104
|
+
|
105
|
+
# This method attempts to find an optimal, profit-maximizing set of
|
106
|
+
# matches by formulating a transportation problem and passing it to
|
107
|
+
# lp_solve. Requires lp_solve, which on ubuntu can be installed with
|
108
|
+
#
|
109
|
+
# $ apt-get install lp_solve
|
110
|
+
def tp_solve
|
111
|
+
lpf = File.new('profit.lp', 'w')
|
112
|
+
|
113
|
+
# Write the objective function
|
114
|
+
lpf.print("max: ")
|
115
|
+
penalty = -1000.0
|
116
|
+
have_eqn = false
|
117
|
+
@purchases.each do |p|
|
118
|
+
@sales.each do |s|
|
119
|
+
if p.date.within_6mos_of?(s.date) and s.price > p.price
|
120
|
+
profit = s.price - p.price
|
121
|
+
lpf.print("#{profit} X_#{p.ref}@@#{s.ref} + ")
|
122
|
+
have_eqn = true
|
123
|
+
else
|
124
|
+
lpf.print("#{penalty} X_#{p.ref}@@#{s.ref} + ")
|
125
|
+
have_eqn = true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
lpf.print("0;\n\n") if have_eqn
|
130
|
+
|
131
|
+
# Write the purchase contraints
|
132
|
+
@purchases.each do |p|
|
133
|
+
lpf.print("P_#{p.ref}: ")
|
134
|
+
@sales.each do |s|
|
135
|
+
lpf.print("X_#{p.ref}@@#{s.ref} + ")
|
136
|
+
end
|
137
|
+
lpf.print("0 <= #{p.shares};\n")
|
138
|
+
end
|
139
|
+
|
140
|
+
# Write the sales contraints
|
141
|
+
@sales.each do |s|
|
142
|
+
lpf.print("S_#{s.ref}: ")
|
143
|
+
@purchases.each do |p|
|
144
|
+
lpf.print("X_#{p.ref}@@#{s.ref} + ")
|
145
|
+
end
|
146
|
+
lpf.print("0 <= #{s.shares};\n")
|
147
|
+
end
|
148
|
+
lpf.close
|
149
|
+
|
150
|
+
# Now, pass the file into lp_solve and parse the solution
|
151
|
+
calculated_profit = 0.0
|
152
|
+
IO.popen("lp_solve profit.lp") do |sol|
|
153
|
+
#File.open("profit.sol") do |sol|
|
154
|
+
sol.each do |line|
|
155
|
+
next if line =~ /^\s*$/
|
156
|
+
if line =~ /^Value of objective function:\s+([-+e.\d]+)/
|
157
|
+
calculated_profit = $1.to_f
|
158
|
+
elsif line =~ /^X_(\S*)@@(\S*)\s+(\d+)$/
|
159
|
+
next if $3.to_f == 0.0
|
160
|
+
p = @trans_by_ref[$1].dup
|
161
|
+
s = @trans_by_ref[$2].dup
|
162
|
+
p.shares = s.shares = $3.to_f
|
163
|
+
if s.price - p.price < 0.01
|
164
|
+
STDERR.print "Sale #{s.ref} @ #{s.price} and Purchase #{p.ref} @ #{p.price} has no profit.\n"
|
165
|
+
else
|
166
|
+
m = Match.new(p, s)
|
167
|
+
@matches << m
|
168
|
+
@profit += m.profit
|
169
|
+
@purchase_shares += p.shares
|
170
|
+
@sale_shares += s.shares
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
unless calculated_profit == @profit
|
176
|
+
STDERR.print "Warning: profit calculated by lp_solve was #{calculated_profit}; by matches was #{@profit}\n"
|
177
|
+
end
|
178
|
+
!@matches.empty?
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
data/test/TestAddress.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class AddressTest < ActiveSupport::TestCase
|
4
|
+
def test_easy
|
5
|
+
a = Address.new('14625 Dearborn', '', 'Overland Park', 'KS', '66223', '')
|
6
|
+
assert_equal('14625 Dearborn', a.street1, "Bad parse of street1")
|
7
|
+
assert_equal('', a.street2, "Bad parse of street2")
|
8
|
+
assert_equal('Overland Park', a.city, "Bad parse of city")
|
9
|
+
assert_equal('KS', a.state, "Bad parse of state")
|
10
|
+
assert_equal('66223', a.zip, "Bad parse of zip")
|
11
|
+
assert_equal('United States', a.country, "Bad parse of country")
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_hard
|
15
|
+
a = Address.new('14625 Dearborn', '', 'Overland Park, KS 66223')
|
16
|
+
assert_equal('14625 Dearborn', a.street1, "Bad parse of street1")
|
17
|
+
assert_equal('', a.street2, "Bad parse of street2")
|
18
|
+
assert_equal('Overland Park', a.city, "Bad parse of city")
|
19
|
+
assert_equal('KS', a.state, "Bad parse of state")
|
20
|
+
assert_equal('66223', a.zip, "Bad parse of zip")
|
21
|
+
assert_equal('United States', a.country, "Bad parse of country")
|
22
|
+
end
|
23
|
+
end
|
data/test/TestDBase.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'Section16/DBase' unless defined? $db
|
3
|
+
|
4
|
+
begin
|
5
|
+
$db.do('DROP TABLE junk') if $db.tables.include?('junk')
|
6
|
+
res = $db.do('CREATE TABLE junk (i integer primary key, s text);')
|
7
|
+
rescue DBI::DatabaseError => e
|
8
|
+
puts "Error: #{e.errstr}"
|
9
|
+
end
|
10
|
+
|
11
|
+
class TestDate < Test::Unit::TestCase
|
12
|
+
|
13
|
+
def test_dbase_is_up
|
14
|
+
assert($db.connected?)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_insert
|
18
|
+
$db.do("INSERT INTO junk (i, s) VALUES (1, 'a');")
|
19
|
+
$db.do("INSERT INTO junk (i, s) VALUES (2, 'b');")
|
20
|
+
$db.do("INSERT INTO junk (i, s) VALUES (3, 'c');")
|
21
|
+
$db.do("INSERT INTO junk (i, s) VALUES (4, 'd');")
|
22
|
+
rows = $db.select_all('SELECT * FROM junk;')
|
23
|
+
assert_equal(4, rows.length)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_odb
|
27
|
+
$odb.trace(2)
|
28
|
+
assert($odb.ping, "No ping response to $odb")
|
29
|
+
assert($odb.tables.include?('owner'), "Can't find owner table")
|
30
|
+
assert($odb.tables.include?('issuer'), "Can't find issuer table")
|
31
|
+
assert($odb.tables.include?('own_iss'), "Can't find own_iss table")
|
32
|
+
assert($odb.tables.include?('own_fil'), "Can't find own_fil table")
|
33
|
+
end
|
34
|
+
|
35
|
+
class Junk < ActiveRecord::Base
|
36
|
+
set_table_name('junk')
|
37
|
+
set_primary_key('i')
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_zactive_record
|
41
|
+
assert_equal(Junk.find(2, 4).length, 2,
|
42
|
+
"Active record class didn't find 2 Junks")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
|
data/test/TestDate.rb
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
# Code Generated by ZenTest v. 2.3.0
|
2
|
+
# classname: asrt / meth = ratio%
|
3
|
+
# Date: 0 / 15 = 0.00%
|
4
|
+
|
5
|
+
require 'test/unit' unless defined? $ZENTEST and $ZENTEST
|
6
|
+
require 'Section16/Date'
|
7
|
+
|
8
|
+
class TestDate < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def test_nth_wday_in_year_month(n, wday, year, month)
|
11
|
+
assert_equals(Date.new(1957, 30, 11),
|
12
|
+
Date.nth_wday_in_year_month(5, 6, 1957, 11),
|
13
|
+
"1957-11-30 is 5th Saturday in November, 1957")
|
14
|
+
assert_equals(Date.new(1957, 30, 11),
|
15
|
+
Date.nth_wday_in_year_month(-1, 6, 1957, 11),
|
16
|
+
"1957-11-30 is last Saturday in November, 1957")
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_last_day_in_year_month
|
20
|
+
assert_equal(29, Date.last_day_in_year_month(2000, 2),
|
21
|
+
"February 28 is last day in Feb, 2000")
|
22
|
+
assert_equal(28, Date.last_day_in_year_month(1900, 2),
|
23
|
+
"February 29 is last day in Feb, 1900")
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_weekend_eh
|
27
|
+
assert(!Date.new(1985, 7, 16).weekend?,
|
28
|
+
"1985-07-16 is not a weekend day")
|
29
|
+
assert(Date.new(1957, 9, 22).weekend?,
|
30
|
+
"1957-09-22 is a weekend day")
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_easter_this_year
|
34
|
+
assert_equal(Date.new(2000, 4, 23),
|
35
|
+
Date.new(2000, 9, 22).easter_this_year,
|
36
|
+
"2000-4-23 should be Easter in 2000")
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_easter_eh
|
40
|
+
d = Date.new(2000, 8, 18)
|
41
|
+
assert(!d.easter?, "#{d} should not be Easter")
|
42
|
+
assert(Date.new(2000, 4, 23).easter?,
|
43
|
+
"2000-04-23 should be Easter")
|
44
|
+
assert(Date.new(1988, 4, 3).easter?,
|
45
|
+
"1988-04-03 should be Easter")
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_nth_wday_in_month_eh
|
49
|
+
assert(Date.new(1957, 11, 30).nth_wday_in_month?(5, 6, 11),
|
50
|
+
'1957-11-30 is the 5th Saturday in November of its year')
|
51
|
+
assert(Date.new(1957, 9, 22).nth_wday_in_month?(4, 0, 9),
|
52
|
+
'1957-09-22 is the 4th Sunday in September of its year')
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_fed_fixed_holiday_eh
|
56
|
+
assert(Date.new(1988, 1, 1).fed_fixed_holiday?,
|
57
|
+
"Feds celebrate New Years Day")
|
58
|
+
assert(Date.new(1988, 7, 4).fed_fixed_holiday?,
|
59
|
+
"Feds celebrate Independence Day")
|
60
|
+
assert(Date.new(1988, 11, 11).fed_fixed_holiday?,
|
61
|
+
"Feds celebrate Veterans Day")
|
62
|
+
assert(Date.new(1988, 12, 25).fed_fixed_holiday?,
|
63
|
+
"Feds celebrate Christmas Day")
|
64
|
+
assert(Date.new(1988, 12, 31).fed_fixed_holiday?,
|
65
|
+
"Feds celebrate New Years Eve")
|
66
|
+
(1988..1990).each do |y|
|
67
|
+
h = []
|
68
|
+
(1..12).each do |m|
|
69
|
+
(1..Date.last_day_in_year_month(y, m)).each do |d|
|
70
|
+
d = Date.new(y, m, d)
|
71
|
+
h.push(d) if d.fed_fixed_holiday?
|
72
|
+
end
|
73
|
+
end
|
74
|
+
assert_equal(5, h.length,
|
75
|
+
"Found #{h.length} fixed holidays in #{y}")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_fed_moveable_feast_eh
|
80
|
+
assert(Date.new(1988, 1, 18).fed_moveable_feast?,
|
81
|
+
"Feds celebrate MLK Day")
|
82
|
+
assert(Date.new(1988, 2, 15).fed_moveable_feast?,
|
83
|
+
"Feds celebrate Washington's Birthday")
|
84
|
+
assert(Date.new(1988, 5, 30).fed_moveable_feast?,
|
85
|
+
"Feds celebrate Memorial Day")
|
86
|
+
assert(Date.new(1988, 9, 5).fed_moveable_feast?,
|
87
|
+
"Feds celebrate Labor Day")
|
88
|
+
assert(Date.new(1988, 10, 10).fed_moveable_feast?,
|
89
|
+
"Feds celebrate Columbus Day")
|
90
|
+
assert(Date.new(1988, 11, 24).fed_moveable_feast?,
|
91
|
+
"Feds celebrate Tahnksgiving")
|
92
|
+
(1988..1990).each do |y|
|
93
|
+
h = []
|
94
|
+
(1..12).each do |m|
|
95
|
+
(1..Date.last_day_in_year_month(y, m)).each do |d|
|
96
|
+
d = Date.new(y, m, d)
|
97
|
+
h.push(d) if d.fed_moveable_feast?
|
98
|
+
end
|
99
|
+
end
|
100
|
+
assert_equal(6, h.length,
|
101
|
+
"Found #{h.length} floating holidays in #{y}")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_fed_holiday_eh
|
106
|
+
assert(Date.new(1988, 1, 1).fed_holiday?,
|
107
|
+
"Feds celebrate New Years Day")
|
108
|
+
assert(Date.new(1988, 1, 18).fed_holiday?,
|
109
|
+
"Feds celebrate MLK Day")
|
110
|
+
assert(Date.new(1988, 2, 15).fed_holiday?,
|
111
|
+
"Feds celebrate Washington's Birthday")
|
112
|
+
assert(Date.new(1988, 5, 30).fed_holiday?,
|
113
|
+
"Feds celebrate Memorial Day")
|
114
|
+
assert(Date.new(1988, 7, 4).fed_holiday?,
|
115
|
+
"Feds celebrate Independence Day")
|
116
|
+
assert(Date.new(1988, 9, 5).fed_holiday?,
|
117
|
+
"Feds celebrate Labor Day")
|
118
|
+
assert(Date.new(1988, 10, 10).fed_holiday?,
|
119
|
+
"Feds celebrate Columbus Day")
|
120
|
+
assert(Date.new(1988, 11, 24).fed_holiday?,
|
121
|
+
"Feds celebrate Tahnksgiving")
|
122
|
+
assert(Date.new(1988, 11, 11).fed_holiday?,
|
123
|
+
"Feds celebrate Veterans Day")
|
124
|
+
assert(Date.new(1988, 12, 25).fed_holiday?,
|
125
|
+
"Feds celebrate Christmas Day")
|
126
|
+
assert(Date.new(1988, 12, 31).fed_holiday?,
|
127
|
+
"Feds celebrate New Years Eve")
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_nyse_fixed_holiday_eh
|
131
|
+
assert(Date.new(1988, 1, 1).nyse_fixed_holiday?,
|
132
|
+
"NYSE celebrates New Years Day")
|
133
|
+
assert(Date.new(1988, 7, 4).nyse_fixed_holiday?,
|
134
|
+
"NYSE celebrates Independence Day")
|
135
|
+
assert(Date.new(1988, 12, 25).nyse_fixed_holiday?,
|
136
|
+
"NYSE celebrates Christmas Day")
|
137
|
+
(1988..1990).each do |y|
|
138
|
+
h = []
|
139
|
+
(1..12).each do |m|
|
140
|
+
(1..Date.last_day_in_year_month(y, m)).each do |d|
|
141
|
+
d = Date.new(y, m, d)
|
142
|
+
h.push(d) if d.nyse_fixed_holiday?
|
143
|
+
end
|
144
|
+
end
|
145
|
+
assert_equal(3, h.length,
|
146
|
+
"Found #{h.length} fixed NYSE holidays in #{y}")
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_nyse_moveable_feast_eh
|
151
|
+
assert(Date.new(1988, 1, 18).nyse_moveable_feast?,
|
152
|
+
"NYSE celebrates MLK Day")
|
153
|
+
assert(Date.new(1988, 2, 15).nyse_moveable_feast?,
|
154
|
+
"NYSE celebrates Washington's Birthday")
|
155
|
+
assert(Date.new(1988, 4, 1).nyse_moveable_feast?,
|
156
|
+
"NYSE celebrates Good Friday")
|
157
|
+
assert(Date.new(1988, 5, 30).nyse_moveable_feast?,
|
158
|
+
"NYSE celebrates Memorial Day")
|
159
|
+
assert(Date.new(1988, 9, 5).nyse_moveable_feast?,
|
160
|
+
"NYSE celebrates Labor Day")
|
161
|
+
assert(Date.new(1988, 11, 24).nyse_moveable_feast?,
|
162
|
+
"NYSE celebrates Thanksgiving")
|
163
|
+
(1988..1990).each do |y|
|
164
|
+
h = []
|
165
|
+
(1..12).each do |m|
|
166
|
+
(1..Date.last_day_in_year_month(y, m)).each do |d|
|
167
|
+
d = Date.new(y, m, d)
|
168
|
+
h.push(d) if d.nyse_moveable_feast?
|
169
|
+
end
|
170
|
+
end
|
171
|
+
assert_equal(6, h.length,
|
172
|
+
"Found #{h.length} floating holidays in #{y}")
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_nyse_holiday_eh
|
177
|
+
assert(Date.new(1988, 1, 1).nyse_holiday?,
|
178
|
+
"NYSE celebrates New Years Day")
|
179
|
+
assert(Date.new(1988, 7, 4).nyse_holiday?,
|
180
|
+
"NYSE celebrates Independence Day")
|
181
|
+
assert(Date.new(1988, 12, 25).nyse_holiday?,
|
182
|
+
"NYSE celebrates Christmas Day")
|
183
|
+
assert(Date.new(1988, 1, 18).nyse_holiday?,
|
184
|
+
"NYSE celebrates MLK Day")
|
185
|
+
assert(Date.new(1988, 2, 15).nyse_holiday?,
|
186
|
+
"NYSE celebrates Washington's Birthday")
|
187
|
+
assert(Date.new(1988, 4, 1).nyse_holiday?,
|
188
|
+
"NYSE celebrates Good Friday")
|
189
|
+
assert(Date.new(1988, 5, 30).nyse_holiday?,
|
190
|
+
"NYSE celebrates Memorial Day")
|
191
|
+
assert(Date.new(1988, 9, 5).nyse_holiday?,
|
192
|
+
"NYSE celebrates Labor Day")
|
193
|
+
assert(Date.new(1988, 11, 24).nyse_holiday?,
|
194
|
+
"NYSE celebrates Thanksgiving")
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_fed_workday_eh
|
198
|
+
assert(!Date.new(1988, 1, 1).fed_workday?,
|
199
|
+
"Feds celebrate New Years Day")
|
200
|
+
assert(!Date.new(1988, 1, 18).fed_workday?,
|
201
|
+
"Feds celebrate MLK Day")
|
202
|
+
assert(!Date.new(1988, 2, 15).fed_workday?,
|
203
|
+
"Feds celebrate Washington's Birthday")
|
204
|
+
assert(!Date.new(1988, 5, 30).fed_workday?,
|
205
|
+
"Feds celebrate Memorial Day")
|
206
|
+
assert(!Date.new(1988, 7, 4).fed_workday?,
|
207
|
+
"Feds celebrate Independence Day")
|
208
|
+
assert(!Date.new(1988, 9, 5).fed_workday?,
|
209
|
+
"Feds celebrate Labor Day")
|
210
|
+
assert(!Date.new(1988, 10, 10).fed_workday?,
|
211
|
+
"Feds celebrate Columbus Day")
|
212
|
+
assert(!Date.new(1988, 11, 24).fed_workday?,
|
213
|
+
"Feds celebrate Tahnksgiving")
|
214
|
+
assert(!Date.new(1988, 11, 11).fed_workday?,
|
215
|
+
"Feds celebrate Veterans Day")
|
216
|
+
assert(!Date.new(1988, 12, 25).fed_workday?,
|
217
|
+
"Feds celebrate Christmas Day")
|
218
|
+
assert(!Date.new(1988, 12, 31).fed_workday?,
|
219
|
+
"Feds celebrate New Years Eve")
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_nyse_workday_eh
|
223
|
+
assert(!Date.new(1988, 1, 1).nyse_workday?,
|
224
|
+
"NYSE celebrates New Years Day")
|
225
|
+
assert(!Date.new(1988, 7, 4).nyse_workday?,
|
226
|
+
"NYSE celebrates Independence Day")
|
227
|
+
assert(!Date.new(1988, 12, 25).nyse_workday?,
|
228
|
+
"NYSE celebrates Christmas Day")
|
229
|
+
assert(!Date.new(1988, 1, 18).nyse_workday?,
|
230
|
+
"NYSE celebrates MLK Day")
|
231
|
+
assert(!Date.new(1988, 2, 15).nyse_workday?,
|
232
|
+
"NYSE celebrates Washington's Birthday")
|
233
|
+
assert(!Date.new(1988, 4, 1).nyse_workday?,
|
234
|
+
"NYSE celebrates Good Friday")
|
235
|
+
assert(!Date.new(1988, 5, 30).nyse_workday?,
|
236
|
+
"NYSE celebrates Memorial Day")
|
237
|
+
assert(!Date.new(1988, 9, 5).nyse_workday?,
|
238
|
+
"NYSE celebrates Labor Day")
|
239
|
+
assert(!Date.new(1988, 11, 24).nyse_workday?,
|
240
|
+
"NYSE celebrates Thanksgiving")
|
241
|
+
end
|
242
|
+
|
243
|
+
def test_next_nyse_workday
|
244
|
+
assert_equal(Date.new(1988, 3, 31).next_nyse_workday,
|
245
|
+
Date.new(1988, 4, 4),
|
246
|
+
"Next NYSE workday after 1988-03-31 is 1988-04-04")
|
247
|
+
end
|
248
|
+
|
249
|
+
def test_next_fed_workday
|
250
|
+
assert_equal(Date.new(1988, 3, 31).next_fed_workday,
|
251
|
+
Date.new(1988, 4, 1),
|
252
|
+
"Next Fed workday after 1988-03-31 is 1988-04-01")
|
253
|
+
end
|
254
|
+
|
255
|
+
def test_iso
|
256
|
+
assert_equal(Date.new(1957, 9, 2).iso, "1957-09-02", "Bad iso format")
|
257
|
+
end
|
258
|
+
|
259
|
+
def test_num
|
260
|
+
assert_equal(Date.new(1957, 9, 2).num, "19570902", "Bad num format")
|
261
|
+
end
|
262
|
+
|
263
|
+
def test_within6
|
264
|
+
assert(Date.new(1957, 9, 22).within_6mos_of?(Date.new(1957, 6, 5)))
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|