(报告出品方/作者:方正证券,曹春晓)
1 引言
2022 年 12 月,OpenAI 推出人工智能聊天机器人程序 ChatGPT,迅速 引起广泛的讨论和应用,仅耗时 2 个月月活用户即突破了 1 亿,成为 用户增长速度最快的消费级应用程序。ChatGPT 基于深度学习和神经 网络技术,可以高效的进行自然语言生成、文本分类、问答等多种任 务,有助于进一步实现自动化与提升效率,给予客户更好的交互体验, 未来其对金融领域可能产生较大的影响。 在报告《ChatGPT 在投资研究工作中的应用初探——ChatGPT 应用探 讨系列之一》中,我们较为详细的探讨了 ChatGPT 在金融投研领域中 的可能应用,从日常工作到数据分析,再到 API 接口和其他应用领域。 本文中我们将进一步聚焦于 ChatGPT 在数量化研究领域的应用,分别 从择时、风格轮动、行业轮动、选股因子挖掘等方面进行实践,进一 步阐述 ChatGPT 在投研工作中的应用前景。
2 基于 ChatGPT 构建择时策略
2.1 均线择时策略及其优劣势
在股票市场投资中,择时是最复杂且挑战最大的任务之一,由于市场 影响因素众多,很难从某一个维度构建非常有效的择时模型。在实际 应用中,技术分析领域中的均线策略是一种非常经典且常用的择时策 略,至今仍然大量应用在 CTA 策略中。 均线策略的基本思想是通过计算股票价格在一定时间内的均值,来判 断股票价格走势和买卖信号。具体来说,均线策略包括以下几个步骤: 1)选择合适的均线周期。一般情况下,常用的均线周期为 5 日、10 日、20 日、40 日、60 日、120 日和 240 日等。 2)根据选定的均线周期计算出相应的均线。计算均线的方法有简单 移动平均线(SMA)和指数移动平均线(EMA)等,其中 EMA 更加 灵敏。 3)判断股票价格与均线的关系。当股票价格上穿均线时,称为“金 叉”,表示股票价格开始向上突破均线,为买入信号;当股票价格下 穿均线时,称为“死叉”,表示股票价格开始向下跌破均线,为卖出 信号。 4)根据买卖信号执行买卖操作。
其中,均线周期的选择对策略的影响较为显著,均线周期较短,有利 于抓取短期的波段行情,但频繁切换可能导致交易成本陡增。均线周 期较长,更有利于抓取长期趋势,但其滞后性较为明显,在震荡行情 中可能失效。
实际应用中,长短期均线有不同的优点和劣势,我们可以通过使用多 均线系统的方式将其结合,尝试搭建既能跟踪市场主要趋势,又能在 拐点附近尽可能及时响应的均线系统。
2.2 基于 ChatGPT 构建均线排列择时模型
当我们使用 1 根均线进行择时时,可以得到的是做多或者做空两种信 号,但是当我们使用多根均线时,模型结果不再是多空两种信号,而 是一个相对连贯的打分体系。例如我们设定当短期均线上穿长期均线 时,+1 分,短期均线下穿长期均线时,-1 分,最终将多组均线的结 果汇总,即可得到一个综合长短期均线特点的打分模型。 我们尝试通过 ChatGPT 来实现这个部分: 我们的需求:请写一个 Python 函数,输入是指数日频收盘价,格式 为 DataFrame,输出为指数的均线排列得分。在每个交易日,选择 N 根均线,从短到长排列,判断相邻两根均线的位置,如果短期均线在 长期均线上方,则得 1 分,反之得-1 分,将 N-1 个得分相加,即得到 均线排列得分。
ChatGPT 的回答:
可以看到,ChatGPT 不仅准确给出了计算均线排列得分的函数,而且 给出了详细的解释和使用示例。 得到上述核心部分代码之后,我们就可以来编写策略了,在报告 《ChatGPT在投资研究工作中的应用初探——ChatGPT应用探讨系列 之一》中我们曾介绍过,与 ChatGPT 交互时,尽可能具体的表述对一 次获得正确的结果至关重要,此处我们将整个策略分步骤明晰列示。 我们的需求:请实现一个均线排列择时的策略,按照如下步骤: 1、定义多条均线的时间窗口 ma_list,为 1 到 252 的自然数序列; 2、读取沪深 300 指数日度收盘价数据,并调用上述 ma_score 函数计 算每日的均线排列得分; 3、对上述均线排列得分再做一次移动平滑,窗口为 60 个交易日; 4、当均线排列得分大于 60 日均线时,做多沪深 300 指数,小于 60 日均线做空,计算策略历史表现并将策略净值曲线与沪深 300 指数绘 制于图中。
ChatGPT 的回答:
至此,一个简易的多均线排列择时策略已经构建完成。我们也 可以进一步在此基础之上测试不同参数下的策略表现,从而得到最优 策略参数,或进一步构建更为复杂的择时模型。
3 基于 ChatGPT 构建风格轮动观测模型
3.1 近年来 A 股市场风格切换较为剧烈
风格轮动是获取超额收益的重要来源之一,不论是美股市场还是 A 股 市场,都存在着非常典型的风格轮动现象,如能够有效捕捉到风格切 换的时点,则可以获取较为可观的相对收益。 就 A 股市场而言,近几年风格切换相对较为剧烈,2019-2020 年期间大盘成长风格明显走强,以贵州茅台和宁德时代为代表的核心资产股 价上涨明显,一批大盘成长风格的基金产品业绩表现尤为亮眼。然而 自 2021 年以来,大盘成长风格大幅走弱,与此同时小盘价值风格表 现相对亮眼,部分小盘价值风格的基金再次成为市场关注的焦点。 我们以几种相对较为宽泛的风格为例,来观察 A 股市场 的风格轮动现象,其中大盘/小盘风格分别以沪深 300 指数和中证 1000 指数为代表,价值/成长风格分别以国证价值指数和国证成长指数为代 表,高估值/低估值则以申万高市盈率指数和低市盈率指数为代表,绩 优股和亏损股分别以申万绩优股指数和亏损股指数为代表。可以看 到,近几年切换最为明显的风格是绩优股和亏损股,绩优股在 2019-2020 年迅速走强,估值快速提升,与亏损股的相对收益则出现 了快速拉升,随着 2021 年初白马股抱团的松动,绩优股/亏损股相对 表现大幅逆转,至今亏损股指数仍然显著跑赢绩优股指数。此外,价 值与成长风格的切换也相对较为明显,而大盘/小盘,高估/低估等风 格,相对而言近期并未出现明显的风格对立情形。
关于如何构建风格轮动策略,是投资者非常关注的一个问题,接下来 我们通过与 ChatGPT 交互风格轮动相关的话题,尝试构建一个简单的 观测风格轮动的模型。
显然,由于问题表述不够具体,ChatGPT 只能给我们一些相对宽泛的 回答,但其关于大小盘风格轮动、周期非周期风格轮动的表述,仍然 可以给予我们一些启发。由于根据宏观环境进行风格轮动研判属于长 周期的投资策略,此处我们尝试通过量价交易数据来构建一个短周期 内可观测的风格轮动模型。
显然,当我们尝试让 ChatGPT 给我们设计实际可用的策略时,其回复 的内容相对来讲质量较低,基本上无法形成有效策略,且多次尝试后 依然如此。因此我们直接给出一个策略,让其帮我们实现。
3.2 基于 ChatGPT 实现四象限风格轮动模型
通常我们可以用两个风格指数的相对强弱曲线来观察风格轮动状态, 以大盘/小盘为例,我们假设理想情况下大盘/小盘相对强弱曲线符合 正弦波曲线形态,随着大盘指数逐渐走强,其相对于小盘风格的相对强弱曲线开始加速上升,一段时间后随着市场预期逐步体现,相对强 弱曲线则开始减速上升,直至达到临界点,开始下跌,市场由此转向 小盘风格。 我们通过最小二乘回归来近似拟合相对强弱曲线的一阶导数与二阶 导数,并将其绘制于二维图中,即可得到一个实际可用的风格轮动观 测模型。在四象限中,一象限内大盘/小盘相对强弱指数加速上升,大 盘风格持续占优,三象限中相对强弱指数加速下行,小盘风格明显占 优。
整个策略的构建过程相对简单,我们将全部步骤告诉 ChatGPT,让其 来构建这个风格观测模型。
可以看到,在给定具体的计算步骤之后,ChatGPT 能够快速的完成代 码构建,且整段代码中除了开头和结尾部分有小错误之外,主体计算部分没有任何问题,我们将此代码复制到 Python 环境 下,简单修改后运行即可得到相应的结果。
至此我们已通过 ChatGPT 得到了一个风格轮动观测模型,如上文介 绍,该模型显示了近期价值风格与成长风格的相对强弱趋势变化图, 整个轨迹在四象限图中沿着逆时针方向旋转,截至 4 月底,轨迹运行 至一象限价值风格占优区域,表明目前价值风格相对于成长风格占 优。该模型是一个通用模型,我们将其简单修改调整即可用来观测任 意两组不同风格指数的强弱关系。
从截至 4 月底最新轮动状态来看,在大盘/小盘风格维度,大盘风格已 开始走出相对优势,与此同时,高估值风格相对于低估值风格的轮动 轨迹已进入三象限,表明低估值板块开始走强。
从绩优股与亏损股风格的相对轮动状态来看,截至 4 月底绩优股开始 逐步走强,轨迹刚刚进入一象限区域,同时今年以来表现最强势的 TMT 板块,相对于全市场的相对强弱已开始走弱,目前正处于二象限 区域,后续需关注其是否会继续走弱切换进入三象限,从模型逻辑角 度考虑,我们建议短期保持谨慎。
4 基于 ChatGPT 构建行业轮动策略
4.1 行业配置是获取超额收益的重要来源
从 A 股市场历年表现来看,行业分化现象均较为明显,除市场普跌的 2011 年、2016 年、2018 年外,其余各年份行业涨跌分化均较为明显。 今年以来截至 4 月 30 日,申万一级行业中传媒行业累计上涨 51.78%, 表现最为出色,同期商贸零售行业下跌 10.87%,最好最差行业表现相 差 62.65%,分化较为明显。
行业配置是获取超额收益的重要来源,如何把握行业轮动特征,如何 构建有效的行业轮动策略,是投资者重点关注的问题。影响行业涨跌 分化的原因有很多,此处我们从行业基本面景气变动角度出发,尝试 通过 ChatGPT 来构建一个实际可行的行业轮动策略。
4.2 基于 ChatGPT 构建的基本面景气行业轮动策略
通过上市公司财务数据可以判断出企业的盈利情况,行业或板块也类 似。对于不同行业而言,由于成分股的差异较大,直接对比行业的财 务指标会存在较大的偏差,因此,我们在对行业进行基本面指标比较 时通常采用同比或环比数据。此外,关于行业基本面指标的计算存在 不同方法,本文中我们使用整体法计算行业财务指标。
接下来,我们将分步骤完成行业轮动策略的构建,首先我们需要计算 各行业的基础财务指标。 我们的需求:请写一个 Python 函数,用来实现通过整体法计算行业 的财务指标,输入为 DataFrame,包含字段股票代码 stockcode,财 报日期 reportdate,申万行业分类 sws1,TTM 归母净利润 netpp_ttm, TTM 营业收入 rev_ttm,单季度归母净利润 netpp_q,单季度营业收 入 rev_q,净资产 equitp。 计算各行业的 TTM 归母净利润环比增速、TTM 营业收入环比增速、 单季度归母净利润同比增速、单季度营业收入同比增速、ROE_TTM 环比变动值,单季度 ROE 同比变动值。
ChatGPT 的回答:
从上述代码来看,基本实现了整体法的计算逻辑,对于环比增速和同 比增速的理解正确,并且考虑到了分母为负数的情形,使用了 abs 函 数调整,但其仍然存在一些 bug,比如指标命名全部为_yoy,按照习 惯我们需要将环比增速的指标后缀修改为_qoq,同时代码中遗漏了 ROE 的环比变动和同比变动部分,也需要进一步补充。 接下来,我们需要定义一个时间序列标准化函数 zscore,以实现对行业财务指标进行时间序列的标准化处理,之所以进行标准化,一方面 是因为不同行业之间本身景气趋势存在差异,我们希望通过这种方式 过滤掉行业本身的趋势,只关注其边际变化,另一方面,在此前的研 究中我们也发现,经过 zscore 处理之后的行业财务指标,业绩表现都 较原始指标有明显改善。
我们的需求:请写一个 Python 函数,输入为 DataFrame,包含字段 sws1, reportdate, roe_ttm_qoq, netpp_ttm_qoq, rev_ttm_qoq, netpp_q_yoy, rev_q_yoy , 将 roe_ttm_qoq, netpp_ttm_qoq, rev_ttm_qoq, netpp_q_yoy, rev_q_yoy 等财务指标滚动 8 个季度进 行 zscore 标准化,即最新值减去历史均值除以标准差。
ChatGPT 的回答:
至此,我们的行业轮动策略核心部分已经构建完成,我们进一步将上 述经过标准化之后的财务指标等权合成为行业基本面景气得分,每个 财报期结束之后,根据行业基本面景气得分选取前 6 个一级行业作为 多头组合,后 6 个行业作为空头组合,由于年报和一季报在同一时间 点披露完毕,因此在实际组合构建时我们剔除年报数据,仅使用一季 报的最新得分进行排序和分组。 可以看到,该行业轮动策略虽然每年只调整三次,但是其历史表现非 常出色,自 2012 年以来,多头组合年化收益率为 11.28%,空头组合 年化收益率为 1.75%,多空年化收益约为 9.53%。且该行业轮动模型 在历史大多数年份中,多头组合均较空头组合有明显的超额收益,多 空相对净值曲线整体表现相对较为平稳。 该模型仅利用了历史财报数据计算,更新频率较低,且每年三季报披 露结束之后有较长的业绩真空期,我们可以在此基础之上,添加分析 师预期数据、资金流数据等不同维度指标,进一步丰富和完善行业轮 动策略,此处不再赘述。
5 基于 ChatGPT 挖掘高频量价选股因子
5.1 成交量的“激增时刻”
在股票市场中,成交量的边际变化隐含着非常重要的信息,特别是在 技术分析领域,成交量被认为是股票市场的原动力。俗语“量在价先” 深刻反应了成交量的变化对于股票价格波动的预测具有指示性作用。 我们以利好信息为例,当一个利好信息公布后,可能会引起相应个股 成交量的突然放大。如果在成交量激增的同时,价格却未发生变动, 或者未能引起价格的波动,则表明这一利好消息没能得到市场广泛的 认可。相反,如果成交量激增的同时,价格出现大幅上涨,则表明市 场对于此利好信息反应过于趋同,有可能出现反应过度。 报告《成交量激增时刻蕴含的 alpha 信息——多因子选股系列研究之 一》中我们通过观察日内成交量激增的时段,考察这些时段的收益率 与波动率,并将市场平均水平作为“适度”程度的衡量标准,进而构 建“耀眼波动率”因子和“耀眼收益率”因子,并最终合成为能综合 反应投资者反应不足和反应过度程度的“适度冒险”因子。
“适度冒险”因子的构建大致可以分为如下几个步骤:
1)确定“激增时刻”: ①剔除开盘和收盘数据,仅考虑日内分钟频数据,计算个股每分钟的 成交量相对于上一分钟的成交量的差值,作为该分钟成交量的增加 量。 ②计算每天每只个股分钟频成交量的增加量的均值 mean 和标准差 std。我们定义那些分钟频成交量增加量大于“均值+1 倍标准差”的 时刻为成交量激增的时刻,我们将对应的时刻统称为“激增时刻”。
2)根据“激增时刻”后的价格波动,计算“月耀眼波动率”因子: ①定义“激增时刻”的这一分钟及其随后的 4 分钟,是因成交量激增而 引起投资者关注的 5 分钟。投资者对成交量激增的反应,在这 5 分钟 里表现得最充分最强烈,我们将这 5 分钟称为“耀眼 5 分钟”。 ②使用分钟收盘价,计算每分钟的收益率,进而可以得到每个“耀眼 5 分钟”里收益率的标准差,作为成交量激增引起的价格波动率,我 们将其称为“耀眼波动率”。 ③计算 A 股票在 t 日内所有“耀眼波动率”的均值,作为 t 日 A 股票 对成交量的激增在波动层面上反应的代理变量,记为“日耀眼波动率”。 ④根据前述分析,我们希望“日耀眼波动率”不要太大,也不要太小, 适度最好,为了不引入其他参数,我们选取“日耀眼波动率”的截面 均值作为最“适度”的水平。因此我们将每日的“日耀眼波动率”减 去截面的均值再取绝对值,表示个股的“日耀眼波动率”与市场平均 水平的距离,并将其记为日频因子“适度日耀眼波动率”。 ⑤我们分别计算最近 20 个交易日的“适度日耀眼波动率”的平均值 和标准差,记为“月均耀眼波动率”因子和“月稳耀眼波动率”因子。 ⑥将“月均耀眼波动率”与“月稳耀眼波动率”等权合成,得到“月 耀眼波动率”因子。
3)根据“激增时刻”的价格变动,计算“月耀眼收益率”因子: ①找到“激增时刻”对应的分钟收益率,我们将其称为“耀眼收益率”。 ②对 A 股票在 t 日内所有的“耀眼收益率”求均值,作为股票对成交 量激增在收益率层面反应的代理变量,记为“日耀眼收益率”。 ③根据上述分析,我们同样希望“日耀眼收益率”不要太大,也不要 太小,适度最好。因此我们将每日的“日耀眼收益率”减去截面的均 值再取绝对值,表示个股的“日耀眼收益率”与市场平均水平的距离, 并将其记为“适度日耀眼收益率”。 ④我们分别计算最近 20 个交易日的“适度日耀眼收益率”的平均值 和标准差,记为“月均耀眼收益率”和“月稳耀眼收益率”因子。 ⑤将“月均耀眼收益率”与“月稳耀眼收益率”因子等权合成,得到 “月耀眼收益率”因子。
4)将上述“月耀眼波动率”因子与“月耀眼收益率”因子等权合成, 即得到“适度冒险”因子。 在报告《成交量激增时刻蕴含的 alpha 信息——多因子选股系列研究 之一》中我们对“适度冒险”因子在月频选股效果上的回测结果显示: 合成之后的“适度冒险”因子表现非常出色,Rank IC 为-8.89%,Rank ICIR 为-4.84,多空组合年化收益率达 37.46%,信息比 4.10,因子月 度胜率 87.74%。此外,在剔除了常用的风格因子影响后,“适度冒险” 因子仍然具有较强的选股能力,Rank IC 均值为-3.18%,Rank ICIR 为 -1.89,多空组合年化收益率 18.07%,信息比率 2.23。
接下来我们尝试通过 ChatGPT 来复现这个因子,这是一个相对较为复 杂的因子,为了达到目标,我们需要将因子构建过程分解,逐步得到 最终的因子构建代码。 首先,该因子构建中最核心的问题是关于“激增时刻”的定义,“激 增时刻”指的是在某一分钟上成交量的突然放大,其成交量相比于前一分钟出现明显放量,这些分钟就 是我们所定义的“激增时刻”。
我们的需求:请写一个 Python 函数,用来实现寻找个股日内交易的 激增时刻,读入为 DataFrame,表示个股分钟频交易数据,包含字段 code, amount, money, high, low, close, open, num,其中 num 表示当天 第几分钟,按照如下步骤进行: 1、剔除开盘 1 分钟以及收盘前 1 分钟的数据; 2、计算每只股票每分钟的收益率 ret 和每分钟成交量的增量 diff_amount; 3、计算每只股票当天 diff_amount 的均值和标准差,筛选出该股票大 于其均值加一倍标准差的时刻即为该股票当天的激增时刻。
需要注意的是,此处省略了数据预处理部分,本文中高频数据来源于 米筐 API,其将每天最后三分钟的交易数据合并为一根 bar,因此此处 剔除收盘前 1 分钟的数据实际代表的是剔除收盘前 3 分钟的数据(即 集合竞价数据),如其他数据接口可能需要修改第一步表述部分。 ChatGPT 的第一个回答版本,该函数完全理解了我们的 意图,并按照要求给出了每个股票每天的“激增时刻”及其对应的数 据,但是在函数中使用了 for 循环来计算,这在分钟频数据上的计算 是非常耗时的,因此我们尝试让 ChatGPT 来优化这个过程,我们直接 告诉 ChatGPT:for 循环太耗时,换一种算法。 这个版本的主要不同之处在于它使用了 Pandas 的向量化操作 add 和 mul 来计算均值加一倍标准差,而不是使用 for 循环和逐个计算 每只股票的激增时刻。这种方式更快且更有效率,特别是在处理大量数据时。但其中有一处 bug 是其计算的阈值是均值+2 倍标准差,而非 我们定义的均值+1 倍标准差,因此我们需要修改 mul 的参数为 1,即 可得到正确的结果。
5.2 基于 ChatGPT 构建“适度冒险”因子
接下来,我们复现因子的具体计算过程。找到“激增时刻”之后,我 们需要分别构建两个细分因子:“耀眼波动率因子”与“耀眼收益率 因子”,其中“耀眼波动率因子”需要考察“激增时刻”之后的五分 钟内的波动率情况,“耀眼收益率因子”则通过“激增时刻”这一分钟收益率来计算,我们在同一个函数中实现其计算过程。 我们的需求:请写一个 Python 函数,用来计算日耀眼波动率因子和 日耀眼收益率因子,读入为 DataFrame,表示个股分钟频交易数据, 包含字段 code, amount, money, high, low, close, open, num,其中 num 表示当天第几分钟,按照如下步骤进行: 1、计算每只股票每分钟的收益率 ret; 2、计算每只股票每一分钟及其未来四分钟内的收益率的标准差,得 到该分钟该股票对应的波动率指标 vol; 3、调用上述 find_volume_spikes 函数,得到每只股票当天的激增时 刻,找到各股票激增时刻对应的波动率 vol_s 和收益率指标 ret_s; 4、对每只股票当天所有的 vol_s 和 ret_s 取平均值即可得到日耀眼波 动率因子和日耀眼收益率因子。 5、对日耀眼波动率因子和日耀眼收益率因子进行均值距离化处理(减 去均值后再取绝对值)。
ChatGPT 的回答:
可以看到,由于我们的描述相对较为具体,所以 ChatGPT 可以很好的 理解我们的意图,不过上述计算过程中没有考虑到时间的移动问题, 即计算的波动率指标过程中使用 rolling(5).std()之后,需要再将其前 置 5 分钟,才符合我们所定义的激增时刻触发之后 5 分钟内的波动率。 所 以 这 一 步 正 确 的 表 述 应 该 为 : rolling_vol = df.groupby("code")["ret"].rolling(5).std().shift(-5)。 上述过程中对因子值进行均值距离化的处理,是因为我们在定义“适 度冒险”因子时,我们希望的是那些突然放量之后在波动率和收益率 层面有温和波动的股票,后续表现应该相对较好,而波动太大或太小,或者收益太高或太低的股票,都不是我们想做多的股票。
通过以上代码,我们即可初步得到了两个日度频率的因子“适度日耀 眼波动率”和“适度日耀眼收益率”,接下来的处理过程相对较为简 单,分别先对“适度日耀眼波动率”和“适度日耀眼收益率”因子进 行移动窗口为 20 个交易日的移动平均值和移动标准差,再将平均值 和标准差因子合并,分别得到“月耀眼收益率”因子和“月耀眼收益率” 因子,再将这两个因子合并,即可得到“适度冒险”因子。此过程相 对较为简单,我们不再展示与 ChatGPT 的交互过程。
6 总结
本文中我们通过与 ChatGPT 交互,分别构建了均线排列择时模型、四 象限风格轮动模型、基本面景气行业轮动模型以及“适度冒险”因子 模型。综合来看 ChatGPT 对于复杂金融问题的理解和实现基本没有障 碍,但本文的实现过程均需要有一定的 Python 基础,此外,在测试过 程中我们发现想要通过 ChatGPT 自己来构建一个实际可用的策略目 前还难以实现,但如果我们能够较为准确的描述策略思想和主要过 程,ChatGPT 可以通过强大的理解能力和编程能力,快速的帮我们实 现策略过程,因此 ChatGPT 在量化研究领域同样具有强大的生命力, 可以大幅提升投研人员的开发效率。
(本文仅供参考,不代表我们的任何投资建议。如需使用相关信息,请参阅报告原文。)
精选报告来源:【未来智库】。