# coding :utf-8
#
# The MIT License (MIT)
#
# Copyright (c) 2021-2029 XuHaiJiang/QFF
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from datetime import datetime, timedelta
from functools import wraps
import time
import math
import pandas as pd
from typing import Optional, Callable
year_end = datetime.today().replace(month=12, day=31).strftime("%Y-%m-%d")
precomputed_shanghai_holidays = [
"1991-01-01",
"1991-02-15",
"1991-02-18",
"1991-05-01",
"1991-10-01",
"1991-10-02",
"1992-01-01",
"1992-02-04",
"1992-02-05",
"1992-02-06",
"1992-05-01",
"1992-10-01",
"1992-10-02",
"1993-01-01",
"1993-01-25",
"1993-01-26",
"1993-10-01",
"1994-02-07",
"1994-02-08",
"1994-02-09",
"1994-02-10",
"1994-02-11",
"1994-05-02",
"1994-10-03",
"1994-10-04",
"1995-01-02",
"1995-01-30",
"1995-01-31",
"1995-02-01",
"1995-02-02",
"1995-02-03",
"1995-05-01",
"1995-10-02",
"1995-10-03",
"1996-01-01",
"1996-02-19",
"1996-02-20",
"1996-02-21",
"1996-02-22",
"1996-02-23",
"1996-02-26",
"1996-02-27",
"1996-02-28",
"1996-02-29",
"1996-03-01",
"1996-05-01",
"1996-09-30",
"1996-10-01",
"1996-10-02",
"1997-01-01",
"1997-02-03",
"1997-02-04",
"1997-02-05",
"1997-02-06",
"1997-02-07",
"1997-02-10",
"1997-02-11",
"1997-02-12",
"1997-02-13",
"1997-02-14",
"1997-05-01",
"1997-05-02",
"1997-06-30",
"1997-07-01",
"1997-10-01",
"1997-10-02",
"1997-10-03",
"1998-01-01",
"1998-01-02",
"1998-01-26",
"1998-01-27",
"1998-01-28",
"1998-01-29",
"1998-01-30",
"1998-02-02",
"1998-02-03",
"1998-02-04",
"1998-02-05",
"1998-02-06",
"1998-05-01",
"1998-10-01",
"1998-10-02",
"1999-01-01",
"1999-02-10",
"1999-02-11",
"1999-02-12",
"1999-02-15",
"1999-02-16",
"1999-02-17",
"1999-02-18",
"1999-02-19",
"1999-02-22",
"1999-02-23",
"1999-02-24",
"1999-02-25",
"1999-02-26",
"1999-05-03",
"1999-10-01",
"1999-10-04",
"1999-10-05",
"1999-10-06",
"1999-10-07",
"1999-12-20",
"1999-12-31",
"2000-01-03",
"2000-01-31",
"2000-02-01",
"2000-02-02",
"2000-02-03",
"2000-02-04",
"2000-02-07",
"2000-02-08",
"2000-02-09",
"2000-02-10",
"2000-02-11",
"2000-05-01",
"2000-05-02",
"2000-05-03",
"2000-05-04",
"2000-05-05",
"2000-10-02",
"2000-10-03",
"2000-10-04",
"2000-10-05",
"2000-10-06",
"2001-01-01",
"2001-01-22",
"2001-01-23",
"2001-01-24",
"2001-01-25",
"2001-01-26",
"2001-01-29",
"2001-01-30",
"2001-01-31",
"2001-02-01",
"2001-02-02",
"2001-05-01",
"2001-05-02",
"2001-05-03",
"2001-05-04",
"2001-05-07",
"2001-10-01",
"2001-10-02",
"2001-10-03",
"2001-10-04",
"2001-10-05",
"2002-01-01",
"2002-01-02",
"2002-01-03",
"2002-02-11",
"2002-02-12",
"2002-02-13",
"2002-02-14",
"2002-02-15",
"2002-02-18",
"2002-02-19",
"2002-02-20",
"2002-02-21",
"2002-02-22",
"2002-05-01",
"2002-05-02",
"2002-05-03",
"2002-05-06",
"2002-05-07",
"2002-09-30",
"2002-10-01",
"2002-10-02",
"2002-10-03",
"2002-10-04",
"2002-10-07",
"2003-01-01",
"2003-01-30",
"2003-01-31",
"2003-02-03",
"2003-02-04",
"2003-02-05",
"2003-02-06",
"2003-02-07",
"2003-05-01",
"2003-05-02",
"2003-05-05",
"2003-05-06",
"2003-05-07",
"2003-05-08",
"2003-05-09",
"2003-10-01",
"2003-10-02",
"2003-10-03",
"2003-10-06",
"2003-10-07",
"2004-01-01",
"2004-01-19",
"2004-01-20",
"2004-01-21",
"2004-01-22",
"2004-01-23",
"2004-01-26",
"2004-01-27",
"2004-01-28",
"2004-05-03",
"2004-05-04",
"2004-05-05",
"2004-05-06",
"2004-05-07",
"2004-10-01",
"2004-10-04",
"2004-10-05",
"2004-10-06",
"2004-10-07",
"2005-01-03",
"2005-02-07",
"2005-02-08",
"2005-02-09",
"2005-02-10",
"2005-02-11",
"2005-02-14",
"2005-02-15",
"2005-05-02",
"2005-05-03",
"2005-05-04",
"2005-05-05",
"2005-05-06",
"2005-10-03",
"2005-10-04",
"2005-10-05",
"2005-10-06",
"2005-10-07",
"2006-01-02",
"2006-01-03",
"2006-01-26",
"2006-01-27",
"2006-01-30",
"2006-01-31",
"2006-02-01",
"2006-02-02",
"2006-02-03",
"2006-05-01",
"2006-05-02",
"2006-05-03",
"2006-05-04",
"2006-05-05",
"2006-10-02",
"2006-10-03",
"2006-10-04",
"2006-10-05",
"2006-10-06",
"2007-01-01",
"2007-01-02",
"2007-01-03",
"2007-02-19",
"2007-02-20",
"2007-02-21",
"2007-02-22",
"2007-02-23",
"2007-05-01",
"2007-05-02",
"2007-05-03",
"2007-05-04",
"2007-05-07",
"2007-10-01",
"2007-10-02",
"2007-10-03",
"2007-10-04",
"2007-10-05",
"2007-12-31",
"2008-01-01",
"2008-02-06",
"2008-02-07",
"2008-02-08",
"2008-02-11",
"2008-02-12",
"2008-04-04",
"2008-05-01",
"2008-05-02",
"2008-06-09",
"2008-09-15",
"2008-09-29",
"2008-09-30",
"2008-10-01",
"2008-10-02",
"2008-10-03",
"2009-01-01",
"2009-01-02",
"2009-01-26",
"2009-01-27",
"2009-01-28",
"2009-01-29",
"2009-01-30",
"2009-04-06",
"2009-05-01",
"2009-05-28",
"2009-05-29",
"2009-10-01",
"2009-10-02",
"2009-10-05",
"2009-10-06",
"2009-10-07",
"2009-10-08",
"2010-01-01",
"2010-02-15",
"2010-02-16",
"2010-02-17",
"2010-02-18",
"2010-02-19",
"2010-04-05",
"2010-05-03",
"2010-06-14",
"2010-06-15",
"2010-06-16",
"2010-09-22",
"2010-09-23",
"2010-09-24",
"2010-10-01",
"2010-10-04",
"2010-10-05",
"2010-10-06",
"2010-10-07",
"2011-01-03",
"2011-02-02",
"2011-02-03",
"2011-02-04",
"2011-02-07",
"2011-02-08",
"2011-04-04",
"2011-04-05",
"2011-05-02",
"2011-06-06",
"2011-09-12",
"2011-10-03",
"2011-10-04",
"2011-10-05",
"2011-10-06",
"2011-10-07",
"2012-01-02",
"2012-01-03",
"2012-01-23",
"2012-01-24",
"2012-01-25",
"2012-01-26",
"2012-01-27",
"2012-04-02",
"2012-04-03",
"2012-04-04",
"2012-04-30",
"2012-05-01",
"2012-06-22",
"2012-10-01",
"2012-10-02",
"2012-10-03",
"2012-10-04",
"2012-10-05",
"2013-01-01",
"2013-01-02",
"2013-01-03",
"2013-02-11",
"2013-02-12",
"2013-02-13",
"2013-02-14",
"2013-02-15",
"2013-04-04",
"2013-04-05",
"2013-04-29",
"2013-04-30",
"2013-05-01",
"2013-06-10",
"2013-06-11",
"2013-06-12",
"2013-09-19",
"2013-09-20",
"2013-10-01",
"2013-10-02",
"2013-10-03",
"2013-10-04",
"2013-10-07",
"2014-01-01",
"2014-01-31",
"2014-02-03",
"2014-02-04",
"2014-02-05",
"2014-02-06",
"2014-04-07",
"2014-05-01",
"2014-05-02",
"2014-06-02",
"2014-09-08",
"2014-10-01",
"2014-10-02",
"2014-10-03",
"2014-10-06",
"2014-10-07",
"2015-01-01",
"2015-01-02",
"2015-02-18",
"2015-02-19",
"2015-02-20",
"2015-02-23",
"2015-02-24",
"2015-04-06",
"2015-05-01",
"2015-06-22",
"2015-09-03",
"2015-09-04",
"2015-10-01",
"2015-10-02",
"2015-10-05",
"2015-10-06",
"2015-10-07",
"2016-01-01",
"2016-02-08",
"2016-02-09",
"2016-02-10",
"2016-02-11",
"2016-02-12",
"2016-04-04",
"2016-05-02",
"2016-06-09",
"2016-06-10",
"2016-09-15",
"2016-09-16",
"2016-10-03",
"2016-10-04",
"2016-10-05",
"2016-10-06",
"2016-10-07",
"2017-01-02",
"2017-01-27",
"2017-01-30",
"2017-01-31",
"2017-02-01",
"2017-02-02",
"2017-04-03",
"2017-04-04",
"2017-05-01",
"2017-05-29",
"2017-05-30",
"2017-10-02",
"2017-10-03",
"2017-10-04",
"2017-10-05",
"2017-10-06",
"2018-01-01",
"2018-02-15",
"2018-02-16",
"2018-02-19",
"2018-02-20",
"2018-02-21",
"2018-04-05",
"2018-04-06",
"2018-04-30",
"2018-05-01",
"2018-06-18",
"2018-09-24",
"2018-10-01",
"2018-10-02",
"2018-10-03",
"2018-10-04",
"2018-10-05",
"2018-12-31",
"2019-01-01",
"2019-02-04",
"2019-02-05",
"2019-02-06",
"2019-02-07",
"2019-02-08",
"2019-04-05",
"2019-05-01",
"2019-05-02",
"2019-05-03",
"2019-06-07",
"2019-09-13",
"2019-10-01",
"2019-10-02",
"2019-10-03",
"2019-10-04",
"2019-10-07",
"2020-01-01",
"2020-01-24",
"2020-01-27",
"2020-01-28",
"2020-01-29",
"2020-01-30",
"2020-01-31", # http://english.sse.com.cn/news/newsrelease/c/4993503.shtml
"2020-04-06",
"2020-05-01",
"2020-05-04",
"2020-05-05",
"2020-06-25",
"2020-06-26",
"2020-10-01",
"2020-10-02",
"2020-10-05",
"2020-10-06",
"2020-10-07",
"2020-10-08",
"2021-01-01",
"2021-02-11",
"2021-02-12",
"2021-02-15",
"2021-02-16",
"2021-02-17",
"2021-04-05",
"2021-05-03",
"2021-05-04",
"2021-05-05",
"2021-06-14",
"2021-09-20",
"2021-09-21",
"2021-10-01",
"2021-10-04",
"2021-10-05",
"2021-10-06",
"2021-10-07",
"2022-01-03",
"2022-01-31",
"2022-02-01",
"2022-02-02",
"2022-02-03",
"2022-02-04",
"2022-04-04",
"2022-04-05",
"2022-05-02",
"2022-05-03",
"2022-05-04",
"2022-06-03",
"2022-09-12",
"2022-10-03",
"2022-10-04",
"2022-10-05",
"2022-10-06",
"2022-10-07",
"2023-01-02",
"2023-01-23",
"2023-01-24",
"2023-01-25",
"2023-01-26",
"2023-01-27",
"2023-04-05",
"2023-05-01",
"2023-05-02",
"2023-05-03",
"2023-06-22",
"2023-06-23",
"2023-09-29",
"2023-10-02",
"2023-10-03",
"2023-10-04",
"2023-10-05",
"2023-10-06",
"2024-01-01",
"2024-02-09",
"2024-02-12",
"2024-02-13",
"2024-02-14",
"2024-02-15",
"2024-02-16",
"2024-04-04",
"2024-04-05",
"2024-05-01",
"2024-05-02",
"2024-05-03",
"2024-06-10",
"2024-09-17",
"2024-10-01",
"2024-10-02",
"2024-10-03",
"2024-10-04",
"2024-10-07",
"2025-01-01",
"2025-01-28",
"2025-01-29",
"2025-01-30",
"2025-01-31",
"2025-02-03",
"2025-02-04",
"2025-04-04",
"2025-05-01",
"2025-05-02",
"2025-05-05",
"2025-06-02",
"2025-10-01",
"2025-10-02",
"2025-10-03",
"2025-10-06",
"2025-10-07",
"2025-10-08",
]
trade_date_sse = pd.bdate_range(start="1990-12-19",
end=year_end, freq='C',
holidays=precomputed_shanghai_holidays
).strftime("%Y-%m-%d").tolist()
[文档]def get_real_trade_date(date, towards=-1):
"""
根据给定日期,获取真实的交易日期
:param date: 给定日期 [str,date]
:param towards: 方向, -1 -> 向前, 1 -> 向后 int
:return: 返回计算后的日期str
"""
day = str(date)[0:10]
if towards == 1:
if pd.to_datetime(day) >= pd.to_datetime(trade_date_sse[-1]):
return trade_date_sse[-1]
while day not in trade_date_sse:
day = str(
datetime.strptime(day, "%Y-%m-%d")
+ timedelta(days=1)
)[0:10]
else:
return str(day)[0:10]
elif towards == -1:
if pd.to_datetime(day) <= pd.to_datetime(trade_date_sse[0]):
return trade_date_sse[0]
while day not in trade_date_sse:
day = str(
datetime.strptime(day, "%Y-%m-%d")
- timedelta(days=1)
)[0:10]
else:
return str(day)[0:10]
[文档]def is_trade_day(date: str) -> bool:
"""
判断是否交易日期
:param date: 需判断的日期
:return: True:是交易日期
"""
if date in trade_date_sse:
return True
else:
return False
[文档]def get_date_gap(date: str, gap: int, methods: str) -> Optional[str]:
"""
返回指定日期向前或向后间隔天数的交易日日期
:param date: 字符串起始日
:param gap: 间隔多数个交易日
:param methods: 方向["gt->大于", "gte->大于等于","小于->lt", "小于等于->lte", "等于->==="]
:return: 返回计算后的日期
"""
try:
if methods in [">", "gt"]:
index = trade_date_sse.index(date) + gap
return trade_date_sse[index] if index < len(trade_date_sse) else trade_date_sse[-1]
elif methods in [">=", "gte"]:
index = trade_date_sse.index(date) + gap - 1
return trade_date_sse[index] if index < len(trade_date_sse) else trade_date_sse[-1]
elif methods in ["<", "lt"]:
index = trade_date_sse.index(date) - gap
return trade_date_sse[index] if index > 0 else trade_date_sse[0]
elif methods in ["<=", "lte"]:
index = trade_date_sse.index(date) - gap + 1
return trade_date_sse[index] if index > 0 else trade_date_sse[0]
elif methods in ["==", "=", "eq"]:
return date
except Exception as e:
print("日期格式错误!:{}".format(e))
return None
[文档]def get_next_trade_day(date: str, n: int = 1) -> str:
"""
获取下一个交易日的日期
:param date: 字符串起始日
:param n: 间隔多数个交易日
:return: 返回计算后的日期
"""
date = str(date)[0:10]
if not is_trade_day(date):
date = get_real_trade_date(date, -1)
return get_date_gap(date, n, "gt")
[文档]def get_pre_trade_day(date, n=1, freq='day'):
# type: (str, int, str) -> str
"""
获取前几个交易周期的日期
:param date: 字符串起始日
:param n: 间隔多数个交易日
:param freq: 间隔频率,支持['day','1min','5min','15min','30min','60min']
:return: 返回计算后的日期
"""
date = str(date)[0:10]
if freq in ['daily', '1d', 'day']:
if not is_trade_day(date):
date = get_real_trade_date(date, -1)
return get_date_gap(date, n, "lt")
else:
_time = str(date)[10:]
if not is_trade_day(date):
date = get_real_trade_date(date, -1)
_time = ' 15:00:00'
if str(freq) in ['5m', '5min']:
div = 48
elif str(freq) in ['15m', '15min']:
div = 16
elif str(freq) in ['30m', '30min']:
div = 8
elif str(freq) in ['60m', '60min']:
div = 4
elif str(freq) in ['1m', '1min']:
div = 240
else:
raise ValueError
new = get_date_gap(date, math.ceil(n / div), "lt")
if isinstance(new, str):
return new + _time
else:
raise ValueError
[文档]def get_trade_days(start: str, end: str) -> Optional[list]:
"""
获取指定范围交易日
:param start: 开始日期
:param end: 结束日期
:return: 返回交易日期列表
"""
real_start = get_real_trade_date(start, 1)
real_end = get_real_trade_date(end, -1)
if real_start > real_end:
return None
else:
return trade_date_sse[
trade_date_sse.index(real_start): trade_date_sse.index(real_end) + 1: 1
]
[文档]def get_trade_gap(start: str, end: str) -> int:
"""
返回start到end中间有多少个交易天,算首尾
:param start: 开始日期
:param end: 结束日期
:return: 交易日数量
"""
real_start = get_real_trade_date(start, 1)
real_end = get_real_trade_date(end, -1)
if real_start is not None:
return trade_date_sse.index(real_end) + 1 - trade_date_sse.index(real_start)
else:
return 0
def get_trade_min_list(day, period=1):
"""
获取交易日的分钟列表
:param day: 日期
:param period: 间隔时间(分钟)
:return: list
"""
min_list = []
dt = day + " 09:30:00"
while dt <= day + " 15:00:00":
min_list.append(dt)
dt = (datetime.strptime(dt, "%Y-%m-%d %H:%M:%S")
+ timedelta(minutes=period)).strftime("%Y-%m-%d %H:%M:%S")
if day + " 11:30:00" < dt < day + " 13:00:00":
dt = day + " 13:00:00"
dt = (datetime.strptime(dt, "%Y-%m-%d %H:%M:%S")
+ timedelta(minutes=period)).strftime("%Y-%m-%d %H:%M:%S")
return min_list
def util_date_valid(date):
"""
判断字符串格式(1982-05-11)
:param date: 日期 Str
:return: bool
"""
try:
datetime.strptime(date, "%Y-%m-%d")
return True
except ValueError:
return False
def util_time_valid(date_time):
"""
判断字符串格式(1982-05-11 12:00:00)
:param date_time: 日期 Str
:return: bool
"""
try:
datetime.strptime(date_time, "%Y-%m-%d %H:%M:%S")
return True
except ValueError:
return False
def util_time_stamp(time_):
"""
转换日期时间的字符串为浮点数的时间戳
:param time_: str 日期时间 参数支持: ['2018-01-01 00:00:00']
:return: 浮点数的时间戳
"""
if len(str(time_)) == 10:
# yyyy-mm-dd格式
return time.mktime(time.strptime(time_, '%Y-%m-%d'))
elif len(str(time_)) == 16:
# yyyy-mm-dd hh:mm格式
return time.mktime(time.strptime(time_, '%Y-%m-%d %H:%M'))
else:
return time.mktime(time.strptime(str(time_)[0:19], '%Y-%m-%d %H:%M:%S'))
def util_get_date_gap(date: str, n: int):
"""
获取前/后n天的日期
"""
return str(datetime.strptime(date, "%Y-%m-%d") + timedelta(days=n))[0:10]
[文档]def run_time(func: Callable) -> float:
"""
计算函数运行时间的装饰函数
:param func: 函数名称, 在待计时函数上方输入@run_time即可
:return: 函数运行的时间
"""
@wraps(func) # <- 这里加 wraps(func) 保留函数的元信息
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs) # 函数在这里运行
end = time.time()
cost_time = end - start
print("函数 {} 运行用时: {}".format(func.__name__, cost_time))
return res
return wrapper
def date_to_int(date: str):
return int(''.join(date.split('-')))
def int_to_date(d: int):
d = int(d)
if len(str(d)) <= 6:
s = "{:>06d}".format(d)
ret = s[:2]+'-'+s[2:4]+'-'+s[4:]
if d > 800000:
return '19'+ret
else:
return '20'+ret
else:
s = str(d)
return s[:4]+'-'+s[4:6]+'-'+s[6:]