1
0
mirror of https://github.com/microsoft/qlib.git synced 2026-07-17 01:14:35 +08:00

add CSI500 data collector

This commit is contained in:
BigTreei
2020-12-26 16:51:52 +08:00
committed by Linlang Lv (iSoftStone)
parent ec8969a3ae
commit 74cc21fc2c
4 changed files with 146 additions and 92 deletions

View File

@@ -1,4 +1,4 @@
# CSI300/CSI100 History Companies Collection # CSI300/CSI100/CSI500 History Companies Collection
## Requirements ## Requirements
@@ -15,7 +15,7 @@ python collector.py --index_name CSI300 --qlib_dir ~/.qlib/qlib_data/cn_data --m
# parse new companies # parse new companies
python collector.py --index_name CSI300 --qlib_dir ~/.qlib/qlib_data/cn_data --method save_new_companies python collector.py --index_name CSI300 --qlib_dir ~/.qlib/qlib_data/cn_data --method save_new_companies
# index_name support: CSI300, CSI100 # index_name support: CSI300, CSI100, CSI500
# help # help
python collector.py --help python collector.py --help
``` ```

View File

@@ -5,6 +5,7 @@ import re
import abc import abc
import sys import sys
import importlib import importlib
from tqdm import tqdm
from io import BytesIO from io import BytesIO
from typing import List, Iterable from typing import List, Iterable
from pathlib import Path from pathlib import Path
@@ -12,6 +13,8 @@ from pathlib import Path
import fire import fire
import requests import requests
import pandas as pd import pandas as pd
import baostock as bs
from lxml import etree
from loguru import logger from loguru import logger
CUR_DIR = Path(__file__).resolve().parent CUR_DIR = Path(__file__).resolve().parent
@@ -44,6 +47,7 @@ def retry_request(url: str, method: str = "get", exclude_status: List = None):
class CSIIndex(IndexBase): class CSIIndex(IndexBase):
@property @property
def calendar_list(self) -> List[pd.Timestamp]: def calendar_list(self) -> List[pd.Timestamp]:
"""get history trading date """get history trading date
@@ -314,6 +318,7 @@ class CSIIndex(IndexBase):
class CSI300(CSIIndex): class CSI300(CSIIndex):
@property @property
def index_code(self): def index_code(self):
return "000300" return "000300"
@@ -324,10 +329,11 @@ class CSI300(CSIIndex):
@property @property
def html_table_index(self): def html_table_index(self):
return 1 return 0
class CSI100(CSIIndex): class CSI100(CSIIndex):
@property @property
def index_code(self): def index_code(self):
return "000903" return "000903"
@@ -338,16 +344,54 @@ class CSI100(CSIIndex):
@property @property
def html_table_index(self): def html_table_index(self):
return 2 return 1
class CSI500(CSIIndex):
@property
def index_code(self):
return "000905"
@property
def bench_start_date(self) -> pd.Timestamp:
return pd.Timestamp("2007-01-15")
@property
def html_table_index(self):
return 0
def get_changes(self):
return self.get_changes_with_history_companies(self.get_history_companies())
def get_history_companies(self):
"""
Data sourcehttp://baostock.com/baostock/index.php/%E4%B8%AD%E8%AF%81500%E6%88%90%E5%88%86%E8%82%A1
Avoid a large number of parallel data acquisition,
such as 1000 times of concurrent data acquisition, because IP will be blocked
Returns
-------
"""
lg = bs.login()
today = pd.datetime.now()
date_range = pd.DataFrame(pd.date_range(start="2007-01-15", end=today, freq="7D"))[0].dt.date
ret_list = []
col = ["date", "symbol", "code_name"]
for date in tqdm(date_range, desc="Download CSI500"):
rs = bs.query_zz500_stocks(date=str(date))
zz500_stocks = []
while (rs.error_code == "0") & rs.next():
zz500_stocks.append(rs.get_row_data())
result = pd.DataFrame(zz500_stocks, columns=col)
result["symbol"] = result["symbol"].apply(lambda x: x.replace(".", "").upper())
ret_list.append(result[["date", "symbol"]])
bs.logout()
return pd.concat(ret_list, sort=False)
def get_instruments( def get_instruments(
qlib_dir: str, qlib_dir: str, index_name: str, method: str = "parse_instruments", request_retry: int = 5, retry_sleep: int = 3
index_name: str,
method: str = "parse_instruments",
freq: str = "day",
request_retry: int = 5,
retry_sleep: int = 3,
): ):
""" """

View File

@@ -1,5 +1,8 @@
baostock
logure
fire fire
requests requests
pandas pandas
lxml lxml
loguru loguru
tqdm

View File

@@ -28,15 +28,14 @@ SZSE_CALENDAR_URL = "http://www.szse.cn/api/report/exchange/onepersistenthour/mo
CALENDAR_BENCH_URL_MAP = { CALENDAR_BENCH_URL_MAP = {
"CSI300": CALENDAR_URL_BASE.format(market=1, bench_code="000300"), "CSI300": CALENDAR_URL_BASE.format(market=1, bench_code="000300"),
"CSI500": CALENDAR_URL_BASE.format(market=1, bench_code="000905"),
"CSI100": CALENDAR_URL_BASE.format(market=1, bench_code="000903"), "CSI100": CALENDAR_URL_BASE.format(market=1, bench_code="000903"),
# NOTE: Use the time series of SH600000 as the sequence of all stocks # NOTE: Use the time series of SH600000 as the sequence of all stocks
"ALL": CALENDAR_URL_BASE.format(market=1, bench_code="000905"), "ALL": CALENDAR_URL_BASE.format(market=1, bench_code="000905"),
# NOTE: Use the time series of ^GSPC(SP500) as the sequence of all stocks # NOTE: Use the time series of ^GSPC(SP500) as the sequence of all stocks
"US_ALL": "^GSPC", "US_ALL": "^GSPC",
"IN_ALL": "^NSEI",
} }
_BENCH_CALENDAR_LIST = None _BENCH_CALENDAR_LIST = None
_ALL_CALENDAR_LIST = None _ALL_CALENDAR_LIST = None
_HS_SYMBOLS = None _HS_SYMBOLS = None
@@ -234,13 +233,16 @@ def get_us_stock_symbols(qlib_data_path: [str, Path] = None) -> list:
resp = requests.get(url) resp = requests.get(url)
if resp.status_code != 200: if resp.status_code != 200:
raise ValueError("request error") raise ValueError("request error")
try: try:
_symbols = [_v["f12"].replace("_", "-P") for _v in resp.json()["data"]["diff"].values()] _symbols = [_v["f12"].replace("_", "-P") for _v in resp.json()["data"]["diff"].values()]
except Exception as e: except Exception as e:
logger.warning(f"request error: {e}") logger.warning(f"request error: {e}")
raise raise
if len(_symbols) < 8000: if len(_symbols) < 8000:
raise ValueError("request error") raise ValueError("request error")
return _symbols return _symbols
@deco_retry @deco_retry
@@ -273,6 +275,7 @@ def get_us_stock_symbols(qlib_data_path: [str, Path] = None) -> list:
resp = requests.post(url, json=_parms) resp = requests.post(url, json=_parms)
if resp.status_code != 200: if resp.status_code != 200:
raise ValueError("request error") raise ValueError("request error")
try: try:
_symbols = [_v["symbolTicker"].replace("-", "-P") for _v in resp.json()] _symbols = [_v["symbolTicker"].replace("-", "-P") for _v in resp.json()]
except Exception as e: except Exception as e:
@@ -449,7 +452,9 @@ def symbol_prefix_to_sufix(symbol: str, capital: bool = True) -> str:
def deco_retry(retry: int = 5, retry_sleep: int = 3): def deco_retry(retry: int = 5, retry_sleep: int = 3):
def deco_func(func): def deco_func(func):
@functools.wraps(func) @functools.wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
_retry = 5 if callable(retry) else retry _retry = 5 if callable(retry) else retry
@@ -458,10 +463,12 @@ def deco_retry(retry: int = 5, retry_sleep: int = 3):
try: try:
_result = func(*args, **kwargs) _result = func(*args, **kwargs)
break break
except Exception as e: except Exception as e:
logger.warning(f"{func.__name__}: {_i} :{e}") logger.warning(f"{func.__name__}: {_i} :{e}")
if _i == _retry: if _i == _retry:
raise raise
time.sleep(retry_sleep) time.sleep(retry_sleep)
return _result return _result