问题

从TuShare获取的数据,停牌日是没有数据的,这将会在回测时,不能直接参与账户的净值计算,导致账户的净值以及收益计算不准确。

停盘

股票由于某种消息或进行某种活动引起股价的连续上涨或下跌,由证券交易所暂停其在股票市场上进行交易。待情况澄清或企业恢复正常后,再复牌在交易所挂牌交易。

解决方法

1、 增加is_trading字段,用于区分停牌日还是交易日

2、补充停牌日的日k数据,更加当前数据现状,填充一个交易日的close、volume、high、low为停牌前最后一个交易日的close、volume为0,is_trading为false

填充指定时间段的is_trading

流程图

代码实现

1、获取所有交易日列表

由于指数比如上证指数(000001)是不会停牌的,因此可通过指数来获得交易日期

def get_trading_date(begin_date= None,end_date=None):
"""
获取指定日期范围的按照正序排列的交易日列表
如果没有指定日期范围,则获取从当期日期向前365个自然日内的所有交易日 :param begin_date: 开始日期
:param end_date: 结束日期
:return: 交易日期列表
"""
#当前日期
now = datetime.now()
#开始日期,默认当前日期向前365个自然日
if begin_date is None:
#当前日期减去365天
one_year_ago = now - timedelta(days=365)
#转换成str类型
begin_date = one_year_ago.strftime("%Y-%m-%d")
#结束日期默认为今天
if end_date is None:
end_date = now.strftime("%Y-%m-%d") #用上证指数000001作为查询条件,因为指数是不会停盘的,所以可以查询到所有的交易日期
daily_cursor = DB_CONN.daily.find(
{"code":"000001",'date':{'$gte':begin_date,'$lte':end_date},'index':True},
sort=[('date',ASCENDING)],
projection={'date':True,'_id':False}
)
#转换日期列表
dates = [x['date'] for x in daily_cursor]
return dates

2、填充某个日行情数据的is_trading,并更新数据库(同样采用bulk_write将更新数据写入数据集)

def fill_is_trading_between(begin_date=None,end_date=None):
"""
填充指定时间段内的is_trading字段
:param begin_date :开始日期
:param end_date :结束日期
"""
#获取指定日期范围的左右交易日子列表,按日期正序排列
all_dates = get_trading_date(begin_date,end_date)
#循环填充所有交易日的is_trading字段
for date in all_dates:
#填充daily数据集
fill_single_date_is_trading(date,'daily')
#填充daily_hfq数据集
fill_single_date_is_trading(date,'daily_hfq') def fill_single_date_is_trading(date,collection_name):
"""
填充某一个日行情的数据集的is_trading
:param date: 日期
:param collection_name: 集合名称
"""
print('填充字段,字段名:is_trading,日期:%s,数据集:%s' %(date,collection_name))
daily_cursor = DB_CONN[collection_name].find(
{'date':date},
projection={'code':True,'volume':True,'_id':False},
batch_size = 1000
}) update_requests = []
for daily in daily_cursor:
#当日成交量大于0,则为交易状态
is_trading = daily['volume']>0
update_requests.append(
UpdateOne(
{'code':daily['code'],'date':date},
{'$set':{'is_trading':is_trading}}
)
)
if len(update_requests):
update_result = DB_CONN[collection_name].bulk_write(update_requests,ordered=False)
print("填充字段,字段名:is_trading,日期:%s,数据集:%s,更新:%4d"%
(date,collection_name,update_result.modified_count),flush=True)

获取股票基本信息

用途

  • 获取每日的股票列表

主要字段

  • 股票代码
  • 股票名称
  • 股本(总股本、流通股本)
  • 上市日期
  • 日期

通过采用TuShare中get_stock_basics接口获取股票的基本信息

get_stock_basics()接口信息:

def get_stock_basics(date=None):
"""
获取沪深上市公司基本情况
Parameters
date:日期YYYY-MM-DD,默认为上一个交易日,目前只能提供2016-08-09之后的历史数据 Return
--------
DataFrame
code,代码
name,名称
industry,细分行业
area,地区
pe,市盈率
outstanding,流通股本
totals,总股本(万)
totalAssets,总资产(万)
liquidAssets,流动资产
fixedAssets,固定资产
reserved,公积金
reservedPerShare,每股公积金
eps,每股收益
bvps,每股净资
pb,市净率
timeToMarket,上市日期
"""

详细代码实现

from database import DB_CONN
from datetime import datetime,timedelta
import tushare as ts
from pymongo import UpdateOne
from stock_util import get_trading_date """
从tushare中获取股票的基础数据,保存在本地MongoDB中
"""
def crawl_basic(begin_date=None,end_date=None):
"""
抓取指定时间范围的股票基础信息
:param begin_date:开始时间
:param end_date:结束时间
"""
#如果没有指定日期,则默认为前一日
if begin_date is None :
begin_date = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
if end_date is None:
end_date = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d") #获取指定日期范围的所有交易日列表
all_dates = get_trading_date(begin_date,end_date)
#按每个交易日抓取
for date in all_dates:
try:
#抓取当日的基本信息
crawl_basic_at_date(date)
except:
print("抓取股票基本信息出错,日期:%s" %date,flush=True) def crawl_basic_at_date(date):
"""
从Tushare抓取指定日期的股票基本信息
:param date: 日期
"""
#从tushare获取基本信息,index是股票代码列表
df_basics = ts.get_stock_basics(date)
#如果当日没有基本信息,不做操作
if df_basics is None:
return #初始化更新列表
update_requests=[]
codes = set(df_basics.index)
#按照股票代码提取所有数据
for code in codes:
#获取一只股票的数据
doc = df_basics.loc[code]
try:
#将上市日期,19971113转换成199-11-13
time_to_market = datetime.strptime(str(doc['timeToMarket']),"%Y%m%d").strftime('%Y-%m-%d')
#将总股本和流通股本转为数字
totals = float(doc['totals'])
outstanding = float(doc['outstanding']) #组合基本信息文档
doc.update(
{
#股票代码
'code':code,
#日期
'date':date,
#上市时间
'timeToMarket':time_to_market,
#流通股本
'outstanding':outstanding,
#总股本
'totals':totals
}
)
#生成更新请求,按照code、date创建索引
update_requests.append(
UpdateOne(
{'code':code,'date':date},
{'$set':doc},
upsert=True
)
)
except:
print('发生异常,股票代码:%s,日期:%s' %(code,date),flush=True)
print(doc,flush=True)
if len(update_requests)>0:
update_result = DB_CONN['basic'].bulk_write(update_requests,ordered=False)
print("抓取股票基本信息,日期:%s,插入:%4d条,更新:%4d条" %(date,update_result.upserted_count,update_result.modified_count),flush=True)

执行上述程序后,会将股票基本信息(包括,股票代码,日期,上市时间,流通股本和总股本)保存在MongoDB中名为basic的集合(表)中

填充停牌日的行情数据

代码实现:

ef fill_daily_k_at_suspension_days(begin_date=None,end_date=None):
"""
填充指定日期范围内,股票停牌日的行情数据
填充时,停牌的开盘价、最高价、最低价和收盘价都为最近一个交易日的收盘价,成交量为0
is_trading为False
"""
#当前日期的前一天
before = datetime.now() - timedelta(days=1)
#找到据当前最近一个交易日的所有股票的基本信息
basics =[]
while 1:
#转化成str
last_trading_date = before.strftime("%Y-%m-%d")
#因为Tushare的基本信息从2016-08-09开始,如果早于这个时间就结束查找
if last_trading_date < '2016-08-09':
break
#找当日的基本信息
basic_cursor = DB_CONN['basic'].find(
{'date':last_trading_date},
#填充时需要用到两个子段:股票代码code和上市日期timeToMarket,上市日期用来判断是否上市
projection={'code':True,'timeToMarket':True,'_id':False},
#一次性返回5000条数据,可以降低网络IO开销,提高速度
batch_size=5000
)
#将数据放到basics列表中
basics = [basic for basic in basic_cursor]
#如果查到数据,跳出循环
if len(basics)>0:
break
#没有找到数据,则继续向前一天
before = before-timedelta(days=1) #获取指定日期范围内所有交易日列表
all_dates = get_trading_date(begin_date,end_date) #填充daily数据集中的停牌日数据
fill_daily_k_at_suspension_days_at_date_at_one_collections(basics,all_dates,'daily')
#填充daily_hfq数据集中的停牌日数据
fill_daily_k_at_suspension_days_at_date_at_one_collections(basics,all_dates,'daily_hfq') def fill_daily_k_at_suspension_days_at_date_at_one_collections(basics,all_dates,collection):
"""
更新单个数据集的单个日期的数据
:param basic:基本信息
:param all_dates:日期列表
:param collection:集合名
"""
code_last_trading_daily_dict = dict()
for date in all_dates:
update_requests = []
last_daily_code_set = set(code_last_trading_daily_dict.keys())
for basic in basics:
code = basic['code']
#如果循环日期小于上市日期
if date < basic['timeToMarket']:
print('日期:%s,%s还没上市,上市日期为%s'%(date,code,basic['timeToMarket']),flush=True)
else:
#找到当日数据
daily = DB_CONN[collection].find(
{'code':code,'date':date}
)
if daily is not None:
code_last_trading_daily_dict[code] = daily
last_daily_code_set.add(code)
else:
if code in last_daily_code_set:
last_trading_daily = code_last_trading_daily_dict[code]
suspension_daily_doc = {
'code':code,
'date':date,
'close':last_trading_daily['close'],
'open':last_trading_daily['close'],
'high':last_trading_daily['close'],
'low':last_trading_daily['close'],
'volume':0,
'is_trading':False
}
update_requests.append(
UpdateOne(
{'code':code,'date':date},
{'$set':suspension_daily_doc},
upsert=True
)
)
if len(update_requests)>0:
update_result = DB_CONN[collection].bulk_write(update_requests,ordered=False)
print('填充停牌数据,日期:%s,数据集:%s,插入:%4d条,更新:%4d条'%(date,collection,update_result.upserted_count,update_result.modified_count),flush=True)

补充停牌的日K数据的更多相关文章

  1. android 股票数据通过日K获取周K的数据 算法 源码

    目前的数据是从新浪接口获取的, http://biz.finance.sina.com.cn/stock/flash_hq/kline_data.php?symbol=sh600000&end ...

  2. 分支-15. 日K蜡烛图

    /* * Main.c * 分支-15. 日K蜡烛图 * Created on: 2014年6月18日 * Author: Boomkeeper ****测试通过***** */ #include & ...

  3. 日K蜡烛图

    股票价格涨跌趋势,常用蜡烛图技术中的K线图来表示,分为按日的日K线.按周的周K线.按月的月K线等.以日K线为例,每天股票价格从开盘到收盘走完一天,对应一根蜡烛小图,要表示四个价格:开盘价格Open(早 ...

  4. MySql 按周/月/日统计数据的方法

    知识关键词:DATE_FORMAT  select DATE_FORMAT(create_time,'%Y%u') weeks,count(caseid) count from tc_case gro ...

  5. Hive分组取Top K数据

    阿里交叉面试问到了这个题,当时感觉没有答好,主要是对Hive这块还是不熟悉,其实可以采用row_number()函数. 1.ROW_NUMBER,RANK(),DENSE_RANK() 语法格式:ro ...

  6. MySQL 根据年、季度、月、周、日统计数据

    -- 计算每年订单的总价格 select date_format(t.order_time,'%Y') years,sum(t.order_amount) '总价格' from lf_order t ...

  7. 分支-15. 日K蜡烛图(15)

    #include<iostream> using namespace std; int main(){ float o,h,l,c; while(cin>>o>>h ...

  8. 获取日k数据

    http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?_var=kline_dayqfq&param=sz002921,day,,,320,qfq ...

  9. 按月、按日进行数据统计的Mysql语句

    <select id="getCustomerTJByUser" parameterType="map" resultType="map&quo ...

  10. Agumaster 增加日交易数据列表

随机推荐

  1. vscode launch program "xxx" does not exist

    Error Solution Please Click The Build Button Other This maybe is the one of Makefile Tools or 'c++ e ...

  2. 使用 Visual Paradigm 的业务流程模型和符号 (BPMN) 综合指南

    业务流程模型和符号 (BPMN) 是一种用于建模和记录业务流程的标准化图形符号.它被广泛采用,因为它能够提供一种清晰.通用的语言,所有利益相关者(业务分析师.技术开发人员和管理人员)都能理解.Visu ...

  3. 探秘Transformer系列之(7)--- embedding

    探秘Transformer系列之(7)--- embedding 0x00 概要 在Transformer中,把每个 Token(对应离散的输入数据,如单词或符号)映射到高维稠密向量空间的工作是由嵌入 ...

  4. Vuex:让状态管理不再头疼的“管家”

    如果你正在开发一个 Vue.js 应用程序,但发现自己被各种组件之间的状态共享问题搞得焦头烂额,那么 Vuex 就是你需要的"超级管家".Vuex 是专门为 Vue.js 设计的状 ...

  5. python list 差集

    前言 有时候我们希望基于list得到一个集合C,该集合C的元素可以被描述为元素在集合A中而不在集合B中.即:差集. 基于set A = [1, 2, 3] B = [2, 3, 4] C = set( ...

  6. this和super--java进阶day01

    1.this和super的代表 super是父类的标识符,如堆内存中的标志 2.this和super的访问 重点说访问构造方法,super()访问父类构造方法我们已经清楚,但是this()访问本类构造 ...

  7. 什么是RESTful 或 GraphQL?

    RESTful 与 GraphQL 深度解析 在前端的开发过程中,相信 everyone 对 Get.POST 等请求方式都很熟悉,那么这些请求是归于哪种架构或者设计风格可能又不是很熟.现在在这简单的 ...

  8. GitOps

    什么是GitOps 持续的GitOps,新时代的DevOps实践,通过Kubernetes集群来实现基于git的全自动化的流程来提高交付速度.简单地说,GitOps是使用Git拉请求管理基础设施部署和 ...

  9. GStreamer开发笔记(一):GStreamer介绍,在windows平台部署安装,打开usb摄像头对比测试

    前言   当前GStreamer是开源的多媒体框架,其适配后可以支持板卡的硬编码.硬解码,还提供RTSP服务器等功能,降低了音视频开发的门槛(转移到gstreamer配置和开发上了,但是跨平台),瑞芯 ...

  10. DAY3--launch文件编写

    1.launch启动文件简介 到目前为止,每当我们需要运行一个ROS节点或工具时,都需要打开一个新的终端运行一个命令.当系统中的节点数量不断增加时,"每个节点一个终端"的模式会变得 ...