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 +4 -4
- data/README.md +65 -2
- data/lib/thirteen_f/company.rb +8 -7
- data/lib/thirteen_f/filing.rb +24 -15
- data/lib/thirteen_f/position.rb +11 -6
- data/lib/thirteen_f/version.rb +1 -1
- data/thirteen_f.gemspec +6 -6
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97448bdbcdaea2e3d0e690584c24503c7d9e65c0140441ae8afa037b6a0d96b0
|
4
|
+
data.tar.gz: 4aaf17e842ee4b83693d1977fc3a36036adbdc4a65a2208b44191b0dc4237373
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
-
|
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
|
|
data/lib/thirteen_f/company.rb
CHANGED
@@ -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
|
-
|
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:
|
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
|
data/lib/thirteen_f/filing.rb
CHANGED
@@ -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:,
|
46
|
-
period_of_report:, time_accepted:,
|
47
|
-
|
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:,
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
data/lib/thirteen_f/position.rb
CHANGED
@@ -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
|
|
data/lib/thirteen_f/version.rb
CHANGED
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.
|
9
|
+
spec.summary = %q{A ruby interface for S.E.C. 13F Data.}
|
10
10
|
|
11
|
-
spec.description = %q{thirteen_f lets you easily
|
12
|
-
filing data
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
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
|
71
|
-
filing data
|
72
|
-
|
73
|
-
|
74
|
-
|
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.
|
123
|
+
summary: A ruby interface for S.E.C. 13F Data.
|
124
124
|
test_files: []
|