问题描述
模型需要计算执行后的投资回报率。但是在计算回报率的过程中会遇到如下问题。我没有想到很好的解决办法。在此向各位老师求教。
说明,在计算投资回报时分红数据一般都可以获得。故一下讨论不涉及分红数据的计算。
1、最直白的计算方法就是 (卖出价-买入价)*持仓。但是如果遇到上市公司合股或拆股(例如10股合成一股)。那么价格虽然变成原来的10倍,但是投资者“真实”持仓市值却没有变化。这就会造成投资业绩虚高。针对着这种情况有什么好办法么?针对合股、拆股,平台是否提供了相应的数据可供模型调取?
2、如果针对上述情况,平台没有提供合股拆股的数据,那么计算总市值的增长可以绕过上述问题。投资回报=((卖出价市值-买入价市值)/买入价市值)(买入价持仓数额)
这个方法虽然可以化解合股拆股代理的投资回报失真的问题。但是又会遇到诸如定向增发等扩股造成的“市值”扩张导致回测的投资回报与真实的投资回报不一致的情况。即买入后股价并没上涨。但是定向增发导致公司市值膨胀。进而导致根据公式计算出的投资回报失真!!!
针对以上问题,不知道有没有什么好的办法使得回测可以准确的反应真实的投资回报?
解决方案
在 JoinQuant(聚宽)平台上,您不需要手动去计算(卖出价-买入价)或者通过公司总市值来推算投资回报率。平台底层的回测引擎已经为您完美解决了拆股、合股、分红以及增发带来的收益率计算失真问题。
解决这个问题的核心办法是:开启动态复权(真实价格)模式。
核心解决方案:使用真实价格模式
在策略的 initialize 函数中,加入以下代码:
def initialize(context):
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
为什么这能解决您提到的两个问题?
1. 针对拆股、合股、分红导致的价格失真
在未开启真实价格模式时,如果使用前复权数据,确实会存在未来函数问题,导致回测价格不真实。但当您开启 use_real_price = True 后:
- 真实撮合:回测到历史某一天,系统会使用当天的真实历史价格进行撮合成交。
- 自动调整账户:每到新的一天,如果您的持仓中发生了拆股、合股或分红,JoinQuant 引擎会自动根据复权因子调整您账户中的股票数量或现金。例如,10股合1股,系统会自动将您的持仓数量除以10;如果是分红,系统会自动将扣税后的现金发放到您的账户可用资金中。
- 资产无缝衔接:这种处理方式保证了您的账户总资产(
context.portfolio.total_value)在除权除息日是平滑且真实的,完全符合真实交易场景。
2. 针对定向增发导致的总市值膨胀
您提到的用“总市值”来计算回报率本身就是一个误区。投资者的个人投资回报率只与个人账户的资产净值变化有关,与上市公司的总市值(股本×股价)无关。定向增发虽然增加了公司的总股本和总市值,但只要除权除息处理得当,您手中持有的股票价值变化系统会自动计算。
JoinQuant 平台通过跟踪您账户的资金流入流出(inout_cash)和当前总资产(total_value)来计算收益率,完全避开了公司总市值变动的干扰。
如何获取准确的投资回报率?
在开启了真实价格模式后,您可以在策略的任何地方(如 handle_data 或 after_trading_end 中)直接通过 context 对象获取当前最准确的投资回报率和账户资产:
def handle_data(context, data):
# 获取总账户的持仓价值
positions_value = context.portfolio.positions_value
# 获取总账户的总资产(包含现金和持仓价值)
total_value = context.portfolio.total_value
# 获取总权益的累计收益率(即真实的投资回报率)
# 计算公式大致为:(total_value / inout_cash) - 1
returns = context.portfolio.returns
log.info(f"当前账户总资产: {total_value}, 累计投资回报率: {returns*100:.2f}%")
总结
- 强烈建议在所有回测和模拟交易中调用
set_option('use_real_price', True)。 - 放弃手动计算单只股票买卖差价来统计总收益的做法,直接使用
context.portfolio.returns获取账户级别的真实累计收益率。 - 平台引擎会自动处理所有复杂的除权除息、拆合股逻辑,确保您的回测资金曲线与真实世界一致。