🚀 AI 一键生成 joinquant 策略代码

如何使用 record 和 get_industry API 动态监控并可视化投资组合的行业分布?

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

问题描述

我想在回测中实时查看我的持仓在不同行业中的分布情况,应该怎么做?

解决方案

在JoinQuant回测中查看持仓的行业分布,主要思路是遍历当前账户的持仓列表,利用 get_industry API 获取每一只股票所属的行业(如申万一级行业),然后累加计算各行业的持仓市值及占比,最后通过 log.info 打印出来。

以下是一个完整的策略示例代码。该策略会随机买入几只股票构建持仓,并在每天收盘后计算并打印当前的行业分布情况。

核心步骤说明

  1. 获取持仓:通过 context.portfolio.positions 获取当前所有持仓对象。
  2. 查询行业:使用 get_industry(security_list) 批量查询持仓股票的行业信息(本例使用申万一级行业 sw_l1)。
  3. 统计计算:遍历持仓,累加相同行业的持仓市值。
  4. 输出日志:计算各行业占总资产的比例,并打印到日志中。

策略代码

# -*- 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')
    
    # 设定要操作的股票池(示例:包含银行、地产、白酒、医药等不同行业)
    g.security_pool = [
        '000001.XSHE', # 平安银行(银行)
        '000002.XSHE', # 万科A(房地产)
        '600519.XSHG', # 贵州茅台(食品饮料)
        '600276.XSHG', # 恒瑞医药(医药生物)
        '601318.XSHG'  # 中国平安(非银金融)
    ]
    
    # 每天开盘时运行买入逻辑(为了演示持仓)
    run_daily(buy_stocks, '09:30')
    
    # 每天收盘后运行行业分布统计
    run_daily(check_industry_distribution, '15:10')

def buy_stocks(context):
    """
    简单的买入逻辑,用于构建持仓以便演示行业统计
    """
    # 如果没有持仓,则平均买入股票池中的股票
    if len(context.portfolio.positions) == 0:
        cash_per_stock = context.portfolio.available_cash / len(g.security_pool)
        for stock in g.security_pool:
            order_value(stock, cash_per_stock)
            log.info("买入示例股票: %s" % stock)

def check_industry_distribution(context):
    """
    核心函数:统计并打印持仓行业分布
    """
    # 1. 获取当前所有持仓的股票代码
    # context.portfolio.positions 是一个字典,key是标的代码
    positions = context.portfolio.positions
    holding_list = [stock for stock in positions if positions[stock].total_amount > 0]
    
    if not holding_list:
        log.info("当前无持仓,无法统计行业分布。")
        return

    # 2. 获取持仓股票的行业信息
    # date=context.previous_date 确保获取的是回测当时的数据
    # 这里我们使用 'sw_l1' (申万一级行业) 作为分类标准,也可以换成 'jq_l1' (聚宽一级) 或 'zjw' (证监会行业)
    industry_info = get_industry(holding_list, date=context.previous_date)
    
    # 3. 初始化统计字典
    industry_stats = {} 
    total_holding_value = 0.0 # 统计持仓总市值
    
    # 4. 遍历持仓,累加各行业的市值
    for stock in holding_list:
        position = positions[stock]
        market_value = position.value # 该股持仓市值
        total_holding_value += market_value
        
        # 获取该股票的行业名称
        stock_ind_info = industry_info.get(stock)
        industry_name = '未知行业'
        
        if stock_ind_info and 'sw_l1' in stock_ind_info:
            industry_name = stock_ind_info['sw_l1']['industry_name']
        
        # 累加市值
        if industry_name in industry_stats:
            industry_stats[industry_name] += market_value
        else:
            industry_stats[industry_name] = market_value

    # 5. 计算占比并打印日志
    total_assets = context.portfolio.total_value # 总资产(包含现金)
    
    log.info("=" * 30)
    log.info("【日期】: %s" % context.current_dt.date())
    log.info("【总资产】: %.2f" % total_assets)
    log.info("【持仓市值】: %.2f (仓位: %.2f%%)" % (total_holding_value, (total_holding_value/total_assets)*100))
    log.info("--- 行业分布详情 (按持仓市值占比) ---")
    
    # 按市值从大到小排序
    sorted_stats = sorted(industry_stats.items(), key=lambda x: x[1], reverse=True)
    
    for ind_name, value in sorted_stats:
        # 计算该行业占总资产的比例
        ratio = (value / total_assets) * 100
        log.info("行业: %s | 市值: %.2f | 占比: %.2f%%" % (ind_name, value, ratio))
        
        # 如果你想在回测图表下方看到曲线变化,可以使用 record 函数
        # 注意:record 的 key 必须是英文,这里简单演示记录某个特定行业的占比
        # if ind_name == '银行I':
        #     record(Bank_Ratio=ratio)
            
    log.info("=" * 30)

常见问答 (Q&A)

Q1: get_industry 函数支持哪些行业分类标准?
A1: get_industry 支持多种分类标准,常用的包括:

  • sw_l1, sw_l2, sw_l3: 申万一、二、三级行业。
  • jq_l1, jq_l2: 聚宽一、二级行业。
  • zjw: 证监会行业分类。
    在代码中修改 industry_name = stock_ind_info['sw_l1']['industry_name'] 中的键名即可切换。

Q2: 为什么有时候 get_industry 返回的行业是“未知行业”?
A2: 这通常有两个原因:

  1. 该股票在查询日期已经退市或尚未上市。
  2. 该股票属于新股,行业分类数据尚未更新(通常有滞后)。
  3. 查询的基金(ETF/LOF)等标的,get_industry 主要针对股票,基金通常没有标准的申万行业分类。

Q3: 如何将这些数据画成图表而不是看日志?
A3: JoinQuant 的 record() 函数可以用来画图,但它只能画时间序列曲线(Line Chart),不能画饼图或柱状图。
如果你想看行业占比的变化曲线,可以在代码循环中调用 record(Industry_Name_En=ratio)。注意 record 的参数名必须是英文且不能包含特殊字符,你需要建立一个中文行业名到英文变量名的映射关系。