问题描述
先用get_bars取收盘价,再计算某个时间段的最高价、最低价。
有时显示没问题,有时就显示多位小数,例如 8.3699999999999992, 9.7599999999999998,本来的的价格分别是8.37和9.76。
请问是什么原因导致的,该如何调整语句?
解决方案
问题原因
您遇到的现象是计算机编程中非常经典的浮点数精度问题。在 Python(以及大多数编程语言)中,浮点数是采用 IEEE 754 标准的二进制小数来表示的。由于十进制中的某些小数(如 8.37)无法精确转换为有限位数的二进制小数,计算机只能用一个无限接近的二进制数来近似表示它。这就导致在计算或打印时,可能会出现类似 8.3699999999999992 这样的长串小数。
在 JoinQuant 中,get_bars 返回的价格数据底层是 numpy.float64 类型,在进行聚合计算(如求最高价、最低价、均价)时,这种底层二进制表示的微小误差就会显现出来。
解决方法
要解决这个问题,主要取决于您是需要继续参与计算,还是仅仅为了日志打印和展示。以下提供三种常用的调整方法:
方法一:使用 round() 函数(推荐用于继续计算)
如果您需要将处理后的价格继续用于下单逻辑(如 order_target_value)或比较大小,建议使用 Python 内置的 round() 函数将数值四舍五入到指定的小数位数(A股通常保留2位小数)。
# 假设 price 是您计算出的最高价或最低价
price = 8.3699999999999992
# 保留两位小数
clean_price = round(price, 2)
print(clean_price) # 输出: 8.37
方法二:使用字符串格式化(推荐用于日志打印)
如果您只是想在 log.info() 或 print() 中让显示结果更美观,可以直接使用字符串格式化,这不会改变变量本身的类型和精度,只改变显示效果。
price = 8.3699999999999992
# 方式1:使用 f-string (Python 3.6+ 支持,JoinQuant 回测环境支持)
log.info(f"当前最高价为: {price:.2f}") # 输出: 当前最高价为: 8.37
# 方式2:使用 format 方法
log.info("当前最高价为: {:.2f}".format(price))
# 方式3:使用传统 % 格式化
log.info("当前最高价为: %.2f" % price)
方法三:使用 Pandas/Numpy 自带的舍入方法(处理批量数据)
如果您是通过 get_bars(..., df=True) 获取了 DataFrame,并直接在 DataFrame 上进行列操作,可以使用 Pandas 的 round() 方法批量处理。
import pandas as pd
# 获取 DataFrame 格式的数据
df = get_bars('000001.XSHE', count=5, unit='1d', fields=['close'], df=True)
# 计算最高价并保留2位小数
max_close = round(df['close'].max(), 2)
log.info(f"区间最高收盘价: {max_close:.2f}")
总结建议
在量化策略编写中,价格比较和下单前,建议统一使用 round(price, 2) 处理一下计算得出的衍生价格,以避免因为 8.3699999999999992 < 8.37 这种微小精度误差导致 if 条件判断出现意料之外的结果。