├── .bumpversion.cfg ├── .bumpyverison.cfg ├── .gitignore ├── README.md ├── easyutils ├── __init__.py ├── stock.py └── timeutils.py ├── requirements.txt ├── setup.py └── tests └── test_timeutils.py /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.1.7 3 | commit = True 4 | files = easyutils/__init__.py setup.py 5 | tag = True 6 | tag_name = {new_version} 7 | 8 | -------------------------------------------------------------------------------- /.bumpyverison.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.1.6 3 | commit = True 4 | files = easyutils/__init__.py setup.py 5 | tag = True 6 | tag_name = {new_version} 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # easyutils 2 | 3 | 希望能提供一些股市常用的接口,减少重复劳动 4 | 5 | ## Install 6 | 7 | ``` 8 | pip install easyutils 9 | ``` 10 | 11 | ## Usage 12 | 13 | ``` 14 | import easyutils 15 | easyuitls.is_holiday_today() 16 | ``` 17 | 18 | ## is_holiday 19 | 20 | ``` 21 | def is_holiday(day): 22 | """ 23 | 判断是否节假日 24 | :param day: 日期, 格式为 '20160404' 25 | :return: Bool 26 | ``` 27 | 28 | ## is_holiday_today 29 | 30 | ``` 31 | def is_holiday_today(): 32 | """ 33 | 判断今天是否时节假日 34 | :return: bool 35 | """ 36 | ``` 37 | 38 | ## is_tradetime_now 39 | 40 | ``` 41 | def is_tradetime_now(): 42 | """ 43 | 判断目前是不是交易时间, 并没有对节假日做处理 44 | :return: bool 45 | ``` 46 | 47 | ## get_stock_type 48 | 49 | ``` 50 | def get_stock_type(stock_code): 51 | """判断股票ID对应的证券市场 52 | 匹配规则 53 | ['50', '51', '60', '90', '110'] 为 sh 54 | ['00', '13', '18', '15', '16', '18', '20', '30', '39', '115'] 为 sz 55 | ['5', '6', '9'] 开头的为 sh, 其余为 sz 56 | :param stock_code:股票ID, 若以 'sz', 'sh' 开头直接返回对应类型,否则使用内置规则判断 57 | :return 'sh' or 'sz'""" 58 | ``` 59 | 60 | ## get_code_type 61 | 62 | ``` 63 | def get_code_type(code): 64 | """ 65 | 判断代码是属于那种类型,目前仅支持 ['fund', 'stock'] 66 | :return str 返回code类型, fund 基金 stock 股票 67 | """ 68 | ``` 69 | 70 | 71 | 72 | ## get_all_stock_codes 73 | 74 | ``` 75 | def get_all_stock_codes(): 76 | """获取所有股票 ID""" 77 | ``` 78 | 79 | -------------------------------------------------------------------------------- /easyutils/__init__.py: -------------------------------------------------------------------------------- 1 | # coding:utf8 2 | from .timeutils import * 3 | from .stock import * 4 | 5 | __version__ = "0.1.7" 6 | -------------------------------------------------------------------------------- /easyutils/stock.py: -------------------------------------------------------------------------------- 1 | # coding:utf8 2 | import re 3 | import datetime 4 | 5 | import requests 6 | import io 7 | 8 | 9 | def get_stock_type(stock_code): 10 | """判断股票ID对应的证券市场 11 | 匹配规则 12 | ['50', '51', '60', '90', '110'] 为 sh 13 | ['00', '13', '18', '15', '16', '18', '20', '30', '39', '115'] 为 sz 14 | ['5', '6', '9'] 开头的为 sh, 其余为 sz 15 | :param stock_code:股票ID, 若以 'sz', 'sh' 开头直接返回对应类型,否则使用内置规则判断 16 | :return 'sh' or 'sz'""" 17 | assert type(stock_code) is str, "stock code need str type" 18 | if stock_code.startswith(("sh", "sz")): 19 | return stock_code[:2] 20 | if stock_code.startswith( 21 | ("50", "51", "60", "90", "110", "113", "132", "204") 22 | ): 23 | return "sh" 24 | if stock_code.startswith( 25 | ("00", "13", "18", "15", "16", "18", "20", "30", "39", "115", "1318") 26 | ): 27 | return "sz" 28 | if stock_code.startswith(("5", "6", "9", "7")): 29 | return "sh" 30 | return "sz" 31 | 32 | 33 | def get_code_type(code): 34 | """ 35 | 判断代码是属于那种类型,目前仅支持 ['fund', 'stock'] 36 | :return str 返回code类型, fund 基金 stock 股票 37 | """ 38 | if code.startswith(("00", "30", "60")): 39 | return "stock" 40 | return "fund" 41 | 42 | 43 | def get_all_stock_codes(): 44 | """获取所有股票 ID""" 45 | all_stock_codes_url = "http://www.shdjt.com/js/lib/astock.js" 46 | grep_stock_codes = re.compile("~(\d+)`") 47 | response = requests.get(all_stock_codes_url) 48 | stock_codes = grep_stock_codes.findall(response.text) 49 | return stock_codes 50 | 51 | 52 | def round_price_by_code(price, code): 53 | """ 54 | 根据代码类型[股票,基金] 截取制定位数的价格 55 | :param price: 证券价格 56 | :param code: 证券代码 57 | :return: str 截断后的价格的字符串表示 58 | """ 59 | if isinstance(price, str): 60 | return price 61 | 62 | typ = get_code_type(code) 63 | if typ == "fund": 64 | return "{:.3f}".format(price) 65 | return "{:.2f}".format(price) 66 | 67 | 68 | def get_ipo_info(only_today=False): 69 | import pyquery 70 | 71 | response = requests.get( 72 | "http://vip.stock.finance.sina.com.cn/corp/go.php/vRPD_NewStockIssue/page/1.phtml", 73 | headers={"accept-encoding": "gzip, deflate, sdch"}, 74 | ) 75 | html = response.content.decode("gbk") 76 | 77 | html_obj = pyquery.PyQuery(html) 78 | table_html = html_obj("#con02-0").html() 79 | 80 | import pandas as pd 81 | 82 | df = pd.read_html( 83 | io.StringIO(table_html), 84 | skiprows=3, 85 | converters={"证券代码": str, "申购代码": str}, 86 | )[0] 87 | if only_today: 88 | today = datetime.datetime.now().strftime("%Y-%m-%d") 89 | df = df[df["上网发行日期↓"] == today] 90 | return df 91 | -------------------------------------------------------------------------------- /easyutils/timeutils.py: -------------------------------------------------------------------------------- 1 | # coding:utf8 2 | import datetime 3 | from datetime import timedelta 4 | from functools import lru_cache 5 | 6 | import requests 7 | 8 | import time 9 | 10 | 11 | @lru_cache() 12 | def is_holiday(day): 13 | """ 14 | 判断是否节假日, api 来自百度 apistore: http://apistore.baidu.com/apiworks/servicedetail/1116.html 15 | :param day: 日期, 格式为 '20160404' 16 | :return: bool 17 | """ 18 | api = "http://tool.bitefu.net/jiari/" 19 | params = {"d": day, "apiserviceid": 1116} 20 | rep = requests.get(api, params) 21 | res = rep.text 22 | return True if res != "0" else False 23 | 24 | 25 | def is_holiday_today(): 26 | """ 27 | 判断今天是否时节假日 28 | :return: bool 29 | """ 30 | today = datetime.date.today().strftime("%Y%m%d") 31 | return is_holiday(today) 32 | 33 | 34 | def is_tradetime_now(): 35 | """ 36 | 判断目前是不是交易时间, 并没有对节假日做处理 37 | :return: bool 38 | """ 39 | now_time = time.localtime() 40 | now = (now_time.tm_hour, now_time.tm_min, now_time.tm_sec) 41 | if (9, 15, 0) <= now <= (11, 30, 0) or (13, 0, 0) <= now <= (15, 0, 0): 42 | return True 43 | return False 44 | 45 | 46 | def calc_next_trade_time_delta_seconds(): 47 | now_time = datetime.datetime.now() 48 | now = (now_time.hour, now_time.minute, now_time.second) 49 | if now < (9, 15, 0): 50 | next_trade_start = now_time.replace( 51 | hour=9, minute=15, second=0, microsecond=0 52 | ) 53 | elif (12, 0, 0) < now < (13, 0, 0): 54 | next_trade_start = now_time.replace( 55 | hour=13, minute=0, second=0, microsecond=0 56 | ) 57 | elif now > (15, 0, 0): 58 | distance_next_work_day = 1 59 | while True: 60 | target_day = now_time + timedelta(days=distance_next_work_day) 61 | if is_holiday(target_day.strftime("%Y%m%d")): 62 | distance_next_work_day += 1 63 | else: 64 | break 65 | 66 | day_delta = timedelta(days=distance_next_work_day) 67 | next_trade_start = (now_time + day_delta).replace( 68 | hour=9, minute=15, second=0, microsecond=0 69 | ) 70 | else: 71 | return 0 72 | time_delta = next_trade_start - now_time 73 | return time_delta.total_seconds() 74 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | pyquery -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # coding:utf8 2 | from setuptools import setup, find_packages 3 | 4 | setup( 5 | name="easyutils", 6 | version="0.1.7", 7 | packages=find_packages(), 8 | description="A utility for China Stock", 9 | author="shidenggui", 10 | author_email="longlyshidenggui@gmail.com", 11 | license="BSD", 12 | url="https://github.com/shidenggui/easyutils", 13 | keywords="China stock trade", 14 | install_requires=["requests", "pyquery"], 15 | classifiers=[ 16 | "Development Status :: 4 - Beta", 17 | "Programming Language :: Python :: 2.7", 18 | "Programming Language :: Python :: 3.4", 19 | "Programming Language :: Python :: 3.5", 20 | "License :: OSI Approved :: BSD License", 21 | ], 22 | ) 23 | -------------------------------------------------------------------------------- /tests/test_timeutils.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from easyutils.timeutils import is_holiday 3 | 4 | 5 | def test_is_holiday(): 6 | cases = [ 7 | ("20161001", True), 8 | ("20161008", False), 9 | ("20161118", False), 10 | ("20161119", True), 11 | ] 12 | for day, result in cases: 13 | assert is_holiday(day) == result 14 | --------------------------------------------------------------------------------