社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

中金转债|左右开弓,择时策略的逻辑闭环与Python建议

中金固定收益研究 • 10 月前 • 336 次点击  


这是关于转债择时体系报告的第四篇,我们希望在这里能有一个"可堪一用"的闭环。在此前的三篇报告中,我们分别介绍了择时指标基础(估值为主,偏左侧)、关于趋势分析的工具(主要目的是判定右侧)与强化学习框架。这里,我们准备结合左侧、右侧与强化学习,谈谈如何克服实践中的障碍,建立统一模型。


图表1:转债择时策略的整合思路

资料来源:中金公司研究部


左侧,我们要做的是精简化。我们介绍过的估值指标确实足够多,也各自有一些作用,但如果希望再结合“趋势”来补齐估值过于左侧化的问题——实践中容易出现YTM越高越买,但越买越高的窘境——我们需要用一些更简单而长效的数据。反例是百元溢价率,这个指标我们自2018年提出后经常使用,投资者接受度也逐渐提高,但在更早期的市场里,这个指标存在缺失值。最终,我们选择了最简单的三个指标进入模型:价格中位数、隐含波动率中位数、债底溢价率的20日偏离度,这些数据自2010年以来均不存在缺失,意味着我们可用较长的数据进行训练。


处理方式相对简单,仅注意尽可能矢量化避免进入耗费计算量的循环即可。此外在2019年以来,市场常出现“双高”个券对估值数据影响较大,我们直接剔除。


图表2:剔除双高的转债估值计算逻辑


def stateMatrix(obj):
    mat01 = obj.matNormal # 剔除双高
    close = (obj.Close * mat01).median(axis=1).fillna(method='pad')
    impv = (obj.ImpliedVol * mat01).median(axis=1).fillna(method="pad")
    srsStrbPrem = (obj.StrbPrem *
               mat01).mean(axis=1).fillna(method="pad")

    srsStrbPrem_bias = srsStrbPrem - srsStrbPrem.rolling(20).mean()
    
    dfMat = pd.DataFrame({'median_price': close,
    'impv': impv,
    'srsStrbPrem_bias': srsStrbPrem_bias})

    return dfMat


资料来源:中金公司研究部


即便这些指标相对简单,其也有较强的代表性,对多数市场条件下的“该做”与“不该做”有了基本认识。例如下面是价格中位数在不同时间窗口下,对后续行情的预测效果。(更多结果请参考该系列的第一篇《择时体系1:这些指标怎么看,及Python实现》


图表3:不同价位下转债指数胜率

注:数值截至2024年7月17日 

资料来源:Wind,中金公司研究部


就右侧(正股趋势)而言,我们要放进怎样的代理变量? 上期报告中我们给出了部分答案,对于股价走势我们能做的是划分结构、确认趋势,我们不期待右侧模型做太多预判,大体上做到明辨趋势即可。


图表4:上证指数的趋势划分(摘自:《打造趋势分析的工具》

资料来源:Wind,中金公司研究部


而在上期报告最后,我们给出了一个学习模型。数值如下所示。这里,我们直接用上一章模型产生的结果,作为“趋势”的代理变量。


图表5:趋势模型值 vs 多空趋势

资料来源:Wind,中金公司研究部


综上,进入模型的只有四个维度的数据:价格中位数、隐波中位数、债底溢价率20日偏离度、趋势模型值。追求更高精度的投资者自然可以使用更多有效的指标,和更好的模型结构,但对多数投资者来说,现实环境原本就比模型表征的更为复杂:例如资金端可能总是顺势申赎,例如买卖还要面临反向交易的限制,再如投资者本身的沟通问题——我们介绍的模型在此仍力求简单明确。


综合训练,可能遇到的几个弯路:


1.  不要做“收益率预测”,而是做“应对建议”。即便是对转债而言,对收益率给出“预测”,也非常困难。虽然直观上能有一个“收益率”预期非常吸引人,但这并不显示,一般模型会尽量给出一个接近0的数值——这是一个很接近最小二乘的结果。因此我们也不做最小二乘预期模型,而是沿用该系列报告第二篇提出的强化学习模型。简单理解下,强化模型给出买卖建议,而非收益预测。买入并不是因为收益预期高,而往往是盈利空间大,情况不对亦可在合适的时机,根据同一个模型迎来卖出信号,反之亦然。总而言之,既然我们考虑了左右侧信号,逻辑理当存在闭环,买入后有对应的卖出信号,反之亦然,就是这个维度下相对基础的闭环。


图表6:典型Agent模型结构(训练方式请见《强化学习的逐步实现》)


class Agent:
    def __init__(self, input_size, hidden_size, action_size, lr=0.001, gamma=0.9,
                 weight_decay=0.0005)
:
        self.net = DQN_InOne(input_size, hidden_size, action_size)
        self.optimizer = optim.Adam(self.net.parameters(), lr=lr, weight_decay=weight_decay)
        self.gamma = gamma # discount factor
        self.loss_fn = nn.MSELoss()

    def get_action(self, state, action_space, epsilon=0.1):
        if  np.random.random() < epsilon:  # exploration
            return np.random.choice(action_space), True
        else:  
            with torch.no_grad():
                q_values = self.net(state)
                return torch.argmax(q_values).item(), False

    def update(self, state, action, reward, next_state):
        q_values = self.net(state)
        with torch.no_grad():
            q_next = self.net(next_state)
        q_target = reward + self.gamma * torch.max(q_next)

        loss = self.loss_fn(q_values[action], q_target)
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()


资料来源:中金公司研究部


2.      即便是强化学习模型,2015年的权重可能也较高。鉴于那年发生了太多难以复制的情况——转债的个数一度只有四个(甚至一度只有2支在交易),这时统计数据的有效性受到严重影响,因此,不如直接跳过2015年,而将训练集分为2010~2014以及2016~2019两段(为避免过拟合,2020年后的行情是样本外)。


图为训练3000次后,模型在2015年后的实证。有趣的是,其超额回报反而来自样本外——实际这意味着,2018~2020年间整体上行趋势和低估值下,都应保持多头,重在择券,而择时的作用反而体现在时多时空的2021年后。(此外值得注意在2020和2023年间这个测试有很多频繁切换的时点,这是因为结果数值很接近0.5,模型出现犹豫不定的情况)


图表7:强化学习模型的净值表现

注:数据自2016年至2024年7月17日 

资料来源:Wind,中金公司研究部


3.  但如何吸纳更新的经验呢:一个路线是预训练模型的微调。吸纳“最新经验”的难点在于,无法验证那是学习到了“新知识”还是“过拟合”或者“未来数据”。一个解决思路是对“新知识”也分割为训练阶段与测试阶段,而为了避免重新学习全部参数,我们可以在已有模型的基础上做“微调”——即固定大部分参数,仅在有正则化约束的情况下优化一小部分参数。


对于我们的择时模型而言,如果采取了两层或多层模型结构,那么最后一层相当于直观意义上的“综合信息后决策”,而第一层一般接近于“对高低的认知”,例如“均价为110元算高还是低”。实践中,最后一层的基础框架很少变化,那么我们可以尽量只微调第一层,以下是一个示例。


图表8:模型微调:假设只调整第一个全连接层


def train_finetune(model, dataloader): 
    param_need_update = ['fc1.weight', 'fc1.bias']
    # 冻结模型不需要调整的参数
    for name, param in model.parameters():
        if not name in param_need_update:
            param.requires_grad = False
    
    # 只有存在梯度的参数,即第一层
    optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
    # 定义损失函数,以交叉熵为例
    criterion = nn.CrossEntropyLoss()
    # 假设我们有一个数据加载器 dataloader
    for epoch in range(5): # 训练5个epoch
        for inputs, labels in dataloader:
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            
            # 反向传播和优化
            loss.backward()
            optimizer.step()
        
        print(f'Epoch {epoch+1}, Loss: {loss.item()}')
    
    print("微调完成")


资料来源:中金公司研究部


最后,模型当然不能解决全部问题。主观上投资者经常会提出“某段时间模型的选择不对”——实际上只要不是过拟合的或者使用未来数据的模型,都可能会有误判。我们“可堪一用”的标准是:1. 在不同时间点逻辑是一致的也是可理解的;2. 大体上长期有效;3. 多数时候会给出明确的结论。实际上,就转债的择时而言,我们多数情况下也只是在回答“估值低不低”和“趋势好不好”的问题,这一点上,现在的模型和多年前的框架并无区别。


实际上前述的简化与取舍都是为了这个目的,一些我们常常使用,却未进入统一模型的指标,更多是因为其具备一个更加特定化的应用场景。例如下面的热图(具体见《择时体系1:这些指标怎么看,及Python实现》),我们常常用作观察是否出现情绪性卖盘,并判断估值超调,其他时间并无信号作用。但在一些特定时段,例如上周7月24日左右,就有比通用模型更好的参考价值:中等估值股性组出现足量调整,意味着市场估值存在出清的信号。因此,对近期市场,我们认为随着估值充分调整,转债性价比提升,而小盘股情绪回暖,至少存在反弹机会。


图表9:转债分类型估值调整

资料来源:Wind,中金公司研究部



风险

模型误差,市场风格偏好出现较大调整,公司财务质量超预期调整。




文章来源

本文摘自:2024年8月2日已经发布的《左右开弓:择时策略的逻辑闭环与Python建议》

杨   冰  分析员 SAC 执业证书编号:S0080515120002 SFC CE Ref:BOM868

罗 凡 分析员 SAC执业证书编号:S0080522070003 SFC CE Ref:BUL744

陈健恒  分析员 SAC 执业证书编号:S0080511030011 SFC CE Ref:BBM220





Legal Disclaimer

法律声明

特别提示

本公众号不是中国国际金融股份有限公司(下称“中金公司”)研究报告的发布平台。本公众号只是转发中金公司已发布研究报告的部分观点,订阅者若使用本公众号所载资料,有可能会因缺乏对完整报告的了解或缺乏相关的解读而对资料中的关键假设、评级、目标价等内容产生理解上的歧义。订阅者如使用本资料,须寻求专业投资顾问的指导及解读。

本公众号所载信息、意见不构成所述证券或金融工具买卖的出价或征价,评级、目标价、估值、盈利预测等分析判断亦不构成对具体证券或金融工具在具体价位、具体时点、具体市场表现的投资建议。该等信息、意见在任何时候均不构成对任何人的具有针对性的、指导具体投资的操作意见,订阅者应当对本公众号中的信息和意见进行评估,根据自身情况自主做出投资决策并自行承担投资风险。

中金公司对本公众号所载资料的准确性、可靠性、时效性及完整性不作任何明示或暗示的保证。对依据或者使用本公众号所载资料所造成的任何后果,中金公司及/或其关联人员均不承担任何形式的责任。

本公众号仅面向中金公司中国内地客户,任何不符合前述条件的订阅者,敬请订阅前自行评估接收订阅内容的适当性。订阅本公众号不构成任何合同或承诺的基础,中金公司不因任何单纯订阅本公众号的行为而将订阅人视为中金公司的客户。

一般声明

本公众号仅是转发中金公司已发布报告的部分观点,所载盈利预测、目标价格、评级、估值等观点的给予是基于一系列的假设和前提条件,订阅者只有在了解相关报告中的全部信息基础上,才可能对相关观点形成比较全面的认识。如欲了解完整观点,应参见中金研究网站(http://research.cicc.com)所载完整报告。

本资料较之中金公司正式发布的报告存在延时转发的情况,并有可能因报告发布日之后的情势或其他因素的变更而不再准确或失效。本资料所载意见、评估及预测仅为报告出具日的观点和判断。该等意见、评估及预测无需通知即可随时更改。证券或金融工具的价格或价值走势可能受各种因素影响,过往的表现不应作为日后表现的预示和担保。在不同时期,中金公司可能会发出与本资料所载意见、评估及预测不一致的研究报告。中金公司的销售人员、交易人员以及其他专业人士可能会依据不同假设和标准、采用不同的分析方法而口头或书面发表与本资料意见不一致的市场评论和/或交易观点。

在法律许可的情况下,中金公司可能与本资料中提及公司正在建立或争取建立业务关系或服务关系。因此,订阅者应当考虑到中金公司及/或其相关人员可能存在影响本资料观点客观性的潜在利益冲突。与本资料相关的披露信息请访http://research.cicc.com/disclosure_cn,亦可参见近期已发布的关于相关公司的具体研究报告。

本订阅号是由中金公司研究部建立并维护的官方订阅号。本订阅号中所有资料的版权均为中金公司所有,未经书面许可任何机构和个人不得以任何形式转发、转载、翻版、复制、刊登、发表、修改、仿制或引用本订阅号中的内容。





Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/172868
 
336 次点击