PineHookPineHook

Multi-Target Take-Profit and Stop-Loss

Splitting a single position into up to ten take-profit and ten stop-loss levels with partial closes at each.

Overview#

Multi-TP/SL lets you scale out of a position across multiple price levels. Each take-profit level closes a configured portion of the position when price reaches that level; each stop-loss level does the same on the protective side. You define the schedule in the webhook payload that opens the trade. The EA owns trigger detection and partial-close execution; the broker holds a safety-net stop and take-profit pinned to the deepest configured levels in case the EA stops responding.

Up to ten TP levels and ten SL levels are supported per position, in either percentage-of-original-volume mode or fixed-lots mode. The two modes cannot be mixed within the same side.


How It Works#

When you fire a webhook with multi-target levels, the system delivers it to your running EA, the EA opens the position with the broker, registers the multi-target schedule against that ticket, draws chart lines at each target price, and applies a broker-level safety stop at the furthest configured SL/TP. From that point on, every tick the EA evaluates whether price has reached any non-triggered target. When a target hits, the EA closes the configured portion of the position, marks that target triggered, removes its chart line, and reports the close back to the platform. A second target hit later reduces the position again. When all configured targets have triggered, the schedule is removed and any remaining position rides to the broker safety-net stop, an external close, or position expiry.

Sub-flow: percentage-mode allocation#

In percentage mode you provide a close percentage per level. The system computes how many lots correspond to that percentage of the position size, rounds to the broker's volume step, and uses the result as the close volume at that level. If the broker uses whole-contract steps and a percentage rounds to zero, a round-up rescue lifts the value to one step where it does not over-allocate against the remaining position. If a level still cannot be represented, the system collapses it to zero and emits a warning so you can see why that target will not partial-close. The final level always absorbs any remainder so the schedule sums to the position size.

Sub-flow: fixed-lot allocation#

In fixed-lot mode you provide a literal close volume per level. The system honors each value as written, floored to the broker's volume step and clamped up to the broker's minimum volume, and capped so the cumulative lots never exceed the position size — once the running total reaches the position, any later level drops to zero and the last affordable level is trimmed to exactly what remains (a level whose remaining share is below the broker minimum becomes zero). You can still configure exits totaling less than the position size, in which case the unscheduled portion rides to the broker safety net. This mode is the recommended path for whole-contract instruments such as index futures, where percentage rounding can produce surprising allocations.

Sub-flow: reconcile on a position resize#

Percentage levels are measured against the position size, so the schedule stays correct when the position changes after it was set. If you add to the position (a manual add, or a plain buy/sell signal with no multi-target parameters of its own), partially close it by hand, or the position differs on EA restart because it shifted while the EA was offline, the EA re-splits the un-fired levels across the new remaining volume by their relative percentages. Already-fired levels are left exactly as they were. Adding volume keeps the whole new size managed (the extra is not left as an unmanaged runner); a reduction shrinks the remaining levels to fit. This applies on netting/exchange accounts (where the same ticket's volume changes); on hedging accounts an add is a separate ticket with its own schedule. Fixed-lot levels are not re-scaled by a resize — they keep their exact lots, with only the over-allocate cap re-checked against the new size.

Sub-flow: re-entry on a netting account#

On netting accounts a second buy or sell signal for the same symbol does not create a new ticket. The broker grows the existing position. When the second signal carries multi-target levels, the EA replaces the prior schedule rather than layering on top of it. The previous chart lines are wiped, the new schedule is registered against the broker's net volume after the add, and a replacement notification appears in the trade log. To get a different schedule for the additional volume, close the position first and reopen.

Sub-flow: re-entry on a hedging account#

On hedging accounts each open creates its own ticket with its own independent schedule. The two positions trigger and close independently regardless of how their levels overlap.

Sub-flow: chart line lifecycle#

For every active target the EA draws a horizontal line on the chart at the target price, color-coded green for take-profit and red for stop-loss, labeled with the level number, ticket number, and price. When a target triggers, its line is removed immediately. When the position closes for any reason, all of the position's lines are removed. After an EA restart the lines are redrawn for any positions still open.


Data Flow Summary#

You submit the trade plan to the webhook entry point. The system validates the payload, queues it for delivery to your connected EA, and returns a queued acknowledgement. The EA receives the trade plan, opens the position with the broker, computes the lot allocation per target locally, registers the schedule, draws chart lines, and reports the open back to the platform along with a snapshot of the multi-TP/SL configuration. The platform persists the trade record and the configuration for later reference. As targets trigger, the EA executes partial closes through the broker, reports each one back to the platform, and updates the chart. The platform records each partial close and attributes it to the specific target index that drove the close. If the broker rejects any of these operations the EA reports the rejection with the broker's reason.


User-Facing States#

StateWhat It MeansImpact
RegisteredThe EA has accepted the schedule and is monitoring price. Chart lines are visible.Targets fire automatically as price reaches them.
Partially executedOne or more targets have fired. Position size has been reduced.Remaining targets are still active. The triggered ones do not refire.
ReplacedA second signal on the same symbol on a netting account overwrote the schedule.Old chart lines are gone; new ones reflect the latest signal. The trade log shows the replacement.
Stuck (no resolvable lots)The schedule registered but no target has a closable volume, typically because percentages of a small position rounded to zero.A warning appears in the trade log naming each affected level. The position itself is healthy but partial closes will not fire.
Safety-net onlyThe EA-side schedule completed or was wiped, but the position remains open under the broker's safety-net stop and take-profit.The position will exit when price reaches the broker's deepest stop or target, or on manual close.
ClosedThe position was closed by a target hit, manual action, broker stop-out, or external trigger.All chart lines are removed and the schedule is released.

Error Messages and Meanings#

Messages fall into three groups by where you actually encounter them:

  • Validation errors (group A) are returned by the platform when the webhook is parsed, before the trade ever opens. They surface in three places: the HTTP response to your webhook POST (so TradingView's alert execution status shows them), and (most importantly for self-service troubleshooting) they are persisted as a row in your dashboard at Signal Logs → Rejected Signals tab. That row carries the timestamp, license, command and symbol, a pill identifying the offending parameter, and the verbatim reject reason. The same row also appears in the merged Signal Logs timeline so the rejection is visible in context with successful signals.
  • EA runtime warnings and broker rejections (group B) appear in the MetaTrader Experts tab and persist in the EA's local support log file.
  • Reconciliation alerts (group C) are pushed from the platform to the EA over the WebSocket and rendered as a formatted alert block in the Experts tab.
GroupMessageCauseWhat to do
A"Multi-TP close percentages must sum to 100% (got X%)"Percentage-mode TP levels don't add up to 100.Adjust the close percentages so they total exactly 100, or switch to fixed-lot mode.
A"Multi-SL close percentages must sum to 100% (got X%)"Same as above, on the stop-loss side.Same fix.
A"Targets must be numbered sequentially starting from TP1 with no gaps."Targets numbered non-sequentially, for example TP1 and TP3 without TP2.Renumber the targets so they are consecutive starting at 1.
A"Too many TP targets: N provided, maximum is 10."More than ten levels were submitted on a side.Reduce to at most ten levels per side.
A"Conflicting TPN level parameters: tpN_pips, tpN_price. Use only one of: tpN_pips, tpN_price, or tpN_pct."Two or more distance formats were specified for the same target.Pick one: pips, price, or percent. Remove the others.
A"Conflicting TP close formats: Cannot mix tp_close (percentage) and tp_lots (fixed volume). Use either percentage-based closes for all TP levels or fixed lot closes for all TP levels."Percentage and fixed-volume closes mixed within one side.Choose one mode for the whole TP side (and separately for SL); apply it to every level.
A"Cannot combine a single take profit (tp_pips/tp_price/tp_pct) with multi-TP targets (tp1_..tp10_). Use only one."Both a single base take profit and a multi-TP ladder were sent. They conflict — the base level becomes a full-position broker TP while the ladder runs partial closes, so whichever price hits first corrupts the scale-out. (Same message on the SL side.)Send either a single tp_pips/tp_price/tp_pct or a multi-TP ladder, not both (likewise for SL).
A"Invalid compact TP syntax '...'. Each level must be 'pips:close_pct' …"The compact tp_pips/sl_pips string was malformed — a non-numeric segment, a non-finite value (nan/inf), pips ≤ 0, a close percentage above 100, or a trailing remainder that resolves to 0%. Previously a malformed string silently opened a position with no targets, and a nan slipped past every check to open an unprotected position; both are now rejected.Fix the string so every value is a real positive number and the close percentages sum to 100% (a trailing bare-pips segment supplies the remainder). A complete single level like 30:100 is valid.
A"Too many TP targets: 'tp11_pips' exceeds the maximum of 10 levels (tp1..tp10)."A target index above 10 was used on the individual-parameter form (previously ignored silently).Use at most 10 levels per side, numbered tp1..tp10.
A"Invalid TP target index: 'tp0_pips'. Level indices must be tp1..tp10 (there is no tp0)."A level numbered 0 (e.g. tp0_close) — read by no apply loop, so previously a silent drop. Now rejected.Renumber starting at 1 (tp1..tp10 / sl1..sl10).
A"TPN_lots must be positive" / "TPN_lots must be a finite number" / "TPN pct must be positive"A per-target close volume was negative, zero, NaN, or Infinity, or a JSON level-distance pct was 0. In the JSON tp_targets/sl_targets array, a non-object element or a non-numeric value rejects the whole signal.Supply positive, finite values; make every array element a well-formed object.
A"TPN/SLN has a level but no close percentage or lots specified"A target carries a level (pips/price/pct) but no close_pct and no close_lots. Rejected in both percentage and fixed-lot mode (previously a fixed-lot ladder with an unsized level slipped through and silently closed a minimum lot at that level). The whole ladder is dropped.Give every target an explicit tpN_close= (percentage) or tpN_lots= (volume), or remove the unsized level.
A"TPN has a close amount but no level distance (pips/price/pct)."In the JSON tp_targets/sl_targets array, an element gave a close_pct/close_lots but no distance (e.g. {"close_pct":50}). As the first element it previously disabled the sum-to-100% check and shipped an undersized/unprotected ladder at HTTP 200; now rejected.Add the distance to that element ({"pips":30,"close_pct":50}) or remove it.
A"TPN close percentage must be greater than 0 (0% closes nothing — use a real share or remove the level)"A ladder rung set its close share to exactly 0 (tpN_close=0). Previously accepted as long as the other rungs still summed to 100, but a 0% rung closes nothing — a configured level that never fires. The whole ladder is dropped.Give the rung a positive share, or remove that level and let the remaining rungs sum to 100.
B"Multi-TPN/SLN is on the WRONG SIDE of market…" or "…resolves to a price <= 0 (pct/pips too large)…"A configured multi-target level sits on the wrong side of market (e.g. a buy's TP below price) or resolves past zero (e.g. sl1_pct=150 on a buy). Either would fire instantly at a loss or never fire at all, leaving the position unprotected. The EA now rejects this at open regardless of the broker's stops level (the older check passed it through on zero-freeze-zone brokers).Check the direction and magnitude of every level: a buy's TPs go above entry and SLs below (a sell is the reverse), and a percentage stop must stay under 100 on the side that moves toward zero.
B"Multi-TP/SL stop too close to market for SYMBOL: minimum distance required is N points (broker STOPS_LEVEL = X in price), but TPN/SLN is only Y away from Z. Move all TP and SL levels at least X further from market and resend."One or more configured TP or SL levels are inside the broker's freeze zone for stops, meaning the broker would refuse to set them as a position stop-loss or take-profit. The EA rejects the trade at open (before placing the position) so it is never opened in an unprotected state. Because the minimum stop distance is broker-specific and readable only on the terminal, this check runs on the EA, not the platform, so the signal is still delivered and the rejection surfaces in the MetaTrader Experts tab (not Rejected Signals).Open the symbol in MetaTrader, check the broker's minimum stop distance for that instrument, and rewrite the signal so every TP and SL level sits at least that distance from current market. Common cause: very small pip values on whole-contract futures, or sending a signal during high-volatility windows where bid-ask spread temporarily widens beyond your stops.
B"MultiTPSL: WARNING ticket #X TPN calc_lots=0, will not partial-close. Likely cause: pct (Y%) of totalVolume (Z) is below volume_min on this symbol. Use close_lots= for whole-unit-step instruments."A percentage of the position rounded below the broker's minimum volume after the round-up rescue. Common on whole-contract instruments.Use fixed-lot mode for that instrument or adjust percentages so each one resolves to at least the broker's minimum volume.
B"✗ TPN close FAILED #X retcode=N "..." (vol=Y)"The broker rejected the partial close. The retcode and quoted reason explain why. Common reasons include filling-mode mismatches, invalid volumes, market closed, requote.Read the quoted broker reason. If it persists across retries, contact support with the retcode and instrument.
B"Multi-TP/SL: broker stops too tight for safety-net SL/TP on #X, skipping broker modify..."Defensive-only path. The EA's validate-at-open rejection (the "stop too close" row above) blocks this case at open, so this log line is not expected to fire on current builds. If it does appear, treat it as a bug report: the validator missed a case and a position is now open without a broker-side backstop.Capture the symbol, signal payload, current bid/ask, and the broker's minimum stop distance for that symbol; contact support.
B"Multi-TP/SL: broker safety-net modify FAILED for #X retcode=N "...""The broker rejected the safety-net stop modification with a different reason than freeze-zone. The EA-managed schedule continues normally.No action needed for partial closes. Review the quoted reason if a broker-side backstop is critical.
B"MultiTPSL: modify … REJECTED — new level is inside the broker freeze zone or already past market" / "… resolves to a non-positive price"A modify tried to move a level too close to current market (inside the broker's minimum-stop distance), already on the far side of price, or to a percent distance ≥100% on the side that moves toward zero (e.g. sl1_pct=120 on a long) — which resolves to a price ≤ 0 and could never trigger. The EA skips that level; the tick manager would otherwise fire a too-close level as an instant partial close, and a non-positive level would sit dead. Other levels in the same modify are unaffected. The direction-aware ≥100% check lives on the EA (the relay can't reject it on a modify without knowing the position direction).Move the level further from market / use a smaller percent and resend. This is a guardrail, not a fault.
B"MultiTPSL: … gave up after N zero-progress under-filled attempts"A partial-close leg kept coming back filled with ~0 lots (illiquid symbol or a broker that returns a "partial done" without filling). After a few no-progress attempts the EA stops retrying that leg and lets the remaining volume ride to the broker safety-net stop, rather than re-sending an order every tick.The position is not over-closed; only one leg could not fill. Check broker liquidity / fill mode for that symbol.
B"MultiTPSL: restored ladder … dropped — position REVERSED while the EA was offline"On restart the EA found a saved ladder whose position had reversed direction while the EA was offline. The stale ladder is dropped (it can never fire against the opposite-direction position), so the reversed position is unprotected by it. The same event also fires live when a reversal happens while the EA is running.Send a fresh multi-TP/SL signal for the new direction if you want laddered exits on the reversed position.
CPosition mismatch alert: "Volume mismatch for SYMBOL: expected X, got Y"The platform's expected position size disagreed with the broker's actual size — usually after you changed the position manually or while the EA was offline (or after rapid back-to-back signals on netting). The platform raises this alert once, then recalibrates its expected size to match what your EA reports (MetaTrader is authoritative) and the alert self-heals — nothing to acknowledge.A single alert after a manual or offline change is expected and needs no action. Only look into it if fresh alerts keep appearing on every heartbeat — that points to the EA being disconnected or the broker repeatedly changing the position. Check your connection status and the broker's trade history.

Where to look#

If you see a message from...Where to look
Group A (validation)Dashboard Signal Logs page → Rejected Signals tab. The row shows the timestamp, command, symbol, the offending parameter as a pill, and the verbatim reject reason. The same record also appears in the merged Signal Logs timeline alongside successful signals. Your TradingView alert execution status will also show the message because the platform returns it as the HTTP 400 response.
Group B (EA runtime)The Experts tab in MetaTrader and the EA support log file in your MQL5 Files folder.
Group C (reconciliation)The Experts tab in MetaTrader, where the alert appears as a formatted block.

Caveats for Group A logging:

  • The Rejected Signals row is created only after the platform can resolve a valid license key from the payload. If the payload is malformed enough that no license key can be extracted, you receive an HTTP 400 but no Rejected Signals row is created. If the license key is well-formed but invalid (unknown, inactive, or expired), the webhook returns HTTP 401 and likewise writes no Rejected Signals row. In either case the only surface is the TradingView alert response.
  • The reject reason is stored in full; only very long reasons are shortened, and none of the Multi-TP/SL validation messages are that long.
  • If you have multiple licenses, select the affected license in the dashboard filter to see the relevant rows.

Plan and Tier Differences#

Multi-TP/SL is available across all paid tiers with no tier-specific feature gates. The system limits below are platform constants, not tier limits:

BehaviorStandardProPremium
Maximum TP levels per positionTenTenTen
Maximum SL levels per positionTenTenTen
Percentage and fixed-lot modesAvailableAvailableAvailable
Chart line drawingAvailableAvailableAvailable
Whole-contract round-up rescueAvailableAvailableAvailable
Persistence across EA restartAvailableAvailableAvailable

When you reach the ten-target cap, the platform returns a validation error and the trade does not open. There is no graceful degradation: you must reduce the count.

Trial accounts have access to multi-TP/SL on the same terms as their base tier during the trial window. When a subscription lapses, signal delivery stops; existing multi-TP/SL schedules continue executing locally on the EA until each position closes, but no new schedules can be registered until the subscription is restored.


Known Edge Cases and Gotchas#

  • On whole-contract instruments such as index futures, percentage targets that don't divide evenly into the contract count produce uneven allocations. Fixed-lot mode is the safer choice on those instruments.
  • The deepest configured TP and SL are also pinned as the broker's safety-net stop and take-profit. When price reaches the deepest level, the broker's server-side stop typically fires before the EA's partial-close attempt, closing the entire remaining position in one order. The behavior is identical from a P&L perspective; only the recorded close reason differs.
  • When the broker requires a minimum stop distance from market and any configured TP or SL level falls inside that zone, the EA rejects the trade at open (before placing the position) with a clear message in the MetaTrader Experts tab. The minimum stop distance is broker- and instrument-specific and readable only on the terminal, so this check lives on the EA, not the platform (the signal is delivered normally and the rejection is not recorded in Signal Logs → Rejected Signals). This is intentional: opening a position whose stops cannot be set at the broker would leave it protected only by the EA's tick loop, with no broker-side fallback if the EA crashes, freezes, or disconnects. If you hit this rejection, widen all TP and SL levels beyond the broker's minimum stop distance for that instrument.
  • On netting accounts, a second open signal replaces the prior multi-TP/SL schedule rather than adding to it. Users coming from hedging accounts often expect additive behavior.
  • A target whose computed close volume rounds to zero is silently skipped by the trigger logic and surfaced through a warning at registration. The remainder is not redistributed to other targets.
  • Targets must be numbered sequentially starting at one. Skipping a level rejects the entire signal at validation time.
  • Mixing percentage closes and fixed-lot closes on the same side rejects the signal.
  • The CSV format has a percentage-mode shorthand that lets you write all levels in one parameter, like tp_pips=30:50;60:30;100 (TP1 at +30 pips closing 50%, TP2 at +60 closing 30%, TP3 at +100 closing the remainder). There is no equivalent shorthand for fixed-lot mode. For fixed lots you must spell each level out with the per-level parameters (tp1_pips=30,tp1_lots=0.1,tp2_pips=60,tp2_lots=0.1,...). The same shape 30:0.1;60:0.1;100 parses as percentages, not lots, and is NOT rejected: the values after : become close percentages (so 0.1 means 0.1%) and the trailing bare value absorbs the remainder, yielding TP1=0.1%, TP2=0.1%, TP3=99.8%, which sums to 100% and opens a position with an unintended, near-useless scale-out. Because nothing rejects a lot value in the compact string, never put lots there. If you want compactness with fixed lots, use the JSON tp_targets array form.
  • A multi-TP/SL ladder works on a pending order (buylimit / buystop / selllimit / sellstop / buyat / sellat), but the ladder and its broker-side safety-net stop are not active while the order sits unfilled. The EA attaches them when the order fills and becomes a live position, anchored to the actual fill price and volume. If you place a pending entry with a TP/SL ladder and see no managed stop before fill, that is expected; the protection appears on fill (or never, if the order is cancelled first).
  • After an EA restart the schedule reloads from local storage and chart lines are redrawn. If the local storage was lost, the schedule cannot be recovered from the platform side; you must resend it. A pending order's not-yet-attached ladder is stored alongside it and replays on fill across a restart, as long as the order still exists at the broker.

Frequently Asked Questions#

Q: Can I set up only TP targets without any SL targets, or vice versa? A: Yes. Either side is optional. A position can have multi-TP only, multi-SL only, or both.

Q: Can I combine a single stop-loss with multi-TP targets? A: Yes. A single sl_pips or sl_price alongside tp_targets is honored as the broker's stop-loss for the position. The multi-TP manager handles the take-profit partial closes and the broker stop handles the protective side.

Q: What happens if my fixed-lot targets total less than my position size? A: The configured volume closes through the targets that fire. The remaining unscheduled volume rides to the broker's safety-net stop and take-profit, an external close, or position expiry.

Q: What happens if my fixed-lot targets total more than my position size? A: Each target closes whatever volume the configured close lots specifies, capped at the position's remaining volume. The last few targets may close less than configured if there isn't enough position left, which is logged in the trade history.

Q: Will the same multi-TP schedule fire again if I open a new position? A: No. Schedules are bound to a specific position ticket and do not carry over.

Q: Why did my percentage-mode targets all round to one contract on a futures instrument? A: On instruments with whole-contract steps, the system applies a round-up rescue that lifts each target's volume to one contract when a strict floor would zero it out. Switch to fixed-lot mode if you want explicit control.

Q: Why did my second buy on the same symbol erase my first multi-TP schedule? A: On netting accounts the broker holds one net position per symbol. The platform replaces the schedule on each new multi-target signal because there is only one position to attach a schedule to. Use a hedging account, separate symbols, or close before reopening if you need independent schedules.

Q: A target hit but the close was rejected with retcode 10025 or 10030. What is that? A: Those are filling-mode rejections from the broker. They are instrument-specific and usually self-resolve on retry on the next tick. If they persist, note the instrument and retcode and contact support.

Q: Why are my chart lines visible on one chart but not on another? A: Lines are drawn on the chart where the EA is attached. If you open a second chart of the same symbol without the EA, no lines appear there. Attach the EA to the chart you want to monitor, or accept that the second chart is view-only.

Q: Do trailing stops or auto-breakeven interact with multi-TP/SL? A: Yes. When a trail or breakeven activates on the position, any pending multi-SL targets are released to avoid double-closing from the SL side. Multi-TP targets continue to fire normally.

Q: My signal was rejected with "stop too close to market for SYMBOL." What does that mean? A: The broker enforces a minimum distance for stop-loss and take-profit levels. One or more of your TP or SL levels was closer than that minimum, so the broker would have refused to set the safety-net stop. Rather than open the position with no broker-side protection, the EA rejects it at open (before placing the position) and prints the reason to the MetaTrader Experts tab. Open the symbol in MetaTrader to check the broker's minimum stop distance, then resend the signal with all TP and SL levels at least that far from current market.

Q: Why doesn't the EA just open the position and let me manage stops manually? A: Because if the EA disconnects or crashes between the open and the deepest stop firing, the position has no broker-side fallback and can run to a margin call on a fast move. Rejecting at open is a hard guarantee that any position the EA opens has a broker-pinned safety stop in place.