├── .gitignore ├── GuGu ├── __init__.py ├── base.py ├── billboard.py ├── classify.py ├── config.py ├── lowriskintarb.py ├── macro.py ├── marketdata.py ├── reference.py ├── stockdata.py ├── stockinfo.py └── utility.py ├── LICENSE ├── README.md ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | __pycache__ 3 | build 4 | dist 5 | GuGu.egg-info 6 | -------------------------------------------------------------------------------- /GuGu/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | __version__ = '0.1.0' 4 | __author__ = 'TabQ' 5 | 6 | from GuGu.billboard import (BillBoard) 7 | 8 | from GuGu.classify import (Classify) 9 | 10 | from GuGu.lowriskintarb import (LowRiskIntArb) 11 | 12 | from GuGu.macro import (Macro) 13 | 14 | from GuGu.marketdata import (MarketData) 15 | 16 | from GuGu.reference import (Reference) 17 | 18 | from GuGu.stockdata import (StockData) 19 | 20 | from GuGu.stockinfo import (StockInfo) 21 | 22 | from GuGu.utility import (Utility) 23 | -------------------------------------------------------------------------------- /GuGu/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | 基础类 4 | Created on 2018/12/27 5 | @author: TabQ 6 | @group : GuGu 7 | @contact: 16621596@qq.com 8 | """ 9 | 10 | import pandas as pd 11 | import sys 12 | import requests 13 | from requests.adapters import HTTPAdapter 14 | from urllib3.util import Retry 15 | import config as cf 16 | 17 | 18 | class Base(): 19 | def __init__(self, pandas=True, inter=True): 20 | self.__pandas = False if not pandas else True 21 | self._PY3 = (sys.version_info[0] >= 3) 22 | self._inter = False if not inter else True # 交互模式 23 | 24 | self._session = requests.Session() 25 | retry = Retry(connect=5, backoff_factor=1) 26 | adapter = HTTPAdapter(max_retries=retry) 27 | self._session.mount('http://', adapter) 28 | self._session.mount('https://', adapter) 29 | self._session.keep_alive = False 30 | 31 | self._data = pd.DataFrame() 32 | 33 | 34 | def _result(self): 35 | """ 36 | 返回结果:使用pandas时返回DataFrame否则返回list 37 | Parameters 38 | ----- 39 | dataframe:pandas.DataFrame 40 | index:string 41 | 返回dict时选用的键 e.g. code 42 | return 43 | ----- 44 | pandas.DataFrame or dict 45 | """ 46 | if self._data.empty: 47 | return None 48 | elif self.__pandas: 49 | return self._data 50 | else: 51 | return self._data.to_dict('records') 52 | 53 | 54 | def output(self, full=False): 55 | print('') 56 | 57 | if not full: 58 | print(self._data) 59 | else: 60 | pd.set_option('display.max_columns', None) 61 | pd.set_option('display.max_rows', None) 62 | pd.set_option('max_colwidth',100) 63 | 64 | print(self._data) 65 | 66 | 67 | def getPandas(self): 68 | return self.__pandas 69 | 70 | 71 | def setPandas(self, pandas=True): 72 | self.__pandas = pandas 73 | 74 | 75 | def getInter(self): 76 | """ 77 | 获取交互模式状态 78 | return 79 | ------ 80 | True or False 81 | """ 82 | return self._inter 83 | 84 | 85 | def setInter(self, inter=False): 86 | """ 87 | 设置交互模式 88 | Parameters 89 | ------ 90 | inter: bool 91 | """ 92 | self._inter = inter 93 | 94 | 95 | def _writeHead(self): 96 | if self._inter: 97 | sys.stdout.write(cf.GETTING_TIPS) 98 | sys.stdout.flush() 99 | 100 | 101 | def _writeConsole(self): 102 | if self._inter: 103 | sys.stdout.write(cf.GETTING_FLAG) 104 | sys.stdout.flush() 105 | 106 | 107 | -------------------------------------------------------------------------------- /GuGu/billboard.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | 龙虎榜类 4 | Created on 2019/01/11 5 | @author: TabQ 6 | @group : GuGu 7 | @contact: 16621596@qq.com 8 | """ 9 | 10 | import time 11 | import re 12 | import pandas as pd 13 | from pandas.compat import StringIO 14 | import lxml.html 15 | from lxml import etree 16 | from base import Base, cf 17 | from utility import Utility 18 | 19 | class BillBoard(Base): 20 | def topList(self, date = None, retry=3, pause=0.001): 21 | """ 22 | 获取每日龙虎榜列表 23 | Parameters 24 | -------- 25 | date:string 26 | 明细数据日期 format:YYYY-MM-DD 如果为空,返回最近一个交易日的数据 27 | retry : int, 默认 3 28 | 如遇网络等问题重复执行的次数 29 | pause : int, 默认 0 30 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 31 | 32 | Return 33 | ------ 34 | DataFrame or List: [{'code':, 'name':, ...}, ...] 35 | code:代码 36 | name :名称 37 | pchange:涨跌幅 38 | amount:龙虎榜成交额(万) 39 | buy:买入额(万) 40 | bratio:占总成交比例 41 | sell:卖出额(万) 42 | sratio :占总成交比例 43 | reason:上榜原因 44 | unscramble: 解读 45 | date :日期 46 | """ 47 | self._data = pd.DataFrame() 48 | 49 | if date is None: 50 | if Utility.getHour() < 18: 51 | date = Utility.lastTradeDate() 52 | else: 53 | date = Utility.getToday() 54 | else: 55 | if not Utility.isTradeDay(date): 56 | return None 57 | 58 | for _ in range(retry): 59 | time.sleep(pause) 60 | 61 | try: 62 | # http://data.eastmoney.com/DataCenter_V3/stock2016/TradeDetail/pagesize=200,page=1,sortRule=-1,sortType=,startDate=2019-01-10,endDate=2019-01-10,gpfw=0,js=vardata_tab_1.html 63 | request = self._session.get( cf.LHB_URL % (date, date), timeout=10 ) 64 | request.encoding = 'gbk' 65 | text = request.text.split('_1=')[1] 66 | dataDict = Utility.str2Dict(text) 67 | 68 | self._data = pd.DataFrame(dataDict['data'], columns=cf.LHB_TMP_COLS) 69 | self._data.columns = cf.LHB_COLS 70 | self._data['buy'] = self._data['buy'].astype(float) 71 | self._data['sell'] = self._data['sell'].astype(float) 72 | self._data['amount'] = self._data['amount'].astype(float) 73 | self._data['Turnover'] = self._data['Turnover'].astype(float) 74 | self._data['bratio'] = self._data['buy'] / self._data['Turnover'] 75 | self._data['sratio'] = self._data['sell'] / self._data['Turnover'] 76 | self._data['bratio'] = self._data['bratio'].map(cf.FORMAT) 77 | self._data['sratio'] = self._data['sratio'].map(cf.FORMAT) 78 | self._data['date'] = date 79 | for col in ['amount', 'buy', 'sell']: 80 | self._data[col] = self._data[col].astype(float) 81 | self._data[col] = self._data[col] / 10000 82 | self._data[col] = self._data[col].map(cf.FORMAT) 83 | self._data = self._data.drop('Turnover', axis=1) 84 | except: 85 | pass 86 | else: 87 | return self._result() 88 | 89 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 90 | 91 | 92 | def countTops(self, days=5, retry=3, pause=0.001): 93 | """ 94 | 获取个股上榜统计数据 95 | Parameters 96 | -------- 97 | days:int 98 | 天数,统计n天以来上榜次数,默认为5天,其余是10、30、60 99 | retry : int, 默认 3 100 | 如遇网络等问题重复执行的次数 101 | pause : int, 默认 0 102 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 103 | Return 104 | ------ 105 | DataFrame or List: [{'code':, 'name':, ...}, ...] 106 | code:代码 107 | name:名称 108 | count:上榜次数 109 | bamount:累积购买额(万) 110 | samount:累积卖出额(万) 111 | net:净额(万) 112 | bcount:买入席位数 113 | scount:卖出席位数 114 | """ 115 | self._data = pd.DataFrame() 116 | 117 | if Utility.checkLhbInput(days) is True: 118 | self._writeHead() 119 | 120 | # http://vip.stock.finance.sina.com.cn/q/go.php/vLHBData/kind/ggtj/index.phtml?last=5&p=1 121 | self._data = self.__parsePage(kind=cf.LHB_KINDS[0], last=days, column=cf.LHB_GGTJ_COLS, dataArr=pd.DataFrame(), pageNo=1, retry=retry, pause=pause) 122 | self._data['code'] = self._data['code'].map(lambda x: str(x).zfill(6)) 123 | if self._data is not None: 124 | self._data = self._data.drop_duplicates('code') 125 | 126 | return self._result() 127 | 128 | 129 | def brokerTops(self, days=5, retry=3, pause=0.001): 130 | """ 131 | 获取营业部上榜统计数据 132 | Parameters 133 | -------- 134 | days:int 135 | 天数,统计n天以来上榜次数,默认为5天,其余是10、30、60 136 | retry : int, 默认 3 137 | 如遇网络等问题重复执行的次数 138 | pause : int, 默认 0 139 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 140 | Return 141 | --------- 142 | DataFrame or List: [{'broker':, 'count':, ...}, ...] 143 | broker:营业部名称 144 | count:上榜次数 145 | bamount:累积购买额(万) 146 | bcount:买入席位数 147 | samount:累积卖出额(万) 148 | scount:卖出席位数 149 | top3:买入前三股票 150 | """ 151 | self._data = pd.DataFrame() 152 | 153 | if Utility.checkLhbInput(days) is True: 154 | self._writeHead() 155 | 156 | # http://vip.stock.finance.sina.com.cn/q/go.php/vLHBData/kind/yytj/index.phtml?last=5&p=1 157 | self._data = self.__parsePage(kind=cf.LHB_KINDS[1], last=days, column=cf.LHB_YYTJ_COLS, dataArr=pd.DataFrame(), pageNo=1, retry=retry, pause=pause) 158 | 159 | return self._result() 160 | 161 | 162 | def instTops(self, days=5, retry=3, pause=0.001): 163 | """ 164 | 获取机构席位追踪统计数据 165 | Parameters 166 | -------- 167 | days:int 168 | 天数,统计n天以来上榜次数,默认为5天,其余是10、30、60 169 | retry : int, 默认 3 170 | 如遇网络等问题重复执行的次数 171 | pause : int, 默认 0 172 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 173 | 174 | Return 175 | -------- 176 | DataFrame or List: [{'code':, 'name':, ...}, ...] 177 | code:代码 178 | name:名称 179 | bamount:累积买入额(万) 180 | bcount:买入次数 181 | samount:累积卖出额(万) 182 | scount:卖出次数 183 | net:净额(万) 184 | """ 185 | self._data = pd.DataFrame() 186 | 187 | if Utility.checkLhbInput(days) is True: 188 | self._writeHead() 189 | 190 | # http://vip.stock.finance.sina.com.cn/q/go.php/vLHBData/kind/jgzz/index.phtml?last=5&p=1 191 | self._data = self.__parsePage(kind=cf.LHB_KINDS[2], last=days, column=cf.LHB_JGZZ_COLS, dataArr=pd.DataFrame(), pageNo=1, retry=retry, pause=pause, drop_column=[2,3]) 192 | self._data['code'] = self._data['code'].map(lambda x: str(x).zfill(6)) 193 | 194 | return self._result() 195 | 196 | 197 | def instDetail(self, retry=3, pause=0.001): 198 | """ 199 | 获取最近一个交易日机构席位成交明细统计数据 200 | Parameters 201 | -------- 202 | retry : int, 默认 3 203 | 如遇网络等问题重复执行的次数 204 | pause : int, 默认 0 205 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 206 | 207 | Return 208 | ---------- 209 | DataFrame or List: [{'code':, 'name':, ...}, ...] 210 | code:股票代码 211 | name:股票名称 212 | date:交易日期 213 | bamount:机构席位买入额(万) 214 | samount:机构席位卖出额(万) 215 | type:类型 216 | """ 217 | self._data = pd.DataFrame() 218 | 219 | self._writeHead() 220 | 221 | # http://vip.stock.finance.sina.com.cn/q/go.php/vLHBData/kind/jgmx/index.phtml?last=&p=1 222 | self._data = self.__parsePage(kind=cf.LHB_KINDS[3], last='', column=cf.LHB_JGMX_COLS, dataArr=pd.DataFrame(), pageNo=1, retry=retry, pause=pause) 223 | if len(self._data) > 0: 224 | self._data['code'] = self._data['code'].map(lambda x: str(x).zfill(6)) 225 | 226 | return self._result() 227 | 228 | 229 | def __parsePage(self, kind, last, column, dataArr, pageNo=1, retry=3, pause=0.001, drop_column=None): 230 | self._writeConsole() 231 | 232 | for _ in range(retry): 233 | time.sleep(pause) 234 | 235 | try: 236 | request = self._session.get( cf.LHB_SINA_URL % (kind, last, pageNo), timeout=10 ) 237 | request.encoding = 'gbk' 238 | html = lxml.html.parse(StringIO(request.text)) 239 | res = html.xpath("//table[@id=\"dataTable\"]/tr") 240 | if self._PY3: 241 | sarr = [etree.tostring(node).decode('utf-8') for node in res] 242 | else: 243 | sarr = [etree.tostring(node) for node in res] 244 | sarr = ''.join(sarr) 245 | sarr = '%s
'%sarr 246 | df = pd.read_html(sarr)[0] 247 | if drop_column is not None: 248 | df = df.drop(drop_column, axis=1) 249 | df.columns = column 250 | dataArr = dataArr.append(df, ignore_index=True) 251 | nextPage = html.xpath('//div[@class=\"pages\"]/a[last()]/@onclick') 252 | if len(nextPage) > 0: 253 | pageNo = re.findall(r'\d+', nextPage[0])[0] 254 | return self.__parsePage(kind, last, column, dataArr, pageNo, retry, pause, drop_column) 255 | else: 256 | return dataArr 257 | except Exception as e: 258 | print(e) 259 | 260 | 261 | -------------------------------------------------------------------------------- /GuGu/classify.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | 股票所属类 4 | Created on 2019/01/10 5 | @author: TabQ 6 | @group : GuGu 7 | @contact: 16621596@qq.com 8 | """ 9 | import time 10 | import json 11 | import re 12 | import pandas as pd 13 | from base import Base, cf 14 | 15 | class Classify(Base): 16 | def byIndustry(self, std='sina', retry=3, pause=0.001): 17 | """ 18 | 获取行业分类数据 19 | Parameters 20 | ---------- 21 | std : string 22 | sina:新浪行业 sw:申万 行业 23 | retry : int, 默认 10 24 | 如遇网络等问题重复执行的次数 25 | pause : int, 默认 0.001 26 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 27 | 28 | Returns 29 | ------- 30 | DataFrame or List: [{'code':, 'name':, ...}, ...] 31 | code :股票代码 32 | name :股票名称 33 | c_name :行业名称 34 | """ 35 | self._data = pd.DataFrame() 36 | 37 | if std == 'sw': 38 | # http://vip.stock.finance.sina.com.cn/q/view/SwHy.php 39 | df = self.__getTypeData(cf.SINA_INDUSTRY_INDEX_URL % 'SwHy.php') 40 | else: 41 | # http://vip.stock.finance.sina.com.cn/q/view/newSinaHy.php 42 | df = self.__getTypeData(cf.SINA_INDUSTRY_INDEX_URL % 'newSinaHy.php') 43 | 44 | self._writeHead() 45 | for row in df.values: 46 | rowDf = self.__getDetail(row[0], retry, pause) 47 | rowDf['c_name'] = row[1] 48 | self._data = self._data.append(rowDf, ignore_index=True) 49 | 50 | return self._result() 51 | 52 | 53 | def byConcept(self, retry=3, pause=0.001): 54 | """ 55 | 获取概念分类数据 56 | Parameters 57 | ---------- 58 | retry : int, 默认 3 59 | 如遇网络等问题重复执行的次数 60 | pause : int, 默认 0.001 61 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 62 | Return 63 | -------- 64 | DataFrame 65 | code :股票代码 66 | name :股票名称 67 | c_name :概念名称 68 | """ 69 | self._data = pd.DataFrame() 70 | 71 | self._writeHead() 72 | # http://money.finance.sina.com.cn/q/view/newFLJK.php?param=class 73 | df = self.__getTypeData( cf.SINA_CONCEPTS_INDEX_URL ) 74 | 75 | for row in df.values: 76 | rowDf = self.__getDetail(row[0], retry, pause) 77 | rowDf['c_name'] = row[1] 78 | self._data = self._data.append(rowDf) 79 | 80 | return self._result() 81 | 82 | 83 | def __getTypeData(self, url): 84 | try: 85 | request = self._session.get(url, timeout=10) 86 | request.encoding = 'gbk' 87 | text = request.text.split('=')[1] 88 | dataJson = json.loads(text) 89 | df = pd.DataFrame([[row.split(',')[0], row.split(',')[1]] for row in dataJson.values()], columns=['tag', 'name']) 90 | 91 | return df 92 | except Exception as e: 93 | print(str(e)) 94 | 95 | def __getDetail(self, tag, retry=3, pause=0.001): 96 | self._writeConsole() 97 | 98 | for retryCount in range(retry): 99 | time.sleep(pause) 100 | 101 | try: 102 | # http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/Market_Center.getHQNodeData?page=1&num=1000&sort=symbol&asc=1&node=new_zhhy&symbol=&_s_r_a=page 103 | request = self._session.get( cf.SINA_DATA_DETAIL_URL % tag, timeout=10 ) 104 | reg = re.compile(r'\,(.*?)\:') 105 | text = reg.sub(r',"\1":', request.text) 106 | text = text.replace('"{symbol', '{"symbol') 107 | text = text.replace('{symbol', '{"symbol"') 108 | jstr = json.dumps(text) 109 | js = json.loads(jstr) 110 | 111 | df = pd.DataFrame(pd.read_json(js, dtype={'code':object}), columns=cf.FOR_CLASSIFY_B_COLS) 112 | 113 | return df 114 | except: 115 | time.sleep((retryCount+1)*10) 116 | 117 | raise IOError(cf.NETWORK_ERR_MSG) 118 | 119 | 120 | -------------------------------------------------------------------------------- /GuGu/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | 地址&配置 4 | Created on 2018/12/26 5 | @author: TabQ 6 | @group : GuGu 7 | @contact: 16621596@qq.com 8 | """ 9 | 10 | VERSION = '0.1.0' 11 | HOLIDAY_URL = 'http://timor.tech/api/holiday/info/%s' 12 | K_LABELS = ['D', 'W', 'M'] 13 | K_MIN_LABELS = ['5', '15', '30', '60'] 14 | K_TYPE = {'D': 'akdaily', 'W': 'akweekly', 'M': 'akmonthly'} 15 | TT_K_TYPE = {'D': 'day', 'W': 'week', 'M': 'month'} 16 | FQ_KEY = ['qfqday', 'hfqday', 'day'] 17 | INDEX_LABELS = ['sh', 'sz', 'hs300', 'sz50', 'cyb', 'zxb', 'zx300', 'zh500'] 18 | INDEX_LIST = {'sh': 'sh000001', 'sz': 'sz399001', 'hs300': 'sz399300', 19 | 'sz50': 'sh000016', 'zxb': 'sz399005', 'cyb': 'sz399006', 'zx300': 'sz399008', 'zh500':'sh000905'} 20 | P_TYPE = {'http': 'http://', 'ftp': 'ftp://'} 21 | PAGE_NUM = [40, 60, 80, 100] 22 | FORMAT = lambda x: '%.2f' % x 23 | FORMAT4 = lambda x: '%.4f' % x 24 | INDEX_ETF_URL = 'https://www.jisilu.cn/jisiludata/etf.php?rp=25&page=%s' 25 | INDEX_ETF_COLS = ['fund_id', 'fund_nm', 'index_id', 'creation_unit', 'amount', 'unit_total', 26 | 'unit_incr', 'price', 'volume', 'increase_rt', 'estimate_value', 'discount_rt', 27 | 'fund_nav', 'nav_dt', 'index_nm', 'index_increase_rt', 'pe', 'pb'] 28 | STOCK_LOF_URL = 'https://www.jisilu.cn/data/lof/stock_lof_list/?rp=25&page=%s' 29 | STOCK_LOF_COLS = ['fund_id', 'fund_nm', 'price', 'increase_rt', 'volume', 'amount', 30 | 'fund_nav', 'nav_dt', 'estimate_value', 'discount_rt', 'stock_ratio', 31 | 'stock_increase_rt', 'apply_fee', 'redeem_fee', 'apply_redeem_status'] 32 | INDEX_LOF_URL = 'https://www.jisilu.cn/data/lof/index_lof_list/?rp=25&page=%s' 33 | INDEX_LOF_COLS = ['fund_id', 'fund_nm', 'price', 'increase_rt', 'volume', 'amount', 34 | 'fund_nav', 'nav_dt', 'estimate_value', 'discount_rt', 'index_id', 'index_nm', 35 | 'index_increase_rt', 'apply_fee', 'redeem_fee', 'apply_redeem_status'] 36 | RATING_FUNDA_URL = 'https://www.jisilu.cn/data/sfnew/funda_list/?page=%s' 37 | RATING_FUNDA_COLS = ['funda_id', 'funda_name', 'funda_current_price', 'funda_increase_rt', 'funda_volume', 'funda_value', 38 | 'funda_discount_rt', 'funda_coupon', 'funda_coupon_next', 'funda_profit_rt_next', 'funda_index_id', 39 | 'funda_index_name', 'funda_index_increase_rt', 'funda_lower_recalc_rt', 'lower_recalc_profit_rt', 40 | 'fundb_upper_recalc_rt', 'funda_base_est_dis_rt_t1', 'funda_base_est_dis_rt_t2', 'funda_amount', 41 | 'funda_amount_increase', 'abrate', 'next_recalc_dt'] 42 | RATING_FUNDB_URL = 'https://www.jisilu.cn/data/sfnew/fundb_list/?page=%s' 43 | RATING_FUNDB_COLS = ['fundb_id', 'fundb_name', 'fundb_base_fund_id', 'funda_id', 'funda_name', 'coupon', 'manage_fee', 'funda_current_price', 44 | 'funda_upper_price', 'funda_lower_price', 'funda_increase_rt', 'fundb_current_price', 'fundb_upper_price', 45 | 'fundb_lower_price', 'fundb_increase_rt', 'fundb_volume', 'fundb_value', 'fundm_value', 'fundb_discount_rt', 46 | 'fundb_price_leverage_rt', 'fundb_net_leverage_rt', 'fundb_capital_rasising_rt', 'fundb_lower_recalc_rt', 47 | 'fundb_upper_recalc_rt', 'b_est_val', 'fundb_index_id', 'fundb_index_name', 'fundb_index_increase_rt', 48 | 'funda_ratio', 'fundb_ratio', 'fundb_base_price', 'fundB_amount', 'fundB_amount_increase', 'abrate'] 49 | RATING_FUNDM_URL = 'https://www.jisilu.cn/data/sfnew/fundm_list/?page=%s' 50 | RATING_FUNDM_COLS = ['base_fund_id', 'base_fund_nm', 'market', 'issue_dt', 'manage_fee', 'index_id', 'index_nm', 'lower_recalc_price', 51 | 'a_ratio', 'b_ratio', 'next_recalc_dt', 'fundA_id', 'fundA_nm', 'coupon', 'coupon_next', 'fundB_id', 'fundB_nm', 52 | 'price', 'base_lower_recalc_rt', 'abrate'] 53 | CON_BONDS_URL = 'https://www.jisilu.cn/data/cbnew/cb_list/?page=%s' 54 | CON_BONDS_COLS = ['bond_id', 'bond_nm', 'stock_id', 'stock_nm', 'market', 'convert_price', 'convert_dt', 'issue_dt', 'maturity_dt', 55 | 'next_put_dt', 'put_price', 'put_count_days', 'put_total_days', 'redeem_price', 'redeem_price_ratio', 'redeem_count_days', 56 | 'redeem_total_days', 'orig_iss_amt', 'curr_iss_amt', 'rating_cd', 'issuer_rating_cd', 'guarantor', 'active_fl', 'ration_rt', 57 | 'pb', 'sprice', 'sincrease_rt', 'last_time', 'convert_value', 'premium_rt', 'year_left', 'ytm_rt', 'ytm_rt_tax', 'price', 58 | 'increase_rt', 'volume', 'force_redeem_price', 'put_convert_price', 'convert_amt_ratio', 'stock_cd', 'pre_bond_id'] 59 | CLOSED_STOCK_FUND_URL = 'https://www.jisilu.cn/data/cf/cf_list/?page=%s' 60 | CLOSED_STOCK_FUND_COLS = ['fund_id', 'fund_nm', 'issue_dt', 'duration', 'last_time', 'price', 'increase_rt', 'volume', 'net_value', 61 | 'nav_dt', 'realtime_estimate_value', 'discount_rt', 'left_year', 'annualize_dscnt_rt', 'quote_incr_rt', 62 | 'nav_incr_rt', 'spread', 'stock_ratio', 'report_dt', 'daily_nav_incr_rt', 'daily_spread'] 63 | CLOSED_BOND_FUND_URL = 'https://www.jisilu.cn/jisiludata/CloseBondFund.php?page=%s' 64 | CLOSED_BOND_FUND_COLS = ['fund_id', 'fund_nm', 'maturity_dt', 'left_year', 'est_val', 'discount_rt', 'annual_discount_rt', 65 | 'trade_price', 'increase_rt', 'volume', 'last_time', 'fund_nav', 'last_chg_dt', 'price_incr_rt', 66 | 'stock_ratio', 'bond_ratio', 'report_dt', 'is_outdate'] 67 | AH_RATIO_URL = 'https://www.jisilu.cn/data/ha/index2list/?page=%s' 68 | AH_RATIO_COLS = ['a_code', 'stock_name', 'a_price', 'a_increase_rt', 'h_code', 'h_price', 'h_increase_rt', 'last_time', 69 | 'rmb_price', 'hk_currency', 'ha_ratio', 'h_free_shares', 'a_free_shares'] 70 | DIVIDEND_RATE_URL = 'https://www.jisilu.cn/data/stock/dividend_rate_list/?page=%s' 71 | DIVIDEND_RATE_COLS = ['stock_id', 'stock_nm', 'dividend_rate', 'dividend_rate2', 'ipo_date', 'price', 'volume', 'increase_rt', 'pe', 'pb', 72 | 'total_value', 'eps_growth_ttm', 'roe', 'revenue_average', 'profit_average', 'roe_average', 73 | 'pb_temperature', 'pe_temperature', 'int_debt_rate', 'cashflow_average', 'dividend_rate_average', 74 | 'dividend_rate5', 'industry_nm', 'active_flg', 'last_time'] 75 | HISTORY_TICK_COLUMNS = ['time', 'price', 'change', 'volume', 'amount', 'type'] 76 | TODAY_TICK_COLUMNS = ['time', 'price', 'pchange', 'change', 'volume', 'amount', 'type'] 77 | DAY_TRADING_COLUMNS = ['code', 'symbol', 'name', 'changepercent', 78 | 'trade', 'open', 'high', 'low', 'settlement', 'volume', 'turnoverratio', 79 | 'amount', 'per', 'pb', 'mktcap', 'nmc'] 80 | REPORT_COLS = ['code', 'name', 'eps', 'eps_yoy', 'bvps', 'roe', 81 | 'epcf', 'net_profits', 'profits_yoy', 'distrib', 'report_date'] 82 | FORECAST_COLS = ['code', 'name', 'type', 'report_date', 'pre_eps', 'range'] 83 | PROFIT_COLS = ['code', 'name', 'roe', 'net_profit_ratio', 84 | 'gross_profit_rate', 'net_profits', 'eps', 'business_income', 'bips'] 85 | OPERATION_COLS = ['code', 'name', 'arturnover', 'arturndays', 'inventory_turnover', 86 | 'inventory_days', 'currentasset_turnover', 'currentasset_days'] 87 | GROWTH_COLS = ['code', 'name', 'mbrg', 'nprg', 'nav', 'targ', 'epsg', 'seg'] 88 | DEBTPAYING_COLS = ['code', 'name', 'currentratio', 89 | 'quickratio', 'cashratio', 'icratio', 'sheqratio', 'adratio'] 90 | CASHFLOW_COLS = ['code', 'name', 'cf_sales', 'rateofreturn', 91 | 'cf_nm', 'cf_liabilities', 'cashflowratio'] 92 | DAY_PRICE_COLUMNS = ['date', 'open', 'high', 'close', 'low', 'volume', 'price_change', 'p_change', 93 | 'ma5', 'ma10', 'ma20', 'v_ma5', 'v_ma10', 'v_ma20', 'turnover'] 94 | INX_DAY_PRICE_COLUMNS = ['date', 'open', 'high', 'close', 'low', 'volume', 'price_change', 'p_change', 95 | 'ma5', 'ma10', 'ma20', 'v_ma5', 'v_ma10', 'v_ma20'] 96 | LIVE_DATA_COLS = ['name', 'open', 'pre_close', 'price', 'high', 'low', 'bid', 'ask', 'volume', 'amount', 97 | 'b1_v', 'b1_p', 'b2_v', 'b2_p', 'b3_v', 'b3_p', 'b4_v', 'b4_p', 'b5_v', 'b5_p', 98 | 'a1_v', 'a1_p', 'a2_v', 'a2_p', 'a3_v', 'a3_p', 'a4_v', 'a4_p', 'a5_v', 'a5_p', 'date', 'time', 's'] 99 | FOR_CLASSIFY_B_COLS = ['code','name'] 100 | FOR_CLASSIFY_W_COLS = ['date','code', 'weight'] 101 | FOR_CLASSIFY_W5_COLS = ['date','code', 'name', 'weight'] 102 | THE_FIELDS = ['code','symbol','name','changepercent','trade','open','high','low','settlement','volume','turnoverratio'] 103 | KLINE_TT_COLS = ['date', 'open', 'close', 'high', 'low', 'volume'] 104 | HISTORY_TICKS_URL = 'http://market.finance.sina.com.cn/transHis.php?date=%s&symbol=%s&page=%s' 105 | TODAY_TICKS_PAGE_URL = 'http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_Transactions.getAllPageTime?date=%s&symbol=%s' 106 | TODAY_TICKS_URL = 'http://vip.stock.finance.sina.com.cn/quotes_service/view/vMS_tradedetail.php?symbol=%s&date=%s&page=%s' 107 | HISTORY_URL = 'http://web.ifzq.gtimg.cn/appstock/app/%skline/get?_var=kline_day%s¶m=%s,%s,%s,%s,640,%s&r=0.%s' 108 | HISTORY_MIN_URL = 'http://ifzq.gtimg.cn/appstock/app/kline/mkline?param=%s,m%s,,640&_var=m%s_today&r=0.%s' 109 | DAY_PRICE_URL = '%sapi.finance.%s/%s/?code=%s&type=last' 110 | LIVE_DATA_URL = 'http://hq.sinajs.cn/rn=%s&list=%s' 111 | DAY_PRICE_MIN_URL = '%sapi.finance.%s/akmin?scode=%s&type=%s' 112 | LATEST_URL = 'http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/Market_Center.getHQNodeData?num=80&sort=code&asc=0&node=hs_a&symbol=&_s_r_a=page&page=%s' 113 | REPORT_URL = 'http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/mainindex/index.phtml?s_i=&s_a=&s_c=&reportdate=%s&quarter=%s&p=%s&num=%s' 114 | FORECAST_URL = 'http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/performance/index.phtml?s_i=&s_a=&s_c=&s_type=&reportdate=%s&quarter=%s&p=%s&num=%s' 115 | PROFIT_URL = 'http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/profit/index.phtml?s_i=&s_a=&s_c=&reportdate=%s&quarter=%s&p=%s&num=%s' 116 | OPERATION_URL = 'http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/operation/index.phtml?s_i=&s_a=&s_c=&reportdate=%s&quarter=%s&p=%s&num=%s' 117 | GROWTH_URL = 'http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/grow/index.phtml?s_i=&s_a=&s_c=&reportdate=%s&quarter=%s&p=%s&num=%s' 118 | DEBTPAYING_URL = 'http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/debtpaying/index.phtml?s_i=&s_a=&s_c=&reportdate=%s&quarter=%s&p=%s&num=%s' 119 | CASHFLOW_URL = 'http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/cashflow/index.phtml?s_i=&s_a=&s_c=&reportdate=%s&quarter=%s&p=%s&num=%s' 120 | SHIBOR_TYPE ={'Shibor': 'Shibor数据', 'Quote': '报价数据', 'Tendency': 'Shibor均值数据', 121 | 'LPR': 'LPR数据', 'LPR_Tendency': 'LPR均值数据'} 122 | SHIBOR_DATA_URL = 'http://www.shibor.org/shibor/web/html/downLoad.html?nameNew=Historical_%s_Data_%s.xls&downLoadPath=data&nameOld=%s%s.xls&shiborSrc=http://www.shibor.org/shibor/' 123 | ALL_STOCK_PROFILES_URL = 'http://s.askci.com/stock/a/?reportTime=%s&pageNum=%s#QueryCondition' 124 | ALL_STOCK_PROFILES_COLS = ['code', 'name', 'city', 'staff', 'date', 'industry', 'pro_type', 'main'] 125 | SINA_CONCEPTS_INDEX_URL = 'http://money.finance.sina.com.cn/q/view/newFLJK.php?param=class' 126 | SINA_INDUSTRY_INDEX_URL = 'http://vip.stock.finance.sina.com.cn/q/view/%s' 127 | SINA_DATA_DETAIL_URL = 'http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/Market_Center.getHQNodeData?page=1&num=1000&sort=symbol&asc=1&node=%s&symbol=&_s_r_a=page' 128 | INDEX_C_COMM = 'sseportal/ps/zhs/hqjt/csi' 129 | HS300_CLASSIFY_URL_FTP = '%s%s/webdata/%s' 130 | HS300_CLASSIFY_URL_HTTP = '%s%s/%s/%s' 131 | HIST_FQ_URL = '%s%s/corp/go.php/vMS_FuQuanMarketHistory/stockid/%s.phtml?year=%s&jidu=%s' 132 | HIST_INDEX_URL = '%s%s/corp/go.php/vMS_MarketHistory/stockid/%s/type/S.phtml?year=%s&jidu=%s' 133 | HIST_FQ_FACTOR_URL = '%s%s/api/json.php/BasicStockSrv.getStockFuQuanData?symbol=%s&type=hfq' 134 | INDEX_URL = 'http://hq.sinajs.cn/rn=xppzh&list=sh000001,sh000002,sh000003,sh000008,sh000009,sh000010,sh000011,sh000012,sh000016,sh000017,sh000300,sh000905,sz399001,sz399002,sz399003,sz399004,sz399005,sz399006,sz399008,sz399100,sz399101,sz399106,sz399107,sz399108,sz399333,sz399606' 135 | SSEQ_CQ_REF_URL = '%s%s/assortment/stock/list/name' 136 | SINA_DD = 'http://vip.stock.finance.sina.com.cn/quotes_service/view/cn_bill_download.php?symbol=%s&num=60&page=1&sort=ticktime&asc=0&volume=%s&amount=0&type=0&day=%s' 137 | BOX = 'boxOffice' 138 | MOVIE_BOX = '%s%s/%s/GetHourBoxOffice?d=%s' 139 | BOXOFFICE_DAY = '%s%s/%s/GetDayBoxOffice?num=%s&d=%s' 140 | BOXOFFICE_MONTH = '%s%s/%s/getMonthBox?sdate=%s' 141 | BOXOFFICE_CBD = '%s%s/%s/getCBD?pIndex=%s&dt=%s' 142 | SHIBOR_COLS = ['date', 'ON', '1W', '2W', '1M', '3M', '6M', '9M', '1Y'] 143 | QUOTE_COLS = ['date', 'bank', 'ON', '1W', '2W', '1M', '3M', '6M', '9M', '1Y'] 144 | SHIBOR_MA_COLS = ['date', 'ON_5', 'ON_10', 'ON_20', '1W_5', '1W_10', '1W_20','2W_5', '2W_10', '2W_20', 145 | '1M_5', '1M_10', '1M_20', '3M_5', '3M_10', '3M_20', '6M_5', '6M_10', '6M_20', 146 | '9M_5', '9M_10', '9M_20','1Y_5', '1Y_10', '1Y_20'] 147 | LPR_COLS = ['date', '1Y'] 148 | LPR_MA_COLS = ['date', '1Y_5', '1Y_10', '1Y_20'] 149 | INDEX_HEADER = 'code,name,open,preclose,close,high,low,0,0,volume,amount,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,d,c,3\n' 150 | INDEX_COLS = ['code', 'name', 'change', 'open', 'preclose', 'close', 'high', 'low', 'volume', 'amount'] 151 | HIST_FQ_COLS = ['date', 'open', 'high', 'close', 'low', 'volume', 'amount', 'factor'] 152 | SINA_DD_COLS = ['code', 'name', 'time', 'price', 'volume', 'preprice', 'type'] 153 | GLOBAL_HQ_SYMBOL = 'sh000001,hkHSI,znb_UKX,znb_DAX,znb_INDEXCF,znb_CAC,znb_SMI,znb_FTSEMIB,znb_MADX,znb_OMX,znb_SPX,znb_HEX,znb_OSEAX,znb_ISEQ,znb_AEX,znb_ICEXI,znb_NKY,znb_TWSE,znb_FSSTI,znb_KOSPI,znb_FBMKLCI,znb_SET,znb_JCI,znb_PCOMP,znb_KSE100,znb_SENSEX,znb_VNINDEX,znb_CSEALL,znb_SASEIDX,znb_SPTSX,znb_MEXBOL,znb_IBOV,znb_MERVAL,znb_AS51,znb_NZSE50FG,znb_CASE,znb_JALSH,sz399001,znb_INDU,znb_CCMP' 154 | GLOBAL_HQ_COLS = ['symbol', 'name', 'price', 'chga', 'chgp', 'datetime'] 155 | HIST_FQ_FACTOR_COLS = ['code','value'] 156 | GETTING_TIPS = '[Progress:]' 157 | GETTING_FLAG = '#' 158 | DATA_INPUT_ERROR_MSG = 'date input error.' 159 | NETWORK_URL_ERROR_MSG = '获取失败,请检查网络和URL' 160 | NETWORK_ERR_MSG = '获取失败,请检查网络和URL;或者您访问的过于频繁,被服务器拦截,请稍后再试' 161 | DATE_CHK_MSG = '年度输入错误:请输入1989年以后的年份数字,格式:YYYY' 162 | DATE_CHK_Q_MSG = '季度输入错误:请输入1、2、3或4数字' 163 | TOP_PARAS_MSG = 'top有误,请输入整数或all.' 164 | LHB_MSG = '周期输入有误,请输入数字5、10、30或60' 165 | TOKEN_F_P = 'tk.csv' 166 | BOX_INPUT_ERR_MSG = '请输入YYYY-MM格式的年月数据' 167 | HOLIDAY_SERVE_ERR = '节假日查询服务出错' 168 | INDEX_SYMBOL = {"399990": "sz399990", "000006": "sh000006", "399998": "sz399998", 169 | "399436": "sz399436", "399678": "sz399678", "399804": "sz399804", 170 | "000104": "sh000104", "000070": "sh000070", "399613": "sz399613", 171 | "399690": "sz399690", "399928": "sz399928", "000928": "sh000928", 172 | "000986": "sh000986", "399806": "sz399806", "000032": "sh000032", 173 | "000005": "sh000005", "399381": "sz399381", "399908": "sz399908", 174 | "000908": "sh000908", "399691": "sz399691", "000139": "sh000139", 175 | "399427": "sz399427", "399248": "sz399248", "000832": "sh000832", 176 | "399901": "sz399901", "399413": "sz399413", "000901": "sh000901", 177 | "000078": "sh000078", "000944": "sh000944", "000025": "sh000025", 178 | "399944": "sz399944", "399307": "sz399307", "000052": "sh000052", 179 | "399680": "sz399680", "399232": "sz399232", "399993": "sz399993", 180 | "000102": "sh000102", "000950": "sh000950", "399950": "sz399950", 181 | "399244": "sz399244", "399925": "sz399925", "000925": "sh000925", 182 | "000003": "sh000003", "000805": "sh000805", "000133": "sh000133", 183 | "399677": "sz399677", "399319": "sz399319", "399397": "sz399397", 184 | "399983": "sz399983", "399654": "sz399654", "399440": "sz399440", 185 | "000043": "sh000043", "000012": "sh000012", "000833": "sh000833", 186 | "000145": "sh000145", "000053": "sh000053", "000013": "sh000013", 187 | "000022": "sh000022", "000094": "sh000094", "399299": "sz399299", 188 | "000101": "sh000101", "399817": "sz399817", "399481": "sz399481", 189 | "399434": "sz399434", "399301": "sz399301", "000029": "sh000029", 190 | "399812": "sz399812", "399441": "sz399441", "000098": "sh000098", 191 | "399557": "sz399557", "000068": "sh000068", "399298": "sz399298", 192 | "399302": "sz399302", "000961": "sh000961", "000959": "sh000959", 193 | "399961": "sz399961", "000126": "sh000126", "000036": "sh000036", 194 | "399305": "sz399305", "000116": "sh000116", "399359": "sz399359", 195 | "399810": "sz399810", "000062": "sh000062", "399618": "sz399618", 196 | "399435": "sz399435", "000149": "sh000149", "000819": "sh000819", 197 | "000020": "sh000020", "000061": "sh000061", "000016": "sh000016", 198 | "000028": "sh000028", "399809": "sz399809", "000999": "sh000999", 199 | "399238": "sz399238", "000100": "sh000100", "399979": "sz399979", 200 | "000979": "sh000979", "399685": "sz399685", "000152": "sh000152", 201 | "000153": "sh000153", "399318": "sz399318", "000853": "sh000853", 202 | "000040": "sh000040", "399693": "sz399693", "000076": "sh000076", 203 | "000017": "sh000017", "000134": "sh000134", "399989": "sz399989", 204 | "000042": "sh000042", "000066": "sh000066", "000008": "sh000008", 205 | "000002": "sh000002", "000001": "sh000001", "000011": "sh000011", 206 | "000031": "sh000031", "399403": "sz399403", "000951": "sh000951", 207 | "399951": "sz399951", "000092": "sh000092", "399234": "sz399234", 208 | "000823": "sh000823", "399986": "sz399986", "399647": "sz399647", 209 | "000050": "sh000050", "000073": "sh000073", "399357": "sz399357", 210 | "000940": "sh000940", "000107": "sh000107", "000048": "sh000048", 211 | "399411": "sz399411", "399366": "sz399366", "399373": "sz399373", 212 | "000015": "sh000015", "000021": "sh000021", "000151": "sh000151", 213 | "000851": "sh000851", "000058": "sh000058", "399404": "sz399404", 214 | "399102": "sz399102", "399431": "sz399431", "399971": "sz399971", 215 | "000125": "sh000125", "000069": "sh000069", "000063": "sh000063", 216 | "399395": "sz399395", "000038": "sh000038", "399240": "sz399240", 217 | "399903": "sz399903", "000989": "sh000989", "399321": "sz399321", 218 | "399675": "sz399675", "399235": "sz399235", "000057": "sh000057", 219 | "000056": "sh000056", "000903": "sh000903", "399310": "sz399310", 220 | "000004": "sh000004", "000019": "sh000019", "399919": "sz399919", 221 | "000974": "sh000974", "000919": "sh000919", "399635": "sz399635", 222 | "399663": "sz399663", "399106": "sz399106", "399107": "sz399107", 223 | "399555": "sz399555", "000090": "sh000090", "000155": "sh000155", 224 | "000060": "sh000060", "399636": "sz399636", "000816": "sh000816", 225 | "000010": "sh000010", "399671": "sz399671", "000035": "sh000035", 226 | "399352": "sz399352", "399683": "sz399683", "399554": "sz399554", 227 | "399409": "sz399409", "000018": "sh000018", "399101": "sz399101", 228 | "000992": "sh000992", "399416": "sz399416", "399918": "sz399918", 229 | "399379": "sz399379", "399674": "sz399674", "399239": "sz399239", 230 | "399384": "sz399384", "399367": "sz399367", "000918": "sh000918", 231 | "000914": "sh000914", "399914": "sz399914", "000054": "sh000054", 232 | "000806": "sh000806", "399619": "sz399619", "399015": "sz399015", 233 | "399393": "sz399393", "399313": "sz399313", "399231": "sz399231", 234 | "000846": "sh000846", "000854": "sh000854", "399010": "sz399010", 235 | "399666": "sz399666", "399387": "sz399387", "399399": "sz399399", 236 | "000026": "sh000026", "399934": "sz399934", "000150": "sh000150", 237 | "000934": "sh000934", "399317": "sz399317", "000138": "sh000138", 238 | "399371": "sz399371", "399394": "sz399394", "399659": "sz399659", 239 | "399665": "sz399665", "399931": "sz399931", "000161": "sh000161", 240 | "399380": "sz399380", "000931": "sh000931", "399704": "sz399704", 241 | "399616": "sz399616", "000817": "sh000817", "399303": "sz399303", 242 | "399629": "sz399629", "399624": "sz399624", "399009": "sz399009", 243 | "399233": "sz399233", "399103": "sz399103", "399242": "sz399242", 244 | "399627": "sz399627", "000971": "sh000971", "399679": "sz399679", 245 | "399912": "sz399912", "000982": "sh000982", "399668": "sz399668", 246 | "000096": "sh000096", "399982": "sz399982", "000849": "sh000849", 247 | "000148": "sh000148", "399364": "sz399364", "000912": "sh000912", 248 | "000129": "sh000129", "000055": "sh000055", "000047": "sh000047", "399355": "sz399355", "399622": "sz399622", "000033": "sh000033", "399640": "sz399640", "000852": "sh000852", "399966": "sz399966", "399615": "sz399615", "399802": "sz399802", "399602": "sz399602", "000105": "sh000105", "399660": "sz399660", "399672": "sz399672", 249 | "399913": "sz399913", "399420": "sz399420", "000159": "sh000159", "399314": "sz399314", "399652": "sz399652", 250 | "399369": "sz399369", "000913": "sh000913", "000065": "sh000065", 251 | "000808": "sh000808", "399386": "sz399386", "399100": "sz399100", 252 | "000997": "sh000997", "000990": "sh000990", "000093": "sh000093", "399637": "sz399637", "399439": "sz399439", "399306": "sz399306", "000855": "sh000855", "000123": "sh000123", "399623": "sz399623", 253 | "399312": "sz399312", "399249": "sz399249", "399311": "sz399311", "399975": "sz399975", "399356": "sz399356", 254 | "399400": "sz399400", "399676": "sz399676", "000136": "sh000136", "399361": "sz399361", "399974": "sz399974", "399995": "sz399995", "399316": "sz399316", "399701": "sz399701", "000300": "sh000300", "000030": "sh000030", "000976": "sh000976", "399686": "sz399686", "399108": "sz399108", "399374": "sz399374", 255 | "000906": "sh000906", "399707": "sz399707", "000064": "sh000064", "399633": "sz399633", "399300": "sz399300", "399628": "sz399628", "399398": "sz399398", "000034": "sh000034", 256 | "399644": "sz399644", "399905": "sz399905", "399626": "sz399626", 257 | "399625": "sz399625", "000978": "sh000978", "399664": "sz399664", "399682": "sz399682", "399322": "sz399322", "000158": "sh000158", "000842": "sh000842", "399550": "sz399550", "399423": "sz399423", "399978": "sz399978", "399996": "sz399996", "000905": "sh000905", 258 | "000007": "sh000007", "000827": "sh000827", "399655": "sz399655", "399401": "sz399401", "399650": "sz399650", "000963": "sh000963", "399661": "sz399661", "399922": "sz399922", "000091": "sh000091", "399375": "sz399375", "000922": "sh000922", "399702": "sz399702", "399963": "sz399963", "399011": "sz399011", "399012": "sz399012", 259 | "399383": "sz399383", "399657": "sz399657", "399910": "sz399910", "399351": "sz399351", "000910": "sh000910", "000051": "sh000051", "399376": "sz399376", "399639": "sz399639", "000821": "sh000821", "399360": "sz399360", "399604": "sz399604", "399315": "sz399315", "399658": "sz399658", "000135": "sh000135", 260 | "000059": "sh000059", "399006": "sz399006", 261 | "399320": "sz399320", "000991": "sh000991", "399606": "sz399606", 262 | "399428": "sz399428", "399406": "sz399406", "399630": "sz399630", "000802": "sh000802", "399803": "sz399803", "000071": "sh000071", "399358": "sz399358", 263 | "399013": "sz399013", "399385": "sz399385", "399008": "sz399008", "399649": "sz399649", 264 | "399673": "sz399673", "399418": "sz399418", "399370": "sz399370", "000814": "sh000814", 265 | "399002": "sz399002", "399814": "sz399814", "399641": "sz399641", "399001": "sz399001", 266 | "399662": "sz399662", "399706": "sz399706", "399932": "sz399932", "000095": "sh000095", "000932": "sh000932", "399965": "sz399965", "399363": "sz399363", "399354": "sz399354", "399638": "sz399638", "399648": "sz399648", "399608": "sz399608", "000939": "sh000939", "399939": "sz399939", "399365": "sz399365", "399382": "sz399382", "399631": "sz399631", "399612": "sz399612", "399611": "sz399611", "399645": "sz399645", 267 | "399324": "sz399324", "399552": "sz399552", "000858": "sh000858", "000045": "sh000045", 268 | "000121": "sh000121", "399703": "sz399703", "399003": "sz399003", 269 | "399348": "sz399348", "399389": "sz399389", "399007": "sz399007", "399391": "sz399391", "000973": "sh000973", 270 | "000984": "sh000984", "000969": "sh000969", "000952": "sh000952", "399332": "sz399332", "399952": "sz399952", "399553": "sz399553", "000856": "sh000856", 271 | "399969": "sz399969", "399643": "sz399643", "399402": "sz399402", "399372": "sz399372", "399632": "sz399632", "399344": "sz399344", "399808": "sz399808", "399620": "sz399620", "000103": "sh000103", "399911": "sz399911", "000993": "sh000993", "000983": "sh000983", "399687": "sz399687", "399933": "sz399933", "000933": "sh000933", "399437": "sz399437", "399433": "sz399433", "000046": "sh000046", "000911": "sh000911", "000114": "sh000114", "000049": "sh000049", "399392": "sz399392", "399653": "sz399653", "000975": "sh000975", "000044": "sh000044", "399378": "sz399378", "000828": "sh000828", "399634": "sz399634", 272 | "399005": "sz399005", "000162": "sh000162", "399333": "sz399333", "000122": "sh000122", "399646": "sz399646", "000077": "sh000077", "000074": "sh000074", "399656": "sz399656", "399396": "sz399396", "399415": "sz399415", "399408": "sz399408", "000115": "sh000115", "000987": "sh000987", "399362": "sz399362", "000841": "sh000841", "000141": "sh000141", "000120": "sh000120", "399992": "sz399992", "000807": "sh000807", "399350": "sz399350", "000009": "sh000009", "000998": "sh000998", "399390": "sz399390", "399405": "sz399405", "000099": "sh000099", "399337": "sz399337", "000142": "sh000142", "399419": "sz399419", "399407": "sz399407", "000909": "sh000909", "000119": "sh000119", "399909": "sz399909", "399805": "sz399805", "000996": "sh000996", "000847": "sh000847", "000130": "sh000130", "399377": "sz399377", "399388": "sz399388", "399610": "sz399610", "000958": "sh000958", 273 | "399958": "sz399958", "000075": "sh000075", "399346": "sz399346", "000147": "sh000147", "000132": "sh000132", "000108": "sh000108", "399642": "sz399642", "000977": "sh000977", "399689": "sz399689", "399335": "sz399335", "399977": "sz399977", "399972": "sz399972", "399970": "sz399970", "399004": "sz399004", "399341": "sz399341", "399330": "sz399330", "399917": "sz399917", "000160": "sh000160", "399432": "sz399432", "399429": "sz399429", "000917": "sh000917", 274 | "000128": "sh000128", "000067": "sh000067", "000079": "sh000079", "399236": "sz399236", "399994": "sz399994", "399237": "sz399237", "000966": "sh000966", "000957": "sh000957", "399328": "sz399328", 275 | "399353": "sz399353", "399957": "sz399957", "399412": "sz399412", "000904": "sh000904", "399904": "sz399904", "399410": "sz399410", "000027": "sh000027", "399667": "sz399667", "000857": "sh000857", 276 | "000131": "sh000131", "000964": "sh000964", "399339": "sz399339", "399964": "sz399964", "399991": "sz399991", "399417": "sz399417", "000146": "sh000146", "399551": "sz399551", "000137": "sh000137", "000118": "sh000118", "399976": "sz399976", "000109": "sh000109", "399681": "sz399681", "399438": "sz399438", "000117": "sh000117", "399614": "sz399614", "399669": "sz399669", "000111": "sh000111", "399670": "sz399670", "000097": "sh000097", "000106": "sh000106", "000039": "sh000039", "399935": "sz399935", "000935": "sh000935", "399813": "sz399813", "000037": "sh000037", "399811": "sz399811", "399705": "sz399705", "399556": "sz399556", "000113": "sh000113", "000072": "sh000072", "399651": "sz399651", "399617": "sz399617", "399684": "sz399684", "000041": "sh000041", "399807": "sz399807", "399959": "sz399959", "399967": "sz399967", "399326": "sz399326", "399688": "sz399688", "399368": "sz399368", "399241": "sz399241", "399696": "sz399696", "000850": "sh000850", "000110": "sh000110", "399621": "sz399621", "399243": "sz399243", 277 | "399973": "sz399973", "399987": "sz399987", "000112": "sh000112", "399997": "sz399997", 278 | "hkHSI":"hkHSI"} 279 | DP_163_URL = 'http://quotes.money.163.com/data/caibao/fpyg.html?reportdate=%s&sort=declaredate&order=desc&page=%s' 280 | DP_163_COLS = ['code', 'name', 'year', 'plan', 'report_date'] 281 | RL_URL = 'http://datainterface.eastmoney.com/EM_DataCenter/JS.aspx?type=FD&sty=BST&st=3&sr=true&fd=%s&stat=%s' 282 | RL_COLS = ['code', 'name', 'date', 'count', 'ratio'] 283 | QUARTS_DIC = {'1':('%s-12-31', '%s-03-31'), '2':('%s-03-31', '%s-06-30'), '3':('%s-06-30', '%s-09-30'), '4':('%s-9-30', '%s-12-31')} 284 | FUND_HOLDS_URL = 'http://quotes.money.163.com/hs/marketdata/service/jjcgph.php?host=/hs/marketdata/service/jjcgph.php&page=%s&query=start:%s;end:%s&order=desc&count=60&type=query&req=%s' 285 | FUND_HOLDS_COLS = ['count', 'clast', 'date', 'ratio', 'amount', 'nums','nlast', 'name', 'code'] 286 | NEW_STOCKS_URL = 'http://vip.stock.finance.sina.com.cn/corp/view/vRPD_NewStockIssue.php?page=%s&cngem=0&orderBy=NetDate&orderType=desc' 287 | NEW_STOCKS_COLS = ['code', 'xcode', 'name', 'ipo_date', 'issue_date', 'amount', 'markets', 'price', 'pe', 'limit', 'funds', 'ballot'] 288 | 289 | MAR_URL = "http://dcfm.eastmoney.com//EM_MutiSvcExpandInterface/api/js/get?token=70f12f2f4f091e459a279469fe49eca5&st=tdate&sr=-1&p=%s&ps=50&js=var UYirVWSV={pages:(tp),data: (x)}&type=RZRQ_HSTOTAL_NJ&filter=(market='%s')&mk_time=1&rt=%s" 290 | MAR_COLS = ['tdate', 'close', 'zdf', 'rzye', 'rzyezb', 'rzmre', 'rzche', 'rzjmre', 'rqye', 'rqyl', 'rqmcl', 'rqchl', 'rqjmcl', 'rzrqye', 'rzrqyecz'] 291 | MAR_BOTH_DETAIL = "http://dcfm.eastmoney.com/em_mutisvcexpandinterface/api/js/get?type=RZRQ_DETAIL_NJ&token=70f12f2f4f091e459a279469fe49eca5&filter=(tdate='%sT00:00:00')&st=rzjmre&sr=-1&p=%s&ps=50&js=var nZxTWeXw={pages:(tp),data:(x)}&type=RZRQ_DETAIL_NJ&time=1&rt=%s" 292 | MAR_DET_All_COLS = ['scode', 'sname', 'rzye', 'rzyezb', 'rzmre', 'rzche', 'rzjmre', 'rqye', 'rqyl', 'rqmcl', 'rqchl', 'rqjmcl', 'rzrqye', 'rzrqyecz'] 293 | MAR_TOTAL_URL = 'http://dcfm.eastmoney.com//EM_MutiSvcExpandInterface/api/js/get?token=70f12f2f4f091e459a279469fe49eca5&st=tdate&sr=-1&p=%s&ps=50&js=var BKNgVufZ={pages:(tp),data:(x)}&type=RZRQ_LSTOTAL_NJ&mk_time=1&rt=%s' 294 | MAR_TOTAL_COLS = ['tdate', 'close', 'zdf', 'rzye', 'rzyezb', 'rzmre', 'rzche', 'rzjmre', 'rqye', 'rqyl', 'rqmcl', 'rqchl', 'rqjmcl', 'rzrqye', 'rzrqyecz'] 295 | 296 | MACRO_URL = 'http://money.finance.sina.com.cn/mac/api/jsonp.php/SINAREMOTECALLCALLBACK%s/MacPage_Service.get_pagedata?cate=%s&event=%s&from=0&num=%s&condition=&_=%s' 297 | GDP_YEAR_COLS = ['year','gdp','pc_gdp','gnp','pi','si','industry','cons_industry','ti','trans_industry','lbdy'] 298 | GDP_QUARTER_COLS = ['quarter','gdp','gdp_yoy','pi','pi_yoy','si','si_yoy','ti','ti_yoy'] 299 | GDP_FOR_COLS = ['year','cons_to','cons_rate','asset_to','asset_rate','goods_to','goods_rate'] 300 | GDP_PULL_COLS = ['year','gdp_yoy','pi','si','industry','ti'] 301 | GDP_CONTRIB_COLS = ['year','gdp_yoy','pi','si','industry','ti'] 302 | CPI_COLS = ['month','cpi'] 303 | PPI_COLS = ['month','ppiip','ppi','qm','rmi','pi','cg','food','clothing','roeu','dcg'] 304 | DEPOSIT_COLS = ['date','deposit_type','rate'] 305 | LOAN_COLS = ['date','loan_type','rate'] 306 | RRR_COLS = ['date','before','now','changed'] 307 | MONEY_SUPPLY_COLS = ['month','m2','m2_yoy','m1','m1_yoy','m0','m0_yoy','cd','cd_yoy','qm','qm_yoy','ftd','ftd_yoy','sd','sd_yoy','rests','rests_yoy'] 308 | MONEY_SUPPLY_BLA_COLS = ['year','m2','m1','m0','cd','qm','ftd','sd','rests'] 309 | 310 | TERMINATED_URL = '%s%s/%s?jsonCallBack=jsonpCallback%s&isPagination=true&sqlId=COMMON_SSE_ZQPZ_GPLB_MCJS_ZZSSGGJBXX_L&pageHelp.pageSize=50&_=%s' 311 | TERMINATED_T_COLS = ['COMPANY_CODE', 'COMPANY_ABBR', 'LISTING_DATE', 'CHANGE_DATE'] 312 | TERMINATED_COLS = ['code', 'name', 'oDate', 'tDate'] 313 | 314 | LHB_URL = 'http://data.eastmoney.com/DataCenter_V3/stock2016/TradeDetail/pagesize=200,page=1,sortRule=-1,sortType=,startDate=%s,endDate=%s,gpfw=0,js=vardata_tab_1.html' 315 | LHB_TMP_COLS = ['SCode', 'SName', 'Chgradio', 'ZeMoney', 'Bmoney', 'Smoney', 'Ctypedes', 'Turnover', 'JD'] 316 | LHB_COLS = ['code', 'name', 'pchange', 'amount', 'buy', 'sell', 'reason', 'Turnover', 'unscramble'] 317 | LHB_SINA_URL = 'http://vip.stock.finance.sina.com.cn/q/go.php/vLHBData/kind/%s/index.phtml?last=%s&p=%s' 318 | LHB_KINDS = ['ggtj', 'yytj', 'jgzz', 'jgmx'] 319 | LHB_GGTJ_COLS = ['code', 'name', 'count', 'bamount', 'samount', 'net', 'bcount', 'scount'] 320 | LHB_YYTJ_COLS = ['broker', 'count', 'bamount', 'bcount', 'samount', 'scount', 'top3'] 321 | LHB_JGZZ_COLS = ['code', 'name', 'bamount', 'bcount', 'samount', 'scount', 'net'] 322 | LHB_JGMX_COLS = ['code', 'name', 'date', 'bamount', 'samount', 'type'] 323 | 324 | XQ_TOPICS_URL = 'https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=%d&count=15&category=-1' 325 | XQ_TOPICS_COLS = ['title', 'description', 'target', 'screen_name', 'followers_count', 'source', 'reply_count', 'retweet_count', 'view_count', 'created_at', 'column'] 326 | XQ_NEWS_URL = 'https://xueqiu.com/v4/statuses/user_timeline.json?page=%d&user_id=5124430882' 327 | XQ_NEWS_COLS = ['description', 'text', 'source', 'created_at', 'retweet_count', 'reply_count', 'fav_count', 'view_count', 328 | 'reward_count', 'reward_amount', 'reward_user_count', 'talk_count', 'like_count', 'type', 'target', 'timeBefore'] 329 | XQ_HOT_COMMENTS_URL = 'https://xueqiu.com/statuses/search.json?count=10&comment=0&symbol=%s&hl=0&source=all&sort=alpha&page=%d&q=' 330 | XQ_HOT_COMMENTS_COLS = ['user_id', 'text', 'created_at', 'retweet_count', 'reply_count', 'fav_count', 'description', 331 | 'screen_name', 'friends_count', 'followers_count', 'target', 'view_count', 'reward_count', 'reward_user_count', 332 | 'like_count', 'source'] 333 | 334 | -------------------------------------------------------------------------------- /GuGu/lowriskintarb.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | 低风险及套利类 4 | Created on 2019/01/30 5 | @author: TabQ 6 | @group : GuGu 7 | @contact: 16621596@qq.com 8 | """ 9 | 10 | import re 11 | import json 12 | import pandas as pd 13 | import numpy as np 14 | from base import Base, cf 15 | 16 | class LowRiskIntArb(Base): 17 | def ratingFundA(self): 18 | """ 19 | 分级基金A及其相关数据 20 | return 21 | ------ 22 | DataFrame or List: [{'funda_id':, 'funda_name':, ...}, ...] 23 | funda_id: 分级A代码 24 | funda_name: 分级A名称 25 | funda_current_price: 现价 26 | funda_increase_rt: 涨幅(%) 27 | funda_volume: 成交额 28 | funda_value: 净值 29 | funda_discount_rt: 折价率(%) 30 | funda_coupon: 本期利率 31 | funda_coupon_next: 下期利率 32 | funda_profit_rt_next: 修正收益率(%) 33 | funda_index_id: 参考指数代码 34 | funda_index_name: 参考指数名称 35 | funda_index_increase_rt: 指数涨幅(%) 36 | funda_lower_recalc_rt: 下折母基需跌(%) 37 | lower_recalc_profit_rt: 理论下折收益(%) 38 | fundb_upper_recalc_rt: 上折母基需涨(%) 39 | funda_base_est_dis_rt_t1: T-1溢价率(%) 40 | funda_base_est_dis_rt_t2: T-2溢价率(%) 41 | funda_amount: A份额(万份) 42 | funda_amount_increase: A新增(万份) 43 | abrate: A:B 44 | next_recalc_dt: 下次定折日期 45 | """ 46 | self._data = pd.DataFrame() 47 | 48 | self._data = self.__parsePage(cf.RATING_FUNDA_URL, cf.RATING_FUNDA_COLS) 49 | self._data[self._data=="-"] = np.NaN 50 | self._data['next_recalc_dt'] = self._data['next_recalc_dt'].map(self.__nextRecalcDt) 51 | for col in ['funda_current_price', 'funda_increase_rt', 'funda_volume', 'funda_value', 52 | 'funda_discount_rt', 'funda_coupon', 'funda_coupon_next', 'funda_profit_rt_next', 53 | 'funda_index_increase_rt', 'funda_lower_recalc_rt', 'lower_recalc_profit_rt', 54 | 'fundb_upper_recalc_rt', 'funda_base_est_dis_rt_t1', 'funda_base_est_dis_rt_t2', 55 | 'funda_amount_increase']: 56 | self._data[col] = self._data[col].astype(float) 57 | 58 | return self._result() 59 | 60 | 61 | def __nextRecalcDt(self, x): 62 | pattern = re.compile(r'(\d{4}-\d{2}-\d{2})') 63 | 64 | return re.sub(pattern, r'\1', x) 65 | 66 | 67 | def ratingFundB(self): 68 | """ 69 | 分级基金A及其相关数据 70 | return 71 | ------ 72 | DataFrame or List: [{'fundb_id':, 'fundb_name':, ...}, ...] 73 | fundb_id: 分级B代码 74 | fundb_name: 分级B名称 75 | fundb_base_fund_id: 对应母基代码 76 | funda_id: 分级A代码 77 | funda_name: 分级A名称 78 | coupon: 优惠 79 | manage_fee: 管理费 80 | funda_current_price: 分级A现价 81 | funda_upper_price: 分级A最高价 82 | funda_lower_price: 分级A最低价 83 | funda_increase_rt: 分级A增长率(%) 84 | fundb_current_price: 分级B现价 85 | fundb_upper_price: 分级B最高价 86 | fundb_lower_price: 分级B最低价 87 | fundb_increase_rt: 分级B增长率(%) 88 | fundb_volume: 分级B成交额(万元) 89 | fundb_value: 分级B净值 90 | fundm_value: 母基净值 91 | fundb_discount_rt: 分级B溢价率(%) 92 | fundb_price_leverage_rt: 分级B价格杠杆 93 | fundb_net_leverage_rt: 分级B净值杠杆 94 | fundb_capital_rasising_rt: 分级B融资成本(%) 95 | fundb_lower_recalc_rt: 下折母基需跌(%) 96 | fundb_upper_recalc_rt: 上折母基需涨(%) 97 | b_est_val: 估值 98 | fundb_index_id: 参考指数代码 99 | fundb_index_name: 参考指数名称 100 | fundb_index_increase_rt: 参考指数涨幅(%) 101 | funda_ratio: 分级A占比 102 | fundb_ratio: 分级B占比 103 | fundb_base_price: 分级B基础价格 104 | fundB_amount: B份额(万份) 105 | fundB_amount_increase: B新增份额(万份) 106 | abrate: A:B 107 | """ 108 | self._data = pd.DataFrame() 109 | 110 | self._data = self.__parsePage(cf.RATING_FUNDB_URL, cf.RATING_FUNDB_COLS) 111 | self._data[self._data=="-"] = np.NaN 112 | for col in ['coupon', 'manage_fee', 'funda_current_price', 'funda_upper_price', 'funda_lower_price', 113 | 'funda_increase_rt', 'fundb_current_price', 'fundb_upper_price', 'fundb_lower_price', 114 | 'fundb_increase_rt', 'fundb_volume', 'fundb_value', 'fundm_value', 'fundb_discount_rt', 115 | 'fundb_price_leverage_rt', 'fundb_net_leverage_rt', 'fundb_capital_rasising_rt', 116 | 'fundb_lower_recalc_rt', 'fundb_upper_recalc_rt', 'b_est_val', 'fundb_index_increase_rt', 117 | 'fundb_base_price', 'fundB_amount', 'fundB_amount_increase']: 118 | self._data[col] = self._data[col].astype(float) 119 | 120 | return self._result() 121 | 122 | 123 | def ratingFundM(self): 124 | """ 125 | 分级基金A及其相关数据 126 | return 127 | ------ 128 | DataFrame or List: [{}] 129 | base_fund_id: 母基代码 130 | base_fund_nm: 母基名称 131 | market: 所属市场 132 | issue_dt: 创立日期 133 | manage_fee: 管理费 134 | index_id: 跟踪指数代码 135 | index_nm: 跟踪指数名称 136 | lower_recalc_price: 下折 137 | a_ratio: A占比 138 | b_ratio: B占比 139 | next_recalc_dt: 下次定折 140 | fundA_id: A基代码 141 | fundA_nm: A基名称 142 | coupon: 本期利率 143 | coupon_next: 下期利率 144 | fundB_id: B基代码 145 | fundB_nm: B基名称 146 | price: 母基净值 147 | base_lower_recalc_rt: 下折需跌(%) 148 | abrate: A:B 149 | """ 150 | self._data = pd.DataFrame() 151 | 152 | self._data = self.__parsePage(cf.RATING_FUNDM_URL, cf.RATING_FUNDM_COLS) 153 | self._data[self._data=="-"] = np.NaN 154 | for col in ['manage_fee', 'lower_recalc_price', 'a_ratio', 'b_ratio', 'coupon', 'coupon_next', 155 | 'price', 'base_lower_recalc_rt']: 156 | self._data[col] = self._data[col].astype(float) 157 | 158 | return self._result() 159 | 160 | 161 | def conBonds(self): 162 | """ 163 | 可转债及其相关数据 164 | return 165 | ------ 166 | DataFrame or List: [{'bond_id':, 'bond_nm':, ...}, ...] 167 | bond_id: 代码 168 | bond_nm: 名称 169 | stock_id: 正股全代码 170 | stock_nm: 正股名称 171 | market: 所属市场 172 | convert_price: 转股价 173 | convert_dt: 转股起始日 174 | issue_dt: 创立日期 175 | maturity_dt: 到期日 176 | next_put_dt: 回售起始日 177 | put_price: 回售价 178 | put_count_days: 回售计算天数 179 | put_total_days: 回售总天数 180 | redeem_price: 赎回价 181 | redeem_price_ratio: 赎回率 182 | redeem_count_days: 赎回计算天数 183 | redeem_total_days: 赎回总天数 184 | orig_iss_amt: 发行规模(亿) 185 | curr_iss_amt: 剩余规模(亿) 186 | rating_cd: 债券评级 187 | issuer_rating_cd: 主体评级 188 | guarantor: 担保 189 | active_fl: 活跃标志 190 | ration_rt: 股东配售率(%) 191 | pb: 市净率 192 | sprice: 正股价 193 | sincrease_rt: 正股涨跌(%) 194 | last_time: 最后时间 195 | convert_value: 转股价值 196 | premium_rt: 溢价率(%) 197 | year_left: 剩余年限 198 | ytm_rt: 到期税前收益(%) 199 | ytm_rt_tax: 到期税后收益(%) 200 | price: 现价 201 | increase_rt: 涨跌幅(%) 202 | volume: 成交额(万元) 203 | force_redeem_price: 强赎触发价 204 | put_convert_price: 回售触发价 205 | convert_amt_ratio: 转债占比 206 | stock_cd: 正股简码 207 | pre_bond_id: 转债全码 208 | """ 209 | self._data = pd.DataFrame() 210 | 211 | self._data = self.__parsePage(cf.CON_BONDS_URL, cf.CON_BONDS_COLS) 212 | self._data[self._data=="-"] = np.NaN 213 | for col in ['convert_price', 'put_price', 'redeem_price', 'redeem_price_ratio', 'orig_iss_amt', 214 | 'curr_iss_amt', 'ration_rt', 'pb', 'sprice', 'sincrease_rt', 'convert_value', 'premium_rt', 215 | 'year_left', 'ytm_rt', 'ytm_rt_tax', 'price', 'increase_rt', 'volume', 'force_redeem_price', 216 | 'put_convert_price', 'convert_amt_ratio']: 217 | self._data[col] = self._data[col].astype(float) 218 | 219 | return self._result() 220 | 221 | 222 | def closedStockFund(self): 223 | """ 224 | 封闭股基及其相关数据 225 | return 226 | ------ 227 | DataFrame or List: [{'fund_id':, 'fund_nm':, ...}, ...] 228 | fund_id: 代码 229 | fund_nm: 名称 230 | issue_dt: 创立日期 231 | duration: 持续时间 232 | last_time: 最后时间 233 | price: 现价 234 | increase_rt: 涨幅(%) 235 | volume: 成交金额(万) 236 | net_value: 净值 237 | nav_dt: 净值日期 238 | realtime_estimate_value: 最近估值 239 | discount_rt: 折价率(%) 240 | left_year: 剩余年限 241 | annualize_dscnt_rt: 年化折价率(%) 242 | quote_incr_rt: 周价增(%) 243 | nav_incr_rt: 周净增(%) 244 | spread: 净价差(%) 245 | stock_ratio: 股票占比(%) 246 | report_dt: 报告日期 247 | daily_nav_incr_rt: 当日净增(%) 248 | daily_spread: 日净价差(%) 249 | """ 250 | self._data = pd.DataFrame() 251 | 252 | self._data = self.__parsePage(cf.CLOSED_STOCK_FUND_URL, cf.CLOSED_STOCK_FUND_COLS) 253 | self._data[self._data=="-"] = np.NaN 254 | for col in ['price', 'increase_rt', 'volume', 'net_value', 'realtime_estimate_value', 'discount_rt', 255 | 'left_year', 'annualize_dscnt_rt', 'quote_incr_rt', 'nav_incr_rt', 'spread', 'stock_ratio', 256 | 'daily_nav_incr_rt', 'daily_spread']: 257 | self._data[col] = self._data[col].astype(float) 258 | 259 | return self._result() 260 | 261 | 262 | def closedBondFund(self): 263 | """ 264 | 封闭债基及其相关数据 265 | return 266 | ------ 267 | DataFrame or List: [{'fund_id':, 'fund_nm':, ...}, ...] 268 | fund_id: 代码 269 | fund_nm: 名称 270 | maturity_dt: 到期日期 271 | left_year: 剩余年限 272 | est_val: 最近估值 273 | discount_rt: 折价率(%) 274 | annual_discount_rt: 年化折价率(%) 275 | trade_price: 现价 276 | increase_rt: 涨幅(%) 277 | volume: 成交金额 278 | last_time: 最后时间 279 | fund_nav: 最近净值 280 | last_chg_dt: 净值日期 281 | price_incr_rt: 净值日增(%) 282 | stock_ratio: 股票比例 283 | bond_ratio: 债券比例 284 | report_dt: 报告日期 285 | is_outdate: 是否超期 286 | """ 287 | self._data = pd.DataFrame() 288 | 289 | self._data = self.__parsePage(cf.CLOSED_BOND_FUND_URL, cf.CLOSED_BOND_FUND_COLS) 290 | self._data[self._data=="-"] = np.NaN 291 | for col in ['left_year', 'est_val', 'discount_rt', 'annual_discount_rt', 'trade_price', 292 | 'increase_rt', 'volume', 'fund_nav', 'price_incr_rt', 'stock_ratio', 'bond_ratio']: 293 | self._data[col] = self._data[col].astype(float) 294 | 295 | return self._result() 296 | 297 | 298 | def AHRatio(self): 299 | """ 300 | A/H比价 301 | return 302 | ------ 303 | DataFrame or List: [{'a_code':, 'stock_name':, ...}, ...] 304 | a_code: A股代码 305 | stock_name: 股票名称 306 | a_price: A股价格 307 | a_increase_rt: A股涨跌幅(%) 308 | h_code: H股代码 309 | h_price: H股价格(港元) 310 | h_increase_rt: H股涨跌幅(%) 311 | last_time: 最后时间 312 | rmb_price: H股价格(人民币) 313 | hk_currency: 港元汇率 314 | ha_ratio: 比价(H/A) 315 | h_free_shares: H股自由流通市值(亿港元) 316 | a_free_shares: A股自由流通市值(亿元) 317 | """ 318 | self._data = pd.DataFrame() 319 | 320 | self._data = self.__parsePage(cf.AH_RATIO_URL, cf.AH_RATIO_COLS) 321 | self._data[self._data=="-"] = np.NaN 322 | for col in ['a_price', 'a_increase_rt', 'h_price', 'h_increase_rt', 'rmb_price', 323 | 'hk_currency', 'ha_ratio', 'h_free_shares', 'a_free_shares']: 324 | self._data[col] = self._data[col].astype(float) 325 | 326 | return self._result() 327 | 328 | 329 | def dividendRate(self): 330 | """ 331 | A股股息率 332 | return 333 | ------ 334 | DataFrame or List: [{'stock_id':, 'stock_nm':, ...}, ...] 335 | stock_id: 股票代码 336 | stock_nm: 股票名称 337 | dividend_rate: 股息率(TTM) 338 | dividend_rate2: 静态股息率 339 | ipo_date: ipo日期 340 | price: 价格 341 | volume: 成交额(万元) 342 | increase_rt: 涨幅(%) 343 | pe: 市盈率(TTM) 344 | pb: 市净率 345 | total_value: 市值(亿元) 346 | eps_growth_ttm: 净利同比增长(%) 347 | roe: 最新年报ROE(%) 348 | revenue_average: 5年营收复合增长(%) 349 | profit_average: 5年利润复合增长(%) 350 | roe_average: 5年平均ROE(%) 351 | pb_temperature: PB温度(℃) 352 | pe_temperature: PE温度(℃) 353 | int_debt_rate: 有息负债率(%) 354 | cashflow_average: 5年现金流复合增长(%) 355 | dividend_rate_average: 5年分红率复合增长(%) 356 | dividend_rate5: 5年平均股息率(%) 357 | industry_nm: 所属行业 358 | active_flg: 活跃标志 359 | last_time: 最后时间 360 | """ 361 | self._data = pd.DataFrame() 362 | 363 | self._data = self.__parsePage(cf.DIVIDEND_RATE_URL, cf.DIVIDEND_RATE_COLS) 364 | self._data[self._data=="-"] = np.NaN 365 | for col in ['dividend_rate', 'dividend_rate2', 'price', 'volume', 'increase_rt', 'pe', 'pb', 'total_value', 366 | 'eps_growth_ttm', 'roe', 'revenue_average', 'profit_average', 'roe_average', 'pb_temperature', 367 | 'pe_temperature', 'int_debt_rate', 'cashflow_average', 'dividend_rate_average', 'dividend_rate5']: 368 | self._data[col] = self._data[col].astype(float) 369 | 370 | return self._result() 371 | 372 | 373 | def stockLof(self): 374 | """ 375 | 股票LOF基金及基相关数据 376 | return 377 | ------ 378 | DataFrame or List: [{'fund_id':, 'fund_nm':, ...}, ...] 379 | fund_id: 基金代码 380 | fund_nm: 基金名称 381 | price: 现价 382 | increase_rt: 涨幅(%) 383 | volume: 成交(万元) 384 | amount: 场内份额(万份) 385 | fund_nav: 基金净值 386 | nav_dt: 净值日期 387 | estimate_value: 实时估值 388 | discount_rt: 溢价率(%) 389 | stock_ratio: 股票占比(%) 390 | stock_increase_rt: 重仓涨幅(%) 391 | apply_fee: 申购费(%) 392 | redeem_fee: 赎回费(%) 393 | apply_redeem_status: 申赎状态 394 | """ 395 | self._data = pd.DataFrame() 396 | 397 | self._data = self.__parsePage(cf.STOCK_LOF_URL, cf.STOCK_LOF_COLS) 398 | for col in ['price', 'increase_rt', 'volume', 'amount', 'fund_nav', 399 | 'estimate_value', 'discount_rt', 'stock_ratio', 'stock_increase_rt', 'apply_fee', 'redeem_fee']: 400 | self._data[col] = self._data[col].astype(float) 401 | 402 | return self._result() 403 | 404 | 405 | def indexLof(self): 406 | """ 407 | 指数LOF基金及基相关数据 408 | return 409 | ------ 410 | DataFrame or List: [{'fund_id':, 'fund_nm':, ...}, ...] 411 | fund_id: 基金代码 412 | fund_nm: 基金名称 413 | price: 现价 414 | increase_rt: 涨幅(%) 415 | volume: 成交(万元) 416 | amount: 场内份额(万份) 417 | fund_nav: 基金净值 418 | nav_dt: 净值日期 419 | estimate_value: 实时估值 420 | discount_rt: 溢价率(%) 421 | index_id: 指数代码 422 | index_nm: 指数名称 423 | index_increase_rt: 指数涨幅(%) 424 | apply_fee: 申购费(%) 425 | redeem_fee: 赎回费(%) 426 | apply_redeem_status: 申赎状态 427 | """ 428 | self._data = pd.DataFrame() 429 | 430 | self._data = self.__parsePage(cf.INDEX_LOF_URL, cf.INDEX_LOF_COLS) 431 | for col in ['price', 'increase_rt', 'volume', 'amount', 'fund_nav', 'estimate_value', 432 | 'discount_rt', 'index_increase_rt', 'apply_fee', 'redeem_fee']: 433 | self._data[col] = self._data[col].astype(float) 434 | 435 | return self._result() 436 | 437 | 438 | def __parsePage(self, url, column): 439 | page = 1 440 | while(True): 441 | try: 442 | request = self._session.get(url % page) 443 | text = request.text.replace('%', '') 444 | dataDict = json.loads(text) 445 | if dataDict['page'] < page: 446 | break 447 | 448 | dataList = [] 449 | for row in dataDict['rows']: 450 | dataList.append(row['cell']) 451 | 452 | self._data = self._data.append(pd.DataFrame(dataList, columns = column), ignore_index=True) 453 | 454 | page += 1 455 | except Exception as e: 456 | print(str(e)) 457 | 458 | return self._data 459 | 460 | 461 | -------------------------------------------------------------------------------- /GuGu/macro.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | 宏观经济数据类 4 | Created on 2019/01/09 5 | @author: TabQ 6 | @group : GuGu 7 | @contact: 16621596@qq.com 8 | """ 9 | import pandas as pd 10 | import numpy as np 11 | import re 12 | import json 13 | import time 14 | from utility import Utility 15 | from base import Base, cf 16 | 17 | 18 | class Macro(Base): 19 | def gdpYear(self, retry=3, pause=0.001): 20 | """ 21 | 获取年度国内生产总值数据 22 | Parameters 23 | -------- 24 | retry : int, 默认 3 25 | 如遇网络等问题重复执行的次数 26 | pause : int, 默认 0.001 27 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 28 | 29 | Return 30 | -------- 31 | DataFrame or List: [{'year':, 'gdp':, ...}, ...] 32 | year :统计年度 33 | gdp :国内生产总值(亿元) 34 | pc_gdp :人均国内生产总值(元) 35 | gnp :国民生产总值(亿元) 36 | pi :第一产业(亿元) 37 | si :第二产业(亿元) 38 | industry :工业(亿元) 39 | cons_industry :建筑业(亿元) 40 | ti :第三产业(亿元) 41 | trans_industry :交通运输仓储邮电通信业(亿元) 42 | lbdy :批发零售贸易及餐饮业(亿元) 43 | """ 44 | self._data = pd.DataFrame() 45 | 46 | # http://money.finance.sina.com.cn/mac/api/jsonp.php/SINAREMOTECALLCALLBACK4224641560861/MacPage_Service.get_pagedata?cate=nation&event=0&from=0&num=70&condition=&_=4224641560861 47 | datastr = self.__parsePage('nation', 0, 70, retry, pause) 48 | datastr = datastr.replace('"', '').replace('null', '0') 49 | js = json.loads(datastr) 50 | self._data = pd.DataFrame(js, columns=cf.GDP_YEAR_COLS) 51 | self._data[self._data==0] = np.NaN 52 | 53 | return self._result() 54 | 55 | def gdpQuarter(self, retry=3, pause=0.001): 56 | """ 57 | 获取季度国内生产总值数据 58 | Parameters 59 | -------- 60 | retry : int, 默认 3 61 | 如遇网络等问题重复执行的次数 62 | pause : int, 默认 0.001 63 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 64 | 65 | Return 66 | -------- 67 | DataFrame or List: [{'quarter':, 'gdp':, ...}, ...] 68 | quarter :季度 69 | gdp :国内生产总值(亿元) 70 | gdp_yoy :国内生产总值同比增长(%) 71 | pi :第一产业增加值(亿元) 72 | pi_yoy:第一产业增加值同比增长(%) 73 | si :第二产业增加值(亿元) 74 | si_yoy :第二产业增加值同比增长(%) 75 | ti :第三产业增加值(亿元) 76 | ti_yoy :第三产业增加值同比增长(%) 77 | """ 78 | self._data = pd.DataFrame() 79 | 80 | # http://money.finance.sina.com.cn/mac/api/jsonp.php/SINAREMOTECALLCALLBACK3935140379887/MacPage_Service.get_pagedata?cate=nation&event=1&from=0&num=250&condition=&_=3935140379887 81 | datastr = self.__parsePage('nation', 1, 250, retry, pause) 82 | datastr = datastr.replace('"', '').replace('null', '0') 83 | js = json.loads(datastr) 84 | self._data = pd.DataFrame(js, columns=cf.GDP_QUARTER_COLS) 85 | self._data['quarter'] = self._data['quarter'].astype(object) 86 | self._data[self._data==0] = np.NaN 87 | 88 | return self._result() 89 | 90 | def demandsToGdp(self, retry=3, pause=0.001): 91 | """ 92 | 获取三大需求对GDP贡献数据 93 | Parameters 94 | -------- 95 | retry : int, 默认 3 96 | 如遇网络等问题重复执行的次数 97 | pause : int, 默认 0.001 98 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 99 | 100 | Return 101 | -------- 102 | DataFrame or List: [{'year':, 'cons_to':, ...}, ...] 103 | year :统计年度 104 | cons_to :最终消费支出贡献率(%) 105 | cons_rate :最终消费支出拉动(百分点) 106 | asset_to :资本形成总额贡献率(%) 107 | asset_rate:资本形成总额拉动(百分点) 108 | goods_to :货物和服务净出口贡献率(%) 109 | goods_rate :货物和服务净出口拉动(百分点) 110 | """ 111 | self._data = pd.DataFrame() 112 | 113 | # http://money.finance.sina.com.cn/mac/api/jsonp.php/SINAREMOTECALLCALLBACK3153587567694/MacPage_Service.get_pagedata?cate=nation&event=4&from=0&num=80&condition=&_=3153587567694 114 | datastr = self.__parsePage('nation', 4, 80, retry, pause) 115 | datastr = datastr.replace('"','').replace('null','0') 116 | js = json.loads(datastr) 117 | self._data = pd.DataFrame(js,columns=cf.GDP_FOR_COLS) 118 | self._data[self._data==0] = np.NaN 119 | 120 | return self._result() 121 | 122 | 123 | def idsPullToGdp(self, retry=3, pause=0.001): 124 | """ 125 | 获取三大产业对GDP拉动数据 126 | Parameters 127 | -------- 128 | retry : int, 默认 3 129 | 如遇网络等问题重复执行的次数 130 | pause : int, 默认 0.001 131 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 132 | 133 | Return 134 | -------- 135 | DataFrame or List: [{'year':, 'gdp_yoy':, ...}, ...] 136 | year :统计年度 137 | gdp_yoy :国内生产总值同比增长(%) 138 | pi :第一产业拉动率(%) 139 | si :第二产业拉动率(%) 140 | industry:其中工业拉动(%) 141 | ti :第三产业拉动率(%) 142 | """ 143 | self._data = pd.DataFrame() 144 | 145 | # http://money.finance.sina.com.cn/mac/api/jsonp.php/SINAREMOTECALLCALLBACK1083239038283/MacPage_Service.get_pagedata?cate=nation&event=5&from=0&num=60&condition=&_=1083239038283 146 | datastr = self.__parsePage('nation', 5, 60, retry, pause) 147 | datastr = datastr.replace('"', '').replace('null', '0') 148 | js = json.loads(datastr) 149 | self._data = pd.DataFrame(js, columns=cf.GDP_PULL_COLS) 150 | self._data[self._data==0] = np.NaN 151 | 152 | return self._result() 153 | 154 | 155 | def idsCtbToGdp(self, retry=3, pause=0.001): 156 | """ 157 | 获取三大产业贡献率数据 158 | Parameters 159 | -------- 160 | retry : int, 默认 3 161 | 如遇网络等问题重复执行的次数 162 | pause : int, 默认 0.001 163 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 164 | 165 | Return 166 | -------- 167 | DataFrame or List: [{'year':, 'gdp_yoy':, ...}, ...] 168 | year :统计年度 169 | gdp_yoy :国内生产总值 170 | pi :第一产业献率(%) 171 | si :第二产业献率(%) 172 | industry:其中工业献率(%) 173 | ti :第三产业献率(%) 174 | """ 175 | self._data = pd.DataFrame() 176 | 177 | # http://money.finance.sina.com.cn/mac/api/jsonp.php/SINAREMOTECALLCALLBACK4658347026358/MacPage_Service.get_pagedata?cate=nation&event=6&from=0&num=60&condition=&_=4658347026358 178 | datastr = self.__parsePage('nation', 6, 60, retry, pause) 179 | datastr = datastr.replace('"', '').replace('null', '0') 180 | js = json.loads(datastr) 181 | self._data = pd.DataFrame(js, columns=cf.GDP_CONTRIB_COLS) 182 | self._data[self._data==0] = np.NaN 183 | 184 | return self._result() 185 | 186 | 187 | def cpi(self, retry=3, pause=0.001): 188 | """ 189 | 获取居民消费价格指数数据 190 | Parameters 191 | -------- 192 | retry : int, 默认 3 193 | 如遇网络等问题重复执行的次数 194 | pause : int, 默认 0.001 195 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 196 | 197 | Return 198 | -------- 199 | DataFrame or List: [{'month':, 'cpi':,}, ...] 200 | month :统计月份 201 | cpi :价格指数 202 | """ 203 | self._data = pd.DataFrame() 204 | 205 | datastr = self.__parsePage('price', 0, 600, retry, pause) 206 | js = json.loads(datastr) 207 | self._data = pd.DataFrame(js, columns=cf.CPI_COLS) 208 | self._data['cpi'] = self._data['cpi'].astype(float) 209 | 210 | return self._result() 211 | 212 | 213 | def ppi(self, retry=3, pause=0.001): 214 | """ 215 | 获取工业品出厂价格指数数据 216 | Parameters 217 | -------- 218 | retry : int, 默认 3 219 | 如遇网络等问题重复执行的次数 220 | pause : int, 默认 0.001 221 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 222 | 223 | Return 224 | -------- 225 | DataFrame or List: [{'month':, 'ppiip':, ...}, ...] 226 | month :统计月份 227 | ppiip :工业品出厂价格指数 228 | ppi :生产资料价格指数 229 | qm:采掘工业价格指数 230 | rmi:原材料工业价格指数 231 | pi:加工工业价格指数 232 | cg:生活资料价格指数 233 | food:食品类价格指数 234 | clothing:衣着类价格指数 235 | roeu:一般日用品价格指数 236 | dcg:耐用消费品价格指数 237 | """ 238 | self._data = pd.DataFrame() 239 | 240 | # http://money.finance.sina.com.cn/mac/api/jsonp.php/SINAREMOTECALLCALLBACK6734345383111/MacPage_Service.get_pagedata?cate=price&event=3&from=0&num=600&condition=&_=6734345383111 241 | datastr = self.__parsePage('price', 3, 600, retry, pause) 242 | js = json.loads(datastr) 243 | self._data = pd.DataFrame(js, columns=cf.PPI_COLS) 244 | for i in self._data.columns: 245 | self._data[i] = self._data[i].apply(lambda x:np.where(x is None, np.NaN, x)) 246 | if i != 'month': 247 | self._data[i] = self._data[i].astype(float) 248 | 249 | return self._result() 250 | 251 | 252 | def depositRate(self, retry=3, pause=0.001): 253 | """ 254 | 获取存款利率数据 255 | Parameters 256 | -------- 257 | retry : int, 默认 3 258 | 如遇网络等问题重复执行的次数 259 | pause : int, 默认 0.001 260 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 261 | 262 | Return 263 | -------- 264 | DataFrame or List: [{'date':, 'deposit_type':, ...}, ...] 265 | date :变动日期 266 | deposit_type :存款种类 267 | rate:利率(%) 268 | """ 269 | self._data = pd.DataFrame() 270 | 271 | # http://money.finance.sina.com.cn/mac/api/jsonp.php/SINAREMOTECALLCALLBACK1250640915421/MacPage_Service.get_pagedata?cate=fininfo&event=2&from=0&num=600&condition=&_=1250640915421 272 | datastr = self.__parsePage('fininfo', 2, 600, retry, pause) 273 | js = json.loads(datastr) 274 | self._data = pd.DataFrame(js, columns=cf.DEPOSIT_COLS) 275 | for i in self._data.columns: 276 | self._data[i] = self._data[i].apply(lambda x:np.where(x is None, '--', x)) 277 | 278 | return self._result() 279 | 280 | 281 | def loanRate(self, retry=3, pause=0.001): 282 | """ 283 | 获取贷款利率数据 284 | Parameters 285 | -------- 286 | retry : int, 默认 3 287 | 如遇网络等问题重复执行的次数 288 | pause : int, 默认 0.001 289 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 290 | 291 | Return 292 | -------- 293 | DataFrame or List: [{'date':, 'loan_type':, ...}, ...] 294 | date :执行日期 295 | loan_type :存款种类 296 | rate:利率(%) 297 | """ 298 | self._data = pd.DataFrame() 299 | 300 | # http://money.finance.sina.com.cn/mac/api/jsonp.php/SINAREMOTECALLCALLBACK7542659823280/MacPage_Service.get_pagedata?cate=fininfo&event=3&from=0&num=800&condition=&_=7542659823280 301 | datastr = self.__parsePage('fininfo', 3, 800, retry, pause) 302 | js = json.loads(datastr) 303 | self._data = pd.DataFrame(js, columns=cf.LOAN_COLS) 304 | for i in self._data.columns: 305 | self._data[i] = self._data[i].apply(lambda x:np.where(x is None, '--', x)) 306 | 307 | return self._result() 308 | 309 | 310 | def rrr(self, retry=3, pause=0.001): 311 | """ 312 | 获取存款准备金率数据 313 | Parameters 314 | -------- 315 | retry : int, 默认 3 316 | 如遇网络等问题重复执行的次数 317 | pause : int, 默认 0.001 318 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 319 | 320 | Return 321 | -------- 322 | DataFrame or List: [{'date':, 'before':, ...}, ...] 323 | date :变动日期 324 | before :调整前存款准备金率(%) 325 | now:调整后存款准备金率(%) 326 | changed:调整幅度(%) 327 | """ 328 | self._data = pd.DataFrame() 329 | 330 | # http://money.finance.sina.com.cn/mac/api/jsonp.php/SINAREMOTECALLCALLBACK8028217046046/MacPage_Service.get_pagedata?cate=fininfo&event=4&from=0&num=100&condition=&_=8028217046046 331 | datastr = self.__parsePage('fininfo', 4, 100, retry, pause) 332 | datastr = datastr if self._PY3 else datastr.decode('gbk') 333 | js = json.loads(datastr) 334 | self._data = pd.DataFrame(js, columns=cf.RRR_COLS) 335 | for i in self._data.columns: 336 | self._data[i] = self._data[i].apply(lambda x:np.where(x is None, '--', x)) 337 | 338 | return self._result() 339 | 340 | 341 | def montySupply(self, retry=3, pause=0.001): 342 | """ 343 | 获取货币供应量数据 344 | Parameters 345 | -------- 346 | retry : int, 默认 3 347 | 如遇网络等问题重复执行的次数 348 | pause : int, 默认 0.001 349 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 350 | 351 | Return 352 | -------- 353 | DataFrame or List: [{'month':, 'm2':, ...}, ...] 354 | month :统计时间 355 | m2 :货币和准货币(广义货币M2)(亿元) 356 | m2_yoy:货币和准货币(广义货币M2)同比增长(%) 357 | m1:货币(狭义货币M1)(亿元) 358 | m1_yoy:货币(狭义货币M1)同比增长(%) 359 | m0:流通中现金(M0)(亿元) 360 | m0_yoy:流通中现金(M0)同比增长(%) 361 | cd:活期存款(亿元) 362 | cd_yoy:活期存款同比增长(%) 363 | qm:准货币(亿元) 364 | qm_yoy:准货币同比增长(%) 365 | ftd:定期存款(亿元) 366 | ftd_yoy:定期存款同比增长(%) 367 | sd:储蓄存款(亿元) 368 | sd_yoy:储蓄存款同比增长(%) 369 | rests:其他存款(亿元) 370 | rests_yoy:其他存款同比增长(%) 371 | """ 372 | self._data = pd.DataFrame() 373 | 374 | # http://money.finance.sina.com.cn/mac/api/jsonp.php/SINAREMOTECALLCALLBACK9019314616219/MacPage_Service.get_pagedata?cate=fininfo&event=1&from=0&num=600&condition=&_=9019314616219 375 | datastr = self.__parsePage('fininfo', 1, 600, retry, pause) 376 | datastr = datastr if self._PY3 else datastr.decode('gbk') 377 | js = json.loads(datastr) 378 | self._data = pd.DataFrame(js, columns=cf.MONEY_SUPPLY_COLS) 379 | for i in self._data.columns: 380 | self._data[i] = self._data[i].apply(lambda x:np.where(x is None, '--', x)) 381 | 382 | return self._result() 383 | 384 | 385 | def moneySupplyBal(self, retry=3, pause=0.001): 386 | """ 387 | 获取货币供应量(年底余额)数据 388 | Parameters 389 | -------- 390 | retry : int, 默认 3 391 | 如遇网络等问题重复执行的次数 392 | pause : int, 默认 0.001 393 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 394 | 395 | Return 396 | -------- 397 | DataFrame or List: [{'year':, 'm2':, ...}, ...] 398 | year :统计年度 399 | m2 :货币和准货币(亿元) 400 | m1:货币(亿元) 401 | m0:流通中现金(亿元) 402 | cd:活期存款(亿元) 403 | qm:准货币(亿元) 404 | ftd:定期存款(亿元) 405 | sd:储蓄存款(亿元) 406 | rests:其他存款(亿元) 407 | """ 408 | self._data = pd.DataFrame() 409 | 410 | # http://money.finance.sina.com.cn/mac/api/jsonp.php/SINAREMOTECALLCALLBACK3430820865181/MacPage_Service.get_pagedata?cate=fininfo&event=0&from=0&num=200&condition=&_=3430820865181 411 | datastr = self.__parsePage('fininfo', 0, 200, retry, pause) 412 | datastr = datastr if self._PY3 else datastr.decode('gbk') 413 | js = json.loads(datastr) 414 | self._data = pd.DataFrame(js, columns=cf.MONEY_SUPPLY_BLA_COLS) 415 | for i in self._data.columns: 416 | self._data[i] = self._data[i].apply(lambda x:np.where(x is None, '--', x)) 417 | 418 | return self._result() 419 | 420 | 421 | def __parsePage(self, cate='', event=0, num=0, retry=3, pause=0.001): 422 | for _ in range(retry): 423 | time.sleep(pause) 424 | 425 | try: 426 | rdInt = Utility.random() 427 | request = self._session.get( cf.MACRO_URL % (rdInt, cate, event, num, rdInt), timeout=10 ) 428 | if self._PY3: 429 | request.encoding = 'gbk' 430 | 431 | regSym = re.compile(r'\,count:(.*?)\}') 432 | datastr = regSym.findall(request.text) 433 | datastr = datastr[0] 434 | datastr = datastr.split('data:')[1] 435 | except Exception as e: 436 | print(e) 437 | else: 438 | return datastr 439 | 440 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 441 | 442 | 443 | def shibor(self, year=None): 444 | """ 445 | 获取上海银行间同业拆放利率 446 | Parameters 447 | ------ 448 | year:年份(int) 449 | 450 | Return 451 | ------ 452 | DataFrame or List: [{'date':, 'ON':, ...}, ...] 453 | date:日期 454 | ON:隔夜拆放利率 455 | 1W:1周拆放利率 456 | 2W:2周拆放利率 457 | 1M:1个月拆放利率 458 | 3M:3个月拆放利率 459 | 6M:6个月拆放利率 460 | 9M:9个月拆放利率 461 | 1Y:1年拆放利率 462 | """ 463 | self._data = pd.DataFrame() 464 | 465 | lab = cf.SHIBOR_TYPE['Shibor'] 466 | # http://www.shibor.org/shibor/web/html/downLoad.html?nameNew=Historical_Shibor_Data_2018.xls&downLoadPath=data&nameOld=Shibor数据2018.xls&shiborSrc=http://www.shibor.org/shibor/ 467 | self._data = self.__parseExcel(year, 'Shibor', lab, cf.SHIBOR_COLS) 468 | 469 | return self._result() 470 | 471 | 472 | def shiborQuote(self, year=None): 473 | """ 474 | 获取Shibor银行报价数据 475 | Parameters 476 | ------ 477 | year:年份(int) 478 | 479 | Return 480 | ------ 481 | DataFrame or List: [{'date':, 'bank':, ...}, ...] 482 | date:日期 483 | bank:报价银行名称 484 | ON:隔夜拆放利率 485 | 1W:1周拆放利率 486 | 2W:2周拆放利率 487 | 1M:1个月拆放利率 488 | 3M:3个月拆放利率 489 | 6M:6个月拆放利率 490 | 9M:9个月拆放利率 491 | 1Y:1年拆放利率 492 | """ 493 | self._data = pd.DataFrame() 494 | 495 | lab = cf.SHIBOR_TYPE['Quote'] 496 | # http://www.shibor.org/shibor/web/html/downLoad.html?nameNew=Historical_Quote_Data_2018.xls&downLoadPath=data&nameOld=报价数据2018.xls&shiborSrc=http://www.shibor.org/shibor/ 497 | self._data = self.__parseExcel(year, 'Quote', lab, cf.QUOTE_COLS) 498 | 499 | return self._result() 500 | 501 | 502 | def shiborMa(self, year=None): 503 | """ 504 | 获取Shibor均值数据 505 | Parameters 506 | ------ 507 | year:年份(int) 508 | 509 | Return 510 | ------ 511 | DataFrame or List: [{'date':, 'ON_5':, ...}, ...] 512 | date:日期 513 | 其它分别为各周期5、10、20均价 514 | """ 515 | self._data = pd.DataFrame() 516 | 517 | lab = cf.SHIBOR_TYPE['Tendency'] 518 | self._data = self.__parseExcel(year, 'Shibor_Tendency', lab, cf.SHIBOR_MA_COLS) 519 | 520 | return self._result() 521 | 522 | 523 | def lpr(self, year=None): 524 | """ 525 | 获取贷款基础利率 526 | Parameters 527 | ------ 528 | year:年份(int) 529 | 530 | Return 531 | ------ 532 | DataFrame or List: [{'date':, '1Y':, ...}, ...] 533 | date:日期 534 | 1Y:1年贷款基础利率 535 | """ 536 | self._data = pd.DataFrame() 537 | 538 | lab = cf.SHIBOR_TYPE['LPR'] 539 | self._data = self.__parseExcel(year, 'LPR', lab, cf.LPR_COLS) 540 | 541 | return self._result() 542 | 543 | 544 | def lprMa(self, year=None): 545 | """ 546 | 获取贷款基础利率均值数据 547 | Parameters 548 | ------ 549 | year:年份(int) 550 | 551 | Return 552 | ------ 553 | DataFrame or List: [{'date':, '1Y_5':, ...}, ...] 554 | date:日期 555 | 1Y_5:5日均值 556 | 1Y_10:10日均值 557 | 1Y_20:20日均值 558 | """ 559 | self._data = pd.DataFrame() 560 | 561 | lab = cf.SHIBOR_TYPE['LPR_Tendency'] 562 | self._data = self.__parseExcel(year, 'LPR_Tendency', lab, cf.LPR_MA_COLS) 563 | 564 | return self._result() 565 | 566 | 567 | def __parseExcel(self, year, datatype, lab, column): 568 | year = Utility.getYear() if year is None else year 569 | lab = lab.encode('utf-8') if self._PY3 else lab 570 | 571 | try: 572 | df = pd.read_excel( cf.SHIBOR_DATA_URL % (datatype, year, lab, year), skiprows=[0] ) 573 | df.columns = column 574 | df['date'] = df['date'].map(lambda x: x.date()) 575 | df['date'] = df['date'].astype('datetime64[ns]') 576 | except Exception as e: 577 | print(e) 578 | else: 579 | return df 580 | 581 | 582 | -------------------------------------------------------------------------------- /GuGu/marketdata.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | 市场类 4 | Created on 2018/12/26 5 | @author: TabQ 6 | @group : GuGu 7 | @contact: 16621596@qq.com 8 | """ 9 | 10 | import pandas as pd 11 | from pandas.compat import StringIO 12 | import re 13 | import json 14 | import numpy as np 15 | from base import Base, cf 16 | 17 | 18 | class MarketData(Base): 19 | def index(self): 20 | """ 21 | 获取大盘指数行情 22 | return 23 | ------- 24 | DataFrame or list: [{'code':, 'name':, ...}, ...] 25 | code:指数代码 26 | name:指数名称 27 | change:涨跌幅 28 | open:开盘价 29 | preclose:昨日收盘价 30 | close:收盘价 31 | high:最高价 32 | low:最低价 33 | volume:成交量(手) 34 | amount:成交金额(亿元) 35 | """ 36 | self._data = pd.DataFrame() 37 | 38 | # http://hq.sinajs.cn/rn=xppzh&list=sh000001,sh000002,sh000003,sh000008,sh000009,sh000010,sh000011,sh000012,sh000016,sh000017,sh000300,sh000905,sz399001,sz399002,sz399003,sz399004,sz399005,sz399006,sz399008,sz399100,sz399101,sz399106,sz399107,sz399108,sz399333,sz399606 39 | request = self._session.get( cf.INDEX_URL, timeout=10 ) 40 | request.encoding = 'gbk' 41 | text = request.text.replace('var hq_str_', '') 42 | text = text.replace('";', '').replace('"', '').replace('=', ',') 43 | text = '%s%s'%(cf.INDEX_HEADER, text) 44 | 45 | self._data = pd.read_csv(StringIO(text), sep=',', thousands=',') 46 | self._data['change'] = (self._data['close'] / self._data['preclose'] - 1 ) * 100 47 | self._data['amount'] = self._data['amount'] / 100000000 48 | self._data['change'] = self._data['change'].map(cf.FORMAT) 49 | self._data['amount'] = self._data['amount'].map(cf.FORMAT4) 50 | self._data = self._data[cf.INDEX_COLS] 51 | self._data['code'] = self._data['code'].map(lambda x:str(x).zfill(6)) 52 | self._data['change'] = self._data['change'].astype(float) 53 | self._data['amount'] = self._data['amount'].astype(float) 54 | 55 | return self._result() 56 | 57 | 58 | def latest(self): 59 | """ 60 | 一次性获取最近一个日交易日所有股票的交易数据 61 | return 62 | ------- 63 | DataFrame or list: [{'code':, 'name':, ...}, ...] 64 | 属性:代码,名称,涨跌幅,现价,开盘价,最高价,最低价,最日收盘价,成交量,换手率,成交额,市盈率,市净率,总市值,流通市值 65 | """ 66 | self._data = pd.DataFrame() 67 | 68 | self._writeHead() 69 | 70 | self._data = self.__handleLatest(1) 71 | if self._data is not None: 72 | for i in range(2, cf.PAGE_NUM[0]): 73 | newData = self.__handleLatest(i) 74 | self._data = self._data.append(newData, ignore_index=True) 75 | 76 | return self._result() 77 | 78 | 79 | def __handleLatest(self, pageNum=1): 80 | """ 81 | 处理当日行情分页数据,格式为json 82 | Parameters 83 | ------ 84 | pageNum:页码 85 | return 86 | ------- 87 | DataFrame 当日所有股票交易数据(DataFrame) 88 | """ 89 | self._writeConsole() 90 | # http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/Market_Center.getHQNodeData?num=80&sort=code&asc=0&node=hs_a&symbol=&_s_r_a=page&page=1 91 | request = self._session.get( cf.LATEST_URL % pageNum, timeout=10 ) 92 | if self._PY3: 93 | request.encoding = 'gbk' 94 | text = request.text 95 | if text == 'null': 96 | return None 97 | 98 | reg = re.compile(r'\,(.*?)\:') 99 | text = reg.sub(r',"\1":', text) 100 | text = text.replace('"{symbol', '{"symbol') 101 | text = text.replace('{symbol', '{"symbol"') 102 | jstr = json.dumps(text) 103 | js = json.loads(jstr) 104 | df = pd.DataFrame(pd.read_json(js, dtype={'code':object}), columns=cf.DAY_TRADING_COLUMNS) 105 | df = df.drop('symbol', axis=1) 106 | 107 | return df 108 | 109 | 110 | def indexETF(self): 111 | """ 112 | 获取指数ETF及其相关数据 113 | return 114 | ------ 115 | DataFrame or List: [{'fund_id':, 'fund_nm':, ...}, ...] 116 | fund_id: 基金代码 117 | fund_nm: 基金名称 118 | index_id: 跟踪指数代码 119 | creation_unit: 最小申赎单位(万份) 120 | amount: 份额(万份) 121 | unit_total: 规模(亿元) 122 | unit_incr: 规模变化(亿元) 123 | price: 现价 124 | volume: 成交额(万元) 125 | increase_rt: 涨幅(%) 126 | estimate_value: 估值 127 | discount_rt: 溢价率(%) 128 | fund_nav: 净值 129 | nav_dt: 净值日期 130 | index_nm: 指数名称 131 | index_increase_rt: 指数涨幅(%) 132 | pe: 市盈率 133 | pb: 市净率 134 | """ 135 | self._data = pd.DataFrame() 136 | 137 | page = 1 138 | while(True): 139 | try: 140 | request = self._session.get( cf.INDEX_ETF_URL % page ) 141 | text = request.text.replace('%', '') 142 | dataDict = json.loads(text) 143 | if dataDict['page'] < page: 144 | break 145 | 146 | dataList = [] 147 | for row in dataDict['rows']: 148 | dataList.append(row['cell']) 149 | self._data = self._data.append(pd.DataFrame(dataList, columns = cf.INDEX_ETF_COLS), ignore_index=True) 150 | 151 | page += 1 152 | except Exception as e: 153 | print(str(e)) 154 | 155 | self._data[self._data=="-"] = np.NaN 156 | for col in ['creation_unit', 'amount', 'unit_total', 'unit_incr', 'price', 'volume', 'increase_rt', 157 | 'estimate_value', 'discount_rt', 'fund_nav', 'index_increase_rt', 'pe', 'pb']: 158 | self._data[col] = self._data[col].astype(float) 159 | 160 | self._data['index_id'] = self._data['index_id'].map(lambda x : x if x != "0" else None) 161 | 162 | return self._result() 163 | 164 | 165 | -------------------------------------------------------------------------------- /GuGu/reference.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | 投资参考类 4 | Created on 2019/01/03 5 | @author: TabQ 6 | @group : GuGu 7 | @contact: 16621596@qq.com 8 | """ 9 | 10 | from __future__ import division 11 | 12 | import math 13 | import time 14 | import pandas as pd 15 | from pandas.compat import StringIO 16 | import lxml.html 17 | from lxml import etree 18 | import re 19 | import json 20 | from utility import Utility 21 | from base import Base, cf 22 | 23 | class Reference(Base): 24 | def distriPlan(self, year=2015, top=25, retry=3, pause=0.001): 25 | """ 26 | 获取分配预案数据 27 | Parameters 28 | -------- 29 | year:年份 30 | top:取最新n条数据,默认取最近公布的25条 31 | retry : int, 默认 3 32 | 如遇网络等问题重复执行的次数 33 | pause : int, 默认 0.001 34 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 35 | 36 | returns 37 | ------- 38 | DataFrame or List: [{'code', 'name', ...}, ...] 39 | code:股票代码 40 | name:股票名称 41 | year:分配年份 42 | report_date:公布日期 43 | divi:分红金额(每10股) 44 | shares:转增和送股数(每10股) 45 | """ 46 | self._data = pd.DataFrame() 47 | 48 | if top == 'all': 49 | self._writeHead() 50 | 51 | self._data, pages = self.__handleDistriPlan(year, 0, retry, pause) 52 | for i in range(1, int(pages)): 53 | self._data = self._data.append(self.__handleDistriPlan(year, i, retry, pause), ignore_index=True) 54 | 55 | return self._result() 56 | elif top <= 25: 57 | self._data, pages = self.__handleDistriPlan(year, 0, retry, pause) 58 | self._data = self._data.head(top) 59 | 60 | return self._result() 61 | else: 62 | if isinstance(top, int): 63 | self._writeHead() 64 | 65 | allPages = int(math.ceil(top/25)) 66 | self._data, pages = self.__handleDistriPlan(year, 0, retry, pause) 67 | pages = min(allPages, int(pages)) 68 | for i in range(1, pages): 69 | self._data = self._data.append(self.__handleDistriPlan(year, i, retry, pause), ignore_index=True) 70 | 71 | self._data = self._data.head(top) 72 | 73 | return self._result() 74 | else: 75 | print(cf.TOP_PARAS_MSG) 76 | 77 | 78 | def __handleDistriPlan(self, year, pageNo, retry, pause): 79 | for _ in range(retry): 80 | time.sleep(pause) 81 | 82 | try: 83 | if pageNo > 0: 84 | self._writeConsole() 85 | 86 | # http://quotes.money.163.com/data/caibao/fpyg.html?reportdate=2018&sort=declaredate&order=desc&page=0 87 | html = lxml.html.parse(cf.DP_163_URL % (year, pageNo)) 88 | res = html.xpath('//table[@class=\"fn_cm_table\"]/tr') 89 | if self._PY3: 90 | sarr = [etree.tostring(node).decode('utf-8') for node in res] 91 | else: 92 | sarr = [etree.tostring(node) for node in res] 93 | sarr = ''.join(sarr) 94 | sarr = '%s
' % sarr 95 | df = pd.read_html(sarr)[0] 96 | df = df.drop(0, axis=1) 97 | df.columns = cf.DP_163_COLS 98 | df['divi'] = df['plan'].map(self.__bonus) 99 | df['shares'] = df['plan'].map(self.__gift) 100 | df = df.drop('plan', axis=1) 101 | df['code'] = df['code'].astype(object) 102 | df['code'] = df['code'].map(lambda x : str(x).zfill(6)) 103 | pages = [] 104 | if pageNo == 0: 105 | page = html.xpath('//div[@class=\"mod_pages\"]/a') 106 | if len(page)>1: 107 | asr = page[len(page)-2] 108 | pages = asr.xpath('text()') 109 | except Exception as e: 110 | print(e) 111 | else: 112 | if pageNo == 0: 113 | return df, pages[0] if len(pages)>0 else 0 114 | else: 115 | return df 116 | 117 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 118 | 119 | 120 | def __bonus(self, x): 121 | if self._PY3: 122 | reg = re.compile(r'分红(.*?)元', re.UNICODE) 123 | res = reg.findall(x) 124 | return 0 if len(res)<1 else float(res[0]) 125 | else: 126 | if isinstance(x, unicode): 127 | s1 = unicode('分红','utf-8') 128 | s2 = unicode('元','utf-8') 129 | reg = re.compile(r'%s(.*?)%s'%(s1, s2), re.UNICODE) 130 | res = reg.findall(x) 131 | return 0 if len(res)<1 else float(res[0]) 132 | else: 133 | return 0 134 | 135 | 136 | def __gift(self, x): 137 | if self._PY3: 138 | reg1 = re.compile(r'转增(.*?)股', re.UNICODE) 139 | reg2 = re.compile(r'送股(.*?)股', re.UNICODE) 140 | res1 = reg1.findall(x) 141 | res2 = reg2.findall(x) 142 | res1 = 0 if len(res1)<1 else float(res1[0]) 143 | res2 = 0 if len(res2)<1 else float(res2[0]) 144 | 145 | return res1 + res2 146 | else: 147 | if isinstance(x, unicode): 148 | s1 = unicode('转增','utf-8') 149 | s2 = unicode('送股','utf-8') 150 | s3 = unicode('股','utf-8') 151 | reg1 = re.compile(r'%s(.*?)%s'%(s1, s3), re.UNICODE) 152 | reg2 = re.compile(r'%s(.*?)%s'%(s2, s3), re.UNICODE) 153 | res1 = reg1.findall(x) 154 | res2 = reg2.findall(x) 155 | res1 = 0 if len(res1)<1 else float(res1[0]) 156 | res2 = 0 if len(res2)<1 else float(res2[0]) 157 | 158 | return res1 + res2 159 | else: 160 | return 0 161 | 162 | 163 | def forecast(self, year, quarter, retry=3, pause=0.001): 164 | """ 165 | 获取业绩预告数据 166 | Parameters 167 | -------- 168 | year:int 年度 e.g:2014 169 | quarter:int 季度 :1、2、3、4,只能输入这4个季度 170 | 说明:由于是从网站获取的数据,需要一页页抓取,速度取决于您当前网络速度 171 | retry : int, 默认 3 172 | 如遇网络等问题重复执行的次数 173 | pause : int, 默认 0.001 174 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 175 | Return 176 | -------- 177 | DataFrame or List: [{'code':, 'name':, ...}, ...] 178 | code,代码 179 | name,名称 180 | type,业绩变动类型【预增、预亏等】 181 | report_date,发布日期 182 | pre_eps,上年同期每股收益 183 | range,业绩变动范围 184 | """ 185 | self._data = pd.DataFrame() 186 | 187 | if Utility.checkQuarter(year, quarter) is True: 188 | self._writeHead() 189 | self._data = self.__handleForecast(year, quarter, 1, pd.DataFrame(), retry, pause) 190 | self._data = pd.DataFrame(self._data, columns=cf.FORECAST_COLS) 191 | self._data['code'] = self._data['code'].map(lambda x: str(x).zfill(6)) 192 | 193 | return self._result() 194 | 195 | 196 | def __handleForecast(self, year, quarter, pageNo, dataArr, retry, pause): 197 | self._writeConsole() 198 | 199 | for _ in range(retry): 200 | time.sleep(pause) 201 | 202 | try: 203 | # http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/performance/index.phtml?s_i=&s_a=&s_c=&s_type=&reportdate=2018&quarter=3&p=1&num=60 204 | request = self._session.get( cf.FORECAST_URL%( year, quarter, pageNo, cf.PAGE_NUM[1]), timeout=10 ) 205 | request.encoding = 'gbk' 206 | text = request.text.replace('--', '') 207 | html = lxml.html.parse(StringIO(text)) 208 | res = html.xpath("//table[@class=\"list_table\"]/tr") 209 | if self._PY3: 210 | sarr = [etree.tostring(node).decode('utf-8') for node in res] 211 | else: 212 | sarr = [etree.tostring(node) for node in res] 213 | sarr = ''.join(sarr) 214 | sarr = '%s
'%sarr 215 | df = pd.read_html(sarr)[0] 216 | df = df.drop([4, 5, 8], axis=1) 217 | df.columns = cf.FORECAST_COLS 218 | dataArr = dataArr.append(df, ignore_index=True) 219 | nextPage = html.xpath('//div[@class=\"pages\"]/a[last()]/@onclick') 220 | if len(nextPage)>0: 221 | pageNo = re.findall(r'\d+',nextPage[0])[0] 222 | return self.__handleForecast(year, quarter, pageNo, dataArr, retry, pause) 223 | else: 224 | return dataArr 225 | except Exception as e: 226 | print(e) 227 | 228 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 229 | 230 | 231 | def restrictedLift(self, year=None, month=None, retry=3, pause=0.001): 232 | """ 233 | 获取限售股解禁数据 234 | Parameters 235 | -------- 236 | year:年份,默认为当前年 237 | month:解禁月份,默认为当前月 238 | retry : int, 默认 3 239 | 如遇网络等问题重复执行的次数 240 | pause : int, 默认 0 241 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 242 | 243 | Return 244 | ------ 245 | DataFrame or List: [{'code':, 'name':, ...}, ...] 246 | code:股票代码 247 | name:名称 248 | date:解禁日期 249 | count:解禁数量(万股) 250 | ratio:占总盘比率 251 | """ 252 | self._data = pd.DataFrame() 253 | 254 | year = Utility.getYear() if year is None else year 255 | month = Utility.getMonth() if month is None else month 256 | 257 | for _ in range(retry): 258 | time.sleep(pause) 259 | 260 | try: 261 | # http://datainterface.eastmoney.com/EM_DataCenter/JS.aspx?type=FD&sty=BST&st=3&sr=true&fd=2019&stat=1 262 | request = self._session.get( cf.RL_URL % (year, month), timeout = 10 ) 263 | if self._PY3: 264 | request.encoding = 'utf-8' 265 | lines = request.text 266 | except Exception as e: 267 | print(e) 268 | else: 269 | da = lines[3:len(lines)-3] 270 | list = [] 271 | for row in da.split('","'): 272 | list.append([data for data in row.split(',')]) 273 | self._data = pd.DataFrame(list) 274 | self._data = self._data[[1, 3, 4, 5, 6]] 275 | for col in [5, 6]: 276 | self._data[col] = self._data[col].astype(float) 277 | self._data[5] = self._data[5]/10000 278 | self._data[6] = self._data[6]*100 279 | self._data[5] = self._data[5].map(cf.FORMAT) 280 | self._data[6] = self._data[6].map(cf.FORMAT) 281 | self._data.columns = cf.RL_COLS 282 | 283 | return self._result() 284 | 285 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 286 | 287 | 288 | def fundHoldings(self, year, quarter, retry=3, pause=0.001): 289 | """ 290 | 获取基金持股数据 291 | Parameters 292 | -------- 293 | year:年份e.g 2014 294 | quarter:季度(只能输入1,2,3,4这个四个数字) 295 | retry : int, 默认 3 296 | 如遇网络等问题重复执行的次数 297 | pause : int, 默认 0 298 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 299 | 300 | Return 301 | ------ 302 | DataFrame or List: [{'code':, 'name':, ...}, ...] 303 | code:股票代码 304 | name:名称 305 | date:报告日期 306 | nums:基金家数 307 | nlast:与上期相比(增加或减少了) 308 | count:基金持股数(万股) 309 | clast:与上期相比 310 | amount:基金持股市值 311 | ratio:占流通盘比率 312 | """ 313 | self._data = pd.DataFrame() 314 | 315 | start, end = cf.QUARTS_DIC[str(quarter)] 316 | if quarter == 1: 317 | start = start % str(year-1) 318 | end = end % year 319 | else: 320 | start, end = start % year, end % year 321 | 322 | self._writeHead() 323 | 324 | self._data, pages = self.__handleFoundHoldings(start, end, 0, retry, pause) 325 | for idx in range(1, pages): 326 | self._data = self._data.append(self.__handleFoundHoldings(start, end, idx, retry, pause), ignore_index=True) 327 | 328 | return self._result() 329 | 330 | 331 | def __handleFoundHoldings(self, start, end, pageNo, retry, pause): 332 | for _ in range(retry): 333 | time.sleep(pause) 334 | if pageNo>0: 335 | self._writeConsole() 336 | try: 337 | # http://quotes.money.163.com/hs/marketdata/service/jjcgph.php?host=/hs/marketdata/service/jjcgph.php&page=0&query=start:2018-06-30;end:2018-09-30&order=desc&count=60&type=query&req=73259 338 | request = self._session.get( cf.FUND_HOLDS_URL % (pageNo, start, end, Utility.random(5)), timeout=10 ) 339 | if self._PY3: 340 | request.encoding = 'utf-8' 341 | lines = request.text 342 | lines = lines.replace('--', '0') 343 | lines = json.loads(lines) 344 | data = lines['list'] 345 | df = pd.DataFrame(data) 346 | df = df.drop(['CODE', 'ESYMBOL', 'EXCHANGE', 'NAME', 'RN', 'SHANGQIGUSHU', 'SHANGQISHIZHI', 'SHANGQISHULIANG'], axis=1) 347 | for col in ['GUSHU', 'GUSHUBIJIAO', 'SHIZHI', 'SCSTC27']: 348 | df[col] = df[col].astype(float) 349 | df['SCSTC27'] = df['SCSTC27']*100 350 | df['GUSHU'] = df['GUSHU']/10000 351 | df['GUSHUBIJIAO'] = df['GUSHUBIJIAO']/10000 352 | df['SHIZHI'] = df['SHIZHI']/10000 353 | df['GUSHU'] = df['GUSHU'].map(cf.FORMAT) 354 | df['GUSHUBIJIAO'] = df['GUSHUBIJIAO'].map(cf.FORMAT) 355 | df['SHIZHI'] = df['SHIZHI'].map(cf.FORMAT) 356 | df['SCSTC27'] = df['SCSTC27'].map(cf.FORMAT) 357 | df.columns = cf.FUND_HOLDS_COLS 358 | df = df[['code', 'name', 'date', 'nums', 'nlast', 'count', 359 | 'clast', 'amount', 'ratio']] 360 | except Exception as e: 361 | print(e) 362 | else: 363 | if pageNo == 0: 364 | return df, int(lines['pagecount']) 365 | else: 366 | return df 367 | 368 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 369 | 370 | 371 | def ipo(self, retry=3, pause=0.001): 372 | """ 373 | 获取新股上市数据 374 | Parameters 375 | -------- 376 | retry : int, 默认 3 377 | 如遇网络等问题重复执行的次数 378 | pause : int, 默认 0 379 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 380 | 381 | Return 382 | ------ 383 | DataFrame or List: [{'code':, 'name':, ...}, ...] 384 | code:股票代码 385 | xcode:申购代码 386 | name:名称 387 | ipo_date:上网发行日期 388 | issue_date:上市日期 389 | amount:发行数量(万股) 390 | markets:上网发行数量(万股) 391 | price:发行价格(元) 392 | pe:发行市盈率 393 | limit:个人申购上限(万股) 394 | funds:募集资金(亿元) 395 | ballot:网上中签率(%) 396 | """ 397 | self._data = pd.DataFrame() 398 | 399 | self._writeHead() 400 | 401 | self._data = self.__handleIpo(self._data, 1, retry, pause) 402 | 403 | return self._result() 404 | 405 | 406 | def __handleIpo(self, data, pageNo, retry, pause): 407 | self._writeConsole() 408 | 409 | for _ in range(retry): 410 | time.sleep(pause) 411 | 412 | try: 413 | # http://vip.stock.finance.sina.com.cn/corp/view/vRPD_NewStockIssue.php?page=1&cngem=0&orderBy=NetDate&orderType=desc 414 | html = lxml.html.parse(cf.NEW_STOCKS_URL % pageNo) 415 | res = html.xpath('//table[@id=\"NewStockTable\"]/tr') 416 | if not res: 417 | return data 418 | 419 | if self._PY3: 420 | sarr = [etree.tostring(node).decode('utf-8') for node in res] 421 | else: 422 | sarr = [etree.tostring(node) for node in res] 423 | sarr = ''.join(sarr) 424 | sarr = sarr.replace('*', '') 425 | sarr = '%s
'%sarr 426 | df = pd.read_html(StringIO(sarr), skiprows=[0, 1])[0] 427 | df = df.drop([df.columns[idx] for idx in [12, 13, 14]], axis=1) 428 | df.columns = cf.NEW_STOCKS_COLS 429 | df['code'] = df['code'].map(lambda x : str(x).zfill(6)) 430 | df['xcode'] = df['xcode'].map(lambda x : str(x).zfill(6)) 431 | res = html.xpath('//table[@class=\"table2\"]/tr[1]/td[1]/a/text()') 432 | tag = '下一页' if self._PY3 else unicode('下一页', 'utf-8') 433 | hasNext = True if tag in res else False 434 | data = data.append(df, ignore_index=True) 435 | pageNo += 1 436 | if hasNext: 437 | data = self.__handleIpo(data, pageNo, retry, pause) 438 | except Exception as ex: 439 | print(ex) 440 | else: 441 | return data 442 | 443 | 444 | def shMargins(self, retry=3, pause=0.001): 445 | """ 446 | 沪市融资融券历史数据 447 | Parameters 448 | -------- 449 | retry : int, 默认 3 450 | 如遇网络等问题重复执行的次数 451 | pause : int, 默认 0 452 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 453 | 454 | Return 455 | ------ 456 | DataFrame or List: [{'date':, 'close':, ...}, ...] 457 | date: 日期 458 | close: 上证指数收盘点数 459 | zdf: 上证指数收盘涨跌幅(%) 460 | rzye: 融资余额(元) 461 | rzyezb: 融资余额占比(%) 462 | rzmre: 融资买入额(元) 463 | rzche: 融资偿还额(元) 464 | rzjmre: 融资净买入额(元) 465 | rqye: 融券余额(元) 466 | rqyl: 融券余量(股) 467 | rqmcl: 融券卖出量(股) 468 | rqchl: 融券偿还量(股) 469 | rqjmcl: 融券净卖出量(股) 470 | rzrqye: 融资融券余额(元) 471 | rzrqyecz: 融资融券余额差值(元) 472 | """ 473 | self._data = pd.DataFrame() 474 | 475 | self._writeHead() 476 | 477 | self._data = self.__handleMargins(self._data, 1, 'SH', Utility.random(8), cf.MAR_COLS, retry, pause) 478 | self._data.rename(columns={'tdate':'date'}, inplace=True) 479 | 480 | return self._result() 481 | 482 | 483 | def szMargins(self, retry=3, pause=0.001): 484 | """ 485 | 深市融资融券历史数据 486 | Parameters 487 | -------- 488 | retry : int, 默认 3 489 | 如遇网络等问题重复执行的次数 490 | pause : int, 默认 0 491 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 492 | 493 | Return 494 | ------ 495 | DataFrame or List: [{'date':, 'close':, ...}, ...] 496 | date: 日期 497 | close: 深证成指收盘点数 498 | zdf: 深证成指收盘涨跌幅(%) 499 | rzye: 融资余额(元) 500 | rzyezb: 融资余额占比(%) 501 | rzmre: 融资买入额(元) 502 | rzche: 融资偿还额(元) 503 | rzjmre: 融资净买入额(元) 504 | rqye: 融券余额(元) 505 | rqyl: 融券余量(股) 506 | rqmcl: 融券卖出量(股) 507 | rqchl: 融券偿还量(股) 508 | rqjmcl: 融券净卖出量(股) 509 | rzrqye: 融资融券余额(元) 510 | rzrqyecz: 融资融券余额差值(元) 511 | """ 512 | self._data = pd.DataFrame() 513 | 514 | self._writeHead() 515 | 516 | self._data = self.__handleMargins(self._data, 1, 'SZ', Utility.random(8), cf.MAR_COLS, retry, pause) 517 | self._data.rename(columns={'tdate':'date'}, inplace=True) 518 | 519 | return self._result() 520 | 521 | 522 | def __handleMargins(self, dataArr, page, market, randInt, column, retry, pause): 523 | self._writeConsole() 524 | 525 | for _ in range(retry): 526 | time.sleep(pause) 527 | 528 | try: 529 | request = self._session.get( cf.MAR_URL % (page, market, randInt) ) 530 | text = request.text.split('=')[1] 531 | text = text.replace('{pages:', '{"pages":').replace(',data:', ',"data":').replace('T00:00:00', '').replace('"-"', '0') 532 | dataDict = Utility.str2Dict(text) 533 | data = dataDict['data'] 534 | df = pd.DataFrame(data, columns=column) 535 | 536 | df['close'] = df['close'].map(cf.FORMAT) 537 | df['rzyezb'] = df['rzyezb'].astype(float) 538 | 539 | dataArr = dataArr.append(df, ignore_index=True) 540 | if page < dataDict['pages']: 541 | dataArr = self.__handleMargins(dataArr, page+1, market, randInt, column, retry, pause) 542 | except Exception as e: 543 | print(e) 544 | else: 545 | return dataArr 546 | 547 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 548 | 549 | 550 | def marginDetailsAllByDate(self, date, retry=3, pause=0.001): 551 | """ 552 | 按日期获取两市融资融券明细列表 553 | Parameters 554 | -------- 555 | date : string 556 | 选择日期 format:YYYY-MM-DD 557 | retry : int, 默认 3 558 | 如遇网络等问题重复执行的次数 559 | pause : int, 默认 0 560 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 561 | 562 | Return 563 | ------ 564 | DataFrame or List: [{'code':, 'name':, ...}, ...] 565 | code: 股票代码 566 | name: 名称 567 | rzye: 当日融资余额(元) 568 | rzyezb: 当日融资余额占比(%) 569 | rzmre: 当日融资买入额(元) 570 | rzche: 当日融资偿还额(元) 571 | rzjmre: 当日融资净买入额(元) 572 | rqye: 当日融券余额(元) 573 | rqyl: 当日融券余量(股) 574 | rqmcl: 当日融券卖出量(股) 575 | rqchl: 当日融券偿还量(股) 576 | rqjmcl: 当日融券净卖出量(股) 577 | rzrqye: 当日融资融券余额(元) 578 | rzrqyecz: 当日融资融券余额差值(元) 579 | """ 580 | self._data = pd.DataFrame() 581 | 582 | self._writeHead() 583 | 584 | self._data = self.__handleMarginDetailsAllByDate(self._data, date, 1, Utility.random(8), retry, pause) 585 | self._data.rename(columns={'scode':'code', 'sname':'name'}, inplace=True) 586 | 587 | return self._result() 588 | 589 | 590 | def __handleMarginDetailsAllByDate(self, dataArr, date, page, randInt, retry, pause): 591 | self._writeConsole() 592 | 593 | for _ in range(retry): 594 | time.sleep(pause) 595 | 596 | try: 597 | request = self._session.get(cf.MAR_BOTH_DETAIL % (date, page, randInt)) 598 | text = request.text.split('=')[1] 599 | text = text.replace('{pages:', '{"pages":').replace(',data:', ',"data":').replace('"-"', '0') 600 | dataDict = Utility.str2Dict(text) 601 | data = dataDict['data'] 602 | df = pd.DataFrame(data, columns=cf.MAR_DET_All_COLS) 603 | 604 | df['date'] = date 605 | df['rzyezb'] = df['rzyezb'].astype(float) 606 | 607 | dataArr = dataArr.append(df, ignore_index=True) 608 | if page < dataDict['pages']: 609 | dataArr = self.__handleMarginDetailsAllByDate(dataArr, date, page+1, randInt, retry, pause) 610 | except Exception as e: 611 | print(e) 612 | else: 613 | return dataArr 614 | 615 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 616 | 617 | 618 | def marginTotal(self, retry=3, pause=0.001): 619 | """ 620 | 两市合计融资融券历史数据 621 | Parameters 622 | -------- 623 | retry : int, 默认 3 624 | 如遇网络等问题重复执行的次数 625 | pause : int, 默认 0 626 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 627 | 628 | Return 629 | ------ 630 | DataFrame or List: [{'date':, 'close':, ...}, ...] 631 | date: 日期 632 | close: 沪深300收盘点数 633 | zdf: 沪深300收盘涨跌幅(%) 634 | rzye: 融资余额(元) 635 | rzyezb: 融资余额占比(%) 636 | rzmre: 融资买入额(元) 637 | rzche: 融资偿还额(元) 638 | rzjmre: 融资净买入额(元) 639 | rqye: 融券余额(元) 640 | rqyl: 融券余量(股) 641 | rqmcl: 融券卖出量(股) 642 | rqchl: 融券偿还量(股) 643 | rqjmcl: 融券净卖出量(股) 644 | rzrqye: 融资融券余额(元) 645 | rzrqyecz: 融资融券余额差值(元) 646 | """ 647 | self._data = pd.DataFrame() 648 | 649 | self._writeHead() 650 | 651 | self._data = self.__handleMarginTotal(self._data, 1, Utility.random(8), retry, pause) 652 | self._data.rename(columns={'tdate':'date'}, inplace=True) 653 | 654 | return self._result() 655 | 656 | 657 | def __handleMarginTotal(self, dataArr, page, randInt, retry, pause): 658 | self._writeConsole() 659 | 660 | for _ in range(retry): 661 | time.sleep(pause) 662 | 663 | try: 664 | request = self._session.get(cf.MAR_TOTAL_URL % (page, randInt), timeout=10) 665 | text = request.text.split('=')[1] 666 | text = text.replace('{pages:', '{"pages":').replace(',data:', ',"data":').replace('T00:00:00', '').replace('"-"', '0') 667 | dataDict = Utility.str2Dict(text) 668 | data = dataDict['data'] 669 | df = pd.DataFrame(data, columns=cf.MAR_TOTAL_COLS) 670 | 671 | df['close'] = df['close'].map(cf.FORMAT) 672 | df['rzyezb'] = df['rzyezb'].astype(float) 673 | 674 | dataArr = dataArr.append(df, ignore_index=True) 675 | if page < dataDict['pages']: 676 | dataArr = self.__handleMarginTotal(dataArr, page+1, randInt, retry, pause) 677 | except Exception as e: 678 | print(e) 679 | else: 680 | return dataArr 681 | 682 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 683 | 684 | -------------------------------------------------------------------------------- /GuGu/stockdata.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | 股票交易类 4 | Created on 2018/12/26 5 | @author: TabQ 6 | @group : GuGu 7 | @contact: 16621596@qq.com 8 | """ 9 | from __future__ import division 10 | 11 | import time 12 | import json 13 | import re 14 | import lxml.html 15 | from lxml import etree 16 | import pandas as pd 17 | from pandas.compat import StringIO 18 | from base import Base, cf 19 | from utility import Utility 20 | 21 | class StockData(Base): 22 | def __init__(self, code=None, pandas=True, inter=True): 23 | Base.__init__(self, pandas, inter) 24 | self.__code = code 25 | 26 | 27 | def history(self, start='', end='', ktype='D', autype='qfq', index=False, retry=3, pause=0.001): 28 | """ 29 | 获取股票交易历史数据 30 | --------- 31 | Parameters: 32 | start:string 33 | 开始日期 format:YYYY-MM-DD 为空时取上市首日 34 | end:string 35 | 结束日期 format:YYYY-MM-DD 为空时取最近一个交易日 36 | autype:string 37 | 复权类型,qfq-前复权 hfq-后复权 None-不复权,默认为qfq 38 | ktype:string 39 | 数据类型,D=日k线 W=周 M=月 5=5分钟 15=15分钟 30=30分钟 60=60分钟,默认为D 40 | retry: int, 默认 3 41 | 如遇网络等问题重复执行的次数 42 | pause : int, 默认 0 43 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 44 | return 45 | ------- 46 | DataFrame or list: [{'date':, 'open':, ...}, ...] 47 | date 交易日期 (index) 48 | open 开盘价 49 | high 最高价 50 | close 收盘价 51 | low 最低价 52 | volume 成交量 53 | code 股票代码 54 | """ 55 | self._data = pd.DataFrame() 56 | 57 | url = '' 58 | dataflag = '' 59 | symbol = cf.INDEX_SYMBOL[self.__code] if index else Utility.symbol(self.__code) 60 | autype = '' if autype is None else autype 61 | 62 | if (start is not None) & (start != ''): 63 | end = Utility.getToday() if end is None or end == '' else end 64 | 65 | if ktype.upper() in cf.K_LABELS: 66 | fq = autype if autype is not None else '' 67 | if self.__code[:1] in ('1', '5') or index: 68 | fq = '' 69 | 70 | kline = '' if autype is None else 'fq' 71 | if (start is None or start == '') & (end is None or end == ''): 72 | # http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?_var=kline_dayqfq¶m=sh600000,day,,,640,qfq&r=0.73327461855388564 73 | urls = [cf.HISTORY_URL % (kline, fq, symbol, cf.TT_K_TYPE[ktype.upper()], start, end, fq, Utility.random(17))] 74 | else: 75 | years = Utility.ttDates(start, end) 76 | urls = [] 77 | for year in years: 78 | startdate = str(year) + '-01-01' 79 | enddate = str(year+1) + '-12-31' 80 | # http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?_var=kline_dayqfq2008¶m=sh600000,day,2008-01-01,2009-12-31,640,qfq&r=0.73327461855388564 81 | url = cf.HISTORY_URL % (kline, fq+str(year), symbol, cf.TT_K_TYPE[ktype.upper()], startdate, enddate, fq, Utility.random(17)) 82 | urls.append(url) 83 | dataflag = '%s%s'%(fq, cf.TT_K_TYPE[ktype.upper()]) # qfqday 84 | elif ktype in cf.K_MIN_LABELS: 85 | # http://ifzq.gtimg.cn/appstock/app/kline/mkline?param=sh600000,m30,,640&_var=m30_today&r=0.5537154641907898 86 | urls = [cf.HISTORY_MIN_URL % (symbol, ktype, ktype, Utility.random(16))] 87 | dataflag = 'm%s'%ktype # m30 88 | else: 89 | raise TypeError('ktype input error.') 90 | 91 | for url in urls: 92 | self._data = self._data.append(self.__handleHistory(url, dataflag, symbol, index, ktype, retry, pause), ignore_index=True) 93 | if ktype not in cf.K_MIN_LABELS: 94 | if ((start is not None) & (start != '')) & ((end is not None) & (end != '')): 95 | self._data = self._data[(self._data.date >= start) & (self._data.date <= end)] 96 | 97 | return self._result() 98 | 99 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 100 | 101 | 102 | def __handleHistory(self, url, dataflag='', symbol='', index = False, ktype = '', retry=3, pause=0.001): 103 | for _ in range(retry): 104 | time.sleep(pause) 105 | 106 | try: 107 | request = self._session.get(url, timeout=10) 108 | if self._PY3: 109 | request.encoding = 'gbk' 110 | lines = request.text 111 | if len(lines) < 100: #no data 112 | return None 113 | except Exception as e: 114 | print(e) 115 | else: 116 | lines = lines.split('=')[1] 117 | reg = re.compile(r',{"nd.*?}') 118 | lines = re.subn(reg, '', lines) 119 | js = json.loads(lines[0]) 120 | dataflag = dataflag if dataflag in list(js['data'][symbol].keys()) else cf.TT_K_TYPE[ktype.upper()] 121 | 122 | if ktype in cf.K_MIN_LABELS: 123 | for value in js['data'][symbol][dataflag]: 124 | value.pop() 125 | value.pop() 126 | 127 | df = pd.DataFrame(js['data'][symbol][dataflag], columns=cf.KLINE_TT_COLS) 128 | df['code'] = symbol if index else self.__code 129 | if ktype in cf.K_MIN_LABELS: 130 | df['date'] = df['date'].map(lambda x: '%s-%s-%s %s:%s'%(x[0:4], x[4:6], x[6:8], x[8:10], x[10:12])) 131 | for col in df.columns[1:6]: 132 | df[col] = df[col].astype(float) 133 | 134 | return df 135 | 136 | 137 | def xrxd(self, date='', retry=3, pause=0.001): 138 | """ 139 | 获取股票除权除息信息 140 | Parameters 141 | ------ 142 | date: string 143 | format:YYYY-MM-DD 144 | retry: int, 默认 3 145 | 如遇网络等问题重复执行的次数 146 | pause : int, 默认 0 147 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 148 | 149 | return 150 | ------ 151 | Dict or None 152 | nd, 对应年份 153 | fh_sh, 分红数额 154 | djr, 股权登记日 155 | cqr, 除权日 156 | FHcontent, 除权除息信息 157 | """ 158 | data = None 159 | 160 | if not date: 161 | date = Utility.getToday() 162 | 163 | symbol = Utility.symbol(self.__code) 164 | 165 | for _ in range(retry): 166 | time.sleep(pause) 167 | 168 | url = cf.HISTORY_URL % ('fq', 'qfq', symbol, 'day', date, date, 'qfq', Utility.random(17)) 169 | try: 170 | request = self._session.get(url, timeout=10) 171 | pattern = re.compile(r'({"nd".+?})') 172 | result = re.search(pattern, request.text) 173 | if result: 174 | data = eval(result.group(1)) 175 | except Exception as e: 176 | print(str(e)) 177 | 178 | return data 179 | 180 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 181 | 182 | 183 | def realtime(self): 184 | """ 185 | 获取实时交易数据 getting real time quotes data 186 | 用于跟踪交易情况(本次执行的结果-上一次执行的数据) 187 | Parameters 188 | ------ 189 | stockdata(code) code : string, array-like object (list, tuple, Series). 190 | return 191 | ------- 192 | DataFrame 实时交易数据 or list: [{'name':, 'open':, ...}, ...] 193 | 属性:0:name,股票名字 194 | 1:open,今日开盘价 195 | 2:pre_close,昨日收盘价 196 | 3:price,当前价格 197 | 4:high,今日最高价 198 | 5:low,今日最低价 199 | 6:bid,竞买价,即“买一”报价 200 | 7:ask,竞卖价,即“卖一”报价 201 | 8:volumn,成交量 maybe you need do volumn/100 202 | 9:amount,成交金额(元 CNY) 203 | 10:b1_v,委买一(笔数 bid volume) 204 | 11:b1_p,委买一(价格 bid price) 205 | 12:b2_v,“买二” 206 | 13:b2_p,“买二” 207 | 14:b3_v,“买三” 208 | 15:b3_p,“买三” 209 | 16:b4_v,“买四” 210 | 17:b4_p,“买四” 211 | 18:b5_v,“买五” 212 | 19:b5_p,“买五” 213 | 20:a1_v,委卖一(笔数 ask volume) 214 | 21:a1_p,委卖一(价格 ask price) 215 | ... 216 | 30:date,日期; 217 | 31:time,时间; 218 | """ 219 | self._data = pd.DataFrame() 220 | 221 | symbols_list = '' 222 | if isinstance(self.__code, list) or isinstance(self.__code, set) or isinstance(self.__code, tuple) or isinstance(self.__code, pd.Series): 223 | for code in self.__code: 224 | symbols_list += Utility.symbol(code) + ',' 225 | else: 226 | symbols_list = Utility.symbol(self.__code) 227 | symbols_list = symbols_list[:-1] if len(symbols_list) > 8 else symbols_list 228 | 229 | # http://hq.sinajs.cn/rn=4879967949085&list=sh600000,sh600004 230 | request = self._session.get( cf.LIVE_DATA_URL % (Utility.random(), symbols_list), timeout=10 ) 231 | request.encoding = 'gbk' 232 | reg = re.compile(r'\="(.*?)\";') 233 | data = reg.findall(request.text) 234 | regSym = re.compile(r'(?:sh|sz)(.*?)\=') 235 | syms = regSym.findall(request.text) 236 | data_list = [] 237 | syms_list = [] 238 | for index, row in enumerate(data): 239 | if len(row)>1: 240 | data_list.append([astr for astr in row.split(',')]) 241 | syms_list.append(syms[index]) 242 | if len(syms_list) == 0: 243 | return None 244 | 245 | self._data = pd.DataFrame(data_list, columns=cf.LIVE_DATA_COLS) 246 | self._data = self._data.drop('s', axis=1) 247 | self._data['code'] = syms_list 248 | ls = [cls for cls in self._data.columns if '_v' in cls] 249 | for txt in ls: 250 | self._data[txt] = self._data[txt].map(lambda x : x[:-2]) 251 | 252 | return self._result() 253 | 254 | 255 | def historyTicks(self, date=None, retry=3, pause=0.001): 256 | """ 257 | 获取历史分笔明细数据 258 | Parameters 259 | ------ 260 | date : string 261 | 日期 format:YYYY-MM-DD, 默认为前一个交易日 262 | retry : int, 默认 3 263 | 如遇网络等问题重复执行的次数 264 | pause : int, 默认 0 265 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 266 | return 267 | ------- 268 | DataFrame 当日所有股票交易数据(DataFrame) or list: [{'time':, 'price':, ...}, ...] 269 | 属性:成交时间、成交价格、价格变动,成交手、成交金额(元),买卖类型 270 | """ 271 | self._data = pd.DataFrame() 272 | 273 | symbol = Utility.symbol(self.__code) 274 | date = Utility.lastTradeDate() if date is None else date 275 | 276 | try: 277 | self._writeHead() 278 | 279 | page = 1 280 | while(True): 281 | # http://vip.stock.finance.sina.com.cn/quotes_service/view/vMS_tradehistory.php?symbol=sh600000&date=2018-12-26&page=1 282 | # http://market.finance.sina.com.cn/transHis.php?date=2019-01-25&symbol=sh600000&page=1 283 | url = cf.HISTORY_TICKS_URL % (date, symbol, page) 284 | tick_data = self.__handleTicks(url, cf.HISTORY_TICK_COLUMNS, retry, pause) 285 | if tick_data is not None: 286 | self._data = self._data.append(tick_data, ignore_index=True) 287 | page = page + 1 288 | else: 289 | break 290 | except Exception as er: 291 | print(str(er)) 292 | else: 293 | return self._result() 294 | 295 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 296 | 297 | 298 | def __handleTicks(self, url, column, retry, pause): 299 | self._writeConsole() 300 | 301 | for _ in range(retry): 302 | time.sleep(pause) 303 | 304 | try: 305 | html = lxml.html.parse(url) 306 | res = html.xpath('//table[@id=\"datatbl\"]/tbody/tr') 307 | if not res: 308 | return None 309 | 310 | if self._PY3: 311 | sarr = [etree.tostring(node).decode('utf-8') for node in res] 312 | else: 313 | sarr = [etree.tostring(node) for node in res] 314 | sarr = ''.join(sarr) 315 | sarr = '%s
'%sarr 316 | sarr = sarr.replace('--', '0') 317 | df = pd.read_html(StringIO(sarr), parse_dates=False) 318 | df = pd.read_html(StringIO(sarr), parse_dates=False)[0] 319 | df.columns = column 320 | if 'pchange' in column: 321 | df['pchange'] = df['pchange'].map(lambda x : x.replace('%', '')) 322 | except Exception as e: 323 | print(e) 324 | else: 325 | return df 326 | 327 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 328 | 329 | def todayTicks(self, retry=3, pause=0.001): 330 | """ 331 | 获取当日分笔明细数据 332 | Parameters 333 | ------ 334 | retry : int, 默认 3 335 | 如遇网络等问题重复执行的次数 336 | pause : int, 默认 0 337 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 338 | return 339 | ------- 340 | DataFrame 当日所有股票交易数据(DataFrame) or list: [{'time':, 'price':, ...}, ...] 341 | 属性:成交时间、成交价格、涨跌幅、价格变动,成交手、成交金额(元),买卖类型 342 | """ 343 | self._data = pd.DataFrame() 344 | 345 | if self.__code is None or len(self.__code)!=6 : 346 | return None 347 | 348 | if not Utility.isTradeDay(): 349 | return None 350 | 351 | # 不到交易时间 352 | openTime = time.mktime(time.strptime(Utility.getToday() + ' 09:25:00', '%Y-%m-%d %H:%M:%S')) 353 | now = time.time() 354 | if now < openTime: 355 | return None 356 | 357 | symbol = Utility.symbol(self.__code) 358 | date = Utility.getToday() 359 | 360 | for _ in range(retry): 361 | time.sleep(pause) 362 | 363 | try: 364 | # http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_Transactions.getAllPageTime?date=2018-12-26&symbol=sh600000 365 | request = self._session.get( cf.TODAY_TICKS_PAGE_URL % (date, symbol), timeout=10 ) 366 | request.encoding = 'gbk' 367 | text = request.text[1:-1] 368 | data_dict = Utility.str2Dict(text) 369 | pages = len(data_dict['detailPages']) 370 | 371 | self._writeHead() 372 | for pNo in range(1, pages+1): 373 | # http://vip.stock.finance.sina.com.cn/quotes_service/view/vMS_tradedetail.php?symbol=sh600000&date=2018-12-26&page=1 374 | url = cf.TODAY_TICKS_URL % (symbol, date, pNo) 375 | self._data = self._data.append(self.__handleTicks(url, cf.TODAY_TICK_COLUMNS, retry, pause), ignore_index=True) 376 | except Exception as e: 377 | print(str(e)) 378 | else: 379 | return self._result() 380 | 381 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 382 | 383 | 384 | def bigDeal(self, date=None, vol=400, retry=3, pause=0.001): 385 | """ 386 | 获取大单数据 387 | Parameters 388 | ------ 389 | date:string 390 | 日期 format:YYYY-MM-DD 391 | retry : int, 默认 3 392 | 如遇网络等问题重复执行的次数 393 | pause : int, 默认 0 394 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 395 | return 396 | ------- 397 | DataFrame 当日所有股票交易数据(DataFrame) or list: [{'code':, 'name', ...}, ...] 398 | 属性:股票代码 股票名称 交易时间 价格 成交量 前一笔价格 类型(买、卖、中性盘) 399 | """ 400 | self._data = pd.DataFrame() 401 | 402 | if self.__code is None or len(self.__code) != 6 or date is None: 403 | return None 404 | 405 | symbol = Utility.symbol(self.__code) 406 | vol = vol*100 407 | 408 | for _ in range(retry): 409 | time.sleep(pause) 410 | 411 | try: 412 | # http://vip.stock.finance.sina.com.cn/quotes_service/view/cn_bill_download.php?symbol=sh600000&num=60&page=1&sort=ticktime&asc=0&volume=40000&amount=0&type=0&day=2018-12-26 413 | request = self._session.get( cf.SINA_DD % (symbol, vol, date), timeout=10 ) 414 | request.encoding = 'gbk' 415 | lines = request.text 416 | if len(lines) < 100: 417 | return None 418 | self._data = pd.read_csv(StringIO(lines), names=cf.SINA_DD_COLS, skiprows=[0]) 419 | if self._data is not None: 420 | self._data['code'] = self._data['code'].map(lambda x: x[2:]) 421 | except Exception as e: 422 | print(e) 423 | else: 424 | return self._result() 425 | 426 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 427 | 428 | 429 | -------------------------------------------------------------------------------- /GuGu/stockinfo.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | 股票信息类 4 | Created on 2019/01/02 5 | @author: TabQ 6 | @group : GuGu 7 | @contact: 16621596@qq.com 8 | """ 9 | from __future__ import division 10 | 11 | import time 12 | import pandas as pd 13 | from pandas.compat import StringIO 14 | import lxml.html 15 | from lxml import etree 16 | import re 17 | from utility import Utility 18 | from base import Base, cf 19 | 20 | class StockInfo(Base): 21 | def stockProfiles(self, retry=3, pause=0.001): 22 | """ 23 | 获取上市公司基本情况 24 | Parameters 25 | -------- 26 | retry : int, 默认 3 27 | 如遇网络等问题重复执行的次数 28 | pause : int, 默认 0 29 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 30 | 31 | Return 32 | -------- 33 | DataFrame or List: [{'code':, 'name':, ...}, ...] 34 | code,代码 35 | name,名称 36 | city,所在城市 37 | staff,员工人数 38 | date,上市日期 39 | industry,行业分类 40 | pro_type,产品类型 41 | main,主营业务 42 | """ 43 | self._data = pd.DataFrame() 44 | 45 | self._writeHead() 46 | 47 | date = '%s-12-31' % Utility.getYear() 48 | self._data = pd.DataFrame() 49 | 50 | self._data = self.__handleStockProfiles(self._data, date, 1, retry, pause) 51 | 52 | return self._result() 53 | 54 | 55 | def __handleStockProfiles(self, dataArr, date, page, retry, pause): 56 | self._writeConsole() 57 | 58 | for _ in range(retry): 59 | time.sleep(pause) 60 | 61 | try: 62 | html = lxml.html.parse(cf.ALL_STOCK_PROFILES_URL % (date, page)) 63 | res = html.xpath('//table[@id="myTable04"]/tbody/tr') 64 | if not res: 65 | return dataArr 66 | 67 | if self._PY3: 68 | sarr = [etree.tostring(node).decode('utf-8') for node in res] 69 | else: 70 | sarr = [etree.tostring(node) for node in res] 71 | sarr = ''.join(sarr) 72 | sarr = '%s
' % sarr 73 | 74 | df = pd.read_html(sarr)[0] 75 | df = df.drop([0, 3, 5, 6, 7, 10, 11], axis = 1) 76 | df.columns = cf.ALL_STOCK_PROFILES_COLS 77 | df['code'] = df['code'].map(lambda x: str(x).zfill(6)) 78 | 79 | dataArr = dataArr.append(df, ignore_index=True) 80 | except Exception as e: 81 | print(e) 82 | else: 83 | return self.__handleStockProfiles(dataArr, date, page+1, retry, pause) 84 | 85 | 86 | def report(self, year, quarter, retry=3, pause=0.001): 87 | """ 88 | 获取业绩报表数据 89 | Parameters 90 | -------- 91 | year:int 年度 e.g:2014 92 | quarter:int 季度 :1、2、3、4,只能输入这4个季度 93 | 说明:由于是从网站获取的数据,需要一页页抓取,速度取决于您当前网络速度 94 | retry : int, 默认 3 95 | 如遇网络等问题重复执行的次数 96 | pause : int, 默认 0.001 97 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 98 | Return 99 | -------- 100 | DataFrame or List: [{'code':, 'name':, ...}, ...] 101 | code,代码 102 | name,名称 103 | eps,每股收益 104 | eps_yoy,每股收益同比(%) 105 | bvps,每股净资产 106 | roe,净资产收益率(%) 107 | epcf,每股现金流量(元) 108 | net_profits,净利润(万元) 109 | profits_yoy,净利润同比(%) 110 | distrib,分配方案 111 | report_date,发布日期 112 | """ 113 | self._data = pd.DataFrame() 114 | 115 | if Utility.checkQuarter(year, quarter) is True: 116 | self._writeHead() 117 | 118 | # http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/mainindex/index.phtml?s_i=&s_a=&s_c=&reportdate=2018&quarter=3&p=1&num=60 119 | self._data = self.__parsePage(cf.REPORT_URL, year, quarter, 1, cf.REPORT_COLS, pd.DataFrame(), retry, pause, 11) 120 | if self._data is not None: 121 | self._data['code'] = self._data['code'].map(lambda x:str(x).zfill(6)) 122 | 123 | return self._result() 124 | 125 | def profit(self, year, quarter, retry=3, pause=0.001): 126 | """ 127 | 获取盈利能力数据 128 | Parameters 129 | -------- 130 | year:int 年度 e.g:2014 131 | quarter:int 季度 :1、2、3、4,只能输入这4个季度 132 | 说明:由于是从网站获取的数据,需要一页页抓取,速度取决于您当前网络速度 133 | retry : int, 默认 3 134 | 如遇网络等问题重复执行的次数 135 | pause : int, 默认 0.001 136 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 137 | Return 138 | -------- 139 | DataFrame or List: [{'code':, 'name':, ...}, ...] 140 | code,代码 141 | name,名称 142 | roe,净资产收益率(%) 143 | net_profit_ratio,净利率(%) 144 | gross_profit_rate,毛利率(%) 145 | net_profits,净利润(万元) 146 | eps,每股收益 147 | business_income,营业收入(百万元) 148 | bips,每股主营业务收入(元) 149 | """ 150 | self._data = pd.DataFrame() 151 | 152 | if Utility.checkQuarter(year, quarter) is True: 153 | self._writeHead() 154 | 155 | # http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/profit/index.phtml?s_i=&s_a=&s_c=&reportdate=2018&quarter=3&p=1&num=60 156 | self._data = self.__parsePage(cf.PROFIT_URL, year, quarter, 1, cf.PROFIT_COLS, pd.DataFrame(), retry, pause) 157 | if self._data is not None: 158 | self._data['code'] = self._data['code'].map(lambda x: str(x).zfill(6)) 159 | 160 | return self._result() 161 | 162 | def operation(self, year, quarter, retry=3, pause=0.001): 163 | """ 164 | 获取营运能力数据 165 | Parameters 166 | -------- 167 | year:int 年度 e.g:2014 168 | quarter:int 季度 :1、2、3、4,只能输入这4个季度 169 | 说明:由于是从网站获取的数据,需要一页页抓取,速度取决于您当前网络速度 170 | retry : int, 默认 3 171 | 如遇网络等问题重复执行的次数 172 | pause : int, 默认 0.001 173 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 174 | Return 175 | -------- 176 | DataFrame or List: [{'code':, 'name':, ...}, ...] 177 | code,代码 178 | name,名称 179 | arturnover,应收账款周转率(次) 180 | arturndays,应收账款周转天数(天) 181 | inventory_turnover,存货周转率(次) 182 | inventory_days,存货周转天数(天) 183 | currentasset_turnover,流动资产周转率(次) 184 | currentasset_days,流动资产周转天数(天) 185 | """ 186 | self._data = pd.DataFrame() 187 | 188 | if Utility.checkQuarter(year, quarter) is True: 189 | self._writeHead() 190 | 191 | # http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/operation/index.phtml?s_i=&s_a=&s_c=&reportdate=2018&quarter=3&p=1&num=60 192 | self._data = self.__parsePage(cf.OPERATION_URL, year, quarter, 1, cf.OPERATION_COLS, pd.DataFrame(), retry, pause) 193 | if self._data is not None: 194 | self._data['code'] = self._data['code'].map(lambda x: str(x).zfill(6)) 195 | 196 | return self._result() 197 | 198 | def growth(self, year, quarter, retry=3, pause=0.001): 199 | """ 200 | 获取成长能力数据 201 | Parameters 202 | -------- 203 | year:int 年度 e.g:2014 204 | quarter:int 季度 :1、2、3、4,只能输入这4个季度 205 | 说明:由于是从网站获取的数据,需要一页页抓取,速度取决于您当前网络速度 206 | retry : int, 默认 3 207 | 如遇网络等问题重复执行的次数 208 | pause : int, 默认 0.001 209 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 210 | Return 211 | -------- 212 | DataFrame or List: [{'code':, 'name':, ...}, ...] 213 | code,代码 214 | name,名称 215 | mbrg,主营业务收入增长率(%) 216 | nprg,净利润增长率(%) 217 | nav,净资产增长率 218 | targ,总资产增长率 219 | epsg,每股收益增长率 220 | seg,股东权益增长率 221 | """ 222 | self._data = pd.DataFrame() 223 | 224 | if Utility.checkQuarter(year, quarter) is True: 225 | self._writeHead() 226 | 227 | # http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/grow/index.phtml?s_i=&s_a=&s_c=&reportdate=2018&quarter=3&p=1&num=60 228 | self._data = self.__parsePage(cf.GROWTH_URL, year, quarter, 1, cf.GROWTH_COLS, pd.DataFrame(), retry, pause) 229 | if self._data is not None: 230 | self._data['code'] = self._data['code'].map(lambda x: str(x).zfill(6)) 231 | 232 | return self._result() 233 | 234 | def debtPaying(self, year, quarter, retry=3, pause=0.001): 235 | """ 236 | 获取偿债能力数据 237 | Parameters 238 | -------- 239 | year:int 年度 e.g:2014 240 | quarter:int 季度 :1、2、3、4,只能输入这4个季度 241 | 说明:由于是从网站获取的数据,需要一页页抓取,速度取决于您当前网络速度 242 | retry : int, 默认 3 243 | 如遇网络等问题重复执行的次数 244 | pause : int, 默认 0.001 245 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 246 | Return 247 | -------- 248 | DataFrame or List: [{'code':, 'name':, ...}, ...] 249 | code,代码 250 | name,名称 251 | currentratio,流动比率 252 | quickratio,速动比率 253 | cashratio,现金比率 254 | icratio,利息支付倍数 255 | sheqratio,股东权益比率 256 | adratio,股东权益增长率 257 | """ 258 | self._data = pd.DataFrame() 259 | 260 | if Utility.checkQuarter(year, quarter) is True: 261 | self._writeHead() 262 | 263 | # http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/debtpaying/index.phtml?s_i=&s_a=&s_c=&reportdate=2018&quarter=3&p=1&num=60 264 | self._data = self.__parsePage(cf.DEBTPAYING_URL, year, quarter, 1, cf.DEBTPAYING_COLS, pd.DataFrame(), retry, pause) 265 | if self._data is not None: 266 | self._data['code'] = self._data['code'].map(lambda x: str(x).zfill(6)) 267 | 268 | return self._result() 269 | 270 | def cashFlow(self, year, quarter, retry=3, pause=0.001): 271 | """ 272 | 获取现金流量数据 273 | Parameters 274 | -------- 275 | year:int 年度 e.g:2014 276 | quarter:int 季度 :1、2、3、4,只能输入这4个季度 277 | 说明:由于是从网站获取的数据,需要一页页抓取,速度取决于您当前网络速度 278 | retry : int, 默认 3 279 | 如遇网络等问题重复执行的次数 280 | pause : int, 默认 0.001 281 | 重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题 282 | Return 283 | -------- 284 | DataFrame or List: [{'code':, 'name':, ...}, ...] 285 | code,代码 286 | name,名称 287 | cf_sales,经营现金净流量对销售收入比率 288 | rateofreturn,资产的经营现金流量回报率 289 | cf_nm,经营现金净流量与净利润的比率 290 | cf_liabilities,经营现金净流量对负债比率 291 | cashflowratio,现金流量比率 292 | """ 293 | self._data = pd.DataFrame() 294 | 295 | if Utility.checkQuarter(year, quarter) is True: 296 | self._writeHead() 297 | 298 | # http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/cashflow/index.phtml?s_i=&s_a=&s_c=&reportdate=2018&quarter=3&p=1&num=60 299 | self._data = self.__parsePage(cf.CASHFLOW_URL, year, quarter, 1, cf.CASHFLOW_COLS, pd.DataFrame(), retry, pause) 300 | if self._data is not None: 301 | self._data['code'] = self._data['code'].map(lambda x: str(x).zfill(6)) 302 | 303 | return self._result() 304 | 305 | def __parsePage(self, url, year, quarter, page, column, dataArr, retry, pause, drop_column=None): 306 | self._writeConsole() 307 | 308 | for _ in range(retry): 309 | time.sleep(pause) 310 | 311 | try: 312 | request = self._session.get( url % (year, quarter, page, cf.PAGE_NUM[1]), timeout=10 ) 313 | request.encoding = 'gbk' 314 | text = request.text.replace('--', '') 315 | html = lxml.html.parse(StringIO(text)) 316 | res = html.xpath("//table[@class=\"list_table\"]/tr") 317 | if self._PY3: 318 | sarr = [etree.tostring(node).decode('utf-8') for node in res] 319 | else: 320 | sarr = [etree.tostring(node) for node in res] 321 | sarr = ''.join(sarr) 322 | sarr = '%s
'%sarr 323 | df = pd.read_html(sarr)[0] 324 | if drop_column is not None: 325 | df = df.drop(drop_column, axis=1) 326 | df.columns = column 327 | dataArr = dataArr.append(df, ignore_index=True) 328 | nextPage = html.xpath('//div[@class=\"pages\"]/a[last()]/@onclick') 329 | if len(nextPage) > 0: 330 | page = re.findall(r'\d+', nextPage[0])[0] 331 | return self.__parsePage(url, year, quarter, page, column, dataArr, retry, pause, drop_column) 332 | else: 333 | return dataArr 334 | except Exception as e: 335 | print(e) 336 | 337 | raise IOError(cf.NETWORK_URL_ERROR_MSG) 338 | 339 | 340 | -------------------------------------------------------------------------------- /GuGu/utility.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | 公用类 4 | Created on 2019/01/15 5 | @author: TabQ 6 | @group : GuGu 7 | @contact: 16621596@qq.com 8 | """ 9 | 10 | import datetime 11 | import json 12 | import requests 13 | from requests.adapters import HTTPAdapter 14 | from urllib3.util import Retry 15 | import config as cf 16 | 17 | class Utility(): 18 | @staticmethod 19 | def str2Dict(string): 20 | string = eval(string, type('Dummy', (dict,), dict(__getitem__ = lambda s, n:n))()) 21 | string = json.dumps(string) 22 | 23 | return json.loads(string) 24 | 25 | 26 | @staticmethod 27 | def checkQuarter(year, quarter): 28 | if isinstance(year, str) or year < 1989 : 29 | raise TypeError(cf.DATE_CHK_MSG) 30 | elif quarter is None or isinstance(quarter, str) or quarter not in [1, 2, 3, 4]: 31 | raise TypeError(cf.DATE_CHK_Q_MSG) 32 | else: 33 | return True 34 | 35 | 36 | @staticmethod 37 | def checkLhbInput(last): 38 | if last not in [5, 10, 30, 60]: 39 | raise TypeError(cf.LHB_MSG) 40 | else: 41 | return True 42 | 43 | 44 | @staticmethod 45 | def symbol(code): 46 | """ 47 | 生成symbol代码标志 48 | """ 49 | if code in cf.INDEX_LABELS: 50 | return cf.INDEX_LIST[code] 51 | elif len(code) != 6 : 52 | return '' 53 | else: 54 | return 'sh%s' % code if code[:1] in ['5', '6', '9'] else 'sz%s' % code 55 | 56 | 57 | @staticmethod 58 | def random(n=13): 59 | from random import randint 60 | 61 | start = 10**(n-1) 62 | end = (10**n)-1 63 | 64 | return str(randint(start, end)) 65 | 66 | 67 | @staticmethod 68 | def getToday(): 69 | return str(datetime.datetime.today().date()) 70 | 71 | 72 | @staticmethod 73 | def getHour(): 74 | return datetime.datetime.today().hour 75 | 76 | 77 | @staticmethod 78 | def getMonth(): 79 | return datetime.datetime.today().month 80 | 81 | 82 | @staticmethod 83 | def getYear(): 84 | return datetime.datetime.today().year 85 | 86 | 87 | @staticmethod 88 | def getTodayLastYear(): 89 | return str(Utility.getTodayHistory(-365)) 90 | 91 | 92 | @staticmethod 93 | def getTodayLastMonth(): 94 | return str(Utility.getTodayHistory(-30)) 95 | 96 | 97 | @staticmethod 98 | def getTodayLastWeek(): 99 | return str(Utility.getTodayHistory(-7)) 100 | 101 | 102 | @staticmethod 103 | def getTodayHistory(days): 104 | return datetime.datetime.today().date() + datetime.timedelta(days) 105 | 106 | 107 | @staticmethod 108 | def diffDays(start=None, end=None): 109 | d1 = datetime.datetime.strptime(end, '%Y-%m-%d') 110 | d2 = datetime.datetime.strptime(start, '%Y-%m-%d') 111 | delta = d1 - d2 112 | 113 | return delta.days 114 | 115 | 116 | @staticmethod 117 | def ttDates(start='', end=''): 118 | startyear = int(start[0:4]) 119 | endyear = int(end[0:4]) 120 | dates = [d for d in range(startyear, endyear+1, 2)] 121 | 122 | return dates 123 | 124 | 125 | @staticmethod 126 | def lastTradeDate(): 127 | today = datetime.datetime.today().date() 128 | today=int(today.strftime("%w")) 129 | 130 | if today == 0: 131 | return Utility.getTodayHistory(-2) 132 | elif today == 1: 133 | return Utility.getTodayHistory(-3) 134 | else: 135 | return Utility.getTodayHistory(-1) 136 | 137 | 138 | @staticmethod 139 | def isHoliday(date=str(datetime.datetime.today().date())): 140 | """ 141 | 节假日判断 142 | Parameters 143 | ------ 144 | date: string 145 | 查询日期 format:YYYY-MM-DD 为空时取当前日期 146 | return 147 | ------ 148 | True or False 149 | """ 150 | session = requests.Session() 151 | retry = Retry(connect=5, backoff_factor=1) 152 | adapter = HTTPAdapter(max_retries=retry) 153 | session.mount('http://', adapter) 154 | session.keep_alive = False 155 | 156 | try: 157 | request = session.get( cf.HOLIDAY_URL % date, timeout=10 ) 158 | dataDict = json.loads(request.text) 159 | if dataDict['code'] != 0: 160 | raise IOError(cf.HOLIDAY_SERVE_ERR) 161 | elif dataDict['holiday'] is None: 162 | return False 163 | else: 164 | return True 165 | except Exception as e: 166 | print(e) 167 | 168 | 169 | @staticmethod 170 | def isTradeDay(date=str(datetime.datetime.today().date())): 171 | """ 172 | 交易日判断 173 | Parameters 174 | ------ 175 | date: string 176 | 查询日期 format:YYYY-MM-DD 为空时取当前日期 177 | return 178 | ------ 179 | True or False 180 | """ 181 | date = datetime.datetime.strptime(date, '%Y-%m-%d') 182 | w = int(date.strftime('%w')) 183 | 184 | if w in [6, 0]: 185 | return False 186 | else: 187 | return not Utility.isHoliday(date) 188 | 189 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gugu(股估) 2 | 3 | gugu是适用于量化工程及金融/投资领域数据分析的开源项目,通过对互联网上的公开数据进行采集、清洗和存储,完成了对股票/债券/基金等金融数据的统一调用和分析。其优点是速度快、可定制及高度的可复用性。您不仅可以将其作为单独的数据接口使用,还可以将其集成在您的项目中作为数据获取api进行调用。 4 | 5 | 大家可能之前用过类似的开源项目,本人之前在进行金融数据分析时也是使用其他开源项目获取数据的。然而现在很多开源项目变成了半开源甚至闭源,您需要获取许可才能使用或者需要积累积分才能获得更多的权限,这对于喜欢自己动手并自由定制的朋友会有很多的不便。为了充分发扬开源精神倡导自由软件理念同时为了自身项目需要本人开发了gugu,作为开源社区版的金融数据api,我们承诺gugu将永远开源并不做任何限制以提供给广大金融数据分析及量化爱好者免费使用! 6 | 7 | 8 | Dependencies: 9 | 10 | python 2.x/3.x 11 | 12 | pandas 13 | 14 | Installation 15 | 16 | 方式1: pip install gugu 17 | 18 | 方式2: python setup.py install 19 | 20 | 方式3:访问 https://pypi.python.org/pypi/gugu/ 下载安装 21 | 22 | Upgrade 23 | 24 | pip install gugu --upgrade 25 | 26 | Quick Start 27 | Example 1: 获取大盘指数行情 28 | import gugu as gg 29 | 30 | obj = gg.MarketData() # 创建市场数据类对象 31 | obj.index() # 获取大盘指数行情 32 | 33 | 获取结果: 34 | obj.output() # 打印结果 35 | 36 | 37 | code name change open preclose close high \ 38 | 0 sh000001 上证指数 1.30 2597.7777 2584.5724 2618.2323 2618.4765 39 | 1 sh000002 A股指数 1.30 2720.4606 2706.5853 2741.8442 2742.1007 40 | 2 sh000003 B股指数 1.18 271.9034 272.1140 275.3162 275.3162 41 | 3 sh000008 综合指数 0.61 2557.2054 2543.6348 2559.2362 2561.5066 42 | 4 sh000009 上证380 2.47 3892.8231 3880.0032 3975.8523 3976.0933 43 | 5 sh000010 上证180 1.07 7293.4790 7241.9589 7319.4839 7320.2873 44 | 6 sh000011 基金指数 0.90 5642.7602 5629.3801 5679.8086 5679.8339 45 | 7 sh000012 国债指数 0.01 170.9805 170.9642 170.9819 170.9892 46 | 8 sh000016 上证50 0.65 2502.1044 2483.4709 2499.6442 2503.5526 47 | 9 sh000017 新综指 1.30 2193.5148 2182.3286 2210.7524 2210.9592 48 | 10 sh000300 沪深300 1.43 3225.7335 3201.6331 3247.3971 3247.4956 49 | 11 sh000905 中证500 2.82 4189.1098 4176.4739 4294.2696 4295.0379 50 | 12 sz399001 深证成指 2.74 7524.6650 7479.2150 7683.9970 7684.1120 51 | 13 sz399002 深成指R 2.74 9097.7900 9042.8390 9290.4330 9290.5710 52 | 14 sz399003 成份B指 1.99 4442.0740 4445.4680 4534.0600 4534.0600 53 | 15 sz399004 深证100R 2.49 4063.9900 4029.3260 4129.5800 4130.6630 54 | 16 sz399005 中小板指 3.47 4866.8680 4836.6130 5004.4890 5004.4890 55 | 17 sz399006 创业板指 3.53 1233.3840 1227.9880 1271.2750 1272.1930 56 | 18 sz399008 中小300 3.27 947.0020 942.1740 973.0090 973.0090 57 | 19 sz399100 新 指 数 2.69 5850.7930 5819.9190 5976.5880 5976.5880 58 | 20 sz399101 中小板综 3.03 7393.8140 7358.2710 7581.1830 7581.1830 59 | 21 sz399106 深证综指 2.77 1281.1620 1274.7440 1309.9910 1309.9910 60 | 22 sz399107 深证A指 2.77 1339.4990 1332.7690 1369.6510 1369.6510 61 | 23 sz399108 深证B指 2.09 857.4780 856.8890 874.7950 874.7950 62 | 24 sz399333 中小板R 3.47 5412.0040 5378.3600 5565.0400 5565.0400 63 | 25 sz399606 创业板R 3.53 1292.0270 1286.3740 1331.7190 1332.6810 64 | 65 | low volume amount 66 | 0 2590.5543 131986850 1115.2263 67 | 1 2712.8352 131757820 1114.1248 68 | 2 271.9034 229031 1.1015 69 | 3 2538.8951 35179591 275.8689 70 | 4 3892.8231 33132080 260.5541 71 | 5 7247.4721 45958066 529.0995 72 | 6 5637.2069 41987625 243.7168 73 | 7 170.9733 135921 1.3286 74 | 8 2479.3375 21950054 293.6402 75 | 9 2187.3669 130134579 1096.8641 76 | 10 3207.7168 82019641 947.6641 77 | 11 4189.1098 66099677 476.9347 78 | 12 7517.8340 18099026538 1502.2290 79 | 13 9089.5310 8948052337 883.8526 80 | 14 4442.0740 6063605 0.4918 81 | 15 4045.8820 3531953166 453.0613 82 | 16 4866.4270 2440239019 270.5996 83 | 17 1233.3840 1566379186 196.9856 84 | 18 947.0020 4974249025 426.8148 85 | 19 5850.7930 17446832212 1472.6754 86 | 20 7393.8140 8257066751 651.9033 87 | 21 1281.1620 18099026538 1502.2290 88 | 22 1339.4990 18082520936 1501.3883 89 | 23 857.4780 16505602 0.8408 90 | 24 5411.5130 2440239019 270.5996 91 | 25 1292.0270 1566379186 196.9856 92 | 93 | Example 2: 获取指数ETF及其相关数据 94 | obj = gg.MarketData() # 创建市场数据类对象 95 | obj.indexETF() # 获取指数ETF及其相关数据 96 | 97 | 获取结果: 98 | obj.output() # 打印结果 99 | 100 | 101 | fund_id fund_nm index_id creation_unit amount unit_total \ 102 | 0 159901 深100ETF 399330 20.0 101937.0 36.95 103 | 1 159902 中 小 板 399005 50.0 74693.0 18.28 104 | 2 159903 深成ETF 399001 300.0 44815.0 3.74 105 | 3 159905 深红利 399324 50.0 52744.0 7.64 106 | 4 159906 深成长 399346 100.0 14364.0 0.95 107 | 5 159907 中小300 399008 150.0 17660.0 1.79 108 | 6 159908 博时创业 399006 150.0 3623.0 0.40 109 | 7 159909 深TMT 399610 25.0 1552.0 0.52 110 | 8 159910 深F120 399702 100.0 48091.0 7.10 111 | 9 159911 民营ETF 399337 50.0 1307.0 0.39 112 | .. ... ... ... ... ... ... 113 | 110 512660 军工ETF 399967 100.0 111508.0 7.25 114 | 111 512680 军工基金 399967 60.0 35932.0 2.29 115 | 112 512700 银行基金 399986 20.0 11058.0 1.11 116 | 113 512800 银行ETF 399986 30.0 57254.0 5.55 117 | 114 512810 军工行业 399967 100.0 8353.0 0.51 118 | 115 512880 证券ETF 399975 100.0 389729.0 30.94 119 | 116 512900 证券基金 399975 30.0 60671.0 4.64 120 | 117 512980 传媒ETF 399971 100.0 110272.0 7.29 121 | 118 513600 恒指ETF NaN 100.0 2883.0 0.72 122 | 119 513660 恒生通 NaN 100.0 33500.0 8.40 123 | 124 | unit_incr price volume increase_rt estimate_value discount_rt \ 125 | 0 0.33 3.625 18697.36 2.57 3.6215 0.10 126 | 1 -0.05 2.448 6731.85 3.51 2.4471 0.04 127 | 2 0.00 0.835 77.66 1.83 0.8382 -0.37 128 | 3 0.09 1.448 594.53 1.69 1.4477 0.03 129 | 4 0.00 0.663 9.30 2.31 0.6582 0.30 130 | 5 0.00 1.012 55.09 2.43 1.0128 -0.05 131 | 6 -0.01 1.097 28.92 3.49 1.0976 0.02 132 | 7 0.00 3.375 3.50 1.75 3.4384 -1.79 133 | 8 0.19 1.476 128.26 1.72 1.4767 -0.04 134 | 9 0.00 2.970 4.67 2.73 2.9834 -0.42 135 | .. ... ... ... ... ... ... 136 | 110 0.08 0.650 2920.85 2.20 0.6495 0.08 137 | 111 0.01 0.637 20.94 2.25 0.6388 -0.25 138 | 112 0.00 1.000 69.16 0.10 1.0029 -0.29 139 | 113 -0.10 0.969 2861.82 0.21 0.9688 0.02 140 | 114 0.00 0.614 34.02 2.16 0.6154 -0.18 141 | 115 0.54 0.794 21511.10 2.72 0.7940 0.00 142 | 116 0.00 0.765 391.30 2.82 0.7654 -0.07 143 | 117 0.06 0.661 1043.38 2.64 0.6591 0.20 144 | 118 0.00 2.507 49.85 -0.36 2.5237 -0.81 145 | 119 0.00 2.507 99.09 0.68 2.4765 1.23 146 | 147 | fund_nav nav_dt index_nm index_increase_rt pe pb 148 | 0 3.6212 2019-02-01 深证100 0.0 16.665 2.368 149 | 1 2.4470 2019-02-01 中小板指 0.0 20.140 2.814 150 | 2 0.8381 2019-02-01 深证成指 0.0 17.235 2.121 151 | 3 1.4475 2019-02-01 深证红利 0.0 13.370 2.079 152 | 4 0.6610 2019-02-01 深证成长 0.0 18.516 2.893 153 | 5 1.0125 2019-02-01 中小300 0.0 21.089 2.451 154 | 6 1.0968 2019-02-01 创业板指 0.0 28.853 3.598 155 | 7 3.4364 2019-02-01 TMT50 0.0 27.880 2.940 156 | 8 1.4766 2019-02-01 深证F120 0.0 13.895 1.844 157 | 9 2.9825 2019-02-01 深证民营 0.0 21.518 2.945 158 | .. ... ... ... ... ... ... 159 | 110 0.6495 2019-02-01 中证军工 0.0 47.329 2.166 160 | 111 0.6386 2019-02-01 中证军工 0.0 47.329 2.166 161 | 112 1.0029 2019-02-01 中证银行 0.0 6.588 0.873 162 | 113 0.9688 2019-02-01 中证银行 0.0 6.588 0.873 163 | 114 0.6151 2019-02-01 中证军工 0.0 47.329 2.166 164 | 115 0.7940 2019-02-01 证券公司 0.0 25.216 1.341 165 | 116 0.7655 2019-02-01 证券公司 0.0 25.216 1.341 166 | 117 0.6597 2019-02-01 中证传媒 0.0 19.697 2.362 167 | 118 2.5274 2019-02-01 恒生指数 0.0 NaN NaN 168 | 119 2.4907 2019-01-30 恒生指数 0.0 NaN NaN 169 | 170 | Example 3: 获取股票交易历史数据 171 | obj = gg.StockData('600000') # 创建股票交易类对象 172 | obj.history(start='2019-01-02', end='2019-01-31') # 获取浦发银行2019年1月份的历史数据 173 | 174 | 获取结果: 175 | obj.output() # 打印结果 176 | 177 | 178 | date open close high low volume code 179 | 0 2019-01-02 9.74 9.70 9.79 9.58 237628.0 600000 180 | 1 2019-01-03 9.70 9.81 9.82 9.66 186542.0 600000 181 | 2 2019-01-04 9.73 9.96 10.00 9.70 271728.0 600000 182 | 3 2019-01-07 10.09 9.98 10.09 9.92 235973.0 600000 183 | 4 2019-01-08 10.03 9.96 10.03 9.91 151049.0 600000 184 | 5 2019-01-09 10.06 9.99 10.16 9.98 231637.0 600000 185 | 6 2019-01-10 9.94 9.96 10.02 9.92 159235.0 600000 186 | 7 2019-01-11 10.03 10.05 10.15 9.96 190690.0 600000 187 | 8 2019-01-14 10.03 10.06 10.10 10.01 203528.0 600000 188 | 9 2019-01-15 10.10 10.11 10.15 10.05 156321.0 600000 189 | 10 2019-01-16 10.10 10.13 10.15 10.07 129883.0 600000 190 | 11 2019-01-17 10.17 10.17 10.28 10.07 227660.0 600000 191 | 12 2019-01-18 10.29 10.43 10.44 10.22 290871.0 600000 192 | 13 2019-01-21 10.36 10.35 10.46 10.30 189477.0 600000 193 | 14 2019-01-22 10.38 10.25 10.38 10.23 162505.0 600000 194 | 15 2019-01-23 10.29 10.27 10.34 10.25 165203.0 600000 195 | 16 2019-01-24 10.34 10.38 10.39 10.27 150066.0 600000 196 | 17 2019-01-25 10.45 10.50 10.56 10.38 196965.0 600000 197 | 18 2019-01-28 10.53 10.42 10.66 10.39 217690.0 600000 198 | 19 2019-01-29 10.39 10.58 10.63 10.38 237680.0 600000 199 | 20 2019-01-30 10.59 10.50 10.64 10.47 160542.0 600000 200 | 21 2019-01-31 10.59 10.73 10.73 10.51 251310.0 600000 201 | 202 | 203 | Example 4: 获取三大需求对GDP贡献数据 204 | obj = gg.Macro() # 创建宏观经济数据类对象 205 | obj.demandsToGdp() # 获取获取三大需求对GDP贡献数据 206 | 207 | 获取结果: 208 | obj.output() # 打印结果 209 | 210 | 211 | year cons_to cons_rate asset_to asset_rate goods_to goods_rate 212 | 0 2017 58.8 4.1 32.1 2.2 9.1 0.6 213 | 1 2016 66.5 4.5 43.1 2.9 -9.6 -0.7 214 | 2 2015 59.7 4.1 41.6 2.9 -1.3 -0.1 215 | 3 2014 48.8 3.6 46.9 3.4 4.3 0.3 216 | 4 2013 48.2 3.6 54.2 4.3 -2.3 -0.1 217 | 5 2012 56.7 4.3 42.0 3.4 1.7 0.2 218 | 6 2011 61.9 5.9 46.2 4.4 -8.1 -0.8 219 | 7 2010 46.9 4.8 66.0 7.1 -11.2 -1.3 220 | 8 2009 56.1 5.3 86.5 8.1 -42.6 -4.0 221 | 9 2008 44.2 4.3 53.2 5.1 2.6 0.3 222 | 10 2007 45.3 6.4 44.1 6.3 10.6 1.5 223 | 11 2006 42.0 5.3 42.9 5.5 15.1 1.9 224 | 12 2005 54.4 6.2 33.1 3.8 12.5 1.4 225 | 13 2004 42.6 4.3 61.6 6.2 -4.2 -0.4 226 | 14 2003 35.4 3.6 70.0 7.0 -5.4 -0.6 227 | 15 2002 43.9 5.1 48.5 3.6 4.6 0.4 228 | 16 2001 49.0 4.1 64.0 5.3 -13.0 -1.1 229 | 17 2000 78.1 6.6 22.4 1.9 -0.5 NaN 230 | 18 1999 74.7 5.7 23.7 1.8 1.6 0.1 231 | 19 1998 57.1 4.4 26.4 2.1 16.5 1.3 232 | 20 1997 37.0 3.4 18.6 1.7 44.4 4.2 233 | 21 1996 60.1 6.0 34.3 3.4 5.6 0.6 234 | 22 1995 44.7 4.9 55.0 6.0 0.3 NaN 235 | 23 1994 30.2 4.0 43.8 5.7 26.0 3.4 236 | 24 1993 59.5 8.3 78.6 11.0 -38.1 -5.3 237 | 25 1992 72.5 10.3 34.2 4.9 -6.8 -1.0 238 | 26 1991 65.1 6.0 24.3 2.2 10.6 1.0 239 | 27 1990 47.8 1.8 1.8 0.1 50.4 1.9 240 | 28 1989 39.6 1.6 16.4 0.7 44.0 1.8 241 | 29 1988 49.6 5.6 39.4 4.5 11.0 1.2 242 | 30 1987 50.3 5.8 23.5 2.7 26.2 3.1 243 | 31 1986 45.0 4.0 23.2 2.0 31.8 2.8 244 | 32 1985 85.5 11.5 80.9 10.9 -66.4 -8.9 245 | 33 1984 69.3 10.5 40.5 6.2 -9.8 -1.5 246 | 34 1983 74.1 8.1 40.4 4.4 -14.5 -1.6 247 | 35 1982 64.7 5.9 23.8 2.2 11.5 1.0 248 | 36 1981 93.4 4.9 -4.3 -0.2 10.9 0.5 249 | 37 1980 71.8 5.6 26.4 2.1 1.8 0.1 250 | 38 1979 87.3 6.6 15.4 1.2 -2.7 -0.2 251 | 39 1978 39.4 4.6 66.0 7.7 -5.4 -0.6 252 | 253 | Example 5: 获取可转债及其相关数据 254 | obj = gg.LowRiskIntArb() # 创建低风险及套利类对象 255 | obj.conBonds() # 获取可转债及其相关数据 256 | 257 | 获取结果: 258 | obj.output() # 打印结果 259 | 260 | 261 | bond_id bond_nm stock_id stock_nm market convert_price convert_dt \ 262 | 0 127008 特发转债 sz000070 特发信息 sz 6.78 2019-05-22 263 | 1 113506 鼎信转债 sh603421 鼎信通讯 sh 21.65 2018-10-20 264 | 2 113507 天马转债 sh603668 天马科技 sh 7.37 2018-10-23 265 | 3 113520 百合转债 sh603313 梦百合 sh 19.03 2019-05-14 266 | 4 128041 盛路转债 sz002446 盛路通信 sz 6.88 2019-01-23 267 | 5 110032 三一转债 sh600031 三一重工 sh 7.25 2016-07-04 268 | 6 127009 冰轮转债 sz000811 冰轮环境 sz 5.52 2019-07-18 269 | 7 128052 凯龙转债 sz002783 凯龙股份 sz 6.97 2019-06-27 270 | 8 123018 溢利转债 sz300381 溢多利 sz 8.41 2019-06-26 271 | 9 123012 万顺转债 sz300057 万顺股份 sz 6.47 2019-01-26 272 | .. ... ... ... ... ... ... ... 273 | 118 128018 时达转债 sz002527 新时达 sz 11.83 2018-05-10 274 | 119 128028 赣锋转债 sz002460 赣锋锂业 sz 42.58 2018-06-27 275 | 120 128023 亚太转债 sz002284 亚太股份 sz 10.34 2018-06-08 276 | 121 132008 17山高EB sh600350 山东高速 sh 9.62 2018-04-26 277 | 122 127004 模塑转债 sz000700 模塑科技 sz 7.72 2017-12-08 278 | 123 128010 顺昌转债 sz002245 澳洋顺昌 sz 9.28 2016-07-29 279 | 124 123003 蓝思转债 sz300433 蓝思科技 sz 16.08 2018-06-14 280 | 125 132011 17浙报EB sh600633 浙数文化 sh 24.39 2018-08-17 281 | 126 128012 辉丰转债 sz002496 ST辉丰 sz 7.71 2016-10-28 282 | 127 128013 洪涛转债 sz002325 洪涛股份 sz 9.98 2017-02-06 283 | 284 | Example 6: 股票LOF基金及基相关数据 285 | obj = gg.LowRiskIntArb() # 创建低风险及套利类对象 286 | obj.stockLof() # 获取股票LOF基金及基相关数据 287 | 288 | 获取结果: 289 | obj.output() # 打印结果 290 | 291 | 292 | fund_id fund_nm price increase_rt volume amount fund_nav nav_dt \ 293 | 0 160105 南方积配 0.812 1.37 6.73 2175.0 0.8155 2019-02-01 294 | 1 160106 南方高增 0.847 2.05 13.22 6851.0 0.8666 2019-02-01 295 | 2 160133 南方天元 1.956 0.98 28.82 22162.0 1.9830 2019-02-01 296 | 3 160211 国泰小盘 1.647 1.92 41.34 5115.0 1.6620 2019-02-01 297 | 4 160212 国泰估值 1.658 2.35 64.51 8375.0 1.6750 2019-02-01 298 | 5 160215 国泰价值 1.192 1.27 10.85 2205.0 1.1990 2019-02-01 299 | 6 160220 国泰民益 1.132 1.71 3.83 3223.0 1.1382 2019-02-01 300 | 7 160311 华夏蓝筹 1.297 0.93 27.65 10515.0 1.3040 2019-02-01 301 | 8 160314 华夏行业 0.842 1.45 12.76 13094.0 0.8490 2019-02-01 302 | 9 160323 华夏磐泰 1.009 0.00 0.00 145.0 0.9721 2019-02-01 303 | .. ... ... ... ... ... ... ... ... 304 | 68 168401 红土定增 0.966 0.00 1.04 1010.0 1.0139 2019-02-01 305 | 69 169101 东证睿丰 1.252 1.54 356.30 39830.0 1.2600 2019-02-01 306 | 70 169201 浙商鼎盈 0.971 0.00 0.00 771.0 0.9783 2019-02-01 307 | 71 501000 国金鑫新 0.961 4.34 2.09 82.0 0.9460 2019-02-01 308 | 72 501001 财通精选 0.953 -2.46 10.37 5423.0 0.9590 2019-02-01 309 | 73 501015 财通升级 0.757 0.40 12.29 36579.0 0.7650 2019-02-01 310 | 74 501017 国泰融丰 0.783 0.00 0.00 5178.0 0.7768 2019-02-01 311 | 75 501022 银华鑫盛 0.779 0.39 3.33 12266.0 0.7980 2019-02-01 312 | 76 501027 国泰融信 0.989 0.00 0.00 311.0 1.0055 2019-02-01 313 | 77 501035 创金睿选 0.938 0.32 2.14 877.0 0.9627 2019-02-01 314 | 315 | 更多文档: 316 | 317 | 请查阅文档 318 | 319 | Change Log: 320 | 0.1.0 2019/02/03 321 | 创建第一个版本 322 | 323 | 324 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [egg_info] 2 | tag_build = 3 | tag_date = 0 4 | tag_svn_revision = 0 5 | 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup, find_packages 4 | 5 | try: 6 | import pypandoc 7 | long_description = pypandoc.convert('README.md', 'rst') 8 | except (IOError, ImportError): 9 | long_description = '' 10 | 11 | 12 | setup( 13 | name='GuGu', 14 | version='0.1.0', 15 | description='an api for getting financial data', 16 | long_description=long_description, 17 | author='TabQ', 18 | author_email='16621596@qq.com', 19 | url='http://www.infodata.cc', 20 | license="Apache License, Version 2.0", 21 | packages=find_packages(), 22 | install_requires=[ 23 | 'pandas', 24 | 'requests', 25 | 'lxml', 26 | 'simplejson', 27 | 'xlrd' 28 | ], 29 | keywords='Financial Data Crawler', 30 | classifiers=['Development Status :: 4 - Beta', 31 | 'Programming Language :: Python :: 2.6', 32 | 'Programming Language :: Python :: 2.7', 33 | 'Programming Language :: Python :: 3.2', 34 | 'Programming Language :: Python :: 3.3', 35 | 'Programming Language :: Python :: 3.4', 36 | 'Programming Language :: Python :: 3.5'], 37 | ) 38 | --------------------------------------------------------------------------------