3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在聚宽(JoinQuant)平台进行实盘或模拟盘交易时,由于A股实行 T+1 交易制度,当天买入的股票当天无法卖出。如果策略在计算调仓时,简单地使用目标市值差额计算出需要卖出的股数,并直接下单,很容易因为卖出尚未可用的持仓而导致委托失败或系统报错。
为了解决这一问题,我们需要在计算出目标卖出股数后,结合 Position 对象的 closeable_amount(可卖出仓位)属性进行二次校验,确保卖出数量不超过实际可卖数量。
closeable_amount(可卖出仓位):
在聚宽 API 中,context.portfolio.positions[security].closeable_amount 表示当前持仓中可以卖出的股票数量(即排除了当天买入的、以及已被其他未完成订单冻结的股数)。
目标市值差额计算:
调仓时,我们通常会计算目标持仓市值与当前持仓市值的差额。若:
$$\text{目标股数} < \text{当前总股数}$$
则需要卖出差额部分:
$$\text{计划卖出股数} = \text{当前总股数} - \text{目标股数}$$
安全卖出控制:
为了防止废单,实际下单的卖出股数应为:
$$\text{实际卖出股数} = \min(\text{计划卖出股数}, \text{closeable_amount})$$
以下是在 handle_data 或定时运行函数中,安全计算并执行卖出调仓的逻辑:
def adjust_position_to_target(context, security, target_value):
# 1. 获取当前标的的最新价格
current_data = get_current_data()
price = current_data[security].last_price
if price <= 0:
return
# 2. 计算目标股数(按100股向下取整)
target_amount = int(target_value / price / 100) * 100
# 3. 获取当前持仓信息
position = context.portfolio.positions[security]
current_amount = position.total_amount
closeable = position.closeable_amount
# 4. 判断是否需要卖出
if target_amount < current_amount:
# 计划卖出股数
sell_amount = current_amount - target_amount
# 结合 closeable_amount 限制实际卖出股数,防止超卖报错
actual_sell_amount = min(sell_amount, closeable)
if actual_sell_amount > 0:
# 卖出时 amount 传入负数
order(security, -actual_sell_amount)
log.info(f"计划卖出 {security} {sell_amount}股,实际可卖 {actual_sell_amount}股,已下单。")
# 5. 判断是否需要买入
elif target_amount > current_amount:
buy_amount = target_amount - current_amount
# 检查可用资金是否足够
cash = context.portfolio.available_cash
if cash >= buy_amount * price:
order(security, buy_amount)
log.info(f"买入 {security} {buy_amount}股。")
下面是一个完整的聚宽策略,展示了如何在每日调仓中,安全地使用 closeable_amount 控制卖出数量:
# 导入聚宽函数库
import jqdata
def initialize(context):
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 定义操作股票
g.security = '000001.XSHE' # 平安银行
# 每天 09:30 运行
run_daily(market_open, time='09:30')
def market_open(context):
security = g.security
# 获取历史数据计算均线
close_data = attribute_history(security, 10, '1d', ['close'])
ma5 = close_data['close'][-5:].mean()
ma10 = close_data['close'][-10:].mean()
# 获取当前账户总资产
total_value = context.portfolio.total_value
# 设定目标仓位比例
if ma5 > ma10:
# 金叉:目标配置 80% 仓位
target_ratio = 0.8
else:
# 死叉:目标配置 0% 仓位(清仓)
target_ratio = 0.0
# 计算目标市值
target_value = total_value * target_ratio
# 执行安全调仓
safe_order_target_value(context, security, target_value)
def safe_order_target_value(context, security, target_value):
current_data = get_current_data()
price = current_data[security].last_price
if price <= 0:
return
# 计算目标股数(100股整数倍)
target_amount = int(target_value / price / 100) * 100
position = context.portfolio.positions[security]
current_amount = position.total_amount
closeable = position.closeable_amount
if target_amount < current_amount:
# 需要减仓或清仓
sell_amount = current_amount - target_amount
# 核心安全控制:确保卖出量不超过可卖量
actual_sell_amount = min(sell_amount, closeable)
if actual_sell_amount > 0:
order(security, -actual_sell_amount)
log.info(f"调仓卖出:{security} 计划卖出 {sell_amount} 股,受 closeable_amount 限制,实际执行卖出 {actual_sell_amount} 股。")
elif target_amount > current_amount:
# 需要加仓
buy_amount = target_amount - current_amount
cash = context.portfolio.available_cash
# 确保资金充足
if cash < buy_amount * price:
buy_amount = int(cash / price / 100) * 100
if buy_amount > 0:
order(security, buy_amount)
log.info(f"调仓买入:{security} 执行买入 {buy_amount} 股。")
closeable_amount:持仓状态每天都会随清算而更新,必须在每次下单前实时通过 context.portfolio.positions[security].closeable_amount 获取最新值。closeable_amount 会相应减少。因此,在调仓前建议先调用 get_open_orders() 检查并撤销未完成的订单,释放可卖额度。