🚀 AI 一键生成 joinquant 策略代码

JoinQuant 平台如何计算并应用市场广度指标(上涨下跌家数)?

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

问题描述

如何计算全市场的上涨和下跌家数指标?

解决方案

在聚宽(JoinQuant)平台中,计算全市场上涨和下跌家数(通常称为市场广度指标)的核心逻辑是:获取全市场所有股票列表,通过 get_current_data() 获取当天的行情快照,比较最新价(last_price)前收盘价(pre_close)

以下是具体的实现代码和详细说明。

核心逻辑步骤

  1. 获取股票列表:使用 get_all_securities(['stock']) 获取全市场股票代码。
  2. 获取行情快照:使用 get_current_data() 获取当前的停牌状态、最新价和前收盘价。
  3. 遍历统计
    • 剔除停牌股票(paused == True)。
    • 上涨:last_price > pre_close
    • 下跌:last_price < pre_close
    • 平盘:last_price == pre_close

策略代码实现

# -*- 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')
    
    # 设定定时运行,例如每天收盘前10分钟计算一次
    run_daily(calc_market_breadth, '14:50')

def calc_market_breadth(context):
    """
    计算全市场涨跌家数
    """
    # 1. 获取当前交易日上市的所有股票代码
    # date参数确保只获取当时还在上市的股票
    all_securities = get_all_securities(['stock'], date=context.current_dt)
    stock_list = list(all_securities.index)
    
    # 2. 获取当前时刻的行情快照数据
    # get_current_data 返回一个 dict,key 是股票代码,value 是对象
    current_data = get_current_data()
    
    # 初始化计数器
    up_count = 0      # 上涨家数
    down_count = 0    # 下跌家数
    flat_count = 0    # 平盘家数
    paused_count = 0  # 停牌家数
    
    # 3. 遍历统计
    for code in stock_list:
        # 获取该股票的数据对象
        data = current_data[code]
        
        # 如果股票停牌,通常不计入涨跌统计,或者单独统计
        if data.paused:
            paused_count += 1
            continue
            
        # 获取最新价和前收盘价
        # 注意:在回测中,last_price 在盘中代表当前分钟收盘价,收盘后代表当日收盘价
        price = data.last_price
        pre_close = data.pre_close
        
        # 比较价格
        if price > pre_close:
            up_count += 1
        elif price < pre_close:
            down_count += 1
        else:
            flat_count += 1
            
    # 4. 打印或记录结果
    log.info("=== 市场广度统计 (%s) ===" % context.current_dt)
    log.info("上涨家数: %d" % up_count)
    log.info("下跌家数: %d" % down_count)
    log.info("平盘家数: %d" % flat_count)
    log.info("停牌家数: %d" % paused_count)
    
    # 可以在回测图表中画出曲线
    record(up_count=up_count, down_count=down_count)

    # 5. (可选) 计算涨跌比 ADL 或 涨跌家数差
    # diff = up_count - down_count
    # ratio = up_count / down_count if down_count > 0 else 0

代码说明

  1. get_all_securities: 必须传入 date=context.current_dt,这样可以确保获取的是回测当时实际上市的股票列表,自动剔除已退市或未上市的股票,避免未来函数。
  2. get_current_data: 这是回测和模拟交易中获取“当前切片数据”最高效的API。它包含了 last_price(最新价)、pre_close(昨收价)和 paused(是否停牌)等关键字段。
  3. 停牌处理: 代码中通过 if data.paused: 剔除了停牌股票。在实际的市场情绪分析中,停牌股票通常不具备参考意义,因此将其排除。
  4. 性能提示: 虽然 Python 的 for 循环处理 4000+ 只股票在回测中速度尚可,但如果策略逻辑非常复杂,建议尽量减少循环内部的计算量。

Q&A

Q: 为什么使用 last_price 而不是 close
A: get_current_data() 返回的对象属性中,last_price 代表最新成交价。在盘中运行时(例如 10:00 运行),它代表 10:00 的价格;在收盘后运行时,它代表收盘价。这比使用 history 获取数据更符合“当前状态”的语义。

Q: 如何计算涨停和跌停家数?
A: get_current_data() 的返回对象中包含 high_limit(涨停价)和 low_limit(跌停价)。可以在循环中增加判断:

  • 涨停:data.last_price >= data.high_limit
  • 跌停:data.last_price <= data.low_limit

Q: 这个指标可以用来做什么?
A: 涨跌家数是衡量市场情绪的重要指标。

  • 当指数上涨但上涨家数很少时,说明是权重股拉升,市场赚钱效应不佳(二八现象)。
  • 当上涨家数远大于下跌家数时,说明市场整体情绪高涨。
  • 可以基于此构建 ADL(腾落指数)或 ARMS(阿姆氏指标)等广度指标。