yf_as_dataframe 0.2.15
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 +7 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.rst +0 -0
- data/CODE_OF_CONDUCT.md +15 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +299 -0
- data/Rakefile +8 -0
- data/chart.png +0 -0
- data/lib/yf_as_dataframe/analysis.rb +68 -0
- data/lib/yf_as_dataframe/financials.rb +304 -0
- data/lib/yf_as_dataframe/fundamentals.rb +53 -0
- data/lib/yf_as_dataframe/holders.rb +253 -0
- data/lib/yf_as_dataframe/multi.rb +238 -0
- data/lib/yf_as_dataframe/price_history.rb +2045 -0
- data/lib/yf_as_dataframe/price_technical.rb +579 -0
- data/lib/yf_as_dataframe/quote.rb +343 -0
- data/lib/yf_as_dataframe/ticker.rb +380 -0
- data/lib/yf_as_dataframe/tickers.rb +50 -0
- data/lib/yf_as_dataframe/utils.rb +354 -0
- data/lib/yf_as_dataframe/version.rb +3 -0
- data/lib/yf_as_dataframe/yf_connection.rb +304 -0
- data/lib/yf_as_dataframe/yfinance_exception.rb +15 -0
- data/lib/yf_as_dataframe.rb +24 -0
- metadata +139 -0
@@ -0,0 +1,238 @@
|
|
1
|
+
require 'polars-df'
|
2
|
+
|
3
|
+
class YfAsDataframe
|
4
|
+
class Multi
|
5
|
+
|
6
|
+
def download(tickers, start: nil, fin: nil, actions: false, threads: true,
|
7
|
+
ignore_tz: nil, group_by: 'column', auto_adjust: false,
|
8
|
+
back_adjust: false, repair: false, keepna: false, progress: true,
|
9
|
+
period: "max", show_errors: nil, interval: "1d", prepost: false,
|
10
|
+
proxy: nil, rounding: false, timeout: 10, session: nil)
|
11
|
+
# """Download yahoo tickers
|
12
|
+
# :Parameters:
|
13
|
+
# tickers : str, list
|
14
|
+
# List of tickers to download
|
15
|
+
# period : str
|
16
|
+
# Valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max
|
17
|
+
# Either Use period parameter or use start and end
|
18
|
+
# interval : str
|
19
|
+
# Valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo
|
20
|
+
# Intraday data cannot extend last 60 days
|
21
|
+
# start: str
|
22
|
+
# Download start date string (YYYY-MM-DD) or _datetime, inclusive.
|
23
|
+
# Default is 99 years ago
|
24
|
+
# E.g. for start="2020-01-01", the first data point will be on "2020-01-01"
|
25
|
+
# fin: str
|
26
|
+
# Download end date string (YYYY-MM-DD) or _datetime, exclusive.
|
27
|
+
# Default is now
|
28
|
+
# E.g. for end="2023-01-01", the last data point will be on "2022-12-31"
|
29
|
+
# group_by : str
|
30
|
+
# Group by 'ticker' or 'column' (default)
|
31
|
+
# prepost : bool
|
32
|
+
# Include Pre and Post market data in results?
|
33
|
+
# Default is false
|
34
|
+
# auto_adjust: bool
|
35
|
+
# Adjust all OHLC automatically? Default is false
|
36
|
+
# repair: bool
|
37
|
+
# Detect currency unit 100x mixups and attempt repair
|
38
|
+
# Default is false
|
39
|
+
# keepna: bool
|
40
|
+
# Keep NaN rows returned by Yahoo?
|
41
|
+
# Default is false
|
42
|
+
# actions: bool
|
43
|
+
# Download dividend + stock splits data. Default is false
|
44
|
+
# threads: bool / int
|
45
|
+
# How many threads to use for mass downloading. Default is true
|
46
|
+
# ignore_tz: bool
|
47
|
+
# When combining from different timezones, ignore that part of datetime.
|
48
|
+
# Default depends on interval. Intraday = false. Day+ = true.
|
49
|
+
# proxy: str
|
50
|
+
# Optional. Proxy server URL scheme. Default is None
|
51
|
+
# rounding: bool
|
52
|
+
# Optional. Round values to 2 decimal places?
|
53
|
+
# show_errors: bool
|
54
|
+
# Optional. Doesn't print errors if false
|
55
|
+
# DEPRECATED, will be removed in future version
|
56
|
+
# timeout: None or float
|
57
|
+
# If not None stops waiting for a response after given number of
|
58
|
+
# seconds. (Can also be a fraction of a second e.g. 0.01)
|
59
|
+
# session: None or Session
|
60
|
+
# Optional. Pass your own session object to be used for all requests
|
61
|
+
# """
|
62
|
+
logger = Rails.logger
|
63
|
+
|
64
|
+
if show_errors
|
65
|
+
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)")
|
66
|
+
logger.level = Logger::ERROR
|
67
|
+
else
|
68
|
+
YfAsDataframe::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
|
+
logger.level = Logger::CRITICAL
|
70
|
+
end
|
71
|
+
|
72
|
+
if logger.debug?
|
73
|
+
threads = false if threads
|
74
|
+
logger.debug('Disabling multithreading because DEBUG logging enabled')
|
75
|
+
progress = false if progress
|
76
|
+
end
|
77
|
+
|
78
|
+
ignore_tz = interval[1..-1].match?(/[mh]/) ? false : true if ignore_tz.nil?
|
79
|
+
|
80
|
+
tickers = tickers.is_a?(Array) ? tickers : tickers.gsub(',', ' ').split
|
81
|
+
_tickers_ = []
|
82
|
+
tickers.each do |ticker|
|
83
|
+
if YfAsDataframe::Utils.is_isin(ticker)
|
84
|
+
isin = ticker
|
85
|
+
ticker = YfAsDataframe::Utils.get_ticker_by_isin(ticker, proxy, session: session)
|
86
|
+
# @shared::_ISINS[ticker] = isin
|
87
|
+
end
|
88
|
+
_tickers_ << ticker
|
89
|
+
end
|
90
|
+
tickers = _tickers_
|
91
|
+
|
92
|
+
tickers = tickers.map(&:upcase).uniq
|
93
|
+
|
94
|
+
if threads
|
95
|
+
threads = [tickers.length, Multitasking.cpu_count * 2].min if threads == true
|
96
|
+
Multitasking.set_max_threads(threads)
|
97
|
+
tickers.each_with_index do |ticker, i|
|
98
|
+
_download_one_threaded(ticker, period: period, interval: interval,
|
99
|
+
start: start, fin: fin, prepost: prepost,
|
100
|
+
actions: actions, auto_adjust: auto_adjust,
|
101
|
+
back_adjust: back_adjust, repair: repair,
|
102
|
+
keepna: keepna, progress: (progress && i.positive?),
|
103
|
+
proxy: proxy, rounding: rounding, timeout: timeout)
|
104
|
+
end
|
105
|
+
sleep 0.01 until @shared::_DFS.length == tickers.length
|
106
|
+
else
|
107
|
+
tickers.each_with_index do |ticker, i|
|
108
|
+
data = _download_one(ticker, period: period, interval: interval,
|
109
|
+
start: start, fin: fin, prepost: prepost,
|
110
|
+
actions: actions, auto_adjust: auto_adjust,
|
111
|
+
back_adjust: back_adjust, repair: repair,
|
112
|
+
keepna: keepna, proxy: proxy,
|
113
|
+
rounding: rounding, timeout: timeout)
|
114
|
+
@shared::_PROGRESS_BAR.animate if progress
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
@shared::_PROGRESS_BAR.completed if progress
|
119
|
+
|
120
|
+
unless @shared::_ERRORS.empty?
|
121
|
+
logger.error("\n#{@shared::_ERRORS.length} Failed download#{@shared::_ERRORS.length > 1 ? 's' : ''}:")
|
122
|
+
|
123
|
+
errors = {}
|
124
|
+
@shared::_ERRORS.each do |ticker, err|
|
125
|
+
err = err.gsub(/%ticker%/, ticker)
|
126
|
+
errors[err] ||= []
|
127
|
+
errors[err] << ticker
|
128
|
+
end
|
129
|
+
errors.each do |err, tickers|
|
130
|
+
logger.error("#{tickers.join(', ')}: #{err}")
|
131
|
+
end
|
132
|
+
|
133
|
+
tbs = {}
|
134
|
+
@shared::_TRACEBACKS.each do |ticker, tb|
|
135
|
+
tb = tb.gsub(/%ticker%/, ticker)
|
136
|
+
tbs[tb] ||= []
|
137
|
+
tbs[tb] << ticker
|
138
|
+
end
|
139
|
+
tbs.each do |tb, tickers|
|
140
|
+
logger.debug("#{tickers.join(', ')}: #{tb}")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
if ignore_tz
|
145
|
+
@shared::_DFS.each do |tkr, df|
|
146
|
+
next if df.nil? || df.empty?
|
147
|
+
@shared::_DFS[tkr].index = df.index.tz_localize(nil)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
if tickers.length == 1
|
152
|
+
ticker = tickers.first
|
153
|
+
return @shared::_DFS[ticker]
|
154
|
+
end
|
155
|
+
|
156
|
+
begin
|
157
|
+
data = Polars::concat(@shared::_DFS.values, axis: 1, sort: true,
|
158
|
+
keys: @shared::_DFS.keys, names: ['Ticker', 'Price'])
|
159
|
+
rescue
|
160
|
+
_realign_dfs
|
161
|
+
data = Polars::concat(@shared::_DFS.values, axis: 1, sort: true,
|
162
|
+
keys: @shared::_DFS.keys, names: ['Ticker', 'Price'])
|
163
|
+
end
|
164
|
+
data.index = Polars.to_datetime(data.index)
|
165
|
+
data.rename(columns: @shared::_ISINS, inplace: true)
|
166
|
+
|
167
|
+
if group_by == 'column'
|
168
|
+
data.columns = data.columns.swaplevel(0, 1)
|
169
|
+
data.sort_index(level: 0, axis: 1, inplace: true)
|
170
|
+
end
|
171
|
+
|
172
|
+
data
|
173
|
+
end
|
174
|
+
|
175
|
+
def _realign_dfs
|
176
|
+
idx_len = 0
|
177
|
+
idx = nil
|
178
|
+
|
179
|
+
@shared::_DFS.values.each do |df|
|
180
|
+
if df.length > idx_len
|
181
|
+
idx_len = df.length
|
182
|
+
idx = df.index
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
@shared::_DFS.each do |key, df|
|
187
|
+
begin
|
188
|
+
@shared::_DFS[key] = Polars::DataFrame.new(index: idx, data: df).drop_duplicates
|
189
|
+
rescue
|
190
|
+
@shared::_DFS[key] = Polars.concat([YfAsDataframe::Utils.empty_df(idx), df.dropna], axis: 0, sort: true)
|
191
|
+
end
|
192
|
+
|
193
|
+
@shared::_DFS[key] = @shared::_DFS[key].loc[!@shared::_DFS[key].index.duplicated(keep: 'last')]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
Multitasking.task :_download_one_threaded do |ticker, start: nil, fin: nil,
|
198
|
+
auto_adjust: false, back_adjust: false,
|
199
|
+
repair: false, actions: false,
|
200
|
+
progress: true, period: "max",
|
201
|
+
interval: "1d", prepost: false,
|
202
|
+
proxy: nil, keepna: false,
|
203
|
+
rounding: false, timeout: 10|
|
204
|
+
_download_one(ticker, start, fin, auto_adjust, back_adjust, repair,
|
205
|
+
actions, period, interval, prepost, proxy, rounding,
|
206
|
+
keepna, timeout)
|
207
|
+
@shared::_PROGRESS_BAR.animate if progress
|
208
|
+
end
|
209
|
+
|
210
|
+
def _download_one(ticker, start: nil, fin: nil,
|
211
|
+
auto_adjust: false, back_adjust: false, repair: false,
|
212
|
+
actions: false, period: "max", interval: "1d",
|
213
|
+
prepost: false, proxy: nil, rounding: false,
|
214
|
+
keepna: false, timeout: 10)
|
215
|
+
data = nil
|
216
|
+
begin
|
217
|
+
data = Ticker.new(ticker).history(
|
218
|
+
period: period, interval: interval,
|
219
|
+
start: start, fin: fin, prepost: prepost,
|
220
|
+
actions: actions, auto_adjust: auto_adjust,
|
221
|
+
back_adjust: back_adjust, repair: repair, proxy: proxy,
|
222
|
+
rounding: rounding, keepna: keepna, timeout: timeout,
|
223
|
+
raise_errors: true
|
224
|
+
)
|
225
|
+
rescue Exception => e
|
226
|
+
@shared::_DFS[ticker.upcase] = YfAsDataframe::Utils.empty_df
|
227
|
+
@shared::_ERRORS[ticker.upcase] = e.to_s
|
228
|
+
@shared::_TRACEBACKS[ticker.upcase] = e.backtrace.join("\n")
|
229
|
+
else
|
230
|
+
@shared::_DFS[ticker.upcase] = data
|
231
|
+
end
|
232
|
+
|
233
|
+
data
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
|