【之前考虑不周,现在我把采集集思录数据的部分删除了,保留从新浪财经采集数据的代码,如果各位老师觉得还有什么不妥请告诉我~】写这篇文章的起因还是之前有人留言想知道我怎么每天自动计算双低策略轮动标的。在集思录潜水很久,终于碰到我会的领域,所以打算公开一下代码。
数据的获取渠道有很多,包括许多量化平台(如聚宽、优矿等)都会提供各种量化数据。但是使用量化平台有着许多限制:
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
正股数据采集
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
#
数据获取渠道数据的获取渠道有很多,包括许多量化平台(如聚宽、优矿等)都会提供各种量化数据。但是使用量化平台有着许多限制:
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
}}}
这贴好像太长了,明天再发两个数据处理的案例吧
17
赞同来自: alexandre1 、prettycat 、calorand 、freetstar89 、嗜血的韭菜 、 、 、 、 、 、 、 、 、 、 、 、更多 »
集思录大气一点直接开发API接口