Skip to content

Commit ee4a087

Browse files
authored
Merge pull request #722 from suminb/feature/naver-reader
Daily historical data from Naver Finance
2 parents c47efbe + 369c780 commit ee4a087

File tree

6 files changed

+173
-0
lines changed

6 files changed

+173
-0
lines changed

docs/source/readers/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Data Readers
1414
iex
1515
moex
1616
nasdaq-trader
17+
naver
1718
oecd
1819
quandl
1920
stooq

docs/source/readers/naver.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Naver Finance
2+
-------------
3+
4+
.. py:module:: pandas_datareader.naver
5+
6+
.. autoclass:: NaverDailyReader
7+
:members:
8+
:inherited-members: read

docs/source/remote_data.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Currently the following sources are supported:
4141
- :ref:`Nasdaq Trader symbol definitions<remote_data.nasdaq_symbols>`
4242
- :ref:`Stooq<remote_data.stooq>`
4343
- :ref:`MOEX<remote_data.moex>`
44+
- :ref:`Naver Finance<remote_data.naver>`
4445

4546
It should be noted, that various sources support different kinds of data, so not all sources implement the same methods and the data elements returned might also differ.
4647

@@ -685,3 +686,19 @@ The Moscow Exchange (MOEX) provides historical data.
685686
import pandas_datareader.data as web
686687
f = web.DataReader('USD000UTSTOM', 'moex', start='2017-07-01', end='2017-07-31')
687688
f.head()
689+
690+
.. _remote_data.naver:
691+
692+
Naver Finance Data
693+
==================
694+
`Naver Finance <https://finance.naver.com>`_ provides Korean stock market
695+
(`KOSPI`_, `KOSDAQ`_) historical data.
696+
697+
.. ipython:: python
698+
699+
import pandas_datareader.data as web
700+
df = web.DataReader('005930', 'naver', start='2019-09-10', end='2019-10-09')
701+
df.head()
702+
703+
.. _KOSPI: https://en.wikipedia.org/wiki/KOSPI
704+
.. _KOSDAQ: https://en.wikipedia.org/wiki/KOSDAQ

pandas_datareader/data.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
)
3131
from pandas_datareader.moex import MoexReader
3232
from pandas_datareader.nasdaq_trader import get_nasdaq_symbols
33+
from pandas_datareader.naver import NaverDailyReader
3334
from pandas_datareader.oecd import OECDReader
3435
from pandas_datareader.quandl import QuandlReader
3536
from pandas_datareader.stooq import StooqDailyReader
@@ -44,6 +45,7 @@
4445
from pandas_datareader.yahoo.options import Options as YahooOptions
4546
from pandas_datareader.yahoo.quotes import YahooQuotesReader
4647

48+
4749
__all__ = [
4850
"get_components_yahoo",
4951
"get_data_enigma",
@@ -364,6 +366,7 @@ def DataReader(
364366
"av-monthly-adjusted",
365367
"av-intraday",
366368
"econdb",
369+
"naver",
367370
]
368371

369372
if data_source not in expected_source:
@@ -662,6 +665,16 @@ def DataReader(
662665
session=session,
663666
).read()
664667

668+
elif data_source == "naver":
669+
return NaverDailyReader(
670+
symbols=name,
671+
start=start,
672+
end=end,
673+
retry_count=retry_count,
674+
pause=pause,
675+
session=session,
676+
).read()
677+
665678
else:
666679
msg = "data_source=%r is not implemented" % data_source
667680
raise NotImplementedError(msg)

pandas_datareader/naver.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
from datetime import datetime
2+
from xml.etree import ElementTree
3+
4+
import numpy as np
5+
from pandas import DataFrame, to_datetime
6+
from pandas_datareader.base import _DailyBaseReader
7+
from six import string_types
8+
9+
10+
class NaverDailyReader(_DailyBaseReader):
11+
"""Fetches daily historical data from Naver Finance.
12+
13+
:param symbols: A single symbol; multiple symbols are not currently supported.
14+
:param adjust_price: Not implemented
15+
:param interval: Not implemented
16+
:param adjust_dividends: Not implemented
17+
"""
18+
19+
def __init__(
20+
self,
21+
symbols=None,
22+
start=None,
23+
end=None,
24+
retry_count=3,
25+
pause=0.1,
26+
session=None,
27+
adjust_price=False,
28+
ret_index=False,
29+
chunksize=1,
30+
interval="d",
31+
get_actions=False,
32+
adjust_dividends=True,
33+
):
34+
if not isinstance(symbols, string_types):
35+
raise NotImplementedError("Bulk-fetching is not implemented")
36+
37+
super(NaverDailyReader, self).__init__(
38+
symbols=symbols,
39+
start=start,
40+
end=end,
41+
retry_count=retry_count,
42+
pause=pause,
43+
session=session,
44+
chunksize=chunksize,
45+
)
46+
47+
self.headers = {
48+
"Sec-Fetch-Mode": "no-cors",
49+
"Referer": "https://finance.naver.com/item/fchart.nhn?code={}".format(
50+
symbols
51+
),
52+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36", # noqa
53+
}
54+
55+
@property
56+
def get_actions(self):
57+
return self._get_actions
58+
59+
@property
60+
def url(self):
61+
return "https://fchart.stock.naver.com/sise.nhn"
62+
63+
def _get_params(self, symbol):
64+
# NOTE: The server does not take start, end dates as inputs; it only
65+
# takes the number of trading days as an input. To circumvent this
66+
# pitfall, we calculate the number of business days between self.start
67+
# and the current date. And then we filter by self.end before returning
68+
# the final result (in _read_one_data()).
69+
days = np.busday_count(self.start.date(), datetime.now().date())
70+
params = {"symbol": symbol, "timeframe": "day", "count": days, "requestType": 0}
71+
return params
72+
73+
def _read_one_data(self, url, params):
74+
"""Read one data from specified symbol.
75+
76+
:rtype: DataFrame
77+
"""
78+
resp = self._get_response(url, params=params)
79+
parsed = self._parse_xml_response(resp.text)
80+
prices = DataFrame(
81+
parsed, columns=["Date", "Open", "High", "Low", "Close", "Volume"]
82+
)
83+
prices["Date"] = to_datetime(prices["Date"])
84+
prices = prices.set_index("Date")
85+
86+
# NOTE: See _get_params() for explanations.
87+
return prices[(prices.index >= self.start) & (prices.index <= self.end)]
88+
89+
def _parse_xml_response(self, xml_content):
90+
"""Parses XML response from the server.
91+
92+
An example of response:
93+
94+
<?xml version="1.0" encoding="EUC-KR" ?>
95+
<protocol>
96+
<chartdata symbol="005930" name="Samsung Elctronics" count="500"
97+
timeframe="day" precision="0" origintime="19900103">
98+
<item data="20170918|218500|222000|217000|220500|72124" />
99+
<item data="20170919|218000|221000|217500|219000|62753" />
100+
...
101+
</protocol>
102+
"""
103+
root = ElementTree.fromstring(xml_content)
104+
items = root.findall("chartdata/item")
105+
106+
for item in items:
107+
yield item.attrib["data"].split("|")

pandas_datareader/tests/test_naver.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from datetime import datetime
2+
3+
from pandas_datareader import DataReader
4+
import pytest
5+
6+
7+
class TestNaver(object):
8+
@pytest.mark.parametrize(
9+
"symbol, start, end",
10+
[
11+
("005930", (2019, 10, 1), (2019, 10, 7)),
12+
("000660", (2018, 1, 1), (2018, 12, 31)),
13+
("069500", (2017, 6, 3), (2018, 9, 9)),
14+
],
15+
)
16+
def test_naver_daily_reader(self, symbol, start, end):
17+
start = datetime(*start)
18+
end = datetime(*end)
19+
reader = DataReader(symbol, "naver", start, end)
20+
21+
assert reader.shape[1] == 5
22+
assert reader.index.min() >= start
23+
assert reader.index.max() <= end
24+
25+
def test_bulk_fetch(self):
26+
with pytest.raises(NotImplementedError):
27+
DataReader(["005930", "000660"])

0 commit comments

Comments
 (0)