PineHookPineHook

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:

ParameterRequiredDescription
size_lotsOne of the twoFixed lot size
size_pctOne of the twoRisk percentage of account balance. Requires a stop loss parameter.
sl_pips / sl_price / sl_pctRequired when using size_pctStop loss (use one)
tp_pips / tp_price / tp_pctOptionalTake profit (use one)
magicRequired in Multi-Strategy modeTrade identifier
delayOptional (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.
closetimeOptionalSeconds until auto-close (1 to 2,592,000)
maxspread_pips / maxspread_pctOptionalMaximum acceptable spread
marketpriceOptionalReference price for stale-signal detection
active_start / active_endOptionalOverride 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_atrOptionalActivate trailing stop on the resulting position
be_trigger_pips / be_trigger_pct / be_trigger_priceOptionalActivate auto-breakeven on the resulting position
tp1_pips through tp10_pips (and variants)OptionalMulti-target take profit schedule
sl1_pips through sl10_pips (and variants)OptionalMulti-target stop loss schedule
commentOptionalAccepted 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.
eaoffOptionalBlock 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:

ParameterRequiredDescription
priceYesEntry price
expiryOptionalSeconds 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_timeOptionalISO 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 asExample
A specific UTC timebare, or append Zexpiry_time=2024-01-15T14:30:00Z
Your broker's server time (the MT5 chart clock)append your broker's UTC offsetGMT+3 broker → expiry_time=2024-01-15T14:30:00+03:00
A specific local / exchange timezoneappend that zone's offsetEST (−05:00) → expiry_time=2024-01-15T14:30:00-05:00
A relative duration (no timezone math)use expiry in secondsexpiry=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):

ParameterDescription
close_pctPercentage of the original (initial) position volume to close. 0 to 100.
close_lotsFixed 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).

ParameterRequiredDescription
sl_pips / sl_price / sl_pctAt least one SL or TPNew stop loss
tp_pips / tp_price / tp_pctAt least one SL or TPNew take profit
directionOptionallong or short, limits modification to one side (hedging accounts only; ignored on netting)
targetOptionalsl, tp, or both (default: both)
order_typeOptionalbuystop, buylimit, sellstop, or selllimit, target pending orders instead of positions
magicOptionalFilter 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#

CommandRequires magic in Multi mode?Notes
buyYesRejected without it
sellYesRejected without it
buylimitYesRejected without it
buystopYesRejected without it
selllimitYesRejected without it
sellstopYesRejected without it
buyatYesRejected without it
sellatYesRejected without it
closelongYesRejected without it. Use magic=ALL for a cross-magic sweep (or closeall)
closeshortYesRejected without it. Use magic=ALL for a cross-magic sweep (or closeall)
closeallOnly when a symbol is givenSymbol-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
closecancelallOnly when a symbol is givenSame rule: symbol-scoped needs magic=N/magic=ALL; symbol-less with no magic sweeps the entire terminal, with magic=N only that strategy
closeallprofitOnly when a symbol is givenSame rule: symbol-scoped needs magic=N/magic=ALL; symbol-less with no magic sweeps the entire terminal, with magic=N only that strategy
closealllossOnly when a symbol is givenSame rule: symbol-scoped needs magic=N/magic=ALL; symbol-less with no magic sweeps the entire terminal, with magic=N only that strategy
cancellongYesRejected without it. Use magic=ALL for a cross-magic sweep (or cancelall)
cancelshortYesRejected without it. Use magic=ALL for a cross-magic sweep (or cancelall)
cancelallOnly when a symbol is givenSymbol-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
breakevenYesRejected without it
nobreakevenYesRejected without it
modifyYesRejected without it
trailYesRejected without it
notrailYesRejected without it
closealleaoffNoEA control commands are not magic-scoped
closecancelalleaoffNoEA control commands are not magic-scoped
eaonNoSymbol-scoped only
eaoffNoSymbol-scoped only
eaoffexceptNoSymbol-scoped only
eaonexceptNoSymbol-scoped only
clearqueueOnly when a symbol is givenSymbol-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 for sell, 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 for breakeven, 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.

  1. 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".
  2. 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.
  3. 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_reasonSurfaces inWhat happened
auth_failedTradingView 401 + admin Edge CasesLicense-validation failure (key not found, inactive, deleted, trial expired). Generic message returned to prevent license-key enumeration.
missing_authSignal Logs + Rejected SignalsSignal authentication is enabled for this license and the signal arrived without the auth= parameter. When authentication is on, every signal must include it.
invalid_authSignal Logs + Rejected SignalsThe auth= value didn't match the configured password.
verification_errorSignal Logs + Rejected Signals + admin TroubleshootingAn internal error occurred while verifying the signal-auth password (fail-closed). Contact support; this is not a user configuration issue.
rate_limitedSignal Logs + Rejected SignalsPer-minute signals/min cap exceeded for this license's tier.
rate_limited_dailySignal Logs + Rejected SignalsPer-day signals cap exceeded. Upgrade or wait 24h.
pending_order_ceilingSignal Logs + Rejected SignalsLive 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.
delaySignal Logs + Rejected Signalsdelay= 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_timeSignal Logs + Rejected SignalsPending-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_requiredSignal Logs + Rejected SignalsEA in Multi-Strategy mode and the command needs an explicit magic=.
symbol_filteredSignal Logs + Rejected SignalsThe trade symbol is blocked by the active blacklist or not in the active whitelist.
queue_ceiling_per_licenseSignal Logs + Rejected SignalsDelayed-signal queue full for this license.
queue_ceiling_globalSignal Logs + Rejected SignalsDelayed-signal queue at system capacity.
queue_fullSignal Logs + Rejected SignalsThe immediate per-license queue is at its maximum size. EA may be offline or processing too slowly.
filter_list_too_longSignal Logs + Rejected SignalsSymbol filter would exceed 100 entries. Use the inverse mode (eaonexcept / eaoffexcept).
missing_symbolSignal Logs + Rejected SignalsCommand requires a symbol (closelong / closeshort / cancellong / cancelshort / eaon / eaoff / eaoffexcept / eaonexcept) and none was provided.
pending_order_no_priceSignal Logs + Rejected SignalsPending order command sent without price=.
conflicting_offsetsSignal Logs + Rejected Signalsbreakeven received more than one of be_offset_pips / be_offset_pct / be_offset_price.
conflicting_sizeSignal Logs + Rejected SignalsBoth size_lots and size_pct were provided. Use exactly one.
invalid_symbolSignal Logs + Rejected SignalsSymbol contains characters that don't match ^[A-Z0-9]{1,20}$.
param_invalidSignal Logs + Rejected SignalsOne 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_unavailableSignal Logs + Rejected SignalsA temporary internal error prevented the symbol-filter update; transient. Retry.
active_start / active_endSignal Logs + Rejected SignalsOnly 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_filterSignal Logs (via EA ACK; no Rejected Signals row)EA-side rejection because the symbol was disabled at fill time.
active_hoursSignal 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 typeTypical failure_reason substring
Broker errorERR_INVALID_PRICE, ERR_TRADE_DISABLE, ERR_INVALID_TRADE_VOLUME, requote, 10004, 10006, etc. (broker-specific MT5 retcode strings)
SL/TP off-gridstop level, stops level, invalid stops, off grid
Position-limit-exceededposition limit, max long lots, max short lots, would exceed position limit
Spread-too-widespread, pips spread exceeds
Insufficient marginmargin, not enough money
Pending-order expiredexpired, order expiration
Invalid ticketinvalid 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.

MessageCauseAction
"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 clearqueueUpdate the TradingView alert template to use clearqueue
"Missing 'license' - signals require: license, action, symbol"License field absent from payloadVerify the alert template includes the license key
"Missing 'action'" or "Missing 'symbol'"Required field absentAdd the missing field to the alert
"Invalid JSON: {detail}"Malformed JSON payloadValidate the JSON structure in the TradingView alert
"Empty request body"Request body is emptyConfirm 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 usedRemove one
"Magic number required for {command} in Multi-Strategy mode. ..."Missing magic parameter in Multi-Strategy modeAdd magic=N to the signal
"Gap detected in TP target numbering. Missing: TP2."Non-sequential multi-target numberingRenumber targets sequentially with no gaps
"{param} out of allowed range {bounds}"Parameter value outside valid rangeCorrect the value to be within range
"Invalid symbol format: '{symbol}'."Symbol contains invalid charactersUse uppercase alphanumeric only, 1 to 20 characters
"Conflicting {parameter} parameters"Multiple mutually exclusive parameters specifiedKeep 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 futureUse 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.

MessageCauseAction
"Authentication failed"License key is invalid, inactive, or does not existVerify 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.

MessageCauseAction
"Delayed signals require a paid plan. Upgrade to use delay=."delay= parameter used on a trial accountUpgrade 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.

MessageCauseAction
"Request entity too large"Webhook body exceeds size limitShorten 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.

MessageCauseAction
"Rate limit exceeded"Per-minute signal rate exceeded for the tierReduce alert frequency; check the tier's signals-per-minute allowance
"Daily signal limit reached ({cap}). Limit resets every 24 hours."Daily signal cap reachedWait 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 limitSlots 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 capacitySend 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 ReasonSurfaceMeaningAction
missing_authSignal Logs + Rejected Signals tabSignal authentication is enabled for this license but no auth= password was includedAdd auth=PASSWORD to the alert template
invalid_authSignal Logs + Rejected Signals tabSignal authentication is enabled and an auth= password was included but it is wrongCorrect the password in the alert template; verify it matches the one set on the dashboard
verification_errorSignal Logs + Rejected Signals tabAn internal error occurred verifying the authentication passwordReport to support; this is not a user configuration issue
not in whitelistSignal Logs + Rejected Signals tabSymbol filter is in whitelist mode and this symbol is not on the listAdd the symbol to the whitelist via eaoffexcept, or switch to blacklist mode
in blacklistSignal Logs + Rejected Signals tabSymbol filter is in blacklist mode and this symbol is on the blocked listRemove the symbol from the blacklist via eaon=SYMBOL
missing_symbolSignal Logs + Rejected Signals tabeaon or eaoff command sent without specifying a symbolAdd a symbol or ALL as the parameter value
symbol_filterSignal Logs (label "Blocked: Symbol Filter")EA's local symbol filter rejected the signal after it was dispatchedSend eaon=SYMBOL or update the symbol filter in dashboard EA Settings
active_hoursSignal 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 surfaceWhere to look
TradingView shows a non-200 status codeOpen the TradingView alert history, click the failed execution, and read the error body.
Trade never appeared but TradingView shows a 200 responseGo 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 orderCheck if delay= is set in the alert. Send clearqueue for that symbol to drain any accumulated delayed signals.

Plan & Tier Differences#

FeatureTrialStandardProPremium
Signals per minute601504501,500
Signals per day5,00010,00030,000100,000
delay= parameterNoYesYesYes
Max queued delayed signals050150500
Max pending order expiry15 minutes24 hours24 hours24 hours
Max live pending orders5505005,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 closeall and closecancelall are 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_pct calculates from the original (initial) position volume, not the current remaining volume. If you send close_pct=50 twice, you close 50% + 50% = 100% of the original position, even if 50% was already closed on the first signal.

  • canceldelay was renamed to clearqueue. 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=long or direction=short on a modify command 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 and modify affected both sides, this is expected behavior.

  • trail_step=0 is rejected with a validation error. Omitting trail_step entirely 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_time without 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:00 meaning 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=EURUSD can 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.