FAQ & Troubleshooting

Common Strategy Studio problems and fixes — intra-bar firing, warmup/NaN, validation errors, rejected orders, and signal-vs-level connection errors.

Can I publish a strategy privately?

Yes. When you Publish from Studio, choose a Visibility:

  • Public — listed in the marketplace (browse / search / sort).
  • Private (unlisted) — hidden from marketplace browse, search, and sort, but still loads for anyone who has its version-hash link, and it shows in your library's Published tab (with a Private badge).

Can I edit a strategy I already published?

You can edit the listing, not the graph. After publishing, the publisher can update the title, summary, tags, risk disclosure, licence, classification, and the public/private visibility in place via a signed update — use the Edit button on the Published tab. The graph itself stays immutable: changing the graph is a new-version publish (a new version hash), not an edit.

How do I run a strategy from the order form?

Any executable strategy (Graph type = strategy) in your Studio library is automatically listed in the order form's Pro dropdown under Your strategies. There is no "Add to order form" step. Select it to swap the order-form body for its frontend, set the fields, and press Run Strategy to run it live on this device or in Borsa Cloud. Edits you make in Studio reflect in the order form live. To paper-test, use Test (paper) in Studio. See Strategy Frontends.

What happened to "quick strategies"?

The standalone quick-conditional-strategy flow is gone. The strategy wizard (New strategy… in the order form's Pro menu, or File → New → With the wizard… in Studio) builds "when … then …" rules with a budget and compiles them into a Strategy Studio node graph. After Create strategy you can Run from the order form or Open in Studio to refine it; Empty canvas in Studio on the first step skips the wizard. The order form's native TWAP / ALO / Scale / Burst order types are unaffected and keep working. See Strategies.

My strategy fires on every bar / mid-bar instead of once

Exec nodes default to fireMode: "edge" — they fire once per false→true transition of fire, so a held-true condition (e.g. a sustained logic.compare boolean) places ONE order, not one per bar. If yours re-fires every bar, check:

  • The node's fireMode is set to level (the legacy fire-every-eligible-tick mode) — switch it back to edge, or pulse fire yourself.
  • Your condition genuinely flickers false→true each bar (each transition is a fresh edge). Debounce it with logic.sustain, throttle it with logic.cooldown, or latch it with logic.toggle.
  • For an explicit one-tick event, convert with logic.edge (rising) or use an event source: logic.cross, clock.bar, clock.everyN, clock.boundary.
  • To repeat on purpose, set Trigger (fireMode) to Every N minutes while true with Repeat every, or use First time only for a single order per run.

If it fires *mid-bar* on live data, check that the exec node's fireOnBarClose is true (the default), or AND your signal with a clock.bar.tick. Only set fireOnBarClose: false for deliberate intra-bar timing (e.g. a clock.everyMs TWAP, or an instant ui.button panel). See the two-tick model.

"Type mismatch: boolean → signal<boolean>" (or number → signal<number>)

Port types are strict, and level → signal is rejected by design. You can't feed a sustained boolean into a signal<boolean> input — insert a logic.edge to turn the level into an event first. The reverse (signal → level) is always allowed. See signal vs level.

Everything outputs NaN / no trades happen

Rolling indicators return NaN until their lookback fills — an SMA(200) needs 200 bars. Causes and fixes:

  • Range too short. A backtest warms up on the first bars of its range, so an SMA(200) is NaN for the first 200 bars. A 30-day range on a 1d timeframe is only 30 bars, not enough for slow indicators or for a cross to occur. Widen the Backtest range in the Inspector.
  • Wrong source. If you wired a candle into an indicator's value, the source param picks the field; the default is close.

"order rejected: notional ... below Hyperliquid minimum $10"

The order's size × price is under the $10 minimum. A sizeFallback like 0.0001 BTC is far below $10. Increase the size, or compute it from notional with exec.notionalToSize / exec.sizeByEquityPct. Sub-lot sizes are likewise floored and may round to zero. Check the Console for the exact order:rejected reason.

Validation errors I might hit

These block the run (toolbar pill + Issues tab + red nodes):

  • Only one Timeframe node is allowed per graph. — keep a single data.timeframe.
  • Type mismatch: <a> → <b>. — incompatible port types; see signal/level above.
  • Timeframe override '<x>' must be coarser than and a multiple of the strategy timeframe '<y>'. — a per-node timeframe override must be a higher, integer-multiple timeframe (e.g. 4h on a 1h graph), never finer or non-aligned.
  • Cycle detected involving node … — feedback loops must pass through a state.* node (or dev.js).
  • Indicator graph has no Plot node. — an indicator-kind graph needs at least one debug.plot.
  • Missing param '<name>'. / Unknown node kind '<kind>'. — usually an imported/forked graph from a different engine version.

My higher-timeframe indicator's value looks "stuck"

That's correct. A node with a coarser timeframe override only updates when its HTF bar closes, holding the last completed value between closes (no repaint). A 4h EMA on a 1h graph changes once every four 1h bars. See per-node timeframe.

My funding strategy makes nothing in backtest

Backtest has no historical funding series: the rate is effectively 0 and data.fundingRate returns NaN. Templates that depend on it are marked Live only. Funding only accrues in paper/live, where the live rate flows. Run funding-sensitive graphs in paper mode. See Backtest Modeling → Funding.

Paper results differ slightly from my backtest

Backtest is bar-close only; paper sees live intra-bar ticks and fills against the live BBO/mid, plus real funding accrues. The bar-by-bar decisions match — crosses and sampled indicators commit only on closed-bar ticks in every mode, reduce-only and bracket OCO are enforced in both simulators — but fill prices and funding differ because the data source differs. This is expected. See Backtest Modeling for the remaining live-only gaps (static live trailing stop, no live bracket OCO).

My dev.js node won't publish to the marketplace

By design — the marketplace publish flow rejects any graph containing dev.js so no remote code runs on import, and the same untrusted-graph guard blocks file imports (.borsa.json) containing it. Replace the dev.js logic with built-in nodes, or keep the graph local/self-hosted. Locally, dev.js scripts are also capped at 1M loop iterations per tick — a runaway loop is aborted with a Console error instead of hanging the page.

A strategy kept running after I left Studio

Browser runs live in a module-scoped registry and survive navigation inside the app; they're observable from the /strategies tab. Use Stop to end a run. Closing the tab stops runs on this device (a paper run is restored after a refresh; a live one asks Resume live or Discard). Borsa Cloud runs keep going until you stop them.

"Already running in another tab"

A strategy runs at most once. If another tab of this browser runs it, stop it there; if it runs in Borsa Cloud, stop it from the Strategies tab first. This prevents every order going out twice.

My strategy traded a different coin than the chart

Coin params that are pinned (e.g. BTC) trade that coin on any chart; the ticket says "Trades BTC, not the chart's …". Set them to Current market, or use Edit → Use current market for all coins in Studio. A run started on one market stays on it when you switch charts.

Are my strategies on my other devices?

Yes. The Studio library syncs per wallet across every device signed in with the same wallet, once that device's trading agent is approved. See Library sync.

risk.preTradeValidate doesn't seem to block anything

It now does — but only when wired correctly. Connect an exec node's intent output into preTradeValidate.intent and a boolean into ok. When ok is false, the engine drops that exec node's intent at the submit barrier. An unwired ok defaults to true (passes everything). See the risk barrier.

FAQ & Troubleshooting · Docs | Borsa