Skip to main content

15 Common Trade Command Mistakes

The footguns the parameter table can't warn you about. Read this page before deploying any payload to live trading.

TL;DR
  • The single biggest one: postOnly defaults to true. Limit orders at the touch fail silently.
  • The second biggest: units is the base asset, not quote. units: 100 on BTCUSDT means 100 BTC.
  • The third biggest: any strategyComment containing the substring close, exit, or flat auto-closes the position.

#1: postOnly defaults to true

Problem: your limit order is rejected by the exchange the moment it would take liquidity. You see no fill, no clear error in TradingView — just "trade failed" in the Activity Log.

Why it happens: postOnly defaults to true. Every payload that doesn't override it submits maker-only.

Fix: flip the flag for marketable limits:

{
"isLimit": true,
"price": 65000,
"postOnly": false
}

Or keep postOnly: true and use chase logic so the order stays maker-side:

{
"isLimit": true,
"limitPriceType": "bestPrice",
"chaseLimitOrder": true
}

See also: Futures Options → postOnly default trap, Limit Order Management.


#2: URL-encoding strips = characters

Problem: a JSON value with a literal = character (rare but possible — base64 padding, certain ID formats) arrives at TVH with the = stripped.

Why it happens: TradingView sends webhook bodies as application/x-www-form-urlencoded. TVH first URL-decodes the body, then strips all = characters to compensate for a TradingView quirk where the body sometimes ends in a stray =.

Fix: avoid literal = in JSON string values. If you must transport base64, use a wrapping format that escapes it (e.g. base64url).

See also: Anatomy → Lifecycle.


#3: Symbol format is exchange-native

Problem: "Invalid symbol" from the exchange. Your trade silently fails.

Why it happens: TVH does not normalise pair names across exchanges. Each exchange has its own convention.

Fix: match the exact format the exchange expects:

ExchangeFormatExample
Binance Spot / Futures USDT-MBASEQUOTE (no separator)BTCUSDT
Binance Futures COIN-MBASEUSD_PERPBTCUSD_PERP
BybitBASEQUOTEBTCUSDT
OKX (perps)BASE-QUOTE-SWAPBTC-USDT-SWAP
OKX (spot)BASE-QUOTEBTC-USDT
Coinbase PerpsBASE-USD-PERPBTC-USD-PERP
BitMEXXBTUSDT (X-prefix)XBTUSDT

See also: Core Parameters → pair.


#4: units is the base asset, not the quote

Problem: you meant "buy $100 worth of BTC" and sent units: 100 — you got an order for 100 BTC rejected by the exchange (because you don't have $6.5 million).

Why it happens: units is the position size in the base asset of the pair. For BTCUSDT, base is BTC. For ETHUSDT, base is ETH.

Fix: to size in quote currency, use useFixedSize:

{
"useFixedSize": true,
"fixedQuoteSize": 100
}

Or use percent-of-balance:

{
"unitsType": "percent",
"unitsPercent": 5
}

See also: Sizing → Mode C — fixed quote-currency amount.


#5: No alertTimestamp means TradingView retries double the position

Problem: TradingView's webhook system retries on network errors. Without an idempotency key, the same intended fill becomes two or three fills.

Why it happens: TVH has no other way to recognise a retry vs an intended duplicate.

Fix: add alertTimestamp to every payload. Use the recommended Pine value:

{
"alertTimestamp": "{{ticker}}-{{timenow}}"
}

TVH inserts a permanent dedup row keyed on (token, apiKey-or-exchange, alertTimestamp). Any payload with the same triple as a previous one is recognised as a duplicate and silently dropped. That catches every TradingView retry pattern we've observed.

See also: Timing & Idempotency → alertTimestamp.


#6: strategyComment containing "close" / "exit" / "flat" auto-closes

Problem: you wrote comment="Closed above EMA" on an entry alert. TVH closes your position instead of opening one.

Why it happens: TVH's auto-mapping scans strategyComment for the substrings close, exit, flat (case-insensitive). Any substring match forces isClose=true, isMarket=true.

Fix: keep close, exit, and flat out of entry comments entirely — there is no flag that disables the built-in scan. strategyCloseValue does not replace it: it only adds extra close tokens on top of the built-in three. So this still closes the position, despite the custom token:

{
"strategyComment": "Long Entry on close cross",
"strategyCloseValue": "EXIT-SIGNAL",
"isBuy": true
}

The built-in scan sees close in the comment and forces a close before strategyCloseValue is even considered. Use strategyCloseValue to register an additional close token for a comment that is otherwise clean (e.g. strategyComment: "FLATTEN", strategyCloseValue: "FLATTEN"), not to whitelist a comment that contains one of the three reserved words.

See also: Anatomy → strategyComment auto-close.


#7: stopLossExpression errors are silently swallowed

Problem: your math expression has a typo (^ for exponent, an undefined function, a malformed parenthesis). The trade submits with no stop loss.

Why it happens: if the expression can't be evaluated, TVH swallows the error silently and leaves stopLoss at its previous value (often 0 = no SL placed). There's no warning in the Activity Log.

Fix:

  1. Test the expression on testnet before going live.
  2. Echo the substituted expression in strategyComment for logging: "sl_expr":"{{strategy.order.price}}-0.02*{{strategy.order.price}}".
  3. Keep expressions simple — two operands, one operator.

See also: Math Expressions → Error handling.


#8: Leverage above the exchange cap is silently capped

Problem: you set leverage: 100 on a KuCoin pair that caps at 20x. The trade submits with 20x leverage, not 100x. Your risk math is wrong.

Why it happens: exchanges accept the leverage-update call but clamp to their per-pair limit. TVH does not validate against caps because they change per-pair and per-tier.

Fix: check the per-exchange cap before deploying:

ExchangeTypical max
Binance Futures125x (tier-dependent)
Bybit100x
OKX125x
KuCoin Futures20-100x (pair-dependent)
Coinbase Perps20x
BitMEX100x

The Activity Log shows the actual leverage applied — review after the first trade on a new pair.

See also: Futures Options → Leverage and margin mode.


#9: Spot exchanges silently ignore futures-only fields

Problem: you sent leverage: 10 and marginType: "isolated" to exchange: "binance" (spot). The fields are dropped — the trade goes through as a regular spot buy, with no leverage applied.

Why it happens: spot adapters ignore fields they don't understand. No warning, no error.

Fix: double-check the exchange identifier. Spot vs derivatives are separate slugs:

SpotDerivatives
binancebinance-futures, binance-futures-coin
bybit-spotbybit
okx-spotokx (with instrumentType: "swap")
kucoin-spotkucoin
coinbase-spotcoinbase

See also: Core Parameters → exchange.


#10: useFixedSize is supported on every exchange but useEntireAccountBalance is Bybit-only

Problem: you set useEntireAccountBalance: true on a Binance Futures payload. The flag is silently ignored; sizing falls back to whatever's in units / unitsType.

Why it happens: the flag was added for Bybit's Unified Trading Account (UTA) which has a specific "use all" API call. Other exchanges don't expose that primitive.

Fix: for non-Bybit exchanges, compute the full-balance amount in Pine or off-chain and pass it via units + unitsType: "absolute" or useFixedSize.

See also: Sizing → Bybit-only shortcut.


#11: targets amounts interpreted as absolute when targetAmountInPercent is false

Problem: you sent targets: [{ "takeProfitPercent": 1.0, "amount": 50 }] expecting "close 50% of the position at +1%". Instead, TVH tried to close 50 BTC (base-asset absolute).

Why it happens: targetAmountInPercent defaults to false. Without it set, amount is read as an absolute base-asset quantity.

Fix: always set targetAmountInPercent: true when you mean percent:

{
"targetType": "percent",
"targetAmountInPercent": true,
"targets": [
{ "takeProfitPercent": 1.0, "amount": 50 }
]
}

The Trade Command Builder UI defaults the flag to true because percent-of-position is the more common pattern; hand-written payloads need to set it explicitly.

See also: Take Profit → targetAmountInPercent.


#12: closeCurrentPosition is silently ignored when there's nothing to close

Problem: you expect closeCurrentPosition: true to gate or error when no position exists. It does neither. With no open position, the close is silently skipped and any entry in the same payload just runs.

Why it happens: closeCurrentPosition flattens whatever is open on the pair (any side), then execution continues to the entry. If there's nothing to flatten, the close step is a no-op — it does not block the entry.

Fix: this is usually what you want for a flip-and-open. Just be aware the entry always runs:

{
"isBuy": true,
"closeCurrentPosition": true,
"units": 0.05
}
  • Position open (either side): it closes first, then the new long opens.
  • No position open: the close is silently ignored and only the new long opens — you end up long even though you also asked to close. If you want "close only, never open", send closeCurrentPosition (or isClose) without any entry fields.

See also: Close & Cancel → Closing a position.


#13: Token leak via shared Pine scripts

Problem: you published a Pine script with the token embedded in the alert_message template string. Anyone who copies the script also copies your token and can trade your account.

Why it happens: the token is a single value that grants trading authority. There's no per-script scope.

Fix:

  1. Use a TVH-rendered webhook URL with the token in the path, not the body. The URL itself authenticates the request; the body's token field is checked but isn't the only mechanism.
  2. In published Pine snippets, use <token> as a literal placeholder and tell users to paste their own.
  3. If you shared a token by accident, contact support to have it reset — there is no self-service token rotation yet.

See also: Core Parameters → token.


#14: {{strategy.order.action}} placeholder for Strategy Tester compatibility

Problem: your Pine strategy works in real-time but fails in Strategy Tester because the alert template hard-codes "isBuy": true for entries and "isSell": true for exits. The Strategy Tester only fires one alert per bar — the entry direction is wrong on the exit fill.

Why it happens: TradingView's Strategy Tester fires a single alert per strategy.entry() or strategy.close() call, and the strategy itself decides which side. The template needs to derive direction from the strategy's decision.

Fix: use {{strategy.order.action}} for orderType:

alert_message = '{"orderType":"{{strategy.order.action}}", ...}'

if longCondition
strategy.entry("L", strategy.long, alert_message=alert_message)
if shortCondition
strategy.entry("S", strategy.short, alert_message=alert_message)

TradingView substitutes {{strategy.order.action}} with "buy" or "sell" depending on the side the strategy ordered. One template, both directions. Works in both real-time alerts and Strategy Tester.

See also: Anatomy → Dynamic values via TradingView placeholders, Pine Placeholders.


When in doubt

  • Test on a demo account first. Bybit, OKX, and Binance (Spot + USD-M Futures) all support free demo trading on the same exchange identifier — you just flag the API key as demo. Binance Coin-M and BitMEX have no demo flag and use separate testnet identifiers instead. See Demo Accounts.
  • Watch the Activity Log. Every trade attempt is logged with the parsed payload and the exchange response. Errors show up there even when they don't bubble back to TradingView.
  • Send the minimum payload first. Start with exchange + pair + orderType + units + token. Add fields one at a time until something breaks — then you know which addition caused it.
  • Compare your JSON against the schema. Drop /schemas/trade-command.schema.json into an IDE that validates against JSON Schema. Most type and field-name errors get flagged before the payload ever leaves your machine.

Next

You're done with Chapter 2. The natural next steps: