策略名称:多因子支持向量回归选股策略
策略概述:
该策略通过结合财务指标、动量因子和技术指标,使用支持向量回归(SVR)模型对股票进行评分,并根据评分结果动态调整持仓。策略的核心逻辑是通过多因子模型筛选出具有良好基本面和成长性的股票,并结合择时策略(如FISHER择时)进行买卖决策,以实现长期稳定的超额收益。
策略核心逻辑:
-
财务指标筛选:
-
使用市盈率(PE)、市净率(PB)、股息率、营业收入同比增长率、净利润同比增长率等财务指标,筛选出基本面良好的股票。
-
剔除ST股票、停牌股票、涨停股票、跌停股票等不符合条件的股票。
-
-
多因子模型:
-
结合动量因子、技术指标(如FISHER择时)和市值因子,计算每只股票的综合评分。
-
使用支持向量回归(SVR)模型对股票进行评分,并根据评分结果对股票进行排序。
-
-
动态调仓:
-
每月初根据最新的财务数据和市场表现,重新筛选股票并调整持仓。
-
根据评分结果,卖出低评分股票,买入高评分股票。
-
-
择时策略:
-
使用FISHER择时策略判断市场趋势,选择买入时机。
-
结合FISHER择时信号,动态调整持仓。
-
策略特点:
-
多因子结合:通过财务指标、动量因子和技术指标的结合,筛选出具有良好基本面和成长性的股票。
-
支持向量回归:使用SVR模型对股票进行评分,提高选股的准确性。
-
择时优化:结合FISHER择时策略,动态判断市场趋势,避免逆势操作。
-
自动化交易:策略实现全自动化交易,减少人为干预。
策略参数:
| 参数名称 | 参数值 | 说明 |
|---|---|---|
| 调仓间隔 | 10 | 每10个交易日调仓一次 |
| 最大持仓数 | 3 | 每次调仓时持有的股票数量 |
| 市值筛选上限 | 1000 | 筛选市值小于1000亿的股票 |
| 市盈率筛选上限 | 100 | 筛选市盈率小于100的股票 |
| FISHER择时周期 | 10 | 计算FISHER择时的周期 |
策略流程:
-
初始化:
-
设置基准指数(沪深300)。
-
定义财务指标筛选条件。
-
初始化多因子评分模型的参数。
-
-
每月调仓:
-
使用财务指标筛选出符合条件的股票。
-
结合动量因子和技术指标,计算每只股票的综合评分。
-
使用支持向量回归(SVR)模型对股票进行评分和排序。
-
根据评分结果,卖出低评分股票,买入高评分股票。
-
-
多因子评分模型:
-
计算每只股票的动量因子、技术指标和市值因子。
-
使用SVR模型对股票进行评分,并根据评分对股票进行排序。
-
-
择时策略:
-
使用FISHER择时策略判断市场趋势,选择买入时机。
-
结合FISHER择时信号,动态调整持仓。
-
-
动态调仓:
-
卖出当前持仓中评分较低的股票。
-
买入评分最高的股票,确保持仓数量不超过最大持仓数。
-
策略风险:
-
市场风险:
-
如果市场整体下跌,多因子模型可能失效,导致策略表现不佳。
-
-
财务数据滞后:
-
财务数据的更新可能存在滞后,导致策略的选股结果不准确。
-
-
流动性风险:
-
部分股票的流动性不足可能导致买卖价差较大,影响策略收益。
-
-
参数敏感性:
-
策略对评分权重、市值筛选百分比等参数较为敏感,参数设置不当可能导致策略失效。
-
策略优化方向:
-
多因子优化:
-
引入更多因子(如波动率、动量、质量等)优化选股模型。
-
-
动态参数调整:
-
根据市场环境动态调整评分权重和市值筛选百分比。
-
-
风险控制增强:
-
加入止损机制,进一步控制回撤。
-
-
行业轮动:
-
结合行业轮动策略,优化资产配置。
-
策略适用场景:
-
市场环境:适用于市场风格偏向价值投资、成长性股票具备反弹潜力的环境。
-
投资者类型:适合风险承受能力中等、追求长期稳定收益的投资者。
策略回测表现:
-
年化收益率:根据历史回测数据,策略年化收益率较高(具体数值需通过回测验证)。
-
最大回撤:通过多因子评分模型和动态调仓,策略能够有效控制回撤。
总结:
“多因子支持向量回归选股策略”通过结合财务指标、动量因子和技术指标,实现了对股票的动态筛选和优化配置。策略具备较强的自动化能力和风险控制机制,适合在价值投资和成长性股票表现良好的市场环境中运行。通过进一步优化和扩展,策略有望在更多市场环境中表现出色。
策略实现代码
import pandas as pd
import numpy as np
import math
from sklearn.svm import SVR # 支持向量机回归模型
# 性能评估指标
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error, explained_variance_score
import statsmodels.api as sm # 统计模型库
from statsmodels import regression
import jqdata # 聚宽数据接口
import datetime as dt # 日期时间库
from jqlib.alpha191 import * # 导入聚宽因子库
# 定义 initialize 函数,该函数在策略启动前运行一次,用于初始化策略设置
def initialize(context):
# 调用 set_params 函数设置策略参数
set_params()
# 调用 set_backtest 函数设置回测参数
set_backtest()
# 定义全局变量 g.factors 存储将要使用的因子列表
g.factors=['alpha_006', 'alpha_015', 'alpha_026']
# 设置策略运行的参数
def set_params():
g.days = 0 # 日期计数器开始值
g.refresh_rate = 10 # 设置调仓间隔周期为 10 个交易日
g.stocknum = 3 # 同时持有个股数量
# 设置回测环境的参数
def set_backtest():
# 当前价格的百分比设置滑点
# set_slippage(PriceRelatedSlippage(0.002))
# 设置佣金
set_order_cost(OrderCost(open_tax=0, close_tax=0.001, open_commission=0.0003, \
close_commission=0.0003, close_today_commission=0, min_commission=5), type='stock')
# 设置对比基准
set_benchmark('000300.XSHG')
# 使用真实价格
set_option('use_real_price', True)
# 使用 log.set_level 函数设置日志级别,这里将订单和系统日志级别设置为 'error',只记录错误信息
log.set_level('order', 'error')
log.set_level('system', 'error')
# 过滤股票,过滤停牌退市ST股票,选股时使用
def filter_stock_ST(stock_list):
curr_data = get_current_data() # 获取当前所有股票的数据
for stock in stock_list:
if (curr_data[stock].paused) or (curr_data[stock].is_st) or ('ST' in curr_data[stock].name)\
or ('*' in curr_data[stock].name)\
or ('退' in curr_data[stock].name):
stock_list.remove(stock)
return stock_list
# 交易时使用,过滤每日开盘时的涨跌停股filter low_limit/high_limit
def filter_stock_limit(stock_list):
curr_data = get_current_data() # 获取当前所有股票的数据
for stock in stock_list:
price = curr_data[stock].day_open
if (curr_data[stock].high_limit <= price) or (price <= curr_data[stock].low_limit):
stock_list.remove(stock)
return stock_list
# 可选项: 过滤上市180日以内次新股
def remove_new_stocks(security_list,context):
for stock in security_list:
days_public = (context.current_dt.date() - get_security_info(stock).start_date).days
if days_public < 180:
security_list.remove(stock)
return security_list
# 取得股票某个区间内的所有收盘价(用于取前interval日和当前日收盘价)
def getStockPrice(stock, interval): # 输入stock证券名,interval期
h = attribute_history(stock, interval, unit='1d', fields=('close'), skip_paused=True)
return (h['close'].values[0] , h['close'].values[-1])
# 0是第一个(interval周期的值,-1是最近的一个值(昨天收盘价))
关键函数解锁后查看:
# ================= 以下是正式回测===============
# 定义 handle_data 函数,用于在每个调仓日处理数据和执行交易
def handle_data(context, data):
# 如果是调仓日,则进行数据提取和计算
if g.days % g.refresh_rate == 0:
# 从股票池提取样本股
sample = get_index_stocks('000985.XSHG', date = None)
# 过滤停牌退市ST股票,次新股
sample = filter_stock_ST(sample)
sample = remove_new_stocks(sample,context)
sample = filter_stock_limit(sample)
# 获取基本面数据并作初步计算
q = query(valuation.code,
valuation.market_cap, # 总市值
indicator.roe / valuation.pb_ratio, # PB-ROE
valuation.pe_ratio / indicator.inc_net_profit_year_on_year, # PEG
).filter(
valuation.pe_ratio < 100,
valuation.market_cap < 1000,
valuation.code.in_(sample)
).order_by(
# 按市值降序排列
# valuation.market_cap.asc()
(valuation.pe_ratio / indicator.inc_net_profit_year_on_year).asc()
).limit(
# 最多返回500个
500)
df = get_fundamentals(q, date = None)
select_list =list(df['code'])
# select_list = sample
# 获取价量因子和技术指标
# alpha_datas = {}
for factor in g.factors:
df[factor] = np.array(eval(factor)(select_list, end_date=None))
# a004 = alpha_004(select_list, end_date = None)
# a004 = np.array(a004)
# df['a004'] = a004
# a052 = alpha_052(select_list, end_date = None)
# a052 = np.array(a052)
# df['a052'] = a052
# a126 = alpha_126(select_list, end_date = None)
# a126 = np.array(a126)
# df['a126'] = a126
# 求个股调仓周期回报率
momentum = []
for i in select_list:
interval,Yesterday = getStockPrice(i, g.refresh_rate)
stock_momentum = Yesterday / interval - 1
momentum.append((i, stock_momentum))
# 转化成np.array结构,方便取出第二列数据(回报率)
np_momentuma = np.array(momentum)
momentum_value = np_momentuma[:,1].astype(float).tolist()
df['momentum_value'] = momentum_value
# ========== 所有整合完毕的因子数据dataframe表格=========
# 所有整合完毕的因子数据dataframe表格
df.columns = ['code','market_cap','PB_ROE','PE_G',\
g.factors[0],g.factors[1],g.factors[2],'momentum_value']
# print df
df = df_neutralization(df,'market_cap')
# df.index是这个dataframe的index索引
df.index = df.code.values
del df['code']
df = df.fillna(0)
# ==================== 支持向量回归 ====================
# 使用支持向量回归
X = df[g.factors]
# 应变量Y列表
Y = df[['market_cap']]
X = X.fillna(0)
Y = Y.fillna(0)
svr = SVR(kernel='poly', gamma=0.01)
model = svr.fit(X, Y)
# factor是真实值 - 回归值 = 残差
predict = svr.predict(X)
# 记录回归模型的性能指标
record(R2=r2_score(Y, predict))
record(MAE=mean_absolute_error(Y, predict))
record(MSE=mean_squared_error(Y, predict))
record(EVS=explained_variance_score(Y, predict))
# 计算残差
factor = Y - pd.DataFrame(predict, index = Y.index, columns = ['market_cap'])
# 按回报率升序排列,残差最低的被排名靠前,优先买入
factor = factor.sort_index(by = 'market_cap', ascending = True)
# order_list = list(factor.index)
# 过滤300开头的创业板股票
# order_list = [s for s in order_list if s[0:3] != '300'][:g.stocknum]
# 通过list函数将factor的index列转化成list数据类型,然后买入前g.stocknum个
# order_list = list(factor.index[:g.stocknum])
order_list = fisher_transform_strategy(context, list(factor.index))[:g.stocknum]
# 最终名单,分别下单给两个交易函数
order_stock_sell(context,order_list)
order_stock_buy(context,order_list)
g.days += 1
else:
g.days += 1 # 如果不是调仓日,仅更新调仓日计数器
# 执行卖出
def order_stock_sell(context,order_list):
# 对于不需要持仓的股票,全仓卖出
for stock in context.portfolio.positions:
# 除去buy_list内的股票,其他都卖出
if stock not in order_list:
order_target_value(stock, 0)
# 执行买入
def order_stock_buy(context,order_list):
# 先求出可用资金,如果持仓个数小于g.stocknum
if len(context.portfolio.positions) < g.stocknum:
# 求出要买的数量num
num = g.stocknum - len(context.portfolio.positions)
# 求出每只股票要买的金额cash
g.each_stock_cash = context.portfolio.cash/num
else:
# 如果持仓个数满足要求,不再计算g.each_stock_cash
cash = 0
num = 0
# 执行买入
for stock in order_list:
if stock not in context.portfolio.positions:
order_target_value(stock, g.each_stock_cash)
2025-02-21
