sec_query 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,45 +1,50 @@
1
- module SecQuery
2
- class Filing
3
- attr_accessor :cik, :title, :summary, :link, :term, :date, :file_id
4
- def initialize(filing)
5
- @cik = filing[:cik]
6
- @title = filing[:title]
7
- @summary = filing[:summary]
8
- @link = filing[:link]
9
- @term = filing[:term]
10
- @date = filing[:date]
11
- @file_id = filing[:file_id]
12
- end
13
-
14
-
15
- def self.find(entity, start, count, limit)
1
+ # encoding: UTF-8
16
2
 
17
- if start == nil; start = 0; end
18
- if count == nil; count = 80; end
19
- url ="http://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK="+entity[:cik]+"&output=atom&count="+count.to_s+"&start="+start.to_s
20
- response = Entity.query(url)
21
- doc = Hpricot::XML(response)
22
- entries = doc.search(:entry);
23
- query_more = false;
24
- for entry in entries
25
- query_more = true;
26
- filing={}
27
- filing[:cik] = entity[:cik]
28
- filing[:title] = (entry/:title).innerHTML
29
- filing[:summary] = (entry/:summary).innerHTML
30
- filing[:link] = (entry/:link)[0].get_attribute("href")
31
- filing[:term] = (entry/:category)[0].get_attribute("term")
32
- filing[:date] = (entry/:updated).innerHTML
33
- filing[:file_id] = (entry/:id).innerHTML.split("=").last
3
+ module SecQuery
4
+ # => SecQuery::Filing
5
+ # SecQuery::Filing requests and parses filings for any given SecQuery::Entity
6
+ class Filing
7
+ COLUMNS = :cik, :title, :summary, :link, :term, :date, :file_id
8
+ attr_accessor(*COLUMNS)
34
9
 
35
- entity[:filings] << Filing.new(filing)
36
- end
37
- if query_more and limit == nil || query_more and !limit
38
- Filing.find(entity, start+count, count, limit);
39
- else
40
- return entity
41
- end
42
- end
10
+ def initialize(filing)
11
+ COLUMNS.each do |column|
12
+ instance_variable_set("@#{ column }", filing[column])
13
+ end
43
14
  end
44
15
 
16
+ def self.find(entity, start, count, limit)
17
+ start ||= 0
18
+ count ||= 80
19
+ url = SecURI.browse_edgar_uri({
20
+ action: :getcompany,
21
+ CIK: entity[:cik],
22
+ output: :atom,
23
+ count: count,
24
+ start: start
25
+ })
26
+ response = Entity.query(url)
27
+ doc = Hpricot::XML(response)
28
+ entries = doc.search(:entry)
29
+ query_more = false
30
+ entries.each do |entry|
31
+ query_more = true
32
+ filing = {}
33
+ filing[:cik] = entity[:cik]
34
+ filing[:title] = (entry/:title).innerHTML
35
+ filing[:summary] = (entry/:summary).innerHTML
36
+ filing[:link] = (entry/:link)[0].get_attribute('href')
37
+ filing[:term] = (entry/:category)[0].get_attribute('term')
38
+ filing[:date] = (entry/:updated).innerHTML
39
+ filing[:file_id] = (entry/:id).innerHTML.split('=').last
40
+
41
+ entity[:filings] << Filing.new(filing)
42
+ end
43
+ if (query_more && limit.nil?) || (query_more && !limit)
44
+ Filing.find(entity, start + count, count, limit)
45
+ else
46
+ return entity
47
+ end
48
+ end
49
+ end
45
50
  end
@@ -1,63 +1,72 @@
1
+ # encoding: UTF-8
2
+
1
3
  module SecQuery
2
- class Relationship
3
-
4
- ## Relationships are Owner / Issuer Relationships between Entities, forged by Transactions.
5
-
6
- attr_accessor :name, :position, :date, :cik
7
- def initialize(relationship)
8
- @cik = relationship[:cik];
9
- @name = relationship[:name];
10
- @position = relationship[:position];
11
- date = relationship[:date].split("-")
12
- @date = Time.utc(date[0],date[1],date[2].to_i)
13
- end
14
-
15
-
16
- def self.find(entity)
17
- @relationships =[]
18
-
19
- if entity[:doc] != nil
20
- doc = entity[:doc]
21
- elsif entity[:cik] != nil
22
- doc = Entity.document(entity[:cik])[0]
23
- end
24
-
25
- type = "Ownership Reports for Issuers:"
26
- lines = doc.search("//table").search("//td").search("b[text()*='"+type+"']")
27
- if lines.empty?
28
- type = "Ownership Reports from:"
29
- lines = doc.search("//table").search("//td").search("b[text()*='"+type+"']");
30
- end
31
- if !lines.empty?
32
- relationship = {}
33
- lines= lines[0].parent.search("//table")[0].search("//tr")
34
- for line in lines
35
- link = line.search("//a")[0]
36
- if link.innerHTML != "Owner" and link.innerHTML != "Issuer"
37
- relationship[:name] = link.innerHTML;
38
- relationship[:cik] = line.search("//td")[1].search("//a").innerHTML
39
- relationship[:date] = position = line.search("//td")[2].innerHTML
40
- relationship[:position] = line.search("//td")[3].innerHTML
41
- @relationship = Relationship.new(relationship)
42
- @relationships << @relationship
43
-
44
- end
45
- end
46
- return @relationships
47
- else
48
- return false
49
- end
4
+ # => SecQuery::Relationship
5
+ # Relationships are Owner / Issuer Relationships between Entities,
6
+ # forged by Transactions.
7
+ class Relationship
8
+ COLUMNS = :name, :position, :cik
9
+ attr_accessor(*COLUMNS, :date)
10
+
11
+ def initialize(relationship)
12
+ COLUMNS.each do |column|
13
+ instance_variable_set("@#{ column }", relationship[column])
14
+ end
15
+ date = relationship[:date].split('-')
16
+ @date = Time.utc(date[0], date[1], date[2].to_i)
17
+ end
18
+
19
+ def self.find(entity)
20
+ @relationships = []
21
+
22
+ if entity[:doc]
23
+ doc = entity[:doc]
24
+ elsif entity[:cik]
25
+ doc = Entity.document(entity[:cik])[0]
26
+ end
27
+
28
+ type = 'Ownership Reports for Issuers:'
29
+ lines = doc.search('//table').search('//td').search("b[text()*='"+type+"']")
30
+ if lines.empty?
31
+ type = 'Ownership Reports from:'
32
+ lines = doc.search('//table').search('//td').search("b[text()*='"+type+"']")
33
+ end
34
+
35
+ return false if lines.empty?
36
+
37
+ relationship = {}
38
+ lines = lines[0].parent.search('//table')[0].search('//tr')
39
+ lines.each do |line|
40
+ link = line.search('//a')[0]
41
+ if link.innerHTML != 'Owner' && link.innerHTML != 'Issuer'
42
+ relationship[:name] = link.innerHTML
43
+ relationship[:cik] = line.search('//td')[1].search('//a').innerHTML
44
+ relationship[:date] = line.search('//td')[2].innerHTML
45
+ relationship[:position] = line.search('//td')[3].innerHTML
46
+ @relationships << Relationship.new(relationship)
50
47
  end
51
- def self.print(relationships)
52
- if relationships
53
- puts "\n\t"+relationships[1]+"\n"
54
- printf("\t%-30s %-10s %-40s %-10s\n\n","Entity", "CIK", "Position", "Date")
55
- for relationship in issuer[:relationships]
56
- printf("\t%-30s %-10s %-40s %-10s\n",relationship.name, relationship.cik, relationship.position, relationship.date)
57
- end
58
- else
59
- puts "No relationships"
60
- end
48
+ end
49
+ @relationships
50
+ end
51
+
52
+ def self.print(relationships)
53
+ if relationships
54
+ puts "\n\t#{ relationships[1] }\n"
55
+ printf("\t%-30s %-10s %-40s %-10s\n\n",
56
+ 'Entity',
57
+ 'CIK',
58
+ 'Position',
59
+ 'Date')
60
+ issuer[:relationships].each do |relationship|
61
+ printf("\t%-30s %-10s %-40s %-10s\n",
62
+ relationship.name,
63
+ relationship.cik,
64
+ relationship.position,
65
+ relationship.date)
61
66
  end
67
+ else
68
+ puts 'No relationships'
69
+ end
62
70
  end
71
+ end
63
72
  end
@@ -0,0 +1,98 @@
1
+ # encoding: UTF-8
2
+ require 'debugger'
3
+
4
+ module SecQuery
5
+ class SecURI
6
+ attr_accessor :host, :scheme, :path, :query_values
7
+
8
+ def self.browse_edgar_uri(args = nil)
9
+ build_with_path('/browse-edgar', args)
10
+ end
11
+
12
+ def self.ownership_display_uri(args = nil)
13
+ build_with_path('/own-disp', args)
14
+ end
15
+
16
+ def self.build_with_path(path, args)
17
+ instance = SecURI.new
18
+ instance.path += path
19
+ return instance if args.nil?
20
+ options = send("handle_#{ args.class.to_s.underscore }_args", args)
21
+ instance.query_values = options
22
+ instance
23
+ end
24
+
25
+ private_class_method :build_with_path
26
+
27
+ def self.handle_string_args(string_arg)
28
+ options = {}
29
+ # Uhhhg. I hate using Float here. HACK
30
+ begin Float(string_arg)
31
+ options[:CIK] = string_arg
32
+ rescue
33
+ if string_arg.length <= 4
34
+ options[:CIK] = string_arg
35
+ else
36
+ options[:company] = string_arg.gsub(/[(,?!\''"":.)]/, '')
37
+ end
38
+ end
39
+ options
40
+ end
41
+
42
+ private_class_method :handle_string_args
43
+
44
+ def self.handle_hash_args(hash_arg)
45
+ options = hash_arg
46
+ if hash_arg[:symbol] || hash_arg[:cik]
47
+ options[:CIK] = (hash_arg[:symbol] || hash_arg[:cik])
48
+ return options
49
+ end
50
+ options[:company] = company_name_from_hash_args(hash_arg)
51
+ options
52
+ end
53
+
54
+ private_class_method :handle_hash_args
55
+
56
+ def self.company_name_from_hash_args(args)
57
+ return "#{ args[:last] } #{ args[:first] }" if args[:first] && args[:last]
58
+ return args[:name].gsub(/[(,?!\''"":.)]/, '') if args[:name]
59
+ end
60
+
61
+ private_class_method :company_name_from_hash_args
62
+
63
+ def initialize
64
+ self.host = 'www.sec.gov'
65
+ self.scheme = 'http'
66
+ self.path = 'cgi-bin'
67
+ end
68
+
69
+ def []=(key, value)
70
+ query_values[key] = value
71
+ self
72
+ end
73
+
74
+ def output_atom
75
+ query_values.merge!(output: 'atom')
76
+ self
77
+ end
78
+
79
+ def to_s
80
+ uri.to_s
81
+ end
82
+
83
+ def to_str
84
+ to_s
85
+ end
86
+
87
+ private
88
+
89
+ def uri
90
+ Addressable::URI.new(
91
+ host: host,
92
+ scheme: scheme,
93
+ path: path,
94
+ query_values: query_values
95
+ )
96
+ end
97
+ end
98
+ end
@@ -1,89 +1,106 @@
1
+ # encoding: UTF-8
2
+
1
3
  module SecQuery
2
- class Transaction
4
+ # => SecQuery::Transactions
5
+ # SecQuery filings for any given SecQuery::Entity instance.
6
+ class Transaction
7
+ STR_COLUMNS = :code, :form, :type, :modes, :owner_cik, :security_name,
8
+ :deemed, :exercise, :nature, :derivative, :exercised,
9
+ :reporting_owner
3
10
 
4
- attr_accessor :filing_number, :code, :date, :reporting_owner, :form, :type, :modes, :shares, :price, :owned, :number, :owner_cik, :security_name, :deemed, :exercise, :nature, :derivative, :underlying_1, :exercised, :underlying_2, :expires, :underlying_3
5
-
6
- def initialize(transaction)
7
- @filing_number = transaction[:form].split("/")[-2][0..19]
8
- @code = transaction[:code]
9
- if transaction[:date] != nil and transaction[:date] != "-"
10
- date = transaction[:date].split("-")
11
- @date = Time.utc(date[0],date[1],date[2])
12
- end
13
- @reporting_owner = transaction[:reporting_owner]
14
- @form = transaction[:form]
15
- @type = transaction[:type]
16
- @modes = transaction[:modes]
17
- @shares = transaction[:shares].to_f
18
- @price = transaction[:price].gsub("$", "").to_f
19
- @owned = transaction[:owned].to_f
20
- @number = transaction[:number].to_i
21
- @owner_cik = transaction[:owner_cik]
22
- @security_name = transaction[:security_name]
23
- @deemed = transaction[:deemed]
24
- @exercise = transaction[:exercise]
25
- @nature = transaction[:nature]
26
- @derivative = transaction[:derivative]
27
- @underlying_1 = transaction[:underlying_1].to_f
28
- @exercised = transaction[:exercised]
29
- @underlying_2 = transaction[:underlying_2].to_f
30
- if transaction[:expires] != nil;
31
- expires = transaction[:expires].split("-")
32
- @expires = Time.utc(expires[0],expires[1],expires[2].to_i)
33
- end
34
- @underlying_3 = transaction[:underlying_3].to_f
35
- end
36
-
37
- def self.find(entity, start, count, limit)
11
+ FLOAT_COLUMNS = :shares, :owned, :underlying_1, :underlying_2,
12
+ :underlying_3
38
13
 
39
- if start == nil; start = 0; end
40
- if count == nil; count = 80; end
41
- url = "http://www.sec.gov/cgi-bin/own-disp?action=get"+entity[:type]+"&CIK="+entity[:cik]+"&start="+start.to_s+"&count="+count.to_s
42
- response = Entity.query(url)
43
- doc = Hpricot(response)
44
- trans = doc.search("//td[@width='40%']")[0].parent.parent.search("//tr")
45
- i= start;
46
- query_more = false;
47
- for tran in trans
48
- td = tran.search("//td")
49
- if td[2] != nil and td[1].innerHTML != "Exercise"
50
- query_more = true;
51
- if !td[0].empty?
52
- transaction={}
53
- transaction[:code] = td[0].innerHTML;
54
- transaction[:date] = td[1].innerHTML;
55
- transaction[:reporting_owner] = td[2].innerHTML;
56
- transaction[:form] = td[3].innerHTML;
57
- transaction[:type] = td[4].innerHTML;
58
- transaction[:modes] = td[5].innerHTML;
59
- transaction[:shares] = td[6].innerHTML;
60
- transaction[:price] = td[7].innerHTML;
61
- transaction[:owned] = td[8].innerHTML;
62
- transaction[:number] = td[9].innerHTML;
63
- transaction[:owner_cik] = td[10].innerHTML;
64
- transaction[:security_name] = td[11].innerHTML;
65
- transaction[:deemed] = td[12].innerHTML;
66
- if trans[i+1]; n_td = trans[i+1].search("//td"); end
67
- if n_td != nil and n_td.count ==7 and n_td[0].innerHTML.empty?
68
- transaction[:exercise] = n_td[1].innerHTML;
69
- transaction[:nature] = n_td[2].innerHTML;
70
- transaction[:derivative] = n_td[3].innerHTML;
71
- transaction[:underlying_1] = n_td[4].innerHTML;
72
- transaction[:exercised] = n_td[5].innerHTML;
73
- transaction[:underlying_2] = n_td[6].innerHTML;
74
- transaction[:expires] = n_td[7].innerHTML;
75
- transaction[:underlying_3] =n_td[8].innerHTML;
76
- end
77
- entity[:transactions] << Transaction.new(transaction)
78
- end
79
- end
80
- i=i+1
81
- end
82
- if query_more and limit == nil || query_more and !limit
83
- Transaction.find(entity, start+count, count, limit);
84
- else
85
- return entity
14
+ attr_accessor(*STR_COLUMNS, *FLOAT_COLUMNS, :filing_number, :date, :price,
15
+ :owned, :number, :expires)
16
+
17
+ def initialize(transaction)
18
+ @number = transaction[:number].to_i
19
+ @price = transaction[:price].gsub('$', '').to_f
20
+ @filing_number = transaction[:form].split('/')[-2][0..19]
21
+ setup_columns(transaction)
22
+ setup_date(transaction)
23
+ setup_expires(transaction)
24
+ end
25
+
26
+ def setup_columns(transaction)
27
+ STR_COLUMNS.each do |column|
28
+ instance_variable_set("@#{ column }", transaction[column])
29
+ end
30
+ FLOAT_COLUMNS.each do |column|
31
+ instance_variable_set("@#{ column }", transaction[column].to_f)
32
+ end
33
+ end
34
+
35
+ def setup_date(transaction)
36
+ if transaction[:date] && transaction[:date] != '-'
37
+ date = transaction[:date].split('-')
38
+ @date = Time.utc(date[0], date[1], date[2])
39
+ end
40
+ end
41
+
42
+ def setup_expires(transaction)
43
+ if transaction[:expires]
44
+ expires = transaction[:expires].split('-')
45
+ @expires = Time.utc(expires[0], expires[1], expires[2].to_i)
46
+ end
47
+ end
48
+
49
+ def self.find(entity, start, count, limit)
50
+ start ||= 0
51
+ count ||= 80
52
+ url = SecURI.ownership_display_uri({
53
+ action: "get#{entity[:type]}",
54
+ CIK: entity[:cik],
55
+ start: start,
56
+ count: count
57
+ })
58
+ response = Entity.query(url)
59
+ doc = Hpricot(response)
60
+ trans = doc.search("//td[@width='40%']")[0].parent.parent.search('//tr')
61
+ i = start
62
+ query_more = false
63
+ trans.each do |tran|
64
+ td = tran.search('//td')
65
+ if td[2] && td[1].innerHTML != 'Exercise'
66
+ query_more = true
67
+ unless td[0].empty?
68
+ transaction = {}
69
+ transaction[:code] = td[0].innerHTML
70
+ transaction[:date] = td[1].innerHTML
71
+ transaction[:reporting_owner] = td[2].innerHTML
72
+ transaction[:form] = td[3].innerHTML
73
+ transaction[:type] = td[4].innerHTML
74
+ transaction[:modes] = td[5].innerHTML
75
+ transaction[:shares] = td[6].innerHTML
76
+ transaction[:price] = td[7].innerHTML
77
+ transaction[:owned] = td[8].innerHTML
78
+ transaction[:number] = td[9].innerHTML
79
+ transaction[:owner_cik] = td[10].innerHTML
80
+ transaction[:security_name] = td[11].innerHTML
81
+ transaction[:deemed] = td[12].innerHTML
82
+ n_td = trans[i + 1].search('//td') if trans[i + 1]
83
+ if n_td && n_td.count == 7 && n_td[0].innerHTML.empty?
84
+ transaction[:exercise] = n_td[1].innerHTML
85
+ transaction[:nature] = n_td[2].innerHTML
86
+ transaction[:derivative] = n_td[3].innerHTML
87
+ transaction[:underlying_1] = n_td[4].innerHTML
88
+ transaction[:exercised] = n_td[5].innerHTML
89
+ transaction[:underlying_2] = n_td[6].innerHTML
90
+ transaction[:expires] = n_td[7].innerHTML
91
+ transaction[:underlying_3] = n_td[8].innerHTML
86
92
  end
93
+ entity[:transactions] << Transaction.new(transaction)
94
+ end
87
95
  end
96
+ i += 1
97
+ end
98
+
99
+ if (query_more && limit.nil?) || (query_more && !limit)
100
+ Transaction.find(entity, start + count, count, limit)
101
+ else
102
+ return entity
103
+ end
88
104
  end
105
+ end
89
106
  end