---
updatedAt: 2026-07-10T10:27:39.000Z
agentTools:
  projectIndex: https://docs.getembed.ai/llms.txt
---

# 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](https://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](./supported-indices). The index determines what the algorithm can return:

| Feed goal                      | Start with                                                                                           |
| ------------------------------ | ---------------------------------------------------------------------------------------------------- |
| Token or market discovery      | `token-items`, `polymarket-items`, `hyperliquid-items`                                               |
| Recent activity                | `solana-trades`, `polymarket-trades`, `alpha-trade-events-kalshi`                                    |
| Holdings or exposure           | `base-wallet-positions`, `polymarket-wallet-positions`, `hyperliquid-wallet-positions`               |
| Trader or wallet discovery     | `polymarket-wallets`, `hyperliquid-wallets`, `kalshi-wallets`, `wallet-users`                        |
| Notification-backed candidates | `dex-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:

| Filter           | Use it for                                                                              |
| ---------------- | --------------------------------------------------------------------------------------- |
| Term or terms    | Match categorical values such as chain, symbol, market type, labels, or status          |
| Numeric filters  | Filter volume, liquidity, price movement, PnL, counts, or timestamps                    |
| Null / not-null  | Require fields needed by the UI, such as `item_id`, `user_id`, `symbol`, or `timestamp` |
| In-app users     | Scope results to users known by your app                                                |
| Console account  | Scope results to records associated with your Console account                           |
| User interaction | Include or exclude items/users based on interaction fields                              |
| Group boost      | Boost candidates from lookup groups, such as wallet tags or labels                      |
| Custom JSON      | Use 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

```js
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

```js
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

```js
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

```js
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.

| Pattern                       | When to use it                                       | Main DSL ideas                                                     |
| ----------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------ |
| Following or watchlist trades | A user-specific feed of trades from followed wallets | `userInteraction`, `polymarket-trades`, `timestamp` sort           |
| Whale trades                  | Large-trade discovery or alerts                      | `numeric`, trade-size threshold, `timestamp` sort                  |
| Trade-to-market discovery     | Start from trade activity but show market cards      | Search trades, collect market/token IDs, search `polymarket-items` |
| Wallet-position signals       | Show markets connected to wallet positions           | `polymarket-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.

```js
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.

```js
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.

```js
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.

```js
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

| Problem                                 | What to check                                                                    |
| --------------------------------------- | -------------------------------------------------------------------------------- |
| Search returns no hits                  | Index, query mode, required fields, numeric ranges, date range                   |
| Results are noisy                       | Add categorical, numeric, account, or in-app-user filters                        |
| Cards are missing UI fields             | Add hydration targets or required-field filters                                  |
| DSL does not match the UI               | Review the DSL tab after every Search or Hydration change                        |
| Saved algo does not behave like preview | Re-run draft preview, save again, then test from the feed or notification config |

## Next

* See every search source in [Supported Indices](./supported-indices).
* Learn filters in [Search](./search).
* Deploy saved algorithms with [Feed Configs](../Feeds/deploy-feed-configs).
* Use saved algorithms for notifications in [Notifications](../Notifications/guide-notifications).