问题描述
解决方案
ETF折价与溢价率计算方法
在PTrade量化交易平台中,计算ETF(交易所交易基金)的折价或溢价率,主要涉及两个核心数据:ETF的市场价格和ETF的净值(NAV)或实时参考净值(IOPV)。
1. 计算公式
- 溢价(Premium):市场价格 > 净值
- 折价(Discount):市场价格 < 净值
计算公式如下:
$$ \text{折溢价率} = \frac{\text{市场价格} - \text{参考净值}}{\text{参考净值}} \times 100% $$
2. 所需API接口
根据PTrade API文档,我们需要使用以下接口获取数据:
get_snapshot(security): 获取ETF的实时行情快照,从中提取最新成交价 (last_px)。get_etf_info(etf_code): 获取ETF的基础信息,从中提取T-1日的单位净值 (nav_pre)。
注意:
get_etf_info返回的是前一交易日的单位净值(nav_pre)。在盘中实时计算时,通常使用交易所发布的**IOPV(基金份额参考净值)**更为准确。但在API文档提供的基础字段中,get_etf_info主要提供静态数据。如果您的券商环境在get_snapshot中推送了 IOPV 数据,可优先使用;若无,则通常使用nav_pre进行估算或作为盘前参考。
3. 策略代码实现
以下是一个完整的策略示例,展示如何在盘中获取ETF价格和净值信息,并计算折溢价率。
def initialize(context):
"""
初始化函数
"""
# 设置我们要监测的ETF代码,例如:510050.SS (上证50ETF)
g.security = '510050.SS'
# 设置股票池
set_universe(g.security)
# 设置每分钟运行一次,用于实时监控折溢价
# 注意:回测模式下handle_data会自动按频率运行
def handle_data(context, data):
"""
盘中运行函数
"""
# 1. 获取ETF基础信息 (包含T-1日净值)
# 注意:get_etf_info 返回的是一个字典,key是ETF代码
etf_basic_info = get_etf_info(g.security)
# 2. 获取ETF实时行情快照 (包含最新市价)
snapshot = get_snapshot(g.security)
# 数据有效性检查
if not etf_basic_info or not snapshot:
log.info("未获取到ETF信息或行情快照")
return
# 3. 提取数据
# 获取 T-1 日单位净值 (nav_pre)
# 注意:这里使用的是昨收净值。如果是实盘且券商支持IOPV,数据可能在snapshot中
nav = etf_basic_info[g.security].get('nav_pre')
# 获取最新成交价
current_price = snapshot[g.security].get('last_px')
# 4. 计算折溢价率
# 确保分母不为0且数据有效
if nav and current_price and nav > 0:
# 计算公式:(现价 - 净值) / 净值 * 100%
premium_rate = (current_price - nav) / nav * 100
# 5. 打印结果
log.info("ETF代码: %s" % g.security)
log.info("最新市价: %.3f" % current_price)
log.info("参考净值(T-1): %.3f" % nav)
if premium_rate > 0:
log.info("状态: 溢价 | 溢价率: %.3f%%" % premium_rate)
elif premium_rate < 0:
log.info("状态: 折价 | 折价率: %.3f%%" % premium_rate)
else:
log.info("状态: 平价 | 折溢价率: 0.00%")
# 6. 简单的交易逻辑示例 (仅供参考)
# 如果折价超过 2% (即价格远低于净值),可能存在买入机会
if premium_rate < -2.0:
log.info("触发折价套利信号,尝试买入...")
# order(g.security, 1000)
else:
log.info("数据缺失,无法计算折溢价率")
4. 代码解析
- 获取净值:通过
get_etf_info(g.security)获取返回的字典,并使用.get('nav_pre')提取前一交易日的单位净值。 - 获取市价:通过
get_snapshot(g.security)获取实时快照,使用.get('last_px')提取最新成交价。 - 计算逻辑:简单的百分比变化率计算。
- Python兼容性:代码使用了
%s和%.3f等格式化字符串方法,完全兼容 Python 3.5 及以上版本。
Q&A
Q: 为什么使用 nav_pre (T-1日净值) 而不是实时 IOPV?
A: 在 PTrade 的标准 API 文档 get_etf_info 中,明确提供的字段是 nav_pre (T-1日基金单位净值)。虽然实盘中交易所会推送实时的 IOPV (IOPV 是盘中估算的净值),但该字段在标准文档的 get_snapshot 返回值中未明确列出。如果您的券商环境在 Level-2 行情或快照中包含了 IOPV 字段,您可以将代码中的 nav 替换为快照中的 IOPV 数据以获得更高精度。
Q: 如何利用折溢价率进行交易?
A:
- 折价套利:当折价率较高(例如 < -1%)时,意味着二级市场价格低于净值,投资者可以在二级市场买入ETF,然后在一级市场申请赎回(需要满足最小赎回单位,通常为几十万份),从而获取差价。
- 溢价套利:当溢价率较高(例如 > 1%)时,意味着二级市场价格高于净值,投资者可以在一级市场申购ETF份额(通常T+0或T+1可用),然后在二级市场卖出。
Q: 回测和实盘有什么区别?
A: 在回测模式下,get_snapshot 无法使用(文档说明回测不支持),回测中应使用 data[security]['close'] 获取收盘价或当前分钟价格,并配合 get_history 或 get_fundamentals 获取历史净值数据进行模拟。上述代码主要针对**交易(实盘/模拟盘)**环境设计。