dflow-kalshi-market-scanner

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

DFlow Kalshi Market Scanner

DFlow Kalshi 市场扫描器

Find Kalshi markets that match a criterion. This skill is a set of named scans (filter-and-rank recipes) over the DFlow Metadata API.
查找符合特定条件的Kalshi市场。本技能是基于DFlow Metadata API的一组命名扫描规则(过滤与排序方案)。

Prerequisites

前置条件

  • DFlow docs MCP (
    https://pond.dflow.net/mcp
    ) — install per the repo README. This skill is the recipe; the MCP is the reference. Look up exact query params, pagination, response shapes, and anything else field-level via
    search_d_flow
    /
    query_docs_filesystem_d_flow
    — don't guess.
  • DFlow文档MCP
    https://pond.dflow.net/mcp
    )——请按照仓库README中的说明安装。本技能是执行方案,MCP是参考依据。如需查询精确的请求参数、分页规则、响应结构及其他字段级信息,请使用
    search_d_flow
    /
    query_docs_filesystem_d_flow
    ——请勿自行猜测。

Surface

使用场景

All scans here run against the Metadata API (
https://pond.dflow.net/build/metadata-api
) — REST for point-in-time queries, WebSockets for continuous streams. You can call both from anywhere: a quick
curl
from the command line, a Node/Python script, a cron job, a backend service, or a Next.js route proxying a browser UI.
If the user says "run this from my terminal", don't reach for the
dflow
CLI
— it has no discovery subcommands. Write a short HTTP/WS script that hits the Metadata API instead.
所有扫描均基于Metadata API
https://pond.dflow.net/build/metadata-api
)运行——REST接口用于时点查询,WebSocket用于持续流式推送。你可以在任意环境调用这两种接口:比如命令行中快速执行
curl
命令、Node/Python脚本、定时任务、后端服务,或是Next.js路由代理的浏览器UI。
如果用户要求“在我的终端运行”,不要使用
dflow
CLI
——它没有发现类子命令。请编写一个简短的HTTP/WS脚本直接调用Metadata API。

The scanner skeleton

扫描框架

Every scan is the same four steps. Build around this pattern — don't reinvent it per scan:
  1. Enumerate the universe
    GET /api/v1/markets
    (flat) or
    GET /api/v1/events?withNestedMarkets=true
    (grouped). Filter to
    status=active
    . Page through until done (see the pagination gotcha for the
    { markets, cursor }
    shape). Pass
    isInitialized=true
    only if the user wants markets tradable on DFlow right now (see Gotchas).
  2. Grab the per-market signal. For top-of-book scans the signal is already on the market object —
    yesBid
    /
    yesAsk
    /
    noBid
    /
    noAsk
    (4-decimal probability strings),
    volume24hFp
    /
    volumeFp
    /
    openInterestFp
    (dollar-equivalent strings),
    closeTime
    (unix) — no orderbook call needed. For momentum use candlesticks or the
    prices
    /
    trades
    WebSocket channels. For ladder depth use
    /api/v1/orderbook/by-mint/{mint}
    . For recent prints use
    /api/v1/trades
    or the
    trades
    channel.
  3. Compute the metric.
  4. Filter and rank. Return the top-N (default 10, ask if the user wants more).
所有扫描均遵循相同的四个步骤。请围绕该模式构建——请勿为每个扫描重新发明流程:
  1. 枚举全量市场——调用
    GET /api/v1/markets
    (扁平结构)或
    GET /api/v1/events?withNestedMarkets=true
    (按事件分组)。过滤条件设置为
    status=active
    。分页遍历直到完成(注意
    { markets, cursor }
    结构的分页陷阱)。仅当用户希望获取当前可在DFlow上交易的市场时,才传入
    isInitialized=true
    (详见注意事项)。
  2. 获取单市场信号。对于最优报价扫描,信号已包含在市场对象中——
    yesBid
    /
    yesAsk
    /
    noBid
    /
    noAsk
    (4位小数概率字符串)、
    volume24hFp
    /
    volumeFp
    /
    openInterestFp
    (美元等价金额字符串)、
    closeTime
    (unix时间戳)——无需调用订单簿接口。对于动量扫描,使用K线数据或
    prices
    /
    trades
    WebSocket频道。对于订单簿深度,使用
    /api/v1/orderbook/by-mint/{mint}
    。对于最新成交记录,使用
    /api/v1/trades
    trades
    频道。
  3. 计算指标
  4. 过滤与排序。返回排名前N的结果(默认10个,若用户需要更多可询问)。

Polling vs streaming

轮询 vs 流式推送

Pick the mode that fits the intent:
  • Polling (REST) — right when the user wants a snapshot ("show me the top 10 right now", "list all markets with X"). Re-run on a cadence if they want it fresh.
  • Streaming (WebSocket) — right when the user wants to act on an event as it happens ("alert me when YES+NO drops below $1", "flag any market that moves > 5% in a minute", "trade when X trades"). Subscribe to the relevant channel (
    prices
    ,
    trades
    ,
    orderbook
    ) at
    wss://<host>/api/v1/ws
    and compute the metric on each update. For the full streaming plumbing (reconnection, backoff, subscription lifecycle), hand off to
    dflow-kalshi-market-data
    .
Exact endpoint params, channel payloads, and pagination → docs MCP.
根据用户意图选择合适的模式:
  • 轮询(REST)——适用于用户需要即时快照的场景(“现在给我显示排名前10的市场”“列出所有符合X条件的市场”)。若用户需要实时更新,可按一定频率重新执行。
  • 流式推送(WebSocket)——适用于用户需要在事件发生时即时响应的场景(“当YES+NO价格低于1美元时提醒我”“标记任何1分钟内波动超过5%的市场”“当X成交时执行交易”)。在
    wss://<host>/api/v1/ws
    订阅相关频道(
    prices
    trades
    orderbook
    ),并在每次更新时计算指标。如需完整的流式处理机制(重连、退避、订阅生命周期),请移交至
    dflow-kalshi-market-data
    处理。
精确的接口参数、频道负载及分页规则请参考文档MCP。

Scans to offer

可提供的扫描类型

Each scan = a user question + a metric. Plug the metric into the skeleton.
Prefer rank-based filters over fixed numeric thresholds. Kalshi volume alone spans 5+ orders of magnitude across active markets — any hardcoded dollar floor is either a pass-through (too low) or excludes everything (too high), and it drifts as the platform grows. When a scan needs "busy" or "cheap" or "moved a lot," compute the cutoff from the scan result (percentile of the current universe), not from a number baked in here.
Only use a fixed number when it's semantic — e.g.
YES + NO < $1.00
for arbitrage (that's the no-arb invariant, not a tunable), or
status=active
(a field value, not a threshold). If the user supplies a specific number, use theirs. If the user's phrasing implies a threshold you don't have ("serious volume", "big movers"), ask them — don't guess.
每种扫描对应一个用户问题及一个指标。将指标代入上述框架即可。
优先使用基于排名的过滤,而非固定数值阈值。 Kalshi各活跃市场的交易量跨度可达5个数量级以上——任何硬编码的金额下限要么过于宽松(几乎无过滤效果),要么过于严苛(排除所有市场),且会随着平台发展逐渐失效。当扫描需要“活跃”“低价”“大幅波动”等条件时,请根据扫描结果计算截断值(当前全量市场的百分位数),而非使用硬编码数值。
仅当阈值具有语义明确性时才使用固定数值——例如套利场景中的
YES + NO < $1.00
(这是无套利不变量,而非可调参数),或
status=active
(字段值,而非阈值)。若用户提供具体数值,则使用用户指定的值。若用户表述隐含未定义的阈值(“大额交易量”“大幅波动”),请询问用户——请勿自行猜测。

1. Arbitrage —
YES + NO < $1

1. 套利——
YES + NO < $1

"Find markets where I can buy both sides for under a dollar."
  • Metric:
    parseFloat(yesAsk) + parseFloat(noAsk) < 1.00
    . Semantic threshold — the no-arb invariant; keep this one fixed.
  • Rank: largest gap (
    1 - sum
    ) descending.
  • Skip rows where either
    yesAsk
    or
    noAsk
    is null (no resting ask on that side — not a real arb).
“查找可以低于1美元价格同时买入双边合约的市场。”
  • 指标:
    parseFloat(yesAsk) + parseFloat(noAsk) < 1.00
    。这是语义明确的阈值——无套利不变量,请保持固定。
  • 排序:按
    1 - sum
    (价差)降序排列。
  • 跳过
    yesAsk
    noAsk
    为null的行(对应单边无挂单报价——不属于真正的套利机会)。

2. Long-shot YES

2. 低价冷门YES合约

"Cheap YES that's actually trading."
  • Rank by
    parseFloat(volume24hFp)
    descending, take the top quartile of the active universe as the "actually trading" pool (compute the 75th-percentile cutoff from the scan, don't hardcode a dollar figure).
  • Within that pool, sort by
    parseFloat(yesAsk)
    ascending and return the bottom-N cheapest. If the user supplies a cap ("under 20¢", "under 3¢") use theirs; otherwise rank-only — don't invent a cents ceiling. "Cheap" isn't a fixed number; what counts as a long-shot depends on how much the user is willing to tolerate.
  • Alternate rank for "best expected payoff":
    parseFloat(volume24hFp) / parseFloat(yesAsk)
    — busy and cheap at once. Ask the user which they want if ambiguous.
  • A cheap market with no volume is a zombie ticker, not a long-shot. Volume-rank first, then look at price — that's the order that filters noise.
“有交易量的低价YES合约。”
  • 先按
    parseFloat(volume24hFp)
    降序排列,选取活跃市场的前四分之一作为“有实际交易”的池(根据扫描结果计算75百分位数截断值,请勿硬编码金额)。
  • 在该池中,按
    parseFloat(yesAsk)
    升序排列,返回排名后N的最低价合约。若用户提供上限(“低于20美分”“低于3美分”)则使用该值;否则仅按排名筛选——请勿自行设定价格上限。“低价”并非固定数值,冷门机会的定义取决于用户的风险承受能力。
  • 另一种排序方式为“最佳预期收益”:
    parseFloat(volume24hFp) / parseFloat(yesAsk)
    ——兼顾活跃度与低价。若用户表述模糊,请询问用户偏好哪种排序方式。
  • 无交易量的低价市场属于僵尸标的,而非冷门机会。应先按交易量排序,再筛选价格——此顺序可过滤无效噪声。

3. Near-certain short-dated YES

3. 短期大概率YES合约

"YES above 97¢ closing soon — grind the theta."
  • Filter:
    parseFloat(yesAsk)
    above a user-supplied threshold. "Near-certain" is a phrase, not a number — if the user says 97¢, use 0.97; if they say 99¢, use 0.99. If they just say "near-certain" with no number, ask them what bar they want (95¢? 99¢?). Don't invent a default cutoff.
  • Rank:
    closeTime
    ascending — soonest-to-close first.
  • Return top-N. If the user specifies a window ("under 48h", "this week"), apply that as an override; don't invent a default window.
“价格高于97美分且即将到期的YES合约——赚取时间价值。”
  • 过滤条件:
    parseFloat(yesAsk)
    高于用户指定的阈值。“大概率”是表述而非固定数值——若用户说97美分,则使用0.97;若用户说99美分,则使用0.99。若用户仅说“大概率”未提供数值,请询问用户阈值(95美分?99美分?)。请勿自行设定默认阈值
  • 排序:按
    closeTime
    升序排列——到期时间越靠前排名越前。
  • 返回排名前N的结果。若用户指定时间窗口(“48小时内”“本周内”),则应用该过滤条件;请勿自行设定默认窗口。

4. Momentum

4. 动量扫描

"What moved in the last hour?" or "Alert me when something moves"
  • Polling:
    /api/v1/market/{ticker}/candlesticks
    (or
    /market/by-mint/{mint}/candlesticks
    ) per market at the smallest interval, compare latest close vs the close N minutes ago. Per-market and expensive — pre-filter the universe to top-of-volume first (re-use scan #6's ranking, take top-N busy markets, compute momentum on those).
  • Streaming: subscribe to the
    prices
    channel (
    all: true
    or a ticker list) and compute rolling pct change in memory. Much cheaper for the "alert when X happens" variant, and this is how you'd wire "trade when a market moves > N%" (hand the matching market to
    dflow-kalshi-trading
    ).
  • Rank by absolute pct change (two-sided) or signed (directional) over a user-supplied window — default to 60 minutes if they don't specify, but no default pct threshold. Return top-N. If the user says "moved > N%" they supply N.
“过去一小时哪些市场波动了?”“当市场波动时提醒我”
  • 轮询模式:为每个市场调用
    /api/v1/market/{ticker}/candlesticks
    (或
    /market/by-mint/{mint}/candlesticks
    )获取最小时间间隔的数据,比较最新收盘价与N分钟前的收盘价。此方式针对单市场且开销较高——应先按交易量筛选全量市场(复用扫描#6的排序结果,选取前N个活跃市场),再计算动量。
  • 流式推送模式:订阅
    prices
    频道(
    all: true
    或指定标的列表),在内存中计算滚动百分比变化。此方式更适合“当X发生时提醒我”的场景,也可用于实现“当市场波动超过N%时执行交易”(将匹配的市场移交至
    dflow-kalshi-trading
    )。
  • 排序:按绝对百分比变化(双向)或带符号百分比变化(单向)排序,时间窗口由用户指定——若用户未指定则默认60分钟,但不设定默认波动阈值。返回排名前N的结果。若用户说“波动超过N%”,则使用用户指定的N值。

5. Widest bid-ask spreads

5. 买卖价差最大的市场

"Inefficient markets — market-make or avoid."
  • Metric:
    parseFloat(yesAsk) - parseFloat(yesBid)
    (NO side is symmetric).
  • Rank: spread descending.
  • Source: market object; no extra calls.
“低效市场——做市或避开。”
  • 指标:
    parseFloat(yesAsk) - parseFloat(yesBid)
    (NO侧逻辑对称)。
  • 排序:按价差降序排列。
  • 数据来源:市场对象;无需额外调用接口。

6. Highest volume

6. 交易量最高的市场

"Where's the action?"
  • Metric:
    parseFloat(volume24hFp)
    (24h dollar-equivalent) or sum over
    /api/v1/trades
    since a cutoff (intraday). For a live feed, subscribe to the
    trades
    channel and aggregate in a rolling window.
  • Rank: volume descending.
“交易最活跃的市场在哪里?”
  • 指标:
    parseFloat(volume24hFp)
    (24小时美元等价金额)或截止某时间点
    /api/v1/trades
    的累计交易量(日内)。如需实时推送,订阅
    trades
    频道并按滚动窗口聚合数据。
  • 排序:按交易量降序排列。

7. Closing soonest

7. 即将到期的市场

"Theta clock."
  • Metric:
    closeTime - now
    .
  • Rank: ascending.
  • Most useful stacked with scan 3 ("near-certain AND closing soon") or scan 6 ("busy AND closing soon").
“时间价值倒计时。”
  • 指标:
    closeTime - now
  • 排序:按升序排列。
  • 最适合与扫描3(“大概率且即将到期”)或扫描6(“活跃且即将到期”)组合使用。

8. Event- and series-level scans

8. 事件与系列级扫描

"Cheapest YES across all outcomes in this event", "do mutually-exclusive buckets sum > 1?"
  • Within one event (e.g. "Fed raises rates by X bps" with a bucket per outcome): pull
    GET /api/v1/event/{eventTicker}?withNestedMarkets=true
    , then reduce across the nested markets (
    min(yesAsk)
    ,
    Σ yesAsk
    , etc.). Events are the natural scope for single-winner scans.
  • Across a series: pull
    GET /api/v1/series/{seriesTicker}
    plus its events, then roll up.
  • There is no
    mutuallyExclusive
    flag on series or events.
    Summing YES across outcomes only makes sense when the outcomes are a partition of one future (one must happen, exactly one can happen). That's a judgment from the event/series title and contract terms — not a field lookup. When in doubt, surface the numbers and flag the assumption to the user.
“该事件所有结果中最便宜的YES合约”“互斥标的总和是否超过1?”
  • 单事件内(例如“美联储加息X基点”,每个结果对应一个标的):调用
    GET /api/v1/event/{eventTicker}?withNestedMarkets=true
    获取数据,然后对嵌套的市场进行聚合计算(
    min(yesAsk)
    Σ yesAsk
    等)。事件是单赢家扫描的天然范围。
  • 跨系列:调用
    GET /api/v1/series/{seriesTicker}
    获取系列及其包含的事件,然后进行汇总计算。
  • 系列或事件没有
    mutuallyExclusive
    标记
    。对所有结果的YES价格求和仅在结果构成单一未来的分区时才有意义(必有且仅有一个结果发生)。这需要根据事件/系列标题及合约条款判断——而非通过字段查询。若存在疑问,请展示数据并向用户说明假设前提。

Point lookups (N=1)

单点查询(N=1)

When the user already has one market in mind, skip the skeleton:
  • By ticker:
    GET /api/v1/market/{ticker}
    (singular
    market
    , not plural).
  • By outcome mint:
    GET /api/v1/market/by-mint/{mint}
    (slash, not hyphen).
  • By event ticker:
    GET /api/v1/event/{eventTicker}?withNestedMarkets=true
    (singular
    event
    ).
  • Free-text:
    GET /api/v1/search
    (natural-language to events/markets).
The plural forms (
/markets
,
/events
) are the list endpoints and take
?cursor=&limit=
for pagination. The singular forms are point lookups by id. Mixing them up gets you 404s.
当用户已明确指定某一市场时,跳过上述框架:
  • 通过标的代码:
    GET /api/v1/market/{ticker}
    (单数
    market
    ,非复数)。
  • 通过结果mint地址:
    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错误。

What to ASK the user (and what NOT to ask)

需要询问用户的内容(及无需询问的内容)

Query shape — infer if unambiguous, confirm if not:
  1. Which scan (or a plain-English intent you can map to one).
  2. Thresholds the user supplies — use theirs verbatim. If they say "> 5%" or "under 2¢" or "this week", use those numbers. Otherwise, use the rank-based defaults from each scan above (top quartile by volume, etc.); do not propose a fixed numeric threshold of your own. If the user's phrasing implies a threshold the scan doesn't define ("big movers", "serious volume"), ask them — don't guess a number.
  3. Polling vs streaming — if the intent sounds like "show me now" go REST; if it sounds like "alert me / react when" go WebSocket.
  4. Top-N (default 10).
Infra — always ask, never infer:
  1. DFlow API key — for the discovery / HTTP portion of the script only; CLI shell-outs authenticate themselves (see the "two auth paths" gotcha below). Ask with a clean, neutral question: "For the scanner / discovery side, do you have a DFlow API key?" Don't presuppose where the key lives — phrasings like "do you have it in env?" or "is
    DFLOW_API_KEY
    set?"
    nudge the user toward env-var defaults they didn't ask for. Surface the choice; don't silently fall back to env or to dev. It's one DFlow key everywhere — same
    x-api-key
    unlocks the Trade API and the Metadata API, REST and WebSocket. If yes → prod hosts (
    https://prediction-markets-api.dflow.net
    REST,
    wss://prediction-markets-api.dflow.net/api/v1/ws
    WS) with
    x-api-key
    on every request (REST and the WS upgrade). If no → dev hosts (
    https://dev-prediction-markets-api.dflow.net
    ,
    wss://dev-prediction-markets-api.dflow.net/api/v1/ws
    ), rate-limited; point them at
    https://pond.dflow.net/build/api-key
    for a prod key. When you generate a script, log the resolved host + key-presence at startup (
    Using prod Metadata API
    /
    Using dev Metadata API — rate-limited
    ) so the user can see which rails they're on without spelunking through code.
Do NOT ask about:
  • RPC, wallet, signing — this skill is read-only public metadata. No transactions.
  • Settlement mint / slippage / fees — those are trade-side concerns. If the user pivots to placing an order on a market you surfaced, hand off to
    dflow-kalshi-trading
    .
查询结构——表述明确则自行推断,表述模糊则确认:
  1. 扫描类型(或用户用自然语言表述的意图,你可映射到对应的扫描类型)。
  2. 用户提供的阈值——严格使用用户指定的值。若用户说“>5%”“低于2美分”“本周内”,则使用这些数值。否则,使用上述各扫描中基于排名的默认规则(如交易量前四分之一等);请勿自行提议固定数值阈值。若用户表述隐含扫描未定义的阈值(“大幅波动”“大额交易量”),请询问用户——请勿自行猜测数值。
  3. 轮询 vs 流式推送——若用户意图是“现在给我看”则使用REST;若用户意图是“提醒我/当发生时响应”则使用WebSocket。
  4. 返回数量Top-N(默认10个)。
基础设施——务必询问,绝不自行推断:
  1. DFlow API密钥——仅用于脚本的发现/HTTP部分;CLI调用会自行认证(详见下文“两种认证路径”的注意事项)。请用清晰中立的方式询问:“用于扫描/发现环节,你是否拥有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和WebSocket升级)中携带
    x-api-key
    。若用户没有密钥→使用开发环境地址(
    https://dev-prediction-markets-api.dflow.net
    wss://dev-prediction-markets-api.dflow.net/api/v1/ws
    ),存在速率限制;请引导用户访问
    https://pond.dflow.net/build/api-key
    获取生产环境密钥。当生成脚本时,请在启动时记录已解析的地址及密钥状态
    Using prod Metadata API
    /
    Using dev Metadata API — rate-limited
    ),以便用户无需查看代码即可确认当前使用的环境。
无需询问的内容:
  • RPC、钱包、签名——本技能仅处理只读的公开元数据。无需涉及交易。
  • 结算mint地址/滑点/手续费——这些属于交易环节的问题。若用户转向为你发现的市场下单,请移交至
    dflow-kalshi-trading
    处理。

Gotchas (the docs MCP won't volunteer these)

注意事项(文档MCP不会主动提及)

  • Top-of-book lives on the market object.
    yesBid
    /
    yesAsk
    /
    noBid
    /
    noAsk
    are already there. Don't loop the orderbook endpoint just to get best prices.
  • Prices and volume are market-wide; trading is rail-scoped. Every initialized market has both a USDC rail and a CASH rail under
    market.accounts
    , each with its own
    marketLedger
    /
    yesMint
    /
    noMint
    . The scan's
    yesBid
    /
    yesAsk
    /
    volume24hFp
    come off the shared Kalshi orderbook and don't tell you which rail you'll trade on. When handing off to
    dflow-kalshi-trading
    , pass the market ticker and let the trading step pick the rail (default: USDC). Don't silently pre-select a rail in the scan output — state it if you do.
  • The orderbook returns only bid ladders (
    yes_bids
    ,
    no_bids
    ). Best YES ask is derived:
    1 - max(no_bids keys)
    (a NO bid at
    p
    is a YES offer at
    1-p
    ). Only matters if the user wants ladder depth.
  • Two price scales. Market/orderbook prices are 4-decimal probability strings (
    "0.4200"
    ). Trade prices (REST and
    trades
    channel) are integer 0–10000, with
    yes_price_dollars
    /
    no_price_dollars
    string fields alongside. Normalize before you compute.
  • market.title
    is often event-level, not market-specific.
    On multi-outcome events (a market per candidate, per rate-hike bucket, per game winner, etc.), every market under that event shares the same
    title
    — the outcome-specific wording lives in
    yesSubTitle
    (and
    noSubTitle
    ). If your scan output only prints
    title
    , adjacent rows look identical and the user can't tell which outcome is which. Render
    title — yesSubTitle
    (or fall back to just
    title
    when
    yesSubTitle
    is null / empty, which happens on simple binary markets). Same applies when you hand a market off to a trade prompt: a "buy YES on X" confirmation that just shows
    title
    is ambiguous for multi-outcome events.
  • Volume fields — string, dollar-equivalent, and no
    volume24h
    .
    The market object has
    volume
    (int, cumulative raw units),
    volumeFp
    (string, cumulative dollar-equivalent), and
    volume24hFp
    (string, 24h dollar-equivalent). There is no
    volume24h
    field. Always
    parseFloat
    the
    *Fp
    fields before comparing. Same shape on
    openInterest
    /
    openInterestFp
    .
  • isInitialized
    filter.
    Short-duration markets (15-min crypto, etc.) are often active on Kalshi but not yet tokenized on DFlow. Without the filter, scans include them; with
    isInitialized=true
    , only markets tradable on DFlow right now. Usually you want
    true
    .
  • Null bids/asks. Illiquid markets have null top-of-book fields. Every scan that reads them must skip nulls, not treat them as zero.
  • Maintenance window — Kalshi is offline Thursdays 3:00–5:00 AM ET. Top-of-book and volume fields can go stale or missing during the window; WS updates can go quiet. If scans look empty or weirdly wrong during the window, that's why.
  • Pagination shape.
    /markets
    (and
    /events
    ) return
    { markets: [...], cursor: <number> }
    . First call: omit
    cursor
    or pass
    cursor=0
    — equivalent. The returned
    cursor
    is the offset of the next page (= running row count); pass it back as
    ?cursor=N
    on the next call. Terminate when
    markets.length < limit
    (canonical); a
    next === cursor
    sanity check is harmless paranoia but not necessary.
    limit
    caps at 255
    limit=256
    + returns HTTP 400
    "number too large to fit in target type"
    (it's a
    u8
    on the backend). Docs use
    200
    as a conservative default;
    255
    is the true ceiling. Large scans that skip pagination silently drop matches.
  • WebSocket
    all: true
    is firehose-y.
    Subscribing
    all: true
    on busy channels (esp.
    prices
    ) streams every update across every market. Prefer ticker lists when the scan only cares about a known set; use
    all: true
    only when the scan really is universe-wide.
  • "Scan then buy" = one interactive script, not two separate artifacts. When the user wants to find markets and then act on the result from the command line, the right output is a single script that scans, prints the ranked list, prompts the user to pick one (or confirm y/N per row), and then shells out to
    dflow trade
    . Don't write a non-interactive scan script and tell the user to "pick one and run
    dflow trade
    yourself" — that forces them to context-switch and copy identifiers around. A
    readline
    prompt in Node /
    input()
    in Python is fine. For full-auto flows (no per-row prompt), still keep it one script and make the "no confirmation" behavior explicit up front.
  • Handoff shape to
    dflow trade
    .
    The scan result is a market object, not a CLI-ready invocation. The CLI's
    --market
    flag takes the
    marketLedger
    from
    market.accounts[<settlementMint>].marketLedger
    — not
    market.ticker
    , not
    yesMint
    /
    noMint
    , not any top-level mint. Default to the USDC rail unless the user says CASH (see the "prices and volume are market-wide; trading is rail-scoped" gotcha above). The same
    marketLedger
    is used for both YES and NO buys — side selection is
    --side yes|no
    , not a different
    --market
    . For the full CLI argument shape (settlement-mint →
    marketLedger
    lookup, atomic-unit conversion, priority-fee flags, the "buy N whole contracts" idiom), see
    dflow-kalshi-trading
    . Don't reinvent it here.
  • Mixed-surface scripts: two auth paths, not one. The natural shape of this skill plus
    dflow-kalshi-trading
    is a script that does discovery over the Metadata API, then shells out to
    dflow trade
    for execution. The two legs authenticate independently:
    • Discovery (Metadata API HTTP) — plain
      fetch
      /
      curl
      . Needs a DFlow API key in the script's env or config (see #5). No key → dev host, rate-limited.
    • Execution (
      dflow trade
      shell-out)
      — uses whatever the CLI has stored from
      dflow setup
      (key, wallet, RPC). The script plumbs nothing for CLI invocations.
    When a user says "the CLI is already set up," that's the execution leg covered — not the discovery leg. Frame the API-key ask as "for the scanner/discovery side, do you have a DFlow API key?" — not as "the CLI isn't enough, you need another key." The two are independent: one DFlow key, two plumbing sites.
  • 最优报价已包含在市场对象中
    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。所有读取这些字段的扫描必须跳过空值,而非将其视为0。
  • 维护窗口——Kalshi在美国东部时间每周四3:00–5:00处于离线状态。维护窗口期间,最优报价和交易量字段可能过期或缺失;WebSocket推送可能中断。若扫描结果为空或异常,且时间处于维护窗口内,原因即在于此。
  • 分页结构
    /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
    ——而非
    market.ticker
    yesMint
    /
    noMint
    或任何顶级mint地址。默认使用USDC渠道,除非用户指定CASH(详见上文“价格和交易量是市场级的;交易是渠道特定的”注意事项)。同一个
    marketLedger
    可用于买入YES和NO合约——方向选择通过
    --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密钥,用于两个不同的环节。

When something doesn't fit

未覆盖场景的处理

For anything not covered above — full parameter lists, pagination tokens, response schemas, WS reconnection semantics, rare filters (sports, tags, categories, series search), candlestick intervals — query the docs MCP (
search_d_flow
,
query_docs_filesystem_d_flow
). Don't guess.
对于上述未覆盖的内容——完整参数列表、分页令牌、响应 schema、WebSocket重连逻辑、罕见过滤器(体育、标签、分类、系列搜索)、K线时间间隔等——请查询文档MCP(
search_d_flow
query_docs_filesystem_d_flow
)。请勿自行猜测。

Sibling skills

关联技能

When the user pivots from discovery to action, hand off:
  • dflow-kalshi-trading
    — actually buy/sell/redeem a market you found here.
  • dflow-kalshi-portfolio
    — view their positions and P&L.
  • dflow-kalshi-market-data
    — general live orderbook / trade / price streaming outside the "named scan" shape (reconnection patterns, full payload schemas, in-game live data).
  • dflow-proof-kyc
    — verify a wallet so it can actually buy what you surfaced.
当用户从发现转向操作时,移交至以下技能:
  • dflow-kalshi-trading
    ——实际买入/卖出/赎回你在此发现的市场。
  • dflow-kalshi-portfolio
    ——查看用户自身的持仓及盈亏。
  • dflow-kalshi-market-data
    ——处理超出“命名扫描”范围的通用实时订单簿/交易/价格推送(重连模式、完整负载schema、实时游戏数据)。
  • dflow-proof-kyc
    ——验证钱包以便用户能够买入你发现的标的。",