策略分享

小市值与动量结合的量化策略

由jliang创建,最终由jliang 被浏览 240 用户

策略介绍

在量化投资领域,小市值股票和动量因子是两个广泛应用的选股指标。小市值股票因其相对较小的市值,更容易受到市场情绪和资金流入的影响,从而表现出高收益特性。而动量因子则反映了股票价格在一段时间内的趋势,具有延续性的特点。本文结合这两个因子,构建一个针对全A股市场的量化策略,旨在通过选择具有高动量的小市值股票来实现最大化的年化收益率。

策略背景

小市值股票和动量因子在量化投资中具有重要地位。小市值股票通常具备高成长潜力,能在短期内带来显著收益。动量因子则是基于“强者恒强,弱者恒弱”的市场动量效应,追踪价格上涨或下跌的趋势。结合这两个因子可以捕捉市场中的高收益机会。小市值股票的高波动性和动量因子的价格延续性,使得这一策略在牛市中表现尤为优异。

策略优势

  1. 高收益潜力:小市值股票由于其较小的市值,更容易受到市场资金的关注,涨幅潜力大。结合动量因子,可以筛选出短期内表现优异的股票,进一步提升收益。
  2. 简单易操作:策略仅依赖于市值和动量两个因子,不需要复杂的模型和数据处理,易于实现和执行。
  3. 市场适应性强:策略适用于全A股市场,无需特定行业或板块的限制,具有广泛的适用性。

策略核心

策略核心在于两点:一是选择市值较小的股票,二是选择动量较强的股票。具体步骤包括:

  1. 构建市值因子,选择市值较小的股票。
  2. 构建动量因子,选择动量较强的股票。
  3. 结合市值因子和动量因子进行排序,选择综合评分最高的股票。
  4. 每月调仓,持有评分最高的前50只股票,等权重分配。

策略劣势及改进

  1. 高波动性:小市值股票波动性较大,可能带来较高的风险。虽然用户明确表示不需要控制风险,但在实际应用中,建议加入一定的风险管理手段,如止损策略或仓位控制。
  2. 市场不稳定性:策略在市场下行阶段可能表现不佳,动量因子在市场剧烈波动时失效。可以考虑引入更多因子,如价值因子、质量因子等,进一步提升选股精度。
  3. 调仓成本:频繁调仓可能导致较高的交易成本,影响策略实际收益。建议优化调仓频率,或采用更高效的交易机制。

未来展望

未来,小市值与动量结合的量化策略仍然具有广阔的发展空间。随着市场数据的丰富和计算能力的提升,可以进一步引入机器学习和深度学习模型,优化因子组合和权重分配,提高策略的收益和稳定性。同时,结合更多的市场情绪和宏观经济数据,增强策略的适应性和抗风险能力。此外,跨市场应用也是一个重要方向,可以尝试在港股、美股等市场推广这一策略,实现全球化投资。

策略流程

该策略的具体流程如下:

  1. 股票池筛选:选取全A股市场作为股票池,覆盖上交所、深交所和北交所的所有股票。
  2. 子构建:使用市值因子和动量因子,分别计算股票的市值排名和动量值。
  3. 因子综合:将市值因子和动量因子进行综合,得到综合评分。
  4. 排序与持仓:根据综合评分从高到低排序,选择前50只股票进行等权重持仓。
  5. 调仓与回测:每月调仓一次,基于最新因子数据调整持仓组合,并进行策略回测。

BigQuant 平台策略实现

模块说明

cn_stock_basic_selector:A股-基础选股,用于筛选全A股市场股票池。 参数:

exchanges=["上交所", "深交所", "北交所"](全A股市场)

input_features_dai:输入特征(DAI SQL),用于输入因子表达式。 参数:

expr="""
c_rank(float_market_cap) AS score_market_cap
momentum_5
score_market_cap - momentum_5 AS score
"""

score_to_position:仓位分配,根据因子排序规则和持仓股票数量控制仓位。 参数:

score_field="score DESC"(根据综合因子从大到小排序)
hold_count=50(持有50只股票)
position_expr="1 AS position"(等权重分配)

extract_data_dai:数据抽取(DAI),用于抽取因子数据。 参数:

start_date="2021-01-01"(回测开始日期)
end_date="2024-04-29"(回测结束日期)

bigtrader:BigTrader(高性能回测),实现回测逻辑。 参数:

initialize=m5_initialize_bigquant_run(初始化函数)
handle_data=m5_handle_data_bigquant_run(交易逻辑函数)
capital_base=1000000(初始资金)
frequency="daily"(日线数据)
product_type="股票"(产品类型为股票)
rebalance_period_type="月度交易日"(每月调仓)
rebalance_period_days="1"(每月第一个交易日调仓)
benchmark="沪深300指数"(基准为沪深300指数)

因子表达式

构建市值因子和动量因子:

c_rank(float_market_cap) AS score_market_cap
momentum_5
score_market_cap - momentum_5 AS score

策略代码

from bigmodule import M

# <aistudiograph>

# @param(id="m5", name="initialize")
def m5_initialize_bigquant_run(context):  # type: ignore
    from bigtrader.finance.commission import PerOrder

    # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数
    context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))

# @param(id="m5", name="handle_data")
def m5_handle_data_bigquant_run(context, data):  # type: ignore
    import pandas as pd

    # 下一个交易日不是调仓日,则不生成信号
    if not context.rebalance_period.is_signal_date(data.current_dt.date()):
        return

    # 从传入的数据 context.data 中读取今天的信号数据
    today_df = context.data[context.data["date"] == data.current_dt.strftime("%Y-%m-%d")]
    target_instruments = set(today_df["instrument"])

    # 获取当前已持有股票
    holding_instruments = set(context.get_account_positions().keys())

    # 卖出不在目标持有列表中的股票
    for instrument in holding_instruments - target_instruments:
        context.order_target_percent(instrument, 0)
    # 买入目标持有列表中的股票
    for i, x in today_df.iterrows():
        position = 0.0 if pd.isnull(x.position) else float(x.position)
        context.order_target_percent(x.instrument, position)

# @module(position="-437,-854", comment="""使用基本信息对股票池过滤""", comment_collapsed=False)
m1 = M.cn_stock_basic_selector.v6(  # type: ignore
    exchanges=["上交所", "深交所", "北交所"],
    list_sectors=["主板", "创业板", "科创板"],
    indexes=["中证500", "上证指数", "创业板指", "深证成指", "上证50", "科创50", "沪深300", "中证1000", "中证100", "深证100"],
    st_statuses=["正常", "ST", "*ST"],
    margin_tradings=["两融标的", "非两融标的"],
    sw2021_industries=["农林牧渔", "采掘", "基础化工", "钢铁", "有色金属", "建筑建材", "机械设备", "电子", "汽车", "交运设备", "信息设备", "家用电器", "食品饮料", "纺织服饰", "轻工制造", "医药生物", "公用事业", "交通运输", "房地产", "金融服务", "商贸零售", "社会服务", "信息服务", "银行", "非银金融", "综合", "建筑材料", "建筑装饰", "电力设备", "国防军工", "计算机", "传媒", "通信", "煤炭", "石油石化", "环保", "美容护理"],
    drop_suspended=True,
    m_name="m1"
)

# @module(position="-315,-727", comment="""因子特征""", comment_collapsed=False)
m2 = M.input_features_dai.v29(  # type: ignore
    input_1=m1.data,
    mode="表达式",
    expr="""
c_rank(float_market_cap) AS score_market_cap
momentum_5
score_market_cap - momentum_5 AS score
""",
    expr_filters="",
    expr_tables="cn_stock_prefactors",
    extra_fields="date, instrument",
    order_by="date, instrument",
    expr_drop_na=True,
    extract_data=False,
    m_name="m2"
)

# @module(position="-207,-592", comment="""持股数量、打分到仓位""", comment_collapsed=False)
m3 = M.score_to_position.v3(  # type: ignore
    input_1=m2.data,
    score_field="score DESC",
    hold_count=50,
    position_expr="1 AS position",
    total_position=1,
    extract_data=False,
    m_name="m3"
)

# @module(position="-98,-463", comment="""抽取预测数据""", comment_collapsed=False)
m4 = M.extract_data_dai.v17(  # type: ignore
    sql=m3.data,
    start_date="2021-01-01",
    start_date_bound_to_trading_date=True,
    end_date="2024-04-29",
    end_date_bound_to_trading_date=True,
    before_start_days=90,
    debug=False,
    m_name="m4"
)

# @module(position="43,-350", comment="""交易,日线,设置初始化函数和K线处理函数,以及初始资金、基准等""", comment_collapsed=False)
m5 = M.bigtrader.v20(  # type: ignore
    data=m4.data,
    start_date="2021-01-01",
    end_date="2024-04-29",
    initialize=m5_initialize_bigquant_run,
    handle_data=m5_handle_data_bigquant_run,
    capital_base=1000000,
    frequency="daily",
    product_type="股票",
    rebalance_period_type="月度交易日",
    rebalance_period_days="1",
    rebalance_period_roll_forward=True,
    backtest_engine_mode="标准模式",
    before_start_days=0,
    volume_limit=1,
    order_price_field_buy="open",
    order_price_field_sell="open",
    benchmark="沪深300指数",
    plot_charts=True,
    debug=False,
    backtest_only=False,
    m_name="m5"
)
# </aistudiograph>

\

标签

量化投资小市值股票动量因子
{link}