3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个基于 沪深300指数 的增强策略示例。
000300.XSHG)为基准。get_index_weights 获取指数成分股及其原始权重。# -*- coding: utf-8 -*-
from jqdata import *
import pandas as pd
def initialize(context):
"""
初始化函数
"""
# 1. 设定基准:沪深300
g.index_id = '000300.XSHG'
set_benchmark(g.index_id)
# 2. 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 3. 设置手续费(模拟股票交易佣金)
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# 4. 设定调仓周期:每月第一个交易日运行
run_monthly(rebalance, monthday=1, time='09:30')
# 打印日志
log.info("策略初始化完成,基准:{}".format(g.index_id))
def rebalance(context):
"""
调仓核心逻辑
"""
log.info("开始进行调仓...")
# --- 第一步:获取指数成分股及原始权重 ---
# 注意:使用上一交易日日期,避免未来函数
date = context.previous_date
# get_index_weights 返回 DataFrame,索引为股票代码,包含 'weight', 'display_name', 'date'
index_weights = get_index_weights(g.index_id, date)
if index_weights is None or index_weights.empty:
log.warn("未获取到指数权重数据,跳过本次调仓")
return
# 提取股票列表
stock_list = list(index_weights.index)
# --- 第二步:获取增强因子数据(此处以PE为例) ---
q = query(
valuation.code,
valuation.pe_ratio
).filter(
valuation.code.in_(stock_list),
valuation.pe_ratio > 0 # 过滤掉亏损股票(PE为负)
)
df_fund = get_fundamentals(q, date=date)
if df_fund.empty:
log.warn("未获取到财务数据")
return
# 将财务数据索引设为股票代码,方便合并
df_fund.set_index('code', inplace=True)
# --- 第三步:数据合并与增强逻辑 ---
# 合并 权重数据 和 因子数据
# inner join 确保股票既在指数中,又有财务数据
df_merged = pd.concat([index_weights['weight'], df_fund['pe_ratio']], axis=1, join='inner')
# 增强逻辑:剔除PE最高的20%的股票(负向剔除)
# 1. 按PE升序排列 (小的在前)
df_merged = df_merged.sort_values(by='pe_ratio', ascending=True)
# 2. 计算保留的数量 (保留前80%)
keep_count = int(len(df_merged) * 0.8)
df_target = df_merged.iloc[:keep_count].copy()
# 3. 权重归一化
# 因为剔除了部分股票,剩余股票的权重加起来小于100%,需要重新调整让它们加起来等于100%
total_weight = df_target['weight'].sum()
df_target['final_weight'] = df_target['weight'] / total_weight
log.info("选股完成,原始成分股数量: {}, 剔除后数量: {}".format(len(index_weights), len(df_target)))
# --- 第四步:执行交易 ---
do_trade(context, df_target)
def do_trade(context, df_target):
"""
执行下单交易
"""
# 获取当前账户总资产
total_value = context.portfolio.total_value
# 1. 卖出不在目标持仓中的股票
current_positions = context.portfolio.positions
for stock in current_positions:
if stock not in df_target.index:
order_target_value(stock, 0)
# 2. 买入/调整目标股票
# 遍历目标股票,按计算出的权重下单
# 注意:为了防止资金不足导致无法买入最后几只,通常可以预留少量现金或按比例下单
for stock in df_target.index:
target_weight = df_target.loc[stock, 'final_weight']
target_value = total_value * target_weight
# 下单调整到目标金额
order_target_value(stock, target_value)
get_index_weights(index_id, date):
index 是股票代码,weight 列是权重(通常是百分比形式,如 0.5 代表 0.5%)。df_target['weight'] / total_weight),所以无论原始权重是百分比还是小数都不影响最终比例。增强逻辑 (Negative Screening):
pd.concat 将指数权重和 PE 数据对齐。sort_values 和切片 iloc[:keep_count] 实现了剔除高估值股票的操作。权重归一化:
df_target['weight'] / total_weight,将资金分配填满,否则会导致仓位长期不满,跑输基准。交易执行:
order_target_value 根据总资产和目标权重计算每只股票应持有的金额,自动进行买卖调整。Q: 为什么要使用 context.previous_date 获取权重?
A: 为了避免未来函数。在回测中,如果在开盘前(09:30)获取“今天”的指数权重,这在现实中是不可能的(因为今天的权重可能受今日行情影响)。使用前一交易日的数据更符合实盘逻辑。
Q: 这个策略可以换成其他指数吗?
A: 可以。只需将 g.index_id 修改为其他指数代码,例如中证500(000905.XSHG)或上证50(000016.XSHG)。
Q: 如何更换增强因子?
A: 修改 get_fundamentals 中的查询语句。例如,如果你想用 ROE(净资产收益率) 增强:
indicator.roe。ascending=False(因为ROE越高越好,我们要保留头部股票)。