基于python的可转债数据采集处理(开源代码)

【之前考虑不周,现在我把采集集思录数据的部分删除了,保留从新浪财经采集数据的代码,如果各位老师觉得还有什么不妥请告诉我~】写这篇文章的起因还是之前有人留言想知道我怎么每天自动计算双低策略轮动标的。在集思录潜水很久,终于碰到我会的领域,所以打算公开一下代码。

#

数据获取渠道
数据的获取渠道有很多,包括许多量化平台(如聚宽、优矿等)都会提供各种量化数据。但是使用量化平台有着许多限制:
1.量化平台往往要收取高额的费用,对于个人投资者而言负担较重。(当然,如果您所在的单位/学校/研究组采购了这些数据库,那么它自然是最方便的数据获取方式)
2.可转债市场在A股市场仍属于小众市场(成交量仅在百亿级别),因此许多量化平台的可转债数据种类都不太全面,甚至没有可转债数据。
3.量化平台数据库有更新延迟:以聚宽为例,可转债行情数据每日19:00、22:00更新,在盘中交易时间无法进行实时检测。

因此,我会使用简单的爬虫技术,从各个财经网站上获取实时数据,本篇文章将会详细介绍各种数据的采集方法和代码。

#

可转债数据采集

这里将介绍从各种网站采集数据的通用基本方法流程,如果想看采集代码可以直接往下滑。

#

可转债实时数据和基本面数据

首先打开想采集的数据所在的网页页面,以火狐浏览器为例,按F12调出开发者工具,切换到网络选项卡,刷新页面,可以看到我们发送的请求和返回值;一般数据都会以json或者csv格式返回,将消息头复制到新标签也打开,发现得到了一个json文件,粗略查看一下,确认是想要的数据,因此我们就可以得到一条获得数据的请求。

现在我们可以开始编写代码了:(经过考虑,将从集思录请求数据的代码删除了)

根据数据结构和我们的需要对采集的数据进行简单的处理:

{{{
​def process_data(self,data):

bond_list=[]
for one in data['rows']:
row=one['cell']
bond_info = {
'convert_value':float(row['convert_value']),
'pre_bond_id':row['pre_bond_id'],#带前缀
'code': row['bond_id'],
'short_name': row['bond_nm'],
'stock_name':row['stock_nm'] ,
'stock_code': row['stock_cd'],
'convert_price': float(row['convert_price']),
'last_cash_date': datetime.strptime(row['maturity_dt'],"%Y-%m-%d").date(),
# 发行总量
'raise_fund_volume':float(row['orig_iss_amt']),
'current_fund_volume':float(row['curr_iss_amt']),
# 当前市场收盘价格
'price':float(row['price']),
# 成交额
'day_market_volume':float(row['volume']),
# 溢价率
'convert_premium_ratio':float(row['premium_rt'].strip("%"))/100,
# 双低值
'double_low':float(row['dblow']),
'stock_price':float(row['sprice']),
'circulating_market_cap':float(row['float_shares'])*float(row['sprice']),
# 转债占流动市值比
'market_cap_ratio':float(row['convert_amt_ratio'].strip("%"))/100
}
if bond_info['price'] < 1:
# 停牌
continue

bond_list.append(bond_info)
bond_list = sorted(bond_list, key=lambda x: x['double_low'])
return bond_list

}}}

这样我们就得到了基础的可转债数据了。

#

可转债历史行情数据

同样的方法,我们也可以获取可转债的历史行情数据,来源是新浪财经:

{{{
from py_mini_racer import py_mini_racer
import pandas as pd
import requests
import datetime
import re
def bond_zh_hs_cov_daily(symbol: str = "sh113542") -> pd.DataFrame:
"""
新浪财经-债券-沪深可转债的历史行情数据, 大量抓取容易封IP
http://vip.stock.finance.sina.com.cn/mkt/#hskzz_z
:param symbol: 沪深可转债代码; e.g., sh010107
:type symbol: str
:return: 指定沪深可转债代码的日 K 线数据
:rtype: pandas.DataFrame
"""

res = requests.get(
zh_sina_bond_hs_cov_hist_url.format(
symbol, datetime.datetime.now().strftime("%Y_%m_%d")
)
)
js_code = py_mini_racer.MiniRacer()
js_code.eval(hk_js_decode)
dict_list = js_code.call(
"d", res.text.split("=")[1].split(";")[0].replace('"', "")
) # 执行js解密代码
data_df = pd.DataFrame(dict_list)
data_df.index = pd.to_datetime(data_df["date"])
del data_df["date"]
data_df = data_df.astype("float")
return data_df

}}}

##
正股数据采集

#

PE、PB指标

在《基于凯利公式的仓位控制及股性因子》一文(发在公众号了)中提到过,正股的市盈率、市净率等指标也是我们构建量化模型的因子,这些数据的获取过程和前面可转债数据采集的过程类似,下面直接给出代码。

{{{
import pandas as pd
import requests
def get_dat_stock_indicator(stock: str) -> pd.DataFrame:
"""
市盈率, 市净率, 股息率数据接口
:param stock: 通过 stock_a_indicator(stock="all") 来获取所有股票的代码
:type stock: str
:return: 市盈率, 市净率, 股息率查询
:rtype: pandas.DataFrame
"""

url = f"https://www.legulegu.com/s/base-info/{stock}"
r = requests.get(url)
temp_json = r.json()
temp_df = pd.DataFrame(temp_json["data"]["items"], columns=temp_json["data"]["fields"])
temp_df["trade_date"] = pd.to_datetime(temp_df["trade_date"])
temp_df.iloc[:, 1:] = temp_df.iloc[:, 1:].astype(float)
return temp_df

}}}

###
正股历史行情数据

正股的历史行情数据在溢价率的时间修正中也有着重要的作用,下面给出代码,数据来源是新浪财经。

{{{
​def stock_zh_a_daily(
symbol: str = "sz000001",
start_date: str = "19900101",
end_date: str = "21000118",
adjust: str = "",
) -> pd.DataFrame:
"""
新浪财经-A股-个股的历史行情数据, 大量抓取容易封 IP
https://finance.sina.com.cn/realstock/company/sh689009/nc.shtml
:param start_date: 20201103; 开始日期
:type start_date: str
:param end_date: 20201103; 结束日期
:type end_date: str
:param symbol: sh600000
:type symbol: str
:param adjust: 默认为空: 返回不复权的数据; qfq: 返回前复权后的数据; hfq: 返回后复权后的数据; hfq-factor: 返回后复权因子; hfq-factor: 返回前复权因子
:type adjust: str
:return: specific data
:rtype: pandas.DataFrame
"""
def _fq_factor(method):
if method == "hfq":
res = requests.get(zh_sina_a_stock_hfq_url.format(symbol))
hfq_factor_df = pd.DataFrame(
eval(res.text.split("=")[1].split("\n")[0])["data"]
)
if hfq_factor_df.shape[0] == 0:
raise ValueError("sina hfq factor not available")
hfq_factor_df.columns = ["date", "hfq_factor"]
hfq_factor_df.index = pd.to_datetime(hfq_factor_df.date)
del hfq_factor_df["date"]
return hfq_factor_df
else:
res = requests.get(zh_sina_a_stock_qfq_url.format(symbol))
qfq_factor_df = pd.DataFrame(
eval(res.text.split("=")[1].split("\n")[0])["data"]
)
if qfq_factor_df.shape[0] == 0:
raise ValueError("sina hfq factor not available")
qfq_factor_df.columns = ["date", "qfq_factor"]
qfq_factor_df.index = pd.to_datetime(qfq_factor_df.date)
del qfq_factor_df["date"]
return qfq_factor_df

if adjust in ("hfq-factor", "qfq-factor"):
return _fq_factor(adjust.split("-")[0])

res = requests.get(zh_sina_a_stock_hist_url.format(symbol))
js_code = py_mini_racer.MiniRacer()
js_code.eval(hk_js_decode)
dict_list = js_code.call(
"d", res.text.split("=")[1].split(";")[0].replace('"', "")
) # 执行js解密代码
data_df = pd.DataFrame(dict_list)
data_df.index = pd.to_datetime(data_df["date"])
del data_df["date"]
data_df = data_df.astype("float")
r = requests.get(zh_sina_a_stock_amount_url.format(symbol, symbol))
amount_data_json = demjson.decode(r.text[r.text.find("["): r.text.rfind("]") + 1])
amount_data_df = pd.DataFrame(amount_data_json)
amount_data_df.index = pd.to_datetime(amount_data_df.date)
del amount_data_df["date"]
temp_df = pd.merge(
data_df, amount_data_df, left_index=True, right_index=True, how="outer"
)
temp_df.fillna(method="ffill", inplace=True)
temp_df = temp_df.astype(float)
temp_df["amount"] = temp_df["amount"] * 10000
temp_df["turnover"] = temp_df["volume"] / temp_df["amount"]
temp_df.columns = [
"open",
"high",
"low",
"close",
"volume",
"outstanding_share",
"turnover",
]
if adjust == "":
temp_df = temp_df[start_date:end_date]
temp_df.drop_duplicates(subset=["open", "high", "low", "close"], inplace=True)
temp_df["open"] = round(temp_df["open"], 2)
temp_df["high"] = round(temp_df["high"], 2)
temp_df["low"] = round(temp_df["low"], 2)
temp_df["close"] = round(temp_df["close"], 2)
temp_df.dropna(inplace=True)
temp_df.drop_duplicates(inplace=True)
return temp_df
if adjust == "hfq":
res = requests.get(zh_sina_a_stock_hfq_url.format(symbol))
hfq_factor_df = pd.DataFrame(
eval(res.text.split("=")[1].split("\n")[0])["data"]
)
hfq_factor_df.columns = ["date", "hfq_factor"]
hfq_factor_df.index = pd.to_datetime(hfq_factor_df.date)
del hfq_factor_df["date"]
temp_df = pd.merge(
temp_df, hfq_factor_df, left_index=True, right_index=True, how="outer"
)
temp_df.fillna(method="ffill", inplace=True)
temp_df = temp_df.astype(float)
temp_df.dropna(inplace=True)
temp_df.drop_duplicates(subset=["open", "high", "low", "close", "volume"], inplace=True)
temp_df["open"] = temp_df["open"] * temp_df["hfq_factor"]
temp_df["high"] = temp_df["high"] * temp_df["hfq_factor"]
temp_df["close"] = temp_df["close"] * temp_df["hfq_factor"]
temp_df["low"] = temp_df["low"] * temp_df["hfq_factor"]
temp_df = temp_df.iloc[:, :-1]
temp_df = temp_df[start_date:end_date]
temp_df["open"] = round(temp_df["open"], 2)
temp_df["high"] = round(temp_df["high"], 2)
temp_df["low"] = round(temp_df["low"], 2)
temp_df["close"] = round(temp_df["close"], 2)
temp_df.dropna(inplace=True)
return temp_df

if adjust == "qfq":
res = requests.get(zh_sina_a_stock_qfq_url.format(symbol))
qfq_factor_df = pd.DataFrame(
eval(res.text.split("=")[1].split("\n")[0])["data"]
)
qfq_factor_df.columns = ["date", "qfq_factor"]
qfq_factor_df.index = pd.to_datetime(qfq_factor_df.date)
del qfq_factor_df["date"]

temp_df = pd.merge(
temp_df, qfq_factor_df, left_index=True, right_index=True, how="outer"
)
temp_df.fillna(method="ffill", inplace=True)
temp_df = temp_df.astype(float)
temp_df.dropna(inplace=True)
temp_df.drop_duplicates(subset=["open", "high", "low", "close", "volume"], inplace=True)
temp_df["open"] = temp_df["open"] / temp_df["qfq_factor"]
temp_df["high"] = temp_df["high"] / temp_df["qfq_factor"]
temp_df["close"] = temp_df["close"] / temp_df["qfq_factor"]
temp_df["low"] = temp_df["low"] / temp_df["qfq_factor"]
temp_df = temp_df.iloc[:, :-1]
temp_df = temp_df[start_date:end_date]
temp_df["open"] = round(temp_df["open"], 2)
temp_df["high"] = round(temp_df["high"], 2)
temp_df["low"] = round(temp_df["low"], 2)
temp_df["close"] = round(temp_df["close"], 2)
temp_df.dropna(inplace=True)
return temp_df

}}}

这贴好像太长了,明天再发两个数据处理的案例吧
0

z7c9

赞同来自:

可转债 源码
2022-08-18 08:28 来自北京 引用
0

wk379

赞同来自:

怎么排版呀
2022-08-17 23:55 来自北京 引用
0

大黑那个黑啊

赞同来自:

期待看到楼主数据处理的案例。
2022-02-22 10:27 引用
0

拿不住大师

赞同来自:

真的感谢,祝投资顺利
2022-02-22 10:17 引用
0

litbao

赞同来自:

感谢分享!
2022-02-22 08:37 引用
2

爱尚丑丑

赞同来自: 三川衡 prettycat

顶下,我也想看到集思录自己出一个api接口,反正都是会员出了钱的
2022-02-22 07:55 引用
0

ffsxsy

赞同来自:

宁稳网的怎么爬啊,哪位大佬指导下,
2021-10-22 18:50 引用
0

网络搬砖工

赞同来自:

群主能否分享到github或者码云上,参考学习下,谢谢
2021-07-17 19:03 引用
0

钮钴禄茜央娜迪 - 用今天的勤奋打败昨天的自己

赞同来自:

请问老师在聚宽上做的折价可转债测策略 里面用到的可转债数据是聚宽免费的吗
2021-05-24 16:44 引用
0

Jamly

赞同来自:

可以哦这个
2021-05-24 16:31 引用
0

没钱个子矮

赞同来自:

学习了
2021-05-05 14:12 引用
0

buffonyu

赞同来自:

感谢分享!
祝楼主的股债齐犇!
2021-05-05 11:58 引用
0

Hickeyhsu

赞同来自:

确实之前考虑不周,现在我把爬虫集思录的部分删除了
2021-05-05 11:14 引用
1

lorzen

赞同来自: 随机漫步的胖子

发这确实有点尴尬,不如主动删了,爬虫也不是量化里的难点吧,想学网上也挺多的,集思录加密就都失效了
2021-05-05 11:09 引用
1

李小样

赞同来自: macrochen

其实我更想做个历史的收益回测,不过集思录的历史数据是图片,很难识别,只是有一些简单的指标能直接使用。开了会员就能看到,五一把数据都扒下来了,回头慢慢看。后面买入只要设置条件单就行。
2021-05-05 10:53 引用
0

趋势交易者

赞同来自:

牛逼,在集思录发如何爬集思录数据的帖子,让天书作何感想!
2021-05-05 10:37 引用
0

Hickeyhsu

赞同来自:

啊要是官方觉得我这个不太好就联系我删除/编辑吧…
2021-05-05 10:31 引用
1

lhy5507

赞同来自: 趋势交易者

这个让集思录很尴尬啊……
2021-05-05 08:49 引用
0

未来好

赞同来自:

最近也在开始学python,主要侧重实现程序化交易这块(自动交易)~感觉比预想的简单多了~
量化选股,选债的没怎么研究过,感觉好像容易过拟合,还是用最简单的大思路选选就好了~
2021-05-04 23:13 引用
17
2021-05-04 21:29 引用
8

主任卡员

赞同来自: newbison littlefrog Dio00 lorzen luckzpz 趋势交易者 沙丁2012 风云紫轩更多 »

这位大哥,我真服了你了。
你在人家网站发爬人家数据的方法,是嫌人家不删你贴子,还是嫌人家数据没加密?
2021-05-04 21:18 引用
0

攻击型核潜艇

赞同来自:

也想学Python,奈何看书就犯困,似乎有点懂,但是又莫法下手,惭愧!
2021-05-04 20:24 引用
0

baixiqiang

赞同来自:

太牛了,很多语句都看不懂,大神能否说得详细点?
2021-05-04 20:19 引用
0

东方龙2014

赞同来自:

我学废了
2021-05-04 20:00 引用
0

lwhuq

赞同来自:

github地址有吗,谢谢!
2021-05-04 19:30 引用
0

keaven

赞同来自:

不明觉厉
2021-05-04 19:30 引用
0

BeeBeeSee - 2B or not 2B, that's a question.

赞同来自:

楼主可以发个txt格式的吗?晚上来学习一下。
2021-05-04 19:22 引用
0

宁不烦

赞同来自:

牛,学习。楼主问一下,用这些代码需要安装什么python模块不?
2021-05-04 19:19 引用
1

Hickeyhsu

赞同来自: zhong028

啊这,怎么发出来排版就乱了
2021-05-04 18:42 引用

要回复问题请先登录注册

发起人

问题状态

  • 最新活动: 2022-08-18 08:28
  • 浏览: 16780
  • 关注: 178