🚀 AI 一键生成 joinquant 策略代码

JoinQuant 策略实战:如何实现固定周期的周度调仓逻辑?

joinquant | 发布时间: 2025/12/19
以下内容由 EasyQuant 生成。

问题描述

怎么写一个每周一固定调仓的策略?

解决方案

在聚宽(JoinQuant)中实现“每周一固定调仓”的策略,核心在于使用 run_weekly 定时运行函数。

核心思路

  1. 初始化 (initialize): 设置基准、手续费、滑点等,并定义要操作的股票池。
  2. 定时任务 (run_weekly): 使用 run_weekly(func, weekday=1, time='09:30') 来指定在每周的第一个交易日(周一)开盘时执行调仓函数。
  3. 调仓逻辑 (weekly_adjustment):
    • 卖出不在目标股票池中的持仓。
    • 将资金等分,买入目标股票池中的股票(使用 order_target_value 自动调整仓位)。

策略代码示例

以下是一个完整的策略代码,该策略设定了一个固定的股票池(如平安银行和贵州茅台),并在每周一开盘时将仓位调整为等权重持有。

# -*- coding: utf-8 -*-
# 导入函数库
from jqdata import *

def initialize(context):
    # 1. 设定基准(这里使用沪深300)
    set_benchmark('000300.XSHG')
    
    # 2. 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    
    # 3. 设置手续费(股票:买入万三,卖出万三+千一印花税,最低5元)
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 4. 定义全局变量:目标股票池
    # 这里示例选取了 平安银行(000001.XSHE) 和 贵州茅台(600519.XSHG)
    g.target_stocks = ['000001.XSHE', '600519.XSHG']
    
    # 5. 设定定时运行:每周一 09:30 执行 weekly_adjustment 函数
    # weekday=1 代表周一,time='09:30' 代表开盘时运行
    run_weekly(weekly_adjustment, weekday=1, time='09:30')
    
    # 打印日志
    log.info("策略初始化完成,将在每周一进行调仓")

def weekly_adjustment(context):
    """
    周一调仓函数
    """
    log.info("检测到周一,开始执行调仓...")
    
    # 获取当前账户的总资产(现金 + 持仓价值)
    total_value = context.portfolio.total_value
    
    # 获取目标股票数量
    target_count = len(g.target_stocks)
    
    # 防止除以0的错误
    if target_count == 0:
        log.warn("目标股票池为空,不执行买入操作")
        return

    # 计算每只股票的目标持仓价值(等权重分配)
    target_value_per_stock = total_value / target_count
    
    # --- 步骤1:卖出操作 ---
    # 遍历当前持仓,如果持仓股票不在目标池中,则清仓
    for stock in context.portfolio.positions:
        if stock not in g.target_stocks:
            order_target_value(stock, 0)
            log.info("卖出非目标股票: %s" % stock)
            
    # --- 步骤2:买入/调整操作 ---
    # 遍历目标股票池,将每只股票调整到目标价值
    for stock in g.target_stocks:
        # order_target_value 会自动计算买卖数量
        # 如果持有价值低于目标价值,它会买入;如果高于,它会卖出
        order_target_value(stock, target_value_per_stock)
        log.info("调整股票 %s 持仓至目标价值: %.2f" % (stock, target_value_per_stock))

def before_trading_start(context):
    """
    (可选) 每天开盘前运行,用于处理一些盘前逻辑或日志
    """
    pass

def after_trading_end(context):
    """
    (可选) 每天收盘后运行
    """
    pass

代码关键点解析

  1. run_weekly(func, weekday=1, ...):

    • 这是实现周频策略的关键 API。
    • weekday=1 表示周一。如果周一休市(例如节假日),聚宽系统通常会顺延到该周的下一个交易日执行(取决于 force 参数,默认为 True 时会尝试在当周执行)。
    • time='09:30' 确保在开盘时刻立即触发信号。
  2. order_target_value(security, value):

    • 这是最适合“调仓”的下单函数。
    • 它不需要你计算要买多少股或卖多少股,你只需要告诉它“我希望这只股票最终持有多少钱”。
    • 例如:如果你持有 8000 元的茅台,目标是 10000 元,它会自动买入 2000 元;如果持有 12000 元,它会自动卖出 2000 元。
  3. 资金分配逻辑:

    • total_value = context.portfolio.total_value 获取当前账户总权益。
    • target_value_per_stock = total_value / len(g.target_stocks) 实现了简单的等权重分配。

常见问题 (Q&A)

Q: 如果周一是节假日怎么办?
A: run_weekly 函数在回测中,如果指定的 weekday 是非交易日,系统会根据该周剩余的交易日情况进行调整。通常情况下,如果周一休市,策略可能会在周二开盘运行(视具体年份的节假日安排和 API 内部逻辑而定),或者该周跳过。如果对调仓日期有极其严格的要求(必须是周一,否则不调),可以在函数内部增加 context.current_dt.isoweekday() == 1 的判断。

Q: 如何改成每月1号调仓?
A: 将 run_weekly 替换为 run_monthly(func, monthday=1, time='09:30') 即可。

Q: 为什么使用 order_target_value 而不是 order_value
A: order_value 是“买入/卖出指定金额”,是增量操作;而 order_target_value 是“调整至指定金额”,是存量调整。在定期再平衡(Rebalance)策略中,我们需要根据股价波动将仓位恢复到设定比例,因此必须使用 order_target_value