yf_as_dataframe 0.3.1 → 0.4.0

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: 33f890b28552ff173c4aa6949a073d5e5dae98d9531a00b6b7c4b3028a5cb493
4
- data.tar.gz: 7056aee7a785f649493200065a3cf8ef77ad06cecd89c1b446d64a1277efed68
3
+ metadata.gz: 13496d1eaf3e5ce09c9477de83957acb3d2183214eec2b0dedabdf8b612bd80b
4
+ data.tar.gz: 604c01e5bba073e9350146c6636c6ce29f1ac62a7d2a9a4e6e9338a33f1a65e0
5
5
  SHA512:
6
- metadata.gz: 7bbc4573fa5cd0dcbb76de1026551bd49909d71355184cd2fafe52ec155daba4a2503659fec21eddb2cfe170baec08dfc8d16b53ebcdb0df6f895bd61ad75423
7
- data.tar.gz: 8559c84bfd359ed3b7d4fba8d87fc201a4207a95067f5afa664b882b30adccfb98c4f5cbbe34e05a644a28aa05449d2205ec8c1906da2e6864d0ee67c1a65d86
6
+ metadata.gz: e758fafbc0396c7582a9ad0654c94d7721afd2457a403408376be07fa7ace874887a83499c4507a122f1d027a0126da31c9447cb5b61a3d9fc3ab190e7e36f96
7
+ data.tar.gz: 74a1263cd148add7c56417b4912052471ae6c49fc4163daacc5d5ce3988e53135802f4ec7912aeef372e735fec470072921f15f536d628e5449315fb31ea3142
data/Gemfile.lock ADDED
@@ -0,0 +1,99 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ yf_as_dataframe (0.4.0)
5
+ activesupport
6
+ httparty
7
+ nokogiri
8
+ polars-df (~> 0.12.0)
9
+ tulirb
10
+ tzinfo
11
+ tzinfo-data
12
+ zache
13
+
14
+ GEM
15
+ remote: https://rubygems.org/
16
+ specs:
17
+ activesupport (7.2.2.1)
18
+ base64
19
+ benchmark (>= 0.3)
20
+ bigdecimal
21
+ concurrent-ruby (~> 1.0, >= 1.3.1)
22
+ connection_pool (>= 2.2.5)
23
+ drb
24
+ i18n (>= 1.6, < 2)
25
+ logger (>= 1.4.2)
26
+ minitest (>= 5.1)
27
+ securerandom (>= 0.3)
28
+ tzinfo (~> 2.0, >= 2.0.5)
29
+ ast (2.4.3)
30
+ base64 (0.3.0)
31
+ benchmark (0.4.1)
32
+ bigdecimal (3.2.2)
33
+ concurrent-ruby (1.3.5)
34
+ connection_pool (2.5.3)
35
+ csv (3.3.5)
36
+ drb (2.2.3)
37
+ httparty (0.23.1)
38
+ csv
39
+ mini_mime (>= 1.0.0)
40
+ multi_xml (>= 0.5.2)
41
+ i18n (1.14.7)
42
+ concurrent-ruby (~> 1.0)
43
+ json (2.12.2)
44
+ language_server-protocol (3.17.0.5)
45
+ lint_roller (1.1.0)
46
+ logger (1.7.0)
47
+ mini_mime (1.1.5)
48
+ minitest (5.25.5)
49
+ multi_xml (0.7.1)
50
+ bigdecimal (~> 3.1)
51
+ nokogiri (1.18.8-arm64-darwin)
52
+ racc (~> 1.4)
53
+ parallel (1.27.0)
54
+ parser (3.3.8.0)
55
+ ast (~> 2.4.1)
56
+ racc
57
+ polars-df (0.12.0-arm64-darwin)
58
+ bigdecimal
59
+ prism (1.4.0)
60
+ racc (1.8.1)
61
+ rainbow (3.1.1)
62
+ rake (13.3.0)
63
+ regexp_parser (2.10.0)
64
+ rubocop (1.77.0)
65
+ json (~> 2.3)
66
+ language_server-protocol (~> 3.17.0.2)
67
+ lint_roller (~> 1.1.0)
68
+ parallel (~> 1.10)
69
+ parser (>= 3.3.0.2)
70
+ rainbow (>= 2.2.2, < 4.0)
71
+ regexp_parser (>= 2.9.3, < 3.0)
72
+ rubocop-ast (>= 1.45.1, < 2.0)
73
+ ruby-progressbar (~> 1.7)
74
+ unicode-display_width (>= 2.4.0, < 4.0)
75
+ rubocop-ast (1.45.1)
76
+ parser (>= 3.3.7.2)
77
+ prism (~> 1.4)
78
+ ruby-progressbar (1.13.0)
79
+ securerandom (0.4.1)
80
+ tulirb (1.0.0)
81
+ tzinfo (2.0.6)
82
+ concurrent-ruby (~> 1.0)
83
+ tzinfo-data (1.2025.2)
84
+ tzinfo (>= 1.0.0)
85
+ unicode-display_width (3.1.4)
86
+ unicode-emoji (~> 4.0, >= 4.0.4)
87
+ unicode-emoji (4.0.4)
88
+ zache (0.15.0)
89
+
90
+ PLATFORMS
91
+ arm64-darwin-23
92
+
93
+ DEPENDENCIES
94
+ rake (~> 13.0)
95
+ rubocop (~> 1.21)
96
+ yf_as_dataframe!
97
+
98
+ BUNDLED WITH
99
+ 2.6.9
@@ -0,0 +1,227 @@
1
+ # Minimal Curl-Impersonate Integration
2
+
3
+ ## Overview
4
+
5
+ This is a minimal integration that makes curl-impersonate the **default behavior** for all Yahoo Finance requests. No changes to your existing code are required - curl-impersonate is used automatically to bypass TLS fingerprinting.
6
+
7
+ ## Installation
8
+
9
+ ### 1. Install curl-impersonate
10
+
11
+ ```bash
12
+ # macOS
13
+ brew tap shakacode/brew
14
+ brew install curl-impersonate
15
+
16
+ # Verify installation
17
+ ls -la /usr/local/bin/curl_*
18
+ ```
19
+
20
+ ### 2. Custom Installation Directory (Optional)
21
+
22
+ If you have curl-impersonate installed in a different directory, you can set the `CURL_IMPERSONATE_DIR` environment variable:
23
+
24
+ ```bash
25
+ # Set custom directory
26
+ export CURL_IMPERSONATE_DIR="/opt/curl-impersonate/bin"
27
+
28
+ # Or set it for a single command
29
+ CURL_IMPERSONATE_DIR="/opt/curl-impersonate/bin" ruby your_script.rb
30
+ ```
31
+
32
+ The default directory is `/usr/local/bin` if the environment variable is not set.
33
+
34
+ ### 3. Add Integration Files
35
+
36
+ Copy these two files to your project's `lib/yf_as_dataframe/` directory:
37
+
38
+ 1. `lib/yf_as_dataframe/curl_impersonate_integration.rb`
39
+ 2. `lib/yf_as_dataframe/yf_connection_minimal_patch.rb`
40
+
41
+ ### 4. Enable Integration
42
+
43
+ Add this single line to your code **before** any Yahoo Finance requests:
44
+
45
+ ```ruby
46
+ require 'yf_as_dataframe/curl_impersonate_integration'
47
+ require 'yf_as_dataframe/yf_connection_minimal_patch'
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ ### Default Behavior (Recommended)
53
+
54
+ Your existing code works exactly as before, but now uses curl-impersonate automatically:
55
+
56
+ ```ruby
57
+ require 'yf_as_dataframe'
58
+ require 'yf_as_dataframe/curl_impersonate_integration'
59
+ require 'yf_as_dataframe/yf_connection_minimal_patch'
60
+
61
+ # Your existing code - no changes needed!
62
+ msft = YfAsDataframe::Ticker.new("MSFT")
63
+ hist = msft.history(period: "1mo") # Uses curl-impersonate automatically
64
+ puts "Retrieved #{hist.length} data points"
65
+ ```
66
+
67
+ ### Configuration (Optional)
68
+
69
+ You can configure the behavior if needed:
70
+
71
+ ```ruby
72
+ # Disable curl-impersonate (use HTTParty only)
73
+ YfAsDataframe::YfConnection.enable_curl_impersonate(false)
74
+
75
+ # Disable fallback (fail if curl-impersonate fails)
76
+ YfAsDataframe::YfConnection.enable_curl_impersonate_fallback(false)
77
+
78
+ # Set timeout
79
+ YfAsDataframe::YfConnection.set_curl_impersonate_timeout(45)
80
+
81
+ # Check available executables
82
+ executables = YfAsDataframe::YfConnection.get_available_curl_impersonate_executables
83
+ puts "Available: #{executables.length} executables"
84
+
85
+ # Check which directory is being used
86
+ puts "Using directory: #{YfAsDataframe::CurlImpersonateIntegration.executable_directory}"
87
+ ```
88
+
89
+ ## How It Works
90
+
91
+ 1. **Automatic Detection**: Dynamically finds curl-impersonate executables in the configured directory
92
+ 2. **Default Behavior**: Uses curl-impersonate for all requests by default
93
+ 3. **Seamless Fallback**: Falls back to HTTParty if curl-impersonate fails
94
+ 4. **Zero Interface Changes**: All existing method signatures remain the same
95
+
96
+ ## Key Features
97
+
98
+ ### ✅ **Zero Code Changes**
99
+ - Your existing code works exactly as before
100
+ - No new method names to learn
101
+ - No changes to method signatures
102
+
103
+ ### ✅ **Automatic Browser Rotation**
104
+ - Randomly selects from available curl-impersonate executables
105
+ - Supports Chrome, Firefox, Edge, and Safari configurations
106
+ - Automatically adapts to new browser versions
107
+
108
+ ### ✅ **Robust Fallback**
109
+ - Falls back to HTTParty if curl-impersonate fails
110
+ - Configurable fallback behavior
111
+ - Maintains compatibility with existing code
112
+
113
+ ### ✅ **Dynamic Discovery**
114
+ - Automatically finds curl-impersonate executables
115
+ - Configurable directory via environment variable
116
+ - Works with any curl-impersonate installation
117
+
118
+ ### ✅ **Environment Variable Support**
119
+ - Set `CURL_IMPERSONATE_DIR` to customize installation directory
120
+ - Defaults to `/usr/local/bin` if not set
121
+ - Supports both persistent and per-command configuration
122
+
123
+ ## Example
124
+
125
+ ```ruby
126
+ require 'yf_as_dataframe'
127
+ require 'yf_as_dataframe/curl_impersonate_integration'
128
+ require 'yf_as_dataframe/yf_connection_minimal_patch'
129
+
130
+ # Check what's available
131
+ executables = YfAsDataframe::YfConnection.get_available_curl_impersonate_executables
132
+ puts "Found #{executables.length} curl-impersonate executables"
133
+
134
+ # Check which directory is being used
135
+ puts "Using directory: #{YfAsDataframe::CurlImpersonateIntegration.executable_directory}"
136
+
137
+ # Use as normal - curl-impersonate is used automatically
138
+ msft = YfAsDataframe::Ticker.new("MSFT")
139
+
140
+ begin
141
+ # These all use curl-impersonate automatically
142
+ hist = msft.history(period: "1mo")
143
+ info = msft.info
144
+ actions = msft.actions
145
+
146
+ puts "✅ All requests successful using curl-impersonate"
147
+ puts "History: #{hist.length} data points"
148
+ puts "Company: #{info['longName']}"
149
+ puts "Actions: #{actions.length} items"
150
+ rescue => e
151
+ puts "❌ Error: #{e.message}"
152
+ end
153
+ ```
154
+
155
+ ## Troubleshooting
156
+
157
+ ### "No curl-impersonate executables found"
158
+ ```bash
159
+ # Check if executables exist in default location
160
+ ls -la /usr/local/bin/curl_*
161
+
162
+ # Check if executables exist in custom location
163
+ ls -la $CURL_IMPERSONATE_DIR/curl_*
164
+
165
+ # If not found, reinstall curl-impersonate
166
+ brew reinstall curl-impersonate
167
+ ```
168
+
169
+ ### Permission errors
170
+ ```bash
171
+ sudo chmod +x /usr/local/bin/curl_*
172
+ # or
173
+ sudo chmod +x $CURL_IMPERSONATE_DIR/curl_*
174
+ ```
175
+
176
+ ### Still getting blocked
177
+ ```ruby
178
+ # Try disabling fallback to see if curl-impersonate is working
179
+ YfAsDataframe::YfConnection.enable_curl_impersonate_fallback(false)
180
+
181
+ # Check available executables
182
+ executables = YfAsDataframe::YfConnection.get_available_curl_impersonate_executables
183
+ puts executables.map { |e| "#{e[:browser]} #{e[:executable]}" }
184
+
185
+ # Check which directory is being used
186
+ puts "Directory: #{YfAsDataframe::CurlImpersonateIntegration.executable_directory}"
187
+ ```
188
+
189
+ ## Configuration Options
190
+
191
+ | Option | Default | Description |
192
+ |--------|---------|-------------|
193
+ | `curl_impersonate_enabled` | `true` | Use curl-impersonate for requests |
194
+ | `curl_impersonate_fallback` | `true` | Fall back to HTTParty if curl-impersonate fails |
195
+ | `curl_impersonate_timeout` | `30` | Timeout in seconds for curl-impersonate requests |
196
+ | `CURL_IMPERSONATE_DIR` | `/usr/local/bin` | Directory containing curl-impersonate executables |
197
+
198
+ ## Benefits
199
+
200
+ 1. **Immediate Solution**: Bypasses TLS fingerprinting immediately
201
+ 2. **Zero Learning Curve**: No new APIs or methods to learn
202
+ 3. **Future-Proof**: Automatically adapts to new curl-impersonate versions
203
+ 4. **Robust**: Multiple fallback strategies ensure reliability
204
+ 5. **Minimal**: Only two small files to add
205
+ 6. **Flexible**: Configurable installation directory via environment variable
206
+
207
+ ## Comparison with Previous Approach
208
+
209
+ | Aspect | Previous Approach | Minimal Approach |
210
+ |--------|------------------|------------------|
211
+ | Interface Changes | New method names | No changes |
212
+ | Learning Curve | High | Zero |
213
+ | Integration | Complex | Simple |
214
+ | Default Behavior | HTTParty | curl-impersonate |
215
+ | Configuration | Required | Optional |
216
+ | Files to Add | 3 files | 2 files |
217
+ | Directory Config | Hardcoded | Environment variable |
218
+
219
+ ## Next Steps
220
+
221
+ 1. **Install curl-impersonate** following the instructions above
222
+ 2. **Set CURL_IMPERSONATE_DIR** if using a custom installation directory
223
+ 3. **Add the two integration files** to your project
224
+ 4. **Add the require statements** to your code
225
+ 5. **Test with your existing code** - it should work immediately
226
+
227
+ That's it! Your existing Yahoo Finance scraping code will now automatically use curl-impersonate to bypass TLS fingerprinting.
data/README.md CHANGED
@@ -197,6 +197,71 @@ YfAsDataframe.zlema(df, column: 'Adj Close', window: 5)
197
197
 
198
198
  ---
199
199
 
200
+ ## TLS Fingerprinting Protection
201
+
202
+ **New in v0.4.0**: This gem now includes built-in support for [curl-impersonate](https://github.com/lwthiker/curl-impersonate). The curl-impersonate integration is **enabled by default** in v0.4.0+. Existing code will automatically use curl-impersonate to bypass TLS fingerprinting:
203
+
204
+ ### Installation Requirements
205
+
206
+ To use the TLS fingerprinting protection, you need to install curl-impersonate:
207
+
208
+ ```bash
209
+ # macOS
210
+ brew tap shakacode/brew
211
+ brew install curl-impersonate
212
+
213
+ # Verify installation
214
+ ls -la /usr/local/bin/curl_*
215
+ ```
216
+
217
+ ### Custom Installation Directory
218
+
219
+ If you have curl-impersonate installed in a different directory, you can set the `CURL_IMPERSONATE_DIR` environment variable:
220
+
221
+ ```bash
222
+ # Set custom directory
223
+ export CURL_IMPERSONATE_DIR="/opt/curl-impersonate/bin"
224
+
225
+ # Or set it for a single command
226
+ CURL_IMPERSONATE_DIR="/opt/curl-impersonate/bin" ruby your_script.rb
227
+ ```
228
+
229
+ The default directory is `/usr/local/bin` if the environment variable is not set.
230
+
231
+ ### Configuration (Optional)
232
+
233
+ You can configure the curl-impersonate behavior if needed:
234
+
235
+ ```ruby
236
+ # Disable curl-impersonate (use HTTParty only)
237
+ YfAsDataframe::YfConnection.enable_curl_impersonate(false)
238
+
239
+ # Disable fallback (fail if curl-impersonate fails)
240
+ YfAsDataframe::YfConnection.enable_curl_impersonate_fallback(false)
241
+
242
+ # Set timeout
243
+ YfAsDataframe::YfConnection.set_curl_impersonate_timeout(45)
244
+
245
+ # Check available executables
246
+ executables = YfAsDataframe::YfConnection.get_available_curl_impersonate_executables
247
+ puts "Available: #{executables.length} executables"
248
+
249
+ # Check which directory is being used
250
+ puts "Using directory: #{YfAsDataframe::CurlImpersonateIntegration.executable_directory}"
251
+ ```
252
+
253
+ ### How It Works
254
+
255
+ 1. **Automatic Detection**: Dynamically finds curl-impersonate executables in the configured directory
256
+ 2. **Default Behavior**: Uses curl-impersonate for all requests by default
257
+ 3. **Seamless Fallback**: Falls back to HTTParty if curl-impersonate fails
258
+ 4. **Browser Rotation**: Randomly selects from Chrome, Firefox, Edge, and Safari configurations
259
+ 5. **Zero Interface Changes**: All existing method signatures remain the same
260
+
261
+ For more detailed information, see [MINIMAL_INTEGRATION.md](MINIMAL_INTEGRATION.md).
262
+
263
+ ---
264
+
200
265
  ## Graphing
201
266
 
202
267
  To graph any of the series using [Vega](https://github.com/ankane/vega-ruby), per the information [here](https://github.com/ankane/vega-ruby#exporting-charts-experimental), you will need to run
@@ -0,0 +1,110 @@
1
+ require 'open3'
2
+ require 'json'
3
+ require 'ostruct'
4
+
5
+ class YfAsDataframe
6
+ module CurlImpersonateIntegration
7
+ # Configuration
8
+ @curl_impersonate_enabled = true
9
+ @curl_impersonate_fallback = true
10
+ @curl_impersonate_timeout = 5
11
+ @curl_impersonate_retries = 2
12
+ @curl_impersonate_retry_delay = 1
13
+
14
+ class << self
15
+ attr_accessor :curl_impersonate_enabled, :curl_impersonate_fallback,
16
+ :curl_impersonate_timeout, :curl_impersonate_retries,
17
+ :curl_impersonate_retry_delay
18
+ end
19
+
20
+ # Get the curl-impersonate executable directory from environment variable or default
21
+ def self.executable_directory
22
+ ENV['CURL_IMPERSONATE_DIR'] || '/usr/local/bin'
23
+ end
24
+
25
+ # Find available curl-impersonate executables
26
+ def self.available_executables
27
+ @available_executables ||= begin
28
+ executables = []
29
+ Dir.glob(File.join(executable_directory, "curl_*")).each do |path|
30
+ executable = File.basename(path)
31
+ if executable.start_with?('curl_')
32
+ browser_type = case executable
33
+ when /^curl_chrome/ then :chrome
34
+ when /^curl_ff/ then :firefox
35
+ when /^curl_edge/ then :edge
36
+ when /^curl_safari/ then :safari
37
+ else :unknown
38
+ end
39
+ executables << { path: path, executable: executable, browser: browser_type }
40
+ end
41
+ end
42
+ executables
43
+ end
44
+ end
45
+
46
+ # Get a random executable
47
+ def self.get_random_executable
48
+ available = available_executables
49
+ return nil if available.empty?
50
+ available.sample
51
+ end
52
+
53
+ # Make a curl-impersonate request
54
+ def self.make_request(url, headers: {}, params: {}, timeout: nil)
55
+ executable_info = get_random_executable
56
+ return nil unless executable_info
57
+
58
+ timeout ||= @curl_impersonate_timeout
59
+
60
+ # Build command
61
+ cmd = [executable_info[:path], "--max-time", timeout.to_s]
62
+
63
+ # Add headers
64
+ headers.each do |key, value|
65
+ cmd.concat(["-H", "#{key}: #{value}"])
66
+ end
67
+
68
+ # Add query parameters
69
+ unless params.empty?
70
+ query_string = params.map { |k, v| "#{k}=#{v}" }.join('&')
71
+ separator = url.include?('?') ? '&' : '?'
72
+ url = "#{url}#{separator}#{query_string}"
73
+ end
74
+
75
+ # Add URL
76
+ cmd << url
77
+
78
+ # Debug output
79
+ puts "DEBUG: curl-impersonate command: #{cmd.join(' ')}"
80
+ puts "DEBUG: curl-impersonate timeout: #{timeout} seconds"
81
+
82
+ # Execute
83
+ stdout, stderr, status = Open3.capture3(*cmd)
84
+
85
+ puts "DEBUG: curl-impersonate stdout: #{stdout[0..200]}..." if stdout && !stdout.empty?
86
+ puts "DEBUG: curl-impersonate stderr: #{stderr}" if stderr && !stderr.empty?
87
+ puts "DEBUG: curl-impersonate status: #{status.exitstatus}"
88
+
89
+ if status.success?
90
+ # Create a response object similar to HTTParty
91
+ response = OpenStruct.new
92
+ response.body = stdout
93
+ response.code = 200
94
+ response.define_singleton_method(:success?) { true }
95
+ response.parsed_response = parse_json_if_possible(stdout)
96
+ response
97
+ else
98
+ nil
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def self.parse_json_if_possible(response_body)
105
+ JSON.parse(response_body)
106
+ rescue JSON::ParserError
107
+ response_body
108
+ end
109
+ end
110
+ end
@@ -1,4 +1,5 @@
1
1
  require 'polars-df'
2
+ require 'logger'
2
3
 
3
4
  class YfAsDataframe
4
5
  module Financials
@@ -111,7 +112,7 @@ class YfAsDataframe
111
112
  ts_url_base = "https://query2.finance.yahoo.com/ws/fundamentals-timeseries/v1/finance/timeseries/#{symbol}?symbol=#{symbol}"
112
113
  url = ts_url_base + "&type=" + ts_keys.map { |k| "#{timescale}#{k}" }.join(",")
113
114
  start_dt = DateTime.new(2016, 12, 31)
114
- end_dt = DateTime.now.tomorrow.midnight
115
+ end_dt = Time.now.tomorrow.midnight
115
116
  url += "&period1=#{start_dt.to_i}&period2=#{end_dt.to_i}"
116
117
 
117
118
  json_str = get(url).parsed_response
@@ -160,7 +161,7 @@ class YfAsDataframe
160
161
  statement = _create_financials_table(nam, timescale)
161
162
  return statement unless statement.nil?
162
163
  rescue Yfin::YfinDataException => e
163
- Rails.logger.error {"#{@symbol}: Failed to create #{nam} financials table for reason: #{e}"}
164
+ Logger.new(STDOUT).error {"#{@symbol}: Failed to create #{nam} financials table for reason: #{e}"}
164
165
  end
165
166
  Polars::DataFrame.new()
166
167
  end
@@ -1,3 +1,5 @@
1
+ require 'logger'
2
+
1
3
  class YfAsDataframe
2
4
  module Holders
3
5
  extend ActiveSupport::Concern
@@ -97,7 +99,7 @@ class YfAsDataframe
97
99
  result = get_raw_json(QUOTE_SUMMARY_URL + "/#{symbol}", user_agent_headers=user_agent_headers, params=params_dict)
98
100
  # Rails.logger.info { "#{__FILE__}:#{__LINE__} result = #{result.inspect}" }
99
101
  rescue Exception => e
100
- Rails.logger.error("ERROR: #{e.message}")
102
+ Logger.new(STDOUT).error("ERROR: #{e.message}")
101
103
  return nil
102
104
  end
103
105
  return result
@@ -133,7 +135,7 @@ class YfAsDataframe
133
135
 
134
136
  def _parse_result(result)
135
137
  data = result.parsed_response['quoteSummary']['result'].first #.dig('quoteSummary', 'result', 0)
136
- Rails.logger.info { "#{__FILE__}:#{__LINE__} data = #{data.inspect}" }
138
+ Logger.new(STDOUT).info { "#{__FILE__}:#{__LINE__} data = #{data.inspect}" }
137
139
  _parse_institution_ownership(data['institutionOwnership'])
138
140
  _parse_fund_ownership(data['fundOwnership'])
139
141
  _parse_major_holders_breakdown(data['majorHoldersBreakdown'])
@@ -1,4 +1,5 @@
1
1
  require 'polars-df'
2
+ require 'logger'
2
3
 
3
4
  class YfAsDataframe
4
5
  class Multi
@@ -59,7 +60,7 @@ class YfAsDataframe
59
60
  # session: None or Session
60
61
  # Optional. Pass your own session object to be used for all requests
61
62
  # """
62
- logger = Rails.logger
63
+ logger = Logger.new(STDOUT)
63
64
 
64
65
  if show_errors
65
66
  YfAsDataframe::Utils.print_once("yfinance: download(show_errors=#{show_errors}) argument is deprecated and will be removed in future version. Do this instead: logging.getLogger('yfinance').setLevel(logging.ERROR)")