Building Algorithms

Build feed and notification algorithms in Console with Search, Hydration, and DSL.

Building Algorithms

Console algorithms are saved DSL programs that return candidates for feeds or notifications. In the current Console at console.mbd.xyz, the builder is organized as:

  1. Search: choose an index, add filters, choose sort order, and preview hits.
  2. Hydration: add metadata targets for the hits returned by Search.
  3. DSL: review or edit the generated DSL and save the algorithm.

The builder currently focuses on search and hydration. There is no separate feature-engineering, scoring, or ranking step to configure before output.

Console Workflow

  1. Open Algo in Console.
  2. Click New Algo.
  3. In Search, choose an index from the dropdown.
  4. Choose a query mode:
    • Semantic for text or vector-style retrieval.
    • ES query for explicit Elasticsearch JSON.
    • Sort for field-sorted feeds without a text query.
  5. Add filters that match the selected index.
  6. Click Run search and inspect the preview.
  7. Open Hydration and add the metadata targets needed by your UI.
  8. Click Run hydration and verify enriched results.
  9. Open DSL, review the generated code, then save.
  10. Use the saved algorithm in a feed config or notification config.

Search Sources

Use the exact names in Supported Indices. The index determines what the algorithm can return:

Feed goalStart with
Token or market discoverytoken-items, polymarket-items, hyperliquid-items
Recent activitysolana-trades, polymarket-trades, alpha-trade-events-kalshi
Holdings or exposurebase-wallet-positions, polymarket-wallet-positions, hyperliquid-wallet-positions
Trader or wallet discoverypolymarket-wallets, hyperliquid-wallets, kalshi-wallets, wallet-users
Notification-backed candidatesdex-notifications, hyperliquid-notifications, kalshi-notifications, polymarket-notifications

Filter Types In Console

The Search tab exposes filters based on the selected index. Common filter families include:

FilterUse it for
Term or termsMatch categorical values such as chain, symbol, market type, labels, or status
Numeric filtersFilter volume, liquidity, price movement, PnL, counts, or timestamps
Null / not-nullRequire fields needed by the UI, such as item_id, user_id, symbol, or timestamp
In-app usersScope results to users known by your app
Console accountScope results to records associated with your Console account
User interactionInclude or exclude items/users based on interaction fields
Group boostBoost candidates from lookup groups, such as wallet tags or labels
Custom JSONUse advanced Elasticsearch filters when the visual filter controls are not enough

Start with strict required fields, then add business filters. If the preview is empty, remove one filter at a time until the candidate pool is healthy.

Hydration

Hydration runs after Search. It adds metadata to the hits so your product can render a usable card or notification.

In Console:

  1. Run Search first.
  2. Open Hydration.
  3. Select one or more hydration targets.
  4. Set a per-target limit.
  5. Optionally enable drop_empty.
  6. Run hydration and inspect the enriched results.

Use hydration for display fields and metadata. Do not use it as a replacement for filtering: remove bad candidates in Search first, then enrich the remaining hits.

Example: Fresh Token Feed

import mbd from "algo-dsl";

export default async function algo() {
  return mbd
    .search()
    .index("token-items")
    .notNull("symbol")
    .notNull("name")
    .range("volume_24hr", { gt: 0 })
    .sortBy("timestamp", "desc")
    .size(30);
}

Use this when you need a feed of active token items. Hydrate the fields your UI needs, such as symbol, name, image, URL, and market metadata.

Example: Trader Discovery

import mbd from "algo-dsl";

export default async function algo() {
  return mbd
    .search()
    .index("polymarket-wallets")
    .notNull("user_id")
    .range("volume_1mo", { gt: 0 })
    .sortBy("volume_1mo", "desc")
    .size(25);
}

Use this for wallet or trader discovery. Keep wallet feeds on wallet indexes instead of forcing item indexes to behave like user indexes.

Example: Kalshi Activity

import mbd from "algo-dsl";

export default async function algo() {
  return mbd
    .search()
    .index("alpha-trade-events-kalshi")
    .notNull("user_id")
    .notNull("timestamp")
    .sortBy("timestamp", "desc")
    .size(50);
}

Use this when the product surface should react to recent Kalshi trade activity.

Example: Notification Candidates

import mbd from "algo-dsl";

export default async function algo() {
  return mbd
    .search()
    .index("polymarket-notifications")
    .notNull("user_id")
    .notNull("item_id")
    .sortBy("timestamp", "desc")
    .size(100);
}

Save notification candidate algorithms the same way as feed algorithms. Notification configs reference saved algorithm IDs.

DSL Patterns You Can Build In Console

Use these patterns as starting points for common Console algos. They are based on real Console workflows, but do not require copying any saved algo ID.

PatternWhen to use itMain DSL ideas
Following or watchlist tradesA user-specific feed of trades from followed walletsuserInteraction, polymarket-trades, timestamp sort
Whale tradesLarge-trade discovery or alertsnumeric, trade-size threshold, timestamp sort
Trade-to-market discoveryStart from trade activity but show market cardsSearch trades, collect market/token IDs, search polymarket-items
Wallet-position signalsShow markets connected to wallet positionspolymarket-wallet-positions, user/wallet filters, hydration

Pattern: Following Or Watchlist Trades

Use this when a user-specific feed should show recent Polymarket trades from wallets the viewer follows or watches.

import mbd from "algo-dsl";

export default async function algo({ polymarketWallet } = {}) {
  const viewer = polymarketWallet?.toLowerCase();
  if (!viewer) {
    mbd.show();
    return;
  }

  const trades = await mbd.search()
    .index("polymarket-trades")
    .size(50)
    .include()
      .userInteraction("wallet_address", viewer, "following")
    .sortBy("timestamp", "desc")
    .execute();

  mbd.addCandidates(trades);
  mbd.show();
}

How to build it in Console:

  1. Choose polymarket-trades in Search.
  2. Add a user-interaction filter for the wallet field.
  3. Sort by timestamp desc.
  4. Run Search, then hydrate fields needed by your feed card.
  5. Review the generated DSL and save.

Pattern: Whale Trades

Use this when the feed should show larger trade events.

import mbd from "algo-dsl";

export default async function algo({ polymarketWallet } = {}) {
  const viewer = polymarketWallet?.toLowerCase();
  if (!viewer) {
    mbd.show();
    return;
  }

  const trades = await mbd.search()
    .index("polymarket-trades")
    .size(20)
    .include()
      .userInteraction("wallet_address", viewer, "following")
      .numeric("size_usd", ">=", 1000)
    .sortBy("timestamp", "desc")
    .execute();

  mbd.addCandidates(trades);
  mbd.show();
}

How to tune it:

  • Increase the size_usd threshold for stricter whale feeds.
  • Lower the threshold if preview results are sparse.
  • Remove the user-interaction filter for a global whale feed.

Pattern: Trade-To-Market Discovery

Use this when trade activity should lead to market cards rather than raw trade rows.

import mbd from "algo-dsl";

export default async function algo({ clobTokenIds = [] } = {}) {
  const markets = await mbd.search()
    .index("polymarket-items")
    .size(50)
    .include()
      .terms("clob_token_ids.keyword", clobTokenIds)
    .sortBy("volume_24hr", "desc")
    .execute();

  mbd.addCandidates(markets);
  mbd.show();
}

How to build it:

  1. Use a trade source to identify token or market IDs.
  2. Use polymarket-items to return renderable market items.
  3. Sort markets by activity, such as volume_24hr.
  4. Hydrate market metadata for the UI.

Pattern: Wallet-Position Signals

Use this when the feed should react to wallet positions instead of raw trades.

import mbd from "algo-dsl";

export default async function algo({ polymarketWallet } = {}) {
  const viewer = polymarketWallet?.toLowerCase();
  if (!viewer) {
    mbd.show();
    return;
  }

  const positions = await mbd.search()
    .index("polymarket-wallet-positions")
    .size(50)
    .include()
      .userInteraction("wallet_address", viewer, "following")
    .sortBy("timestamp", "desc")
    .execute();

  mbd.addCandidates(positions);
  mbd.show();
}

Hydrate market metadata after Search so the UI can render each position as a feed card.

Debugging Checklist

ProblemWhat to check
Search returns no hitsIndex, query mode, required fields, numeric ranges, date range
Results are noisyAdd categorical, numeric, account, or in-app-user filters
Cards are missing UI fieldsAdd hydration targets or required-field filters
DSL does not match the UIReview the DSL tab after every Search or Hydration change
Saved algo does not behave like previewRe-run draft preview, save again, then test from the feed or notification config

Next


Did this page help you?