一、引言
在A股市场中,中证500指数作为表征中小盘成长股的核心指数,兼具高弹性与高成长属性,长期以来受到量化投资者的广泛关注。本研究构建了一套创新的多策略融合量化投资体系,将指数增强策略与期货CTA策略有机结合,通过基本面选股与趋势跟踪相结合的方式,在控制风险的前提下追求稳健的超额收益。
随着市场有效性的逐步提升,单一策略的超额收益空间逐渐收窄,而多策略融合的投资框架展现出更强的适应性。本文基于Python量化交易框架,构建了一套以中证500指数为基准的股期混合策略,通过基本面多因子选股筛选优质标的,结合期货趋势跟踪实现风险对冲,最终实现组合收益的稳健增强。本研报将详细阐述策略的构建逻辑、多策略融合机制及风险控制体系,为量化投研提供新的思路和实践参考。
二、研究目的
1.构建一套逻辑清晰、可落地的中证 500 指数增强量化策略,核心目标是在控制组合波动率与最大回撤的前提下,持续获取超越中证 500 指数的超额收益。
2.验证趋势跟踪因子在中证 500 成分股中的有效性,通过线性回归拟合股价趋势斜率,筛选具备持续上涨动能的标的,提升组合收益弹性。
3.建立多维度风险控制体系,通过涨幅过滤避免标的过度上涨后的回调风险,通过静态止损、动态回调止损、均线止损等多种机制,有效控制个股及组合的下行风险。
4.优化策略参数与交易执行逻辑,确保策略在不同市场环境下的稳健性,为实际投资决策提供可参考的量化依据。
三、回测框架
3.1 回测环境与数据来源
1.回测平台:基于聚宽(JoinQuant)量化交易平台,依托其提供的 jqdata 数据接口获取全市场股票行情、财务数据及指数成分股数据。
2.数据类型:包括股票日度开盘价、收盘价、最高价、最低价等行情数据,指数成分股调整数据,股票基本信息(如上市日期、是否 ST、是否退市等)。
3.复权方式:采用前复权处理,确保价格数据的连续性与可比性;启用真实价格模式,模拟实际交易中的价格成交场景。
3.1.1 资产配置框架
本策略采用创新的子账户管理模式,将总资金按7:3的比例分配到股票和期货两个子账户:
1.股票子账户:70%资金,用于指数增强策略
2.期货子账户:30%资金,用于CTA趋势策略
通过定期再平衡机制,确保资产配置比例保持稳定。
3.1.2 交易成本设置
股票交易成本:买入佣金万分之三,卖出佣金万分之三加千分之一印花税
期货交易成本:开平仓佣金万分之0.23,平今仓佣金万分之2.3
设置合理的滑点成本,模拟真实交易环境。
3.2 核心参数设定
| 类别 | 参数 | 取值 | 说明 |
|---|---|---|---|
| 股票配置 | 持仓数量 | 20只 | 通过基本面多因子选股确定 |
| 期货配置 | 合约类型 | IC | 中证500股指期货 |
| 风控参数 | ATR止损倍数 | 5 | 基于波动率的动态止损 |
| 交易频率 | 股票调仓 | 月度 | 降低交易成本 |
| 交易频率 | 期货交易 | 每日 | 捕捉短期趋势 |
3.3 策略核心逻辑框架
本策略采用"股票指数增强+期货趋势跟踪"的双引擎架构,通过资产配置实现风险分散,各环节紧密衔接,确保策略的稳健运行。
3.3.1 股票指数增强模块
1.标的池净化:以中证500指数成分股为初始标的池,剔除ST股、涨跌停股、停牌股等异常标的
2.基本面筛选:通过资产负债率、资产质量、营运效率、盈利能力等多维度财务指标层层过滤
3.组合构建:精选20只优质标的,等权重配置,月度调仓
3.3.2 期货CTA模块
- 趋势信号生成:基于多周期EMA均线系统,通过长短周期均线的相对位置关系,识别市场的整体趋势方向,判断当前处于上升、下降或盘整阶段。
- 波动率调整:根据ATR指标动态评估市场波动水平,在波动率较低时适度增加仓位以捕捉趋势机会,在波动率较高时则相应减少仓位以控制风险。
- 风险控制:采用ATR动态止损机制,依据市场波动幅度灵活设置止损位,确保单笔交易损失控制在合理范围内,保护资金安全。
3.3.3 资产配置与再平衡
- 定期再平衡机制:我们将在每月固定日期执行资金划转操作,通过及时调整股票和期货的资金分配,确保投资组合中的股期配置比例始终稳定维持在7:3的预设目标水平。
- 风险预算管理体系:在期货子账户中设置严格的保证金比例限制,通过实时监控和调整杠杆水平,有效控制整体投资组合的杠杆风险敞口,确保风险控制在可承受范围内。
四、研究过程
4.1 策略代码核心逻辑解析
本策略代码基于Python语言开发,采用模块化设计原则,将复杂的多策略体系分解为相对独立的功能模块。
4.1.1 资产配置模块(initialize)
初始化模块主要负责完成策略的基础配置,其核心逻辑涵盖以下关键环节:
1.首先需要设置基准指数,通常以中证500作为衡量标准,并配置回测环境的基本参数;
2.其次,需要合理分配股票和期货两个子账户的资金比例,确保资金使用效率与风险控制相匹配;同时,还需设定交易成本、滑点等实际交易参数,以更真实地模拟市场交易环境;
3.最后,模块负责调度不同频率的交易任务,包括股票部分的月度调仓和期货部分的每日交易执行。
def initialize(context):
# 设定中证500作为基准
g.benchmark = '000905.XSHG'
set_benchmark(g.benchmark)
# 开启动态复权模式(真实价格)
# 用真实价格交易
set_option('use_real_price', True)
set_option("avoid_future_data", True)
log.set_level('order', 'error')
warnings.filterwarnings("ignore")
#设置初始账户资金分配
g.stock_share = 0.7#指增子账户占总账户资金比例
g.future_share = 0.3#期货子账户占总账户资金比例
g.future_position = 0.35 #期货持仓所需保证金占用的期货子账户资金比例
set_subportfolios([SubPortfolioConfig(cash=context.portfolio.starting_cash * g.stock_share, type='stock'),
SubPortfolioConfig(cash=context.portfolio.starting_cash * g.future_share, type='futures')])
4.1.2 股票选股模块(get_stock_list)
选股模块采用基本面多因子层层递进的筛选方法,通过多维度指标逐步过滤,提高投资组合的质量和稳定性。
第一层:资产负债率过滤,剔除杠杆率过高的企业,筛选财务结构稳健、偿债能力较强的标的,确保基础安全性。
第二层:资产质量过滤,重点关注应收账款和存货周转情况,剔除不良资产占比过高、资产减值风险较大的标的,提升资产健康度。
第三层:营运效率过滤,通过分析总资产周转率、存货周转率等指标,选择营运效率高、资源利用充分的优质企业。
第四层:盈利能力过滤,聚焦ROA(总资产收益率)持续改善、盈利质量稳定的标的,确保企业具备可持续的盈利成长能力。
最后,结合估值指标如市净率(PB)和市销率(PS),在优质标的中精选估值合理的个股,构建最终的投资组合。
代码过长,完整代码见下文第七章附录1展示代码
4.1.3 期货交易模块(market_trade_future)
期货交易模块通过系统化的设计实现了趋势跟踪与风险控制的有效结合:
1.在趋势判断方面,采用长短周期EMA指标之间的相对位置关系来准确识别当前市场趋势的方向变化。
2.仓位管理模块则会根据市场波动率状态进行智能调节,当波动率较低时适当增加持仓规模,而在高波动环境下则主动缩减头寸以控制风险。
3.止损机制采用基于ATR指标的倍数止损策略,能够根据市场波动幅度动态调整止损位置,有效控制交易的下行风险。同时,系统还具备自动处理期货合约到期换月的功能,确保在合约交割时能够平稳过渡到新的主力合约,保持交易的连续性。
g.future_type = 'IC'
g.futures_margin_rate = 0.15#保证金比例(现在好像是14%,懒得改了)
g.unitprice = 200
g.long_days = 6 # 几日均线以下开空
g.short_days = 2 # 几日以上均线开多
#ATR止损模块参数
g.ATRdays = 20 #计算ATR的时间区间长度
g.boundrydays = 5#计算最高最低价格的区间长度
g.stop = 5 # ATR止损倍数
#根据短期ATR和长期ATR的差确定波动率volatility。如果 短ATR-para*长ATR,表明即将变盘,可适当仓位重
g.shortdays = 20
g.longdays = 50
g.para = 1
# 期货类每笔交易时的手续费是:买入时万分之0.23,卖出时万分之0.23,平今仓为万分之0.23
set_order_cost(OrderCost(open_commission=0.000023, close_commission=0.000023,close_today_commission=0.0023), type='index_futures')
# 设定保证金比例
set_option('futures_margin_rate', g.futures_margin_rate)
# 设置期货交易的滑点
set_slippage(StepRelatedSlippage(2))
# 设置样本序列长度、模型占位、拟合模型时间间隔、时间计数
g.day = 20#每个月期货到期,20日为一个周期
g.day_count = int(g.day)
g.k = 1#初始交易期货手数
# 新增:初始化合约变量,避免未定义
g.code_1 = None
### 期货相关设定
4.2 多策略融合机制
本策略的核心创新在于将股票投资策略与期货交易策略进行了有机融合,通过两者的优势互补与风险对冲,构建出一个更加稳健且具备适应性的资产配置框架。
4.2.1 风险分散效应
股票和期货两个子策略具有不同的收益特征和风险来源,通过资产配置实现天然的风险分散。当股票市场出现系统性下跌时,期货空头仓位可以提供有效的对冲保护。
4.2.2 收益来源多元化
策略收益主要来源于三个相互独立的维度:
1.首先是股票alpha收益,即通过深入的基本面分析精选个股,力求获得超越市场基准的稳定超额回报;
2.其次是期货趋势收益,通过系统化跟踪期货品种的价格趋势,捕捉明确的方向性波动带来的收益机会;
3.最后是资产配置收益,通过定期对股票和期货资产进行动态再平衡,优化整体组合的风险收益比,实现更为稳健的配置回报。
def stock_alpha_generation():
# 基于财务指标的四层过滤体系
# 第一层:财务安全过滤(资产负债率后70%)
safe_stocks = filter_by_leverage(initial_universe)
# 第二层:资产质量过滤(不良资产比率20%-80%)
quality_stocks = filter_by_asset_quality(safe_stocks)
# 第三层:运营效率过滤(优质资产周转率前75%)
efficient_stocks = filter_by_operation_efficiency(quality_stocks)
# 第四层:盈利能力过滤(ROA改善前20%)
final_portfolio = filter_by_profitability_improvement(efficient_stocks)
return final_portfolio
4.2.3 风险控制体系
多层次风险控制机制:
1.个股层面:通过深入的基本面分析筛选优质标的,有效规避财务表现不佳、经营状况不稳定的劣质企业,从源头上降低投资风险。
2.组合层面:采用科学的资产配置策略,通过分散投资于不同行业、不同市场、不同资产类别的标的,有效降低非系统性风险,提升投资组合的整体稳健性。
3.系统层面:灵活运用股指期货等金融衍生工具进行对冲操作,在市场整体下跌时能够有效抵消部分损失,降低系统性风险带来的冲击。
4.交易层面:建立严格的动态止损机制,根据市场行情变化及时调整止损点位,有效控制极端行情下的潜在损失,保障投资资金的安全。
4.3 关键技术创新
4.3.1 基本面多因子选股体系
传统量化策略多依赖技术指标,本策略创新性地构建了完整的基本面选股体系,通过财务指标的层层过滤,确保投资标的的基本面质量。这种基于基本面的选股方法具有更强的逻辑支撑和可持续性。
4.3.2 股期动态配置机制
通过子账户管理和定期再平衡机制,实现了股票和期货两类资产的动态配置。这种配置方式既保证了策略的灵活性,又确保了风险控制的纪律性。
五、研究结果
5.1 核心业绩指标
基于上述回测设置进行了回测,策略的核心业绩指标如下表所示:
| 指标名称 | 策略组合 | 中证 500 指数 | 超额收益 |
|---|---|---|---|
| 累计收益率 | 2574.55% | 7.57% | 2386.29% |
| 年化收益率 | 40.21% | 0.72% | 39.49% |
| 最大回撤 | 26.41% | - | - |
| 夏普比率 | 1.526 | - | - |
| 波动率(年化) | 23.7% | 25.7% | -2.0% |
| 信息比率 | 1.522 | - | - |
| 卡玛比率 | 1.523 | - | - |
5.2 策略贡献分析
5.2.1 股票指数增强和期货CTA收益分析
策略年化超额收益达到40.21%%,信息比率为1.522,表现出稳定的选股能力。基于基本面多因子的选股方法在不同市场环境下均能产生持续的正向超额收益。呈现良好的互补性。在股市下跌期间,期货策略的空头收益有效对冲了股票部分的损失。
5.2.2 资产配置效应
通过股期配置和定期再平衡,策略实现了显著的风险分散效果,整体波动率明显低于单一资产策略。
5.3 风险控制效果评估
5.3.1 回撤控制
策略最大回撤为26.41%,远低于基准指数的42.76%。在2015年股灾、2018年熊市等极端市场环境下,策略通过期货对冲有效控制了净值回撤。
5.3.2 波动率管理
年化波动率23.7%,显著低于基准的25.7%,体现了多策略融合在风险分散方面的优势。
5.3.3 极端风险防护
在市场极端波动期间,策略的ATR止损机制和期货对冲功能发挥了重要作用,避免了大幅损失。
5.4 策略优势与价值
5.4.1 创新性优势
1.我们采用多策略融合的投资方法,将股票指数增强策略与期货CTA策略有机结合,充分发挥各自优势,实现收益来源的多元化与风险的有效分散。
2.同时,我们坚持基本面驱动的选股逻辑,基于财务指标等基本面数据进行深度分析,筛选优质标的,使策略逻辑清晰、具备持续性和可解释性。
3.在风险控制方面,我们构建了多层次动态风控体系,通过期货对冲和基于ATR的止损机制,实时监测并管理组合风险,力求在复杂市场环境中保持稳健运行。
5.4.2 实用价值
- 稳健性:该策略具备出色的稳健性能,在不同市场环境下,无论是牛市、熊市还是震荡市,均能保持相对稳定的收益表现,有效控制回撤幅度,展现了较强的抗风险能力。
- 可扩展性:策略框架设计灵活,允许通过调整关键参数来适应不同的风险偏好和收益目标需求,用户可根据自身投资风格和市场判断进行个性化定制,具有良好的扩展空间。
- 实盘可行性:在策略设计阶段已充分考虑实际交易中的各项成本约束,包括手续费、滑点等因素,确保策略在实盘环境中具备可操作性,具有较强的实际应用价值。
六、未来优化与展望
6.1 策略优化方向
6.1.1 因子融合优化
当前策略仅依赖趋势跟踪单一因子,虽然效果良好,但单一因子在特定市场环境下仍可能失效。未来可引入多因子融合模型,提升筛选结果的准确性与稳定性:
1.引入价值因子:如市盈率(PE)、市净率(PB)、市销率(PS)等,筛选估值合理的标的,避免买入估值过高的标的,降低价值回归风险;
2.引入成长因子:如净利润增速、营收增速、ROE(净资产收益率)等,筛选具备持续成长能力的标的,增强组合的长期收益潜力;
3.引入流动性因子:如日均成交额、日均换手率等,剔除流动性不足的标的,降低交易滑点与流动性风险;
4.引入行业因子:通过行业分散化配置,避免组合过度暴露于单一行业,降低行业系统性风险。可设置行业持仓比例限制,确保组合行业分布均衡。
通过多因子加权评分,综合考虑趋势、价值、成长、流动性等多个维度,筛选出综合质量更高的标的,提升策略的稳健性与长期收益能力。
6.1.2 参数自适应调整
当前策略参数为固定值,虽然可手动调整,但无法自动适应市场环境变化。未来可引入参数自适应机制,根据市场状态动态调整核心参数:
1.市场状态识别:通过波动率、趋势强度、成交量等指标,识别当前市场处于牛市、熊市还是震荡市;
参数动态调整:
2.牛市中:延长趋势筛选周期,提高涨幅过滤上限,捕捉更长周期的趋势机会;
3.熊市中:缩短趋势筛选周期,降低止损线,加快止损速度,控制回撤;
4.震荡市中:启用回调过滤,降低换仓频率,减少交易成本。
参数自适应机制可通过机器学习算法实现,根据历史数据训练最优参数组合,提升策略对市场环境的适应性。
6.1.3 风险控制升级
当前策略的风险控制主要集中在个股层面,未来可升级至组合层面,构建更全面的风险控制体系:
1.组合波动率控制:设置组合年化波动率上限,当组合波动率超过阈值时,自动降低仓位或调整持仓结构;
2.行业集中度控制:设置单一行业持仓比例上限,避免组合过度暴露于单一行业,降低行业系统性风险;
3.最大回撤控制:设置最大回撤阈值,当组合最大回撤达到阈值时,自动降低仓位至 50% 以下,直至市场趋势好转;
4.极端风险对冲:在极端市场环境下,自动触发全仓止损,规避系统性风险。
6.2 研究展望
6.2.1 人工智能算法的应用
随着人工智能技术的发展,未来可将其应用于策略优化,提升策略的智能化水平:
1.股价趋势预测:采用 LSTM 神经网络、Transformer 等深度学习模型,基于历史行情数据、基本面数据、舆情数据等多维度信息,预测标的未来价格趋势,替代传统的线性回归拟合,提升趋势判断的准确性;
2.因子权重优化:通过强化学习算法,动态优化多因子模型的权重组合,根据市场环境变化自适应调整各因子的重要性,提升筛选结果的有效性;
3.风险预警模型:构建机器学习风险预警模型,基于标的基本面、技术面、市场情绪等多维度数据,预测标的的潜在风险,提前从标的池中剔除高风险标的。
6.2.2 多指数与多市场拓展
当前策略仅针对中证 500 指数,未来可拓展至其他指数与市场,构建多指数增强产品矩阵:
1.其他宽基指数:将策略逻辑应用于沪深 300、中证 500、创业板指等其他宽基指数,分析策略在不同指数中的适应性,为投资者提供更多选择;
2.行业指数:针对消费、科技、医药等行业指数,优化策略参数,构建行业指数增强策略,满足投资者的行业配置需求;
3.跨市场拓展:将策略逻辑拓展至港股、美股等海外市场,结合海外市场的交易规则与市场特征,优化策略参数与交易执行逻辑,实现跨市场量化投资。
6.3 总结
本研究基于趋势跟踪与多维度风控逻辑,构建了一套完整的中证 500 指数增强量化策略。通过回测验证,策略在累计收益率、年化收益率、最大回撤、夏普比率等核心指标上均显著优于基准指数,实现了稳定的超额收益,展现出良好的收益能力与风险控制能力。
策略的核心优势在于逻辑清晰、风控全面、适应性强、参数可调,能够满足不同投资者的个性化需求;同时,策略也存在趋势滞后性、震荡市表现较弱、交易成本侵蚀等局限性,需要在未来的研究中持续优化。
未来,通过多因子融合、参数自适应调整、交易成本优化、风险控制升级等措施,可进一步提升策略的稳健性与长期收益能力;同时,结合人工智能算法、多指数拓展、实盘验证等方向的研究,能够丰富策略的应用场景,提升策略的实践价值。
七、附录
附录1【中证500指数增强】完整代码:
# 导入函数库
from jqdata import *
from jqlib.technical_analysis import *
import pandas as pd
from jqfactor import get_factor_values
import numpy as np
import warnings
# 初始化函数,设定基准等等
def initialize(context):
# 设定中证500作为基准
g.benchmark = '000905.XSHG'
set_benchmark(g.benchmark)
# 开启动态复权模式(真实价格)
# 用真实价格交易
set_option('use_real_price', True)
set_option("avoid_future_data", True)
log.set_level('order', 'error')
warnings.filterwarnings("ignore")
#设置初始账户资金分配
g.stock_share = 0.7#指增子账户占总账户资金比例
g.future_share = 0.3#期货子账户占总账户资金比例
g.future_position = 0.35 #期货持仓所需保证金占用的期货子账户资金比例
set_subportfolios([SubPortfolioConfig(cash=context.portfolio.starting_cash * g.stock_share, type='stock'),
SubPortfolioConfig(cash=context.portfolio.starting_cash * g.future_share, type='futures')])
### 股票相关设定 ###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 股票类每笔交易时的手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
#选股参数
g.stock_num = 20 # 持仓数
### 股票相关设定 ###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### 期货相关设定 ###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
g.future_type = 'IC'
g.futures_margin_rate = 0.15#保证金比例(现在好像是14%,懒得改了)
g.unitprice = 200
g.long_days = 6 # 几日均线以下开空
g.short_days = 2 # 几日以上均线开多
#ATR止损模块参数
g.ATRdays = 20 #计算ATR的时间区间长度
g.boundrydays = 5#计算最高最低价格的区间长度
g.stop = 5 # ATR止损倍数
#根据短期ATR和长期ATR的差确定波动率volatility。如果 短ATR-para*长ATR,表明即将变盘,可适当仓位重
g.shortdays = 20
g.longdays = 50
g.para = 1
# 期货类每笔交易时的手续费是:买入时万分之0.23,卖出时万分之0.23,平今仓为万分之0.23
set_order_cost(OrderCost(open_commission=0.000023, close_commission=0.000023,close_today_commission=0.0023), type='index_futures')
# 设定保证金比例
set_option('futures_margin_rate', g.futures_margin_rate)
# 设置期货交易的滑点
set_slippage(StepRelatedSlippage(2))
# 设置样本序列长度、模型占位、拟合模型时间间隔、时间计数
g.day = 20#每个月期货到期,20日为一个周期
g.day_count = int(g.day)
g.k = 1#初始交易期货手数
# 新增:初始化合约变量,避免未定义
g.code_1 = None
### 期货相关设定 ###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### 股票交易运行 ###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
run_monthly(my_select, monthday=-1, time='9:30', reference_security=g.benchmark)# 选股
run_monthly(my_buy, monthday=-1, time='11:15', reference_security=g.benchmark)# 买入指数增强选出的股票
run_monthly(my_sell, monthday=10, time='10:30', reference_security=g.benchmark)# 月中卖出调仓换股
run_monthly(rebalance, monthday=10, time='11:00', reference_security=g.benchmark)# 股票和期货账户间资金划转
### 股票交易运行 ###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### 期货交易运行 ###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 开盘前运行
run_daily( before_market_open_future, time='9:00', reference_security='IF8888.CCFX')
# 开盘时运行
run_daily( market_trade_future, time='11:15', reference_security='IF8888.CCFX')
### 期货交易运行 ###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'''
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
指数增强部分代码
'''
# 开盘时运行函数
def my_select(context):
# 获取选股列表并过滤掉:st,st*,退市,涨停,跌停,停牌
check_out_list,g.position = get_stock_list(context)
return check_out_list
def my_sell(context):
# 获取选股列表并过滤掉:st,st*,退市,涨停,跌停,停牌
adjust_position_sell(context, my_select(context))
def my_buy(context):
# 获取选股列表并过滤掉:st,st*,退市,涨停,跌停,停牌
adjust_position_buy(context, my_select(context))
# 2-2 选股+仓位管理
# 选出资产负债率后20%且大于0,优质资产周转率前20%,roa改善最多的股票列表
def get_stock_list(context):
# type: (Context) -> list
curr_data = get_current_data()
yesterday = context.previous_date
df_stocknum = pd.DataFrame(columns=['当前符合条件股票数量'])
# 过滤次新股
by_date = yesterday - datetime.timedelta(days=1200) # 三年
initial_list = get_index_stocks('000905.XSHG',date=by_date)
# 0. 过滤创业板,科创板,st,今天涨跌停的,停牌的
initial_list = [stock for stock in initial_list if not (
(curr_data[stock].day_open == curr_data[stock].high_limit) or
(curr_data[stock].day_open == curr_data[stock].low_limit) or
curr_data[stock].paused
)]
# 1,选出资产负债率由高到低后70%的,low_liability_list
df = get_fundamentals(
query(
balance.code, balance.total_liability, balance.total_assets
).filter(
valuation.code.in_(initial_list)
)
).dropna()
df = df.fillna(0)
df['ratio'] = df['total_liability'] / df['total_assets'] # 资产负债率
df = df.sort_values(by = 'ratio',ascending=False)
low_liability_list = list(df.code)[int(0.3*len(list(df.code))):]
# 2,从low_liability_list中选出不良资产比率在总体中[20%-80%]范围内的的股票,proper_receivable_list
df1 = get_fundamentals(
query(balance.code,
balance.total_assets, # 总资产
balance.bill_receivable, # 应收票据
balance.account_receivable, # 应收账款
balance.other_receivable, # 其他应收款
balance.good_will, # 商誉
balance.intangible_assets, # 无形资产
balance.inventories, # 存货
balance.constru_in_process,# 在建工程
).filter(
balance.code.in_(low_liability_list)
)
).dropna()
df1 = df1.fillna(0)
df1['bad_assets'] = df1.sum(axis=1) - df1['total_assets'] # 其中bad_assets占的比例
df1['ratio1'] = df1['bad_assets'] / df1['total_assets']
df1 = df1.sort_values(by = 'ratio1',ascending=False)
proper_receivable_list = list(df1.code)[int(0.1*len(list(df1.code))):int(0.9*len(list(df1.code)))]
# 3,从proper_receivable_list中选出优质资产周转率前75%的公司,proper_receivable_list1
df2 = get_fundamentals(
query(balance.code,
balance.total_assets, # 总资产
balance.bill_receivable, # 应收票据
balance.account_receivable, # 应收账款
balance.other_receivable, # 其他应收款
balance.good_will, # 商誉
balance.intangible_assets, # 无形资产
balance.inventories, # 存货
balance.constru_in_process,# 在建工程
income.total_operating_revenue# 营业收入
).filter(
balance.code.in_(proper_receivable_list)
)
).dropna()
df2 = df2.fillna(0)
df2['good_assets'] = df2['total_assets'] - (df2.sum(axis=1) - df2['total_assets'] - df2['total_operating_revenue']) # 其中bad_assets占的比例
df2['ratio2'] = df2['total_operating_revenue'] / df2['good_assets']
df2 = df2.sort_values(by = 'ratio2',ascending=False)
proper_receivable_list1 = list(df2.code)[:int(0.75*len(list(df2.code)))]
# 4,从proper_receivable_list1中过去十二个季度ROA增长最多(前20%)的股票,按照ROA`增长量降序排列,roa_list
df3 = get_history_fundamentals(
proper_receivable_list1,
fields=[indicator.code, indicator.roa],
watch_date=yesterday,
count=4,
interval='1q'
).dropna()
s_delta_avg = df3.groupby('code')['roa'].apply(
lambda x: x.iloc[3] - x.mean() if len(x) == 4 else 0.0
).sort_values(
ascending=False
)
roa_list = list(s_delta_avg[:int(0.2 * len(s_delta_avg))].index)
# 5.从过去五个季度ROA增长量前20%的股票中选出市净率大于0的按照资产负债率升序排列选出前g.stock_num个,final_list
pb_list = get_fundamentals(
query(
valuation.code
).filter(
valuation.code.in_(roa_list),
valuation.pb_ratio > 0.7,
valuation.ps_ratio < 3,
).order_by(
valuation.pb_ratio.asc()
)
)['code'].tolist()
df_stocknum = df_stocknum.append({'满足条件股票数量': len(pb_list)}, ignore_index=True)
final_list = pb_list[:g.stock_num]
position = 1
return final_list, position
def adjust_position_sell(context, buy_stocks):
#order_value(g.bond,context.portfolio.available_cash)
for stock in context.portfolio.positions:
if stock not in buy_stocks:
order_target(stock, 0)
def adjust_position_buy(context, buy_stocks):
position_count = len(context.portfolio.positions)
if g.stock_num > position_count:
value = context.portfolio.cash * g.position / (g.stock_num - position_count)
for stock in buy_stocks:
if stock not in context.portfolio.positions:
order_target_value(stock, value)
if len(context.portfolio.positions) == g.stock_num:
break
'''
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
资金划转代码
'''
#对冲比例调整+账户间资金划转
def rebalance(context):
# 计算资产总价值
total_value = context.portfolio.total_value
# 计算预期的股票账户价值
expected_stock_value = total_value * g.stock_share
# 将两个账户的钱调到预期的水平
transfer_cash(1, 0, min(context.subportfolios[1].transferable_cash, max(0, expected_stock_value-context.subportfolios[0].total_value)))
transfer_cash(0, 1, min(context.subportfolios[0].transferable_cash, max(0, context.subportfolios[0].total_value-expected_stock_value)))
# 计算股票账户价值(预期价值和实际价值其中更小的那个)
stock_value = min(context.subportfolios[0].total_value, expected_stock_value)
'''
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CTA部分代码
'''
## 开盘前运行函数
def before_market_open_future(context):
# ========== 关键修改1:增加空列表检查 ==========
# 获取当月合约列表
future_contracts = get_future_contracts(g.future_type)
if not future_contracts: # 检查列表是否为空
log.warn(f"未获取到{g.future_type}品种的期货合约,跳过本次期货操作")
g.code_1 = None # 标记合约为空
# 重置计数,避免后续异常
g.day_count = int(g.day)
return
# 获取当月合约
g.code_1 = future_contracts[0]
# 交割日
de_day = get_CCFX_end_date(g.code_1)
#判断是否交割日,确定下一月交易的手数
if context.current_dt.date() == de_day:
g.de_day = 1
#资产全价值的15%(70%指增,30%的CTA,其中30%的50%为保证金专用)
value = int(context.subportfolios[1].total_value) * g.future_position
#用于交易头寸的保证金占用
margin = int(get_bars(g.benchmark, 1, '1d', ['close'], end_dt=context.previous_date,include_now=True)['close'][0]) * g.unitprice * g.futures_margin_rate
#计算最大持仓手数(保证金15%)最高不超过100手
g.k = min(int(value / margin),100)
log.info('手数',g.k)
else:
g.de_day = 0
# 计数,每g.day天拟合一次
if g.day_count == g.day:
g.day_count = 0
else:
g.day_count += 1
#开盘时运行交易函数(波动率小开仓开1.2倍,波动率大开仓开0.8倍),外加止损模块
def market_trade_future(context):
# ========== 关键修改2:检查合约是否有效 ==========
if g.code_1 is None:
log.warn("期货合约未初始化,跳过本次交易")
return
g.sign = update_niu_signal(context,g.benchmark)
g.loss_stop = loss_stop(context,g.benchmark)
g.volatility = volatility(context, g.benchmark)
#如果波动率小,可以按1.5倍加仓;波动率大就正常
if g.volatility == -1:
future_position = int(1.5 *g.k)
elif g.volatility == 1:
future_position = int(1 *g.k)
else:
future_position = g.k # 新增:默认值,避免未定义
#开仓信号
if (len(context.subportfolios[1].long_positions) == 0) & (len(context.subportfolios[1].short_positions) == 0):
if g.sign > 0:
order(g.code_1, future_position, side='long', pindex=1)
elif g.sign < 0:
order(g.code_1, future_position, side='short', pindex=1)
#平仓信号
elif (len(context.subportfolios[1].long_positions) + len(context.subportfolios[1].short_positions)) > 0:
if g.de_day == 0:
#止损:如果从最大收益处回撤g.stop倍ATR,则止损清仓
if (len(context.subportfolios[1].long_positions) > 0) & (g.loss_stop == 1):
order_target(g.code_1, 0, side='long',pindex=1)
elif (len(context.subportfolios[1].short_positions) > 0) & (g.loss_stop == 1):
order_target(g.code_1, 0, side='short',pindex=1)
#平开仓:价格上穿或下穿ema线,调整仓位
elif (len(context.subportfolios[1].long_positions) > 0) & (g.sign == 0):
order_target(g.code_1, 0, side='long', pindex=1)
order(g.code_1, future_position, side='short', pindex=1)
elif (len(context.subportfolios[1].short_positions) > 0) & (g.sign > 0):
order_target(g.code_1, 0, side='short', pindex=1)
order(g.code_1, future_position, side='long', pindex=1)
# 交割日平仓
else:
if len(context.subportfolios[1].long_positions) > 0:
order_target(g.code_1, 0, side='long', pindex=1)
else:
order_target(g.code_1, 0, side='short', pindex=1)
#ATR波动率信号
def volatility(context,ind):
ind=g.benchmark
#ATR(security_list, check_date, timeperiod=14)
current_ATR_a = ATR(ind,context.current_dt, g.shortdays)
current_ATR_b = ATR(ind,context.current_dt, g.longdays)
k = current_ATR_a[-1][ind] - g.para * current_ATR_b[-1][ind]
if k < 0:
volatility = -1 #波动率低,可重仓
elif k > 0:
volatility = 1 #波动率高,可轻仓
else:
volatility = 0 # 新增:默认值,避免返回None
return volatility
#ATR止损信号
def loss_stop(context,ind):
include_now = True#表示读取当天的日K线
unit='1d'
ind=g.benchmark
#ATR(security_list, check_date, timeperiod=14)
current_ATR = ATR(ind,context.current_dt, g.ATRdays)
close = get_bars(ind, 1, '1d', ['close'], end_dt=context.current_dt,include_now=include_now)['close']
high = max(get_bars(ind, g.boundrydays, unit, ['high'], end_dt=context.previous_date,include_now=include_now)['high'])#5日最高
low = min(get_bars(ind, g.boundrydays, unit, ['low'], end_dt=context.previous_date,include_now=include_now)['low'])#5日最低
if (len(context.portfolio.long_positions) > 0) & (close < high - g.stop * current_ATR[-1][ind]):
loss_stop = 1 #多头止损
elif (len(context.portfolio.short_positions) > 0) & (close > low + g.stop * current_ATR[-1][ind]):
loss_stop = 1 #空头止损
else:
loss_stop = 0
return loss_stop
#开平仓信号
def update_niu_signal(context,ind):
include_now = True#表示读取当天的日K线
unit='1d'
#-------------------标的指数的5日均线,如果均线朝下表示趋势向下,暂停交易---------------
ind=g.benchmark
close = get_bars(ind, 1, '1d', ['close'], end_dt=context.current_dt,include_now=include_now)['close']
#当天获取5日均线
current = EMA(ind,context.current_dt, timeperiod=g.long_days, unit = unit, include_now =include_now, fq_ref_date = None)[ind]
#前一天的5日均线
previous = EMA(ind,context.previous_date, timeperiod=g.long_days, unit=unit, fq_ref_date = None)[ind]
#当天获取2日均线
current_close = EMA(ind,context.current_dt, timeperiod=g.short_days, unit = unit, include_now =include_now, fq_ref_date = None)[ind]
#当天获取2日均线
previous_close = EMA(ind,context.previous_date, timeperiod=g.short_days, unit=unit, fq_ref_date = None)[ind]
if close<current:#<previous:#当价格低于5日均线且5日均线空头排列的时候开空
niu_signal = -1 #开仓数量=0
elif close>current_close:#>previous_close:#当价格高于5日均线且5日均线多头排列的时候开多
niu_signal = 1 #开仓数量=1
else:
niu_signal = 0
return niu_signal
# 获取金融期货合约到期日
def get_CCFX_end_date(future_code):
# 获取金融期货合约到期日
return get_security_info(future_code).end_date