问题描述
根据,api文档的说明
avg_cost 是当前持仓成本,只有在开仓/加仓时会更新: new_avg_cost = (posiont_value + trade_value + commission) / (position_amount + trade_amount)
每次买入后会调整avg_cost, 卖出时avg_cost不变. 这个值也会被用来计算浮动盈亏.
用context.Portfolio.Position中的avg_cost来计算浮动盈亏,是否没考虑拆分合并的情况?
比如买入价是2,当天发生了拆分,价格变为1,用来计算浮动盈亏的话,在回测中会直接变成浮亏50%。
策略里面计算持仓盈亏也有同样问题。请问应该如何处理呢?
--
比如,5.13买入512690.XSHG(酒ETF),5.17拆分,被记成浮亏
解决方案
在 JoinQuant 平台中,关于 avg_cost 计算浮动盈亏以及拆分合并的处理,主要与是否开启动态复权(真实价格)模式以及**交易标的的类型(如场内基金)**有关。
1. 平台的默认处理机制(动态复权模式)
根据 JoinQuant API 文档,如果您在 initialize 函数中开启了动态复权(真实价格)模式:
set_option('use_real_price', True)
每到新的一天,如果持仓中的股票发生了拆合或者分红等影响复权因子的情形,系统会根据复权因子自动调整股票的数量。为了保证 context.portfolio.total_value(总资产)不变,持仓数量会相应增加或减少,同时 avg_cost 也会被系统自动按比例调整,因此理论上不会出现因为拆分导致直接浮亏 50% 的情况。
2. 为什么场内基金(如酒ETF 512690.XSHG)会出现异常?
您提到的 512690.XSHG 属于场内基金(ETF)。在 JoinQuant 的官方文档中有特别的注意事项:
注意: 对期货不生效,对场内基金会生效,但因场内基金在拆分/合并时除权日披露不标准,目前采用的是折算基准日,和实际除权日可能有差异,鉴于此原因不建议给含有场内基金的策略开启动态复权。
正是因为场内基金的除权日和折算基准日存在时间差,导致系统在某一天更新价格时,持仓数量和 avg_cost 可能没有在同一天同步调整,从而在回测或模拟中表现为巨大的“浮亏”或“浮盈”。
3. 应该如何处理?
针对这种情况,建议采用以下几种方式来处理策略中的持仓盈亏计算:
方案一:自行使用前复权价格计算盈亏(推荐)
不要依赖 context.portfolio.positions[security].avg_cost 来计算盈亏。您可以在买入时,记录当时的前复权价格,并在后续判断时,获取当前的前复权价格进行对比。
def initialize(context):
set_option('use_real_price', True)
g.buy_prices = {} # 记录买入时的前复权价格
def market_open(context):
security = '512690.XSHG'
# 获取当前的前复权价格
current_price = get_price(security, count=1, end_date=context.current_dt, frequency='1m', fields=['close'], fq='pre')['close'][0]
# 买入逻辑
if condition_to_buy:
order(security, 1000)
g.buy_prices[security] = current_price
# 计算浮动盈亏比例
if security in context.portfolio.positions and security in g.buy_prices:
cost_price = g.buy_prices[security]
profit_ratio = (current_price - cost_price) / cost_price
log.info(f"{security} 当前浮动盈亏比例: {profit_ratio*100:.2f}%")
注:get_price 或 attribute_history 默认返回的都是基于当天的前复权价格,这样计算出的盈亏比例是准确的,不受拆分合并的干扰。
方案二:避免在场内基金拆分合并期间交易
如果您主要交易的是场内基金,且依赖系统的 avg_cost,由于平台数据的局限性,您可以提前查阅该 ETF 的拆分/折算公告,在策略中硬编码避开这些日期进行交易和盈亏判断。
方案三:关闭动态复权模式(仅限回测)
如果您只做回测,可以关闭动态复权模式(即不调用 set_option('use_real_price', True))。此时回测全程使用基于回测当天的前复权价格,价格是连续的,avg_cost 也是连续的,不会出现突变。但强烈不建议在模拟交易中使用此方法,因为这会导致下单价格与真实市场价格不符。