Skip to content

Commit

Permalink
(Fixed errors in eastmoneyfund, linted)
Browse files Browse the repository at this point in the history
  • Loading branch information
blais committed Jun 19, 2024
1 parent 48508be commit 517b712
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 62 deletions.
80 changes: 50 additions & 30 deletions beanprice/sources/eastmoneyfund.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
Timezone information: the http API requests GMT+8,
the function transfers timezone to GMT+8 automatically
"""

import datetime
import re
from decimal import Decimal
Expand All @@ -23,64 +24,82 @@


# All of the easymoney funds are in CNY.
CURRENCY = 'CNY'
CURRENCY = "CNY"

TIMEZONE = datetime.timezone(datetime.timedelta(hours=+8), 'Asia/Shanghai')
TIMEZONE = datetime.timezone(datetime.timedelta(hours=+8), "Asia/Shanghai")


headers = {'content-type': 'application/json',
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:22.0)'
'Gecko/20100101 Firefox/22.0'}
headers = {
"content-type": "application/json",
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:22.0)"
"Gecko/20100101 Firefox/22.0",
}


class EastMoneyFundError(ValueError):
"An error from the EastMoneyFund API."


UnsupportTickerError = EastMoneyFundError(
'header not match, dont support this ticker type')
"header not match, dont support this ticker type"
)


def parse_page(page):
tr_re = re.compile(r'<tr>(.*?)</tr>')
tr_re = re.compile(r"<tr>(.*?)</tr>")
item_re = re.compile(
r'<td>(\d{4}-\d{2}-\d{2})</td><td.*?>(.*?)</td><td.*?>(.*?)</td>'
'<td.*?>(.*?)</td><td.*?>(.*?)</td><td.*?>(.*?)</td><td.*?></td>',
re.X)
r"<td>(\d{4}-\d{2}-\d{2})</td><td.*?>(.*?)</td><td.*?>(.*?)</td>"
"<td.*?>(.*?)</td><td.*?>(.*?)</td><td.*?>(.*?)</td><td.*?></td>",
re.X,
)
header_match = re.compile(
r'<th.*?净值日期</th><th>单位净值</th><th>累计净值</th><th>日增长率</th>'
'<th>申购状态</th><th>赎回状态</th>.*?分红送配</th>')
r"<th.*?净值日期</th><th>单位净值</th><th>累计净值</th><th>日增长率</th>"
"<th>申购状态</th><th>赎回状态</th>.*?分红送配</th>"
)
table = tr_re.findall(page)
if not header_match.match(table[0]):
raise UnsupportTickerError
try:
table = [(datetime.datetime.fromisoformat(t[0]).
replace(hour=15, tzinfo=TIMEZONE), Decimal(t[1]))
for t in map(lambda x: item_re.match(x).groups(), table[1:])]
table = [
(
datetime.datetime.fromisoformat(t[0]).replace(hour=15, tzinfo=TIMEZONE),
Decimal(t[1]),
)
for t in [item_re.match(x).groups() for x in table[1:]]
]
except AttributeError:
return None
return table


def get_price_series(ticker: str, time_begin: datetime.datetime, time_end: datetime.datetime):
base_url = 'https://fundf10.eastmoney.com/F10DataApi.aspx'
time_delta_day = (time_end-time_begin).days+1
pages = time_delta_day//30 + 1
def get_price_series(
ticker: str, time_begin: datetime.datetime, time_end: datetime.datetime
):
base_url = "https://fundf10.eastmoney.com/F10DataApi.aspx"
time_delta_day = (time_end - time_begin).days + 1
pages = time_delta_day // 30 + 1
res = []
for page in range(1, pages+1):
query = {'code': ticker, 'page': page,
'sdate': time_begin.astimezone(TIMEZONE).date(),
'edate': time_end.astimezone(TIMEZONE).date(), 'type': 'lsjz', 'per': 30}
for page in range(1, pages + 1):
query = {
"code": ticker,
"page": page,
"sdate": time_begin.astimezone(TIMEZONE).date(),
"edate": time_end.astimezone(TIMEZONE).date(),
"type": "lsjz",
"per": 30,
}
response = requests.get(base_url, params=query, headers=headers)
if response.status_code != requests.codes.ok:
raise EastMoneyFundError(
f"Invalid response ({response.status_code}): {response.text}")
f"Invalid response ({response.status_code}): {response.text}"
)

price = parse_page(response.text)
if price is None and page == 1:
raise EastMoneyFundError(
f'Invalid ticker {ticker} or '
f'search day {time_begin.date().isoformat()}~{time_end.date().isoformat()}')
f"Invalid ticker {ticker} or "
f"search day {time_begin.date().isoformat()}~{time_end.date().isoformat()}"
)
if price is None:
break
res.extend(price)
Expand All @@ -97,12 +116,13 @@ def get_latest_price(self, ticker):
return source.SourcePrice(last_price[1], last_price[0], CURRENCY)

def get_historical_price(self, ticker, time):
prices = get_price_series(
ticker, time-datetime.timedelta(days=10), time)
prices = get_price_series(ticker, time - datetime.timedelta(days=10), time)
last_price = prices[0]
return source.SourcePrice(last_price[1], last_price[0], CURRENCY)

def get_prices_series(self, ticker, time_begin, time_end):
res = [source.SourcePrice(x[1], x[0], CURRENCY)
for x in get_price_series(ticker, time_begin, time_end)]
res = [
source.SourcePrice(x[1], x[0], CURRENCY)
for x in get_price_series(ticker, time_begin, time_end)
]
return sorted(res, key=lambda x: x.time)
72 changes: 40 additions & 32 deletions beanprice/sources/eastmoneyfund_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,78 +7,86 @@

import requests

import eastmoneyfund
from beanprice.sources import eastmoneyfund
from beanprice import source


contents = '''
var apidata={ content:"<table class='w782 comm lsjz'><thead><tr><th class='first'>净值日期</th><th>单位净值</th><th>累计净值</th><th>日增长率</th><th>申购状态</th><th>赎回状态</th><th class='tor last'>分红送配</th></tr></thead><tbody><tr><td>2020-10-09</td><td class='tor bold'>5.1890</td><td class='tor bold'>5.1890</td><td class='tor bold red'>4.11%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-30</td><td class='tor bold'>4.9840</td><td class='tor bold'>4.9840</td><td class='tor bold red'>0.12%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-29</td><td class='tor bold'>4.9780</td><td class='tor bold'>4.9780</td><td class='tor bold red'>1.14%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-28</td><td class='tor bold'>4.9220</td><td class='tor bold'>4.9220</td><td class='tor bold red'>0.22%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-25</td><td class='tor bold'>4.9110</td><td class='tor bold'>4.9110</td><td class='tor bold red'>0.88%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-24</td><td class='tor bold'>4.8680</td><td class='tor bold'>4.8680</td><td class='tor bold grn'>-3.81%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-23</td><td class='tor bold'>5.0610</td><td class='tor bold'>5.0610</td><td class='tor bold red'>2.41%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-22</td><td class='tor bold'>4.9420</td><td class='tor bold'>4.9420</td><td class='tor bold grn'>-1.02%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-21</td><td class='tor bold'>4.9930</td><td class='tor bold'>4.9930</td><td class='tor bold grn'>-1.29%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-18</td><td class='tor bold'>5.0580</td><td class='tor bold'>5.0580</td><td class='tor bold red'>0.48%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-17</td><td class='tor bold'>5.0340</td><td class='tor bold'>5.0340</td><td class='tor bold red'>0.60%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-16</td><td class='tor bold'>5.0040</td><td class='tor bold'>5.0040</td><td class='tor bold grn'>-1.28%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-15</td><td class='tor bold'>5.0690</td><td class='tor bold'>5.0690</td><td class='tor bold red'>1.06%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-14</td><td class='tor bold'>5.0160</td><td class='tor bold'>5.0160</td><td class='tor bold red'>0.42%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-11</td><td class='tor bold'>4.9950</td><td class='tor bold'>4.9950</td><td class='tor bold red'>3.39%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-10</td><td class='tor bold'>4.8310</td><td class='tor bold'>4.8310</td><td class='tor bold grn'>-0.29%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr></tbody></table>",records:16,pages:1,curpage:1};'''
# ruff: noqa: E501,RUF001

unsupport_content = '''
var apidata={ content:"<table class='w782 comm lsjz'><thead><tr><th class='first'>净值日期</th><th>每万份收益</th><th>7日年化收益率(%)</th><th>申购状态</th><th>赎回状态</th><th class='tor last'>分红送配</th></tr></thead><tbody><tr><td>2020-09-10</td><td class='tor bold'>0.4230</td><td class='tor bold'>1.5730%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr></tbody></table>",records:1,pages:1,curpage:1};'''
contents = """
var apidata={ content:"<table class='w782 comm lsjz'><thead><tr><th class='first'>净值日期</th><th>单位净值</th><th>累计净值</th><th>日增长率</th><th>申购状态</th><th>赎回状态</th><th class='tor last'>分红送配</th></tr></thead><tbody><tr><td>2020-10-09</td><td class='tor bold'>5.1890</td><td class='tor bold'>5.1890</td><td class='tor bold red'>4.11%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-30</td><td class='tor bold'>4.9840</td><td class='tor bold'>4.9840</td><td class='tor bold red'>0.12%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-29</td><td class='tor bold'>4.9780</td><td class='tor bold'>4.9780</td><td class='tor bold red'>1.14%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-28</td><td class='tor bold'>4.9220</td><td class='tor bold'>4.9220</td><td class='tor bold red'>0.22%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-25</td><td class='tor bold'>4.9110</td><td class='tor bold'>4.9110</td><td class='tor bold red'>0.88%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-24</td><td class='tor bold'>4.8680</td><td class='tor bold'>4.8680</td><td class='tor bold grn'>-3.81%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-23</td><td class='tor bold'>5.0610</td><td class='tor bold'>5.0610</td><td class='tor bold red'>2.41%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-22</td><td class='tor bold'>4.9420</td><td class='tor bold'>4.9420</td><td class='tor bold grn'>-1.02%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-21</td><td class='tor bold'>4.9930</td><td class='tor bold'>4.9930</td><td class='tor bold grn'>-1.29%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-18</td><td class='tor bold'>5.0580</td><td class='tor bold'>5.0580</td><td class='tor bold red'>0.48%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-17</td><td class='tor bold'>5.0340</td><td class='tor bold'>5.0340</td><td class='tor bold red'>0.60%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-16</td><td class='tor bold'>5.0040</td><td class='tor bold'>5.0040</td><td class='tor bold grn'>-1.28%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-15</td><td class='tor bold'>5.0690</td><td class='tor bold'>5.0690</td><td class='tor bold red'>1.06%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-14</td><td class='tor bold'>5.0160</td><td class='tor bold'>5.0160</td><td class='tor bold red'>0.42%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-11</td><td class='tor bold'>4.9950</td><td class='tor bold'>4.9950</td><td class='tor bold red'>3.39%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr><tr><td>2020-09-10</td><td class='tor bold'>4.8310</td><td class='tor bold'>4.8310</td><td class='tor bold grn'>-0.29%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr></tbody></table>",records:16,pages:1,curpage:1};\
"""

unsupport_content = """
var apidata={ content:"<table class='w782 comm lsjz'><thead><tr><th class='first'>净值日期</th><th>每万份收益</th><th>7日年化收益率(%)</th><th>申购状态</th><th>赎回状态</th><th class='tor last'>分红送配</th></tr></thead><tbody><tr><td>2020-09-10</td><td class='tor bold'>0.4230</td><td class='tor bold'>1.5730%</td><td>开放申购</td><td>开放赎回</td><td class='red unbold'></td></tr></tbody></table>",records:1,pages:1,curpage:1};"""


def response(contents, status_code=requests.codes.ok):
"""Return a context manager to patch a JSON response."""
response = mock.Mock()
response.status_code = status_code
response.text = contents
return mock.patch('requests.get', return_value=response)
return mock.patch("requests.get", return_value=response)


class EastMoneyFundFetcher(unittest.TestCase):

def test_error_network(self):
with response(None, 404):
with self.assertRaises(ValueError) as exc:
with self.assertRaises(ValueError):
eastmoneyfund.get_price_series(
'377240', datetime.datetime.now(), datetime.datetime.now())
"377240", datetime.datetime.now(), datetime.datetime.now()
)

def test_unsupport_page(self):
with response(unsupport_content):
with self.assertRaises(ValueError) as exc:
eastmoneyfund.get_price_series(
'377240', datetime.datetime.now(), datetime.datetime.now())
self.assertEqual(
eastmoneyfund.UnsupportTickerError, exc.exception)
"377240", datetime.datetime.now(), datetime.datetime.now()
)
self.assertEqual(eastmoneyfund.UnsupportTickerError, exc.exception)

def test_latest_price(self):
with response(contents):
srcprice = eastmoneyfund.Source().get_latest_price('377240')
srcprice = eastmoneyfund.Source().get_latest_price("377240")
self.assertIsInstance(srcprice, source.SourcePrice)
self.assertEqual(Decimal('5.1890'), srcprice.price)
self.assertEqual('CNY', srcprice.quote_currency)
self.assertEqual(Decimal("5.1890"), srcprice.price)
self.assertEqual("CNY", srcprice.quote_currency)

def test_historical_price(self):
with response(contents):
time = datetime.datetime(2018, 3, 27, 0, 0, 0, tzinfo=tz.tzutc())
srcprice = eastmoneyfund.Source().get_historical_price('377240', time)
srcprice = eastmoneyfund.Source().get_historical_price("377240", time)
self.assertIsInstance(srcprice, source.SourcePrice)
self.assertEqual(Decimal('5.1890'), srcprice.price)
self.assertEqual('CNY', srcprice.quote_currency)
self.assertEqual(datetime.datetime(2020, 10, 9, 15, 0, 0,
tzinfo=eastmoneyfund.TIMEZONE),
srcprice.time)
self.assertEqual(Decimal("5.1890"), srcprice.price)
self.assertEqual("CNY", srcprice.quote_currency)
self.assertEqual(
datetime.datetime(2020, 10, 9, 15, 0, 0, tzinfo=eastmoneyfund.TIMEZONE),
srcprice.time,
)

def test_get_prices_series(self):
with response(contents):
time = datetime.datetime(2018, 3, 27, 0, 0, 0, tzinfo=tz.tzutc())
srcprice = eastmoneyfund.Source().get_prices_series(
'377240', time-datetime.timedelta(days=10), time)
"377240", time - datetime.timedelta(days=10), time
)
self.assertIsInstance(srcprice, list)
self.assertIsInstance(srcprice[-1], source.SourcePrice)
self.assertEqual(Decimal('5.1890'), srcprice[-1].price)
self.assertEqual('CNY', srcprice[-1].quote_currency)
self.assertEqual(datetime.datetime(2020, 10, 9, 15, 0, 0,
tzinfo=eastmoneyfund.TIMEZONE),
srcprice[-1].time)
self.assertEqual(Decimal("5.1890"), srcprice[-1].price)
self.assertEqual("CNY", srcprice[-1].quote_currency)
self.assertEqual(
datetime.datetime(2020, 10, 9, 15, 0, 0, tzinfo=eastmoneyfund.TIMEZONE),
srcprice[-1].time,
)
self.assertIsInstance(srcprice[0], source.SourcePrice)
self.assertEqual(Decimal('4.8310'), srcprice[0].price)
self.assertEqual('CNY', srcprice[0].quote_currency)
self.assertEqual(datetime.datetime(2020, 9, 10, 15, 0, 0,
tzinfo=eastmoneyfund.TIMEZONE),
srcprice[0].time)
self.assertEqual(Decimal("4.8310"), srcprice[0].price)
self.assertEqual("CNY", srcprice[0].quote_currency)
self.assertEqual(
datetime.datetime(2020, 9, 10, 15, 0, 0, tzinfo=eastmoneyfund.TIMEZONE),
srcprice[0].time,
)


if __name__ == '__main__':
if __name__ == "__main__":
unittest.main()

0 comments on commit 517b712

Please sign in to comment.