🚀 AI 一键生成 joinquant 策略代码

如何在 JoinQuant 策略中实时计算每日盈亏(P&L)等自定义指标?

joinquant | 发布时间: 2025/12/18
以下内容由 EasyQuant 生成。

问题描述

怎样在策略运行时计算并记录每天的盈亏金额?

解决方案

在聚宽(JoinQuant)平台中,要计算并记录每天的盈亏金额,核心逻辑是利用 context.portfolio.total_value(当前总资产)减去前一交易日的总资产。

我们可以利用全局变量 g 来存储“昨日总资产”,并在每天收盘后(after_trading_end)进行计算和记录。

以下是具体的实现步骤和完整代码示例。

实现思路

  1. 初始化:在 initialize 函数中,定义一个全局变量 g.last_total_value,初始值为启动资金(context.portfolio.starting_cash)。
  2. 每日计算:在每天收盘后(建议使用 after_trading_end 函数),获取当前的 context.portfolio.total_value
  3. 计算差额当日盈亏 = 当前总资产 - 昨日总资产
  4. 记录数据:使用 record() 函数将盈亏金额绘制在回测图表上,方便直观查看。
  5. 更新状态:计算完成后,将 g.last_total_value 更新为当前总资产,供第二天使用。

完整策略代码

# -*- coding: utf-8 -*-
# 导入函数库
from jqdata import *

def initialize(context):
    # 设定沪深300作为基准
    set_benchmark('000300.XSHG')
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    
    ### 股票相关设定 ###
    # 股票类每笔交易时的手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 定义一个全局变量, 保存要操作的股票
    g.security = '000001.XSHE'
    
    # 【关键步骤1】初始化昨日总资产,初始值为启动资金
    g.last_total_value = context.portfolio.starting_cash
    
    # 设置交易函数,每天开盘时运行
    run_daily(market_open, time='09:30')
    
    # 【关键步骤2】设置收盘后处理函数,用于计算当日盈亏
    # after_trading_end 会在每天 15:30 运行
    run_daily(after_trading_end, time='after_trading_end')

def market_open(context):
    # 一个简单的买入策略示例,用于产生盈亏波动
    if g.security not in context.portfolio.positions:
        # 如果没有持仓,用一半资金买入
        order_value(g.security, context.portfolio.available_cash * 0.5)
    else:
        # 如果有持仓,且盈利超过5%则卖出(仅为演示产生交易)
        cost = context.portfolio.positions[g.security].avg_cost
        price = context.portfolio.positions[g.security].price
        if price > cost * 1.05:
            order_target(g.security, 0)

def after_trading_end(context):
    # 【关键步骤3】获取当前总资产(现金 + 持仓市值)
    current_total_value = context.portfolio.total_value
    
    # 【关键步骤4】计算当日盈亏金额
    # 当日盈亏 = 今日总资产 - 昨日总资产
    daily_pnl = current_total_value - g.last_total_value
    
    # 【关键步骤5】使用 record 函数记录,这样回测结束后可以在图表中看到曲线
    # key='daily_pnl' 是图例名称,value 是具体的数值
    record(daily_pnl=daily_pnl)
    
    # 打印日志,方便在日志区查看具体数值
    log.info("日期: %s, 当日盈亏: %.2f, 当前总资产: %.2f" % (context.current_dt.date(), daily_pnl, current_total_value))
    
    # 【关键步骤6】更新昨日总资产,为下一个交易日做准备
    g.last_total_value = current_total_value

代码解析

  1. g.last_total_value: 这是一个全局变量。在策略第一天运行前,它等于初始资金。
  2. after_trading_end: 这个函数在每天交易结束后(15:30)调用。此时当天的收盘价已确定,计算出的总资产是准确的。
  3. record(daily_pnl=daily_pnl): 这个函数非常重要。它会在回测结果页面的收益曲线下方增加一个新的图表区域,专门显示 daily_pnl 的走势。
  4. 逻辑闭环: 每天计算完盈亏后,必须执行 g.last_total_value = current_total_value,这样明天的计算基准才是今天的收盘资产。

Q&A

Q: 为什么要在 after_trading_end 中计算,而不是 handle_data
A: handle_datamarket_open 通常在盘中运行。盘中价格在不断跳动,计算出的盈亏是浮动的。而 after_trading_end 在收盘后运行,此时使用的是当天的收盘价(Close),计算出的盈亏是当天的最终结算盈亏,更加准确且具有统计意义。

Q: 如果我在盘中出入金(inout_cash),这个计算逻辑还准确吗?
A: 如果策略中有使用 inout_cash 进行出入金操作,上述简单逻辑会将出入金视为盈亏。如果需要剔除出入金影响,公式应修改为:
当日盈亏 = 当前总资产 - 昨日总资产 - 当日出入金总额
可以通过 context.portfolio.inout_cash 获取累计出入金,通过记录昨日累计出入金来计算当日变化量。

Q: record 函数记录的数据在哪里看?
A: 回测结束后,在收益率曲线图的下方,会多出一个坐标轴或图表区域,显示你自定义记录的 daily_pnl 曲线。同时,你也可以通过 get_backtest(id).get_records() 在研究环境中提取这些数据。