1
0
mirror of https://github.com/microsoft/qlib.git synced 2026-07-18 01:44:34 +08:00

Compare commits

...

7 Commits

Author SHA1 Message Date
Linlang
34a2372f01 fix: possible bug causing missing calendar_list data 2026-01-28 16:59:40 +08:00
Linlang
1ace03cf77 fix: resolving conflicts between pylint and black 2026-01-28 15:28:07 +08:00
Linlang
6f31dac4a5 fix: resolving conflicts between pylint and black 2026-01-28 15:22:05 +08:00
Linlang
895db4c352 fix: lint with pylint 2026-01-28 15:17:37 +08:00
Linlang
ce87fe345c fix: str format error 2026-01-28 15:02:50 +08:00
Linlang
5a2bb14273 fix: lint with black 2026-01-28 14:42:49 +08:00
Linlang
fb606ec874 fix: strategies for enhancing crawlers 2026-01-28 14:39:07 +08:00

View File

@@ -3,10 +3,12 @@
import re
import copy
import datetime
import importlib
import time
import bisect
import pickle
import random
import requests
import functools
from pathlib import Path
@@ -23,7 +25,7 @@ from bs4 import BeautifulSoup
HS_SYMBOLS_URL = "http://app.finance.ifeng.com/hq/list.php?type=stock_a&class={s_type}"
CALENDAR_URL_BASE = "http://push2his.eastmoney.com/api/qt/stock/kline/get?secid={market}.{bench_code}&fields1=f1%2Cf2%2Cf3%2Cf4%2Cf5&fields2=f51%2Cf52%2Cf53%2Cf54%2Cf55%2Cf56%2Cf57%2Cf58&klt=101&fqt=0&beg=19900101&end=20991231"
CALENDAR_URL_BASE = "http://push2his.eastmoney.com/api/qt/stock/kline/get?secid={market}.{bench_code}&fields1=f1%2Cf2%2Cf3%2Cf4%2Cf5&fields2=f51%2Cf52%2Cf53%2Cf54%2Cf55%2Cf56%2Cf57%2Cf58&klt=101&fqt=0"
SZSE_CALENDAR_URL = "http://www.szse.cn/api/report/exchange/onepersistenthour/monthList?month={month}&random={random}"
CALENDAR_BENCH_URL_MAP = {
@@ -38,6 +40,24 @@ CALENDAR_BENCH_URL_MAP = {
"BR_ALL": "^BVSP",
}
CHROME_UA_POOL = [
# Windows
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/121.0.6167.85 Safari/537.36",
# macOS
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/121.0.0.0 Safari/537.36",
# Linux
"Mozilla/5.0 (X11; Linux x86_64) " # pylint: disable=W1404
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36",
]
_BENCH_CALENDAR_LIST = None
_ALL_CALENDAR_LIST = None
_HS_SYMBOLS = None
@@ -51,6 +71,16 @@ _CALENDAR_MAP = {}
MINIMUM_SYMBOLS_NUM = 3900
def build_headers():
return {
"User-Agent": random.choice(CHROME_UA_POOL),
"Accept": "application/json,text/plain,*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Referer": "https://quote.eastmoney.com/",
"Connection": "keep-alive",
}
def get_calendar_list(bench_code="CSI300") -> List[pd.Timestamp]:
"""get SH/SZ history calendar list
@@ -66,9 +96,38 @@ def get_calendar_list(bench_code="CSI300") -> List[pd.Timestamp]:
logger.info(f"get calendar list: {bench_code}......")
def _get_calendar(url):
_value_list = requests.get(url, timeout=None).json()["data"]["klines"]
return sorted(map(lambda x: pd.Timestamp(x.split(",")[0]), _value_list))
def _get_calendar(url, max_retry=3):
session = requests.Session()
session.headers.update(build_headers())
current_datetime = datetime.datetime.now()
cur_year = current_datetime.year
res_list = []
failed_years = []
for year in range(2000, cur_year + 1):
start = f"{year}0101"
end = f"{year}1231"
formatted_url = url + f"&beg={start}&end={end}".format(start=start, end=end)
for attempt in range(max_retry):
try:
resp = session.get(formatted_url, timeout=10)
resp.raise_for_status()
data = resp.json().get("data")
if not data or "klines" not in data:
raise ValueError("missing klines")
res_list.extend(pd.Timestamp(x.split(",")[0]) for x in data["klines"])
break
except Exception as e:
time.sleep(random.uniform(0.8, 1.5))
else:
failed_years.append(year)
if failed_years:
logger.warning(f"Calendar incomplete, failed years: {failed_years}")
return sorted(set(res_list))
calendar = _CALENDAR_MAP.get(bench_code, None)
if calendar is None:
@@ -246,7 +305,11 @@ def get_hs_stock_symbols() -> list:
# Add suffix after the stock code to conform to yahooquery standard, otherwise the data will not be fetched.
_symbols = [
_symbol + ".ss" if _symbol.startswith("6") else _symbol + ".sz" if _symbol.startswith(("0", "3")) else None
(
_symbol + ".ss"
if _symbol.startswith("6")
else _symbol + ".sz" if _symbol.startswith(("0", "3")) else None
)
for _symbol in _symbols
]
_symbols = [_symbol for _symbol in _symbols if _symbol is not None]
@@ -357,7 +420,14 @@ def get_us_stock_symbols(qlib_data_path: [str, Path] = None) -> list:
s_ = s_.strip("*")
return s_
_US_SYMBOLS = sorted(set(map(_format, filter(lambda x: len(x) < 8 and not x.endswith("WS"), _all_symbols))))
_US_SYMBOLS = sorted(
set(
map(
_format,
filter(lambda x: len(x) < 8 and not x.endswith("WS"), _all_symbols),
)
)
)
return _US_SYMBOLS
@@ -471,7 +541,10 @@ def get_en_fund_symbols(qlib_data_path: [str, Path] = None) -> list:
raise ValueError("request error")
try:
_symbols = []
for sub_data in re.findall(r"[\[](.*?)[\]]", resp.content.decode().split("= [")[-1].replace("];", "")):
for sub_data in re.findall(
r"[\[](.*?)[\]]",
resp.content.decode().split("= [")[-1].replace("];", ""),
):
data = sub_data.replace('"', "").replace("'", "")
# TODO: do we need other information, like fund_name from ['000001', 'HXCZHH', '华夏成长混合', '混合型', 'HUAXIACHENGZHANGHUNHE']
_symbols.append(data.split(",")[0])
@@ -652,7 +725,11 @@ def get_instruments(
"""
_cur_module = importlib.import_module("data_collector.{}.collector".format(market_index))
obj = getattr(_cur_module, f"{index_name.upper()}Index")(
qlib_dir=qlib_dir, index_name=index_name, freq=freq, request_retry=request_retry, retry_sleep=retry_sleep
qlib_dir=qlib_dir,
index_name=index_name,
freq=freq,
request_retry=request_retry,
retry_sleep=retry_sleep,
)
getattr(obj, method)()
@@ -660,7 +737,10 @@ def get_instruments(
def _get_all_1d_data(_date_field_name: str, _symbol_field_name: str, _1d_data_all: pd.DataFrame):
df = copy.deepcopy(_1d_data_all)
df.reset_index(inplace=True)
df.rename(columns={"datetime": _date_field_name, "instrument": _symbol_field_name}, inplace=True)
df.rename(
columns={"datetime": _date_field_name, "instrument": _symbol_field_name},
inplace=True,
)
df.columns = list(map(lambda x: x[1:] if x.startswith("$") else x, df.columns))
return df