3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在聚宽(JoinQuant)量化平台上,order_target_value(security, value) 是一个非常高频且好用的 API,它能自动帮我们将某只标的的仓位调整到指定的期望价值(value)。
然而,在实盘交易或高仿真模拟盘中,直接依赖 order_target_value 可能会面临以下安全隐患:
order_target_value 内部自动计算买卖方向。在市场剧烈波动、多空双向持仓(如期货)或融资融券账户中,简单的价值差额计算极易因价格跳动而导致买卖方向判定错误,甚至发生“想平仓却开仓”的严重失误。为了保障实盘资金安全,我们需要通过底层 Order 对象的 action(开/平,即 'open'/'close')和 side(多/空,即 'long'/'short')属性,对目标价值下单逻辑进行安全改造。
在聚宽 API 中,每一个下单函数成功执行后都会返回一个 Order 对象。要精准控制买卖方向,必须理解以下两个属性:
side:多/空方向,取值为 'long'(多单/股票买入)或 'short'(空单/期货做空)。action:开/平仓动作,取值为 'open'(开仓/买入建仓)或 'close'(平仓/卖出减仓)。对于股票交易:
side='long', action='open'side='long', action='close'对于期货交易,则严格区分开多、平多、开空、平空。
safe_order_target_value为了避免直接调用 order_target_value 带来的方向不确定性,我们可以编写一个包装函数。该函数会:
open(买入/加仓)还是 close(卖出/减仓)动作。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)
closeable_amount 为 0)。改造后的函数通过 position.closeable_amount 进行了二次校验,避免了实盘中因 T+1 限制导致的废单。get_current_data() 检查 paused 状态以及是否封死涨跌停,防止实盘资金被异常挂单锁定。set_subportfolios 创建了多个子账户(如股票、期货、融券分仓),在调用自定义函数时必须传入正确的 pindex,否则默认在 subportfolios[0] 中操作,会导致资产计算混乱。