3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在 ETF 自动化套利交易中,**实时基金净值估算(IOPV,Indicative Optimized Portfolio Value)**是捕捉二级市场价格与一级市场净值间折溢价空间的关键指标。当 ETF 二级市场交易价格显著高于或低于 IOPV 时,即可触发溢价套利(二级市场卖出,一级市场申购)或折价套利(二级市场买入,一级市场赎回)。
本文将基于 PTrade 平台的原生 API,详细讲解如何实时提取 ETF 成分券数据、动态计算 IOPV,并实现自动化的折溢价套利策略。
在 PTrade 中进行 ETF 套利,需组合使用以下关键接口:
get_etf_info(etf_code)
report_unit)、T-1 日现金差额(cash_balance)、T-1 日申购基准单位净值(nav_percu)等。get_etf_stock_info(etf_code, security)
code_num)、现金替代标志(cash_replace_flag)及替代金额等。get_snapshot(security)
last_px)。etf_purchase_redemption(etf_code, amount)
etf_basket_order(etf_code, amount, ...)
IOPV 的核心计算公式如下:
$$\text{IOPV} = \frac{\sum (\text{成分券实时价格} \times \text{成分券数量}) + \text{预估现金部分}}{\text{最小申购赎回单位}}$$
在代码实现中,我们需要在盘中(如 run_interval 或 tick_data)实时遍历所有成分券,结合实时行情更新该预估净值。
def initialize(context):
# 设定目标 ETF
g.etf_code = '510050.SS'
set_universe([g.etf_code])
# 套利触发门槛(%)
g.premium_threshold = 0.005 # 溢价 0.5% 触发
g.discount_threshold = -0.005 # 折价 0.5% 触发
# 每 3 秒运行一次套利检测
run_interval(context, check_arbitrage, seconds=3)
def before_trading_start(context, data):
# 盘前获取 ETF 基础申赎信息与成分券列表
g.etf_info = get_etf_info(g.etf_code).get(g.etf_code, {})
g.report_unit = g.etf_info.get('report_unit', 1000000) # 申赎单位
g.cash_balance = g.etf_info.get('cash_balance', 0.0) # 现金差额
# 获取成分券列表及数量
g.stock_list = get_etf_stock_list(g.etf_code)
g.stocks_detail = get_etf_stock_info(g.etf_code, g.stock_list)
def calculate_iopv():
"""实时计算 IOPV"""
if not g.stock_list or not g.report_unit:
return None
# 批量获取成分股实时快照
snapshots = get_snapshot(g.stock_list)
total_stock_value = 0.0
for stock in g.stock_list:
stock_info = g.stocks_detail.get(stock, {})
share_num = stock_info.get('code_num', 0)
# 取最新成交价,若获取失败取 0
px = snapshots.get(stock, {}).get('last_px', 0.0)
total_stock_value += px * share_num
# 结合现金差额计算总资产并除以最小申赎单位
iopv = (total_stock_value + g.cash_balance) / g.report_unit
return round(iopv, 4)
def check_arbitrage(context):
"""折溢价套利逻辑监控"""
etf_snapshot = get_snapshot(g.etf_code).get(g.etf_code, {})
etf_price = etf_snapshot.get('last_px', 0.0)
if etf_price <= 0:
return
iopv = calculate_iopv()
if not iopv or iopv <= 0:
return
# 计算折溢价率
spread = (etf_price - iopv) / iopv
log.info("ETF价格: %s, 实时IOPV: %s, 折溢价率: %.2f%%" % (etf_price, iopv, spread * 100))
# 1. 溢价套利:二级市场高估,买入成分股篮子 -> 一级市场申购 ETF -> 二级市场卖出 ETF
if spread > g.premium_threshold:
log.info("触发溢价套利,开始买入成分股篮子并申购 ETF")
# 一键买入 1 份成分股篮子
etf_basket_order(g.etf_code, 1, price_style='S1', position=True)
# 申购 ETF
etf_purchase_redemption(g.etf_code, g.report_unit)
# 卖出 ETF
order(g.etf_code, -g.report_unit)
# 2. 折价套利:二级市场低估,二级市场买入 ETF -> 一级市场赎回 ETF -> 二级市场卖出成分股篮子
elif spread < g.discount_threshold:
log.info("触发折价套利,买入 ETF 并赎回")
# 买入 ETF
order(g.etf_code, g.report_unit)
# 赎回 ETF
etf_purchase_redemption(g.etf_code, -g.report_unit)
# 卖出成分股篮子
etf_basket_order(g.etf_code, -1, price_style='B1')
def handle_data(context, data):
pass
premium_threshold)中预留足够的滑点与交易手续费安全边际(通常设置在 0.3% - 0.5% 以上)。get_etf_stock_info 中的 cash_replace_flag 判断是否必须/允许现金替代,并在计算 IOPV 时对停牌股采用替代金额或昨收价计算。position=True 参数实现持仓替代),避免因资金不足导致篮子买入失败或废单。