Alerts are not orders
The Strategy Tester lists two hundred trades. Your alert log is empty. Nothing is broken, and this is the documented reason:
Alerts only trigger in the realtime bar. [1]
Every fill in the backtest is a replay of history. An alert is a realtime event. Nothing on the historical part of the chart ever fired an alert, and nothing ever will — no setting changes that.
The frequency you did not choose
alert(message, freq)
freq defaults to alert.freq_once_per_bar [1]. There are three values:
| Constant [1] | Fires |
|---|---|
alert.freq_once_per_bar | the first time the condition is true on a bar — the default |
alert.freq_once_per_bar_close | only on the bar's confirmed close |
alert.freq_all | every time the condition is true |
If your strategy decides on bar close, the default is the wrong one. It will notify you on an intrabar condition that has reversed by the time the bar closes — the repainting problem wearing a different hat, and the reason a live alert stream can disagree with the backtest that produced it.
Order-fill alerts and their text
A script alert created from a strategy can be set so that order fill events
also trigger it [1]. Per-order text comes from the alert_message parameter,
which strategy.entry(), strategy.exit(), strategy.close() and
strategy.order() all accept [1]; you read it back in the alert body with the
{{strategy.order.alert_message}} placeholder [1].
Other placeholders available in alert messages include {{ticker}},
{{exchange}}, {{interval}}, {{close}}, {{time}} and {{timenow}} [1].
The part TradingView does not promise
An alert is a notification. It does not place an order with your broker. Getting from a fired alert to a filled position needs a broker integration, and the delay between the two is yours — it is not in the tester, which assumes the fill happened at the price it modelled. That gap is measured in the tester versus live comparison.
The limits that decide your results
Some of what your backtest reports is decided by your account, not by your strategy. These are worth knowing before you spend a weekend debugging one of them.
Your plan changes your backtest
Historical bars available, by plan [2]:
| Plan [2] | Historical bars |
|---|---|
| Ultimate | 40,000 |
| Expert | 25,000 |
| Premium | 20,000 |
| Essential and Plus | 10,000 |
| Other plans | 5,000 |
Read that table again, because the consequence is larger than it looks: two people running an identical script on an identical symbol and timeframe get different backtests. The one with more history tests more market regimes, and the one with less may not reach the drawdown that would have changed their mind.
So a reported profit factor is not reproducible unless the plan is stated alongside it. If you publish a number, state your plan; if you read one that does not, you cannot check it.
The order cap that stops your backtest quietly
A script can place a maximum of 9,000 orders when backtesting, rising to 1,000,000 with Deep Backtesting [2].
A frequently-trading script on a long range reaches 9,000 and simply stops adding trades. The equity curve goes flat and the statistics stay plausible. Nothing tells you the test ended early — check the trade count against the cap before concluding the strategy stopped working.
Execution time, which fails a script that used to pass
The time allotted to run across all bars of a dataset is 20 seconds for basic accounts and 40 for others [2].
This one is confusing because your logic does not have to change to hit it. Add
bars, add a request.* call, upgrade to a symbol with more history, and a
script that ran yesterday times out today.
The rest, in one table
| Limit [2] | Value |
|---|---|
| Plot counts per script | 64 |
Unique request.* calls | 40, or 64 on Ultimate |
| Historical buffer, most series | 5,000 bars |
Historical buffer for open, high, low, close, time | up to 10,000 bars |
| Lines, boxes, polylines and labels shown by default | last 50 |
| Maximum line, box and label IDs | 500 |
| Maximum polyline IDs | 100 |
Two of those cause reports of bugs that are not bugs. Drawings beyond the
displayed limit are not shown, so a label you expected and cannot find is
usually the cap, not your condition — and setting a drawing's properties to
na still counts it against the total [2]. And a series indexed further back
than its buffer allows is a limit, not a typo.
The plot count is worth a second look too, because it is not one per plot()
call: plotarrow(), plotcandle(), plotchar(), plotshape(),
alertcondition(), bgcolor(), barcolor() and a series-coloured fill()
all consume from the same 64 [2].
What to actually check, and how often
First, what you do not have to do: keep a machine running. A Pine strategy executes on TradingView's chart, not on your computer, so there is no terminal to restart and no VPS to rent. This is the structural difference from an MT5 Expert Advisor, and most maintenance advice you will find for Pine is an MT5 article with the words swapped — see deploying and maintaining an EA for what that genuinely does require.
What is left is checking that the things your backtest assumed are still true.
After every re-export — the declaration
This is the only mandatory check, because the strategy() declaration is where
your results are decided and a fresh export rewrites it.
| Confirm | Because |
|---|---|
commission_type and commission_value are set | a default export may cost nothing |
slippage is in ticks for this instrument | a tick is not the same size on two symbols |
pyramiding is the number you intended | it defaults to 1 [3]; a higher value stacks positions |
| every input control actually changes behaviour | move a threshold and confirm the trade list changes |
That last row is not paranoia. An input that is declared but never referenced in the logic renders a working control that does nothing, and there is no way to tell by looking at it — see the cost parameters in detail.
Weekly — two spot checks
Pick three trades from the List of Trades and find them on the chart. Does the fill price correspond to a price that bar actually traded at? This one check catches future leak, a non-standard chart type and a misread tick size, and it takes under a minute.
Confirm your session filter still matches the instrument's hours. Daylight saving moves them twice a year, and a session string is interpreted in the exchange's timezone rather than the chart's — the session-filter rules cover why that bites.
Monthly — is it still trading?
A strategy that has stopped opening positions is either in a market regime it was not built for, or has a condition that no longer becomes true. In the equity curve these look identical: a flat line.
Tell them apart by plotting the condition itself rather than the result. If the
condition is firing and no orders follow, look at pyramiding, at the order
cap, or at a filter that is now permanently false. If the condition is not
firing at all, the market changed — and that is a decision to make, not a bug to
fix.
What does not need checking
The code, unless you changed it or the platform did. Version defaults are the exception worth a calendar reminder: the v5 to v6 changes altered behaviour without raising errors, and that class of change is the only one that can break a script you never touched.
Tactix AI on this workflow
AlfaTactix includes Tactix AI: use Tactix Studio to describe your idea in plain language and draft timeframes, signals, filters, and risk into the same six-step Strategy Builder form — or open Tactix Guide on any step when you only need a field explained. Review every value, then export MQL5 or Pine Script from Code Generator (form-first — not untested prompt-to-code).

