策略分享

基于小市值因子和动量因子的全A股量化策略

由ydong创建,最终由ydong 被浏览 129 用户

策略介绍

量化投资策略通过数学和统计方法,从历史数据中提取出有用的信息,指导投资决策。今天我们要介绍的是一个基于小市值因子和动量因子的全A股量化策略。该策略通过选择市值较小且动量较高的股票,力图在不进行额外风险控制的情况下,实现高年化收益。

小市值因子,即市值较小的股票往往具有较高的预期收益,这是因为小市值股票通常具有较高的成长性,但也伴随着较高的风险。动量因子,是指股票的价格具有惯性,即过去表现好的股票在未来一段时间内仍然可能表现较好。在实际操作中,动量因子常通过一定期间内的价格变化率来衡量。

该策略的优点在于通过组合小市值因子和动量因子,能够捕捉到市场中的高成长性机会,获得较高的年化收益。而缺点在于不进行风险控制,可能会在市场波动较大时面临较高的回撤风险。

策略流程

该量化策略的具体流程如下:

  1. 股票池:选择全A股市场,剔除停牌和ST股票。
  2. 因子计算:计算每只股票的小市值因子和动量因子。
  3. 综合排序:综合考虑小市值因子和动量因子,对股票进行排序。
  4. 持仓配置:选择排名靠前的30只股票,等权重分配持仓。
  5. 调仓逻辑:每月初进行一次调仓,根据最新的因子排序结果进行调整。
  6. 风险管理:不进行特别的风险管理。

策略实现

模块说明

为了实现上述策略,我们将使用BigQuant平台提供的多种模块,包括股票基础选股、因子特征计算、打分到仓位分配、数据抽取和高性能回测。

股票基础选股模块cn_stock_basic_selector

  • 用于筛选股票池,剔除停牌和ST股票。
  • 参数:
    • drop_suspended=True:剔除停牌股票
    • st_statuses=["正常"]:剔除ST股票

因子特征计算模块input_features_dai

  • 用于计算小市值因子和动量因子,并进行综合排序。
  • 参数:
    • expr

      c_rank(float_market_cap) AS market_cap_rank
      m_avg(close, 20) / m_avg(close, 60) AS momentum
      market_cap_rank + momentum AS score
      

打分到仓位分配模块score_to_position

  • 根据综合评分进行仓位分配,选择排名前30的股票,等权重持仓。
  • 参数:
    • score_field="score DESC":按综合评分从高到低排序
    • hold_count=30:持仓股票数量为30只
    • position_expr="1 AS position":等权重分配

数据抽取模块extract_data_dai

  • 用于抽取构建好的因子数据。
  • 参数:无特别修改

高性能回测模块bigtrader

  • 实现策略的回测逻辑,包括初始化函数和交易逻辑。
  • 参数:
    • initialize:初始化函数
    • handle_data:交易逻辑

因子表达式

构建因子表达式是策略的核心部分。我们使用以下因子表达式:

  1. 小市值因子:c_rank(float_market_cap) AS market_cap_rank
  2. 动量因子:m_avg(close, 20) / m_avg(close, 60) AS momentum
  3. 综合评分:market_cap_rank + momentum AS score

小市值因子通过市值的倒数排名实现,动量因子通过20日均价与60日均价的比值实现。综合评分通过将两者相加得到。

策略实现

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=["上交所", "深交所", "北交所"],
    st_statuses=["正常"],
    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 market_cap_rank
m_avg(close, 20) / m_avg(close, 60) AS momentum
market_cap_rank + momentum 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=30,
    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="2016-06-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="",
    end_date="",
    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}