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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: faa86b34f746b8c24e08c909422945e7d16c1fc970532f0ed47b030484f3c75b
4
- data.tar.gz: 2f6bea3847ab8ae71d4ad93935935f736b3f6aa2dbf7857ebc3ddd118d6f46cc
3
+ metadata.gz: 3b6299aaa6691236c2f61152986f601cb6714609759b85ecbfac375d74522e32
4
+ data.tar.gz: d9135d7b9f522841ce392cffcada7db8b8bf557bc071ed05602f904ebaf794ef
5
5
  SHA512:
6
- metadata.gz: bae8ed73b154e06568f55d4debeef353dc67bf5dfef321f52a9a42c126177ed4ebcd92fa21e6eb0c2fdf307258b5d02faa8c09965a895de6d37e8e352b2f8b71
7
- data.tar.gz: 876b5c9291b0845f14932e36143c9e5d19f5dc723a826975a0fceb390421d8ce4a522b78891ca87394b5b4a403543e5274a6719ee798d864e9b3228c1f2e0713
6
+ metadata.gz: 62336e373ed54038ac532e0500eae824be5886e876bc24eff2b1be255ceb85e20585f069348a63baa6e3ed9bb96953eb03aa8af92e8c8c40557096ad9808a5a4
7
+ data.tar.gz: 3a97bb255bb67dae639ef478e5954fa64b1dbb479ae6b50707bc7e3b356d2d128209b6c11237e4c4af287765253dd2abb4ec74c71b76721c607e0c6919d6dbff
data/README.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # ThirteenF
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/thirteen_f`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
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
 
@@ -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 get_links_to_filings
46
- response = HTTP.get thirteen_f_filings_url
47
- page = Nokogiri::HTML response.to_s
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
 
@@ -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
- def initialize
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
+
@@ -25,7 +25,7 @@ class ThirteenF
25
25
  else
26
26
  raise 'SEC results are not available right now'
27
27
  end
28
-
28
+ true
29
29
  end
30
30
 
31
31
  private
@@ -1,3 +1,3 @@
1
1
  class ThirteenF
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
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{thirteen_f lets you easily interact with SEC 13-F
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 and make
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.0
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-28 00:00:00.000000000 Z
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 and make
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: thirteen_f lets you easily interact with SEC 13-F filing data in all the
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: []