问题描述
解决方案
在聚宽(JoinQuant)平台中,计算全市场上涨和下跌家数(通常称为市场广度指标)的核心逻辑是:获取全市场所有股票列表,通过 get_current_data() 获取当天的行情快照,比较最新价(last_price)与前收盘价(pre_close)。
以下是具体的实现代码和详细说明。
核心逻辑步骤
- 获取股票列表:使用
get_all_securities(['stock'])获取全市场股票代码。 - 获取行情快照:使用
get_current_data()获取当前的停牌状态、最新价和前收盘价。 - 遍历统计:
- 剔除停牌股票(
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
代码说明
get_all_securities: 必须传入date=context.current_dt,这样可以确保获取的是回测当时实际上市的股票列表,自动剔除已退市或未上市的股票,避免未来函数。get_current_data: 这是回测和模拟交易中获取“当前切片数据”最高效的API。它包含了last_price(最新价)、pre_close(昨收价)和paused(是否停牌)等关键字段。- 停牌处理: 代码中通过
if data.paused:剔除了停牌股票。在实际的市场情绪分析中,停牌股票通常不具备参考意义,因此将其排除。 - 性能提示: 虽然 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(阿姆氏指标)等广度指标。