ruby-edgar 0.1.0
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/README.md +120 -0
- data/lib/edgar/attachment.rb +69 -0
- data/lib/edgar/attachments.rb +75 -0
- data/lib/edgar/company.rb +217 -0
- data/lib/edgar/config.rb +62 -0
- data/lib/edgar/data_objects.rb +521 -0
- data/lib/edgar/entity.rb +98 -0
- data/lib/edgar/entity_data.rb +189 -0
- data/lib/edgar/entity_facts.rb +322 -0
- data/lib/edgar/filing.rb +170 -0
- data/lib/edgar/filing_facts.rb +312 -0
- data/lib/edgar/filing_header.rb +124 -0
- data/lib/edgar/filing_homepage.rb +118 -0
- data/lib/edgar/filing_index.rb +87 -0
- data/lib/edgar/filing_sections.rb +43 -0
- data/lib/edgar/filings.rb +216 -0
- data/lib/edgar/financials.rb +361 -0
- data/lib/edgar/http_client.rb +116 -0
- data/lib/edgar/ttm_calculator.rb +185 -0
- data/lib/edgar/version.rb +5 -0
- data/lib/edgar/xbrl/xbrl.rb +134 -0
- data/lib/edgar.rb +83 -0
- metadata +164 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c84e8a66ea352689f9a15dbf08929fa4e9756179c39584bb4c2b2a0171273fe3
|
|
4
|
+
data.tar.gz: 8bed90b9eecebf10d7128169481bbc15b992eb809557aedccaece3dfdd6536bc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 05dc039c7a2a9be8793772ec81994ea63cf58ec9f68563e055f3fdea7636e9e4466be9af2a9bc1ec83499fc862dbf2acde7c1bc2c839eeac1ea71543bc5c8ed7
|
|
7
|
+
data.tar.gz: c5534a89eb6b87fe1a9b70bb94b6c258bb10f449442428d297df9155be3937ae270f354bf85b1e7eac89a7cefbc7819f0e4b00fbd41b6743ed469475ea6006a6
|
data/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Edgar
|
|
2
|
+
|
|
3
|
+
A Ruby library for accessing SEC EDGAR filings. Port of the Python [edgartools](https://github.com/dgunning/edgartools) library.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
gem "ruby-edgar", git: "https://github.com/tian-im/edgar"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or build locally:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
gem build ruby-edgar.gemspec
|
|
15
|
+
gem install ruby-edgar-0.1.0.gem
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
### Company Lookup
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
require "edgar"
|
|
24
|
+
|
|
25
|
+
# By ticker
|
|
26
|
+
aapl = Edgar.Company("AAPL")
|
|
27
|
+
aapl.cik # => 320193
|
|
28
|
+
|
|
29
|
+
# By CIK
|
|
30
|
+
company = Edgar::Company.new(cik: 320193)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Entity Data
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
data = aapl.data
|
|
37
|
+
data.name # => "Apple Inc."
|
|
38
|
+
data.tickers # => ["AAPL"]
|
|
39
|
+
data.exchanges # => ["Nasdaq"]
|
|
40
|
+
data.sic # => 3571
|
|
41
|
+
data.category # => "Large accelerated filer"
|
|
42
|
+
data.business_address # => Address
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Financial Facts
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
facts = aapl.facts
|
|
49
|
+
facts.concept_values("Revenues", limit: 1)
|
|
50
|
+
facts.concept_values("NetIncomeLoss", limit: 1)
|
|
51
|
+
facts.list_concepts(search: "Revenue", limit: 10)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Search Filings by Quarter
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
filings = Edgar::Filings.from_quarter(2024, 4)
|
|
58
|
+
aapl_filings = filings.filter(cik: 320193)
|
|
59
|
+
aapl_filings.latest(5).each { |f| puts "#{f.form} #{f.filing_date}" }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Access a Filing
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
filing = aapl_filings.first
|
|
66
|
+
filing.form # => "10-K"
|
|
67
|
+
filing.filing_date # => Date
|
|
68
|
+
filing.html # => Primary HTML document
|
|
69
|
+
filing.text # => Full submission text
|
|
70
|
+
filing.header.period_of_report # => "20240928"
|
|
71
|
+
filing.header.filer_company # => "Apple Inc."
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Attachments
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
filing.attachments.each do |a|
|
|
78
|
+
puts "#{a.document} (#{a.document_type})"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
filing.exhibits # Exhibit attachments
|
|
82
|
+
filing.datafiles # XML data files
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Data Objects
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
obj = Edgar::DataObjects.for(filing)
|
|
89
|
+
# Returns TenK, TenQ, Form4, etc. based on form type
|
|
90
|
+
obj.form_type
|
|
91
|
+
obj.company_name
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### XBRL
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
xbrl = filing.xbrl
|
|
98
|
+
xbrl.facts # Array of fact hashes
|
|
99
|
+
xbrl.context_refs # Reporting contexts
|
|
100
|
+
xbrl.financial_concepts # US-GAAP concepts
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Configuration
|
|
104
|
+
|
|
105
|
+
Set the `EDGAR_IDENTITY` environment variable:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
export EDGAR_IDENTITY="Your Name your.email@example.com"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Development
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
bundle install
|
|
115
|
+
ruby -e 'require "bundler/setup"; require "edgar"'
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edgar
|
|
4
|
+
# Represents a single file attachment within an SEC filing.
|
|
5
|
+
class Attachment
|
|
6
|
+
EXHIBIT_DESCRIPTIONS = {
|
|
7
|
+
"10-K" => "Annual Report",
|
|
8
|
+
"10-Q" => "Quarterly Report",
|
|
9
|
+
"8-K" => "Current Report",
|
|
10
|
+
"EX-10" => "Material Contract",
|
|
11
|
+
"EX-21" => "Subsidiaries",
|
|
12
|
+
"EX-23" => "Consent of Expert",
|
|
13
|
+
"EX-31" => "Certification",
|
|
14
|
+
"EX-32" => "Certification"
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
attr_reader :sequence_number, :description, :document, :ixbrl, :path,
|
|
18
|
+
:document_type, :size, :sgml_document, :purpose, :url
|
|
19
|
+
|
|
20
|
+
def initialize(sequence_number:, description: nil, document: nil, ixbrl: false,
|
|
21
|
+
path: nil, document_type: nil, size: nil, sgml_document: nil,
|
|
22
|
+
purpose: nil, url: nil)
|
|
23
|
+
@sequence_number = sequence_number
|
|
24
|
+
@description = description
|
|
25
|
+
@document = document
|
|
26
|
+
@ixbrl = ixbrl
|
|
27
|
+
@path = path
|
|
28
|
+
@document_type = document_type
|
|
29
|
+
@size = size
|
|
30
|
+
@sgml_document = sgml_document
|
|
31
|
+
@purpose = purpose
|
|
32
|
+
@url = url
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def display_description
|
|
36
|
+
EXHIBIT_DESCRIPTIONS[@document_type] || @description
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def extension
|
|
40
|
+
return "" unless @document
|
|
41
|
+
|
|
42
|
+
File.extname(@document).downcase
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def text?
|
|
46
|
+
extension == ".txt" || document_type&.include?("TEXT")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def xml?
|
|
50
|
+
[".xml", ".xsl"].include?(extension)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def html?
|
|
54
|
+
[".htm", ".html"].include?(extension)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def binary?
|
|
58
|
+
!text? && !xml? && !html?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def empty?
|
|
62
|
+
@size.nil? || @size.to_i.zero?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def to_s
|
|
66
|
+
"#<Attachment seq=#{@sequence_number} type=#{@document_type} desc='#{@description}'>"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edgar
|
|
4
|
+
# Enumerable collection of Attachment objects for a filing.
|
|
5
|
+
class Attachments
|
|
6
|
+
include Enumerable
|
|
7
|
+
|
|
8
|
+
attr_reader :attachments
|
|
9
|
+
|
|
10
|
+
def initialize(attachments = [])
|
|
11
|
+
@attachments = attachments
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def each(&block)
|
|
15
|
+
@attachments.each(&block)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def [](index)
|
|
19
|
+
@attachments[index]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def length
|
|
23
|
+
@attachments.length
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def exhibits
|
|
27
|
+
select { |a| a.purpose&.downcase&.include?("exhibit") }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def reports
|
|
31
|
+
select { |a| a.purpose&.downcase&.include?("report") }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def statements
|
|
35
|
+
select { |a| a.purpose&.downcase&.include?("statement") }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def primary_documents
|
|
39
|
+
sorted = sort_by(&:sequence_number)
|
|
40
|
+
sorted.select { |a| a.sequence_number == sorted.first&.sequence_number }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def primary_html_document
|
|
44
|
+
sorted = sort_by(&:sequence_number)
|
|
45
|
+
sorted.find(&:html?)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def primary_xml_document
|
|
49
|
+
find(&:xml?)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def xbrl_document
|
|
53
|
+
# Standard XBRL instance (EX-101.INS) or iXBRL-tagged document
|
|
54
|
+
found = find { |a| a.document_type == "EX-101.INS" || a.ixbrl }
|
|
55
|
+
return found if found
|
|
56
|
+
|
|
57
|
+
# Fallback: look for _htm.xml extracted XBRL instance (common in modern
|
|
58
|
+
# iXBRL filings where the instance is embedded in the HTML and an XML
|
|
59
|
+
# copy is provided as a datafile)
|
|
60
|
+
find { |a| a.document&.end_with?("_htm.xml") }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def datafiles
|
|
64
|
+
select(&:xml?)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def documents
|
|
68
|
+
select { |a| a.html? || a.text? }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def to_s
|
|
72
|
+
"#<Attachments count=#{@attachments.length}>"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edgar
|
|
4
|
+
# A company identified by ticker symbol, extending Entity with SEC lookup.
|
|
5
|
+
class Company < Entity
|
|
6
|
+
attr_reader :ticker
|
|
7
|
+
|
|
8
|
+
def initialize(cik: nil, ticker: nil)
|
|
9
|
+
if ticker
|
|
10
|
+
@ticker = ticker.upcase
|
|
11
|
+
cik ||= Company.lookup_cik(@ticker)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
super(cik: cik)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def tickers
|
|
18
|
+
data.tickers
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def primary_ticker
|
|
22
|
+
tickers.first
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def exchanges
|
|
26
|
+
data.exchanges
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def sic
|
|
30
|
+
data.sic
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def industry
|
|
34
|
+
data.sic_description
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def fiscal_year_end
|
|
38
|
+
data.fiscal_year_end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def website
|
|
42
|
+
data.website
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def phone
|
|
46
|
+
data.phone
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def state_of_incorporation
|
|
50
|
+
data.state_of_incorporation
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def description
|
|
54
|
+
data.description
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def financials
|
|
58
|
+
@financials ||= begin
|
|
59
|
+
filing = find_latest_filing_by_form("10-K") ||
|
|
60
|
+
find_latest_filing_by_form("20-F") ||
|
|
61
|
+
find_latest_filing_by_form("40-F")
|
|
62
|
+
if filing
|
|
63
|
+
Financials.extract(filing) || Financials.new(cik: @cik)
|
|
64
|
+
else
|
|
65
|
+
Financials.new(cik: @cik)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def facts
|
|
71
|
+
@facts ||= EntityFacts.new(cik: @cik)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def latest_tenk
|
|
75
|
+
find_latest_filing_by_form("10-K")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def latest_tenq
|
|
79
|
+
find_latest_filing_by_form("10-Q")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def annual_financials
|
|
83
|
+
filing = latest_tenk
|
|
84
|
+
return Financials.extract(filing) if filing
|
|
85
|
+
|
|
86
|
+
%w[20-F 40-F].each do |form|
|
|
87
|
+
f = find_latest_filing_by_form(form)
|
|
88
|
+
return Financials.extract(f) if f
|
|
89
|
+
end
|
|
90
|
+
nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def quarterly_financials
|
|
94
|
+
filing = latest_tenq
|
|
95
|
+
return Financials.extract(filing) if filing
|
|
96
|
+
|
|
97
|
+
filing = find_latest_filing_by_form("6-K")
|
|
98
|
+
return Financials.extract(filing) if filing
|
|
99
|
+
|
|
100
|
+
nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def business_category
|
|
104
|
+
sic_str = sic.to_s
|
|
105
|
+
return "Bank" if %w[6021 6022 6035 6036].include?(sic_str)
|
|
106
|
+
return "Insurance Company" if %w[6311 6321 6331].include?(sic_str)
|
|
107
|
+
return "REIT" if sic_str == "6798"
|
|
108
|
+
|
|
109
|
+
et = data.entity_type.to_s.downcase
|
|
110
|
+
return "Investment Manager" if et.include?("investment")
|
|
111
|
+
return "Operating Company" if et == "operating"
|
|
112
|
+
|
|
113
|
+
"Operating Company"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def fund?
|
|
117
|
+
["ETF", "Mutual Fund", "Closed-End Fund"].include?(business_category)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def operating_company?
|
|
121
|
+
business_category == "Operating Company"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def financial_institution?
|
|
125
|
+
["Bank", "Insurance Company", "Investment Manager", "BDC"].include?(business_category)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def foreign?
|
|
129
|
+
%w[Foreign Canadian].include?(filer_type)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def filer_type
|
|
133
|
+
return "Domestic" if state_of_incorporation == "CA"
|
|
134
|
+
return "Foreign" unless %w[CA DE FL IL MA MD NJ NY NV OH PA TX VA WA].include?(state_of_incorporation)
|
|
135
|
+
|
|
136
|
+
"Domestic"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def to_s
|
|
140
|
+
ticker_str = @ticker || tickers.first
|
|
141
|
+
"#<Company #{ticker_str} (#{@cik})>"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def self.lookup_cik(ticker)
|
|
145
|
+
return @ticker_cache[ticker.upcase] if @ticker_cache&.key?(ticker.upcase)
|
|
146
|
+
|
|
147
|
+
data = load_company_data
|
|
148
|
+
mapping = {}
|
|
149
|
+
|
|
150
|
+
data.each do |entry|
|
|
151
|
+
t = entry[:ticker]
|
|
152
|
+
mapping[t] = entry[:cik]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
cik = mapping[ticker.upcase]
|
|
156
|
+
(@ticker_cache ||= {})[ticker.upcase] = cik if cik
|
|
157
|
+
cik
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def self.search(query, limit: 10)
|
|
161
|
+
return [] if query.nil? || query.strip.empty?
|
|
162
|
+
|
|
163
|
+
data = load_company_data
|
|
164
|
+
q = query.strip.downcase
|
|
165
|
+
|
|
166
|
+
matches = data.select do |entry|
|
|
167
|
+
entry[:ticker].downcase.include?(q) ||
|
|
168
|
+
entry[:title].downcase.include?(q)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
matches.first(limit)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def self.load_company_data
|
|
175
|
+
return @company_data_cache if @company_data_cache
|
|
176
|
+
|
|
177
|
+
@company_data_cache = []
|
|
178
|
+
url = Config.tickers_url
|
|
179
|
+
raw = Edgar.http_client.download_json(url)
|
|
180
|
+
|
|
181
|
+
raw.each_value do |entry|
|
|
182
|
+
@company_data_cache << {
|
|
183
|
+
cik: entry["cik_str"].to_i,
|
|
184
|
+
ticker: entry["ticker"],
|
|
185
|
+
title: entry["title"]
|
|
186
|
+
}
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
@company_data_cache
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def self.clear_search_cache!
|
|
193
|
+
@company_data_cache = nil
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
private_class_method :load_company_data
|
|
197
|
+
|
|
198
|
+
private
|
|
199
|
+
|
|
200
|
+
def find_latest_filing_by_form(form)
|
|
201
|
+
recent = data.recent_filings
|
|
202
|
+
forms = recent["form"] || []
|
|
203
|
+
dates = recent["filingDate"] || []
|
|
204
|
+
accs = recent["accessionNumber"] || []
|
|
205
|
+
idx = forms.index(form)
|
|
206
|
+
return nil unless idx
|
|
207
|
+
|
|
208
|
+
Filing.new(
|
|
209
|
+
cik: @cik,
|
|
210
|
+
company: display_name,
|
|
211
|
+
form: form,
|
|
212
|
+
filing_date: dates[idx],
|
|
213
|
+
accession_number: accs[idx]
|
|
214
|
+
)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
data/lib/edgar/config.rb
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edgar
|
|
4
|
+
# SEC endpoint URLs, rate limit constants, and URL builder methods.
|
|
5
|
+
module Config
|
|
6
|
+
SEC_BASE_URL = "https://www.sec.gov"
|
|
7
|
+
SEC_DATA_URL = "https://data.sec.gov"
|
|
8
|
+
SEC_ARCHIVE_URL = "#{SEC_BASE_URL}/Archives/edgar".freeze
|
|
9
|
+
|
|
10
|
+
EDGAR_RATE_LIMIT_PER_SEC = 9
|
|
11
|
+
EDGAR_IDENTITY = ENV.fetch("EDGAR_IDENTITY", "SampleCompanyName sample.email@example.com")
|
|
12
|
+
|
|
13
|
+
DEFAULT_TIMEOUT = 30
|
|
14
|
+
DEFAULT_CONNECT_TIMEOUT = 10
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
def user_agent
|
|
19
|
+
"#{EDGAR_IDENTITY} (Ruby edgar gem #{Edgar::VERSION})"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def submissions_url(cik)
|
|
23
|
+
padded = cik.to_s.rjust(10, "0")
|
|
24
|
+
"#{SEC_DATA_URL}/submissions/CIK#{padded}.json"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def company_facts_url(cik)
|
|
28
|
+
padded = cik.to_s.rjust(10, "0")
|
|
29
|
+
"#{SEC_DATA_URL}/api/xbrl/companyfacts/CIK#{padded}.json"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def full_index_url(year, quarter, type = "company")
|
|
33
|
+
"#{SEC_ARCHIVE_URL}/full-index/#{year}/QTR#{quarter}/#{type}.gz"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def daily_index_url(year, quarter, date)
|
|
37
|
+
"#{SEC_ARCHIVE_URL}/daily-index/#{year}/QTR#{quarter}/#{date}.idx"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def filing_homepage_url(cik, accession_number)
|
|
41
|
+
"#{SEC_ARCHIVE_URL}/data/#{cik}/#{accession_number}-index.html"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def filing_text_url(cik, accession_number)
|
|
45
|
+
no_dashes = accession_number.delete("-")
|
|
46
|
+
"#{SEC_ARCHIVE_URL}/data/#{cik}/#{no_dashes}/#{accession_number}.txt"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def filing_dir_url(cik, accession_number)
|
|
50
|
+
no_dashes = accession_number.delete("-")
|
|
51
|
+
"#{SEC_ARCHIVE_URL}/data/#{cik}/#{no_dashes}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def tickers_url
|
|
55
|
+
"#{SEC_BASE_URL}/files/company_tickers.json"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def submissions_file_url(_cik, filename)
|
|
59
|
+
"#{SEC_DATA_URL}/submissions/#{filename}"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|