sec_query 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in sec_query.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sec_query (0.0.1)
5
+ hpricot
6
+ rest-client
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ diff-lcs (1.1.3)
12
+ hpricot (0.8.4)
13
+ mime-types (1.17.2)
14
+ rest-client (1.6.7)
15
+ mime-types (>= 1.16)
16
+ rspec (2.7.0)
17
+ rspec-core (~> 2.7.0)
18
+ rspec-expectations (~> 2.7.0)
19
+ rspec-mocks (~> 2.7.0)
20
+ rspec-core (2.7.1)
21
+ rspec-expectations (2.7.0)
22
+ diff-lcs (~> 1.1.2)
23
+ rspec-mocks (2.7.0)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ rspec
30
+ sec_query!
data/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # sec_query
2
+
3
+ A ruby gem for searching and retrieving data from the Security and Exchange Commission's Edgar web system.
4
+
5
+ Look-up an Entity - person or company - by Central Index Key (CIK), stock symbol, company name or person (by first and last name).
6
+
7
+ Additionally retrieve some, or all, Relationships, Transactions and Filings as recorded by the SEC.
8
+
9
+ ## Installation
10
+
11
+ To install the 'sec_query' Ruby Gem run the following command at the terminal prompt.
12
+
13
+ `gem install sec_query`
14
+
15
+ For an example of what type of information 'sec_query' can retrieve, run the following command:
16
+
17
+ `bundle exec rspec spec`
18
+
19
+ If running 'sec_query' from the command prompt in irb:
20
+
21
+ `irb -rubygems`
22
+
23
+ `require "sec_query"`
24
+
25
+ `include SecQuery`
26
+
27
+ ## Functionality
28
+
29
+ ### FIND COMPANY:
30
+
31
+ #### By Stock Symbol:
32
+
33
+ `SecQuery::Entity.find("appl")`
34
+
35
+ Or:
36
+
37
+ `SecQuery::Entity.find({:symbol=> "aapl"})`
38
+
39
+ #### By Name:
40
+
41
+ `SecQuery::Entity.find("Apple, Inc.")`
42
+
43
+ Or:
44
+
45
+ `SecQuery::Entity.find({:name=> "Apple, Inc."})`
46
+
47
+ #### By Central Index Key, CIK:
48
+
49
+ `SecQuery::Entity.find( "0000320193")`
50
+
51
+ Or:
52
+
53
+ `SecQuery::Entity.find({:cik=> "0000320193"})`
54
+
55
+ #### FIND PERSON:
56
+
57
+ By First, Middle and Last Name.
58
+
59
+ `SecQuery::Entity.find({:first=> "Steve", :middle=> "P", :last=> "Jobs"})`
60
+
61
+ Middle initial or name is optional, but helps when there are multiple results for First and Last Name.
62
+
63
+ ### RELATIONSHIPS, TRANSACTIONS, FILINGS
64
+
65
+ To return everything - All Relationships, Transactions and Filings - that the SEC Edgar system has stored on a company or person, do any of the following commands (They all do the same thing.):
66
+
67
+ `SecQuery::Entity.find("AAPL", true)`
68
+
69
+ `SecQuery::Entity.find("AAPL", true, true, true)`
70
+
71
+ `SecQuery::Entity.find("AAPL", {:relationships=> true, :transactions=> true, :filings=>true})`
72
+
73
+ `SecQuery::Entity.find("AAPL", :relationships=> true, :transactions=> true, :filings=>true)`
74
+
75
+ `SecQuery::Entity.find("AAPL", :relationships=> true, :transactions=> {:start=> 0, :count=> 80}, :filings=>{:start=> 0, :count=> 80})`
76
+
77
+ You may also limit either the transactions or filings by adding the :limit to the transaction or filing arguements.
78
+
79
+ For example,
80
+
81
+ `SecQuery::Entity.find("AAPL", :relationships=> true, :transactions=> {:start=> 0, :count=>20, :limit=> 20}, :filings=>{:start=> 0, :count=> 20, :limit=> 20})`
82
+
83
+ The above query will only return the last 20 transactions and filings. This is helpful when querying companies that may have thousands or tens of thousands of transactions or filings.
84
+
85
+ ### Entity.log(entity)
86
+
87
+ For doing terminal queries, there is a log function that formats and prints the entity data to your terminal window.
88
+
89
+ `entity = SecQuery::Entity.find("AAPL", true)`
90
+
91
+ `SecQuery::Entity.log(entity)`
92
+
93
+
94
+ ## Classes
95
+
96
+ This gem contains four classes - Entity, Relationship, Transaction and Filing. Each Class contains the listed fields. (Everything I could parse out of the query results.)
97
+
98
+ * Entity
99
+
100
+ `:first, :middle, :last, :name, :symbol, :cik, :url, :type, :sic, :location, :state_of_inc, :formerly, :mailing_address, :business_address, :relationships, :transactions, :filings`
101
+
102
+ * Relationship
103
+
104
+ `:name, :position, :date, :cik`
105
+
106
+ * Transaction
107
+
108
+ `: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`
109
+
110
+ * Filing
111
+
112
+ `:cik, :title, :summary, :link, :term, :date, :file_id`
113
+
114
+ ## To Whom It May Concern at the SEC
115
+
116
+ Over the last decade, I have gotten to know Edgar quite extensively and I have grown quite fond of it and the information it contains. So it is with my upmost respect that I make the following suggestions:
117
+
118
+ * Edgar is in dire need of a proper, published RESTful API.
119
+ * Edgar needs to be able to return XML or JSON for any API query.
120
+ * Edgar's search engine is atrocious; Rigid to the point of being almost unusable.
121
+ * Edgar only goes back as far as 1993, and in most cases, only provides extensive information after 2000.
122
+
123
+ It is my humble opinion that these four issues are limiting the effectiveness of Edgar and the SEC in general. The information the SEC contains is vitally important to National Security and the stability of the American Economy and the World. It is time to make all information available and accessible.
124
+
125
+ ## License
126
+
127
+ Copyright (c) 2011 Ty Rauber
128
+
129
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
130
+
131
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
132
+
133
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/.DS_Store ADDED
Binary file
Binary file
@@ -0,0 +1,305 @@
1
+ module SecQuery
2
+ class Entity
3
+
4
+ attr_accessor :first, :middle, :last, :name, :symbol, :cik, :url, :type, :sic, :location, :state_of_inc, :formerly, :mailing_address, :business_address, :relationships, :transactions, :filings
5
+
6
+ def initialize(entity)
7
+ @first = entity[:first]
8
+ @middle = entity[:middle]
9
+ @last = entity[:last]
10
+ @name = entity[:name]
11
+ @sic = entity[:sic]
12
+ @url = entity[:url]
13
+ @location = entity[:location]
14
+ @state_of_inc = entity[:state_of_inc]
15
+ @formerly = entity[:formerly]
16
+ @symbol = entity[:symbol]
17
+ @cik = entity[:cik]
18
+ @type =entity[:type]
19
+ @mailing_address = entity[:mailing_address]
20
+ @business_address = entity[:business_address]
21
+ @relationships = entity[:relationships]
22
+ @transactions = entity[:transactions]
23
+ @filings = entity[:filings]
24
+ end
25
+
26
+
27
+ def self.find(entity_args, *options)
28
+
29
+ temp = {}
30
+ temp[:url] = Entity.url(entity_args)
31
+ temp[:cik] = Entity.cik(temp[:url], entity_args)
32
+
33
+ if !temp[:cik]; return false; end
34
+
35
+ ### Get Document and Entity Type
36
+ doc = Entity.document(temp[:cik])
37
+ temp = Entity.parse_document(temp, doc)
38
+
39
+ ### Get Additional Arguments and Query Additional Details
40
+ if !options.empty?;
41
+ temp[:transactions]=[]
42
+ temp[:filings] =[]
43
+ options = Entity.options(temp, options);
44
+ temp = Entity.details(temp, options);
45
+ end
46
+
47
+ ### Return entity Object
48
+ @entity = Entity.new(temp)
49
+
50
+ return @entity
51
+
52
+ end
53
+
54
+
55
+ def self.query(url)
56
+ RestClient.get(url){ |response, request, result, &block|
57
+ case response.code
58
+ when 200
59
+ return response
60
+ else
61
+ response.return!(request, result, &block)
62
+ end
63
+ }
64
+ end
65
+
66
+
67
+ def self.url(args)
68
+ if args.is_a?(Hash)
69
+ if args[:symbol] != nil
70
+ string = "CIK="+args[:symbol]
71
+ elsif args[:cik] != nil
72
+ string = "CIK="+args[:cik]
73
+ elsif args[:first] != nil and args[:last]
74
+ string = "company="+args[:last]+" "+args[:first]
75
+ elsif args[:name] != nil
76
+ string = "company="+args[:name].gsub(/[(,?!\''"":.)]/, '')
77
+ end
78
+ elsif args.is_a?(String)
79
+ begin Float(args)
80
+ string = "CIK="+args
81
+ rescue
82
+ if args.length <= 4
83
+ string = "CIK="+args
84
+ else
85
+ string = "company="+args.gsub(/[(,?!\''"":.)]/, '')
86
+ end
87
+ end
88
+ end
89
+ string = string.to_s.gsub(" ", "+")
90
+ url = "http://www.sec.gov/cgi-bin/browse-edgar?"+string+"&action=getcompany"
91
+ return url
92
+ end
93
+
94
+ def self.cik(url, entity)
95
+ response = Entity.query(url+"&output=atom")
96
+ doc = Hpricot::XML(response)
97
+ data = doc.search(:title)[0];
98
+ if data.inner_text == "EDGAR Search Results"
99
+ tbl = doc.search("//span[@class='companyMatch']")
100
+ if tbl && tbl.innerHTML != ""
101
+ tbl = tbl[0].parent.search("table")[0].search("tr")
102
+ for tr in tbl
103
+ td = tr.search("td")
104
+ if td[1] != nil && entity[:middle] != nil && td[1].innerHTML.downcase == (entity[:last]+" "+entity[:first]+" "+entity[:middle]).downcase or td[1] != nil && td[1].innerHTML.downcase == (entity[:last]+" "+entity[:first]).downcase
105
+ cik = td[0].search("a").innerHTML
106
+ return cik;
107
+ end
108
+ end
109
+ else
110
+ return false;
111
+ end
112
+ else
113
+ cik = data.inner_text.scan(/\(([^)]+)\)/).to_s
114
+ return cik
115
+ end
116
+ end
117
+
118
+ def self.document(cik)
119
+ url ="http://www.sec.gov/cgi-bin/own-disp?action=getissuer&CIK="+cik
120
+ response = query(url)
121
+ doc = Hpricot(response)
122
+ text = "Ownership Reports from:"
123
+ type = "issuer"
124
+ entity = doc.search("//table").search("//td").search("b[text()*='"+text+"']")
125
+ if entity.empty?
126
+ url= "http://www.sec.gov/cgi-bin/own-disp?action=getowner&CIK="+cik
127
+ response = query(url)
128
+ doc = Hpricot(response)
129
+ text = "Ownership Reports for entitys:"
130
+ type = "owner"
131
+ entity = doc.search("//table").search("//td").search("b[text()*='"+text+"']")
132
+ end
133
+ return [doc, type]
134
+
135
+ end
136
+
137
+ def self.parse_document(temp, doc)
138
+
139
+ temp[:type] = doc[1]
140
+ info = Entity.info(doc[0])
141
+ temp[:name] = info[:name]
142
+ temp[:location] = info[:location]
143
+ temp[:sic] = info[:sic]
144
+ temp[:state_of_inc] = info[:state_of_inc]
145
+ temp[:formerly] = info[:formerly]
146
+
147
+ ### Get Mailing Address
148
+ temp[:mailing_address] = Entity.mailing_address(doc[0])
149
+
150
+ ### Get Business Address
151
+ temp[:business_address] = Entity.business_address(doc[0])
152
+
153
+ return temp;
154
+ end
155
+
156
+ def self.info(doc)
157
+ info={}
158
+ lines = doc.search("//td[@bgcolor='#E6E6E6']")[0].parent.parent.search("//tr")
159
+ td = lines[0].search("//td//b").innerHTML
160
+ info[:name] = td.gsub(td.scan(/\(([^)]+)\)/).to_s, "").gsub("()", "").gsub("\n", "")
161
+ lines = lines[1].search("//table")[0].search("//tr//td")
162
+ if lines[0].search("a")[0] != nil
163
+ info[:sic] = lines[0].search("a")[0].innerHTML
164
+ end
165
+ if lines[0].search("a")[1] != nil
166
+ info[:location] = lines[0].search("a")[1].innerHTML
167
+ end
168
+
169
+ if lines[0].search("b")[0] != nil and lines[0].search("b")[0].innerHTML.squeeze(" ") != " "
170
+ info[:state_of_inc] = lines[0].search("b")[0].innerHTML
171
+ end
172
+ if lines[1] != nil and lines[1].search("font")
173
+ info[:formerly] = lines[1].search("font").innerHTML.gsub("formerly: ", "").gsub("<br />", "").gsub("\n", "; ")
174
+ end
175
+ return info
176
+ end
177
+
178
+
179
+ def self.business_address(doc)
180
+ addie = doc.search("//table").search("//td").search("b[text()*='Business Address']")
181
+ if !addie.empty?;
182
+ business_address = addie[0].parent.innerHTML.gsub('<b class="blue">Business Address</b>', '').gsub('<br />', ' ');
183
+ return business_address
184
+ else
185
+ return false;
186
+ end
187
+ end
188
+
189
+ def self.mailing_address(doc)
190
+ addie = doc.search("//table").search("//td").search("b[text()*='Mailing Address']")
191
+ if !addie.empty?;
192
+ mailing_address = addie[0].parent.innerHTML.gsub('<b class="blue">Mailing Address</b>', '').gsub('<br />', ' ');
193
+ return mailing_address
194
+ else
195
+ return false;
196
+ end
197
+ end
198
+
199
+
200
+ def self.options(temp, options)
201
+
202
+ args={}
203
+ if options.is_a?(Array) && options.length == 1 && options[0] == true;
204
+ args[:relationships] = true;
205
+ args[:transactions]= true;
206
+ args[:filings] = true;
207
+ elsif options.is_a?(Array) && options.length > 1
208
+ args[:relationships] = options[0];
209
+ args[:transactions]= options[1];
210
+ args[:filings] = options[2];
211
+ elsif options[0].is_a?(Hash)
212
+ args[:relationships] = options[0][:relationships];
213
+ args[:transactions]= options[0][:transactions];
214
+ args[:filings] = options[0][:filings];
215
+ end
216
+ return args;
217
+ end
218
+
219
+ def self.details(temp, options)
220
+
221
+ ## Get Relationships for entity
222
+ if options[:relationships] == true
223
+ relationships = Relationship.find(temp)
224
+ temp[:relationships] =relationships
225
+ end
226
+
227
+ ## Get Transactions for entity
228
+ if options[:transactions] != nil and options[:transactions].is_a?(Hash)
229
+ temp = Transaction.find(temp, options[:transactions][:start], options[:transactions][:count], options[:transactions][:limit])
230
+ elsif options[:transactions] != nil && options[:transactions] == true
231
+ temp = Transaction.find(temp, nil, nil, nil)
232
+ end
233
+
234
+
235
+ ## Get Filings for entity
236
+
237
+ if options[:filings] != nil and options[:filings].is_a?(Hash)
238
+ temp = Filing.find(temp, options[:filings][:start], options[:filings][:count], options[:filings][:limit])
239
+ elsif options[:filings] != nil and options[:filings] == true
240
+ temp = Filing.find(temp, nil, nil, nil)
241
+ end
242
+
243
+ return temp;
244
+ end
245
+
246
+
247
+ def self.log(entity)
248
+
249
+ if entity != false
250
+ puts "\n\t# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n\n"
251
+ puts "\t"+entity.name
252
+ puts "\t("+entity.cik+")"
253
+ if entity.formerly && entity.formerly != ""
254
+ puts "\tFormerly: "+entity.formerly
255
+ end
256
+ if entity.sic
257
+ puts "\tSIC = "+entity.sic
258
+ end
259
+ if entity.location
260
+ puts "\tLocation: "+entity.location+", "
261
+ end
262
+ if entity.state_of_inc
263
+ puts "\tState of Incorporation: "+entity.state_of_inc
264
+ end
265
+ if entity.mailing_address
266
+ puts "\tMailing Address:\t"+ entity.mailing_address.inspect.gsub('\n', ' ').squeeze(" ")
267
+ end
268
+
269
+ if entity.business_address
270
+ puts "\tBusiness Address:\t"+ entity.business_address.inspect.gsub('\n', ' ').squeeze(" ")
271
+ end
272
+
273
+
274
+ if !entity.relationships
275
+ puts "\n\tRELATIONSHIPS:\t0 Total"
276
+ else
277
+ puts "\n\tRELATIONSHIPS:\t"+ entity.relationships.count.to_s+" Total"
278
+ printf("\t%-40s %-15s %-30s %-10s\n\n","Entity", "CIK", "Position", "Date")
279
+ for relationship in entity.relationships
280
+ printf("\t%-40s %-15s %-30s %-10s\n",relationship.name, relationship.cik, relationship.position, relationship.date)
281
+ end
282
+ end
283
+ if entity.transactions
284
+ puts "\n\tTRANSACTIONS:\t"+ entity.transactions.count.to_s+" Total"
285
+ printf("\t%-20s %-10s %-5s %-10s %-10s %-10s %-15s %-10s\n\n","Owner", "CIK", "Modes", "Type","Shares","Price","Owned","Date")
286
+ for transaction in entity.transactions
287
+ printf("\t%-20s %-10s %-5s %-10s%-10s %-10s %-15s %-10s\n", transaction.reporting_owner,transaction.owner_cik,transaction.modes, transaction.type,transaction.shares,transaction.price,transaction.owned,transaction.date)
288
+ end
289
+ end
290
+ if entity.filings
291
+ puts "\n\tFILINGS:\t"+ entity.filings.count.to_s+" Total"
292
+ printf("\t%-10s %-30s %-20s\n\n","Type", "File ID", "Date")
293
+ for filing in entity.filings
294
+ printf("\t%-10s %-30s %-20s\n",filing.term, filing.file_id, filing.date)
295
+ end
296
+
297
+ end
298
+ puts "\t"+entity.url+"\n\n"
299
+ else
300
+ return "No Entity found."
301
+ end
302
+
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,45 @@
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)
16
+
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
34
+
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
43
+ end
44
+
45
+ end
@@ -0,0 +1,63 @@
1
+ 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
50
+ 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
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,89 @@
1
+ module SecQuery
2
+ class Transaction
3
+
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)
38
+
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
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,3 @@
1
+ module SecQuery
2
+ VERSION = "1.0.0"
3
+ end
data/lib/sec_query.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "rubygems"
2
+ require "rest-client"
3
+ require "hpricot"
4
+
5
+
6
+ require "sec_query/version"
7
+ require "sec_query/entity"
8
+ require "sec_query/relationship"
9
+ require "sec_query/transaction"
10
+ require "sec_query/filing"
11
+
Binary file
data/sec_query.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "sec_query/version"
4
+ require "sec_query"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "sec_query"
8
+ s.version = SecQuery::VERSION
9
+ s.authors = ["Ty Rauber"]
10
+ s.email = ["tyrauber@mac.com"]
11
+ s.homepage = "https://github.com/tyrauber/sec_query"
12
+ s.summary = "A ruby gem for querying the United States Securities and Exchange Commission Edgar System."
13
+ s.description = "Search for company or person, by name, symbol or Central Index Key (CIK), and retrieve relationships, transactions and filings."
14
+
15
+ s.rubyforge_project = "sec_query"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ # specify any dependencies here; for example:
23
+ s.add_development_dependency "rspec"
24
+ s.add_runtime_dependency "rest-client"
25
+ s.add_runtime_dependency "hpricot"
26
+ end
@@ -0,0 +1,65 @@
1
+
2
+ #cfr_directors = ["Carla A. Hills", "Robert E. Rubin", "Richard E. Salomon", "Richard N. Haass", "John P. Abizaid", "Peter Ackerman", "Fouad Ajami", "Madeleine K. Albright", "Alan S. Blinder", "Mary Boies", "David G. Bradley", "Tom Brokaw", "Sylvia Mathews Burwell", "Kenneth M. Duberstein", "Martin S. Feldstein", "Stephen Friedman","Ann M. Fudge", "Pamela Gann", "Thomas H. Glocer", "J. Tomilson Hill","Donna J. Hrinak", "Alberto Ibargüen", "Shirley Ann Jackson", "Henry R. Kravis", "Jami Miscik", "Joseph S. Nye", "James W. Owens", "Eduardo J. Padrón", "Colin L. Powell", "Penny Pritzker", "David M. Rubenstein", "George Rupp", "Frederick W. Smith", "Christine Todd Whitman", "Fareed Zakaria", "Leslie H. Gelb", "Maurice R. Greenberg", "Peter G. Peterson","David Rockefeller"]
3
+
4
+
5
+
6
+ cfr_directors = [
7
+ {:middle=>"A", :first=>"Carla", :last=>"Hills", :cik_id=> "0001194913"},
8
+ {:middle=>"E", :first=>"Robert", :last=>"Rubin", :cik_id=> "0001225178"},
9
+ {:middle=>"E", :first=>"Richard", :last=>"Salomon", :cik_id => "0001217109"},
10
+ {:middle=>"N", :first=>"Richard", :last=>"Haass", :cik_id => "0001123553"},
11
+ {:middle=>"P", :first=>"John", :last=>"Abizaid", :cik_id => "0001425152"},
12
+ {:first=>"Peter", :last=>"Ackerman", :cik_id => "0001111564"},
13
+ {:first=>"Fouad", :last=>"Ajami"},
14
+ {:middle=>"K", :first=>"Madeleine", :last=>"Albright"},
15
+ {:middle=>"S", :first=>"Alan", :last=>"Blinder"},
16
+ {:first=>"Mary", :last=>"Boies", :cik_id => "0001291933"},
17
+ {:middle=>"G", :first=>"David", :last=>"Bradley", :cik_id=> "0001106734"},
18
+ {:first=>"Tom", :last=>"Brokaw"},
19
+ {:first=>"Sylvia", :last=>"Burwell"},
20
+ {:middle=>"M", :first=>"Kenneth", :last=>"Duberstein", :cik_id=> "0001179625"},
21
+ {:middle=>"S", :first=>"Martin", :last=>"Feldstein", :cik_id=> "0001236596"},
22
+ {:first=>"Stephen", :last=>"Friedman", :cik_id=> "0001029607"},
23
+ {:middle=>"M", :first=>"Ann", :last=>"Fudge", :cik_id=> "0001198098"},
24
+ {:first=>"Pamela", :last=>"Gann"},
25
+ {:middle=>"H", :first=>"Thomas", :last=>"Glocer", :cik_id=> "0001140799"},
26
+ {:middle=>"J", :first=>"J.", :last=>"Hill"},
27
+ {:middle=>"J", :first=>"Donna", :last=>"Hrinak", :cik_id=> "0001296811"},
28
+ {:first=>"Alberto", :last=>"Ibarguen", :cik_id=> "0001339732"},
29
+ {:first=>"Shirley", :last=>"Jackson", :cik_id=> "0001168019"},
30
+ {:middle=>"R", :first=>"Henry", :last=>"Kravis", :cik_id=> "0001081714"},
31
+ {:first=>"Jami", :last=>"Miscik"},
32
+ {:middle=>"S", :first=>"Joseph", :last=>"Nye", :cik_id=> "0001299821"},
33
+ {:middle=>"W", :first=>"James", :last=>"Owens", :cik_id=> "0001443909"},
34
+ {:middle=>"J", :first=>"Eduardo", :last=>"Padron"},
35
+ {:middle=>"L", :first=>"Colin", :last=>"Powell"},
36
+ {:first=>"Penny", :last=>"Pritzker", :cik_id=> "0001087398"},
37
+ {:middle=>"M", :first=>"David", :last=>"Rubenstein"},
38
+ {:first=>"George", :last=>"Rupp"},
39
+ {:middle=>"W", :first=>"Frederick", :last=>"Smith", :cik_id=> "0001033677"},
40
+ {:first=>"Christine", :last=>"Whitman", :cik_id=> "0001271384"},
41
+ {:first=>"Fareed", :last=>"Zakaria"},
42
+ {:middle=>"H", :first=>"Leslie", :last=>"Gelb", :cik_id=> "0001240500"},
43
+ {:middle=>"R", :first=>"Maurice", :last=>"Greenberg", :cik_id=> "0001236599"},
44
+ {:middle=>"G", :first=>"Peter", :last=>"Peterson", :cik_id=> "0001070843"},
45
+ {:first=>"David", :last=>"Rockefeller", :cik_id=> "0001204357"}
46
+ ]
47
+
48
+ include SecQuery
49
+
50
+ describe SecQuery::Entity do
51
+ puts "\n\nCOUNCIL ON FOREIGN RELATIONS' DIRECTORS\nAs perceived by the Security and Exchange Commission Edgar System:\n"
52
+ for t in cfr_directors
53
+ entity = SecQuery::Entity.find(t, true)
54
+ # entity = SecQuery::Entity.find(t, true, {:start => 0, :count => 20, :limit => 20}, {:start => 0, :count => 20, :limit => 20})
55
+ if entity != false
56
+ Entity.log(entity)
57
+ it t[:cik].to_s+" is company "+entity.name.to_s do
58
+ if t[:cik] != nil
59
+ entity.cik.should eql(t[:cik_id])
60
+ end
61
+
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1 @@
1
+ require "sec_query"
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sec_query
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Ty Rauber
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-12-02 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rest-client
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: hpricot
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :runtime
62
+ version_requirements: *id003
63
+ description: Search for company or person, by name, symbol or Central Index Key (CIK), and retrieve relationships, transactions and filings.
64
+ email:
65
+ - tyrauber@mac.com
66
+ executables: []
67
+
68
+ extensions: []
69
+
70
+ extra_rdoc_files: []
71
+
72
+ files:
73
+ - Gemfile
74
+ - Gemfile.lock
75
+ - README.md
76
+ - Rakefile
77
+ - lib/.DS_Store
78
+ - lib/sec_query.rb
79
+ - lib/sec_query/.DS_Store
80
+ - lib/sec_query/entity.rb
81
+ - lib/sec_query/filing.rb
82
+ - lib/sec_query/relationship.rb
83
+ - lib/sec_query/transaction.rb
84
+ - lib/sec_query/version.rb
85
+ - pkg/sec_query-0.0.1.gem
86
+ - sec_query-1.0.0.gem
87
+ - sec_query.gemspec
88
+ - spec/sec_query_spec.rb
89
+ - spec/spec_helper.rb
90
+ has_rdoc: true
91
+ homepage: https://github.com/tyrauber/sec_query
92
+ licenses: []
93
+
94
+ post_install_message:
95
+ rdoc_options: []
96
+
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ hash: 3
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ requirements: []
118
+
119
+ rubyforge_project: sec_query
120
+ rubygems_version: 1.5.3
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: A ruby gem for querying the United States Securities and Exchange Commission Edgar System.
124
+ test_files:
125
+ - spec/sec_query_spec.rb
126
+ - spec/spec_helper.rb