问题描述
解决方案
在 JoinQuant 聚宽平台中,要获取每一笔成交的详细信息,主要使用 get_trades() 函数。该函数返回当日所有的成交记录。
要计算“实际交易滑点”,我们需要定义滑点的概念:通常指 “实际成交价”与“下单时的预期价(或基准价)”之间的差额。
由于系统自动撮合时不会自动记录你心目中的“预期价”,因此在策略代码中,我们需要分两步走:
- 下单时:记录当时的基准价格(例如当时的最新价
last_price)作为预期价。 - 收盘后:通过
get_trades()获取实际成交价,并与记录的预期价进行对比计算。
以下是完整的策略代码示例,展示了如何获取成交详情并计算滑点。
策略代码实现
# -*- coding: utf-8 -*-
# 导入函数库
from jqdata import *
def initialize(context):
# 1. 设置基准
set_benchmark('000300.XSHG')
# 2. 开启真实价格模式(动态复权)
set_option('use_real_price', True)
# 3. 设置日志级别
log.set_level('order', 'error')
# 4. 设置滑点:为了演示计算效果,这里设置一个较大的固定滑点(双边各0.02元)
# 这意味着买入价比市价高0.01,卖出价比市价低0.01
set_slippage(FixedSlippage(0.02))
# 5. 定义一个全局字典,用于记录下单时的“预期价格”
# 格式: {order_id: expected_price}
g.order_expectations = {}
# 每天运行
run_daily(market_open, time='every_bar')
# 收盘后进行统计
run_daily(after_trading_end, time='15:30')
def market_open(context):
# 示例:每天买入一只股票,卖出一只股票
stock_to_buy = '000001.XSHE' # 平安银行
# 获取当前时刻的最新数据
current_data = get_current_data()
# --- 模拟买入操作 ---
if stock_to_buy not in context.portfolio.positions:
# 1. 获取下单前的最新价作为“预期价格”
expected_price = current_data[stock_to_buy].last_price
# 2. 下单
order_obj = order(stock_to_buy, 1000)
# 3. 如果下单成功(返回了Order对象),记录预期价格
if order_obj:
g.order_expectations[order_obj.order_id] = expected_price
log.info("【下单买入】标的:%s,预期价格:%.2f" % (stock_to_buy, expected_price))
def after_trading_end(context):
log.info("================ 每日成交详情与滑点统计 ================")
# 1. 获取当日所有成交记录
# get_trades() 返回一个dict,key是trade_id,value是Trade对象
trades = get_trades()
if not trades:
log.info("今日无成交。")
return
# 2. 遍历每一笔成交
for trade_id, trade in trades.items():
# Trade对象包含:time, security, amount, price, trade_id, order_id
# 获取对应的订单ID
order_id = trade.order_id
# 获取实际成交信息
actual_price = trade.price
amount = trade.amount
security = trade.security
# 获取我们之前记录的预期价格
expected_price = g.order_expectations.get(order_id)
# 开始计算滑点
if expected_price:
# 滑点金额 = (实际价格 - 预期价格) * 数量
# 注意:
# 买入时(amount>0),实际价格通常高于预期,差值为正代表成本增加(滑点损失)
# 卖出时(amount<0),实际价格通常低于预期,差值为负代表收入减少(滑点损失)
# 为了统一表示“滑点造成的每股价差”,我们通常取绝对值或者根据方向计算
price_diff = actual_price - expected_price
# 计算总滑点成本(金额)
# 如果是买入:(实际 - 预期) * 正数量 = 正数 (代表多花的钱)
# 如果是卖出:(实际 - 预期) * 负数量 = 正数 (代表少赚的钱,即损失)
# 所以直接相乘即可得到滑点带来的“不利影响金额”
slippage_cost = price_diff * amount
log.info(f"【成交记录】ID: {trade_id}")
log.info(f" 标的: {security}")
log.info(f" 时间: {trade.time}")
log.info(f" 方向: {'买入' if amount > 0 else '卖出'}")
log.info(f" 数量: {amount}")
log.info(f" 预期价格: {expected_price:.4f}")
log.info(f" 实际成交价: {actual_price:.4f}")
log.info(f" 每股滑点: {abs(price_diff):.4f}")
log.info(f" 总滑点成本: {slippage_cost:.4f} 元")
log.info("-" * 30)
else:
log.info(f"【成交记录】{trade_id} (未找到预期价格记录,可能是非策略自动单)")
# 清理旧数据(可选,视策略逻辑而定)
g.order_expectations.clear()
关键点解析
-
get_trades()函数:- 这是获取成交详情的核心 API。
- 它返回一个字典,Key 是
trade_id,Value 是Trade对象。 Trade对象包含属性:trade.time: 交易时间trade.security: 标的代码trade.amount: 交易数量(正数为买,负数为卖)trade.price: 实际成交均价(已包含滑点)trade.order_id: 对应的订单 ID
-
滑点计算逻辑:
- 预期价:API 不会自动记录你“想”以多少钱买入。对于市价单,通常以“下单瞬间的最新价”作为预期;对于限价单,通常以“限价价格”作为预期。
- 全局变量
g:代码中使用g.order_expectations字典,在下单瞬间记录order_id和当时的last_price。 - 对比:在收盘后(
after_trading_end),通过trade.order_id找回当时的预期价,与trade.price做减法即可得出滑点。
-
set_slippage:- 为了在回测中看到滑点效果,必须在
initialize中设置滑点模型。代码中使用了FixedSlippage(0.02),这会导致买入价比基准价高 0.01,卖出价低 0.01(双边合计0.02,单边0.01),从而产生可计算的滑点。
- 为了在回测中看到滑点效果,必须在
Q&A
Q: 为什么 get_trades() 返回的 price 和我在行情软件上看到的 K 线收盘价不一样?
A: get_trades() 返回的是您的策略在回测引擎中撮合的实际成交价。这个价格受您设置的滑点模型(set_slippage)影响。如果您设置了滑点,成交价会比当时的行情价更差(买得更贵,卖得更便宜)。
Q: 如何区分这笔成交是买入还是卖出?
A: 查看 trade.amount 属性。如果 amount > 0,表示买入(做多);如果 amount < 0,表示卖出(做空或平仓)。
Q: 如果一个订单分多次成交(分笔成交),get_trades() 会怎么显示?
A: get_trades() 会返回多个 Trade 对象。每个 Trade 对象代表一次撮合结果,它们会有不同的 trade_id,但可能拥有相同的 order_id。您需要遍历所有 Trade 来获取完整的成交细节。