thirteen_f 0.1.2 → 0.2.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: c49261f579858881f09bf7f929b5e26ab270a3212f04d873ced78c8ff2cca5a7
4
- data.tar.gz: 7357540f69f1cd5e3dd754463d362e1f8efccebf23d2a1add14d9c8a26226049
3
+ metadata.gz: 97448bdbcdaea2e3d0e690584c24503c7d9e65c0140441ae8afa037b6a0d96b0
4
+ data.tar.gz: 4aaf17e842ee4b83693d1977fc3a36036adbdc4a65a2208b44191b0dc4237373
5
5
  SHA512:
6
- metadata.gz: 990cfd15c836e9337df3d364feb813a47e2fd20b7a1269cf622b564c52e296a2fbef56d91b84f7e530237ca3594290ad05c8dbd748abb30df4ab1164e5379858
7
- data.tar.gz: 2f120c6e8afc60a95b8be94358e8aa3f03ee5f19e7e87a37451a44408618146d75ffff8684986b22dcbe0ab2b040c863c2e9d73b557b89f8d1d1fafdd82770d2
6
+ metadata.gz: eacc83d2d8aaf67caff1807e5e4368e6c954ac5f4a59758c020241b56e53d018c430b2c7874d56a2761a8da0ad288f01fcba32ff8ab60dd651a86d822f3d4759
7
+ data.tar.gz: 483483c8d663da64483efa03a8292308f2425e201071155c87810d715e8d54470b6fb39ca643db6b9a96f06be1a23c58815458e726017318b88b5c7821ef0812
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ThirteenF
2
2
 
3
- A ruby interface for S.E.C. 13-F Data. There is a lot of great finance and
3
+ A ruby interface for S.E.C. 13F Data. There is a lot of great finance and
4
4
  investing data that is available for free, but few developer tools that let
5
5
  us interact with the data outside of commercial platforms. This library
6
6
  aims to remedy a small piece of this by providing a robust API to search and
@@ -12,6 +12,11 @@ Reports. What is a 13F Report? Please visit
12
12
  This library is meant to serve a lightweight API which developers can enhance
13
13
  via a persistence layer and/or UI in their own applications.
14
14
 
15
+ At this time, I am sure there are a number of edge cases the gem does
16
+ not handle. Also it only imports 13F's submitted as XML files, which only goes
17
+ back to 2013. It is a low priority for myself to robustly parse the text files
18
+ provided before this time.
19
+
15
20
  ## Installation
16
21
 
17
22
  Add this line to your application's Gemfile:
@@ -30,7 +35,65 @@ Or install it yourself as:
30
35
 
31
36
  ## Usage
32
37
 
33
- TODO: Write usage instructions here
38
+ ### Search
39
+
40
+ ```ruby
41
+ search = ThirteenF::Search.new('Berkshire Hathaway')
42
+ search = ThirteenF::Search.new('BERKSHIRE HATHAWAY INC')
43
+ search.get_companies
44
+ search.companies
45
+ ```
46
+
47
+ ### Companies
48
+
49
+ ```ruby
50
+ company = search.companies.first
51
+ company.get_filings # grabs 10 13F filings by default which is the minimum
52
+ company.get_filings(count: 20) # can supply an optional count keyword arg to get more filings
53
+ company.get_most_recent_holdings
54
+ company.most_recent_holdings # returns positions from more recent 13F filing
55
+
56
+ company.cik # type: String | ex: "0001067983"
57
+ company.name # type: String | ex: "BERKSHIRE HATHAWAY INC"
58
+ company.state_or_country # type: String | ex: "NE"
59
+ ```
60
+
61
+ ### Filings
62
+
63
+ ```ruby
64
+ company.get_filings
65
+ filing = company.filings.first
66
+ filing.company
67
+ filing.get_positions
68
+ filing.positions # returns the US public securities held by the company at the
69
+ # time of the period of the report
70
+
71
+ filing.index_url # type: String
72
+ filing.response_status # type: String | ex: "200 OK"
73
+ filing.period_of_report # type: Date or nil
74
+ filing.time_accepted # type: DateTime or nil
75
+ filing.table_html_url # type: String or nil
76
+ filing.table_xml_url # type: String or nil
77
+ filing.cover_page_html_url # String or nil
78
+ ```
79
+
80
+ ### Positions
81
+
82
+ ```ruby
83
+ position = filing.positions.first
84
+ position.filing
85
+
86
+ position.name_of_issuer # type: String | ex: "EBAY INC"
87
+ position.title_of_class # type: String | ex: "COM"
88
+ position.cusip # type: String | ex: "278642103"
89
+ position.value_in_thousands # type: Integer | ex: 722018
90
+ position.shares_or_principal_amount # type: Integer | ex: 19994970
91
+ position.shares_or_principal_amount_type # type: String | ex: "SH"
92
+ position.put_or_call # type: String or nil | ex: "PUT"
93
+ position.investment_discretion # type: String | ex: "DFND"
94
+ position.other_managers # type: String or nil | ex: "1,4,11"
95
+ position.voting_authority # type: Hash | ex: { sole: 19994970, shared: 0, none: 0 }
96
+ ```
34
97
 
35
98
  ## Development
36
99
 
@@ -37,7 +37,13 @@ class ThirteenF
37
37
  def get_most_recent_holdings
38
38
  get_filings unless filings
39
39
  most_recent_filing = filings.select(&:period_of_report).max_by(&:period_of_report)
40
- @most_recent_holdings = Position.from_xml_filing most_recent_filing
40
+ most_recent_filing.get_positions
41
+ @most_recent_holdings = most_recent_filing.positions
42
+ true
43
+ end
44
+
45
+ def get_filings(count: 10)
46
+ @filings = Filing.from_index_urls thirteen_f_urls(count: count), self
41
47
  true
42
48
  end
43
49
 
@@ -45,15 +51,10 @@ class ThirteenF
45
51
  "#{BASE_URL}/cgi-bin/browse-edgar?CIK=#{cik}"
46
52
  end
47
53
 
48
- def thirteen_f_filings_url(count: 100)
54
+ def thirteen_f_filings_url(count: 10)
49
55
  "#{sec_filings_page_url}&type=13f&count=#{count}"
50
56
  end
51
57
 
52
- def get_filings(count: 100)
53
- @filings = Filing.from_index_urls thirteen_f_urls(count: count)
54
- true
55
- end
56
-
57
58
  private
58
59
  def self.parse_name(name_cell)
59
60
  name_cell.text.split("\n").first
@@ -7,23 +7,23 @@ class ThirteenF
7
7
  class Filing
8
8
  attr_reader :index_url, :table_html_url, :table_xml_url,
9
9
  :cover_page_html_url, :cover_page_xml_url, :complete_text_file_url,
10
- :period_of_report, :time_accepted, :response_status
10
+ :period_of_report, :time_accepted, :response_status, :company, :positions
11
11
 
12
12
  BASE_URL = 'https://www.sec.gov'
13
13
 
14
- def self.from_index_urls(urls)
14
+ def self.from_index_urls(urls, company)
15
15
  redo_count = 0
16
16
  urls.map do |index_url|
17
17
  response = HTTP.get index_url
18
18
  sleep 0.33
19
19
  if response.status == 200
20
20
  redo_count = 0
21
- attributes = set_attributes(response, index_url)
21
+ attributes = set_attributes(response, index_url, company)
22
22
  new(**attributes)
23
23
  else
24
24
  redo_count += 1
25
25
  redo unless redo_count > 1
26
- attributes = bad_response_attributes(response, index_url)
26
+ attributes = bad_response_attributes(response, index_url, company)
27
27
  new(**attributes)
28
28
  end
29
29
  end
@@ -42,10 +42,11 @@ class ThirteenF
42
42
  end
43
43
  end
44
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)
45
+ def initialize(response_status:, index_url:, company:,
46
+ complete_text_file_url:, period_of_report:, time_accepted:,
47
+ table_html_url: nil, table_xml_url: nil,
48
+ cover_page_html_url: nil, cover_page_xml_url: nil)
49
+ @company = company
49
50
  @response_status = response_status
50
51
  @index_url = index_url
51
52
  @table_html_url = table_html_url
@@ -58,11 +59,18 @@ class ThirteenF
58
59
  true
59
60
  end
60
61
 
62
+ def get_positions
63
+ return false unless period_of_report
64
+ @positions = Position.from_xml_filing self
65
+ true
66
+ end
67
+
61
68
  private
62
- def self.set_attributes(response, index_url)
69
+ def self.set_attributes(response, index_url, company)
63
70
  page = Nokogiri::HTML response.to_s
64
71
  table_links = page.search('table.tableFile')[0].search('a')
65
72
  attributes = Hash.new
73
+ attributes[:company] = company
66
74
  attributes[:response_status] = response.status.to_s
67
75
  attributes[:index_url] = index_url
68
76
  attributes[:period_of_report] = get_period_of_report page
@@ -98,8 +106,9 @@ class ThirteenF
98
106
  attributes
99
107
  end
100
108
 
101
- def self.bad_response_attributes(response, index_url)
109
+ def self.bad_response_attributes(response, index_url, company)
102
110
  attributes = Hash.new
111
+ attributes[:company] = company
103
112
  attributes[:response_status] = response.status.to_s
104
113
  attributes[:index_url] = index_url
105
114
  attributes[:period_of_report] = nil
@@ -108,12 +117,12 @@ class ThirteenF
108
117
  attributes
109
118
  end
110
119
 
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)
120
+ def assign_attributes(response_status:, index_url:, company:,
121
+ complete_text_file_url:, period_of_report:,
122
+ time_accepted:, table_html_url: nil, table_xml_url:
123
+ nil, cover_page_html_url: nil,
124
+ cover_page_xml_url: nil)
115
125
  @response_status = response_status
116
- @index_url = index_url
117
126
  @table_html_url = table_html_url
118
127
  @table_xml_url = table_xml_url
119
128
  @cover_page_html_url = cover_page_html_url
@@ -9,7 +9,7 @@ class ThirteenF
9
9
  :investment_discretion, :other_managers, :voting_authority, :filing
10
10
 
11
11
  def self.from_xml_filing(filing)
12
- return unless filing.table_xml_url
12
+ return nil unless filing.table_xml_url
13
13
  response = HTTP.get filing.table_xml_url
14
14
  xml_doc = Nokogiri::XML response.to_s
15
15
  xml_doc.search('infoTable').map do |info_table|
@@ -27,9 +27,9 @@ class ThirteenF
27
27
  @name_of_issuer = info_table.search('nameOfIssuer').text
28
28
  @title_of_class = info_table.search('titleOfClass').text
29
29
  @cusip = info_table.search('cusip').text
30
- @value_in_thousands = info_table.search('value').text
30
+ @value_in_thousands = to_integer(info_table.search('value').text)
31
31
  @shares_or_principal_amount_type = info_table.search('sshPrnamtType').text
32
- @shares_or_principal_amount = info_table.search('sshPrnamt').text
32
+ @shares_or_principal_amount = to_integer(info_table.search('sshPrnamt').text)
33
33
 
34
34
  not_found = info_table.search('putCall').count == 0
35
35
  @put_or_call = info_table.search('putCall').text unless not_found
@@ -37,11 +37,16 @@ class ThirteenF
37
37
  @investment_discretion = info_table.search('investmentDiscretion').text
38
38
  @other_managers = info_table.search('otherManager').text
39
39
  @voting_authority = {
40
- sole: info_table.search('Sole').text,
41
- shared: info_table.search('Shared').text,
42
- none: info_table.search('None').text
40
+ sole: to_integer(info_table.search('Sole').text),
41
+ shared: to_integer(info_table.search('Shared').text),
42
+ none: to_integer(info_table.search('None').text)
43
43
  }
44
44
  end
45
+
46
+ private
47
+ def to_integer(text)
48
+ text.delete(',').to_i
49
+ end
45
50
  end
46
51
  end
47
52
 
@@ -1,3 +1,3 @@
1
1
  class ThirteenF
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.1"
3
3
  end
data/thirteen_f.gemspec CHANGED
@@ -6,13 +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{A ruby interface for S.E.C. 13-F Data.}
9
+ spec.summary = %q{A ruby interface for S.E.C. 13F Data.}
10
10
 
11
- spec.description = %q{thirteen_f lets you easily interact with SEC 13-F
12
- filing data in all the ways we think normal human beings might want to. The
13
- SEC is the U.S. Securities and Exchange Commission. 13-F filings are
14
- disclosures large institutional investors in public securites have to provide
15
- and make public every quarter.}
11
+ spec.description = %q{thirteen_f lets you easily search and retrieve SEC 13F
12
+ filing data. The SEC is the U.S. Securities and Exchange Commission. 13F
13
+ filings are disclosures large institutional investors in public securites have
14
+ to provide and make public every quarter. It is a great way to follow what
15
+ different investors have been doing.}
16
16
  spec.homepage = "https://github.com/fordfischer/thirteen_f"
17
17
  spec.license = "MIT"
18
18
  spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thirteen_f
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - fordfischer
@@ -67,11 +67,11 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  description: |-
70
- thirteen_f lets you easily interact with SEC 13-F
71
- filing data in all the ways we think normal human beings might want to. The
72
- SEC is the U.S. Securities and Exchange Commission. 13-F filings are
73
- disclosures large institutional investors in public securites have to provide
74
- and make public every quarter.
70
+ thirteen_f lets you easily search and retrieve SEC 13F
71
+ filing data. The SEC is the U.S. Securities and Exchange Commission. 13F
72
+ filings are disclosures large institutional investors in public securites have
73
+ to provide and make public every quarter. It is a great way to follow what
74
+ different investors have been doing.
75
75
  email:
76
76
  - fordfischer07@gmail.com
77
77
  executables: []
@@ -120,5 +120,5 @@ requirements: []
120
120
  rubygems_version: 3.1.2
121
121
  signing_key:
122
122
  specification_version: 4
123
- summary: A ruby interface for S.E.C. 13-F Data.
123
+ summary: A ruby interface for S.E.C. 13F Data.
124
124
  test_files: []