全A股小市值与动量因子策略
由polll创建,最终由polll 被浏览 170 用户
策略介绍
全A股市场的量化投资策略在近年来受到越来越多投资者的关注。此次我们介绍的策略主要依赖小市值因子和动量因子进行股票选择,以期在不进行风险控制的情况下最大化年化收益率。小市值因子(Small Size Factor)和动量因子(Momentum Factor)是量化投资中常用的两个因子。
小市值因子基于“规模效应假说”,认为小市值股票往往具有更高的收益潜力,因为它们的增长空间较大且往往被市场低估。动量因子则基于“惯性效应假说”,认为过去表现好的股票在未来一段时间内仍有可能继续表现优异。这两个因子的结合可以在一定程度上兼顾成长性与市场趋势,形成一个较为稳健的投资组合。
然而,这种策略也有其局限性。首先,策略不进行风险控制,这意味着在市场波动较大时,可能会遭受较大的回撤。此外,小市值股票的流动性较差,在大规模资金进出时可能会面临较高的交易成本。为了改进该策略,可以考虑加入风险控制模块,如设置止损线或采用动态仓位管理。
策略流程
- 股票池:全A股市场。
- 排序条件:使用小市值因子和动量因子进行排序。
- 筛选条件:无额外筛选。
- 仓位设置:等权重持仓,持仓数量设置为50只股票。
- 交易逻辑:每月初进行调仓。
- 风险管理:不进行风险管理。
策略实现
模块说明
input_features_dai:输入特征模块,提取小市值因子和动量因子,并计算综合评分因子。
- 参数:
- expr:表达式,计算市值、动量和综合评分因子。
- expr_tables:数据表,使用A股市场数据。
- extra_fields:额外字段,包含日期和股票代码。
- order_by:排序条件,按日期和股票代码排序。
- expr_drop_na:是否去除NA值。
- extract_data:是否提取数据。
score_to_position:仓位分配模块,根据综合评分因子进行排序,选择前50只股票进行等权重持仓。
- 参数:
- score_field:评分字段,按综合评分因子降序排序。
- hold_count:持仓数量,50只股票。
- position_expr:仓位表达式,等权重持仓。
- total_position:总持仓比例,1表示全仓。
bigtrader:高性能回测模块,执行回测逻辑。
- 参数:
- rebalance_period_type:调仓周期,月度调仓。
- initialize:初始化函数。
- handle_data:交易逻辑函数。
- capital_base:初始资金,100万元。
- frequency:交易频率,日线。
- product_type:产品类型,股票。
- rebalance_period_days:调仓日,1表示每月1日调仓。
- backtest_engine_mode:回测引擎模式,标准模式。
- volume_limit:交易量限制,1表示不限制。
- order_price_field_buy:买入价,开盘价。
- order_price_field_sell:卖出价,开盘价。
- benchmark:基准,沪深300指数。
因子表达式
- 小市值因子:
c_rank(float_market_cap) AS market_cap_rank
- 动量因子:
m_avg(change_ratio, 20) AS momentum_20
- 综合评分因子:
-(market_cap_rank + momentum_20) 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="-315,-727", comment=""""因子特征""", comment_collapsed=False)
m2 = M.input_features_dai.v29( # type: ignore
mode="表达式",
expr="""
c_rank(float_market_cap) AS market_cap_rank
m_avg(change_ratio, 20) AS momentum_20
-(market_cap_rank + momentum_20) AS score
""",
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="2020-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="",
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>
\