🚀 AI 一键生成 joinquant 策略代码
立即体验

聚宽小市值策略:如何处理涨停买不进后的资金再分配?(附 Python 源码)

joinquant | 发布时间: 2026/8/4
以下内容由 EasyQuant 生成。

问题描述



Title: 聚宽小市值策略:如何处理涨停买不进后的资金再分配?

Question: 小市值策略中某只股票因涨停无法买入时,如何在聚宽中轮循补仓,把剩余资金分配给其他候选股?

解决方案

在量化交易中,小市值策略因其历史超额收益而广受欢迎。然而,在实际交易或高仿真模拟盘中,小市值股票经常出现开盘即一字涨停、无法买入的情况。如果直接忽略,会导致账户出现大量闲置资金,降低整体资金利用率。

为了解决这个问题,我们需要在聚宽(JoinQuant)平台中实现轮循补仓机制:当首选的目标股票因涨停无法买入时,策略自动检测并跳过,将剩余资金依次分配给候选股票池中的后续股票。

一、 核心设计思路与聚宽 API 接口

  1. 获取当前涨跌停价与最新价
    使用 get_current_data() 接口获取标的的实时盘面信息。通过 current_data[security].last_price 获取最新价,current_data[security].high_limit 获取涨停价。
  2. 判断是否涨停
    如果 last_price >= high_limit,说明该股当前已封涨停,此时下单大概率无法成交,应判定为“无法买入”状态。
  3. 轮循分配资金
    设定一个目标买入数量(例如 5 只)。如果前 5 只股票中有涨停无法买入的,则从候选池的第 6 只、第 7 只股票中依次递补,直到买满 5 只或候选池耗尽。

二、 聚宽策略完整源码实现

以下是一个完整的小市值轮循补仓策略示例。策略在开盘时获取市值最小的 10 只股票,目标买入前 5 只。若有涨停,则自动用第 6-10 只递补。

import pandas as pd
from jqdata import *

def initialize(context):
    # 设定沪深300作为基准
    set_benchmark('000300.XSHG')
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    # 过滤掉比error级别低的log
    log.set_level('order', 'error')
    
    # 设定交易税费
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 目标持仓股票数量
    g.target_num = 5
    # 候选股票池大小
    g.candidate_num = 15
    
    # 定时运行:每天 09:30 运行
    run_daily(market_open, time='09:30')

def market_open(context):
    # 1. 获取小市值候选股票池
    q = query(
        valuation.code
    ).filter(
        valuation.pe_ratio > 0 # 过滤掉亏损股
    ).order_by(
        valuation.market_cap.asc() # 按市值升序
    ).limit(g.candidate_num)
    
    df = get_fundamentals(q)
    candidate_stocks = list(df['code'])
    
    # 2. 卖出不在候选池中的持仓股票
    current_positions = list(context.portfolio.positions.keys())
    for stock in current_positions:
        if stock not in candidate_stocks[:g.target_num]:
            order_target(stock, 0)
            log.info(f"卖出非目标股: {stock}")
            
    # 3. 轮循买入机制
    current_data = get_current_data()
    bought_count = 0
    
    for stock in candidate_stocks:
        # 如果已经买够了目标数量,停止轮循
        if bought_count >= g.target_num:
            break
            
        # 获取当前股票的持仓状态
        position = context.portfolio.positions[stock]
        
        # 如果已经持有,计入已买入数量,继续轮循
        if position.total_amount > 0:
            bought_count += 1
            continue
            
        # 检测是否涨停
        last_price = current_data[stock].last_price
        high_limit = current_data[stock].high_limit
        paused = current_data[stock].paused
        
        if paused:
            log.info(f"股票 {stock} 停牌,跳过")
            continue
            
        if last_price >= high_limit:
            log.info(f"股票 {stock} 已涨停(当前价 {last_price}),无法买入,自动寻找下一只候选股")
            continue
            
        # 计算可用资金并买入
        # 将剩余可用资金均分给尚未买够的仓位
        remain_slots = g.target_num - bought_count
        available_cash = context.portfolio.available_cash
        
        if available_cash > 5000: # 剩余资金大于起投额再操作
            value_to_buy = available_cash / remain_slots
            order_value(stock, value_to_buy)
            log.info(f"成功递补买入: {stock}, 分配金额: {value_to_buy:.2f}元")
            bought_count += 1

三、 策略要点解析

  1. get_current_data() 的即时性
    handle_datarun_daily09:30 运行时,get_current_data() 能够获取到当天集合竞价结束后的最新开盘价。如果开盘即一字涨停,last_price >= high_limit 判定成立,完美避开废单。
  2. 动态计算 remain_slots
    在轮循买入时,每次成功下单后,bought_count 增加。计算下一只股票的买入金额时,使用 available_cash / (g.target_num - bought_count),确保资金被精准均分,不会因为前序股票未买入而导致资金闲置。
  3. 停牌处理
    除了涨停,停牌(paused == True)也是导致资金闲置的重要原因,代码中一并进行了过滤递补处理。