问题

从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. Deepseek学习随笔(10)--- 本地AI神器Cherry Studio & Chatbox 保姆级教程(附网盘链接)

    本篇介绍的 Cherry Studio 和 Chatbox 两款工具,只需简单几步,即可实现本地化部署AI能力,支持对话.编程.绘图等多场景应用.本文将手把手教你从零开始配置使用! 一.软件下载:两步 ...

  2. DataX - [03] 使用案例

    题记部分 001 || mysql2hdfs (1)查看MySQL被迁移的数据情况 (2)根据需求确定reader为mysqlreader,writer为hdfswriter 查看reader和wri ...

  3. Scala定义方法,可变参数,默认值,递归

    package com.wyh.day01 object ScalaFun1 { def main(args: Array[String]): Unit = { val result = string ...

  4. linux下配置ip为动态获取

    点击查看代码 在Linux系统中配置网络接口以动态获取IP地址,通常需要使用DHCP(Dynamic Host Configuration Protocol).大多数现代Linux发行版都默认支持这个 ...

  5. ccrc 评审资料该如何编写【通用型】

    核心思路:各个过程中和安全检查内容进行对应 首先应该有信息安全服务规范 其次准备好平时项目的所有文档 第三,参照规范内容,每一项要求[准备.需求.设计.编码.测试.验收.维保]在原有文档上进行筛选和补 ...

  6. 写一个简单的SQL生成工具

    知识点: MyBatis 语法概览 MyBatis 是一个强大的数据持久化框架,它提供了一种半自动化的 ORM 实现方式.通过 MyBatis,开发者可以通过简单的 XML 或注解来配置和映射原生信息 ...

  7. 基于React的虚拟滚动方案

    基于React的虚拟滚动方案 在渲染列表时我们通常会一次性将所有列表项渲染到DOM中,在数据量大的时候这种操作会造成页面响应缓慢,因为浏览器需要处理大量的DOM元素.而此时我们通常就需要虚拟滚动来实现 ...

  8. Redis 持久化机制简介【Redis 系列之三】

    〇.前言 Redis 持久化主要有两种:RDB(数据快照模式).AOF(追加模式),另外就是这两种模式的混合模式用. 本文将对这三种情况进行详细介绍. 博主 Redis 相关文章都在这里了:https ...

  9. 编程神器Trae:当我用上后,才知道自己的创造力被低估了多少

    "AI会让每个人都能成为工具创造者,打破你能力边界,有时候只需要一个想法." AI粉嫩特攻队,2025年3月23日. 前几天参加了一场行业闭门研讨会,满满1个半小时的干货演讲让我收 ...

  10. FastAPI Pydantic动态调整Schema

    title: FastAPI Pydantic动态调整Schema date: 2025/3/29 updated: 2025/3/29 author: cmdragon excerpt: Pydan ...