Webhook Commands
Complete reference for all 29 webhook signal commands, parameters, multi-strategy rules, error messages, tier limits, and common failure scenarios.
Overview#
PineHook receives trading instructions from TradingView as webhook HTTP requests and delivers them to your MT5 terminal over an encrypted connection. Each webhook payload contains a license key, a command, a symbol (for most commands), and any additional parameters. The system validates the payload, enforces subscription limits, and queues the signal for delivery to the EA.
There are 29 commands organized into seven categories: market orders, pending orders (explicit type), pending orders (auto-detect), close commands, cancel commands, trade management, and EA control. Understanding which category a command belongs to matters because different rules apply. Close and cancel commands, for example, bypass symbol filters and work even when a subscription has lapsed.
Signal Formatting Quick Reference#
Both CSV and JSON formats are accepted. Commands are case-insensitive. The three required fields in every signal are the license key, the command, and the symbol (symbol may be omitted for a small number of commands that operate globally; see the command catalog).
CSV:
LICENSE_KEY,command,SYMBOL,param1=value1,param2=value2
JSON:
{"license":"LICENSE_KEY","action":"command","symbol":"SYMBOL","param1":value1}
Symbol format rules: 1 to 20 alphanumeric characters only. Slashes, dashes, and underscores are not allowed. Lowercase input is accepted and auto-uppercased on receipt (eurusd becomes EURUSD). EUR/USD (slash) and eur-usd (dash) are invalid; EURUSD is correct.
For full formatting rules, encoding edge cases, and JSON schema details, see the Signal Formatting reference.
Command Catalog#
Category 1: Market Orders#
buy: Open a long position at market price.
sell: Open a short position at market price.
Both commands share the same parameter set:
| Parameter | Required | Description |
|---|---|---|
size_lots | One of the two | Fixed lot size |
size_pct | One of the two | Risk percentage of account balance. Requires a stop loss parameter. |
sl_pips / sl_price / sl_pct | Required when using size_pct | Stop loss (use one) |
tp_pips / tp_price / tp_pct | Optional | Take profit (use one) |
magic | Required in Multi-Strategy mode | Trade identifier |
delay | Optional (paid plans only) | Seconds to delay execution (max 86,400). Values above 86,400 are rejected with HTTP 400 and recorded in Signal Logs, Rejected Signals. |
closetime | Optional | Seconds until auto-close (1 to 2,592,000) |
maxspread_pips / maxspread_pct | Optional | Maximum acceptable spread |
marketprice | Optional | Reference price for stale-signal detection |
active_start / active_end | Optional | Override active trading hours (HH:MM, 24-hour). Both bounds must be supplied together. Providing one without the other is rejected with HTTP 400. Override is scoped per-license, not account-wide. |
distance_pips / distance_pct / distance_atr | Optional | Activate trailing stop on the resulting position |
be_trigger_pips / be_trigger_pct / be_trigger_price | Optional | Activate auto-breakeven on the resulting position |
tp1_pips through tp10_pips (and variants) | Optional | Multi-target take profit schedule |
sl1_pips through sl10_pips (and variants) | Optional | Multi-target stop loss schedule |
comment | Optional | Accepted but not applied. The broker order comment is reserved for PineHook's internal signal-ID tag (SIG:<id>), so a custom comment has no effect. |
eaoff | Optional | Block the signal and disable entries for the named symbol simultaneously |
Sizing rules: size_lots and size_pct are mutually exclusive. size_pct without a stop loss is rejected because the risk calculation requires a known stop distance.
Stop loss priority when multiple are given: _price wins over _pct, which wins over _pips.
For slippage protection parameters, see the Slippage Protection reference. For trailing stop and auto-breakeven parameters, see the Trail and Breakeven reference. For multi-target TP/SL, see the Multi-TP/SL reference.
Category 2: Pending Orders (Explicit Type)#
buylimit: Buy limit order. Entry must be below current ask price.
buystop: Buy stop order. Entry must be above current ask price.
selllimit: Sell limit order. Entry must be above current bid price.
sellstop: Sell stop order. Entry must be below current bid price.
All four share the same parameter set as market orders, with two additions:
| Parameter | Required | Description |
|---|---|---|
price | Yes | Entry price |
expiry | Optional | Seconds until order expires (1 to 86,400; Trial: max 900). Values above 86,400 are rejected with HTTP 400 on every plan and recorded in Signal Logs, Rejected Signals. |
expiry_time | Optional | ISO 8601 datetime for expiration (e.g., 2024-01-15T14:30:00Z) |
Only one of expiry or expiry_time may be specified. expiry_time is interpreted as UTC: a bare or Z-suffixed timestamp is UTC, an explicit ±HH:MM offset is honored, and the EA converts the resulting UTC instant to broker server time for placement — so it expires at the correct real-world moment on any broker. To express a broker-local or other local time, append the matching ±HH:MM offset. Or use expiry in seconds to avoid timezones entirely.
How to write the time you want (all expire at the 14:30 figure):
| You want it to expire at… | Write it as | Example |
|---|---|---|
| A specific UTC time | bare, or append Z | expiry_time=2024-01-15T14:30:00Z |
| Your broker's server time (the MT5 chart clock) | append your broker's UTC offset | GMT+3 broker → expiry_time=2024-01-15T14:30:00+03:00 |
| A specific local / exchange timezone | append that zone's offset | EST (−05:00) → expiry_time=2024-01-15T14:30:00-05:00 |
| A relative duration (no timezone math) | use expiry in seconds | expiry=3600 (1 hour after placement) |
Category 3: Pending Orders (Auto-Detect)#
buyat: Automatically places a buy limit if price is below the current ask, or a buy stop if price is above the current ask.
sellat: Automatically places a sell limit if price is above the current bid, or a sell stop if price is below the current bid.
Parameters are identical to the explicit pending order commands. The auto-detect logic runs on the EA at order placement time using the live market price.
Category 4: Close Commands#
Close commands bypass symbol filters (whitelist and blacklist rules are ignored) and execute regardless of subscription status. This is intentional: a lapsed subscription should not prevent you from closing open positions.
closelong: Close all buy/long positions for the specified symbol.
closeshort: Close all sell/short positions for the specified symbol.
closeall: Close all positions (both buy and sell) for the specified symbol. Without a symbol, closes every open position on the MT5 terminal regardless of symbol, magic number, or which EA opened them. See the "Known Edge Cases" section below.
closecancelall: Close all positions and cancel all pending orders for the symbol. Without a symbol, sweeps the entire terminal.
closeallprofit: Close all positions whose net P&L (floating profit + swap + commission) is positive for the symbol. A position that is up on price but net-negative after swap/commission is treated as a loss and left open. Optional minprofit parameter sets a minimum threshold in price pips; positions below the threshold are left open.
closeallloss: Close all positions whose net P&L (floating profit + swap + commission) is negative for the symbol — including a price-positive position that swap/commission have pushed underwater. Optional maxloss parameter (price pips) prevents closing very large losers.
Partial close parameters (available on closelong, closeshort, closeall):
| Parameter | Description |
|---|---|
close_pct | Percentage of the original (initial) position volume to close. 0 to 100. |
close_lots | Fixed lot amount to close. |
Only one of close_pct or close_lots may be used per signal. close_pct calculates from the original position size, not the current remaining size. Two signals of close_pct=50 together close the full original position.
Volume is rounded down to the nearest valid lot step. If rounding causes the close amount to reach or exceed the remaining position, the EA performs a full close instead.
Close execution policy: Close commands are sent to the broker with a very large deviation allowance so they are not blocked by requotes during fast markets or news events. When the actual fill deviates significantly from the reference price, the Signal Logs page shows an amber "Extreme volatility" pill on that row. This is a disclosure, not an error; the position was closed successfully.
Category 5: Cancel Commands#
Cancel commands bypass symbol filters, just like close commands.
cancellong: Cancel all pending buy orders (both buy limit and buy stop) for the symbol.
cancelshort: Cancel all pending sell orders (both sell limit and sell stop) for the symbol.
cancelall: Cancel all pending orders for the symbol.
Optional magic parameter filters cancellation to positions opened with a specific magic number. In Multi-Strategy mode, cancellong and cancelshort require magic; cancelall does not.
Category 6: Trade Management#
breakeven: Move the stop loss to the position's entry price (immediate execution). Optional be_offset_pips, be_offset_pct, or be_offset_price parameters lock in a small amount of profit rather than moving to exactly breakeven. It has no direction filter and affects every matching position on the symbol (both sides on a hedging account); to move only one side, use modify with direction=long or direction=short.
For auto-triggered breakeven (fires when profit reaches a threshold), use be_trigger_pips, be_trigger_pct, or be_trigger_price on the opening buy or sell signal instead. Full details in the Trail and Breakeven reference.
nobreakeven: Remove the auto-breakeven monitor from matching positions without changing the current stop loss.
trail: Register or update a trailing stop on existing positions. Requires exactly one distance parameter: distance_pips, distance_pct, or distance_atr. Full parameter reference in the Trail and Breakeven reference.
notrail: Remove the trailing stop monitor from matching positions without changing the current stop loss.
modify: Change the stop loss and/or take profit on open positions or pending orders. A single modify can move both the base SL/TP and individual multi-TP/SL target levels (tp1_pips, sl1_price, …) together; the ACK reports failure if the base move is invalid (it is not masked by a valid leg edit).
| Parameter | Required | Description |
|---|---|---|
sl_pips / sl_price / sl_pct | At least one SL or TP | New stop loss |
tp_pips / tp_price / tp_pct | At least one SL or TP | New take profit |
direction | Optional | long or short, limits modification to one side (hedging accounts only; ignored on netting) |
target | Optional | sl, tp, or both (default: both) |
order_type | Optional | buystop, buylimit, sellstop, or selllimit, target pending orders instead of positions |
magic | Optional | Filter by magic number |
order_type and direction cannot be combined (pending orders have an implicit direction). order_type also cannot be combined with individual multi-target parameters (tp1_pips, sl2_price, etc.); those apply to open positions only.
Example signals (both targets):
# Open positions (default, no order_type): set SL and TP on all open EURUSD positions
LICENSE,modify,EURUSD,sl_price=1.0750,tp_price=1.0950
# Pending orders: add order_type to retarget the pending order instead of positions
LICENSE,modify,EURUSD,sl_price=1.0750,order_type=buystop
Category 7: EA Control#
EA control commands update the EA's symbol filter in real time. They do not require a symbol in the standard position; instead, the symbol is specified as a parameter value.
eaon: Enable trading for one or more symbols. Removes the symbols from the active blacklist, or adds them to the active whitelist.
eaoff: Disable new entries for one or more symbols. Adds them to the blacklist or removes them from the whitelist.
eaoffexcept: Switch to whitelist mode. Only the listed symbols may receive entry signals; all others are blocked. At least one symbol is required; an empty list is rejected and surfaces in Signal Logs, Rejected Signals with reason "eaoffexcept requires at least one symbol." Use eaoff,ALL if the intent is to block everything.
eaonexcept: Switch to blacklist mode. The listed symbols are blocked; all others are permitted. At least one symbol is required; an empty list is rejected with reason "eaonexcept requires at least one symbol." Use eaon,ALL if the intent is to enable everything.
closealleaoff: Always global — flattens the entire account (every symbol, every magic — any symbol you include is ignored) and then disables the EA. The disable is sticky: the EA stays halted until you send eaon,ALL (a symbol-specific eaon, a settings update, and a restart do not lift it; the stop persists to disk).
closecancelalleaoff: Same as closealleaoff plus it cancels all pending orders. The most complete emergency shutdown. Also always-global and sticky (only eaon,ALL re-enables).
clearqueue: Cancel scheduled delayed signals. Optional symbol and magic filters narrow the scope. Without parameters, clears all pending delayed signals for the license.
Note: canceldelay (the old name for clearqueue) returns an error directing you to update your alert template.
Symbol format for EA control parameters: Use the symbol name directly (e.g., eaon=EURUSD). Multiple symbols are separated by semicolons (eaoff=EURUSD;GBPUSD;USDJPY). The special value ALL applies the command to every symbol (eaon=ALL re-enables all trading).
Combining EA control with a trade command: eaon and eaoff can be appended to any trade command as inline parameters. The ordering rule is eaon before, eaoff after. eaon is applied before the trade so a re-enable-and-enter signal works in one shot, and eaoff is applied after the trade is queued so an exit-and-disable signal (e.g., closelong,EURUSD,eaoff=EURUSD or sell,EURUSD,eaoff=EURUSD) closes / sells the position first and then disables the symbol for future entries. buy,EURUSD,eaoff=EURUSD is therefore a "one final entry then stop" pattern: the buy fires once, subsequent buys on EURUSD are rejected by the new filter. If the inline eaoff= would push the filter past the 100-symbol limit, the trade still executes and the response body carries a filter_warning field. Only eaon= overflow blocks the whole signal (because eaon is applied before the trade).
Symbol filter is capped at 100 symbols. Any EA control command (standalone or inline) that would leave the filter with more than 100 entries is rejected with block_reason="filter_list_too_long". The Signal Logs row shows the would-be size and suggests using the inverse filter mode. If you keep adding to a blacklist, the rejection nudges you toward a whitelist (and vice versa). The filter is unchanged on rejection. See the EA Control Commands reference for the full message format and supported triggers.
Where to view the current filter: the dashboard Licenses page shows each license's symbol filter inline on the license card, behind an eye-toggle icon. The panel reveals the current mode (Blacklist or Whitelist) and the full list of symbols, and updates live when the filter changes via any webhook command. Use that view for "what's currently disabled / allowed?" questions instead of a webhook command to introspect state. See the EA Control Commands reference for full detail.
Per-license vs global filter, they stack additively. Every EA control command mutates only the per-license filter of the license attached to the signal; it never touches your global filter. But the global filter (set from the dashboard's Global Settings page) and the per-license filter both apply to every signal. A symbol must pass both to be allowed. Common case: eaoffexcept=BTCUSD;ETHUSD previously replaced the effective filter, but now it stacks on top of global. If you send eaoffexcept and now nothing trades on this license, check whether your global filter is also a whitelist whose list doesn't overlap with your per-license list. That intersection collapse is the most common cause. Full composition table is in the EA Control Commands reference.
Multi-Strategy Mode: Parameter Rules#
What It Is#
Magic Number Mode controls how the EA assigns and interprets magic numbers on trades. It is configured in the MT5 EA input dialog (not the dashboard) and reported to the server at connection time.
Single mode (default): The signal's magic parameter is ignored. All trades receive the EA's configured Default Magic Number regardless of what the signal says. Close and modify commands affect only positions that carry the default magic number.
Multi-Strategy mode: The signal's magic parameter is used as the trade's magic number. Opening commands require magic; signals without it are rejected. A targeted close/modify/cancel without a magic is rejected in this mode. A symbol-scoped sweep (closeall,EURUSD, cancelall,EURUSD, closecancelall, closeallprofit, closeallloss, or clearqueue with a symbol) is likewise rejected without a magic in this mode; only the symbol-less form of those sweeps runs magic-free as the cross-strategy emergency brake. An explicit magic on the symbol-less form is honored: magic=N acts on that one strategy across every symbol (a strategy-wide sweep). Be explicit: use magic=N for one strategy, magic=ALL to act on every strategy's positions on the scope, or magic=0 for only manual (magic-0) trades. Negative values are rejected.
Which Commands Require magic in Multi-Strategy Mode#
| Command | Requires magic in Multi mode? | Notes |
|---|---|---|
buy | Yes | Rejected without it |
sell | Yes | Rejected without it |
buylimit | Yes | Rejected without it |
buystop | Yes | Rejected without it |
selllimit | Yes | Rejected without it |
sellstop | Yes | Rejected without it |
buyat | Yes | Rejected without it |
sellat | Yes | Rejected without it |
closelong | Yes | Rejected without it. Use magic=ALL for a cross-magic sweep (or closeall) |
closeshort | Yes | Rejected without it. Use magic=ALL for a cross-magic sweep (or closeall) |
closeall | Only when a symbol is given | Symbol-scoped (closeall,EURUSD) is rejected without a magic: add magic=N or magic=ALL. Symbol-less closeall needs no magic (emergency brake, sweeps the entire terminal); adding magic=N scopes it to that strategy across all symbols |
closecancelall | Only when a symbol is given | Same rule: symbol-scoped needs magic=N/magic=ALL; symbol-less with no magic sweeps the entire terminal, with magic=N only that strategy |
closeallprofit | Only when a symbol is given | Same rule: symbol-scoped needs magic=N/magic=ALL; symbol-less with no magic sweeps the entire terminal, with magic=N only that strategy |
closeallloss | Only when a symbol is given | Same rule: symbol-scoped needs magic=N/magic=ALL; symbol-less with no magic sweeps the entire terminal, with magic=N only that strategy |
cancellong | Yes | Rejected without it. Use magic=ALL for a cross-magic sweep (or cancelall) |
cancelshort | Yes | Rejected without it. Use magic=ALL for a cross-magic sweep (or cancelall) |
cancelall | Only when a symbol is given | Symbol-scoped (cancelall,EURUSD) is rejected without a magic: add magic=N or magic=ALL. Symbol-less cancelall needs no magic (sweeps the entire terminal); adding magic=N scopes it to that strategy across all symbols |
breakeven | Yes | Rejected without it |
nobreakeven | Yes | Rejected without it |
modify | Yes | Rejected without it |
trail | Yes | Rejected without it |
notrail | Yes | Rejected without it |
closealleaoff | No | EA control commands are not magic-scoped |
closecancelalleaoff | No | EA control commands are not magic-scoped |
eaon | No | Symbol-scoped only |
eaoff | No | Symbol-scoped only |
eaoffexcept | No | Symbol-scoped only |
eaonexcept | No | Symbol-scoped only |
clearqueue | Only when a symbol is given | Symbol-less clearqueue clears everything and needs no magic. A symbol-scoped clearqueue,EURUSD is rejected without a magic in Multi mode (add magic=N/magic=ALL); because clearqueue has no EA backstop, it is also rejected when the EA's mode is not yet known to the server |
Error Messages When magic Is Missing in Multi-Strategy Mode#
These messages are returned as HTTP 400 and appear in the Signal Logs, Rejected Signals tab as well as in the TradingView alert execution status.
- Opening commands:
"Magic number required for buy in Multi-Strategy mode. Add magic=N to stamp the new position with your strategy's magic."(same pattern forsell,buylimit, etc.) - Targeted close:
"Magic number required for closelong in Multi-Strategy mode. Add magic=N for one strategy, magic=ALL for a cross-magic sweep, or use closeall." - Targeted cancel:
"Magic number required for cancellong in Multi-Strategy mode. Add magic=N for one strategy, magic=ALL for a cross-magic sweep, or use cancelall." - Trade management:
"Magic number required for modify in Multi-Strategy mode. Add magic=N to target your strategy's positions."(same pattern forbreakeven,trail, etc.)
Fail-Open Behavior#
If the EA has not yet connected and reported its Magic Number Mode to the server, the server cannot determine which mode is active. In this case it accepts the signal without enforcing the magic requirement, and the EA enforces the rule locally when the signal arrives. If the EA has connected and reported Multi-Strategy mode, the server enforces the requirement immediately at the webhook, and the error appears before the signal is queued.
Worked Examples#
Signal missing magic in Multi-Strategy mode (rejected):
LICENSE,buy,EURUSD,size_lots=0.1,sl_pips=50
Response: HTTP 400, "Magic number required for buy in Multi-Strategy mode."
Same signal with magic added (accepted):
LICENSE,buy,EURUSD,size_lots=0.1,sl_pips=50,magic=1001
Cross-magic close sweep, symbol-less emergency brake (accepted without magic):
LICENSE,closeall
Symbol-scoped cross-magic sweep in Multi-Strategy mode (needs magic=ALL; the symbol-scoped form is rejected without a magic):
LICENSE,closeall,EURUSD,magic=ALL
Targeted close for one strategy (accepted with magic):
LICENSE,closelong,EURUSD,magic=1001
Command Interaction Rules#
Symbol-less closeall and closecancelall close everything on the MT5 terminal, including positions from other EAs and manual trades, with no magic filter. They serve as the account's emergency brake.
Close and cancel commands bypass symbol filters. A symbol can be on the blacklist, not on the whitelist, or have eaoff applied; close and cancel commands still execute. New entry commands (buy, sell, pending orders) are blocked by filters; closing an existing position is not.
eaoff inline on an entry signal blocks that specific trade and simultaneously disables entries for the named symbol. LICENSE,sell,EURUSD,eaoff=EURUSD closes nothing and blocks the sell, but from that point forward new EURUSD entries are disabled.
delay= defers the entire signal, regardless of command. Entries, exits, trade management, symbol filter toggles (eaon/eaoff/eaoffexcept/eaonexcept), and clearqueue itself all honor delay=. A delayed closeall does not execute until the delay expires; if positions are already closed by then the EA finds nothing to close (a no-op). A delayed eaoff EURUSD defers the filter mutation, so EURUSD remains tradeable until the scheduled time. A delayed clearqueue cancels everything else queued at fire time. Use an immediate clearqueue to cancel pending delayed signals.
active_start and active_end affect entry commands only. Close, cancel, modify, breakeven, trail, and EA control commands execute regardless of active hours.
Trailing stop and auto-breakeven coexist but cancel each other on first fire. If both are registered on the same position, whichever fires first removes the other. If both would trigger at the same price (within one pip), neither is registered and the trade ACK includes a warning.
breakeven clears multi-SL targets on affected positions. If a position has a multi-target stop loss schedule, a breakeven command replaces the schedule with a single broker-level stop at the breakeven price.
Audit-Trail Guarantee#
Every webhook the relay can attribute to a license appears as a row in your Signal Logs, regardless of whether it was executed, queued, blocked, or rejected. Every rejection also appears in the Rejected Signals tab with the verbatim user-facing message you would have seen in the TradingView alert response.
What's attributable, what's not:
- Attributable (always appears in your Signal Logs): any webhook that contained a valid license key, even if it was then rejected for any other reason: rate limits, tier gates, multi-magic missing magic, malformed parameters, signal auth failure, symbol filter, queue full, filter overflow, conflicting parameters, EA-side trade failures, etc.
- Not attributable (admin-side only, not in your per-user Signal Logs): rejections that happened before the license could be parsed: invalid client IP, body too large, body unreadable, empty body, IP banned by the rate limiter for repeated authentication failures.
Latency on every signal: The Delivery latency column in Signal Logs reports the fastest technically-correct value for each signal, paired with a tier indicator. The headline number on every executed trade is the round-trip from PineHook's edge to the EA: the number worth bragging about.
- Tier 1, Executed trades: the true end-to-end delivery latency, from your alert arriving to your signal reaching the EA. Plain pill, e.g. "42ms".
- Tier 2, Queued, EA hasn't ACK'd yet: the time to queue your signal. PineHook's commitment is met; the EA round-trip just hasn't completed. Pill carries a small "live" indicator and a tooltip explaining the measurement.
- Tier 3, Blocked / rejected: the Delivery column is intentionally blank (
--). Pre-rejection processing time is a different measurement than delivery time and conflating them in the headline column is misleading. The pre-rejection time is shown separately in the row's expanded detail under a "Validated in Xms" label.
The Execution latency column (the EA round-trip, from handing your signal to the EA until its fill confirmation) is null when there's no execution to measure (blocked / rejected / not yet ACK'd). That's intentional.
Block Reason Catalog#
When a signal is blocked, the dashboard displays a block_reason short code on the Signal Logs row. This catalog explains every value you might see, so you can route to the exact issue based on the reason alone.
Surface column legend:
- Signal Logs + Rejected Signals: both surfaces. The platform extracted a valid license from the payload and persisted both a blocked Signal Logs row and a Rejected Signals row.
- via EA ACK: the Signal Logs row was created at receipt time as accepted/sent; the EA later refused execution and the ACK transitioned the row to blocked status. No Rejected Signals row is written for these (broker outcomes route through trade rejection/failure reason instead).
- †: un-attributable: the rejection happened before the platform could parse a license from the payload, so the row appears in admin-side observability only. Surface row reads "TradingView response only".
block_reason | Surfaces in | What happened |
|---|---|---|
auth_failed † | TradingView 401 + admin Edge Cases | License-validation failure (key not found, inactive, deleted, trial expired). Generic message returned to prevent license-key enumeration. |
missing_auth | Signal Logs + Rejected Signals | Signal authentication is enabled for this license and the signal arrived without the auth= parameter. When authentication is on, every signal must include it. |
invalid_auth | Signal Logs + Rejected Signals | The auth= value didn't match the configured password. |
verification_error | Signal Logs + Rejected Signals + admin Troubleshooting | An internal error occurred while verifying the signal-auth password (fail-closed). Contact support; this is not a user configuration issue. |
rate_limited | Signal Logs + Rejected Signals | Per-minute signals/min cap exceeded for this license's tier. |
rate_limited_daily | Signal Logs + Rejected Signals | Per-day signals cap exceeded. Upgrade or wait 24h. |
pending_order_ceiling | Signal Logs + Rejected Signals | Live pending-order count at the tier ceiling (anti-spam: Trial 5, Standard 50, Pro 500, Premium 5,000, per license, no global cap). Slots free automatically on fill, on explicit cancel, or on declared expiry. Cancel unfilled orders or wait for them to expire. |
delay | Signal Logs + Rejected Signals | delay= is paid-only; the trial/beta tier sent it. Upgrade to enable. (tier_gate_delay is the Prometheus metric label; the on-row block_reason is delay.) |
expiry / expiry_time | Signal Logs + Rejected Signals | Pending-order expiry= exceeded the tier ceiling. (tier_gate_expiry is the Prometheus metric label; the on-row block_reason is expiry for the expiry= gate or expiry_time for the expiry_time gate.) |
multi_magic_required | Signal Logs + Rejected Signals | EA in Multi-Strategy mode and the command needs an explicit magic=. |
symbol_filtered | Signal Logs + Rejected Signals | The trade symbol is blocked by the active blacklist or not in the active whitelist. |
queue_ceiling_per_license | Signal Logs + Rejected Signals | Delayed-signal queue full for this license. |
queue_ceiling_global | Signal Logs + Rejected Signals | Delayed-signal queue at system capacity. |
queue_full | Signal Logs + Rejected Signals | The immediate per-license queue is at its maximum size. EA may be offline or processing too slowly. |
filter_list_too_long | Signal Logs + Rejected Signals | Symbol filter would exceed 100 entries. Use the inverse mode (eaonexcept / eaoffexcept). |
missing_symbol | Signal Logs + Rejected Signals | Command requires a symbol (closelong / closeshort / cancellong / cancelshort / eaon / eaoff / eaoffexcept / eaonexcept) and none was provided. |
pending_order_no_price | Signal Logs + Rejected Signals | Pending order command sent without price=. |
conflicting_offsets | Signal Logs + Rejected Signals | breakeven received more than one of be_offset_pips / be_offset_pct / be_offset_price. |
conflicting_size | Signal Logs + Rejected Signals | Both size_lots and size_pct were provided. Use exactly one. |
invalid_symbol | Signal Logs + Rejected Signals | Symbol contains characters that don't match ^[A-Z0-9]{1,20}$. |
param_invalid | Signal Logs + Rejected Signals | One or more parameters failed validation (range, format, type). The Rejected Signals row's warnings field names the specific parameter and shows the offending raw value. |
parse_error †‡ | Signal Logs + Rejected Signals (best-effort) | Malformed CSV/JSON, missing required fields. ‡ Attributable only when the parser can recover the license key from the malformed payload; otherwise admin-side only. |
filter_backend_unavailable | Signal Logs + Rejected Signals | A temporary internal error prevented the symbol-filter update; transient. Retry. |
active_start / active_end | Signal Logs + Rejected Signals | Only one of active_start / active_end provided. Both bounds must be supplied together. (active_hours_partial_override is the Prometheus metric label; the on-row block_reason is whichever bound was provided.) |
symbol_filter | Signal Logs (via EA ACK; no Rejected Signals row) | EA-side rejection because the symbol was disabled at fill time. |
active_hours | Signal Logs (via EA ACK; no Rejected Signals row) | EA dropped the signal because the local clock was outside the configured active-hours window. |
EA-side post-execution failures: status="failed", no block_reason#
The two block_reason values above (symbol_filter, active_hours) are the only specific reasons the EA's ACK handler sets. Every other EA-side failure (broker error, off-grid SL/TP, position-limit-exceeded, spread-too-wide, insufficient margin, pending-order-expired, invalid-ticket, generic trade-gate rejections) surfaces with status="failed" and the EA's verbatim error string in the failure_reason column. The block_reason column is NULL for these.
To find a specific EA-side failure category in Signal Logs, filter by status="failed" and search the failure_reason text. Example mappings:
| EA failure type | Typical failure_reason substring |
|---|---|
| Broker error | ERR_INVALID_PRICE, ERR_TRADE_DISABLE, ERR_INVALID_TRADE_VOLUME, requote, 10004, 10006, etc. (broker-specific MT5 retcode strings) |
| SL/TP off-grid | stop level, stops level, invalid stops, off grid |
| Position-limit-exceeded | position limit, max long lots, max short lots, would exceed position limit |
| Spread-too-wide | spread, pips spread exceeds |
| Insufficient margin | margin, not enough money |
| Pending-order expired | expired, order expiration |
| Invalid ticket | invalid ticket, position not found, position already closed |
The same row also appears in the dedicated Rejected Signals tab with the verbatim failure_reason rendered in the warnings field, regardless of which substring matches. When investigating "why did this trade fail," always check the Rejected Signals row first. The EA's exact message is there verbatim.
HTTP 400: Validation Failure#
Appears on: Signal Logs, Rejected Signals tab (row is created if the license can be extracted from the payload) and the TradingView alert execution status. The reject reason is stored verbatim; very long reasons are shortened.
| Message | Cause | Action |
|---|---|---|
"Unknown command 'xxx' - did you mean 'yyy'?" | Unrecognized command name (spelling close to known command) | Correct the command name in the TradingView alert |
"Unknown command 'xxx' - valid commands include: buy, sell, closelong, closeshort, closeall, etc." | Unrecognized command name (no close match) | Check the command list; confirm the alert template |
"canceldelay was renamed to 'clearqueue' for clarity. Update your TradingView alerts to use 'clearqueue' with the same parameters..." | canceldelay used instead of clearqueue | Update the TradingView alert template to use clearqueue |
"Missing 'license' - signals require: license, action, symbol" | License field absent from payload | Verify the alert template includes the license key |
"Missing 'action'" or "Missing 'symbol'" | Required field absent | Add the missing field to the alert |
"Invalid JSON: {detail}" | Malformed JSON payload | Validate the JSON structure in the TradingView alert |
"Empty request body" | Request body is empty | Confirm TradingView is sending the alert body |
"Conflicting position sizing parameters: size_lots, size_pct. Use either size_lots (fixed volume) or size_pct (risk-based), not both." | Both sizing methods used | Remove one |
"Magic number required for {command} in Multi-Strategy mode. ..." | Missing magic parameter in Multi-Strategy mode | Add magic=N to the signal |
"Gap detected in TP target numbering. Missing: TP2." | Non-sequential multi-target numbering | Renumber targets sequentially with no gaps |
"{param} out of allowed range {bounds}" | Parameter value outside valid range | Correct the value to be within range |
"Invalid symbol format: '{symbol}'." | Symbol contains invalid characters | Use uppercase alphanumeric only, 1 to 20 characters |
"Conflicting {parameter} parameters" | Multiple mutually exclusive parameters specified | Keep only one |
"`delay=N` exceeds the 24-hour (86400-second) platform maximum. Reduce the value or split the strategy into multiple alerts." | delay greater than 86,400 seconds. Applies to all plans (no longer silently capped) | Reduce the delay to 86,400 or less; split the alert into multiple signals |
"`expiry=N` exceeds the 24-hour (86400-second) platform maximum for pending-order expiry." | expiry greater than 86,400 seconds. Applies to all plans including Premium (the previous HTTP 402 "upgrade" message was misleading because no plan supports a longer expiry) | Reduce the expiry to 86,400 or less |
"`expiry_time` resolves to more than 24 hours in the future, which exceeds the platform maximum. Use a closer datetime." | expiry_time value resolves more than 24 hours in the future | Use a datetime closer to now (within 24 hours). The instant is read as UTC (bare/Z) unless an explicit ±HH:MM offset is given. |
"`active_start` was provided without `active_end`. Both `active_start` and `active_end` must be supplied together..." (or vice versa with the names swapped) | Only one of the two active-hours bounds was supplied. The platform no longer silently fills the missing side from the per-license configured value because that combined a typo with the configured window and produced an unintended window. | Either add the missing bound to the alert template, or remove both bounds entirely so the per-license configured window applies. The override is per-license, not account-wide. |
HTTP 401: Authentication Failed#
Appears on: TradingView alert execution status.
| Message | Cause | Action |
|---|---|---|
"Authentication failed" | License key is invalid, inactive, or does not exist | Verify the license key copied into the TradingView alert is correct and the license is active on the dashboard |
HTTP 402: Plan Restriction#
Appears on: Signal Logs, Rejected Signals tab (when the platform extracts a valid license from the payload) and the TradingView alert execution status.
| Message | Cause | Action |
|---|---|---|
"Delayed signals require a paid plan. Upgrade to use delay=." | delay= parameter used on a trial account | Upgrade to a paid plan or remove the delay= parameter |
"Pending-order expiry exceeds this plan's {N}-minute cap. Upgrade for longer expiries." | expiry or expiry_time exceeds the trial-tier ceiling (15 minutes). For paid plans, values above 86,400 seconds are returned as HTTP 400 rather than 402 because no plan supports a longer expiry. | Reduce the expiry to 900 seconds or upgrade to a paid plan |
HTTP 413: Payload Too Large#
Appears on: TradingView alert execution status.
| Message | Cause | Action |
|---|---|---|
"Request entity too large" | Webhook body exceeds size limit | Shorten the alert body; signal payloads should be compact |
HTTP 429: Rate Limited or Queue Full#
Appears on: TradingView alert execution status. Response headers provide additional context.
| Message | Cause | Action |
|---|---|---|
"Rate limit exceeded" | Per-minute signal rate exceeded for the tier | Reduce alert frequency; check the tier's signals-per-minute allowance |
"Daily signal limit reached ({cap}). Limit resets every 24 hours." | Daily signal cap reached | Wait for the 24-hour reset, or upgrade to a higher tier |
"license_queue_full" | Per-license in-flight queue at its limit (1,000 signals) | Confirm the EA is connected and draining the queue; reduce alert frequency |
"Live pending-order limit reached ({current}/{max})." | Unfilled pending order count at tier limit | Slots drain automatically when the EA reports the order filled. Cancel unfilled orders (cancellong / cancelshort / cancelall) or wait for the declared expiry to elapse if you are genuinely at the cap; see X-Pending-Order-Count header. |
"Delayed-signal queue is full for this license. Cancel pending signals with clearqueue..." | Per-license delayed signal queue at capacity | Send clearqueue to cancel pending delayed signals |
HTTP 200 with status=blocked: Signal Accepted but Execution Prevented#
TradingView sees HTTP 200 so the alert is marked as "fired successfully," but the position is never opened. The signal appears as a Blocked or Failed row in Signal Logs with a specific label drawn from the block/failure reason. Both server-side rejections (webhook-time) and EA-side rejections (ACK-time) create a row in the Rejected Signals tab. Server-side rejections are also pushed to the connected EA and printed in the MT5 Experts tab in real time (e.g. ⚠ Signal rejected by server: buy GBPUSD — Symbol GBPUSD is blocked (in blacklist)), so the rejection is visible locally in MetaTrader, not only on the dashboard. EA-side rejections include the EA's own validation refusals (inverted pending order, unprotected stop, invalid lot size, expiry in the past) and transient broker/market conditions (spread too high, price deviation, insufficient margin, broker retcodes). When the EA identifies a specific offending parameter — e.g. price on an inverted pending order — the Rejected Signals row highlights that parameter in red in the raw payload; transient/broker conditions appear with the message only.
| Block Reason | Surface | Meaning | Action |
|---|---|---|---|
missing_auth | Signal Logs + Rejected Signals tab | Signal authentication is enabled for this license but no auth= password was included | Add auth=PASSWORD to the alert template |
invalid_auth | Signal Logs + Rejected Signals tab | Signal authentication is enabled and an auth= password was included but it is wrong | Correct the password in the alert template; verify it matches the one set on the dashboard |
verification_error | Signal Logs + Rejected Signals tab | An internal error occurred verifying the authentication password | Report to support; this is not a user configuration issue |
not in whitelist | Signal Logs + Rejected Signals tab | Symbol filter is in whitelist mode and this symbol is not on the list | Add the symbol to the whitelist via eaoffexcept, or switch to blacklist mode |
in blacklist | Signal Logs + Rejected Signals tab | Symbol filter is in blacklist mode and this symbol is on the blocked list | Remove the symbol from the blacklist via eaon=SYMBOL |
missing_symbol | Signal Logs + Rejected Signals tab | eaon or eaoff command sent without specifying a symbol | Add a symbol or ALL as the parameter value |
symbol_filter | Signal Logs (label "Blocked: Symbol Filter") | EA's local symbol filter rejected the signal after it was dispatched | Send eaon=SYMBOL or update the symbol filter in dashboard EA Settings |
active_hours | Signal Logs (label "Blocked: Active Hours") | EA dropped the signal because the current local time is outside the configured active_start/active_end window. Distinct from a generic "Failed" row so you can tell at a glance that PineHook (not the broker) refused the trade. | Adjust active hours in dashboard EA Settings, or include active_start / active_end in the alert to override for that specific signal |
Where to Look When Things Go Wrong#
| Error surface | Where to look |
|---|---|
| TradingView shows a non-200 status code | Open the TradingView alert history, click the failed execution, and read the error body. |
| Trade never appeared but TradingView shows a 200 response | Go to your Signal Logs, click the Rejected Signals tab. Find the signal by timestamp. Read the block reason column. |
| Trade opened but not as expected (wrong size, no SL, etc.) | Open Signal Logs, find the signal, expand the row. Compare the parsed parameters shown against what the alert template sends. |
| EA shows connected but signals arrive late or out of order | Check if delay= is set in the alert. Send clearqueue for that symbol to drain any accumulated delayed signals. |
Plan & Tier Differences#
| Feature | Trial | Standard | Pro | Premium |
|---|---|---|---|---|
| Signals per minute | 60 | 150 | 450 | 1,500 |
| Signals per day | 5,000 | 10,000 | 30,000 | 100,000 |
delay= parameter | No | Yes | Yes | Yes |
| Max queued delayed signals | 0 | 50 | 150 | 500 |
| Max pending order expiry | 15 minutes | 24 hours | 24 hours | 24 hours |
| Max live pending orders | 5 | 50 | 500 | 5,000 |
When the per-minute limit is hit, the webhook returns HTTP 429 with the message "Rate limit exceeded". The signal is not retried. TradingView will not automatically re-fire the alert; the opportunity is missed.
When the daily limit is hit, HTTP 429 is returned with a message indicating the cap and when it resets.
When the trial tier pending order expiry limit (15 minutes) is exceeded, HTTP 402 is returned. You must either upgrade or reduce the expiry.
When a subscription lapses, entry commands (buy, sell, pending orders) are blocked. Close, cancel, and EA control commands continue to work so you can exit positions.
Known Edge Cases & Gotchas#
-
Symbol-less
closeallandclosecancelallare full account emergency brakes that close positions opened by other EAs and manual trades on the MT5 terminal, not just positions opened by PineHook signals. -
close_pctcalculates from the original (initial) position volume, not the current remaining volume. If you sendclose_pct=50twice, you close 50% + 50% = 100% of the original position, even if 50% was already closed on the first signal. -
canceldelaywas renamed toclearqueue. Signals using the old name return HTTP 400 with a migration message. The fix is to update the TradingView alert template. -
Signal authentication failures (
missing_auth,invalid_auth) return HTTP 200, not HTTP 401. TradingView marks the alert as fired successfully. The only place to see the block is the Signal Logs, Rejected Signals tab. If you expect a non-200 response on auth failure you will miss the problem. -
direction=longordirection=shorton amodifycommand is silently ignored on netting accounts (where only one net position per symbol exists). It applies only to hedging accounts. If you are on a netting account andmodifyaffected both sides, this is expected behavior. -
trail_step=0is rejected with a validation error. Omittingtrail_stepentirely is valid and enables continuous trailing on every tick. -
ATR-based trailing (
distance_atr) has a maximum of 50 concurrent ATR indicator handles per EA instance. Running many symbols with ATR trailing simultaneously may hit this limit; the EA logs a warning and the trailing stop is not registered for that position. -
expiry_timewithout a timezone suffix is interpreted as UTC (the EA then converts that UTC instant to broker server time, so it expires at the correct real-world moment on any broker). The pitfall is the opposite of before: if you type a broker-local wall-clock time without an offset (e.g.expiry_time=2024-01-15T14:30:00meaning 14:30 on a GMT+3 broker), you get 14:30 UTC instead, so it fires 3 hours later than intended. Append the matching offset (+03:00) or send UTC. -
When both a trailing stop trigger and a breakeven trigger are set and they resolve to the same price level (within one pip), neither monitor is registered. The trade executes normally and the ACK response includes a
"warning"field. You will see no trailing stop or auto-breakeven active on the position. -
eaoff=EURUSDcan be appended as an inline parameter to any entry signal. The signal itself is blocked and the symbol is disabled; the trade does not open. This is not an error; it is intentional behavior for disabling entries after a final trade.
Frequently Asked Questions#
Q: Do I need magic= on close commands in Multi-Strategy mode?
A: It depends on the command and whether you include a symbol. Commands that target a specific side (closelong, closeshort, cancellong, cancelshort) require magic= in Multi-Strategy mode. Sweep commands (closeall, cancelall, closecancelall, closeallprofit, closeallloss) run magic-free only in their symbol-less form (the cross-strategy emergency brake); a symbol-scoped sweep such as closeall,EURUSD is rejected in Multi-Strategy mode unless you add magic=N (one strategy) or magic=ALL (every strategy on that symbol). To close only one strategy's positions on a symbol, use closelong/closeshort (or a scoped sweep) with the specific magic=N. To close one strategy's positions on every symbol, send a symbol-less sweep with magic=N — the magic is honored even without a symbol. To close all positions everywhere regardless of strategy, use a symbol-less closeall with no magic.
Q: What is the difference between buyat and buylimit?
A: buylimit requires that the specified price is below the current ask price, so you must know in advance whether you need a limit or a stop. buyat determines the order type automatically at execution time: if price is below the ask, it becomes a buy limit; if price is above the ask, it becomes a buy stop. buyat is useful when the same price level could be either a limit or a stop entry depending on market conditions at signal fire time.
Q: Why did a close command execute even though the symbol was on my blacklist? A: Close and cancel commands bypass symbol filters by design. A blacklist or whitelist only prevents new position entries. Restricting the ability to close an open position would be a safety hazard, so all close commands ignore filter settings entirely.
Q: What happens to my delayed signals if the EA disconnects?
A: Delayed signals are stored server-side and survive EA disconnections. When the EA reconnects, pending delayed signals will execute at their scheduled time (if the scheduled time has not yet passed) or immediately (if the window has already elapsed). Send clearqueue before the EA reconnects to cancel them if you do not want them to fire.
Q: Can I send eaoff=ALL to halt all trading without closing positions?
A: Yes. Send an eaoff command with ALL as the symbol parameter and the EA will block all new entry signals. No positions are closed. Existing positions continue to be managed (trailing stops, auto-breakeven, and timed closes still run). Send eaon=ALL to re-enable.
Q: Why does canceldelay return an error?
A: The command was renamed to clearqueue. The platform returns HTTP 400 with a message explaining the change. Update the TradingView alert template from canceldelay to clearqueue; the parameters and behavior are identical.
Q: What does the "Extreme volatility" amber pill on Signal Logs mean? A: A close command was sent to the broker with a large deviation allowance to ensure execution during fast markets. The actual fill price differed from the reference price by more than the disclosure threshold. The position was closed successfully; this pill is a transparency disclosure, not an error. The measured slippage delta is shown when you expand the pill.
Q: How do I target only one strategy's positions in Multi-Strategy mode?
A: Add magic=N to the command, where N matches the magic number used when the positions were opened. For example, if a strategy was opened with magic=1001, then LICENSE,closelong,EURUSD,magic=1001 closes only that strategy's long positions on EURUSD. To sweep every strategy on a symbol in Multi-Strategy mode, add magic=ALL (LICENSE,closeall,EURUSD,magic=ALL); a symbol-scoped closeall with no magic is rejected. Only a symbol-less closeall sweeps every magic across the whole terminal without a magic.
Q: What is the difference between closealleaoff and closecancelalleaoff?
A: closealleaoff closes all open positions on the whole account (every symbol and magic — any symbol you include is ignored) and disables new entries. closecancelalleaoff does the same but also cancels all pending unfilled orders. If you have pending orders (buy limits, sell stops, etc.) that you want cancelled along with the position close, use closecancelalleaoff. Both leave the EA disabled until you send eaon,ALL — the disable is sticky and survives a restart, and a settings update or a symbol-specific eaon will not clear it.