实战之回归法选股 part 2
- 概述
- 代码
- 效果
概述
继上一片的理论, 我们这次来进行一下回测, 看一下结果如何.
代码
# 可以自己import我们平台支持的第三方python模块,比如pandas、numpy等。
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
# 在这个方法中编写任何的初始化逻辑。context对象将会在你的算法策略的任何方法之间做传递。
def init(context):
# 初始化股票因子权重
context.weights = np.array([0.04549957, 0.01249463, -0.02397849, 0.06077185, -0.00195205, -0.00892116, -0.04641399, -0.05644752, -0.08393869])
# 定义股票池数量
context.stocknum = 20
# 定时每月运行函数
scheduler.run_monthly(regression_select, tradingday=1)
def regression_select(context, bar_dict):
"""回归预测选股逻辑"""
# 获取沪深300
context.hs300 = index_components("000300.XSHG")
# 1. 查询因子
q = query(
fundamentals.eod_derivative_indicator.pe_ratio,
fundamentals.eod_derivative_indicator.pb_ratio,
fundamentals.eod_derivative_indicator.market_cap,
fundamentals.financial_indicator.ev,
fundamentals.financial_indicator.return_on_asset_net_profit,
fundamentals.financial_indicator.du_return_on_equity,
fundamentals.financial_indicator.earnings_per_share,
fundamentals.income_statement.revenue,
fundamentals.income_statement.total_expense
).filter(
fundamentals.stockcode.in_(context.hs300)
)
fund = get_fundamentals(q)
context.factors_data = fund.T
# 2. 数据预处理
process_data(context)
# 3. 根据每月预测下月的收益率
select_stocklist(context)
# 4. 调仓
reblance(context)
def process_data(context):
"""删除空值, 去极值, 标准化, 因子的市值中性化"""
# 删除空值
context.factors_data = context.factors_data.dropna()
# 市值因子不进行去极值, 标准化处理
market_cap_factor = context.factors_data["market_cap"]
# 去极值化, U型你换对每个因子进行处理
for name in context.factors_data.columns:
context.factors_data[name] = mad(context.factors_data[name])
context.factors_data[name] = stand(context.factors_data[name])
# 对因子(除了市值因子) 中性化处理
if name == "market_cap":
continue
# 建立回归方程, 市值中性化
lr = LinearRegression()
x = market_cap_factor.values
y = context.factors_data[name]
# x: 要求二维, y: 要求一维
lr.fit(x.reshape(-1, 1), y)
y_predict = lr.predict(x.reshape(-1, 1))
# 得出误差进行替换原有因子值
context.factors_data[name] = y - y_predict
def select_stocklist(context):
"""回归计算预测得出收益率结果, 筛选收益率高的股票"""
# 特征值是: context.factors_Date (300, 9) 系数: 因子权重
# 进行矩阵运算 (300, 9) * (9, 1) = (300, 1)
stock_return = np.dot(context.factors_data.values, context.weights)
logger.info(stock_return)
# 赋值给因子数据, 注意都是默认对应的股票代码和收益率
context.factors_data["stock_return"] = stock_return
# 进行收益率排序
ordered = context.factors_data.sort_values(by="stock_return", ascending=False)
# 加入股票池
context.stock_list = ordered.index[:context.stocknum]
def reblance(context):
# ----------------卖出----------------
# 遍历股票池
for stock in context.portfolio.positions.keys():
# 判断是否还在股票池
if stock not in context.stock_list:
# 如果不在, 卖出
order_target_percent(stock, 0)
# ----------------买入-----------------
# 买入的百分比
weight = 1.0 / len(context.stock_list)
# 遍历股票池
for stock in context.stock_list:
# 等比例买入
order_target_percent(stock, weight)
# before_trading此函数会在每天策略交易开始前被调用,当天只会被调用一次
def before_trading(context):
pass
# 你选择的证券的数据更新将会触发此段逻辑,例如日或分钟历史数据切片或者是实时数据切片更新
def handle_bar(context, bar_dict):
# 开始编写你的主要的算法逻辑
pass
# after_trading函数会在每天交易结束后被调用,当天只会被调用一次
def after_trading(context):
pass
def mad(factor):
"""3倍中位数去极值"""
# 求出因子值的中位数
median = np.median(factor)
# 求出因子值与中位数的差值, 进行绝对值
mad = np.median(abs(factor - median))
# 定义几倍的中位数上下限
high = median + (3 * 1.4826 * mad)
low = median - (3 * 1.4826 * mad)
# 替换上下限
factor = np.where(factor > high, high, factor)
factor = np.where(factor < low, low, factor)
return factor
def stand(factor):
"""数据标准化"""
mean = factor.mean()
std = factor.std()
return (factor - mean) / std
效果
注: 在实际的应用中大概率不会跑赢大盘 46%, 因为分析的区间较短.