量化交易 米筐 因子数据处理 -- 市值中性化

因子数据处理 – 市值中性化

1. 中性化意义

防止选到的股票集中在固定的某些股票当中

  • 市值影响,默认大部分因子都包含了市值的影响,去除其它因子存在的市值影响。

  • 去除市值影响 市值 <==> 某因子 去除相关性

  • 回归法去除

    建立回归方程: x(特征:市值) * w + b = y(某因子)

    回归方程预测: 市值(X) * 系数(w) + 偏置(b) = 预测值( y_predict)

    计算偏差: 某因子(y) - 预测值(y_predict) = 偏差

    偏差:不受影响的部分

2. 回归法API

from sklearn.linear_model import LinearRegression
  • 把市值设置成特征,市值不进行任何处理
  • 将其它因子设置成目标值

3. 案例:去除市净率与市值之间的联系部分

3.1 分析
  • 获取两个因子数据
  • 对目标值因子-市净率进行去极值,标准化处理
  • 建立市值与市净率回归方程
  • 通过回归系数,预测新的因子结果y_predict
  • 求出市净率与y_predict的偏差 即心点因子值
3.2 代码
# 获取两个因子数据
# 获取所有股票代码
stocks = all_instruments('CS').order_book_id

# 1、获取因子 市净率与市值
fund = get_factor(stocks, factor=['pb_ratio', 'market_cap'], start_date='20180103', end_date='20180103')
# 删除日期index
fund = fund.reset_index(1, drop=True)
# 删除 nan数据
fund = fund.dropna()

# 2、对因子数据进行处理 3倍中位数,stand
fund['pb_ratio'] = mad(fund['pb_ratio'])
fund['pb_ratio'] = stand(fund['pb_ratio'])

# 3、确定建立回归方程特征值和目标值
# 传入训练的特征值是二维的形状
x = np.array(fund['market_cap']).reshape(-1, 1) 
y = fund['pb_ratio']

from sklearn.linear_model import LinearRegression
# 4、利用线性回归进行预测
lr = LinearRegression()
lr.fit(x, y)

# 5、得出每个预测值,让因子的真实值-预测值得出的误差,
# 得到中性化处理后的结果
y_predict = lr.predict(x)
res = y - y_predict
fund['pb_ratio'] = res
3.3 总结
  • 原因:防止回测时选股集中
  • 原理:建立回归关系
3.4 市值中心化选股对比
  • 市值中性化处理:定期分散到不同股票里面
  • 没有市值中性化处理:比较集中在某些股票

4.案例:去除中性化的因子回测

4.1 对pb_ratio进行中性化
  • 1、获取市值和市净率因子数据
    • 因子:极值,标准化,中性化处理
  • 2、选股股票池
    • 市净率小的某些股票
4.2 代码
# 在这个方法中编写任何的初始化逻辑。context对象将会在你的算法策略的任何方法之间做传递。
#  1、获取市值和市净率因子数据
#   + 因子:极值,标准化,中性化处理
#  2、选股股票池
#   + 市净率小的某些股票
from sklearn.linear_model import LinearRegression
import numpy as np
import pandas as pd

def init(context):
    # 获取所有股票代码
    # context.stocks = all_instruments('CS').order_book_id
    stocks = index_components('000300.XSHG') # 沪深300
    context.stocks = stocks
    # 每月定时
    scheduler.run_monthly(get_data, tradingday=1)

def get_data(context, bar_dict):
    # 查询两个因子的数据

    # 获取因子 市净率与市值
    fund = get_factor(context.stocks, ['pb_ratio', 'market_cap'])

    # 删除日期index
    fund = fund.reset_index(1, drop=True)
    
    # 删除 nan数据
    fund = fund.dropna()
    
    context.fund = fund
    # 因子数据的处理、去极值、标准化、市值中性化
    treat_data(context)

    # 利用市净率小的表现好
    quantile = context.fund['pb_ratio'].quantile(0.05) # 分位数(0.05)
    fund_select = context.fund[context.fund['pb_ratio'] <= quantile] # 筛选市净率小的股票

    # 市值排序 升序
    # fund_select = fund_select.sort_values(by='market_cap', ascending=True).head(10)
	
	# 获取股票代码列表
    context.stock_list = fund_select.index.values

    # 去除合约状态异常的股票
    filter_active(context)
    update_universe(context.stock_list)
    
    print(context.stock_list)
    # print(fund)


def treat_data(context):
    """
    因子数据的处理、去极值、标准化、市值中性化
    """
    context.fund['pb_ratio'] = mad(context.fund['pb_ratio'])
    context.fund['pb_ratio'] = stand(context.fund['pb_ratio'])

    # 市值中性化
    x = np.array(context.fund['market_cap']).reshape(-1, 1) 
    y = context.fund['pb_ratio']

    lr = LinearRegression()
    lr.fit(x, y)

    y_predict = lr.predict(x)
    res = y - y_predict
    context.fund['pb_ratio'] = res


# before_trading此函数会在每天策略交易开始前被调用,当天只会被调用一次
def before_trading(context):
    pass


# 你选择的证券的数据更新将会触发此段逻辑,例如日或分钟历史数据切片或者是实时数据切片更新
def handle_bar(context, bar_dict):
    # 获取仓位
    position_keys = context.portfolio.positions.keys()
    if len(position_keys) != 0:
        for stock in position_keys:
            # 如果旧的股票池,不在新的股票池中
            if stock not in context.stock_list:
                order_target_percent(stock, 0)

    # 买入最新的更新的股票
    # 等比率资金买入 投资组合总价值的百分比平分 20份
    weight = 1.0 / len(context.stock_list)
    for stock in context.stock_list:
        order_target_percent(stock, weight)
    pass

# after_trading函数会在每天交易结束后被调用,当天只会被调用一次
def after_trading(context):
    pass

def filter_active(context):
    # 去除合约状态异常的股票
    stock_list = []
    for stock in context.stock_list:
        is_st = is_st_stock(stock, count=1) 
        day_from = instruments(stock).days_from_listed()
        day_expire = instruments(stock).days_to_expire()
        status = instruments(stock).status
        # print(stock, is_st, day_from, day_expire, status)
        
        if status != 'Active':
            continue
        stock_list.append(stock)

    context.stock_list = np.array(stock_list)

def mad(factor, n=3):
    """
    3倍中位数偏差法
    """
    # 1、找中位数
    med = np.median(factor)
    
    # 2.求绝对偏差
    # 3、计算绝对偏差的中位数 MAD 
    mad = np.median(abs(factor - med))
    
    # 求出上下限
    up = med + (1.4826 * mad) * n # 计算 MAD_e = 1.4826 * MAD 然后确定参数n 做出调整
    down = med - (1.4826 * mad) * n
    
    # 去极值
    factor = np.where(factor > up, up, factor)
    factor = np.where(factor < down, down, factor)
    
    return factor

def stand(factor):
    """
    自己实现标准化
    """
    mean = factor.mean()
    std = factor.std()
    
    return (factor - mean) / std

未添加中心化策略
量化交易 米筐 因子数据处理 -- 市值中性化

添加中性化处理
量化交易 米筐 因子数据处理 -- 市值中性化

4.3 市净率因子去选股
# 多因子选择 
# 四个因子的市值影响去除掉
pcf_ratio pe_ratio revenue operating_revenue 
市值影响(选股集中) [01, 02, 06, 10, 09] ==> [01, 02, 06, 10, 09]
去除影响(选股分散) [01, 02, 06, 10, 09] ==> [03, 05, 06, 11, 09] 
上一篇:人工智能-Project 4: Inference in Bayes Nets(2)


下一篇:机器学习进阶-项目实战-信用卡数字识别 1.cv2.findContour(找出轮廓) 2.cv2.boudingRect(轮廓外接矩阵位置) 3.cv2.threshold(图片二值化操作) 4.c