最近在研究期现套利,约了开专享融券,听说是有券的(但愿如此),但还没开。
同时因为需要参加本省的金融案例比赛,所以写的案例,融入了一部分最近在做的工作。当然感觉是老师们不太会重视这方面哈哈哈,毕竟不是热点。
目前做的都是正向套利:
在研究期现套利初期,我发现部分老练的前辈使用固定份额的ETF进行对冲,在某种程度上并不准确,因为有的时候年化收益高但是绝对收益低。所以借助相关资料和GPT写了一份实时计算的代码(仅包括主力合约)。但是我一般是用手机交易,所以后续借助钉钉(微信弄了半天没成功)实时计算并发送相关数据给我。后来发现,不能总是在自己的电脑上运行(占用太多内存)。所以租用了云服务器,使用linux系统,捣鼓了半天,最后成功部署了几个策略。运行了不到一个月,均成功盈利。
因为这段历程完全是从无到有一点点学习来的,所以想把这部分经验分享出来,希望能帮到大家。
当然,我水平也很差,抛砖引玉。
之所以用ETF,是因为可以把握盘中机会;不用LOF是因为容量小;不用联接基金是因为只能收盘,并且份额不好计算。
同时因为需要参加本省的金融案例比赛,所以写的案例,融入了一部分最近在做的工作。当然感觉是老师们不太会重视这方面哈哈哈,毕竟不是热点。
目前做的都是正向套利:
在研究期现套利初期,我发现部分老练的前辈使用固定份额的ETF进行对冲,在某种程度上并不准确,因为有的时候年化收益高但是绝对收益低。所以借助相关资料和GPT写了一份实时计算的代码(仅包括主力合约)。但是我一般是用手机交易,所以后续借助钉钉(微信弄了半天没成功)实时计算并发送相关数据给我。后来发现,不能总是在自己的电脑上运行(占用太多内存)。所以租用了云服务器,使用linux系统,捣鼓了半天,最后成功部署了几个策略。运行了不到一个月,均成功盈利。
因为这段历程完全是从无到有一点点学习来的,所以想把这部分经验分享出来,希望能帮到大家。
当然,我水平也很差,抛砖引玉。
之所以用ETF,是因为可以把握盘中机会;不用LOF是因为容量小;不用联接基金是因为只能收盘,并且份额不好计算。
0
@linns12
这是文档还原出来的源代码,没错。
你可以跑一下试试看,应该会报错吧?
KeyError: 'IOPV实时估值'
稍微查一下,可以发现实际出问题的是这一行:
fund_etf_spot_em_df = ak.fund_etf_spot_em()
如果去查ak.fund_etf_spot_em()的源代码:
def fund_etf_spot_em() -> pd.DataFrame:
url...
我并没有报错哎。刚试了一下,运行是成功的。
IPOV我确实没有准确去看爬取来源,回去我研究一下。如果这个源不行,你手动替换成同花顺的源。也有可能是akshare更新了?退回版本试试,这方面我确实不是很清楚,我写这段跑通了,让GPT润色了一遍就放那个文档里当作业交了。
0
@不够再加
你可以跑一下试试看,应该会报错吧?
KeyError: 'IOPV实时估值'
稍微查一下,可以发现实际出问题的是这一行:
fund_etf_spot_em_df = ak.fund_etf_spot_em()
如果去查ak.fund_etf_spot_em()的源代码:
def fund_etf_spot_em() -> pd.DataFrame:
url = "https://88.push2.eastmoney.com/api/qt/clist/get"
params = {
"pn": "1",
"pz": "5000",
"po": "1",
"np": "1",
"ut": "bd1d9ddb04089700cf9c27f6f7426281",
"fltt": "2",
"invt": "2",
"wbp2u": "|0|0|0|web",
"fid": "f3",
"fs": "b:MK0021,b:MK0022,b:MK0023,b:MK0024",
"fields": "f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f12,f13,f14,f15,f16,f17,f18,f20,f21,f23,f24,f25,f22,f11,f62,f128,f136,f115,f152",
"_": "1672806290972",
}
r = requests.get(url, params=params)
data_json = r.json()
temp_df = pd.DataFrame(data_json["data"]["diff"])
temp_df.rename(
columns={
"f12": "代码",
"f14": "名称",
"f2": "最新价",
"f4": "涨跌额",
"f3": "涨跌幅",
"f5": "成交量",
"f6": "成交额",
"f17": "开盘价",
"f15": "最高价",
"f16": "最低价",
"f18": "昨收",
"f8": "换手率",
"f21": "流通市值",
"f20": "总市值",
},
inplace=True,
)
temp_df = temp_df[
[
"代码",
"名称",
"最新价",
"涨跌额",
"涨跌幅",
"成交量",
"成交额",
"开盘价",
"最高价",
"最低价",
"昨收",
"换手率",
"流通市值",
"总市值",
]
]
temp_df["最新价"] = pd.to_numeric(temp_df["最新价"], errors="coerce")
temp_df["涨跌额"] = pd.to_numeric(temp_df["涨跌额"], errors="coerce")
temp_df["涨跌幅"] = pd.to_numeric(temp_df["涨跌幅"], errors="coerce")
temp_df["成交量"] = pd.to_numeric(temp_df["成交量"], errors="coerce")
temp_df["成交额"] = pd.to_numeric(temp_df["成交额"], errors="coerce")
temp_df["开盘价"] = pd.to_numeric(temp_df["开盘价"], errors="coerce")
temp_df["最高价"] = pd.to_numeric(temp_df["最高价"], errors="coerce")
temp_df["最低价"] = pd.to_numeric(temp_df["最低价"], errors="coerce")
temp_df["昨收"] = pd.to_numeric(temp_df["昨收"], errors="coerce")
temp_df["换手率"] = pd.to_numeric(temp_df["换手率"], errors="coerce")
temp_df["流通市值"] = pd.to_numeric(temp_df["流通市值"], errors="coerce")
temp_df["总市值"] = pd.to_numeric(temp_df["总市值"], errors="coerce")
return temp_df
你可以看下,akshare本身现在是否还处理IOPV?
更进一步,可以看下akshare所爬取的原始网页,哪里也没有IOPV数据了。
所以想问下文档的写作时间,估计是在改版本之前。
import time这是文档还原出来的源代码,没错。
import akshare as ak
from datetime import datetime
import pandas as pd
from dingtalkchatbot.chatbot import DingtalkChatbot
def dingtalk_robot(webhook, secret, message):
dogBOSS = DingtalkC...
你可以跑一下试试看,应该会报错吧?
KeyError: 'IOPV实时估值'
稍微查一下,可以发现实际出问题的是这一行:
fund_etf_spot_em_df = ak.fund_etf_spot_em()
如果去查ak.fund_etf_spot_em()的源代码:
def fund_etf_spot_em() -> pd.DataFrame:
url = "https://88.push2.eastmoney.com/api/qt/clist/get"
params = {
"pn": "1",
"pz": "5000",
"po": "1",
"np": "1",
"ut": "bd1d9ddb04089700cf9c27f6f7426281",
"fltt": "2",
"invt": "2",
"wbp2u": "|0|0|0|web",
"fid": "f3",
"fs": "b:MK0021,b:MK0022,b:MK0023,b:MK0024",
"fields": "f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f12,f13,f14,f15,f16,f17,f18,f20,f21,f23,f24,f25,f22,f11,f62,f128,f136,f115,f152",
"_": "1672806290972",
}
r = requests.get(url, params=params)
data_json = r.json()
temp_df = pd.DataFrame(data_json["data"]["diff"])
temp_df.rename(
columns={
"f12": "代码",
"f14": "名称",
"f2": "最新价",
"f4": "涨跌额",
"f3": "涨跌幅",
"f5": "成交量",
"f6": "成交额",
"f17": "开盘价",
"f15": "最高价",
"f16": "最低价",
"f18": "昨收",
"f8": "换手率",
"f21": "流通市值",
"f20": "总市值",
},
inplace=True,
)
temp_df = temp_df[
[
"代码",
"名称",
"最新价",
"涨跌额",
"涨跌幅",
"成交量",
"成交额",
"开盘价",
"最高价",
"最低价",
"昨收",
"换手率",
"流通市值",
"总市值",
]
]
temp_df["最新价"] = pd.to_numeric(temp_df["最新价"], errors="coerce")
temp_df["涨跌额"] = pd.to_numeric(temp_df["涨跌额"], errors="coerce")
temp_df["涨跌幅"] = pd.to_numeric(temp_df["涨跌幅"], errors="coerce")
temp_df["成交量"] = pd.to_numeric(temp_df["成交量"], errors="coerce")
temp_df["成交额"] = pd.to_numeric(temp_df["成交额"], errors="coerce")
temp_df["开盘价"] = pd.to_numeric(temp_df["开盘价"], errors="coerce")
temp_df["最高价"] = pd.to_numeric(temp_df["最高价"], errors="coerce")
temp_df["最低价"] = pd.to_numeric(temp_df["最低价"], errors="coerce")
temp_df["昨收"] = pd.to_numeric(temp_df["昨收"], errors="coerce")
temp_df["换手率"] = pd.to_numeric(temp_df["换手率"], errors="coerce")
temp_df["流通市值"] = pd.to_numeric(temp_df["流通市值"], errors="coerce")
temp_df["总市值"] = pd.to_numeric(temp_df["总市值"], errors="coerce")
return temp_df
你可以看下,akshare本身现在是否还处理IOPV?
更进一步,可以看下akshare所爬取的原始网页,哪里也没有IOPV数据了。
所以想问下文档的写作时间,估计是在改版本之前。
0
import time
import akshare as ak
from datetime import datetime
import pandas as pd
from dingtalkchatbot.chatbot import DingtalkChatbot
def dingtalk_robot(webhook, secret, message):
dogBOSS = DingtalkChatbot(webhook, secret)
dogBOSS.send_markdown(
title='股指期货升水套利提示',
text=message,
is_at_all=True
)
cffex_text = ak.match_main_contract(symbol="cffex")
current_date = datetime.today()
results_df = pd.DataFrame(columns=['指数', 'ETF代码', 'IOPV实时估值', '所需份额', '最新价', '差值', '保守年化收益率'])
webhook = ''
secrets = ''
while True:
time.sleep(10)
try:
stock_zh_index_spot_sina_df = ak.stock_zh_index_spot_sina()
index_hs300 = stock_zh_index_spot_sina_df[stock_zh_index_spot_sina_df['名称'].str.contains("沪深300", regex=True)]
index_sz50 = stock_zh_index_spot_sina_df[stock_zh_index_spot_sina_df['名称'].str.contains("上证50", regex=True)]
index_zz1000 = stock_zh_index_spot_sina_df[stock_zh_index_spot_sina_df['名称'].str.contains("中证1000", regex=True)]
index_zz500 = stock_zh_index_spot_sina_df[stock_zh_index_spot_sina_df['名称'].str.contains("中证500", regex=True)]
if index_hs300.empty or index_sz50.empty or index_zz1000.empty or index_zz500.empty:
print("某些指数数据不可用,跳过本次循环。")
continue
hs300_true_value = index_hs300['最新价'].values[0] * 300
sz50_true_value = index_sz50['最新价'].values[0] * 300
zz1000_true_value = index_zz1000['最新价'].values[0] * 200
zz500_true_value = index_zz500['最新价'].values[0] * 200
futures_zh_spot_df = ak.futures_zh_spot(symbol=cffex_text, market="FF", adjust='0')
futures_hs300 = futures_zh_spot_df[futures_zh_spot_df['symbol'].str.contains("沪深300", regex=True)]
futures_sz50 = futures_zh_spot_df[futures_zh_spot_df['symbol'].str.contains("上证50", regex=True)]
futures_zz1000 = futures_zh_spot_df[futures_zh_spot_df['symbol'].str.contains("中证1000", regex=True)]
futures_zz500 = futures_zh_spot_df[futures_zh_spot_df['symbol'].str.contains("中证500", regex=True)]
if futures_hs300.empty or futures_sz50.empty or futures_zz1000.empty or futures_zz500.empty:
print("某些期货数据不可用,跳过本次循环。")
continue
futures_hs300_value = futures_hs300['current_price'].values[0] * 300
futures_sz50_value = futures_sz50['current_price'].values[0] * 300
futures_zz1000_value = futures_zz1000['current_price'].values[0] * 200
futures_zz500_value = futures_zz500['current_price'].values[0] * 200
# 获取合约到期日
symbol_first = futures_hs300['symbol'].iloc[0]
year_month_code = symbol_first[-4:]
year = 2000 + int(year_month_code[:2])
month = int(year_month_code[2:])
all_fridays = pd.date_range(start=f"{year}-{month:02d}-01", end=f"{year}-{month:02d}-28", freq='W-FRI')
third_friday = all_fridays[2]
if third_friday.weekday() >= 5:
third_friday = third_friday + pd.offsets.BusinessDay(1)
days_difference = (third_friday - current_date).days
fund_etf_spot_em_df = ak.fund_etf_spot_em()
etf_sz50_list = ['510100', '510050']
etf_hs300_list = ['159919', '510310', '510300']
etf_zz1000_list = ['159845', '512100']
etf_zz500_list = ['159922', '510500']
etf_data_list = [
('沪深300', etf_hs300_list, hs300_true_value, futures_hs300_value),
('上证50', etf_sz50_list, sz50_true_value, futures_sz50_value),
('中证1000', etf_zz1000_list, zz1000_true_value, futures_zz1000_value),
('中证500', etf_zz500_list, zz500_true_value, futures_zz500_value),
]
for index_name, etf_list, true_value, futures_value in etf_data_list:
etf_data = fund_etf_spot_em_df[fund_etf_spot_em_df['代码'].isin(etf_list)]
if not etf_data.empty:
etf_data['所需份额'] = true_value / etf_data['IOPV实时估值']
etf_data['差值'] = futures_value - (etf_data['所需份额'] * etf_data['最新价'])
etf_data['保守年化收益率'] = (etf_data['差值'] / (futures_value + true_value)) * (365 / days_difference)
# 只报告差值绝对值高于10000元的行
filtered_data = etf_data[etf_data['差值'].abs() > 3000]
if not filtered_data.empty:
# 构建 Markdown 表格
table_header = "| 代码 | IOPV实时估值 | 所需份额 | 最新价 | 差值 | 保守年化收益率 |\n"
table_header += "|------|--------------|----------|--------|------|----------------|\n"
table_rows = ""
for _, row in filtered_data.iterrows():
table_rows += f"| {row['代码']} | {row['IOPV实时估值']:.4f} | {row['所需份额']:.6f} | {row['最新价']:.3f} | {row['差值']:.6f} | {row['保守年化收益率']:.6f} |\n"
message = f"{index_name} ETF 每个基金的所需份额、差值与保守年化收益率(绝对值大于10000元):\n" + table_header + table_rows
dingtalk_robot(webhook, secrets, message)
else:
print(f"{index_name} ETF 没有差值绝对值超过10000元的记录。")
else:
print(f"{index_name} ETF 数据为空,无法计算所需份额与差值。")
except Exception as e:
print(f"发生错误: {e}")
0
@不够再加
你分享文档的精神值得鼓励,不幸的是这文档里调用的函数,估计是很早之前的了,现在已经失效了。
有兴趣可以看下akshare的源代码,fund_etf_spot_em在线上描述中的确是有IOPV字段的,但实际你看源代码里面,已经完全没有IOPV了。而且可以看那里面的爬取页面,也没有IOPV字段了。
个人猜测是因为之前那阵子监管不让发估值的时候,akshare和东财两边都干脆把这个字段给下了。但也可能是其他原因,所以只能问问看文档作者了。
如果能稍微优化下,还是很不错的。
akshare里的,IOPV抓取的应该是新浪财经的,这个肯定有延迟,和券商接口的比不了,有条件直接用QMT的所以我问这个文档是什么时候的。最近还有没有测试过?
你分享文档的精神值得鼓励,不幸的是这文档里调用的函数,估计是很早之前的了,现在已经失效了。
有兴趣可以看下akshare的源代码,fund_etf_spot_em在线上描述中的确是有IOPV字段的,但实际你看源代码里面,已经完全没有IOPV了。而且可以看那里面的爬取页面,也没有IOPV字段了。
个人猜测是因为之前那阵子监管不让发估值的时候,akshare和东财两边都干脆把这个字段给下了。但也可能是其他原因,所以只能问问看文档作者了。
如果能稍微优化下,还是很不错的。