calc_profit 0.1.1
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.
- 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
|
+
|