From 708d729883f4fea1a2a15a1b405e132462fb8146 Mon Sep 17 00:00:00 2001 From: refraction-ray Date: Fri, 27 Mar 2020 21:25:54 +0800 Subject: [PATCH] version0.7.0 --- CHANGELOG.md | 5 +++- doc/source/xalpha.rst | 8 ++++++ setup.py | 2 +- tests/test_universal.py | 5 ++++ xalpha/__init__.py | 2 +- xalpha/exceptions.py | 8 ++++++ xalpha/toolbox.py | 14 +++++++++++ xalpha/universal.py | 54 +++++++++++++++++++++++++++++++++++++---- 8 files changed, 90 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ba1d23..93260f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,14 @@ # Changelog ## Unreleased + +## v0.7.0 - 2020.03.27 ### changed * 将面向对象封装的工具箱从 universal 模块移到单独的 toolbox 模块。 ### added * 增加内存缓存作为 IO 缓存的双重缓存层,提高数据读写速度。 -* ``get_daily``增加彭博的日线数据获取。 +* ``get_daily`` 增加彭博的日线数据获取。 * ``mul`` 增加 istatus 选项,可以场内外账单同时统计。 +* ``get_rt`` 增加新浪实时数据源,同时增加 double_check 选项确保实时数据稳定无误。 ### fixed * 完善聚宽云平台使用的导入。 diff --git a/doc/source/xalpha.rst b/doc/source/xalpha.rst index 24af2dd..f601bdb 100644 --- a/doc/source/xalpha.rst +++ b/doc/source/xalpha.rst @@ -81,6 +81,14 @@ xalpha.remain module :undoc-members: :show-inheritance: +xalpha.toolbox module +--------------------------- + +.. automodule:: xalpha.toolbox + :members: + :undoc-members: + :show-inheritance: + xalpha.trade module ------------------- diff --git a/setup.py b/setup.py index cfab675..de3a870 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="xalpha", - version="0.6.2", + version="0.7.0", author="refraction-ray", author_email="refraction-ray@protonmail.com", description="all about fund investment", diff --git a/tests/test_universal.py b/tests/test_universal.py index fabd35c..56a01dc 100644 --- a/tests/test_universal.py +++ b/tests/test_universal.py @@ -48,6 +48,11 @@ def test_get_xueqiu_rt(): assert isinstance(xa.get_rt("SH501018")["percent"], float) +def test_get_sina_rt(): + assert xa.get_rt("PDD", _from="sina")["currency"] == "USD" + xa.get_rt("HK00700", double_check=True) + + def test_get_investing_rt(): assert xa.get_rt("currencies/usd-cny")["currency"] == None assert xa.get_rt("/indices/germany-30")["name"] == "德国DAX30指数 (GDAXI)" diff --git a/xalpha/__init__.py b/xalpha/__init__.py index 5724ec0..b6eeacb 100644 --- a/xalpha/__init__.py +++ b/xalpha/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.6.2" +__version__ = "0.7.0" __author__ = "refraction-ray" __name__ = "xalpha" diff --git a/xalpha/exceptions.py b/xalpha/exceptions.py index b3658d8..d52a7df 100644 --- a/xalpha/exceptions.py +++ b/xalpha/exceptions.py @@ -46,3 +46,11 @@ class DataSourceNotFound(XalphaException): """ pass + + +class DataPossiblyWrong(XalphaException): + """ + Used for data failed to verify + """ + + pass diff --git a/xalpha/toolbox.py b/xalpha/toolbox.py index bb72d1e..6d42b29 100644 --- a/xalpha/toolbox.py +++ b/xalpha/toolbox.py @@ -165,6 +165,10 @@ def summary(self): class Compare: + """ + 将不同金融产品同起点归一化比较 + """ + def __init__(self, *codes, start="20200101", end=yesterday()): """ @@ -200,7 +204,17 @@ def __init__(self, *codes, start="20200101", end=yesterday()): self.codes = codelist def v(self): + """ + 显示日线可视化 + + :return: + """ return self.totdf.plot(x="date", y=self.codes) def corr(self): + """ + 打印相关系数矩阵 + + :return: pd.DataFrame + """ return self.totdf.iloc[:, 1:].pct_change().corr() diff --git a/xalpha/universal.py b/xalpha/universal.py index 8e3c09f..48177ed 100644 --- a/xalpha/universal.py +++ b/xalpha/universal.py @@ -34,6 +34,7 @@ from xalpha.info import fundinfo, mfundinfo from xalpha.cons import rget, rpost, rget_json, rpost_json, yesterday, opendate from xalpha.provider import data_source +from xalpha.exceptions import DataPossiblyWrong thismodule = sys.modules[__name__] @@ -489,6 +490,8 @@ def _float(n): def get_xueqiu_rt(code, token="a664afb60c7036c7947578ac1a5860c4cfb6b3b5"): + if code.startswith("HK"): + code = code[2:] url = "https://stock.xueqiu.com/v5/stock/quote.json?symbol={code}&extend=detail" r = rget_json( url.format(code=code), @@ -551,28 +554,69 @@ def get_cninvesting_rt(suburl): } -def get_rt(code, _from=None): +def get_rt_from_sina(code): + if ( + code.startswith("SH") or code.startswith("SZ") or code.startswith("HK") + ) and code[2:].isdigit(): + tinycode = code[:2].lower() + code[2:] + else: # 美股 + tinycode = "gb_" + if code.startswith("."): + code = code[1:] + tinycode += code.lower() + r = rget("https://hq.sinajs.cn/list={tinycode}".format(tinycode=tinycode)) + l = r.text.split("=")[1].split(",") + d = {} + d["name"] = l[0].strip('"') + if ( + code.startswith("SH") or code.startswith("SZ") or code.startswith("HK") + ) and code[2:].isdigit(): + d["current"] = float(l[3]) + d["percent"] = round((float(l[3]) / float(l[2]) - 1) * 100, 2) + d["current_ext"] = None + if code.startswith("HK"): + d["currency"] = "HKD" + else: + d["currency"] = "CNY" + else: + d["currency"] = "USD" + d["current"] = float(l[1]) + d["percent"] = float(l[2]) + d["current_ext"] = None + return d + + +def get_rt(code, _from=None, double_check=False, double_check_threhold=0.005): """ universal fetcher for realtime price of literally everything. :param code: str. 规则同 :func:`get_daily`. 需要注意场外基金和外汇中间价是不支持实时行情的,因为其每日只有一个报价。对于 investing 的数据源,只支持网址格式代码。 :param _from: Optional[str]. can be one of "xueqiu", "investing". Only used for debug to enfore data source. For common use, _from can be chosed automatically based on code in the run time. + :param double_check: Optional[bool], default False. 如果设为 True,只适用于 A 股,美股,港股实时行情,会通过至少两个不同的数据源交叉验证,确保正确。 + 适用于需要自动交易等情形,防止实时数据异常。 :return: Dict[str, Any]. 包括 "name", "current", "percent" 三个必有项和 "current_ext"(盘后价格), "currency" (计价货币)两个值可能为 ``None`` 的选项。 """ if not _from: if len(code.split("/")) > 1: _from = "investing" - elif code.startswith("HK") and code[2:].isdigit(): - _from = "xueqiu" - code = code[2:] - else: + # elif code.startswith("HK") and code[2:].isdigit(): + # _from = "xueqiu" + else: # 默认不启用新浪实时,只做双重验证 _from = "xueqiu" if _from in ["cninvesting", "investing"]: return get_cninvesting_rt(code) + elif double_check: + r1 = get_xueqiu_rt(code, token=get_token()) + r2 = get_rt_from_sina(code) + if abs(r1["current"] / r2["current"] - 1) > double_check_threhold: + raise DataPossiblyWrong("realtime data unmatch for %s" % code) + return r1 elif _from in ["xueqiu", "xq", "snowball"]: return get_xueqiu_rt(code, token=get_token()) + elif _from in ["sina", "sn", "xinlang"]: + return get_rt_from_sina(code) get_realtime = get_rt