sec_edgar 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ module SecEdgar
2
+ Address = Struct.new(:street1, :street2, :city, :state, :zip, :state_description)
3
+ end
@@ -0,0 +1,26 @@
1
+ module SecEdgar
2
+ module BasicStats
3
+ extend self
4
+
5
+ def precision(tp, fp)
6
+ tp.fdiv(tp + fp)
7
+ end
8
+
9
+ def recall(tp, fn)
10
+ tp.fdiv(tp + fn)
11
+ end
12
+
13
+ def f_beta(tp, fp, fn, beta)
14
+ p = precision(tp, fp)
15
+ r = recall(tp, fn)
16
+ b2 = beta ** 2
17
+ (1 + b2) * ((p * r).fdiv((b2 * p) + r))
18
+ end
19
+
20
+ def f1(tp, fp, fn)
21
+ p = precision(tp, fp)
22
+ r = recall(tp, fn)
23
+ 2 * ((p * r).fdiv(p + r))
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ module SecEdgar
2
+ DerivativeTransaction = Struct.new(
3
+ :acquired_or_disposed_code,
4
+ :conversion_or_exercise_price,
5
+ :deemed_execution_date,
6
+ :exercise_date,
7
+ :expiration_date,
8
+ :underlying_security_title,
9
+ :underlying_security_shares,
10
+ :nature_of_ownership,
11
+ :code,
12
+ :shares,
13
+ :security_title,
14
+ :direct_or_indirect_code,
15
+ :form_type,
16
+ :equity_swap_involved,
17
+ :transaction_date,
18
+ :shares_after,
19
+ :price_per_share
20
+ )
21
+ end
@@ -0,0 +1,74 @@
1
+ # encoding: UTF-8
2
+ module SecEdgar
3
+ class Entity
4
+ COLUMNS = [
5
+ :cik,
6
+ :name,
7
+ :mailing_address,
8
+ :business_address,
9
+ :assigned_sic,
10
+ :assigned_sic_desc,
11
+ :assigned_sic_href,
12
+ :assitant_director,
13
+ :cik_href,
14
+ :formerly_names,
15
+ :state_location,
16
+ :state_location_href,
17
+ :state_of_incorporation
18
+ ]
19
+
20
+ attr_accessor(*COLUMNS)
21
+
22
+ def initialize(entity)
23
+ COLUMNS.each do |column|
24
+ instance_variable_set("@#{ column }", entity[column.to_s])
25
+ end
26
+ end
27
+
28
+ def filings
29
+ Filing.find(@cik)
30
+ end
31
+
32
+ def transactions
33
+ Transaction.find(@cik)
34
+ end
35
+
36
+ def self.query(url)
37
+ RestClient.get(url) do |response, request, result, &block|
38
+ case response.code
39
+ when 200
40
+ return response
41
+ else
42
+ response.return!(request, result, &block)
43
+ end
44
+ end
45
+ end
46
+
47
+ # def self.find(entity_args)
48
+ # temp = {}
49
+ # temp[:url] = SecURI.browse_edgar_uri(entity_args)
50
+ # temp[:url][:action] = :getcompany
51
+ # response = query(temp[:url].output_atom.to_s)
52
+ # document = Nokogiri::HTML(response)
53
+ # xml = document.xpath("//feed/company-info")
54
+ # Entity.new(parse(xml))
55
+ # end
56
+ #
57
+ # def self.parse(xml)
58
+ # content = Hash.from_xml(xml.to_s)
59
+ # if content['company_info'].present?
60
+ # content = content['company_info']
61
+ # content['name'] = content.delete('conformed_name')
62
+ # if content['formerly_names'].present?
63
+ # content['formerly_names'] = content.delete('formerly_names')['names']
64
+ # end
65
+ # content['addresses']['address'].each do |address|
66
+ # content["#{address['type']}_address"] = address
67
+ # end
68
+ # return content
69
+ # else
70
+ # return {}
71
+ # end
72
+ # end
73
+ end
74
+ end
@@ -0,0 +1,171 @@
1
+ # encoding: UTF-8
2
+
3
+ module SecEdgar
4
+ class Filing
5
+ COLUMNS = [:cik, :title, :summary, :link, :term, :date, :file_id]
6
+
7
+ attr_accessor(*COLUMNS)
8
+
9
+ def initialize(filing)
10
+ COLUMNS.each do |column|
11
+ instance_variable_set("@#{ column }", filing[column])
12
+ end
13
+ end
14
+
15
+ def self.fetch(uri, &blk)
16
+ open(uri) do |rss|
17
+ parse_rss(rss, &blk)
18
+ end
19
+ end
20
+
21
+ def self.recent(options = {}, &blk)
22
+ start = options.fetch(:start, 0)
23
+ count = options.fetch(:count, 100)
24
+ limit = options.fetch(:limit, 100)
25
+ limited_count = [limit - start, count].min
26
+ fetch(uri_for_recent(start, limited_count), &blk)
27
+ start += count
28
+ return if start >= limit
29
+ recent({ start: start, count: count, limit: limit }, &blk)
30
+ rescue OpenURI::HTTPError => e
31
+ puts e
32
+ return
33
+ end
34
+
35
+ def self.for_cik(cik, options = {}, &blk)
36
+ start = options.fetch(:start, 0)
37
+ count = options.fetch(:count, 100)
38
+ limit = options.fetch(:limit, 100)
39
+ fetch(uri_for_cik(cik, start, count), &blk)
40
+ start += count
41
+ return if start >= limit
42
+ for_cik(cik, { start: start, count: count, limit: limit }, &blk)
43
+ rescue OpenURI::HTTPError
44
+ return
45
+ end
46
+
47
+ def self.for_date(date, &blk)
48
+ ftp = Net::FTP.new('ftp.sec.gov')
49
+ ftp.login
50
+ file_name = ftp.nlst("edgar/daily-index/#{ date.to_sec_uri_format }*")[0]
51
+ ftp.close
52
+ open("ftp://ftp.sec.gov/#{ file_name }") do |file|
53
+ if file_name[-2..-1] == 'gz'
54
+ gz_reader = Zlib::GzipReader.new(file)
55
+ gz_reader.rewind
56
+ filings_for_index(gz_reader).each(&blk)
57
+ else
58
+ filings_for_index(file).each(&blk)
59
+ end
60
+ end
61
+ rescue Net::FTPTempError
62
+ end
63
+
64
+ def self.filings_for_index(index)
65
+ [].tap do |filings|
66
+ content_section = false
67
+ index.each_line do |row|
68
+ content_section = true if row.include?('-------------')
69
+ next if !content_section || row.include?('------------')
70
+ filing = filing_for_index_row(row)
71
+ filings << filing unless filing.nil?
72
+ end
73
+ end
74
+ end
75
+
76
+ def self.filing_for_index_row(row)
77
+ data = row.split(/ /).reject(&:blank?).map(&:strip)
78
+ data = row.split(/ /).reject(&:blank?).map(&:strip) if data.count == 4
79
+ data[1].gsub!('/ADV', '')
80
+ data.delete_at(1) if data[1][0] == '/'
81
+ return nil unless Regexp.new(/\d{8}/).match(data[3])
82
+ unless data[4][0..3] == 'http'
83
+ data[4] = "http://www.sec.gov/Archives/#{ data[4] }"
84
+ end
85
+ Filing.new(
86
+ term: data[1],
87
+ cik: data[2],
88
+ date: Date.parse(data[3]),
89
+ link: data[4]
90
+ )
91
+ end
92
+
93
+ def self.uri_for_recent(start = 0, count = 100)
94
+ SecURI.browse_edgar_uri(
95
+ action: :getcurrent,
96
+ owner: :include,
97
+ output: :atom,
98
+ start: start,
99
+ count: count
100
+ )
101
+ end
102
+
103
+ def self.uri_for_cik(cik, start = 0, count = 100)
104
+ SecURI.browse_edgar_uri(
105
+ action: :getcompany,
106
+ owner: :include,
107
+ output: :atom,
108
+ start: start,
109
+ count: count,
110
+ CIK: cik
111
+ )
112
+ end
113
+
114
+ def self.parse_rss(rss, &blk)
115
+ feed = RSS::Parser.parse(rss, false)
116
+ feed.entries.each do |entry|
117
+ filing = Filing.new(
118
+ cik: entry.title.content.match(/\((\w{10})\)/)[1],
119
+ file_id: entry.id.content.split('=').last,
120
+ term: entry.category.term,
121
+ title: entry.title.content,
122
+ summary: entry.summary.content,
123
+ date: DateTime.parse(entry.updated.content.to_s),
124
+ link: entry.link.href.gsub('-index.htm', '.txt')
125
+ )
126
+ blk.call(filing)
127
+ end
128
+ end
129
+
130
+ # def self.find(cik, start = 0, count = 80)
131
+ # temp = {}
132
+ # temp[:url] = SecURI.browse_edgar_uri({cik: cik})
133
+ # temp[:url][:action] = :getcompany
134
+ # temp[:url][:start] = start
135
+ # temp[:url][:count] = count
136
+ # response = Entity.query(temp[:url].output_atom.to_s)
137
+ # document = Nokogiri::HTML(response)
138
+ # parse(cik, document)
139
+ # end
140
+
141
+ def self.parse(cik, document)
142
+ filings = []
143
+ if document.xpath('//content').to_s.length > 0
144
+ document.xpath('//content').each do |e|
145
+ if e.xpath('//content/accession-nunber').to_s.length > 0
146
+ content = Hash.from_xml(e.to_s)['content']
147
+ content[:cik] = cik
148
+ content[:file_id] = content.delete('accession_nunber')
149
+ content[:date] = content.delete('filing_date')
150
+ content[:link] = content.delete('filing_href')
151
+ content[:term] = content.delete('filing_type')
152
+ content[:title] = content.delete('form_name')
153
+ filings << Filing.new(content)
154
+ end
155
+ end
156
+ end
157
+ filings
158
+ end
159
+
160
+ def content(&error_blk)
161
+ @content ||= RestClient.get(self.link)
162
+ rescue RestClient::ResourceNotFound => e
163
+ puts "404 Resource Not Found: Bad link #{ self.link }"
164
+ if block_given?
165
+ error_blk.call(e, self)
166
+ else
167
+ raise e
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,216 @@
1
+ module SecEdgar
2
+ class FilingParser
3
+ attr_reader :xsd_uri
4
+
5
+ def initialize(filing)
6
+ @xsd_uri = 'lib/sec4/ownership4Document.xsd.xml'
7
+ @filing = filing
8
+ @content = filing.content
9
+ end
10
+
11
+ def content
12
+ if @content.start_with?('-----BEGIN PRIVACY-ENHANCED MESSAGE-----')
13
+ @content = strip_privacy_wrapper(@content)
14
+ end
15
+ @content
16
+ end
17
+
18
+ def doc
19
+ @doc ||= OwnershipDocument.new
20
+ end
21
+
22
+ def parse(&error_blk)
23
+ if block_given? && !xml_valid?
24
+ error_blk.call(xml_errors)
25
+ puts "Error: returning NilObjectDocument #{ @filing.link }"
26
+ return NilOwnershipDocument.new
27
+ end
28
+
29
+ footnotes | transactions | derivative_transactions # eager init
30
+ parse_doc(xml_doc)
31
+ parse_issuer(xml_doc.xpath('//issuer'))
32
+ parse_owner(xml_doc.xpath('//reportingOwner'))
33
+ parse_non_derivative_table(xml_doc.xpath('//nonDerivativeTable'))
34
+ parse_derivative_table(xml_doc.xpath('//derivativeTable'))
35
+ parse_footnotes(xml_doc.xpath('//footnotes'))
36
+ doc
37
+ end
38
+
39
+ def footnotes
40
+ doc.footnotes ||= []
41
+ end
42
+
43
+ def transactions
44
+ doc.transactions ||= []
45
+ end
46
+
47
+ def derivative_transactions
48
+ doc.derivative_transactions ||= []
49
+ end
50
+
51
+ def xml_filing
52
+ Nokogiri::XML(xml_doc.to_xml)
53
+ end
54
+
55
+ private
56
+
57
+ def strip_privacy_wrapper(raw)
58
+ lines = raw.split("\n")
59
+ lines.shift until lines.first.start_with?('<SEC-DOCUMENT>')
60
+ lines.pop if lines.last.start_with?('-----END PRIVACY-ENHANCED MESSAGE--')
61
+ lines.join("\n")
62
+ end
63
+
64
+ def parse_transaction(el)
65
+ transaction = Transaction.new
66
+ transaction.security_title = el.xpath('securityTitle').text.strip
67
+ transaction.transaction_date = Date.parse(el.xpath('transactionDate').text)
68
+
69
+ parse_transaction_amounts(transaction, el.xpath('transactionAmounts'))
70
+ parse_transaction_ownership_nature(transaction, el.xpath('ownershipNature'))
71
+ parse_transaction_coding(transaction, el.xpath('transactionCoding'))
72
+
73
+ # post transaction amounts
74
+ transaction.shares_after = el
75
+ .xpath('postTransactionAmounts/sharesOwnedFollowingTransaction/value')
76
+ .text
77
+ .to_f
78
+ transactions << transaction
79
+ end
80
+
81
+ def parse_footnote(el)
82
+ footnote = Footnote.new
83
+ footnote.content = el.text.strip
84
+ footnote.id = el.attribute("id").value
85
+ footnotes << footnote
86
+ end
87
+
88
+ def parse_derivative_transaction(el)
89
+ transaction = DerivativeTransaction.new
90
+
91
+ transaction.security_title = el.xpath('securityTitle').text.strip
92
+ transaction.transaction_date = Date.parse(el.xpath('transactionDate').text)
93
+ transaction.conversion_or_exercise_price = el.xpath('conversionOrExercisePrice').text.to_f
94
+
95
+ unless (expiration_date = el.xpath('expirationDate/value').text).blank?
96
+ transaction.expiration_date = Date.parse(expiration_date)
97
+ end
98
+
99
+ unless (exercise_date = el.xpath('exerciseDate/value').text).blank?
100
+ transaction.exercise_date = Date.parse(exercise_date)
101
+ end
102
+
103
+ parse_transaction_amounts(transaction, el.xpath('transactionAmounts'))
104
+ parse_transaction_ownership_nature(transaction, el.xpath('ownershipNature'))
105
+ parse_transaction_coding(transaction, el.xpath('transactionCoding'))
106
+ parse_transaction_underlying(transaction, el.xpath('underlyingSecurity'))
107
+
108
+ # post transaction amounts
109
+ transaction.shares_after = el
110
+ .xpath('postTransactionAmounts/sharesOwnedFollowingTransaction/value')
111
+ .text
112
+ .to_f
113
+ derivative_transactions << transaction
114
+ end
115
+
116
+ def parse_transaction_underlying(transaction, el)
117
+ transaction.underlying_security_title = el.xpath('underlyingSecurityTitle/value').text
118
+ transaction.underlying_security_shares =
119
+ el.xpath('underlyingSecurityShares/value').text.to_f
120
+ end
121
+
122
+ def parse_non_derivative_table(el)
123
+ el.xpath('//nonDerivativeTransaction').each do |transaction_el|
124
+ parse_transaction(transaction_el)
125
+ end
126
+ end
127
+
128
+ def parse_derivative_table(el)
129
+ el.xpath('//derivativeTransaction').each do |transaction_el|
130
+ parse_derivative_transaction(transaction_el)
131
+ end
132
+ end
133
+
134
+ def parse_footnotes(el)
135
+ el.xpath('//footnote').each do |note_el|
136
+ parse_footnote(note_el)
137
+ end
138
+ end
139
+
140
+ def parse_transaction_amounts(transaction, el)
141
+ transaction.acquired_or_disposed_code =
142
+ el.xpath('transactionAcquiredDisposedCode/value').text
143
+ transaction.shares =
144
+ el.xpath('transactionShares/value').text.to_f
145
+ transaction.price_per_share =
146
+ el.xpath('transactionPricePerShare/value').text.to_f
147
+ end
148
+
149
+ def parse_transaction_coding(transaction, el)
150
+ transaction.code = el.xpath('transactionCode').text
151
+ transaction.form_type = el.xpath('transactionFormType').text
152
+ transaction.equity_swap_involved = el.xpath('equitySwapInvolved').text == '1'
153
+ end
154
+
155
+ def parse_transaction_ownership_nature(transaction, el)
156
+ transaction.nature_of_ownership = el.xpath('natureOfOwnership/value').text
157
+ transaction.direct_or_indirect_code = el.xpath('directOrIndirectOwnership/value').text
158
+ end
159
+
160
+ def parse_doc(el)
161
+ doc.schema_version = el.xpath('//schemaVersion').text
162
+ doc.document_type = el.xpath('//documentType').text
163
+ doc.not_subject_to_section_16 =
164
+ (el.xpath('//notSubjectToSection16').text == '1' ||
165
+ el.xpath('//notSubjectToSection16').text == 'true')
166
+ doc.period_of_report = Date.parse(el.xpath('//periodOfReport').text)
167
+ rescue ArgumentError => e
168
+ puts "parse_doc error: #{ el.inspect }"
169
+ puts e
170
+ raise e
171
+ end
172
+
173
+ def parse_issuer(el)
174
+ doc.issuer_cik = el.xpath('//issuerCik').text
175
+ doc.issuer_name = el.xpath('//issuerName').text
176
+ doc.issuer_trading_symbol = el.xpath('//issuerTradingSymbol').text
177
+ end
178
+
179
+ def parse_owner(el)
180
+ doc.owner_cik = el.xpath('//rptOwnerCik').text
181
+ doc.owner_name = el.xpath('//rptOwnerName').text
182
+ doc.is_director = el.xpath('//isDirector').text == '1'
183
+ doc.is_ten_percent_owner = el.xpath('//isTenPercentOwner').text == '1'
184
+ doc.is_other = el.xpath('//isOther').text == '1'
185
+ doc.is_officer =
186
+ (el.xpath('//isOfficer').text == '1' ||
187
+ el.xpath('//isOfficer').text.downcase == 'true')
188
+ doc.officer_title = el.xpath('//officerTitle').text
189
+
190
+ address = Address.new
191
+ address.street1 = el.xpath('//rptOwnerStreet1').text
192
+ address.street2 = el.xpath('//rptOwnerStreet2').text
193
+ address.city = el.xpath('//rptOwnerCity').text
194
+ address.state = el.xpath('//rptOwnerState').text
195
+ address.zip = el.xpath('//rptOwnerZipCode').text
196
+ address.state_description = el.xpath('//rptOwnerStateDescription').text
197
+ doc.owner_address = address
198
+ end
199
+
200
+ def xml_doc
201
+ Nokogiri::XML(content).xpath('//ownershipDocument')
202
+ end
203
+
204
+ def xml_schema
205
+ @schema ||= Nokogiri::XML::Schema(IO.read(xsd_uri))
206
+ end
207
+
208
+ def xml_valid?
209
+ xml_errors.empty? && !content.blank?
210
+ end
211
+
212
+ def xml_errors
213
+ @errors ||= xml_schema.validate(xml_filing)
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,53 @@
1
+ # module SecEdgar
2
+ # class FilingPersister
3
+ # attr_reader :filing
4
+ #
5
+ # def initialize(filing)
6
+ # @filing = filing
7
+ # end
8
+ #
9
+ # def doc
10
+ # @doc ||= RawFiling.for_filing(filing).parsed
11
+ # end
12
+ #
13
+ # def persist!
14
+ # return form if form = Form4.find_by(link: filing.link)
15
+ # doc_json = doc.to_json
16
+ # return if doc_json == '{}'
17
+ #
18
+ # form = Form4.new(
19
+ # cik: filing.cik,
20
+ # title: filing.title,
21
+ # link: filing.link,
22
+ # term: filing.term,
23
+ # date: filing.date,
24
+ # file_id: filing.file_id,
25
+ # dollar_volume: doc.dollar_volume,
26
+ # document: { d: doc_json }
27
+ # )
28
+ # form.company = Company.where(cik: doc.issuer_cik).first_or_initialize
29
+ # form.company.update_attributes(
30
+ # name: doc.issuer_name,
31
+ # ticker: doc.issuer_trading_symbol.upcase
32
+ # )
33
+ #
34
+ # form.insider = Insider.where(cik: doc.owner_cik).first_or_initialize
35
+ # form.insider.update_attributes(
36
+ # name: doc.owner_name[0, 254]
37
+ # )
38
+ #
39
+ # form.day_traded_price = form.company.price_on(form.date)
40
+ # form.day_traded_volume = form.company.volume_on(form.date)
41
+ # form.plus_3_months_price = form.company.price_on(form.date + 3.months)
42
+ # form.plus_6_months_price = form.company.price_on(form.date + 6.months)
43
+ # form.plus_12_months_price = form.company.price_on(form.date + 12.months)
44
+ # # need to find a more detailed data source
45
+ # # form.price_to_earnings = Company.price_to_earnings_on(form.date)
46
+ # # form.price_to_book = Company.price_to_book_on(form.date)
47
+ #
48
+ # form.doc = doc
49
+ # form.save!
50
+ # form
51
+ # end
52
+ # end
53
+ # end
@@ -0,0 +1,52 @@
1
+ module SecEdgar
2
+ class FilingUpdater
3
+ attr_reader :form
4
+
5
+ def initialize(form)
6
+ @form = form
7
+ end
8
+
9
+ def doc
10
+ @doc ||= RawFiling.for_form(form).parsed
11
+ end
12
+
13
+ def update
14
+ doc_json = doc.to_json
15
+ return if doc_json == '{}'
16
+
17
+ form.update({
18
+ dollar_volume: doc.dollar_volume,
19
+ document: { d: doc_json }
20
+ })
21
+
22
+ unless form.company
23
+ form.company = Company.where(cik: doc.issuer_cik).first_or_initialize
24
+ form.company.update_attributes(
25
+ name: doc.issuer_name,
26
+ ticker: doc.issuer_trading_symbol.upcase
27
+ )
28
+ end
29
+
30
+ unless form.insider
31
+ form.insider = Insider.where(cik: doc.owner_cik).first_or_initialize
32
+ form.insider.update_attributes(
33
+ name: doc.owner_name[0, 254]
34
+ )
35
+ end
36
+
37
+ dt = Date.parse(form.date)
38
+ form.day_traded_price = form.company.price_on(dt)
39
+ form.day_traded_volume = form.company.volume_on(dt)
40
+ form.plus_3_months_price = form.company.price_on(dt + 3.months)
41
+ form.plus_6_months_price = form.company.price_on(dt + 6.months)
42
+ form.plus_12_months_price = form.company.price_on(dt + 12.months)
43
+ # need to find a more detailed data source
44
+ # form.price_to_earnings = Company.price_to_earnings_on(form.date)
45
+ # form.price_to_book = Company.price_to_book_on(form.date)
46
+
47
+ form.doc = doc
48
+ form.save!
49
+ form
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ module SecEdgar
2
+ Footnote = Struct.new(:id, :content)
3
+ end
@@ -0,0 +1,35 @@
1
+ module SecEdgar
2
+ class FtpClient
3
+ include Singleton
4
+
5
+ def initialize
6
+ @ftp = Net::FTP.new
7
+ @ftp.passive = true
8
+ connect
9
+ login
10
+
11
+ at_exit do
12
+ puts "Closing Sec4::FtpClient..."
13
+ @ftp.close
14
+ end
15
+ end
16
+
17
+ def fetch(remote_url, local_url)
18
+ @ftp.getbinaryfile(remote_url, local_url)
19
+ end
20
+
21
+ def connect
22
+ @ftp.connect('ftp.sec.gov', 21)
23
+ rescue => e
24
+ puts "Sec4::FtpClient connection failed"
25
+ puts e.message
26
+ end
27
+
28
+ def login
29
+ @ftp.login
30
+ rescue => e
31
+ puts "Sec4::FtpClient login failed"
32
+ puts e.message
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,39 @@
1
+ module Sec4
2
+ class NilOwnershipDocument
3
+ def document_type
4
+ '4'
5
+ end
6
+
7
+ def issuer_trading_symbol
8
+ 'NULL'
9
+ end
10
+
11
+ def is_director
12
+ false
13
+ end
14
+
15
+ def is_officer
16
+ false
17
+ end
18
+
19
+ def is_owner
20
+ false
21
+ end
22
+
23
+ def is_ten_percent_owner
24
+ false
25
+ end
26
+
27
+ def officer_title
28
+ ''
29
+ end
30
+
31
+ def transactions
32
+ []
33
+ end
34
+
35
+ def derivative_transactions
36
+ []
37
+ end
38
+ end
39
+ end