关于 dflow-kalshi-market-scanner
dflow-kalshi-market-scanner 是一项技能,通过 DFlow Metadata API 定义筛选并排序 Kalshi 市场的扫描方案。它涵盖快照查询和流式扫描,提供价格、成交量、价差、动量及结束时间等指标的方案。
主要功能
- 01命名筛选与排序方案
- 02价格及套利扫描
- 03成交量、价差和动量扫描
- 04REST 快照与 WebSocket 流
- 05市场、事件及系列查询
适用场景
- 01查找符合价格或价差条件的市场
- 02按成交量对市场排序
- 03实时监测价格变动
- 04比较同一事件中的不同结果
MCP 相关
它使用 https://pond.dflow.net/mcp 上的 DFlow docs MCP 查询 API 参数、分页方式和响应格式。扫描本身通过 REST 或 WebSocket 查询 DFlow Metadata API。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dflowprotocol/ dflow-skills --skill dflow-kalshi-market-scanner安装后在 Claude Code 中输入/dflow-kalshi-market-scanner确认已加载。
详细介绍
DFlow Kalshi Market Scanner
查找符合某项标准的 Kalshi 市场。此技能是一组命名的扫描(筛选并排序的方案),用于查询 DFlow Metadata API。
前提条件
- DFlow docs MCP(
https://pond.dflow.net/mcp)——按照仓库 README 安装。此技能提供方案;MCP 提供参考资料。通过search_d_flow/query_docs_filesystem_d_flow查找精确的查询参数、分页方式、响应结构,以及其他字段级信息——不要猜测。
范围
此处的所有扫描都通过 Metadata API(https://pond.dflow.net/resources/metadata-api)运行——REST 用于特定时间点的查询,WebSocket 用于连续数据流。你可以在任何地方调用两者:从命令行快速运行 curl,使用 Node/Python 脚本、cron 任务、后端服务,或代理浏览器 UI 的 Next.js 路由。
如果用户说“在我的终端里运行”,不要使用 dflow CLI——它没有发现类子命令。改为编写简短的 HTTP/WS 脚本来调用 Metadata API。
扫描器骨架
每次扫描都包含相同的四个步骤。围绕此模式构建——不要为每次扫描重新发明做法:
- 枚举全集——
GET /api/v1/markets(扁平)或GET /api/v1/events?withNestedMarkets=true(分组)。筛选status=active。持续翻页直到完成(请参阅{ markets, cursor }结构的分页注意事项)。只有当用户希望筛选出此刻可在 DFlow 上交易的市场时,才传入isInitialized=true(请参阅注意事项)。 - 获取每个市场的信号。 对于盘口顶层扫描,信号已包含在市场对象中——
yesBid/yesAsk/noBid/noAsk(4 位小数的概率字符串)、volume24hFp/volumeFp/openInterestFp(美元等值字符串)、closeTime(Unix 时间戳)——无需调用订单簿接口。对于动量,使用 K 线数据或prices/tradesWebSocket 通道。对于档位深度,使用/api/v1/orderbook/by-mint/{mint}。对于近期成交记录,使用/api/v1/trades或trades通道。 - 计算指标。
- 筛选并排序。 返回排名前 N 的结果(默认 10 个;如果用户想看更多,请询问)。
轮询与流式传输
根据用户意图选择合适的模式:
- 轮询(REST)——适用于用户需要快照的场景(“现在显示前 10 个”,“列出所有符合 X 的市场”)。如果用户希望数据保持最新,则按固定频率重新运行。
- 流式传输(WebSocket)——适用于用户希望在事件发生时采取行动的场景(“YES+NO 低于 1 美元时提醒我”,“标记一分钟内波动超过 5% 的任何市场”,“X 成交时交易”)。订阅相关通道(
prices、trades、orderbook),地址为wss://<host>/api/v1/ws,并在每次更新时计算指标。完整的流式处理机制(重新连接、退避、订阅生命周期)请交由dflow-kalshi-market-data处理。
精确的端点参数、通道载荷和分页方式 → docs MCP。
可提供的扫描
每项扫描都对应一个用户问题和一个指标。将指标代入扫描器骨架即可。
优先使用基于排名的筛选条件,而非固定数值阈值。 活跃市场的 Kalshi 成交量跨度超过 5 个数量级——任何硬编码的美元下限要么形同虚设(太低),要么会排除所有结果(太高),而且还会随着平台发展而失效。当扫描需要判断“活跃”“便宜”或“波动很大”时,应根据扫描结果计算阈值(当前全集的百分位数),而不是使用这里硬编码的数值。
仅在数值具有语义含义时使用固定数值——例如套利中的 YES + NO < $1.00(这是无套利不变量,而非可调参数),或 status=active(这是字段值,而非阈值)。如果用户提供了具体数值,就使用他们给出的数值。如果用户的说法隐含了一个你并不知道的阈值(如“成交量很大”“大幅波动的市场”),请询问用户——不要猜测。
1. 套利 — YES + NO < $1
“找出 YES 和 NO 两边合计买入价低于 1 美元的市场。”
- 指标:
parseFloat(yesAsk) + parseFloat(noAsk) < 1.00。这是语义阈值——无套利不变量;保持固定。 - 排序:价差(
1 - sum)从大到小。 - 跳过
yesAsk或noAsk任一为 null 的行(该方向没有挂单卖价,因此不是真正的套利机会)。
2. 长线 YES
“价格便宜且确实有交易的 YES。”
- 按
parseFloat(volume24hFp)降序排列,取活跃市场范围内的最高四分位作为“确实有交易”的池子(根据扫描结果计算第 75 百分位的截断值,不要硬编码金额)。 - 在该池子内,按
parseFloat(yesAsk)升序排列,并返回价格最低的前 N 项。如果用户给出上限(“低于 20¢”“低于 3¢”),就用用户指定的上限;否则只排序——不要凭空设定美分上限。“便宜”不是固定数值;什么算长线,取决于用户愿意承受多少。 - “预期回报最佳”的替代排序方式:
parseFloat(volume24hFp) / parseFloat(yesAsk)——同时满足交易活跃、价格便宜。如果用户的意思不明确,询问他们想要哪种排序。 - 没有成交量的低价市场是僵尸行情,不是长线机会。先按成交量排序,再看价格——这样才能过滤噪声。
3. 即将到期的近乎确定 YES
“YES 高于 97¢,且即将收盘——赚取 Theta。”
- 筛选:
parseFloat(yesAsk)高于用户指定的阈值。“近乎确定”是一种说法,不是具体数值——如果用户说 97¢,就用 0.97;如果说 99¢,就用 0.99。如果用户只说“近乎确定”,没有给出数值,就询问他们想要的标准(95¢?99¢?)。不要自行设定默认截断值。 - 排序:
closeTime升序——最先收盘的排在前面。 - 返回前 N 项。如果用户指定了时间范围(“48 小时以内”“本周”),则按此覆盖;不要自行设定默认时间范围。
4. 动量
“过去一小时内有哪些变动?” 或 “有变动时提醒我”
- 轮询:对每个市场调用
/api/v1/market/{ticker}/candlesticks(或/market/by-mint/{mint}/candlesticks),使用最小间隔,并比较最新收盘价与 N 分钟前的收盘价。此方式按市场逐一查询,成本较高——先按成交量对市场范围进行预筛选(复用扫描 #6 的排序,选取成交最活跃的前 N 个市场,再计算动量)。 - 流式:订阅
prices频道(all: true或指定 ticker 列表),并在内存中计算滚动百分比变化。“发生 X 时提醒我”这种场景用这种方式便宜得多;要实现“市场变动超过 N% 时交易”,也可以这样接入dflow-kalshi-trading(将符合条件的市场交给它)。 - 按用户指定时间范围内的绝对百分比变化(双向)或带符号的百分比变化(有方向)排序——如果用户未指定,默认 60 分钟,但不设默认百分比阈值。返回前 N 项。如果用户说“变动超过 N%”,则由用户提供 N。
5. 买卖价差最大
“低效市场——做市或避开。”
- 指标:
parseFloat(yesAsk) - parseFloat(yesBid)(NO 方向对称)。 - 排序:价差降序。
- 来源:市场对象;无需额外调用。
6. 成交量最高
“哪里最热闹?”
- 指标:
parseFloat(volume24hFp)(24 小时美元等值),或者对截止时间之后的/api/v1/trades结果求和(盘中)。若要实时数据流,订阅trades频道并在滚动时间窗口内聚合。 - 排序:成交量降序。
7. 最快收盘
“Theta 倒计时。”
- 指标:
closeTime - now。 - 排序:升序。
- 最适合与扫描 3(“近乎确定且即将收盘”)或扫描 6(“交易活跃且即将收盘”)组合使用。
8. 按事件和系列扫描
“该事件所有结果中 YES 价格最低的选项”“互斥的选项加起来是否大于 1?”
- 单个事件内(例如,“美联储加息 X 个基点”,每个结果对应一个选项):调用
GET /api/v1/event/{eventTicker}?withNestedMarkets=true,然后汇总嵌套市场(min(yesAsk)、Σ yesAsk等)。事件是扫描单一赢家的自然范围。 - 跨系列:调用
GET /api/v1/series/{seriesTicker}及其事件,然后汇总。 - 系列或事件没有
mutuallyExclusive标记。 只有当各结果构成同一未来情形的完整划分(必定发生其中一个,且恰好只能发生一个)时,汇总各结果的 YES 才有意义。这需要根据事件/系列标题和合约条款作出判断,不能通过查询字段得出。如有疑问,应展示数字并向用户说明这一假设。
单项查询(N=1)
当用户已经指定一个市场时,跳过骨架查询:
- 按交易代码:
GET /api/v1/market/{ticker}(用单数market,不是复数)。 - 按结果铸币地址:
GET /api/v1/market/by-mint/{mint}(用斜杠,不是连字符)。 - 按事件交易代码:
GET /api/v1/event/{eventTicker}?withNestedMarkets=true(用单数event)。 - 自由文本:
GET /api/v1/search(将自然语言映射到事件/市场)。
复数形式(/markets、/events)是列表端点,使用 ?cursor=&limit= 分页。单数形式是按 ID 进行的单项查询。混淆两者会导致 404 错误。
该向用户询问什么(以及不该问什么)
查询形式——明确时自行判断,不明确时进行确认:
- 要执行哪种扫描(或用户用自然语言表达的意图,你可以据此映射到一种扫描)。
- 用户提供的阈值——原样使用。 如果用户说“> 5%”“低于 2¢”或“本周”,就使用这些数值。否则,使用上述各扫描按排名设定的默认值(如成交量前四分之一);不要自行提出固定数值阈值。如果用户的说法隐含了扫描中未定义的阈值(如“大幅波动”“成交量可观”),应询问用户,不要猜测数值。
- 轮询还是流式传输——如果用户的意图听起来像“现在给我看看”,就用 REST;如果听起来像“提醒我/在……时采取行动”,就用 WebSocket。
- Top-N(默认值为 10)。
基础设施——始终询问,绝不自行推断:
- DFlow API 密钥——仅用于脚本中的发现/HTTP 部分;CLI shell 调用会自行进行身份验证(参见下方“两种身份验证路径”注意事项)。用简洁、中立的问题询问:“对于扫描器/发现部分,你有 DFlow API 密钥吗?” 不要预设密钥存放在哪里——类似 “你把它放在环境变量里了吗?” 或 “
DFLOW_API_KEY已设置了吗?” 的说法,会引导用户采用他们没有要求的环境变量默认设置。应明确提供选项;不要默默回退到环境变量或开发环境。所有地方都使用同一个 DFlow 密钥——同一个x-api-key可解锁 Trade API 和 Metadata API,以及 REST 和 WebSocket。如果有 → 使用生产环境主机(REST:https://prediction-markets-api.dflow.net,WS:wss://prediction-markets-api.dflow.net/api/v1/ws),并在每个请求(REST 和 WS 升级请求)中附上x-api-key。如果没有 → 使用开发环境主机(https://dev-prediction-markets-api.dflow.net、wss://dev-prediction-markets-api.dflow.net/api/v1/ws),这些主机有速率限制;引导用户访问https://pond.dflow.net/get-started/api-key获取生产密钥。生成脚本时,应在启动时记录最终使用的主机和密钥是否存在(Using prod Metadata API/Using dev Metadata API — rate-limited),让用户无需翻阅代码即可了解当前使用的环境。
不要询问:
- RPC、钱包、签名——此技能只读取公开元数据,不进行交易。
- 结算铸币地址/滑点/费用——这些属于交易环节。如果用户转而想要在你展示的市场下单,请转交给
dflow-kalshi-trading。
注意事项(文档 MCP 不会主动告知的内容)
- 最优买卖价位于市场对象上。
yesBid/yesAsk/noBid/noAsk已经在其中。不要仅仅为了获取最优价格而循环调用订单簿端点。 - **价格和成交量是市场级的;交易则按结算通道划分。**每个已初始化的市场在
market.accounts下都有 USDC 和 CASH 两种结算通道,各自拥有自己的marketLedger/yesMint/noMint。扫描结果中的yesBid/yesAsk/volume24hFp来自共享的 Kalshi 订单簿,并不能告诉你会通过哪个结算通道交易。移交给dflow-kalshi-trading时,传入市场代码,由交易步骤选择结算通道(默认:USDC)。不要在扫描输出中悄悄预先选择结算通道——如果这样做了,请明确说明。 - 订单簿只返回买单阶梯(
yes_bids、no_bids)。最优 YES 卖价 的计算方式是:1 - max(no_bids keys)(价格为p的 NO 买单对应价格为1-p的 YES 卖单)。只有用户需要查看阶梯深度时,这一点才重要。 - **两种价格刻度。**市场/订单簿价格是 4 位小数的概率字符串(
"0.4200")。交易价格(REST 和trades通道)是 0–10000 的整数,同时带有yes_price_dollars/no_price_dollars字符串字段。计算前先统一格式。 - **
market.title通常是事件级而非市场级标题。**在多结果事件中(例如每位候选人对应一个市场、每个加息幅度区间对应一个市场、每支队伍获胜对应一个市场等),该事件下的每个市场都共享同一个title——描述具体结果的文字位于yesSubTitle(和noSubTitle)中。如果扫描输出只打印title,相邻行看起来会完全相同,用户无法分辨具体结果。应显示title — yesSubTitle(如果yesSubTitle为 null/空字符串,则只显示title;简单的二元市场会出现这种情况)。将市场移交给交易提示词时也一样:对于多结果事件,只显示title的“买入 X 的 YES”确认信息会有歧义。 - **成交量字段——字符串、美元等值,且没有
volume24h。**市场对象包含volume(整数,累计原始单位)、volumeFp(字符串,累计美元等值)和volume24hFp(字符串,24 小时美元等值)。不存在volume24h字段。比较前始终对*Fp字段使用parseFloat。openInterest/openInterestFp的结构也相同。 - **
isInitialized筛选条件。**短时长市场(例如 15 分钟加密货币市场)在 Kalshi 上通常已处于活跃状态,但尚未在 DFlow 上代币化。如果不使用此筛选条件,扫描会包含这些市场;使用isInitialized=true时,则只包含目前可在 DFlow 上交易的市场。通常应设为true。 - **买单/卖单为 null 的情况。**流动性差的市场,其最优买卖价字段可能为 null。所有读取这些字段的扫描都必须跳过 null 值,不能将其视为零。
- 维护时段——Kalshi 每周四美国东部时间凌晨 3:00–5:00 离线。此时段内,最优买卖价和成交量字段可能过时或缺失;WS 更新也可能停止。如果扫描结果为空或明显异常,原因就在这里。
- 分页结构。
/markets(和/events)返回{ markets: [...], cursor: <number> }。首次调用时省略cursor或传入cursor=0,两者等效。返回的cursor是下一页的偏移量(= 已读取的行数);下一次调用时将其作为?cursor=N传回。当markets.length < limit时结束(标准做法);检查next === cursor以确保安全虽然无妨,但并非必要。limit上限为 255——limit=256或更大时会返回 HTTP 400"number too large to fit in target type"(后端使用u8)。文档将200作为保守默认值;实际最大值为255。大型扫描若跳过分页,会悄悄漏掉匹配项。 - **WebSocket 的
all: true会产生大量数据。**在繁忙通道(尤其是prices)上订阅all: true,会流式传输所有市场的每次更新。如果扫描只关注一组已知市场,优先使用代码列表;只有扫描确实要覆盖整个市场范围时才使用all: true。 - **“扫描后买入”应是一个交互式脚本,而不是两个独立产物。**当用户想从命令行查找市场并随后根据结果采取行动时,正确的输出应是一个脚本:扫描市场、打印排序后的列表、提示用户选择一个(或逐行确认 y/N),然后调用
dflow trade。不要编写非交互式扫描脚本,再让用户“选一个并自行运行dflow trade”——这会迫使他们切换上下文并手动复制标识符。在 Node 中使用readline提示,或在 Python 中使用input()都可以。对于完全自动化的流程(无需逐行提示),仍应使用单个脚本,并事先明确说明“不需确认”的行为。 - **移交给
dflow trade时的结构。**扫描结果是市场对象,不是可直接传给 CLI 的调用参数。CLI 的--market标志需要的是market.accounts[<settlementMint>].marketLedger中的marketLedger——不是market.ticker,不是yesMint/noMint,也不是顶层的 mint。除非用户指定 CASH,否则默认使用 USDC 结算通道(参见上方“价格和成交量是市场级的;交易则按结算通道划分”这一注意事项)。买入 YES 和 NO 时使用的是同一个marketLedger——通过--side yes|no选择方向,而不是换一个--market。完整的 CLI 参数结构(从结算 mint 查找marketLedger、原子单位换算、优先费标志、“买入 N 份完整合约”的惯用写法),请参阅dflow-kalshi-trading。不要在这里重新设计。 - **混合接口脚本:两种认证路径,而不是一种。**此技能与
dflow-kalshi-trading配合时,典型脚本结构是通过 Metadata API 发现市场,然后调用dflow trade执行交易。这两步分别独立认证:- 发现(Metadata API HTTP)——直接使用
fetch/curl。脚本的环境变量或配置中需要 DFlow API 密钥(参见 #5)。没有密钥 → 使用开发主机,且会受到速率限制。 - 执行(调用
dflow trade)——使用 CLI 通过dflow setup保存的配置(密钥、钱包、RPC)。脚本无需为 CLI 调用传入任何内容。 当用户说“CLI 已经配置好了”时,这只涵盖了执行环节,而不包括发现环节。询问 API 密钥时,应表述为*“扫描/发现环节需要用到,你有 DFlow API 密钥吗?”,而不是“光有 CLI 不够,你还需要另一个密钥。”*两者相互独立:一个 DFlow 密钥,两个接入点。
- 发现(Metadata API HTTP)——直接使用
遇到不确定的情况时
对于以上未涵盖的内容——完整参数列表、分页令牌、响应架构、WS 重新连接语义、较少见的筛选条件(体育、标签、类别、系列搜索)、K 线间隔——请查询 docs MCP(search_d_flow、query_docs_filesystem_d_flow)。不要猜测。
相关技能
当用户从发现转向行动时,请移交给:
dflow-kalshi-trading— 实际买入/卖出/赎回你在此处发现的市场。dflow-kalshi-portfolio— 查看他们的持仓和盈亏。dflow-kalshi-market-data— 在“指定名称扫描”场景之外获取通用的实时订单簿/交易/价格流(重新连接模式、完整载荷架构、游戏中实时数据)。dflow-proof-kyc— 验证钱包,以便它能实际买入你展示的内容。