问题描述
解决方案
在聚宽(JoinQuant)的期货策略中,判断保证金比例是否过低(即风险度过高)通常有两种方法:
- 手动计算风险度(推荐):通过
当前持仓保证金 / 账户总资产计算风险度。一般当该比例超过 80% 或 90% 时,就需要减仓。 - 使用内置函数:使用
is_dangerous函数判断可用资金是否低于设定比例。
下面我将提供一个完整的策略示例,展示如何实时监控风险度,并在风险过高时自动按比例减仓以防止强平。
核心逻辑说明
- 获取账户信息:通过
context.portfolio获取总资产 (total_value) 和当前占用保证金 (margin)。 - 计算风险度:
risk_ratio = margin / total_value。 - 风控触发:如果
risk_ratio超过设定的阈值(例如 80%),则触发减仓逻辑。 - 执行减仓:遍历当前的多单和空单持仓,按比例(例如平掉 50% 的仓位)或全部平仓来释放保证金。
策略代码实现
# -*- coding: utf-8 -*-
from jqdata import *
def initialize(context):
# 1. 设定期货账户
# 获取初始资金
init_cash = context.portfolio.starting_cash
# 设定账户类型为期货
set_subportfolios([SubPortfolioConfig(cash=init_cash, type='futures')])
# 2. 设定交易费率和滑点 (以螺纹钢为例,实际需按品种调整)
set_order_cost(OrderCost(open_commission=0.0001, close_commission=0.0001, close_today_commission=0.0001), type='futures')
set_slippage(FixedSlippage(0))
# 3. 设定基准
set_benchmark('RB9999.XSGE')
# 4. 定义全局变量
g.future_code = 'RB9999.XSGE' # 示例操作标的
# 5. 设定风控阈值
g.risk_limit = 0.8 # 风险度阈值 (保证金占比超过80%视为危险)
g.reduce_ratio = 0.5 # 触发风控时,减仓比例 (平掉50%持仓)
# 6. 运行频率
run_daily(market_open, time='every_bar')
# 单独注册一个风控检查函数,确保每分钟或定时检查
run_daily(check_risk_management, time='every_bar')
def market_open(context):
# 这里写你的开仓逻辑,仅作演示
# 示例:随机开仓以增加保证金占用
if context.portfolio.margin / context.portfolio.total_value < 0.5:
# 如果风险度低,尝试开多单
order(g.future_code, 1, side='long')
def check_risk_management(context):
"""
风控函数:检查保证金比例,过高则强平部分仓位
"""
# 获取当前账户对象 (如果是多账户,需指定 subportfolios[i])
portfolio = context.portfolio
# 1. 获取资金数据
total_value = portfolio.total_value # 总资产
margin_used = portfolio.margin # 已用保证金
# 防止除以0错误
if total_value == 0:
return
# 2. 计算当前风险度 (保证金占用比例)
current_risk = margin_used / total_value
# 打印当前风险日志
# log.info("当前风险度: {:.2%}".format(current_risk))
# 3. 判断是否超过阈值
if current_risk >= g.risk_limit:
log.warn("【风控触发】当前风险度 {:.2%} 超过阈值 {:.2%},开始执行减仓...".format(current_risk, g.risk_limit))
# 执行减仓操作
reduce_positions(context)
def reduce_positions(context):
"""
减仓逻辑:遍历所有持仓,按比例平仓
"""
portfolio = context.portfolio
# 1. 处理多单 (Long Positions)
for security, pos in portfolio.long_positions.items():
if pos.total_amount > 0:
# 计算需要卖出的数量
target_amount = int(pos.total_amount * (1 - g.reduce_ratio))
# 如果计算后数量为0但原持有量>0,强制至少平掉1手,或者直接全平
if target_amount == pos.total_amount and pos.total_amount > 0:
target_amount = 0
log.info("正在平多单: {}, 当前持仓: {}, 目标持仓: {}".format(security, pos.total_amount, target_amount))
# 平多单操作:side='long', amount设为目标持仓量
order_target(security, target_amount, side='long')
# 2. 处理空单 (Short Positions)
for security, pos in portfolio.short_positions.items():
if pos.total_amount > 0:
# 计算需要卖出的数量
target_amount = int(pos.total_amount * (1 - g.reduce_ratio))
log.info("正在平空单: {}, 当前持仓: {}, 目标持仓: {}".format(security, pos.total_amount, target_amount))
# 平空单操作:side='short', amount设为目标持仓量
order_target(security, target_amount, side='short')
关键API解析
-
context.portfolio.margin:- 获取当前账户已占用的保证金总额。这是判断风险的核心数据。
-
context.portfolio.total_value:- 获取当前账户的总权益(可用资金 + 保证金 + 浮动盈亏)。
- 注意:不要使用
available_cash来做分母,因为可用资金会随着开仓减少,而总权益才是衡量账户抗风险能力的基准。
-
order_target(security, amount, side='...'):- 在期货交易中,平仓必须指定
side。 - 平多单:
side='long',amount设置为比当前更小的值(或0)。 - 平空单:
side='short',amount设置为比当前更小的值(或0)。
- 在期货交易中,平仓必须指定
另一种方法:使用 is_dangerous
聚宽提供了一个内置函数 is_dangerous,但它的逻辑是基于“可用资金”或“保证金比例”的判断,不如手动计算直观。如果你想用它,用法如下:
# 检查 subportfolios[0] 的保证金比例是否低于 20% (即风险度是否极高)
# 注意:这里的参数含义可能因语境不同而异,建议使用上面的手动计算法更稳妥
if context.subportfolios[0].is_dangerous(0.2):
log.warn("账户资金处于危险边缘")
# 执行平仓...
建议
- 阈值设置:建议将
g.risk_limit设置在 0.6 到 0.8 之间。期货波动大,留足 20%-40% 的可用资金作为缓冲是非常必要的。 - 执行频率:建议在
handle_data或分钟级的run_daily中调用风控函数,确保盘中价格剧烈波动时能及时反应。 - 优先平亏损单:上面的代码是按比例平掉所有仓位。进阶的做法是计算每个持仓的盈亏,优先平掉亏损最大的仓位来保命。
Q&A
Q1: 为什么计算风险度要用 margin / total_value 而不是 margin / available_cash?
A1: total_value 代表你的总权益(本金+盈亏)。风险度是指你用了多少总权益去抗单。available_cash 只是剩余没用的钱,它会随着开仓变成0,无法作为分母来衡量整体风险比例。
Q2: order_target 在平仓时,side 参数应该填什么?
A2: side 参数代表你操作的是哪个方向的持仓。
- 如果你持有多单想平仓,
side必须填'long',并将数量设为 0 或更少。 - 如果你持有空单想平仓,
side必须填'short'。 - 不要混淆:平多单虽然是“卖出”动作,但在
order_target里是对long仓位进行目标调整。
Q3: 模拟盘和实盘的保证金计算有区别吗?
A3: 有区别。模拟盘通常按交易所标准保证金计算,而实盘中期货公司通常会在交易所基础上加收几个点(例如交易所10%,期货公司可能收15%)。在编写策略时,建议将风控阈值设得比理论值更保守一些(更低一些),以覆盖这种差异。