问题描述
解决方案
这是一个非常经典的Alpha策略(或称为市场中性策略)的雏形。其核心逻辑是:
- 多头部分:通过选股策略(如基本面因子、多因子模型)构建股票组合,力求获得超越指数的超额收益(Alpha)。
- 空头部分:做空同等价值(或按Beta调整后价值)的股指期货(如IF、IC、IM),以剥离市场波动风险(Beta)。
- 收益来源:最终收益 = (股票组合涨跌幅 - 指数涨跌幅) + 期货基差收益 - 交易成本。只要股票组合跑赢指数,无论大盘涨跌,策略理论上都能获利。
下面是一个完整的策略代码示例。
策略逻辑说明
- 选股范围:沪深300成分股。
- 选股因子:低市盈率(PE)策略,选取PE最低的20只股票。
- 对冲标的:沪深300股指期货主力合约(IF)。
- 资金分配:80%资金用于买股票,20%资金用于期货保证金及抗风险现金。
- 调仓频率:按月调仓。
策略代码
# -*- coding: utf-8 -*-
from jqdata import *
def initialize(context):
"""
初始化函数,设定基准、手续费、资金分配等
"""
# 设定基准为沪深300指数
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 设定股票和期货的手续费
# 股票:买入万三,卖出万三加千一印花税
set_order_cost(OrderCost(open_tax=0, close_tax=0.001, open_commission=0.0003, close_commission=0.0003, close_today_commission=0, min_commission=5), type='stock')
# 期货:万分之0.23
set_order_cost(OrderCost(open_tax=0, close_tax=0, open_commission=0.000023, close_commission=0.000023, close_today_commission=0.0023, min_commission=0), type='index_futures')
# 设定保证金比例,股指期货一般为10%-15%,这里设为15%
set_option('futures_margin_rate', 0.15)
# 关键步骤:设置子账户
# subportfolios[0]: 股票账户,分配80%资金
# subportfolios[1]: 期货账户,分配20%资金
total_cash = context.portfolio.starting_cash
set_subportfolios([
SubPortfolioConfig(cash=total_cash * 0.8, type='stock'),
SubPortfolioConfig(cash=total_cash * 0.2, type='index_futures')
])
# 定义全局变量
g.stock_pool = [] # 选出的股票列表
g.future_code = None # 当前主力合约代码
g.stock_num = 20 # 持仓股票数量
# 设置定时运行:每月第一个交易日进行调仓
run_monthly(rebalance, 1, time='09:30')
# 每天开盘前更新主力合约
run_daily(update_future_contract, time='09:00')
def update_future_contract(context):
"""
每天更新当月的主力合约代码
"""
# 获取沪深300股指期货的主力合约
dominant = get_dominant_future('IF')
if dominant:
g.future_code = dominant
def rebalance(context):
"""
调仓主函数:先调整股票仓位,再调整期货对冲仓位
"""
if g.future_code is None:
log.warn("未获取到主力合约,跳过调仓")
return
# 1. 选股逻辑
select_stocks(context)
# 2. 交易股票
trade_stocks(context)
# 3. 交易期货进行对冲
hedge_futures(context)
def select_stocks(context):
"""
选股函数:选取沪深300中PE最低的股票
"""
# 获取沪深300成分股
index_stocks = get_index_stocks('000300.XSHG')
# 查询财务数据:市值和PE
q = query(
valuation.code,
valuation.pe_ratio
).filter(
valuation.code.in_(index_stocks),
valuation.pe_ratio > 0 # 过滤亏损股
).order_by(
valuation.pe_ratio.asc() # 按PE从小到大排序
).limit(g.stock_num)
df = get_fundamentals(q)
g.stock_pool = list(df['code'])
log.info("今日选股: %s" % g.stock_pool)
def trade_stocks(context):
"""
股票交易函数
"""
# 获取股票子账户
stock_account = context.subportfolios[0]
# 卖出不在股票池中的股票
for stock in list(stock_account.long_positions.keys()):
if stock not in g.stock_pool:
order_target_value(stock, 0, pindex=0)
# 买入股票池中的股票
# 简单的等权重分配
if len(g.stock_pool) > 0:
# 计算每只股票的目标价值 = 股票账户总资产 / 股票数量
target_value = stock_account.total_value / len(g.stock_pool)
for stock in g.stock_pool:
order_target_value(stock, target_value, pindex=0)
def hedge_futures(context):
"""
期货对冲函数
"""
# 获取股票账户和期货账户
stock_account = context.subportfolios[0]
future_account = context.subportfolios[1]
# 1. 计算股票组合的总市值
stock_value = stock_account.positions_value
# 如果股票市值为0,平掉所有期货空单
if stock_value == 0:
for code in future_account.short_positions.keys():
order_target(code, 0, side='short', pindex=1)
return
# 2. 处理主力合约换月
# 如果持仓中有非当前主力合约的空单,先平仓
for code in list(future_account.short_positions.keys()):
if code != g.future_code:
order_target(code, 0, side='short', pindex=1)
log.info("平掉旧合约: %s" % code)
# 3. 计算需要对冲的手数
# 获取期货当前价格
future_price = get_price(g.future_code, count=1, end_date=context.current_dt, fields=['close'])['close'][0]
# 合约乘数,IF是300元/点
multiplier = 300
# 计算合约价值
contract_value = future_price * multiplier
# 计算需做空的手数 = 股票总市值 / 单张合约价值
# 这里采用完全对冲(1:1),也可以根据Beta调整
short_amount = int(stock_value / contract_value)
# 4. 下单调整期货仓位
# 注意:pindex=1 使用期货子账户
# side='short' 表示操作空单
# order_target 调整到目标持仓量
order_target(g.future_code, short_amount, side='short', pindex=1)
log.info("股票市值: %.2f, 期货价格: %.2f, 对冲手数: %d" % (stock_value, future_price, short_amount))
关键点解析
-
子账户设置 (
set_subportfolios):- 这是实现股期联动的关键。我们将资金拆分为两部分:
pindex=0用于股票,pindex=1用于期货。 - 股票账户类型设为
'stock',期货账户类型设为'index_futures'。 - 通常建议预留 20%-30% 的资金在期货账户,以防止期货价格上涨导致保证金不足被强平。
- 这是实现股期联动的关键。我们将资金拆分为两部分:
-
主力合约切换 (
get_dominant_future):- 期货合约会到期,策略必须包含自动换月的逻辑。代码中在
hedge_futures里检查持仓合约是否等于当前主力合约,如果不等则平掉旧合约,并在后续步骤开新合约。
- 期货合约会到期,策略必须包含自动换月的逻辑。代码中在
-
对冲数量计算:
- 公式:
手数 = 股票总市值 / (期货价格 * 合约乘数)。 - 沪深300 (IF) 乘数是 300,中证500 (IC) 乘数是 200。
- 本策略采用 1:1 市值对冲。进阶做法是计算股票组合相对于指数的 Beta 值,
手数 = (股票市值 * Beta) / 合约价值。
- 公式:
-
下单函数 (
order_target):- 期货做空使用的是
side='short'。 order_target会自动计算当前持仓和目标持仓的差额进行买卖,非常适合调仓逻辑。
- 期货做空使用的是
常见问题 Q&A
Q: 为什么我的策略回测时,期货账户经常爆仓?
A: 可能是资金分配问题。如果股票满仓,期货端只留了刚好够开仓的保证金,一旦指数上涨,期货空单会亏损,导致保证金不足。建议在 set_subportfolios 中给期货账户分配更多现金(如20%-30%),或者降低股票仓位。
Q: 如何更换为中证500对冲?
A:
- 将基准改为
'000905.XSHG'。 - 选股范围
get_index_stocks改为'000905.XSHG'。 - 主力合约获取改为
get_dominant_future('IC')。 - 合约乘数
multiplier改为 200。
Q: 什么是基差风险?
A: 股指期货价格通常和现货指数价格不一致(通常是贴水,即期货比现货便宜)。如果贴水幅度扩大,即使股票跑赢了指数,总收益也可能因为期货端的额外亏损而减少。这是此类策略的主要风险之一。