thirteen_f 0.1.0 → 0.1.1
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 +4 -4
- data/README.md +11 -3
- data/lib/thirteen_f/company.rb +12 -8
- data/lib/thirteen_f/filing.rb +118 -1
- data/lib/thirteen_f/search.rb +1 -1
- data/lib/thirteen_f/version.rb +1 -1
- data/thirteen_f.gemspec +3 -7
- metadata +5 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b6299aaa6691236c2f61152986f601cb6714609759b85ecbfac375d74522e32
|
4
|
+
data.tar.gz: d9135d7b9f522841ce392cffcada7db8b8bf557bc071ed05602f904ebaf794ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62336e373ed54038ac532e0500eae824be5886e876bc24eff2b1be255ceb85e20585f069348a63baa6e3ed9bb96953eb03aa8af92e8c8c40557096ad9808a5a4
|
7
|
+
data.tar.gz: 3a97bb255bb67dae639ef478e5954fa64b1dbb479ae6b50707bc7e3b356d2d128209b6c11237e4c4af287765253dd2abb4ec74c71b76721c607e0c6919d6dbff
|
data/README.md
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
# ThirteenF
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
A ruby interface for S.E.C. 13-F Data. There is a lot of great finance and
|
4
|
+
investing data that is available for free, but few developer tools that let
|
5
|
+
us interact with the data outside of commercial platforms. This library
|
6
|
+
aims to remedy a small piece of this by providing a robust API to search and
|
7
|
+
retrieve investment holdings data of institutional investors through 13F
|
8
|
+
Reports. What is a 13F Report? Please visit
|
9
|
+
[this S.E.C. webpage](https://www.sec.gov/fast-answers/answers-form13fhtm.html)
|
10
|
+
for a full description.
|
11
|
+
|
12
|
+
This library is meant to serve a lightweight API which developers can enhance
|
13
|
+
via a persistence layer in their own applications.
|
6
14
|
|
7
15
|
## Installation
|
8
16
|
|
data/lib/thirteen_f/company.rb
CHANGED
@@ -4,7 +4,7 @@ require 'http'
|
|
4
4
|
|
5
5
|
class ThirteenF
|
6
6
|
class Company
|
7
|
-
attr_reader :cik, :name, :state_or_country
|
7
|
+
attr_reader :cik, :name, :state_or_country, :filings
|
8
8
|
|
9
9
|
BASE_URL = 'https://www.sec.gov'
|
10
10
|
|
@@ -42,19 +42,23 @@ class ThirteenF
|
|
42
42
|
"#{sec_filings_page_url}&type=13f&count=#{count}"
|
43
43
|
end
|
44
44
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
# compare these two - documents_button would be more direct
|
49
|
-
rows = page.search('table/tableFile2 > tr')[1..-1]
|
50
|
-
btns = page.search('#documentsbutton')
|
51
|
-
p [rows.count, btns.count]
|
45
|
+
def get_filings(count: 100)
|
46
|
+
@filings = Filing.from_index_urls thirteen_f_urls(count: count)
|
47
|
+
true
|
52
48
|
end
|
53
49
|
|
54
50
|
private
|
55
51
|
def self.parse_name(name_cell)
|
56
52
|
name_cell.text.split("\n").first
|
57
53
|
end
|
54
|
+
|
55
|
+
def thirteen_f_urls(count: 100)
|
56
|
+
response = HTTP.get thirteen_f_filings_url(count: count)
|
57
|
+
page = Nokogiri::HTML response.to_s
|
58
|
+
page.search('#documentsbutton').map do |btn|
|
59
|
+
"#{BASE_URL + btn.attributes['href'].value}"
|
60
|
+
end
|
61
|
+
end
|
58
62
|
end
|
59
63
|
end
|
60
64
|
|
data/lib/thirteen_f/filing.rb
CHANGED
@@ -1,11 +1,128 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'http'
|
4
|
+
require 'date'
|
4
5
|
|
5
6
|
class ThirteenF
|
6
7
|
class Filing
|
7
|
-
|
8
|
+
attr_reader :index_url, :table_html_url, :table_xml_url,
|
9
|
+
:cover_page_html_url, :cover_page_xml_url, :complete_text_file_url,
|
10
|
+
:period_of_report, :time_accepted, :response_status
|
11
|
+
|
12
|
+
BASE_URL = 'https://www.sec.gov'
|
13
|
+
|
14
|
+
def self.from_index_urls(urls)
|
15
|
+
redo_count = 0
|
16
|
+
urls.map do |index_url|
|
17
|
+
response = HTTP.get index_url
|
18
|
+
sleep 0.33
|
19
|
+
if response.status == 200
|
20
|
+
redo_count = 0
|
21
|
+
attributes = set_attributes(response, index_url)
|
22
|
+
new(**attributes)
|
23
|
+
else
|
24
|
+
redo_count += 1
|
25
|
+
redo unless redo_count > 1
|
26
|
+
attributes = bad_response_attributes(response, index_url)
|
27
|
+
new(**attributes)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset_attributes_from_index_url
|
33
|
+
return unless index_url
|
34
|
+
response = HTTP.get index_url
|
35
|
+
sleep 0.33
|
36
|
+
if response.status == 200
|
37
|
+
attributes = self.class.set_attributes(response, index_url)
|
38
|
+
assign_attributes(**attributes)
|
39
|
+
true
|
40
|
+
else
|
41
|
+
false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(response_status:, index_url:, complete_text_file_url:,
|
46
|
+
period_of_report:, time_accepted:, table_html_url: nil,
|
47
|
+
table_xml_url: nil, cover_page_html_url: nil,
|
48
|
+
cover_page_xml_url: nil)
|
49
|
+
@response_status = response_status
|
50
|
+
@index_url = index_url
|
51
|
+
@table_html_url = table_html_url
|
52
|
+
@table_xml_url = table_xml_url
|
53
|
+
@cover_page_html_url = cover_page_html_url
|
54
|
+
@cover_page_xml_url = cover_page_xml_url
|
55
|
+
@complete_text_file_url = complete_text_file_url
|
56
|
+
@period_of_report = period_of_report
|
57
|
+
@time_accepted = time_accepted
|
8
58
|
true
|
9
59
|
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def self.set_attributes(response, index_url)
|
63
|
+
page = Nokogiri::HTML response.to_s
|
64
|
+
table_links = page.search('table.tableFile')[0].search('a')
|
65
|
+
attributes = Hash.new
|
66
|
+
attributes[:response_status] = response.status.to_s
|
67
|
+
attributes[:index_url] = index_url
|
68
|
+
attributes[:period_of_report] = get_period_of_report page
|
69
|
+
attributes[:time_accepted] = get_time_accepted page
|
70
|
+
attributes[:complete_text_file_url] = "#{BASE_URL + table_links[-1].attributes['href'].value}"
|
71
|
+
if table_links.count == 5
|
72
|
+
attributes = xml_present(attributes, table_links)
|
73
|
+
end
|
74
|
+
attributes
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.get_period_of_report(page)
|
78
|
+
period_header_div = page.search('div.infoHead').find do |div|
|
79
|
+
div.text.include?('Period of Report')
|
80
|
+
end
|
81
|
+
period_string = period_header_div.next.next.text.strip
|
82
|
+
Date.parse period_string
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.get_time_accepted(page)
|
86
|
+
accepted_header_div = page.search('div.infoHead').find do |div|
|
87
|
+
div.text.include?('Accepted')
|
88
|
+
end
|
89
|
+
accepted_string = accepted_header_div.next.next.text.strip
|
90
|
+
DateTime.parse accepted_string
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.xml_present(attributes, table_links)
|
94
|
+
attributes[:table_html_url] = "#{BASE_URL + table_links[2].attributes['href'].value}"
|
95
|
+
attributes[:table_xml_url] = "#{BASE_URL + table_links[3].attributes['href'].value}"
|
96
|
+
attributes[:cover_page_html_url] = "#{BASE_URL + table_links[0].attributes['href'].value}"
|
97
|
+
attributes[:cover_page_xml_url] = "#{BASE_URL + table_links[1].attributes['href'].value}"
|
98
|
+
attributes
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.bad_response_attributes(response, index_url)
|
102
|
+
attributes = Hash.new
|
103
|
+
attributes[:response_status] = response.status.to_s
|
104
|
+
attributes[:index_url] = index_url
|
105
|
+
attributes[:period_of_report] = nil
|
106
|
+
attributes[:time_accepted] = nil
|
107
|
+
attributes[:complete_text_file_url] = nil
|
108
|
+
attributes
|
109
|
+
end
|
110
|
+
|
111
|
+
def assign_attributes(response_status:, index_url:, complete_text_file_url:,
|
112
|
+
period_of_report:, time_accepted:, table_html_url: nil,
|
113
|
+
table_xml_url: nil, cover_page_html_url: nil,
|
114
|
+
cover_page_xml_url: nil)
|
115
|
+
@response_status = response_status
|
116
|
+
@index_url = index_url
|
117
|
+
@table_html_url = table_html_url
|
118
|
+
@table_xml_url = table_xml_url
|
119
|
+
@cover_page_html_url = cover_page_html_url
|
120
|
+
@cover_page_xml_url = cover_page_xml_url
|
121
|
+
@complete_text_file_url = complete_text_file_url
|
122
|
+
@period_of_report = period_of_report
|
123
|
+
@time_accepted = time_accepted
|
124
|
+
true
|
125
|
+
end
|
10
126
|
end
|
11
127
|
end
|
128
|
+
|
data/lib/thirteen_f/search.rb
CHANGED
data/lib/thirteen_f/version.rb
CHANGED
data/thirteen_f.gemspec
CHANGED
@@ -6,17 +6,13 @@ Gem::Specification.new do |spec|
|
|
6
6
|
spec.authors = ["fordfischer"]
|
7
7
|
spec.email = ["fordfischer07@gmail.com"]
|
8
8
|
|
9
|
-
spec.summary = %q{
|
10
|
-
filing data in all the ways we think normal human beings might want to. The
|
11
|
-
SEC is the U.S. Securities and Exchange Commission. 13-F filings are
|
12
|
-
disclosures large investors in public securites have to provide and make
|
13
|
-
public every quarter.}
|
9
|
+
spec.summary = %q{A ruby interface for S.E.C. 13-F Data.}
|
14
10
|
|
15
11
|
spec.description = %q{thirteen_f lets you easily interact with SEC 13-F
|
16
12
|
filing data in all the ways we think normal human beings might want to. The
|
17
13
|
SEC is the U.S. Securities and Exchange Commission. 13-F filings are
|
18
|
-
disclosures large investors in public securites have to provide
|
19
|
-
public every quarter.}
|
14
|
+
disclosures large institutional investors in public securites have to provide
|
15
|
+
and make public every quarter.}
|
20
16
|
spec.homepage = "https://github.com/fordfischer/thirteen_f"
|
21
17
|
spec.license = "MIT"
|
22
18
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thirteen_f
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fordfischer
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03-
|
11
|
+
date: 2020-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -70,8 +70,8 @@ description: |-
|
|
70
70
|
thirteen_f lets you easily interact with SEC 13-F
|
71
71
|
filing data in all the ways we think normal human beings might want to. The
|
72
72
|
SEC is the U.S. Securities and Exchange Commission. 13-F filings are
|
73
|
-
disclosures large investors in public securites have to provide
|
74
|
-
public every quarter.
|
73
|
+
disclosures large institutional investors in public securites have to provide
|
74
|
+
and make public every quarter.
|
75
75
|
email:
|
76
76
|
- fordfischer07@gmail.com
|
77
77
|
executables: []
|
@@ -119,8 +119,5 @@ requirements: []
|
|
119
119
|
rubygems_version: 3.1.2
|
120
120
|
signing_key:
|
121
121
|
specification_version: 4
|
122
|
-
summary:
|
123
|
-
ways we think normal human beings might want to. The SEC is the U.S. Securities
|
124
|
-
and Exchange Commission. 13-F filings are disclosures large investors in public
|
125
|
-
securites have to provide and make public every quarter.
|
122
|
+
summary: A ruby interface for S.E.C. 13-F Data.
|
126
123
|
test_files: []
|