《因子投资》读书笔记:单因子模型之规模因子

单因子专题——规模

1. 来源

规模因子的历史要追溯到1981年。Banz(1981)基于纽交所数据发现,将股票按照市值分成5组后,市值最小的一组股票月收益率比其他股票高0.4%,差异非常显著。并且这种效应非线性,而是只对市值最小的股票起作用。

Fama-French的三因子模型将规模、账面市值比和市场因子作为因子预测股票预期收益,发现效果优于CAPM模型。随后,又提出了五因子模型,纳入盈利和投资两个因子,发现能够解释更多的截面差异。

2. 成因

关于规模因子,一种解释来自风险补偿说。Chan and Chen认为,小市值公司普遍在过去遭遇过困境使得市值大幅下滑;Fama and French进一步指出规模效应可能与小市值股票更大的财务风险有关。除此之外,还有一种解释认为非流动性是一个定价因子,规模与非流动性相关,因此呈现出规模效应。由于小市值公司存在这些风险和问题,为了吸引投资者购买股票,必然要用更高的收益弥补这些风险。

另一些研究认为,规模效应来自设定偏误,是遗漏变量问题的自然结果。Berk认为,如果定价模型形式设定错误,则公司规模将总是与股票收益中未被解释的部分负相关。也就是说,规模越大,未被解释的收益率越低;规模越小,未被解释的收益率越高。

3. 实证(单变量排序)

由于A股特殊的IPO制度和壳价值,实证采用流动市值代替总市值作为排序变量构建规模因子(与原书中不同)。每月将股票按照流动市值大小分成10组,计算每组的平均月收益,各组表现如下图所示。
在这里插入图片描述
在这里插入图片描述

由图可知,规模因子在2017年效果很差。在2017年的12个月中,只有极少数月份满足收益率随着市值下降而增加的特点。甚至在很多月份中(如6月、8月、12月)呈现出收益率随着市值增加而上升的特点,违背了小市值效应的结果。实证结果与书中论述一致。

4. 策略

利用规模因子构建套利策略(利用流通市值代替总市值),回测期为2020年1月1日至2020年12月01日。计划每月调仓一次,每次调仓基准为:按照上个月流通市值平均排序,选取市值最小的前10支股票买入。回测结果如下:
在这里插入图片描述

回测期累计收益率为4.29%,年化收益率为4.66%,同期沪深300指数收益率为22.03%,策略整体跑输指数。最大回撤为15.22%,胜率只有57.50%。

如果反向套利,买入市值最大的10支股票,得到的年化收益为12.98%,最大回撤为14.19%,胜率为39.13%,大市值策略整体优于小市值策略。由此可见,规模因子在使用时需要具体情况具体分析。

5. 代码实现

(回测代码基于掘金量化平台)

# coding=utf-8
from __future__ import print_function, absolute_import
from gm.api import *
import pandas as pd
import numpy as np
import time
import datetime


# 策略部分
def init(context):
    # 获取全部A股代码
    stockall = get_instruments(exchanges='SHSE, SZSE', fields='symbol,listed_date, delisted_date,sec_type', df=True)
    context.stockall = stockall[(stockall['listed_date'] < context.backtest_start_time) &
                                (stockall['delisted_date'] > context.backtest_end_time) &
                                (stockall['symbol'].str[5] != '9') &
                                (stockall['symbol'].str[5] != '2') &
                                (stockall['sec_type'] == 1)]['symbol'].tolist()
    # 股票数量
    context.num = 10
    # 资金比例
    context.percent = 0.8
    # 初始化上次交易时间
    context.last_time = context.now.date()
    # 设置定时任务:每个月调仓一次
    schedule(schedule_func=algo, date_rule='1m', time_rule='09:40:00')


def algo(context):
    # 获取开始时间和结束时间
    last_month_end = datetime.datetime(context.now.year, context.now.month, 1) - datetime.timedelta(days=1)  # 上一个月的最后一天

    # 遍历股票
    fin = get_fundamentals(table='trading_derivative_indicator', symbols=context.stockall, start_date=last_month_end,
                               end_date=last_month_end, fields='TOTMKTCAP', limit=10000, df=True)
    # 按照市值排序,买入市值排名靠前的股票
    choose = fin.sort_values(by='TOTMKTCAP', ascending=True).head(context.num)['symbol'].tolist()
    print('%d年%d月的选股完成'%(context.now.year, context.now.month))

    # 查持仓
    positions = context.account().positions()
    percent = context.percent / context.num

    for position in positions:
        symbol = position['symbol']
        if symbol not in choose:
            order_target_percent(symbol=symbol, percent=percent, position_side=PositionSide_Short,
                                 order_type=OrderType_Market)
            print('{}不在买入列表中,平仓'.format(symbol))

    for symbol in choose:
        order_target_percent(symbol=symbol, percent=percent, position_side=PositionSide_Long,
                             order_type=OrderType_Market)
        print('{}在买入列表中,调仓至{}'.format(symbol, percent))


if __name__ == '__main__':
    run(strategy_id='输入策略id',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='输入token',
        backtest_start_time='2020-01-01 13:00:00',
        backtest_end_time='2020-12-01 15:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001)
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页