yfinrb 0.1.0 → 0.2.15

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae85dede3894ef988f67e2ad11cbade387915950c3387e4f593c34eb172e4d27
4
- data.tar.gz: ff90bb244a371653b94696e1137cdeecc2a955f1acd0f355c54068073c439f06
3
+ metadata.gz: 9316643665488097dfb09a3823315a062252b9647588c8af42a194c9e8c787d4
4
+ data.tar.gz: fa9cfd71619b493736744a406aa7f3bbe0d65fb1ae84d7fd64a0bb6f65c68ebf
5
5
  SHA512:
6
- metadata.gz: a5be1264d728ab72e778062b430162a27e6bf40b970893495e2db61c1760434401440b6bda8ca982088c2e9254e9452a2585d5a6e9dd5ad6d3b8157da03d0000
7
- data.tar.gz: f40d51d8164b62fb3edcd04f98011f9450c8523460e22abfe1e052cedaa11c7edc9c8a5ce94a44602acc421e8eec822dc50c763e4a04aa43a90c02c28422f941
6
+ metadata.gz: 5c8f803f68ca2354e995fa5c709d27e399b35c1aa5e516c34498cb4782ddb01fb587d950f87a905e85e7def09fbe1fd7e9f970c4ce58fb92a74061c72b1c9093
7
+ data.tar.gz: 520fa67a0ebfe33ad861cce25d58f769e7ea923b5928987d21a0290ad1227d36fba4e15d7dae50f082280331b894110120d03596c58afc19079cd56928bfabc2
data/README.md CHANGED
@@ -26,20 +26,22 @@ Yahoo! finance API is intended for personal use only.**
26
26
 
27
27
  ---
28
28
 
29
- ## Quick Start
29
+ ## Purpose
30
30
 
31
- ### The Ticker module
31
+ This package provides for pulling data from Yahoo!'s unofficial API, and providing that data using using [Polars](https://github.com/ankane/ruby-polars?tab=readme-ov-file) dataframes in ruby. Data in those dataframes can then be easily post-processed using technical indicators provided by [Tulip](https://tulipindicators.org/) via [Tulirb's](https://www.rubydoc.info/github/ozone4real/tulirb/main/Tulirb) ruby bindings, and visualized using [Vega](https://github.com/ankane/vega-ruby).
32
32
 
33
- The `Ticker` class, which allows you to access ticker data:
33
+ ### Quick Start: The Ticker module
34
+
35
+ The `Ticker` class, which allows you to access ticker data from Yahoo!'s unofficial API:
34
36
 
35
37
  ```ruby
36
38
 
37
- msft = Yfinrb::Ticker("MSFT")
39
+ msft = Yfinrb::Ticker.new("MSFT")
38
40
 
39
41
  # get all stock info
40
42
  msft.info
41
43
 
42
- # get historical market data
44
+ # get historical market data as a dataframe
43
45
  hist = msft.history(period: "1mo")
44
46
  hist2 = msft.history(start: '2020-01-01', fin: '2021-12-31')
45
47
 
@@ -95,8 +97,161 @@ msft.news
95
97
  # get option chain for specific expiration
96
98
  opt = msft.option_chain('2026-12-18')
97
99
  # data available via: opt.calls, opt.puts
100
+
101
+
102
+ # technical operations, using the Tulirb gem, which provides bindings to
103
+ # the Tulip technical indicators library
104
+ h = msft.history(period: '2y', interval: '1d')
105
+ Yfinrb.ad(h)
106
+
107
+ # then
108
+ h.insert_at_idx(h.columns.length, Yfinrb.ad(h))
109
+ h['ad_results'] = Yfinrb.ad(h)
110
+
98
111
  ```
99
112
 
113
+ Most of the indicators are found [here](https://tulipindicators.org/) and [here](https://www.rubydoc.info/github/ozone4real/tulirb/main/Tulirb). Indicator parameters in [Tulirb](https://www.rubydoc.info/github/ozone4real/tulirb/main/Tulirb) called, e.g., "period" or "short_period" are renamed as "window" or "short_window", respectively. There are a few other variants that are affected. Default values are shown below.
114
+
115
+ ```ruby
116
+
117
+ df = msft.history(period: '3y', interval: '1d') # for example
118
+
119
+ Yfinrb.ad(df)
120
+ Yfinrb.adosc(df, short_window: 2, long_window: 5)
121
+ Yfinrb.adx(df, column: 'Adj Close', window: 5)
122
+ Yfinrb.adxr(df, column: 'Adj Close', window: 5)
123
+ Yfinrb.avg_daily_trading_volume(df, window: 20)
124
+ Yfinrb.ao(df)
125
+ Yfinrb.apo(df, column: 'Adj Close', short_window: 12, long_window: 29)
126
+ Yfinrb.aroon(df, window: 20)
127
+ Yfinrb.aroonosc(df, window: 20)
128
+ Yfinrb.avg_price(df)
129
+ Yfinrb.atr(df, window: 20)
130
+ Yfinrb.bbands(df, column: 'Adj Close', window: 20, stddev: 1 )
131
+ Yfinrb.bop(df)
132
+ Yfinrb.cci(df, window: 20)
133
+ Yfinrb.cmo(df, column: 'Adj Close', window: 20)
134
+ Yfinrb.cvi(df, window: 20)
135
+ Yfinrb.dema(df, column: 'Adj Close', window: 20)
136
+ Yfinrb.di(df, window: 20)
137
+ Yfinrb.dm(df, window: 20)
138
+ Yfinrb.dpo(df, column: 'Adj Close', window: 20)
139
+ Yfinrb.dx(df, window: 20)
140
+ Yfinrb.ema(df, column: 'Adj Close', window: 5)
141
+ Yfinrb.emv(df)
142
+ Yfinrb.fisher(df, window: 20)
143
+ Yfinrb.fosc(df, window: 20)
144
+ Yfinrb.hma(df, column: 'Adj Close', window: 5)
145
+ Yfinrb.kama(df, column: 'Adj Close', window: 5)
146
+ Yfinrb.kvo(df, short_window: 5, long_window: 20)
147
+ Yfinrb.linreg(df, column: 'Adj Close', window: 20)
148
+ Yfinrb.linregintercept(df, column: 'Adj Close', window: 20)
149
+ Yfinrb.linregslope(df, column: 'Adj Close', window: 20)
150
+ Yfinrb.macd(df, column: 'Adj Close', short_window: 12, long_window: 26, signal_window: 9)
151
+ Yfinrb.marketfi(df)
152
+ Yfinrb.mass(df, window: 20)
153
+ Yfinrb.max(df, column: 'Adj Close', window: 20)
154
+ Yfinrb.md(df, column: 'Adj Close', window: 20)
155
+ Yfinrb.median_price(df)
156
+ Yfinrb.mfi(df, window: 20)
157
+ Yfinrb.min(df, column: 'Adj Close', window: 20)
158
+ Yfinrb.mom(df, column: 'Adj Close', window: 5)
159
+ Yfinrb.moving_avgs(df, window: 20)
160
+ Yfinrb.natr(df, window: 20)
161
+ Yfinrb.nvi(df)
162
+ Yfinrb.obv(df)
163
+ Yfinrb.ppo(df, column: 'Adj Close', short_window: 12, long_window: 26)
164
+ Yfinrb.psar(df, acceleration_factor_step: 0.2, acceleration_factor_maximum: 2)
165
+ Yfinrb.pvi(df)
166
+ Yfinrb.qstick(df, window: 20)
167
+ Yfinrb.roc(df, column: 'Adj Close', window: 20)
168
+ Yfinrb.rocr(df, column: 'Adj Close', window: 20)
169
+ Yfinrb.rsi(df, window: 20)
170
+ Yfinrb.sma(df, column: 'Adj Close', window: 20)
171
+ Yfinrb.stddev(df, column: 'Adj Close', window: 20)
172
+ Yfinrb.stderr(df, column: 'Adj Close', window: 20)
173
+ Yfinrb.stochrsi(df, column: 'Adj Close', window: 20)
174
+ Yfinrb.sum(df, column: 'Adj Close', window: 20)
175
+ Yfinrb.tema(df, column: 'Adj Close', window: 20)
176
+ Yfinrb.tr(df, column: 'Adj Close')
177
+ Yfinrb.trima(df, column: 'Adj Close', window: 20)
178
+ Yfinrb.trix(df, column: 'Adj Close', window: 20)
179
+ Yfinrb.trima(df, column: 'Adj Close', window: 20)
180
+ Yfinrb.tsf(df, column: 'Adj Close', window: 20)
181
+ Yfinrb.typical_price(df)
182
+ Yfinrb.ultosc(df, short_window: 5, medium_window: 12, long_window: 26)
183
+ Yfinrb.weighted_close_price(df)
184
+ Yfinrb.var(df, column: 'Adj Close', window: 20)
185
+ Yfinrb.vhf(df, column: 'Adj Close', window: 20)
186
+ Yfinrb.vidya(df, column: 'Adj Close', short_window: 5, long_window: 20, alpha: 0.2)
187
+ Yfinrb.volatility(df, column: 'Adj Close', window: 20)
188
+ Yfinrb.vosc(df, column: 'Adj Close', short_window: 5, long_window: 20)
189
+ Yfinrb.vol_weighted_moving_avg(df, window: 20)
190
+ Yfinrb.wad(df)
191
+ Yfinrb.wcprice(df)
192
+ Yfinrb.wilders(df, column: 'Adj Close', window: 20)
193
+ Yfinrb.willr(df, window: 20)
194
+ Yfinrb.wma(df, column: 'Adj Close', window: 5)
195
+ Yfinrb.zlema(df, column: 'Adj Close', window: 5)
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Graphing
201
+
202
+ 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
203
+
204
+ ```sh
205
+ yarn add vega-cli vega-lite
206
+ ```
207
+
208
+ Then, from within irb, you can generate charts, e.g.,
209
+
210
+ ```ruby
211
+ > msft = Yfinrb::Ticker.new("MSFT")
212
+ # =>
213
+ # #<Yfinrb::Ticker:0x000000011e6d50a0
214
+ # ...
215
+
216
+ > df = msft.history(period: '3y', interval: '1d')
217
+ # =>
218
+ # shape: (754, 10)
219
+ # ...
220
+
221
+ > df.insert_at_idx(df.columns.length, Yfinrb.ema(df, column: 'Adj Close', window: 5))
222
+ # =>
223
+ # shape: (753, 11)
224
+ # ┌────────────┬────────────┬────────────┬────────────┬───┬───────────┬───────────────┬──────────────┬──────────────────────┐
225
+ # │ Timestamps ┆ Open ┆ High ┆ Low ┆ … ┆ Dividends ┆ Capital Gains ┆ Stock Splits ┆ EMA(5) for Adj Close │
226
+ # │ --- ┆ --- ┆ --- ┆ --- ┆ ┆ --- ┆ --- ┆ --- ┆ --- │
227
+ # │ date ┆ f64 ┆ f64 ┆ f64 ┆ ┆ f64 ┆ f64 ┆ f64 ┆ f64 │
228
+ # ╞════════════╪════════════╪════════════╪════════════╪═══╪═══════════╪═══════════════╪══════════════╪══════════════════════╡
229
+ # │ 2021-07-12 ┆ 279.160004 ┆ 279.769989 ┆ 276.579987 ┆ … ┆ 0.0 ┆ 0.0 ┆ 0.0 ┆ 270.325745 │
230
+ # │ 2021-07-13 ┆ 277.519989 ┆ 282.850006 ┆ 277.390015 ┆ … ┆ 0.0 ┆ 0.0 ┆ 0.0 ┆ 271.514984 │
231
+ # │ 2021-07-14 ┆ 282.350006 ┆ 283.660004 ┆ 280.549988 ┆ … ┆ 0.0 ┆ 0.0 ┆ 0.0 ┆ 272.804932 │
232
+ # │ 2021-07-15 ┆ 282.0 ┆ 282.51001 ┆ 279.829987 ┆ … ┆ 0.0 ┆ 0.0 ┆ 0.0 ┆ 273.184001 │
233
+ # │ 2021-07-16 ┆ 282.070007 ┆ 284.100006 ┆ 279.459991 ┆ … ┆ 0.0 ┆ 0.0 ┆ 0.0 ┆ 273.345751 │
234
+ # │ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │
235
+ # │ 2024-07-02 ┆ 453.200012 ┆ 459.589996 ┆ 453.109985 ┆ … ┆ 0.0 ┆ 0.0 ┆ 0.0 ┆ 454.288375 │
236
+ # │ 2024-07-03 ┆ 458.190002 ┆ 461.019989 ┆ 457.880005 ┆ … ┆ 0.0 ┆ 0.0 ┆ 0.0 ┆ 456.448913 │
237
+ # │ 2024-07-05 ┆ 459.609985 ┆ 468.350006 ┆ 458.970001 ┆ … ┆ 0.0 ┆ 0.0 ┆ 0.0 ┆ 460.152608 │
238
+ # │ 2024-07-08 ┆ 466.549988 ┆ 467.700012 ┆ 464.459991 ┆ … ┆ 0.0 ┆ 0.0 ┆ 0.0 ┆ 462.181735 │
239
+ # │ 2024-07-09 ┆ 467.0 ┆ 467.329987 ┆ 458.0 ┆ … ┆ 0.0 ┆ 0.0 ┆ 0.0 ┆ 461.30116 │
240
+ # └────────────┴────────────┴────────────┴────────────┴───┴───────────┴───────────────┴──────────────┴──────────────────────┘
241
+
242
+ > File.binwrite('/tmp/chart.png',df.plot("Timestamps", "EMA(5) for Adj Close", type: "line", width:800, height:500).to_png)
243
+ # => 44913
244
+
245
+ ```
246
+
247
+ Then the following image should be saved at the specified location.
248
+
249
+ ![A chart generated with Yfinrb using Vega](./chart.png?raw=true)
250
+
251
+ PNG, SVG, and PDF output formats are supported directly. See [this page](https://github.com/ankane/vega-ruby) for more information in constructing supported charts.
252
+
253
+ While it _has not been tested yet_, images _should_ be able to be produced interactively using [iruby](https://github.com/SciRuby/iruby) operating in a [Jupyter environment](https://github.com/jupyterlab).
254
+
100
255
  ---
101
256
 
102
257
  ## Installation
data/chart.png ADDED
Binary file
@@ -1,4 +1,4 @@
1
- class Yfin
1
+ class Yfinrb
2
2
  module Analysis
3
3
  extend ActiveSupport::Concern
4
4
 
@@ -1,4 +1,6 @@
1
- class Yfin
1
+ require 'polars-df'
2
+
3
+ class Yfinrb
2
4
  module Financials
3
5
  include ActiveSupport::Inflector
4
6
 
@@ -54,7 +56,7 @@ class Yfin
54
56
 
55
57
  if pretty
56
58
  # data = data.dup
57
- # data.index = Utils.camel2title(data.index, sep: ' ', acronyms: ["PPE"])
59
+ # data.index = Yfinrb::Utils.camel2title(data.index, sep: ' ', acronyms: ["PPE"])
58
60
  end
59
61
 
60
62
  as_dict ? data.to_h : data
@@ -65,7 +67,7 @@ class Yfin
65
67
 
66
68
  if pretty
67
69
  # data = data.dup
68
- # data.index = Utils.camel2title(data.index, sep: ' ', acronyms: ["EBIT", "EBITDA", "EPS", "NI"])
70
+ # data.index = Yfinrb::Utils.camel2title(data.index, sep: ' ', acronyms: ["EBIT", "EBITDA", "EPS", "NI"])
69
71
  end
70
72
 
71
73
  as_dict ? data.to_h : data
@@ -77,7 +79,7 @@ class Yfin
77
79
 
78
80
  if pretty
79
81
  # data = data.dup
80
- # data.index = Utils.camel2title(data.index, sep: ' ', acronyms: ["PPE"])
82
+ # data.index = Yfinrb::Utils.camel2title(data.index, sep: ' ', acronyms: ["PPE"])
81
83
  end
82
84
 
83
85
  as_dict ? data.to_h : data
@@ -1,5 +1,4 @@
1
-
2
- class Yfin
1
+ class Yfinrb
3
2
  module Fundamentals
4
3
  extend ActiveSupport::Concern
5
4
 
@@ -1,11 +1,4 @@
1
-
2
- require'date'
3
- require 'open-uri'
4
- require 'json'
5
- require 'csv'
6
-
7
-
8
- class Yfin
1
+ class Yfinrb
9
2
  module Holders
10
3
  extend ActiveSupport::Concern
11
4
  # include YfConnection
data/lib/yfinrb/multi.rb CHANGED
@@ -1,6 +1,6 @@
1
+ require 'polars-df'
1
2
 
2
-
3
- class Yfin
3
+ class Yfinrb
4
4
  class Multi
5
5
 
6
6
  def download(tickers, start: nil, fin: nil, actions: false, threads: true,
@@ -62,10 +62,10 @@ class Yfin
62
62
  logger = Rails.logger
63
63
 
64
64
  if show_errors
65
- 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)")
65
+ Yfinrb::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)")
66
66
  logger.level = Logger::ERROR
67
67
  else
68
- Utils.print_once("yfinance: download(show_errors=#{show_errors}) argument is deprecated and will be removed in future version. Do this instead to suppress error messages: logging.getLogger('yfinance').setLevel(logging.CRITICAL)")
68
+ Yfinrb::Utils.print_once("yfinance: download(show_errors=#{show_errors}) argument is deprecated and will be removed in future version. Do this instead to suppress error messages: logging.getLogger('yfinance').setLevel(logging.CRITICAL)")
69
69
  logger.level = Logger::CRITICAL
70
70
  end
71
71
 
@@ -80,9 +80,9 @@ class Yfin
80
80
  tickers = tickers.is_a?(Array) ? tickers : tickers.gsub(',', ' ').split
81
81
  _tickers_ = []
82
82
  tickers.each do |ticker|
83
- if Utils.is_isin(ticker)
83
+ if Yfinrb::Utils.is_isin(ticker)
84
84
  isin = ticker
85
- ticker = Utils.get_ticker_by_isin(ticker, proxy, session: session)
85
+ ticker = Yfinrb::Utils.get_ticker_by_isin(ticker, proxy, session: session)
86
86
  # @shared::_ISINS[ticker] = isin
87
87
  end
88
88
  _tickers_ << ticker
@@ -187,7 +187,7 @@ class Yfin
187
187
  begin
188
188
  @shared::_DFS[key] = Polars::DataFrame.new(index: idx, data: df).drop_duplicates
189
189
  rescue
190
- @shared::_DFS[key] = Polars.concat([Utils.empty_df(idx), df.dropna], axis: 0, sort: true)
190
+ @shared::_DFS[key] = Polars.concat([Yfinrb::Utils.empty_df(idx), df.dropna], axis: 0, sort: true)
191
191
  end
192
192
 
193
193
  @shared::_DFS[key] = @shared::_DFS[key].loc[!@shared::_DFS[key].index.duplicated(keep: 'last')]
@@ -223,7 +223,7 @@ class Yfin
223
223
  raise_errors: true
224
224
  )
225
225
  rescue Exception => e
226
- @shared::_DFS[ticker.upcase] = Utils.empty_df
226
+ @shared::_DFS[ticker.upcase] = Yfinrb::Utils.empty_df
227
227
  @shared::_ERRORS[ticker.upcase] = e.to_s
228
228
  @shared::_TRACEBACKS[ticker.upcase] = e.backtrace.join("\n")
229
229
  else
@@ -1,5 +1,7 @@
1
+ require 'polars'
2
+ require 'polars-df'
1
3
 
2
- class Yfin
4
+ class Yfinrb
3
5
  module PriceHistory
4
6
  extend ActiveSupport::Concern
5
7
  include ActionView::Helpers::NumberHelper
@@ -35,8 +37,10 @@ class Yfin
35
37
  rounding: false, raise_errors: false, returns: false)
36
38
  logger = Rails.logger # Yfin.get_yf_logger
37
39
  start_user = start
40
+ # Rails.logger.info { "#{__FILE__}:#{__LINE__} here" }
38
41
  end_user = fin || DateTime.now
39
42
 
43
+ # Rails.logger.info { "#{__FILE__}:#{__LINE__} here" }
40
44
  params = _preprocess_params(start, fin, interval, period, prepost, raise_errors)
41
45
  # Rails.logger.info { "#{__FILE__}:#{__LINE__} params=#{params.inspect}" }
42
46
 
@@ -47,11 +51,11 @@ class Yfin
47
51
  end
48
52
 
49
53
  data = _get_data(ticker, params, fin, raise_errors)
54
+ # Rails.logger.info { "#{__FILE__}:#{__LINE__} data = #{data.inspect}" }
50
55
  # Rails.logger.info { "#{__FILE__}:#{__LINE__} data[chart][result].first.keys = #{data['chart']['result'].first.keys.inspect}" }
51
56
  # Rails.logger.info { "#{__FILE__}:#{__LINE__} data[chart][result].first[events] = #{data['chart']['result'].first['events'].inspect}" }
52
57
  # Rails.logger.info { "#{__FILE__}:#{__LINE__} data[chart][result].first[events][dividends] = #{data['chart']['result'].first['events']['dividends'].inspect}" }
53
58
  # Rails.logger.info { "#{__FILE__}:#{__LINE__} data[chart][result].first[events][splits] = #{data['chart']['result'].first['events']['splits'].inspect}" }
54
- # Rails.logger.info { "#{__FILE__}:#{__LINE__} data = #{data.inspect}" }
55
59
  # Rails.logger.info { "#{__FILE__}:#{__LINE__} @history = #{@history.inspect}" }
56
60
 
57
61
  @history_metadata = data["chart"]["result"][0]["meta"] rescue {}
@@ -76,7 +80,7 @@ class Yfin
76
80
  if @reconstruct_start_interval && @reconstruct_start_interval == interval
77
81
  @reconstruct_start_interval = nil
78
82
  end
79
- return Utils.empty_df
83
+ return Yfinrb::Utils.empty_df
80
84
  end
81
85
 
82
86
  # begin
@@ -556,22 +560,25 @@ class Yfin
556
560
  private
557
561
 
558
562
  def _preprocess_params(start, fin, interval, period, prepost, raise_errors)
559
- # Rails.logger.info { "#{__FILE__}:#{__LINE__} start = #{start.inspect}, end_date = #{fin.inspect}, interval = #{interval}, period = #{period}, tz = #{tz}, prepost = #{prepost}, raise_errors = #{raise_errors}" }
563
+ # Rails.logger.info { "#{__FILE__}:#{__LINE__} start = #{start.inspect}, end_date = #{fin.inspect}, interval = #{interval}, period = #{period}, tz = #{tz}, prepost = #{prepost}, raise_errors = #{raise_errors}" }
560
564
 
565
+ # Rails.logger.info { "#{__FILE__}:#{__LINE__} here start = #{fin}, period = #{period}" }
561
566
  if start || period.nil? || period.downcase == "max"
567
+ # Rails.logger.info { "#{__FILE__}:#{__LINE__} here fin = #{fin}" }
562
568
  if tz.nil?
563
569
  err_msg = "No timezone found, symbol may be delisted"
564
- # Yfin.shared_DFS[@ticker] = Utils.empty_df
570
+ # Yfin.shared_DFS[@ticker] = Yfinrb::Utils.empty_df
565
571
  # Yfin.shared_ERRORS[@ticker] = err_msg
566
572
  if raise_errors
567
573
  raise Exception.new("#{@ticker}: #{err_msg}")
568
574
  else
569
575
  Rails.logger.error("#{@ticker}: #{err_msg}")
570
576
  end
571
- return Utils.empty_df
577
+ return Yfinrb::Utils.empty_df
572
578
  end
573
579
 
574
- fin = fin.nil? ? Time.now.to_i : Utils.parse_user_dt(fin, tz)
580
+ # Rails.logger.info { "#{__FILE__}:#{__LINE__} here fin = #{fin}" }
581
+ fin = fin.nil? ? Time.now.to_i : Yfinrb::Utils.parse_user_dt(fin, tz)
575
582
  # Rails.logger.info { "#{__FILE__}:#{__LINE__} fin = #{fin.inspect}" }
576
583
 
577
584
  if start.nil?
@@ -582,7 +589,7 @@ class Yfin
582
589
  start = max_start_datetime.to_i
583
590
  end
584
591
  else
585
- start = Utils.parse_user_dt(start, tz)
592
+ start = Yfinrb::Utils.parse_user_dt(start, tz)
586
593
  end
587
594
 
588
595
  params = { "period1" => start, "period2" => fin }
@@ -592,7 +599,8 @@ class Yfin
592
599
  period = period.downcase
593
600
  # params = { "range" => period }
594
601
  fin = DateTime.now.to_i
595
- start = (fin - Utils.interval_to_timedelta(period)).to_i
602
+ # Rails.logger.info { "#{__FILE__}:#{__LINE__} here fin= #{fin}, period = #{period}" }
603
+ start = (fin - Yfinrb::Utils.interval_to_timedelta(period)).to_i
596
604
  params = { "period1" => start, "period2" => fin }
597
605
  # Rails.logger.info { "#{__FILE__}:#{__LINE__} params = #{params.inspect}" }
598
606
  end
@@ -607,7 +615,7 @@ class Yfin
607
615
  end
608
616
 
609
617
  def _get_data(ticker, params, fin, raise_errors)
610
- url = "https://query2.finance.yahoo.com/v8/finance/chart/#{ticker}"
618
+ url = "https://query2.finance.yahoo.com/v8/finance/chart/#{CGI.escape ticker}"
611
619
  # url = "https://query1.finance.yahoo.com/v7/finance/download/#{ticker}" ... Deprecated
612
620
  # Rails.logger.info { "#{__FILE__}:#{__LINE__} url = #{url}" }
613
621
  data = nil
@@ -702,7 +710,7 @@ class Yfin
702
710
  # startDt = quotes.index[0].floor('D')
703
711
  startDt = quotes['Timestamps'].to_a.map(&:to_date).min
704
712
  # Rails.logger.info { "#{__FILE__}:#{__LINE__} startDt = #{startDt.inspect}" }
705
- endDt = fin.present? ? fin : Time.at(DateTime.now.tomorrow).to_i
713
+ endDt = fin.present? ? fin.to_date : Time.at(DateTime.now.tomorrow).to_i
706
714
 
707
715
  # Rails.logger.info { "#{__FILE__}:#{__LINE__} @history[events][dividends] = #{@history['events']["dividends"].inspect}" }
708
716
  # divi = {}
@@ -1111,7 +1119,7 @@ class Yfin
1111
1119
 
1112
1120
  def _reconstruct_intervals_batch(df, interval, prepost, tag=-1)
1113
1121
  # # Reconstruct values in df using finer-grained price data. Delimiter marks what to reconstruct
1114
- # logger = Rails.logger # Utils.get_yf_logger
1122
+ # logger = Rails.logger # Yfinrb::Utils.get_yf_logger
1115
1123
 
1116
1124
  # # raise Exception.new("'df' must be a Polars DataFrame not", type(df)) unless df.is_a?(Polars::DataFrame)
1117
1125
  # return df if interval == "1m"
@@ -1130,7 +1138,7 @@ class Yfin
1130
1138
  # # If interval is weekly then can construct with daily. But if smaller intervals then
1131
1139
  # # restricted to recent times:
1132
1140
  # intervals = ["1wk", "1d", "1h", "30m", "15m", "5m", "2m", "1m"]
1133
- # itds = intervals.map { |i| [i, Utils.interval_to_timedelta(interval)] }.to_h
1141
+ # itds = intervals.map { |i| [i, Yfinrb::Utils.interval_to_timedelta(interval)] }.to_h
1134
1142
  # nexts = intervals.each_cons(2).to_h
1135
1143
  # min_lookbacks = {"1wk" => nil, "1d" => nil, "1h" => 730.days }
1136
1144
  # ["30m", "15m", "5m", "2m"].each { |i| min_lookbacks[i] = 60.days }
@@ -1561,7 +1569,7 @@ class Yfin
1561
1569
  # return df if df.empty?
1562
1570
 
1563
1571
  # # Easy to detect and fix, just look for outliers = ~100x local median
1564
- # logger = Rails.logger # Utils.get_yf_logger
1572
+ # logger = Rails.logger # Yfinrb::Utils.get_yf_logger
1565
1573
 
1566
1574
  # if df.shape[0] == 0
1567
1575
  # df["Repaired?"] = false if !df.columns.include?("Repaired?")