期现套利提示搭建框架

最近在研究期现套利,约了开专享融券,听说是有券的(但愿如此),但还没开。
同时因为需要参加本省的金融案例比赛,所以写的案例,融入了一部分最近在做的工作。当然感觉是老师们不太会重视这方面哈哈哈,毕竟不是热点。

目前做的都是正向套利:
在研究期现套利初期,我发现部分老练的前辈使用固定份额的ETF进行对冲,在某种程度上并不准确,因为有的时候年化收益高但是绝对收益低。所以借助相关资料和GPT写了一份实时计算的代码(仅包括主力合约)。但是我一般是用手机交易,所以后续借助钉钉(微信弄了半天没成功)实时计算并发送相关数据给我。后来发现,不能总是在自己的电脑上运行(占用太多内存)。所以租用了云服务器,使用linux系统,捣鼓了半天,最后成功部署了几个策略。运行了不到一个月,均成功盈利。

因为这段历程完全是从无到有一点点学习来的,所以想把这部分经验分享出来,希望能帮到大家。
当然,我水平也很差,抛砖引玉。

之所以用ETF,是因为可以把握盘中机会;不用LOF是因为容量小;不用联接基金是因为只能收盘,并且份额不好计算。
发表时间 2024-11-24 18:47     来自安徽

赞同来自: kolanta duiry mtjmtj77 plan30 adodo rourourou 你猜再猜 commontiger zoetina52 rule 灿若辰星 xxx皎皎更多 »

1

linns12

赞同来自: 不够再加

@不够再加

那估计是akshare版本问题了,这我得研究下了……
感谢
2024-11-29 11:13 来自上海 引用
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润色了一遍就放那个文档里当作业交了。
2024-11-29 10:47 来自安徽 引用
0

linns12

赞同来自:

@不够再加
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数据了。

所以想问下文档的写作时间,估计是在改版本之前。
2024-11-29 10:25 来自上海 引用
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}")
2024-11-28 21:07 来自安徽 引用
0

linns12

赞同来自:

@不够再加
akshare里的,IOPV抓取的应该是新浪财经的,这个肯定有延迟,和券商接口的比不了,有条件直接用QMT的
所以我问这个文档是什么时候的。最近还有没有测试过?
你分享文档的精神值得鼓励,不幸的是这文档里调用的函数,估计是很早之前的了,现在已经失效了。
有兴趣可以看下akshare的源代码,fund_etf_spot_em在线上描述中的确是有IOPV字段的,但实际你看源代码里面,已经完全没有IOPV了。而且可以看那里面的爬取页面,也没有IOPV字段了。

个人猜测是因为之前那阵子监管不让发估值的时候,akshare和东财两边都干脆把这个字段给下了。但也可能是其他原因,所以只能问问看文档作者了。
如果能稍微优化下,还是很不错的。
2024-11-28 18:36 来自上海 引用
0

不够再加

赞同来自:

@test189
直接使用IOPV会不会不太准确,而且IOPV的价格更新频率比期货慢太多了
肯定不准确,但是比手动算好一些,主要想实现的功能是起到一个提示的作用,而不是实时计算对应份额达到完美对冲。
因为经常在暴涨的时候想不到升水套利,我自己的多种策略触发条件都不一样,很容易忘掉。
2024-11-28 16:13 来自安徽 引用
0

不够再加

赞同来自:

@linns12
这是啥时候的文档?这里用的IOPV数据有问题吧
akshare里的,IOPV抓取的应该是新浪财经的,这个肯定有延迟,和券商接口的比不了,有条件直接用QMT的
2024-11-28 16:11 来自安徽 引用
0

linns12

赞同来自:

这是啥时候的文档?这里用的IOPV数据有问题吧
2024-11-26 21:44 来自上海 引用
0

test189

赞同来自:

直接使用IOPV会不会不太准确,而且IOPV的价格更新频率比期货慢太多了
2024-11-26 15:49 来自重庆 引用
0

一毛九鼎

赞同来自:

我问了某宝的,7月份就停了所有的融卷了。
2024-11-25 10:18 来自河北 引用
0

不够再加

赞同来自:

@ABFund
谢谢分享,表8中IF和IH的点值应该是300.
谢谢提醒*
2024-11-25 10:05 来自安徽 引用
0

ABFund

赞同来自:

@ABFund
谢谢分享,表8中IF和IH的点值应该是300.
再有就是这两个月期指和ETF流动性和波动率都是历史上较大的,如果适用历史常值的话,还是需要谨慎衡量,至少摩擦成本估计会明显增大。
2024-11-25 10:01 来自北京 引用
0

ABFund

赞同来自:

谢谢分享,表8中IF和IH的点值应该是300.
2024-11-25 09:55 来自北京 引用
0

rourourou

赞同来自:

多谢楼主分享,学习!
2024-11-25 09:34 来自香港 引用
0

不够再加

赞同来自:

@infi
小规模是多小?
升水套利,两个组合以内
2024-11-25 09:27 来自安徽 引用
0

infi

赞同来自:

@不够再加
有,但是还是小规模在做,大部队在等谈融券的问题
小规模是多小?
2024-11-25 08:53 来自河北 引用
0

太卡嗯护

赞同来自:

券不多要靠抢,而且融券费率巨贵,没啥套利空间
2024-11-24 21:59 来自北京 引用
0

不够再加

赞同来自:

@infi
有实盘了吗?
有,但是还是小规模在做,大部队在等谈融券的问题
2024-11-24 21:12 来自安徽 引用
0

infi

赞同来自:

有实盘了吗?
2024-11-24 20:21 来自河北 引用
0

逐利

赞同来自:

没啥大用!
2024-11-24 19:59 来自北京 引用
1

rule

赞同来自: 不够再加

非常感谢!
2024-11-24 19:56 来自河南 引用

要回复问题请先登录注册

发起人

问题状态

  • 最新活动: 2024-11-29 11:13
  • 浏览: 8784
  • 关注: 65