向量回归策略
本策略基于Python编写,利用聚宽平台进行股票交易。策略主要通过支持向量回归(SVR)模型来预测股票市值,并根据预测结果进行买卖操作。
策略核心思想
1. 数据收集与预处理:策略首先收集市场指数成分股的基本面数据,包括市值、总资产、总负债、净利润等,并对这些数据进行对数化处理和归一化处理。
2. 特征工程:除了基本财务指标外,策略还考虑了行业因素,将股票按照行业进行分类,并生成相应的行业特征。
3. 模型训练:使用支持向量回归(SVR)模型对处理后的数据进行训练,以预测股票的市值变化。
4. 交易执行:根据模型的预测结果,策略决定买入或卖出股票,以优化投资组合的表现。
交易逻辑与原理
1. 初始化与参数设置:
- 在`initialize`函数中,策略设置了初始参数,如刷新频率、持仓数量等,并定义了每日执行的交易函数`trade`。
- `set_params`函数用于设置全局变量,如天数计数器和持仓数量。
- `set_backtest`函数用于设置回测基准、使用真实价格以及日志级别。
2. 数据获取与处理:
- 在`trade`函数中,策略首先检查是否达到了数据更新的时间点(每10天一次)。如果是,则获取市场指数的成分股列表,并查询这些股票的基本面数据。
- 数据经过一系列预处理步骤,包括对数化、归一化和缺失值填充,以确保数据的质量和一致性。
3. 特征构建:
- 策略构建了一个包含财务指标和行业特征的复合特征集。这些特征包括市值的对数、净资本的对数、资产负债率、净利润等。
- 行业特征通过将股票按照行业分类并生成相应的二进制向量来表示。
4. 模型训练与预测:
- 使用支持向量回归(SVR)模型对特征集进行训练。SVR是一种适用于回归问题的监督学习算法,能够处理非线性关系并具有良好的泛化能力。
- 训练完成后,模型用于预测股票的市值变化。
5. 交易决策与执行:
- 根据模型的预测结果,策略计算每只股票的预期市值变化,并生成一个排序后的股票列表。
- 策略首先卖出不在预期表现前10名的股票,以确保投资组合的更新和优化。
- 如果当前持仓数量低于目标数量,则买入剩余数量的股票,以保持投资组合的规模。
6. 循环执行:
- 策略在每个交易日结束时更新数据并重新训练模型,以确保交易决策始终基于最新的市场信息。
策略特点
1. 数据驱动:策略依赖于基本面数据和行业特征来做出交易决策,避免了单纯依赖价格波动的交易信号。
2. 机器学习应用:通过引入支持向量回归模型,策略能够捕捉数据中的复杂模式和非线性关系,提高预测准确性。
3. 动态调整:策略定期更新数据和模型,以适应市场环境的变化,保持交易策略的有效性。
4. 风险管理:通过设定止损点和止盈点,以及动态调整持仓数量,策略能够在追求收益的同时控制风险。
注意事项
1. 数据质量:策略的性能高度依赖于输入数据的质量。因此,确保数据的准确性和完整性至关重要。
2. 模型过拟合:由于使用了复杂的机器学习模型,需要注意防止过拟合现象的发生。可以通过交叉验证、正则化等方法来降低过拟合风险。
3. 交易成本:频繁的交易操作可能导致较高的交易成本。在实际应用中,需要考虑这些成本对策略收益的影响。
4. 市场适应性:策略在不同的市场环境下可能表现出不同的效果。因此,在实际应用前需要进行充分的回测和验证。
该策略结合了基本面分析与机器学习技术,旨在通过预测股票市值变化来实现投资收益的最大化。
然而,任何投资策略都存在一定的风险,投资者在使用前应充分了解其原理和潜在风险,并根据自身情况进行适当调整。
策略思维导图:
策略源码如下(pyrhon版):
import pandas as pd
import numpy as np
import math
from sklearn.svm import SVR
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import learning_curve
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
import jqdata
def initialize(context):
set_params()
set_backtest()
run_daily(trade, 'every_bar')
def set_params():
g.days = 0
g.refresh_rate = 10
g.stocknum = 10
def set_backtest():
set_benchmark('000001.XSHG')
set_option('use_real_price', True)
log.set_level('order', 'error')
def trade(context):
if g.days % 10 == 0:
sample = get_index_stocks('000001.XSHG', date = None)
q = query(valuation.code, valuation.market_cap, balance.total_assets - balance.total_liability,
balance.total_assets / balance.total_liability, income.net_profit, income.net_profit + 1,
indicator.inc_revenue_year_on_year, balance.development_expenditure).filter(valuation.code.in_(sample))
df = get_fundamentals(q, date = None)
df.columns = ['code', 'log_mcap', 'log_NC', 'LEV', 'NI_p', 'NI_n', 'g', 'log_RD']
df['log_mcap'] = np.log(df['log_mcap'])
df['log_NC'] = np.log(df['log_NC'])
df['NI_p'] = np.log(np.abs(df['NI_p']))
df['NI_n'] = np.log(np.abs(df['NI_n'][df['NI_n']<0]))
df['log_RD'] = np.log(df['log_RD'])
df.index = df.code.values
del df['code']
df = df.fillna(0)
df[df>10000] = 10000
df[df<-10000] = -10000
industry_set = ['801010', '801020', '801030', '801040', '801050', '801080', '801110', '801120', '801130',
'801140', '801150', '801160', '801170', '801180', '801200', '801210', '801230', '801710',
'801720', '801730', '801740', '801750', '801760', '801770', '801780', '801790', '801880','801890']
for i in range(len(industry_set)):
industry = get_industry_stocks(industry_set[i], date = None)
s = pd.Series([0]*len(df), index=df.index)
s[set(industry) & set(df.index)]=1
df[industry_set[i]] = s
X = df[['log_NC', 'LEV', 'NI_p', 'NI_n', 'g', 'log_RD','801010', '801020', '801030', '801040', '801050',
'801080', '801110', '801120', '801130', '801140', '801150', '801160', '801170', '801180', '801200',
'801210', '801230', '801710', '801720', '801730', '801740', '801750', '801760', '801770', '801780',
'801790', '801880', '801890']]
Y = df[['log_mcap']]
X = X.fillna(0)
Y = Y.fillna(0)
svr = SVR(kernel='rbf', gamma=0.1)
model = svr.fit(X, Y)
factor = Y - pd.DataFrame(svr.predict(X), index = Y.index, columns = ['log_mcap'])
factor = factor.sort_index(by = 'log_mcap')
stockset = list(factor.index[:10])
sell_list = list(context.portfolio.positions.keys())
for stock in sell_list:
if stock not in stockset[:g.stocknum]:
stock_sell = stock
order_target_value(stock_sell, 0)
if len(context.portfolio.positions) < g.stocknum:
num = g.stocknum - len(context.portfolio.positions)
cash = context.portfolio.cash/num
else:
cash = 0
num = 0
for stock in stockset[:g.stocknum]:
if stock in sell_list:
pass
else:
stock_buy = stock
order_target_value(stock_buy, cash)
num = num - 1
if num == 0:
break
g.days += 1
else:
g.days = g.days + 1