The Strategy Studio

The admin screen at /admin/hb/strategies — what a preset row stores, its endpoints and permissions, draft versus published, the six seeded profiles and why editing one silently changes a running bot.

8 min readUpdated 6 August 2026strategy-studio, presets, publishing, seeder, yaml, permissions

Admin → Hummingbot → Strategy Studio at /admin/hb/strategies is where controller presets are authored, versioned and published. It is the fourth of the five Hummingbot admin screens, and it is the one with two very different audiences: a published preset is what your customers download for their own Hummingbot, and it is also what your own server-run bots are launched with.

That second half is what makes this screen an operator surface rather than a content editor. Editing a preset is a deployment.

A server-run instance regenerates its controller YAML from the linked preset on every start, and Restart is a start. So changing a spread ladder here does not touch the live process — but the next restart, the next crash auto-restart, and the next server reboot all pick up your edit.

Change a preset an instance is linked to only when you are willing for that bot to run the new parameters, and prefer creating a new preset for anything experimental.

The list

Column Holds
Preset name, up to 120 characters
Family pmm or xemm — which controller this becomes
Market The pair in BASE-QUOTE form, with a second line: spot or perp for PMM, or maker <connector> → taker <connector> for XEMM
Status draft or published
Version version, bumped on every update
Updated updatedAt
Author The admin who last saved it, from createdBy. Expanded only
Created createdAt. Expanded only
Description The guidance paragraph. Expanded only — in a row it pushed every other column off the right edge

The row actions are Preview / download YAML, Publish or Unpublish, Edit preset and Delete; New preset is in the toolbar. Multi-select delete posts to the collection endpoint.

The nine columns tell you which preset a row is; none of them shows what it would do. The parameters live in the config JSON, and Preview YAML is how you read them.

What a row actually stores

Table hb_strategy_preset. One row is a complete strategy minus its market.

Column Type Notes
id UUID Primary key, and what hb_instance.presetId points at. Not the controller id — a downloaded YAML derives that from its filename stem, and a server-run bot uses instance-<instance id>
name varchar(120) Unique only by convention. The seeder matches on it
description text Up to 2000 characters, collapsed to one line in the YAML header
family enum pmm | xemm Drives the form, the validator and the generator
pair varchar(40) BASE-QUOTE, uppercased, / rewritten to -
makerConnector varchar(60) Defaults to bicrypto
takerConnector varchar(60), nullable Required for XEMM, null for PMM
config JSON The family-specific parameters — the substance of the preset
status enum draft | published Defaults to draft
version int Starts at 1, +1 on every successful update
createdBy UUID, nullable SET NULL if the admin is deleted, so the preset survives

pair is only the default a self-hosting user downloads. Every server-run instance selects its own market and overrides it, which is why the seeded presets are named for market conditions — Tight, Balanced, Wide — and not for assets. Full parameter reference: Strategies and presets.

For PMM the generator picks the connector from config.market (perpbicrypto_perpetual, otherwise bicrypto), not from makerConnector — so a PMM preset's stored maker connector is informational. For XEMM both connectors are used as stored.

The endpoints

Lists every preset, drafts included, newest first
Creates a preset
Replaces a preset and bumps its version
Deletes one preset
Deletes several — the table's multi-select delete
Renders a preset to controller YAML, returning filename and yaml

A few behaviours worth knowing:

  • The list returns up to 500 rows when no perPage is given, so the instance form's preset picker sees everything rather than the first ten. It runs non-paranoid, because this table has no deletedAt column — there is no soft delete and no restore.
  • PUT is a full replace, not a patch. The whole body is re-validated and re-normalised through the same path as create. That includes the publish/unpublish action, which is why toggling status also increments version.
  • Validation runs twice on write — once on the body, once by rendering the YAML — and again on export. A config that cannot render is refused with 422 before it is persisted, so a stored preset can never emit malformed YAML. Common refusals: buySpreads and buyAmountsPct must have the same length (one per level), maxProfitability must be greater than minProfitability, a connector name outside [a-z0-9_], and a pair that is not BASE-QUOTE.
  • Deleting is immediate and permanent. Users who already downloaded a preset keep their copy — their bot runs on their machine — so a delete only delists it here. What it does affect is your own instances: presetId is SET NULL, and an instance with no preset refuses to start with "this instance has no strategy preset, so there would be nothing for Hummingbot to run."

Permissions

Key Grants
access.hb.strategy The screen itself
view.hb.strategy Listing presets and rendering one to YAML
create.hb.strategy New preset — including creating one already published
edit.hb.strategy Edit preset, and Publish / Unpublish on an existing row
delete.hb.strategy Delete, single and bulk

/admin/hb/strategies is gated on view.hb.strategy in the route middleware, and the section root needs access.hb.

Publishing is not an edit-only act. The Publish / Unpublish row action is an ordinary PUT, so it needs edit.hb.strategy — but the create endpoint accepts status in its body, and the New preset dialog carries the same Draft/Published toggle. A role with view and create but no edit can therefore release a preset outright, at the moment it authors it; what it cannot do is change the status of a preset that already exists. If you are handing out create.hb.strategy to staff who should not be publishing to your customers, there is no permission that stops them — review the catalogue instead.

Super Admin bypasses permission checks entirely, so a Studio that works for the owner tells you nothing about your staff. Grant the keys in Admin → Roles and open the screen with a configured role.

Draft versus published

status is the only thing standing between a preset and your customers.

draft published
Visible in the Studio Yes Yes
Listed at /hb/strategies for users No Yes
Downloadable by a user No — GET /api/hb/strategy/{id}/yaml returns 404 Yes
Usable by a server-run instance Yes Yes

The user-facing list returns published presets only, newest-updated first, capped at 200, and is gated by the view_hb KYC feature — the same gate that guards the connector kit, the setup page and the bot console. It is configured per level under Admin → CRM → KYC, not on this screen.

Note the asymmetry in the last row: your own instances can run a draft. The supervisor looks up the preset by id and never checks its status, so unpublishing a preset removes it from the customer catalogue without stopping the bots that use it. That is the safe way to retire a profile.

The six seeded presets

backend/seeders/20260729000001-hbStrategyPresets.js seeds six presets, all with status: "published" and version: 1, so the Studio is not empty on a fresh install.

Name Family Ladder / targets Refresh
PMM — Tight (deep, liquid markets) pmm, spot 0.0005 / 0.0015 / 0.003 at 15/30/55% 8s
PMM — Balanced (start here) pmm, spot 0.002 / 0.005 / 0.01 at 20/30/50% 12s
PMM — Wide (thin or volatile markets) pmm, spot 0.005 / 0.015 / 0.03 at 20/30/50% 25s
PMM — Perpetual, conservative (3x) pmm, perp The Balanced ladder at 3× leverage 12s
XEMM — Hedged on Binance xemm Targets 0.001 and 0.002, band 0.0008–0.003
XEMM — Hedged on MEXC xemm The same, hedging on MEXC

All six default to BTC-USDT and to modest sizes, but the two families are sized in different places. The four PMM presets carry totalAmountQuote — 1000 quote, 500 for Wide. The two XEMM presets have no totalAmountQuote at all; their size is per level, amountQuote 200 then 300, so 500 a side.

They are starting points, not tuned strategies: raise totalAmountQuote on a PMM preset, or the per-level amountQuote on an XEMM one, to your own size, and recompute the XEMM targets against your own fee schedule before running any of them.

What re-running the seeder does

pnpm seed runs every seeder unconditionally — seeder state is not tracked — and pnpm updator runs pnpm seed, so this happens on every platform update whether you asked for it or not.

Your change What the next seed run does
Edited a seeded preset, kept the name Nothing. The name is present, so it is skipped and your parameters survive
Renamed a seeded preset Re-inserts a fresh copy under the original name, beside your renamed one
Deleted a seeded preset Re-inserts it. "Insert what is missing by name" cannot tell a deletion from a fresh install
Created your own presets Never touched. The seeder only ever inserts its own six names

It also deletes six legacy names by exact match — the pair-named presets that shipped before markets moved onto the instance (PMM — BTC/USDT (spot) and its five siblings). If an instance is still linked to one of those, its presetId goes to NULL and it must be pointed at a new profile before it will start.

If you do not want one of the six in your catalogue, unpublish it rather than deleting it. Unpublishing survives a seed run; deleting does not. Renaming is the worst of the three — you end up with two.

Authoring a preset

The editor is a purpose-built dialog rather than the generic table form, because which fields exist depends on the family, and within PMM again on spot versus perpetual.

  1. Name it for a market condition, not a pair. The market is chosen per deployment and overrides pair, so a preset called "PMM — BTC/USDT" can and will be run on something else.

  2. Pick the family. PMM shows the ladder and the risk envelope; XEMM shows the taker venue, the hedge levels and the profitability band.

  3. Enter the parameters. Spreads and amounts are comma-separated lists, one entry per level, and the two lists for a side must be the same length. XEMM levels are targetProfitability:amountQuote pairs. Every field is documented in Strategies and presets.

  4. Leave the status on draft until it has been validated.

  5. Save. A 422 names the exact field — the message is written for the person who typed it, so read it rather than guessing.

Validating a preset before you publish

A preset that renders is not a preset that trades. Three checks, in increasing cost:

  1. Render the YAML. Row menu → Preview / download YAML. Confirm the controller_name is what you expect (bicrypto_pmm or xemm_multiple_levels), the connector matches spot versus perp, and every level you entered is present with the value you meant. The dialog also shows the two filenames a user has to save it under.

  2. Run one instance against it. Size the preset small first — sizing lives on the preset, not on the instance, so that is a low totalAmountQuote for PMM and low per-level amountQuote for XEMM. Then register a bot in Bot Instances, pick a real market, press Diagnose, then Start. The instance's YAML is generated from this preset, so this is the preset running for real.

  3. Read the run log. The instance log tells you the difference between "started" and "working". A market maker can start, stay alive and report RUNNING while never placing an order. Watch for quotes appearing on the instance terminal at /admin/hb/instances/<id>, and cross-check the account in the Command Center.

Only then set the status to published. Remember that publishing is a PUT and bumps the version, which is exactly what you want — the version is how you tell which revision a customer downloaded.

Where to go next