- 同步 Tushare 的股票交易数据到本地 MySQL 进行存储, 采用 T + 0 同步方式
- 首先从 Tushare 拉取全量历史数据
- 然后每日下午 从 Tushare 拉取当日增量数据
- 数据包含: A股、港股、日线、周线、月线等
apt-get install libmysqlclient-dev
pip install pandas
pip install numpy==1.21.5
pip install pymysql
pip install tushare
pip install sqlalchemy
pip install mysqlclient
CREATE DATABASE stock;
CREATE USER 'stock'@'%' IDENTIFIED BY 'stock';
GRANT ALL PRIVILEGES ON stock.* TO 'stock'@'%' ;
FLUSH PRIVILEGES;mv application.ini application.ini
# 然后修改 application.ini 中的 mysql 的地址信息 和 Tushare 账号 tokenpython data_syn.py --mode init说明1: 执行前要求 application.ini 配置中的 mysql.database 库已创建, 程序会自动在该数据库下创建数据表
说明2: Tushare 对不同积分的账户存在权限限制, 本程序代码要求 积分额度 2000 以上, 如不足可根据权限自行删减 data_syn.py 中的部分表
说明3: 部分表数据同步存在流量限制, 全量初始化时间相对较长
python data_syn.py --mode append说明1: 部分表数据量相对较小或者不具备增量同步逻辑,因此选择每日全量同步
沪深股票-行情数据-A股日线行情(daily表数据)
id |ts_code |trade_date|open |high |low |close |pre_close|change|pct_chg|vol |amount |
---+---------+----------+------+------+------+------+---------+------+-------+---------+-----------+
1|689009.SH|2022-09-09| 43.05| 43.9| 42.7| 43.7| 43.03| 0.67| 1.5571| 15372.24| 66680.763|
2|873223.BJ|2022-09-09| 3.8| 3.87| 3.79| 3.82| 3.79| 0.03| 0.7916| 6070.21| 2322.889|
3|873169.BJ|2022-09-09| 6.38| 6.41| 6.36| 6.39| 6.38| 0.01| 0.1567| 1865.95| 1190.126|
4|872925.BJ|2022-09-09| 15.89| 15.92| 15.68| 15.87| 15.92| -0.05|-0.3141| 20.5| 32.486|
5|871981.BJ|2022-09-09| 16.8| 16.8| 16.58| 16.75| 16.68| 0.07| 0.4197| 686.83| 1142.099|
6|871970.BJ|2022-09-09| 9.01| 9.03| 9.01| 9.02| 9.01| 0.01| 0.111| 119.0| 107.361|
7|871857.BJ|2022-09-09| 9.11| 9.11| 8.96| 9.0| 8.99| 0.01| 0.1112| 57.68| 51.906|
8|871642.BJ|2022-09-09| 7.04| 7.04| 6.89| 7.04| 7.04| 0.0| 0.0| 552.55| 383.902|
| MySQL表名 | Tushare 接口名 | 数据说明 |
|---|---|---|
| bak_basic | bak_basic | 沪深股票-基础信息-备用列表(受限:2/min) |
| concept | concept | 沪深股票-市场参考数据-概念股分类(已经停止维护) |
| concept_detail | concept_detail | 沪深股票-市场参考数据-概念股列表 (已经停止维护) |
| cyq_perf | cyq_perf | 沪深股票-特色数据-每日筹码及胜率 (受限:5/min,10/h) |
| cyq_chips | cyq_chips | 沪深股票-市场参考数据-每日筹码分布 (受限:5/min,10/h) |
| bak_daily | bak_daily | 沪深股票-行情数据-备用行情 |
import datetime
import time
from utils.utils import get_tushare_api,get_mock_connection,get_logger,exec_mysql_sql,min_date
def exec_sync_without_ts_code(table_name, api_name, fields,
date_column, start_date, end_date, date_step, limit, interval):
"""
执行数据同步并存储
:param table_name: 表名
:param api_name: API 名
:param fields: 字段列表
:param date_column: 增量时间字段列
:param start_date: 开始时间
:param end_date: 结束时间
:param date_step: 分段查询间隔, 由于 Tushare 分页查询存在性能瓶颈, 因此采用按时间分段拆分微批查询
:param limit: 每次查询的记录条数
:param interval: 每次查询的时间间隔
:return: None
"""
# 创建 API / Connection / Logger 对象
ts_api = get_tushare_api()
connection = get_mock_connection()
logger = get_logger(table_name, 'data_syn.log')
# 清理历史数据
clean_sql = "DELETE FROM %s WHERE %s>='%s' AND %s<='%s'" % \
(table_name, date_column, start_date, date_column, end_date)
logger.info('Execute Clean SQL [%s]' % clean_sql)
exec_mysql_sql(clean_sql)
# 数据同步时间开始时间和结束时间, 包含前后边界
start = datetime.datetime.strptime(start_date, '%Y%m%d')
end = datetime.datetime.strptime(end_date, '%Y%m%d')
step_start = start # 微批开始时间
step_end = min_date(start + datetime.timedelta(date_step - 1), end) # 微批结束时间
while step_start <= end:
start_date = str(step_start.strftime('%Y%m%d'))
end_date = str(step_end.strftime('%Y%m%d'))
offset = 0
while True:
logger.info("Query [%s] from tushare with api[%s] start_date[%s] end_date[%s]"
" from offset[%d] limit[%d]" % (table_name, api_name, start_date, end_date, offset, limit))
data = ts_api.query(api_name,
**{
"start_date": start_date,
"end_date": end_date,
"offset": offset,
"limit": limit
},
fields=fields)
time.sleep(interval)
if data.last_valid_index() is not None:
size = data.last_valid_index() + 1
logger.info('Write [%d] records into table [%s] with [%s]' % (size, table_name, connection.engine))
data.to_sql(table_name, connection, index=False, if_exists='append', chunksize=limit)
offset = offset + size
if size < limit:
break
else:
break
# 更新下一次微批时间段
step_start = step_start + datetime.timedelta(date_step)
step_end = min_date(step_end + datetime.timedelta(date_step), end)以 沪深股票-行情数据-A股日线行情(daily)为例
from utils.utils import exec_mysql_script, exec_sync_without_ts_code
import os
import datetime
def init():
# 创建表
dir_path = os.path.join(os.path.dirname(os.path.abspath(__file__)))
exec_mysql_script(dir_path)
exec_sync_without_ts_code(
table_name='daily',
api_name='daily',
fields=[
"ts_code",
"trade_date",
"open",
"high",
"low",
"close",
"pre_close",
"change",
"pct_chg",
"vol",
"amount"
],
start_date='19901219',
end_date=str(datetime.datetime.now().strftime('%Y%m%d')),
date_step=1,
limit=5000,
interval=0.3
)其中: start_date 可以前回溯一段时间, 防止原始数据中断或者延迟导致的数据丢失
from utils.utils import exec_sync_without_ts_code
import datetime
def append():
exec_sync_without_ts_code(
table_name='daily',
api_name='daily',
fields=[
"ts_code",
"trade_date",
"open",
"high",
"low",
"close",
"pre_close",
"change",
"pct_chg",
"vol",
"amount"
],
start_date=str((datetime.datetime.now() + datetime.timedelta(days=-7)).strftime('%Y%m%d')),
end_date=str(datetime.datetime.now().strftime('%Y%m%d')),
date_step=1,
limit=5000,
interval=0.3
)相关日志打印存储在 项目根目录/logs/data_syn.log 文件中, 日志示例
ssh://anaconda@47.240.xxx.xxx:22/home/anaconda/anaconda3/bin/python -u /app/stock_tushare_syn/data_syn.py --mode init
2022-09-10 09:57:57,588 - stock_basic - INFO - Execute SQL [DROP TABLE IF EXISTS `stock_basic`]
2022-09-10 09:57:57,732 - stock_basic - INFO - Execute SQL [CREATE TABLE `stock_basic`(`id` bigint NOT NULL AUTO_INCREMENT COMMENT ' 主键 ',`ts_code` varchar(16) DEFAULT NULL COMMENT ' TS代码 ',`symbol` varchar(16) DEFAULT NULL COMMENT ' 股票代码 ',`name` varchar(32) DEFAULT NULL COMMENT ' 股票名称 ',`area` varchar(32) DEFAULT NULL COMMENT ' 地域 ',`industry` varchar(32) DEFAULT NULL COMMENT ' 所属行业 ',`fullname` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT ' 股票全称 ',`enname` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT ' 英文全称 ',`cnspell` varchar(32) DEFAULT NULL COMMENT ' 拼音缩写 ',`market` varchar(32) DEFAULT NULL COMMENT ' 市场类型:主板/创业板/科创板/CDR',`exchange` varchar(32) DEFAULT NULL COMMENT ' 交易所代码 ',`curr_type` varchar(32) DEFAULT NULL COMMENT ' 交易货币 ',`list_status` varchar(32) DEFAULT NULL COMMENT ' 上市状态 L上市 D退市 P暂停上市 ',`list_date` int DEFAULT NULL COMMENT ' 上市日期 ',`delist_date` int DEFAULT NULL COMMENT ' 退市日期 ',`is_hs` varchar(32) DEFAULT NULL COMMENT ' 是否沪深港通标:N否 H沪股通 S深股通 ',PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4902 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='基础信息']
2022-09-10 09:57:57,768 - stock_basic - INFO - Execute result: Total [2], Succeed [2] , Failed [0]
2022-09-10 09:57:58,045 - stock_basic - INFO - Query data from tushare with api[stock_basic], fields[ts_code,symbol,name,area,industry,fullname,enname,cnspell,market,exchange,curr_type,list_status,list_date,delist_date,is_hs]
2022-09-10 09:57:59,047 - stock_basic - INFO - Write [4909] records into table [stock_basic] with [Engine(mysql://root:***@47.240.xxx.xxx:3320/stock?charset=utf8&use_unicode=1)]
2022-09-10 09:57:59,683 - trade_cal - INFO - Execute SQL [DROP TABLE IF EXISTS `trade_cal`]
2022-09-10 09:57:59,696 - trade_cal - INFO - Execute SQL [CREATE TABLE `trade_cal`(`id` bigint NOT NULL AUTO_INCREMENT COMMENT ' 主键 ',`exchange` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '交易所 SSE上交所 SZSE深交所',`cal_date` int DEFAULT NULL COMMENT '日历日期',`is_open` varchar(2) DEFAULT NULL COMMENT '是否交易 0休市 1交易',`pretrade_date` int DEFAULT NULL COMMENT '上一个交易日',PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='交易日历']
2022-09-10 09:57:59,716 - trade_cal - INFO - Execute result: Total [2], Succeed [2] , Failed [0]
2022-09-10 09:57:59,720 - trade_cal - INFO - Query data from tushare with api[trade_cal], start[19700101]- end[20220910], fields[exchange,cal_date,is_open,pretrade_date]
2022-09-10 09:58:00,476 - trade_cal - INFO - Write [11589] records into table [trade_cal] with [Engine(mysql://root:***@47.240.xxx.xxx:3320/stock?charset=utf8&use_unicode=1)]
2022-09-10 09:58:00,929 - name_change - INFO - Execute SQL [DROP TABLE IF EXISTS `name_change`]
2022-09-10 09:58:00,944 - name_change - INFO - Execute SQL [CREATE TABLE `name_change`(`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',`ts_code` varchar(16) DEFAULT NULL COMMENT 'TS代码',`name` varchar(32) DEFAULT NULL COMMENT '证券名称',`start_date` int DEFAULT NULL COMMENT '开始日期',`end_date` int DEFAULT NULL COMMENT '结束日期',`ann_date` int DEFAULT NULL COMMENT '公告日期',`change_reason` varchar(64) DEFAULT NULL COMMENT '变更原因',PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='股票曾用名']
2022-09-10 09:58:00,964 - name_change - INFO - Execute result: Total [2], Succeed [2] , Failed [0]
2022-09-10 09:58:00,967 - name_change - INFO - Query data from tushare with api[namechange], fields[ts_code,name,start_date,end_date,ann_date,change_reason]
2022-09-10 09:58:01,700 - name_change - INFO - Write [10000] records into table [stock_basic] with [Engine(mysql://root:***@47.240.xxx.xxx:3320/stock?charset=utf8&use_unicode=1)]欢迎提问或 提交 Bug / PR