sec_query 1.0.3 → 1.0.4

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.
@@ -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