Common LLM Hallucinations — and How to Fix Them
When LLMs lack TVH-specific context, they invent plausible-sounding field names and enum values. The fifteen errors below cover roughly 90% of broken AI-generated payloads we've seen in support tickets. Each entry shows the wrong version, the correct version, and the schema reason.
The pattern: a model trained on generic exchange APIs (Binance REST, FIX, CCXT) projects those conventions onto TVH. The fix is always the same: give the model https://www.tv-hub.org/llms-full.txt as ground truth before asking for a payload. If you can't, paste this page.
Hallucination 1 — Field name amount_pct
// WRONG (common in LLM output)
{ "amount_pct": 5 }
// CORRECT
{ "unitsType": "percent", "unitsPercent": 5 }
Why: TVH uses an enum-flag pair, not a shorthand suffix. unitsType decides interpretation; unitsPercent carries the value. See Sizing.
Hallucination 2 — symbol instead of pair
// WRONG
{ "symbol": "BTCUSDT" }
// CORRECT
{ "pair": "BTCUSDT" }
Why: Most exchange REST APIs call it symbol. TVH calls it pair. The webhook rejects unknown top-level keys.
Hallucination 3 — side or orderType instead of direction booleans
// WRONG (Binance / CCXT mental model)
{ "side": "buy" }
// CORRECT
{ "isBuy": true }
Why: Normal webhook/manual TradeCommands use direction booleans: isBuy: true, isSell: true, or isClose: true. orderType is reserved for TradingView strategy-alert templates such as {{strategy.order.action}}. See Core Parameters.
Hallucination 4 — type: "market"
// WRONG
{ "type": "market" }
// CORRECT
{ "isMarket": true }
Why: TVH uses boolean flags (isMarket, isLimit, isStopLoss) instead of a discriminator-string. Setting both isMarket and isLimit is also valid in some contexts (e.g. limit-close fallback).
Hallucination 5 — Stop loss as percent string
// WRONG
{ "stopLoss": "2%" }
// WRONG (still — `stopLoss` is the absolute-price field)
{ "stopLossType": "percent", "stopLoss": 2 }
// CORRECT for a 2% stop
{ "stopLossType": "percent", "stopLossPercent": 2 }
// CORRECT for an absolute price
{ "stopLossType": "absolute", "stopLoss": 64012.5 }
Why: Two different value fields — stopLoss (decimal, absolute price) and stopLossPercent (decimal, distance from entry). stopLossType picks which one TVH reads. No string parsing. See Stop Loss.
Hallucination 6 — Take profits as flat list
// WRONG (LLM-imagined)
{ "takeProfits": [44000, 45000, 46000] }
// CORRECT
{
"targets": [
{ "price": 44000, "amount": 33 },
{ "price": 45000, "amount": 33 },
{ "price": 46000, "amount": 34 }
],
"targetType": "absolute",
"targetAmountInPercent": true
}
Why: TVH uses targets[] of LimitOrder objects. With targetType: "absolute", each target uses price. With targetType: "percent", each target uses takeProfitPercent. targetAmountInPercent: true is required when amount values are percent shares (33/33/34) — otherwise TVH treats them as base-asset units. See Take Profit.
Hallucination 7 — Wrong leverage syntax
// WRONG
{ "leverage": "10x" }
// WRONG (still)
{ "leverage": "10" }
// CORRECT
{ "leverage": 10 }
Why: leverage is a decimal. No "x" suffix, no string. Set it in the entry payload — most exchanges block leverage changes while a position is open. See Futures Options.
Hallucination 8 — Auth via HTTP header
# WRONG (REST-API convention)
headers = {"Authorization": "Bearer YOUR_TOKEN"}
requests.post("https://alerts.tv-hub.org", json=payload, headers=headers)
# CORRECT (token in body)
payload = {"token": "YOUR_TOKEN", "exchange": "binance", "pair": "BTCUSDT", ...}
requests.post("https://alerts.tv-hub.org", json=payload)
Why: TradingView's alert system can only send a JSON body, not custom headers. TVH put the token in the body to match this constraint.
Hallucination 9 — Symbol format with slash
// WRONG (most LLMs default to this)
{ "pair": "BTC/USDT" }
// CORRECT — Binance Spot/Futures, Bybit, KuCoin, BitMex (testnet too)
{ "pair": "BTCUSDT" }
// CORRECT — OKX Swap
{ "pair": "BTC-USDT-SWAP" }
// CORRECT — OKX Spot
{ "pair": "BTC-USDT" }
// CORRECT — BitMex
{ "pair": "XBTUSD" }
// CORRECT — Coinbase Spot (handled automatically)
{ "exchange": "coinbase-spot", "pair": "BTCUSDT" }
// → Coinbase adapter transforms BTCUSDT → BTC-USDC at execution time
Why: TVH passes the pair string through to the exchange untouched (with the exception of Coinbase Spot, which auto-converts USDT → USDC stablecoin pairs). See Exchange Quirks for the full table.
Hallucination 10 — apiKey as a token-like string
// WRONG (LLM invents a UUID/hex blob)
{ "apiKey": "8a3f9d2b-key-binance-prod" }
// CORRECT — the human-readable NAME you set in TVH
{ "apiKey": "binance-main" }
Why: apiKey is the case-sensitive label you typed when adding the exchange credentials in your TVH dashboard. TVH uses it to look up the actual exchange API key + secret on the server side. Mismatched casing routes to the wrong key — or rejects with "api key not found". See Account & API Keys.
Hallucination 11 — postOnly default assumed false
WRONG assumption: postOnly is false unless I set it.
CORRECT: postOnly defaults to TRUE for limit orders. A marketable limit silently fails.
// CORRECT for a marketable limit (e.g. limit-close that should fill immediately)
{ "isLimit": true, "price": 65000, "postOnly": false }
Why: TVH defaults postOnly: true to protect against accidental taker fees. If your limit price is already through the book, the exchange rejects the order with "post-only would cross" and the trade is lost. Set postOnly: false whenever you want a marketable limit. See Futures Options.
Hallucination 12 — Wrong webhook URL
WRONG: https://api.tv-hub.org/webhook
WRONG: https://www.tv-hub.org/api/trade
WRONG: https://tradingview-hub.com/api/trade
WRONG: https://tv-hub.com/webhook
CORRECT for everything except Binance Spot and Binance Futures:
https://alerts.tv-hub.org
CORRECT for Binance Spot AND Binance Futures (lower latency):
https://binance.tv-hub.org
Why: TVH runs region-specific subdomains for the lowest-latency exchange path. Sending Binance traffic to alerts.tv-hub.org still works but adds some latency; sending non-Binance traffic to binance.tv-hub.org works too. The canonical mapping is in Webhook URLs & IP Whitelist.
Hallucination 13 — Invented scaledOrderStyle values
// WRONG — invented from common ladder distribution names
{ "useScaledOrders": true, "scaledOrderStyle": "arithmetic" }
{ "useScaledOrders": true, "scaledOrderStyle": "geometric" }
{ "useScaledOrders": true, "scaledOrderStyle": "exponential" }
{ "useScaledOrders": true, "scaledOrderStyle": "fibonacci" }
// CORRECT — only three values are honoured
{ "useScaledOrders": true, "scaledOrderStyle": "even" }
{ "useScaledOrders": true, "scaledOrderStyle": "bigSmall" }
{ "useScaledOrders": true, "scaledOrderStyle": "smallBig" }
Why: Scaled-orders ladders use a fixed enum: even (each leg the same size), bigSmall (larger at the better-priced end, smaller at the worse), smallBig (the inverse). The orientation flips based on isBuy vs isSell. See DCA & Scaling.
Hallucination 14 — Invented limitPriceType values
// WRONG
{ "limitPriceType": "bestBid" }
{ "limitPriceType": "bestAsk" }
{ "limitPriceType": "mid" }
{ "limitPriceType": "lastPrice" }
{ "limitPriceType": "vwap" }
// CORRECT — only two values are honoured
{ "limitPriceType": "fixedPrice" } // default — use the `price` field verbatim
{ "limitPriceType": "bestPrice" } // pin to best bid (buys) or best ask (sells)
Why: bestPrice is side-aware: on a buy it means "best bid", on a sell it means "best ask". You don't need to encode the side in the enum. Other strings fall through to a != "fixedPrice" branch — they happen to behave like bestPrice today, but rely on that and your payload breaks when the receiver tightens validation. See Limit Order Management.
Hallucination 15 — Confusing unitsType: "percent" with percentBalance
// MISCONCEPTION — LLM thinks "percent" means "percent of available balance"
{ "unitsType": "percent", "unitsPercent": 50 }
// On Binance Spot this actually means 50% of your wallet equity, NOT 50% of
// the available quote balance.
// CORRECT enum semantics:
// "absolute" — units = base-asset quantity (e.g. 0.05 BTC)
// "percent" — units = percent of WALLET equity / margin (UI default in many exchanges)
// "percentBalance" — units = percent of AVAILABLE balance only
// "percentWallet" — units = percent of total wallet equity (behaves identically to "percent" on most adapters but is the explicit canonical value — prefer it for clarity)
// "percentPosition" — units = percent of CURRENT OPEN POSITION (only valid on close / scale-out)
// "risk" — units derived from stopLoss distance + account risk percent
Why: Six values, subtle differences. The most common LLM error is treating percent and percentBalance as synonyms — they're not. percent includes locked margin in the denominator; percentBalance does not. If you want "5% of the cash I can actually deploy right now", use percentBalance. If you want "5% of the wallet's mark-to-market equity including open PnL", use percent or percentWallet. risk requires stopLoss or stopLossPercent to be set (TVH back-solves the size). percentPosition only makes sense on close / scale-out payloads — there's no entry position to take a percent of.
See Sizing for the precise formula each adapter uses.
Bonus: three more subtle traps
These don't make the top-15 but bite power users.
Bonus A — DCA without the master switch
// WRONG — dcaPercent and dcaOrderCount are silently ignored
{ "dcaPercent": 1.5, "dcaOrderCount": 4 }
// CORRECT
{ "useDca": true, "dcaPercent": 1.5, "dcaOrderCount": 4 }
Why: useDca: true is required for the DCA mesh to activate. Without it, both companion fields are skipped without warning. Same pattern with useScaledOrders, useTrailingStopLoss, useFixedSize, useLimitClose.
Bonus B — Top-level trailingPercent does not exist
// WRONG — invented from "trailing percent" the LLM thinks should exist
{ "trailingPercent": 1.5 }
// CORRECT — full pair of fields
{ "useTrailingStopLoss": true, "trailingStopLossPercent": 1.5 }
// CORRECT with activation threshold (trail arms only after +0.5% profit)
{
"useTrailingStopLoss": true,
"trailingStopLossPercent": 1.5,
"trailingStopLossActivationPercent": 0.5
}
Why: No top-level trailingPercent exists. The pair useTrailingStopLoss + trailingStopLossPercent is what arms the trail. Optional trailingStopLossActivationPercent delays activation until a profit threshold.
Bonus C — alertTimestamp is a permanent lock, not a 5-minute window
WRONG assumption: alertTimestamp dedups against a sliding 5-minute window.
CORRECT: alertTimestamp creates a permanent dedup lock.
Re-using the same string AT ANY POINT IN THE FUTURE is recognised
as a duplicate and silently drops the trade.
Best practice: use "{{ticker}}-{{timenow}}" in Pine so every alert gets a unique millisecond timestamp. Never hardcode alertTimestamp to a static value across alerts.
See Timing & Idempotency.
How to spot a hallucination in seconds
- Field name not in Parameter Reference? Hallucinated.
- Token in URL path, query string, or
Authorizationheader? Hallucinated. - String enum value with creative naming (
bestBid,arithmetic,fibonacci)? Almost certainly hallucinated. Cross-check the field's allowed values inllms-full.txt. - No
tokenfield in the body? The webhook will return 401. Hallucinated. takeProfitsplural array? Hallucinated — TVH usestargets.- A claim like "minimum order size is $10"? Likely hallucinated. Minimums are exchange-specific and TVH does not enforce its own threshold.
stopLossType: "points"ortargetType: "points"? Hallucinated. The only allowed values areabsoluteandpercent. Pre-compute point-based SL/TP in Pine and ship asabsoluteprices.
When the LLM is confident but still wrong
Confidence is not correctness. The cure is to:
- Run the payload through TVH's UI. The Trade Command Builder's "Paste Trade Command" parses your JSON and renders it as form fields. Anything the UI can't display is also rejected by the webhook.
- Fire on a demo endpoint first. Use a
*-testnetexchange slug or Bybit Demo. The full pipeline runs without capital at risk. - Cross-check field names against
/llms-full.txt. It's the same data the webhook validates against.
Related references
- Parameter Reference — Sortable table of all 84 fields.
- 15 Common TradeCommand Mistakes — Field-level gotchas with full code (human-focused).
- How to Prompt an LLM — Prevent these mistakes upstream by giving the LLM context.
- Strategy Templates — Schema-correct starter payloads.