We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Ticker.history: history's index is not of type DateTimeIndex when dividend and split tables are empty
**Issue**:

Ticker.history (line 295):
>       df.index = df.index.tz_localize("UTC").tz_convert(data["chart"]["result"][0]["meta"]["exchangeTimezoneName"])
E       AttributeError: 'Index' object has no attribute 'tz_localize'

**Reason**: 

During the concatenation of a proper price table with empty dividend and split tables, the price table's index has changed from _pandas.DatetimeIndex_ to _pandas.Index_ because the empty tables had this index type.

**Proposed solution**: 

Modify _parse_actions_ (in utils.py) to initialise the dividend and split tables with : 
`dividends = _pd.DataFrame(columns=["Dividends"], index=_pd.DatetimeIndex([]))`
 `splits = _pd.DataFrame(columns=["Stock Splits"], index=_pd.DatetimeIndex([]))`


Local modules' versions:

finance : 0.1.69
pandas : 1.4.0
python : 3.10


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `yfinance/utils.py`
Content:
```
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # yfinance - market data downloader
5 # https://github.com/ranaroussi/yfinance
6 #
7 # Copyright 2017-2019 Ran Aroussi
8 #
9 # Licensed under the Apache License, Version 2.0 (the "License");
10 # you may not use this file except in compliance with the License.
11 # You may obtain a copy of the License at
12 #
13 #     http://www.apache.org/licenses/LICENSE-2.0
14 #
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS,
17 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 # See the License for the specific language governing permissions and
19 # limitations under the License.
20 #
21 
22 from __future__ import print_function
23 
24 import requests as _requests
25 import re as _re
26 import pandas as _pd
27 import numpy as _np
28 import sys as _sys
29 
30 try:
31     import ujson as _json
32 except ImportError:
33     import json as _json
34 
35 
36 user_agent_headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
37 
38 
39 def is_isin(string):
40     return bool(_re.match("^([A-Z]{2})([A-Z0-9]{9})([0-9]{1})$", string))
41 
42 
43 def get_all_by_isin(isin, proxy=None, session=None):
44     if not(is_isin(isin)):
45         raise ValueError("Invalid ISIN number")
46 
47     from .base import _BASE_URL_
48     session = session or _requests
49     url = "{}/v1/finance/search?q={}".format(_BASE_URL_, isin)
50     data = session.get(url=url, proxies=proxy, headers=user_agent_headers)
51     try:
52         data = data.json()
53         ticker = data.get('quotes', [{}])[0]
54         return {
55             'ticker': {
56                 'symbol': ticker['symbol'],
57                 'shortname': ticker['shortname'],
58                 'longname': ticker['longname'],
59                 'type': ticker['quoteType'],
60                 'exchange': ticker['exchDisp'],
61             },
62             'news': data.get('news', [])
63         }
64     except Exception:
65         return {}
66 
67 
68 def get_ticker_by_isin(isin, proxy=None, session=None):
69     data = get_all_by_isin(isin, proxy, session)
70     return data.get('ticker', {}).get('symbol', '')
71 
72 
73 def get_info_by_isin(isin, proxy=None, session=None):
74     data = get_all_by_isin(isin, proxy, session)
75     return data.get('ticker', {})
76 
77 
78 def get_news_by_isin(isin, proxy=None, session=None):
79     data = get_all_by_isin(isin, proxy, session)
80     return data.get('news', {})
81 
82 
83 def empty_df(index=[]):
84     empty = _pd.DataFrame(index=index, data={
85         'Open': _np.nan, 'High': _np.nan, 'Low': _np.nan,
86         'Close': _np.nan, 'Adj Close': _np.nan, 'Volume': _np.nan})
87     empty.index.name = 'Date'
88     return empty
89 
90 
91 def get_html(url, proxy=None, session=None):
92     session = session or _requests
93     html = session.get(url=url, proxies=proxy, headers=user_agent_headers).text
94     return html
95 
96 
97 def get_json(url, proxy=None, session=None):
98     session = session or _requests
99     html = session.get(url=url, proxies=proxy, headers=user_agent_headers).text
100 
101     if "QuoteSummaryStore" not in html:
102         html = session.get(url=url, proxies=proxy).text
103         if "QuoteSummaryStore" not in html:
104             return {}
105 
106     json_str = html.split('root.App.main =')[1].split(
107         '(this)')[0].split(';\n}')[0].strip()
108     data = _json.loads(json_str)[
109         'context']['dispatcher']['stores']['QuoteSummaryStore']
110     # add data about Shares Outstanding for companies' tickers if they are available
111     try:
112         data['annualBasicAverageShares'] = _json.loads(json_str)[
113             'context']['dispatcher']['stores']['QuoteTimeSeriesStore']['timeSeries']['annualBasicAverageShares']
114     except Exception:
115         pass
116 
117     # return data
118     new_data = _json.dumps(data).replace('{}', 'null')
119     new_data = _re.sub(
120         r'\{[\'|\"]raw[\'|\"]:(.*?),(.*?)\}', r'\1', new_data)
121 
122     return _json.loads(new_data)
123 
124 
125 def camel2title(o):
126     return [_re.sub("([a-z])([A-Z])", r"\g<1> \g<2>", i).title() for i in o]
127 
128 
129 def auto_adjust(data):
130     df = data.copy()
131     ratio = df["Close"] / df["Adj Close"]
132     df["Adj Open"] = df["Open"] / ratio
133     df["Adj High"] = df["High"] / ratio
134     df["Adj Low"] = df["Low"] / ratio
135 
136     df.drop(
137         ["Open", "High", "Low", "Close"],
138         axis=1, inplace=True)
139 
140     df.rename(columns={
141         "Adj Open": "Open", "Adj High": "High",
142         "Adj Low": "Low", "Adj Close": "Close"
143     }, inplace=True)
144 
145     df = df[["Open", "High", "Low", "Close", "Volume"]]
146     return df[["Open", "High", "Low", "Close", "Volume"]]
147 
148 
149 def back_adjust(data):
150     """ back-adjusted data to mimic true historical prices """
151 
152     df = data.copy()
153     ratio = df["Adj Close"] / df["Close"]
154     df["Adj Open"] = df["Open"] * ratio
155     df["Adj High"] = df["High"] * ratio
156     df["Adj Low"] = df["Low"] * ratio
157 
158     df.drop(
159         ["Open", "High", "Low", "Adj Close"],
160         axis=1, inplace=True)
161 
162     df.rename(columns={
163         "Adj Open": "Open", "Adj High": "High",
164         "Adj Low": "Low"
165     }, inplace=True)
166 
167     return df[["Open", "High", "Low", "Close", "Volume"]]
168 
169 
170 def parse_quotes(data, tz=None):
171     timestamps = data["timestamp"]
172     ohlc = data["indicators"]["quote"][0]
173     volumes = ohlc["volume"]
174     opens = ohlc["open"]
175     closes = ohlc["close"]
176     lows = ohlc["low"]
177     highs = ohlc["high"]
178 
179     adjclose = closes
180     if "adjclose" in data["indicators"]:
181         adjclose = data["indicators"]["adjclose"][0]["adjclose"]
182 
183     quotes = _pd.DataFrame({"Open": opens,
184                             "High": highs,
185                             "Low": lows,
186                             "Close": closes,
187                             "Adj Close": adjclose,
188                             "Volume": volumes})
189 
190     quotes.index = _pd.to_datetime(timestamps, unit="s")
191     quotes.sort_index(inplace=True)
192 
193     if tz is not None:
194         quotes.index = quotes.index.tz_localize(tz)
195 
196     return quotes
197 
198 
199 def parse_actions(data, tz=None):
200     dividends = _pd.DataFrame(columns=["Dividends"])
201     splits = _pd.DataFrame(columns=["Stock Splits"])
202 
203     if "events" in data:
204         if "dividends" in data["events"]:
205             dividends = _pd.DataFrame(
206                 data=list(data["events"]["dividends"].values()))
207             dividends.set_index("date", inplace=True)
208             dividends.index = _pd.to_datetime(dividends.index, unit="s")
209             dividends.sort_index(inplace=True)
210             if tz is not None:
211                 dividends.index = dividends.index.tz_localize(tz)
212 
213             dividends.columns = ["Dividends"]
214 
215         if "splits" in data["events"]:
216             splits = _pd.DataFrame(
217                 data=list(data["events"]["splits"].values()))
218             splits.set_index("date", inplace=True)
219             splits.index = _pd.to_datetime(splits.index, unit="s")
220             splits.sort_index(inplace=True)
221             if tz is not None:
222                 splits.index = splits.index.tz_localize(tz)
223             splits["Stock Splits"] = splits["numerator"] / \
224                 splits["denominator"]
225             splits = splits["Stock Splits"]
226 
227     return dividends, splits
228 
229 
230 class ProgressBar:
231     def __init__(self, iterations, text='completed'):
232         self.text = text
233         self.iterations = iterations
234         self.prog_bar = '[]'
235         self.fill_char = '*'
236         self.width = 50
237         self.__update_amount(0)
238         self.elapsed = 1
239 
240     def completed(self):
241         if self.elapsed > self.iterations:
242             self.elapsed = self.iterations
243         self.update_iteration(1)
244         print('\r' + str(self), end='')
245         _sys.stdout.flush()
246         print()
247 
248     def animate(self, iteration=None):
249         if iteration is None:
250             self.elapsed += 1
251             iteration = self.elapsed
252         else:
253             self.elapsed += iteration
254 
255         print('\r' + str(self), end='')
256         _sys.stdout.flush()
257         self.update_iteration()
258 
259     def update_iteration(self, val=None):
260         val = val if val is not None else self.elapsed / float(self.iterations)
261         self.__update_amount(val * 100.0)
262         self.prog_bar += '  %s of %s %s' % (
263             self.elapsed, self.iterations, self.text)
264 
265     def __update_amount(self, new_amount):
266         percent_done = int(round((new_amount / 100.0) * 100.0))
267         all_full = self.width - 2
268         num_hashes = int(round((percent_done / 100.0) * all_full))
269         self.prog_bar = '[' + self.fill_char * \
270             num_hashes + ' ' * (all_full - num_hashes) + ']'
271         pct_place = (len(self.prog_bar) // 2) - len(str(percent_done))
272         pct_string = '%d%%' % percent_done
273         self.prog_bar = self.prog_bar[0:pct_place] + \
274             (pct_string + self.prog_bar[pct_place + len(pct_string):])
275 
276     def __str__(self):
277         return str(self.prog_bar)
278
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/yfinance/utils.py b/yfinance/utils.py
--- a/yfinance/utils.py
+++ b/yfinance/utils.py
@@ -197,8 +197,8 @@
 
 
 def parse_actions(data, tz=None):
-    dividends = _pd.DataFrame(columns=["Dividends"])
-    splits = _pd.DataFrame(columns=["Stock Splits"])
+    dividends = _pd.DataFrame(columns=["Dividends"], index=_pd.DatetimeIndex([]))
+    splits = _pd.DataFrame(columns=["Stock Splits"], index=_pd.DatetimeIndex([]))
 
     if "events" in data:
         if "dividends" in data["events"]:

{"golden_diff": "diff --git a/yfinance/utils.py b/yfinance/utils.py\n--- a/yfinance/utils.py\n+++ b/yfinance/utils.py\n@@ -197,8 +197,8 @@\n \n \n def parse_actions(data, tz=None):\n-    dividends = _pd.DataFrame(columns=[\"Dividends\"])\n-    splits = _pd.DataFrame(columns=[\"Stock Splits\"])\n+    dividends = _pd.DataFrame(columns=[\"Dividends\"], index=_pd.DatetimeIndex([]))\n+    splits = _pd.DataFrame(columns=[\"Stock Splits\"], index=_pd.DatetimeIndex([]))\n \n     if \"events\" in data:\n         if \"dividends\" in data[\"events\"]:\n", "issue": "Ticker.history: history's index is not of type DateTimeIndex when dividend and split tables are empty\n**Issue**:\r\n\r\nTicker.history (line 295):\r\n>       df.index = df.index.tz_localize(\"UTC\").tz_convert(data[\"chart\"][\"result\"][0][\"meta\"][\"exchangeTimezoneName\"])\r\nE       AttributeError: 'Index' object has no attribute 'tz_localize'\r\n\r\n**Reason**: \r\n\r\nDuring the concatenation of a proper price table with empty dividend and split tables, the price table's index has changed from _pandas.DatetimeIndex_ to _pandas.Index_ because the empty tables had this index type.\r\n\r\n**Proposed solution**: \r\n\r\nModify _parse_actions_ (in utils.py) to initialise the dividend and split tables with : \r\n`dividends = _pd.DataFrame(columns=[\"Dividends\"], index=_pd.DatetimeIndex([]))`\r\n `splits = _pd.DataFrame(columns=[\"Stock Splits\"], index=_pd.DatetimeIndex([]))`\r\n\r\n\r\nLocal modules' versions:\r\n\r\nfinance : 0.1.69\r\npandas : 1.4.0\r\npython : 3.10\r\n\n", "before_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n#\n# yfinance - market data downloader\n# https://github.com/ranaroussi/yfinance\n#\n# Copyright 2017-2019 Ran Aroussi\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nfrom __future__ import print_function\n\nimport requests as _requests\nimport re as _re\nimport pandas as _pd\nimport numpy as _np\nimport sys as _sys\n\ntry:\n    import ujson as _json\nexcept ImportError:\n    import json as _json\n\n\nuser_agent_headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}\n\n\ndef is_isin(string):\n    return bool(_re.match(\"^([A-Z]{2})([A-Z0-9]{9})([0-9]{1})$\", string))\n\n\ndef get_all_by_isin(isin, proxy=None, session=None):\n    if not(is_isin(isin)):\n        raise ValueError(\"Invalid ISIN number\")\n\n    from .base import _BASE_URL_\n    session = session or _requests\n    url = \"{}/v1/finance/search?q={}\".format(_BASE_URL_, isin)\n    data = session.get(url=url, proxies=proxy, headers=user_agent_headers)\n    try:\n        data = data.json()\n        ticker = data.get('quotes', [{}])[0]\n        return {\n            'ticker': {\n                'symbol': ticker['symbol'],\n                'shortname': ticker['shortname'],\n                'longname': ticker['longname'],\n                'type': ticker['quoteType'],\n                'exchange': ticker['exchDisp'],\n            },\n            'news': data.get('news', [])\n        }\n    except Exception:\n        return {}\n\n\ndef get_ticker_by_isin(isin, proxy=None, session=None):\n    data = get_all_by_isin(isin, proxy, session)\n    return data.get('ticker', {}).get('symbol', '')\n\n\ndef get_info_by_isin(isin, proxy=None, session=None):\n    data = get_all_by_isin(isin, proxy, session)\n    return data.get('ticker', {})\n\n\ndef get_news_by_isin(isin, proxy=None, session=None):\n    data = get_all_by_isin(isin, proxy, session)\n    return data.get('news', {})\n\n\ndef empty_df(index=[]):\n    empty = _pd.DataFrame(index=index, data={\n        'Open': _np.nan, 'High': _np.nan, 'Low': _np.nan,\n        'Close': _np.nan, 'Adj Close': _np.nan, 'Volume': _np.nan})\n    empty.index.name = 'Date'\n    return empty\n\n\ndef get_html(url, proxy=None, session=None):\n    session = session or _requests\n    html = session.get(url=url, proxies=proxy, headers=user_agent_headers).text\n    return html\n\n\ndef get_json(url, proxy=None, session=None):\n    session = session or _requests\n    html = session.get(url=url, proxies=proxy, headers=user_agent_headers).text\n\n    if \"QuoteSummaryStore\" not in html:\n        html = session.get(url=url, proxies=proxy).text\n        if \"QuoteSummaryStore\" not in html:\n            return {}\n\n    json_str = html.split('root.App.main =')[1].split(\n        '(this)')[0].split(';\\n}')[0].strip()\n    data = _json.loads(json_str)[\n        'context']['dispatcher']['stores']['QuoteSummaryStore']\n    # add data about Shares Outstanding for companies' tickers if they are available\n    try:\n        data['annualBasicAverageShares'] = _json.loads(json_str)[\n            'context']['dispatcher']['stores']['QuoteTimeSeriesStore']['timeSeries']['annualBasicAverageShares']\n    except Exception:\n        pass\n\n    # return data\n    new_data = _json.dumps(data).replace('{}', 'null')\n    new_data = _re.sub(\n        r'\\{[\\'|\\\"]raw[\\'|\\\"]:(.*?),(.*?)\\}', r'\\1', new_data)\n\n    return _json.loads(new_data)\n\n\ndef camel2title(o):\n    return [_re.sub(\"([a-z])([A-Z])\", r\"\\g<1> \\g<2>\", i).title() for i in o]\n\n\ndef auto_adjust(data):\n    df = data.copy()\n    ratio = df[\"Close\"] / df[\"Adj Close\"]\n    df[\"Adj Open\"] = df[\"Open\"] / ratio\n    df[\"Adj High\"] = df[\"High\"] / ratio\n    df[\"Adj Low\"] = df[\"Low\"] / ratio\n\n    df.drop(\n        [\"Open\", \"High\", \"Low\", \"Close\"],\n        axis=1, inplace=True)\n\n    df.rename(columns={\n        \"Adj Open\": \"Open\", \"Adj High\": \"High\",\n        \"Adj Low\": \"Low\", \"Adj Close\": \"Close\"\n    }, inplace=True)\n\n    df = df[[\"Open\", \"High\", \"Low\", \"Close\", \"Volume\"]]\n    return df[[\"Open\", \"High\", \"Low\", \"Close\", \"Volume\"]]\n\n\ndef back_adjust(data):\n    \"\"\" back-adjusted data to mimic true historical prices \"\"\"\n\n    df = data.copy()\n    ratio = df[\"Adj Close\"] / df[\"Close\"]\n    df[\"Adj Open\"] = df[\"Open\"] * ratio\n    df[\"Adj High\"] = df[\"High\"] * ratio\n    df[\"Adj Low\"] = df[\"Low\"] * ratio\n\n    df.drop(\n        [\"Open\", \"High\", \"Low\", \"Adj Close\"],\n        axis=1, inplace=True)\n\n    df.rename(columns={\n        \"Adj Open\": \"Open\", \"Adj High\": \"High\",\n        \"Adj Low\": \"Low\"\n    }, inplace=True)\n\n    return df[[\"Open\", \"High\", \"Low\", \"Close\", \"Volume\"]]\n\n\ndef parse_quotes(data, tz=None):\n    timestamps = data[\"timestamp\"]\n    ohlc = data[\"indicators\"][\"quote\"][0]\n    volumes = ohlc[\"volume\"]\n    opens = ohlc[\"open\"]\n    closes = ohlc[\"close\"]\n    lows = ohlc[\"low\"]\n    highs = ohlc[\"high\"]\n\n    adjclose = closes\n    if \"adjclose\" in data[\"indicators\"]:\n        adjclose = data[\"indicators\"][\"adjclose\"][0][\"adjclose\"]\n\n    quotes = _pd.DataFrame({\"Open\": opens,\n                            \"High\": highs,\n                            \"Low\": lows,\n                            \"Close\": closes,\n                            \"Adj Close\": adjclose,\n                            \"Volume\": volumes})\n\n    quotes.index = _pd.to_datetime(timestamps, unit=\"s\")\n    quotes.sort_index(inplace=True)\n\n    if tz is not None:\n        quotes.index = quotes.index.tz_localize(tz)\n\n    return quotes\n\n\ndef parse_actions(data, tz=None):\n    dividends = _pd.DataFrame(columns=[\"Dividends\"])\n    splits = _pd.DataFrame(columns=[\"Stock Splits\"])\n\n    if \"events\" in data:\n        if \"dividends\" in data[\"events\"]:\n            dividends = _pd.DataFrame(\n                data=list(data[\"events\"][\"dividends\"].values()))\n            dividends.set_index(\"date\", inplace=True)\n            dividends.index = _pd.to_datetime(dividends.index, unit=\"s\")\n            dividends.sort_index(inplace=True)\n            if tz is not None:\n                dividends.index = dividends.index.tz_localize(tz)\n\n            dividends.columns = [\"Dividends\"]\n\n        if \"splits\" in data[\"events\"]:\n            splits = _pd.DataFrame(\n                data=list(data[\"events\"][\"splits\"].values()))\n            splits.set_index(\"date\", inplace=True)\n            splits.index = _pd.to_datetime(splits.index, unit=\"s\")\n            splits.sort_index(inplace=True)\n            if tz is not None:\n                splits.index = splits.index.tz_localize(tz)\n            splits[\"Stock Splits\"] = splits[\"numerator\"] / \\\n                splits[\"denominator\"]\n            splits = splits[\"Stock Splits\"]\n\n    return dividends, splits\n\n\nclass ProgressBar:\n    def __init__(self, iterations, text='completed'):\n        self.text = text\n        self.iterations = iterations\n        self.prog_bar = '[]'\n        self.fill_char = '*'\n        self.width = 50\n        self.__update_amount(0)\n        self.elapsed = 1\n\n    def completed(self):\n        if self.elapsed > self.iterations:\n            self.elapsed = self.iterations\n        self.update_iteration(1)\n        print('\\r' + str(self), end='')\n        _sys.stdout.flush()\n        print()\n\n    def animate(self, iteration=None):\n        if iteration is None:\n            self.elapsed += 1\n            iteration = self.elapsed\n        else:\n            self.elapsed += iteration\n\n        print('\\r' + str(self), end='')\n        _sys.stdout.flush()\n        self.update_iteration()\n\n    def update_iteration(self, val=None):\n        val = val if val is not None else self.elapsed / float(self.iterations)\n        self.__update_amount(val * 100.0)\n        self.prog_bar += '  %s of %s %s' % (\n            self.elapsed, self.iterations, self.text)\n\n    def __update_amount(self, new_amount):\n        percent_done = int(round((new_amount / 100.0) * 100.0))\n        all_full = self.width - 2\n        num_hashes = int(round((percent_done / 100.0) * all_full))\n        self.prog_bar = '[' + self.fill_char * \\\n            num_hashes + ' ' * (all_full - num_hashes) + ']'\n        pct_place = (len(self.prog_bar) // 2) - len(str(percent_done))\n        pct_string = '%d%%' % percent_done\n        self.prog_bar = self.prog_bar[0:pct_place] + \\\n            (pct_string + self.prog_bar[pct_place + len(pct_string):])\n\n    def __str__(self):\n        return str(self.prog_bar)\n", "path": "yfinance/utils.py"}], "after_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n#\n# yfinance - market data downloader\n# https://github.com/ranaroussi/yfinance\n#\n# Copyright 2017-2019 Ran Aroussi\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nfrom __future__ import print_function\n\nimport requests as _requests\nimport re as _re\nimport pandas as _pd\nimport numpy as _np\nimport sys as _sys\n\ntry:\n    import ujson as _json\nexcept ImportError:\n    import json as _json\n\n\nuser_agent_headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}\n\n\ndef is_isin(string):\n    return bool(_re.match(\"^([A-Z]{2})([A-Z0-9]{9})([0-9]{1})$\", string))\n\n\ndef get_all_by_isin(isin, proxy=None, session=None):\n    if not(is_isin(isin)):\n        raise ValueError(\"Invalid ISIN number\")\n\n    from .base import _BASE_URL_\n    session = session or _requests\n    url = \"{}/v1/finance/search?q={}\".format(_BASE_URL_, isin)\n    data = session.get(url=url, proxies=proxy, headers=user_agent_headers)\n    try:\n        data = data.json()\n        ticker = data.get('quotes', [{}])[0]\n        return {\n            'ticker': {\n                'symbol': ticker['symbol'],\n                'shortname': ticker['shortname'],\n                'longname': ticker['longname'],\n                'type': ticker['quoteType'],\n                'exchange': ticker['exchDisp'],\n            },\n            'news': data.get('news', [])\n        }\n    except Exception:\n        return {}\n\n\ndef get_ticker_by_isin(isin, proxy=None, session=None):\n    data = get_all_by_isin(isin, proxy, session)\n    return data.get('ticker', {}).get('symbol', '')\n\n\ndef get_info_by_isin(isin, proxy=None, session=None):\n    data = get_all_by_isin(isin, proxy, session)\n    return data.get('ticker', {})\n\n\ndef get_news_by_isin(isin, proxy=None, session=None):\n    data = get_all_by_isin(isin, proxy, session)\n    return data.get('news', {})\n\n\ndef empty_df(index=[]):\n    empty = _pd.DataFrame(index=index, data={\n        'Open': _np.nan, 'High': _np.nan, 'Low': _np.nan,\n        'Close': _np.nan, 'Adj Close': _np.nan, 'Volume': _np.nan})\n    empty.index.name = 'Date'\n    return empty\n\n\ndef get_html(url, proxy=None, session=None):\n    session = session or _requests\n    html = session.get(url=url, proxies=proxy, headers=user_agent_headers).text\n    return html\n\n\ndef get_json(url, proxy=None, session=None):\n    session = session or _requests\n    html = session.get(url=url, proxies=proxy, headers=user_agent_headers).text\n\n    if \"QuoteSummaryStore\" not in html:\n        html = session.get(url=url, proxies=proxy).text\n        if \"QuoteSummaryStore\" not in html:\n            return {}\n\n    json_str = html.split('root.App.main =')[1].split(\n        '(this)')[0].split(';\\n}')[0].strip()\n    data = _json.loads(json_str)[\n        'context']['dispatcher']['stores']['QuoteSummaryStore']\n    # add data about Shares Outstanding for companies' tickers if they are available\n    try:\n        data['annualBasicAverageShares'] = _json.loads(json_str)[\n            'context']['dispatcher']['stores']['QuoteTimeSeriesStore']['timeSeries']['annualBasicAverageShares']\n    except Exception:\n        pass\n\n    # return data\n    new_data = _json.dumps(data).replace('{}', 'null')\n    new_data = _re.sub(\n        r'\\{[\\'|\\\"]raw[\\'|\\\"]:(.*?),(.*?)\\}', r'\\1', new_data)\n\n    return _json.loads(new_data)\n\n\ndef camel2title(o):\n    return [_re.sub(\"([a-z])([A-Z])\", r\"\\g<1> \\g<2>\", i).title() for i in o]\n\n\ndef auto_adjust(data):\n    df = data.copy()\n    ratio = df[\"Close\"] / df[\"Adj Close\"]\n    df[\"Adj Open\"] = df[\"Open\"] / ratio\n    df[\"Adj High\"] = df[\"High\"] / ratio\n    df[\"Adj Low\"] = df[\"Low\"] / ratio\n\n    df.drop(\n        [\"Open\", \"High\", \"Low\", \"Close\"],\n        axis=1, inplace=True)\n\n    df.rename(columns={\n        \"Adj Open\": \"Open\", \"Adj High\": \"High\",\n        \"Adj Low\": \"Low\", \"Adj Close\": \"Close\"\n    }, inplace=True)\n\n    df = df[[\"Open\", \"High\", \"Low\", \"Close\", \"Volume\"]]\n    return df[[\"Open\", \"High\", \"Low\", \"Close\", \"Volume\"]]\n\n\ndef back_adjust(data):\n    \"\"\" back-adjusted data to mimic true historical prices \"\"\"\n\n    df = data.copy()\n    ratio = df[\"Adj Close\"] / df[\"Close\"]\n    df[\"Adj Open\"] = df[\"Open\"] * ratio\n    df[\"Adj High\"] = df[\"High\"] * ratio\n    df[\"Adj Low\"] = df[\"Low\"] * ratio\n\n    df.drop(\n        [\"Open\", \"High\", \"Low\", \"Adj Close\"],\n        axis=1, inplace=True)\n\n    df.rename(columns={\n        \"Adj Open\": \"Open\", \"Adj High\": \"High\",\n        \"Adj Low\": \"Low\"\n    }, inplace=True)\n\n    return df[[\"Open\", \"High\", \"Low\", \"Close\", \"Volume\"]]\n\n\ndef parse_quotes(data, tz=None):\n    timestamps = data[\"timestamp\"]\n    ohlc = data[\"indicators\"][\"quote\"][0]\n    volumes = ohlc[\"volume\"]\n    opens = ohlc[\"open\"]\n    closes = ohlc[\"close\"]\n    lows = ohlc[\"low\"]\n    highs = ohlc[\"high\"]\n\n    adjclose = closes\n    if \"adjclose\" in data[\"indicators\"]:\n        adjclose = data[\"indicators\"][\"adjclose\"][0][\"adjclose\"]\n\n    quotes = _pd.DataFrame({\"Open\": opens,\n                            \"High\": highs,\n                            \"Low\": lows,\n                            \"Close\": closes,\n                            \"Adj Close\": adjclose,\n                            \"Volume\": volumes})\n\n    quotes.index = _pd.to_datetime(timestamps, unit=\"s\")\n    quotes.sort_index(inplace=True)\n\n    if tz is not None:\n        quotes.index = quotes.index.tz_localize(tz)\n\n    return quotes\n\n\ndef parse_actions(data, tz=None):\n    dividends = _pd.DataFrame(columns=[\"Dividends\"], index=_pd.DatetimeIndex([]))\n    splits = _pd.DataFrame(columns=[\"Stock Splits\"], index=_pd.DatetimeIndex([]))\n\n    if \"events\" in data:\n        if \"dividends\" in data[\"events\"]:\n            dividends = _pd.DataFrame(\n                data=list(data[\"events\"][\"dividends\"].values()))\n            dividends.set_index(\"date\", inplace=True)\n            dividends.index = _pd.to_datetime(dividends.index, unit=\"s\")\n            dividends.sort_index(inplace=True)\n            if tz is not None:\n                dividends.index = dividends.index.tz_localize(tz)\n\n            dividends.columns = [\"Dividends\"]\n\n        if \"splits\" in data[\"events\"]:\n            splits = _pd.DataFrame(\n                data=list(data[\"events\"][\"splits\"].values()))\n            splits.set_index(\"date\", inplace=True)\n            splits.index = _pd.to_datetime(splits.index, unit=\"s\")\n            splits.sort_index(inplace=True)\n            if tz is not None:\n                splits.index = splits.index.tz_localize(tz)\n            splits[\"Stock Splits\"] = splits[\"numerator\"] / \\\n                splits[\"denominator\"]\n            splits = splits[\"Stock Splits\"]\n\n    return dividends, splits\n\n\nclass ProgressBar:\n    def __init__(self, iterations, text='completed'):\n        self.text = text\n        self.iterations = iterations\n        self.prog_bar = '[]'\n        self.fill_char = '*'\n        self.width = 50\n        self.__update_amount(0)\n        self.elapsed = 1\n\n    def completed(self):\n        if self.elapsed > self.iterations:\n            self.elapsed = self.iterations\n        self.update_iteration(1)\n        print('\\r' + str(self), end='')\n        _sys.stdout.flush()\n        print()\n\n    def animate(self, iteration=None):\n        if iteration is None:\n            self.elapsed += 1\n            iteration = self.elapsed\n        else:\n            self.elapsed += iteration\n\n        print('\\r' + str(self), end='')\n        _sys.stdout.flush()\n        self.update_iteration()\n\n    def update_iteration(self, val=None):\n        val = val if val is not None else self.elapsed / float(self.iterations)\n        self.__update_amount(val * 100.0)\n        self.prog_bar += '  %s of %s %s' % (\n            self.elapsed, self.iterations, self.text)\n\n    def __update_amount(self, new_amount):\n        percent_done = int(round((new_amount / 100.0) * 100.0))\n        all_full = self.width - 2\n        num_hashes = int(round((percent_done / 100.0) * all_full))\n        self.prog_bar = '[' + self.fill_char * \\\n            num_hashes + ' ' * (all_full - num_hashes) + ']'\n        pct_place = (len(self.prog_bar) // 2) - len(str(percent_done))\n        pct_string = '%d%%' % percent_done\n        self.prog_bar = self.prog_bar[0:pct_place] + \\\n            (pct_string + self.prog_bar[pct_place + len(pct_string):])\n\n    def __str__(self):\n        return str(self.prog_bar)\n", "path": "yfinance/utils.py"}]}