Class: SecEdgar::Filing

Inherits:
Object
  • Object
show all
Defined in:
lib/sec_edgar/filing.rb

Constant Summary collapse

COLUMNS =
[:cik, :title, :summary, :link, :term, :date, :file_id]

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filing) ⇒ Filing

Returns a new instance of Filing.



9
10
11
12
13
# File 'lib/sec_edgar/filing.rb', line 9

def initialize(filing)
  COLUMNS.each do |column|
    instance_variable_set("@#{ column }", filing[column])
  end
end

Class Method Details

.fetch(uri, &blk) ⇒ Object



15
16
17
18
19
# File 'lib/sec_edgar/filing.rb', line 15

def self.fetch(uri, &blk)
  open(uri) do |rss|
    parse_rss(rss, &blk)
  end
end

.filing_for_index_row(row) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/sec_edgar/filing.rb', line 76

def self.filing_for_index_row(row)
  data = row.split(/   /).reject(&:blank?).map(&:strip)
  data = row.split(/  /).reject(&:blank?).map(&:strip) if data.count == 4
  data[1].gsub!('/ADV', '')
  data.delete_at(1) if data[1][0] == '/'
  return nil unless Regexp.new(/\d{8}/).match(data[3])
  unless data[4][0..3] == 'https'
    data[4] = "https://www.sec.gov/Archives/#{ data[4] }"
  end
  SecEdgar::Filing.new(
    term: data[1],
    cik: data[2],
    date: Date.parse(data[3]),
    link: data[4]
  )
end

.filings_for_index(index) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
# File 'lib/sec_edgar/filing.rb', line 64

def self.filings_for_index(index)
  [].tap do |filings|
    content_section = false
    index.each_line do |row|
      content_section = true if row.include?('-------------')
      next if !content_section || row.include?('------------')
      filing = filing_for_index_row(row)
      filings << filing unless filing.nil?
    end
  end
end

.for_cik(cik, options = {}, &blk) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
# File 'lib/sec_edgar/filing.rb', line 35

def self.for_cik(cik, options = {}, &blk)
  start = options.fetch(:start, 0)
  count = options.fetch(:count, 100)
  limit = options.fetch(:limit, 100)
  fetch(uri_for_cik(cik, start, count), &blk)
  start += count
  return if start >= limit
  for_cik(cik, { start: start, count: count, limit: limit }, &blk)
rescue OpenURI::HTTPError
  return
end

.for_date(date, &blk) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/sec_edgar/filing.rb', line 47

def self.for_date(date, &blk)
  ftp = Net::FTP.new('ftp.sec.gov')
  ftp.
  file_name = ftp.nlst("edgar/daily-index/#{ date.to_sec_uri_format }*")[0]
  ftp.close
  open("ftp://ftp.sec.gov/#{ file_name }") do |file|
    if file_name[-2..-1] == 'gz'
      gz_reader = Zlib::GzipReader.new(file)
      gz_reader.rewind
      filings_for_index(gz_reader).each(&blk)
    else
      filings_for_index(file).each(&blk)
    end
  end
rescue Net::FTPTempError
end

.parse(cik, document) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/sec_edgar/filing.rb', line 130

def self.parse(cik, document)
  filings = []
  if document.xpath('//content').to_s.length > 0
    document.xpath('//content').each do |e|
      if e.xpath('//content/accession-nunber').to_s.length > 0
        content = Hash.from_xml(e.to_s)['content']
        content[:cik] = cik
        content[:file_id] = content.delete('accession_nunber')
        content[:date] = content.delete('filing_date')
        content[:link] = content.delete('filing_href')
        content[:term] = content.delete('filing_type')
        content[:title] = content.delete('form_name')
        filings << SecEdgar::Filing.new(content)
      end
    end
  end
  filings
end

.parse_rss(rss, &blk) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/sec_edgar/filing.rb', line 114

def self.parse_rss(rss, &blk)
  feed = RSS::Parser.parse(rss, false)
  feed.entries.each do |entry|
    filing = SecEdgar::Filing.new(
      cik: entry.title.content.match(/\((\w{10})\)/)[1],
      file_id: entry.id.content.split('=').last,
      term:  entry.category.term,
      title: entry.title.content,
      summary: entry.summary.content,
      date: DateTime.parse(entry.updated.content.to_s),
      link: entry.link.href.gsub('-index.htm', '.txt')
    )
    blk.call(filing)
  end
end

.recent(options = {}, &blk) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/sec_edgar/filing.rb', line 21

def self.recent(options = {}, &blk)
  start = options.fetch(:start, 0)
  count = options.fetch(:count, 100)
  limit = options.fetch(:limit, 100)
  limited_count = [limit - start, count].min
  fetch(uri_for_recent(start, limited_count), &blk)
  start += count
  return if start >= limit
  recent({ start: start, count: count, limit: limit }, &blk)
rescue OpenURI::HTTPError => e
  puts e
  return
end

.uri_for_cik(cik, start = 0, count = 100) ⇒ Object



103
104
105
106
107
108
109
110
111
112
# File 'lib/sec_edgar/filing.rb', line 103

def self.uri_for_cik(cik, start = 0, count = 100)
  SecEdgar::SecURI.browse_edgar_uri(
    action: :getcompany,
    owner: :include,
    output: :atom,
    start: start,
    count: count,
    CIK: cik
  )
end

.uri_for_recent(start = 0, count = 100) ⇒ Object



93
94
95
96
97
98
99
100
101
# File 'lib/sec_edgar/filing.rb', line 93

def self.uri_for_recent(start = 0, count = 100)
  SecEdgar::SecURI.browse_edgar_uri(
    action: :getcurrent,
    owner: :include,
    output: :atom,
    start: start,
    count: count
  )
end

Instance Method Details

#content(&error_blk) ⇒ Object



149
150
151
152
153
154
155
156
157
158
# File 'lib/sec_edgar/filing.rb', line 149

def content(&error_blk)
  @content ||= RestClient.get(self.link)
rescue RestClient::ResourceNotFound => e
  puts "404 Resource Not Found: Bad link #{ self.link }"
  if block_given?
    error_blk.call(e, self)
  else
    raise e
  end
end