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

聚宽实盘中如何根据 action 明确区分买入和卖出,并安全地实现 order_target_value 的目标市值调整?

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

问题描述



Title: 聚宽实盘下单:如何改造 order_target_value 避免买卖方向错误?

Question: 聚宽实盘中如何根据 action 明确区分买入和卖出,并安全地实现 order_target_value 的目标市值调整?

解决方案

聚宽实盘下单:如何改造 order_target_value 避免买卖方向错误?

在聚宽(JoinQuant)量化平台上,order_target_value(security, value) 是一个非常高频且好用的 API,它能自动帮我们将某只标的的仓位调整到指定的期望价值(value)。

然而,在实盘交易高仿真模拟盘中,直接依赖 order_target_value 可能会面临以下安全隐患:

  1. 未完成订单干扰:如果该标的存在先前未完成的挂单,调用此接口会强制撤单,可能导致资金占用或持仓状态同步延迟。
  2. 买卖方向模糊order_target_value 内部自动计算买卖方向。在市场剧烈波动、多空双向持仓(如期货)或融资融券账户中,简单的价值差额计算极易因价格跳动而导致买卖方向判定错误,甚至发生“想平仓却开仓”的严重失误。

为了保障实盘资金安全,我们需要通过底层 Order 对象的 action(开/平,即 'open'/'close')和 side(多/空,即 'long'/'short')属性,对目标价值下单逻辑进行安全改造。


一、 核心原理:理解 Order 对象的 Action 与 Side

在聚宽 API 中,每一个下单函数成功执行后都会返回一个 Order 对象。要精准控制买卖方向,必须理解以下两个属性:

  • side:多/空方向,取值为 'long'(多单/股票买入)或 'short'(空单/期货做空)。
  • action:开/平仓动作,取值为 'open'(开仓/买入建仓)或 'close'(平仓/卖出减仓)。

对于股票交易:

  • 买入(建仓/加仓)side='long', action='open'
  • 卖出(平仓/减仓)side='long', action='close'

对于期货交易,则严格区分开多、平多、开空、平空。


二、 安全改造方案:自定义 safe_order_target_value

为了避免直接调用 order_target_value 带来的方向不确定性,我们可以编写一个包装函数。该函数会:

  1. 获取当前标的的最新价格与实际持仓价值。
  2. 计算目标价值与当前价值的差额。
  3. 显式判断是需要进行 open(买入/加仓)还是 close(卖出/减仓)动作。
  4. 检查是否有未完成订单,安全撤单后再显式下单。

Python 安全改造源码:

import jqdata

def initialize(context):
    # 开启真实价格模式
    set_option('use_real_price', True)
    g.security = '000001.XSHE' # 以平安银行为例
    
    # 运行每日交易
    run_daily(market_open, time='09:30')

def safe_order_target_value(security, target_value, pindex=0):
    """
    安全的目标价值调整函数,显式区分买入(open)与卖出(close)
    """
    # 1. 获取当前标的的最新数据
    current_data = get_current_data()
    if current_data[security].paused:
        log.warn(f"{security} 已停牌,无法进行目标价值调整。")
        return None
        
    last_price = current_data[security].last_price
    if last_price <= 0:
        log.warn(f"{security} 获取最新价异常,取消下单。")
        return None

    # 2. 获取当前子账户的持仓信息
    subportfolio = context.subportfolios[pindex]
    position = subportfolio.long_positions.get(security, None)
    
    current_value = position.value if position else 0.0
    current_amount = position.total_amount if position else 0
    
    # 3. 计算价值差额与目标股数
    value_diff = target_value - current_value
    target_amount = int(target_value / last_price)
    
    # A股交易规则:非清仓情况下,交易数量需为100的整数倍
    if target_amount > 0:
        target_amount = (target_amount // 100) * 100
    
    amount_diff = target_amount - current_amount

    # 4. 显式方向判定与下单
    if amount_diff > 0:
        # 期望持仓增加 -> 显式执行买入 (action='open')
        log.info(f"[安全下单] 标的: {security} 当前持仓: {current_amount} 股,目标持仓: {target_amount} 股。显式买入(Open) {amount_diff} 股。")
        # 使用 order 替代 order_target_value,确保方向绝对安全
        return order(security, amount_diff, side='long', pindex=pindex)
        
    elif amount_diff < 0:
        # 期望持仓减少 -> 显式执行卖出 (action='close')
        # 卖出数量不能超过当前可卖出数量
        closeable_amount = position.closeable_amount if position else 0
        sell_amount = min(abs(amount_diff), closeable_amount)
        
        if target_value == 0:
            # 如果目标价值为0,代表一键清仓,不受100股整数倍限制
            sell_amount = closeable_amount
            
        if sell_amount > 0:
            log.info(f"[安全下单] 标的: {security} 当前持仓: {current_amount} 股,可卖: {closeable_amount} 股。显式卖出(Close) {sell_amount} 股。")
            return order(security, -sell_amount, side='long', pindex=pindex)
        else:
            log.warn(f"[安全下单] 标的: {security} 需卖出但当前无可用可卖仓位(可能今日已买入,受T+1限制)。")
            return None
            
    else:
        log.info(f"[安全下单] 标的: {security} 当前价值已接近目标价值,无需调整。")
        return None

def market_open(context):
    # 示例:安全地将平安银行的仓位调整到 100,000 元价值
    target_val = 100000.0
    safe_order_target_value(g.security, target_val)

三、 实盘避坑指南

  1. T+1 制度限制:在 A 股市场中,当天买入的股票当天无法卖出(closeable_amount 为 0)。改造后的函数通过 position.closeable_amount 进行了二次校验,避免了实盘中因 T+1 限制导致的废单。
  2. 停牌与涨跌停控制:在调用下单前,务必通过 get_current_data() 检查 paused 状态以及是否封死涨跌停,防止实盘资金被异常挂单锁定。
  3. 多仓位(pindex)管理:如果使用了 set_subportfolios 创建了多个子账户(如股票、期货、融券分仓),在调用自定义函数时必须传入正确的 pindex,否则默认在 subportfolios[0] 中操作,会导致资产计算混乱。