🚀 AI 一键生成 joinquant 策略代码

JoinQuant 聚宽量化策略:资金平分买入与“最新价格非法: nan”错误解析

joinquant | 发布时间: 2026/5/27
以下内容由 EasyQuant 生成。

问题描述

将资金平分买入股票池所有股票怎么实现?

这样做对不对呢??


# 取得当前的现金
buy_count=len(g.securitys) #获得指数基金个数
print(buy_count)
cash = context.portfolio.available_cash
print(cash)
buy_cash=cash/buy_count #将资金平分将指数基金买入
print(buy_cash)
holdings=set(context.portfolio.positions.keys())
print(holdings)

# 如果ma50高出MA200平均, 则全仓买入
if (MA50 > MA200) and (cash > 0):
for security in g.securitys:
if security not in holdings: #如果当前持仓中并没有该基金,则买入
# 记录这次买入
log.info("50日均线上穿200日均线, 买入 %s" % (security))
# 用平分的 cash 买入股票
order_value(security,buy_cash)
然后日志里有时候会打印出这样一句话:

security: 588000.XSHG 取到的最新价格非法: nan

这个588000.XSHG是 股票列表里最后一个指数基金,每次平分完资金买入前3个没有问题,一到这个就会出现以上这句话的错误提示,,这是为啥????

解决方案

1. 为什么会出现“取到的最新价格非法: nan”?\n\n在 JoinQuant 回测中,出现 security: 588000.XSHG 取到的最新价格非法: nan 这个错误,通常是因为在当前的回测时间点,该标的还没有上市,或者处于长期停牌/退市状态。\n\n588000.XSHG 是华夏科创50ETF,它的上市日期是 2020年11月16日。如果你的回测开始时间早于这个日期,系统在尝试获取它的最新价格时就会返回 nan(Not a Number,非数字),从而导致下单函数 order_value 报错。\n\n### 2. 你的代码逻辑对不对?\n\n你的大体思路(获取可用资金 -> 除以标的数量 -> 循环买入)是正确的,但在实际量化回测中存在几个需要优化的细节:\n\n1. 未过滤未上市/停牌标的:正如你遇到的错误,直接遍历 g.securitys 会把未上市或停牌的标的算进去,导致报错。\n2. 平分资金的分母不对:你用的是 len(g.securitys)(总标的数)作为分母,但实际上你只买入未持仓的标的。如果股票池有5只,你已经持有了2只,剩下的资金应该平分给剩下的3只,而不是除以5。\n3. 未预留手续费:把 available_cash 100% 满仓分配,在实际下单时,由于需要扣除交易佣金和印花税,最后一只股票可能会因为资金不足而买入失败(或者买入数量少于预期)。通常建议乘以 0.99 预留一点手续费空间。\n\n### 3. 正确的“资金平分买入”实现方案\n\n为了解决上述问题,建议在买入前使用 get_current_data() 检查标的的状态(是否停牌、价格是否为 nan),并动态计算需要买入的标的数量。\n\n优化后的代码如下:\n\npython\nimport math\n\ndef handle_data(context, data):\n # 假设 MA50 和 MA200 已经计算好\n # MA50 = ...\n # MA200 = ...\n \n if MA50 > MA200:\n # 1. 获取当前市场数据,用于过滤停牌和未上市标的\n current_data = get_current_data()\n \n # 2. 筛选出当前可以交易的标的(未停牌 且 价格不是 nan)\n valid_securities = []\n for sec in g.securitys:\n if not current_data[sec].paused and not math.isnan(current_data[sec].last_price):\n valid_securities.append(sec)\n \n # 3. 找出当前未持仓,需要买入的标的\n holdings = set(context.portfolio.positions.keys())\n buy_list = [sec for sec in valid_securities if sec not in holdings]\n \n # 4. 执行平分买入\n cash = context.portfolio.available_cash\n if len(buy_list) > 0 and cash > 0:\n # 将可用资金平分给需要买入的标的,乘以 0.99 预留手续费,防止最后下单资金不足\n buy_cash = (cash / len(buy_list)) * 0.99 \n \n for security in buy_list:\n log.info(\"50日均线上穿200日均线, 买入 %s\" % (security))\n order_value(security, buy_cash)\n\n\n### 4. 进阶方案:使用目标价值下单 (等权重持仓)\n\n如果你希望股票池里的所有股票始终保持等权重持仓(即每只股票占总资产的比例相同),使用 order_target_value 会更加优雅。这种方式不仅能处理买入,还能在调仓时自动处理多退少补:\n\npython\ndef handle_data(context, data):\n if MA50 > MA200:\n current_data = get_current_data()\n valid_securities = [sec for sec in g.securitys if not current_data[sec].paused and not math.isnan(current_data[sec].last_price)]\n \n if len(valid_securities) > 0:\n # 计算每只标的应该占有的目标价值(总资产 / 有效标的数量)\n # 同样预留一点点现金作为手续费缓冲\n target_value = (context.portfolio.total_value / len(valid_securities)) * 0.99\n \n for security in valid_securities:\n # order_target_value 会自动计算差额:\n # 如果未持仓,则买入 target_value 的金额\n # 如果已持仓但价值不够,则补仓;如果价值超了,则减仓\n order_target_value(security, target_value)\n log.info(\"调整 %s 仓位至目标价值: %.2f\" % (security, target_value))\n\n\n总结:遇到 nan 错误时,首先检查回测时间段内该标的是否已经上市。在编写批量买入逻辑时,务必养成过滤停牌股 (paused)过滤无效价格 (isnan) 的好习惯。