Using AI to Write a Pine Script Futures Strategy for Webhooks
Learn to prompt AI for a Pine Script v6 futures strategy with webhook-ready alerts, and catch the alert_message, session, and contract mistakes AI makes.
Marketing
Getting an AI assistant to write a Pine Script futures strategy that sends webhook alerts works only when the prompt specifies Pine Script v6, futures-specific symbol behavior, confirmed-bar entries, and a valid per-order JSON message. Without those constraints, the code can compile and still send empty alerts, misread futures sessions, or route a continuous contract differently than expected.
This tutorial extends the Best AI for Pine Script series with a prompt, a corrected moving-average strategy, and an audit checklist. The goal is not to evaluate profitability. It is to produce code that can move safely from the Pine Editor to a paper-trading webhook.
What This Category Covers
Webhook-Ready Futures Strategies
This category covers Pine Script v6 strategies whose strategy.entry(), strategy.exit(), strategy.close(), or strategy.order() calls include an alert_message argument. The argument defines a custom message for the corresponding order fill event and accepts a series string that can be assembled from script variables.1
Pine has no built-in function that produces JSON, so scripts construct JSON-shaped messages through string concatenation and conversions such as str.tostring().2 TradersPost can receive this JSON, read fields such as ticker, action, and quantity, then process the signal through the connected account's subscription settings.
Why Futures Need Special Handling
Futures expose symbol-specific properties that equity-focused examples may ignore. Pine provides values including syminfo.pointvalue, syminfo.mintick, syminfo.root, and contract expiration data for applicable symbols.3 Tick-based stops, point-based targets, contract quantities, and session filters must respect those properties.
Continuous 1! charts also introduce rollover behavior. TradingView sets each symbol's switching rule using historical volume behavior, principally when the next contract's daily volume exceeds the current contract's volume, rather than applying one universal expiration offset.4 An AI assistant trained on equity examples may miss both this rollover model and the meaning of named futures sessions.
Where This Fits
The same review process applies to trend following strategies, mean reversion strategies, breakout and opening range strategies, and momentum strategies. Entry logic changes, but version control, confirmed-bar execution, contract identity, sessions, and webhook formatting remain common review points.
Prompt Template For Webhook-Ready Code
Pin The Pine Version
Start the prompt with an explicit requirement for //@version=6. Also prohibit deprecated v4 or v5 syntax. This prevents the model from mixing old function names, obsolete examples, and current strategy parameters in one draft.
Name Inputs And Conditions
List every input's type, default, and purpose. Define the exact crossover, position-state, stop, and target conditions instead of requesting a generic moving-average strategy. Name the intended futures root and whether the script will run on an explicit contract or a continuous chart.
- Fast EMA: integer, default 20
- Slow EMA: integer, default 50
- Quantity: integer, default 1
- Stop distance: float, default 8.0 points
- Target distance: float, default 16.0 points
- Webhook ticker: string, default explicit contract symbol
Require Confirmed-Bar Logic
Ask the model to gate entries with barstate.isconfirmed. Historical bars execute differently from developing realtime bars, where values can change before confirmation. Strategies recalculate at bar close by default, while calc_on_every_tick = true permits intrabar recalculation and can create behavior that historical bars do not reproduce.1
State whether the strategy should use order fill events, alert() calls, or both. If using alert(), specify its frequency and whether intrabar calculation is intentional.
Specify The Alert Payload
Tell the model to build dynamic values with str.tostring(). Place strategy placeholders such as {{strategy.order.action}} or {{strategy.position_size}} in the alert's Message field when needed, not inside the alert_message argument. Most importantly, require {{strategy.order.alert_message}} in the Message field or compiler annotation so the custom per-order string reaches the notification.2
Worked Example: Prompt To Code
The Submitted AI Prompt
A sufficiently constrained prompt could read:
Write a Pine Script strategy using //@version=6.
Target a CME E-mini S&P 500 futures chart with root ES. Use a 20-period EMA and a 50-period EMA. Enter long when the fast EMA crosses above the slow EMA. Enter short when it crosses below. Allow entries only when barstate.isconfirmed and the strategy is flat.
Use an integer quantity input with default 1. Use a float stop-distance input with default 8.0 points and a float take-profit input with default 16.0 points. Set calc_on_every_tick to false.
Add strategy.exit orders for both directions. Build valid JSON strings for every strategy.entry and strategy.exit alert_message using string concatenation and str.tostring(). Entry messages use the fields ticker, action, quantity, orderType, stopLoss, and takeProfit. Exit messages use ticker, action set to exit, and cancel set to true. Add a string input for an explicit futures contract ticker, default ESZ2026.
Do not put TradingView placeholders such as {{close}} inside alert_message. Add a //@strategy_alert_message annotation containing {{strategy.order.alert_message}}. Use no deprecated v4 or v5 syntax.
Reviewing The First Draft
Do not accept the first draft because it looks plausible. Check whether it embeds {{close}} or other placeholders in alert_message, labels session.regular as daytime trading, or sends the chart's continuous symbol as the executable ticker.
Also inspect any use of syminfo.current_contract. TradingView added this variable in July 2025 for identifying the underlying contract on continuous futures charts.5 Its documentation is inconsistent for non-continuous symbols: the release notes describe na, while the chart-information page describes an empty string.3 Robust code must guard against both states.
Corrected Pine Script V6
//@version=6
//@strategy_alert_message {{strategy.order.alert_message}}
strategy("ES Webhook EMA Cross", overlay = true, pyramiding = 0, calc_on_every_tick = false)
int fastLength = input.int(20, "Fast EMA Length", minval = 1)
int slowLength = input.int(50, "Slow EMA Length", minval = 2)
int orderQuantity = input.int(1, "Contract Quantity", minval = 1)
float stopPoints = input.float(8.0, "Stop Distance", minval = 0.25, step = 0.25)
float targetPoints = input.float(16.0, "Target Distance", minval = 0.25, step = 0.25)
string webhookTicker = input.string("ESZ2026", "Explicit Contract Ticker")
float fastEMA = ta.ema(close, fastLength)
float slowEMA = ta.ema(close, slowLength)
bool correctRoot = syminfo.root == "ES"
bool enterLong = correctRoot and barstate.isconfirmed and strategy.position_size == 0 and ta.crossover(fastEMA, slowEMA)
bool enterShort = correctRoot and barstate.isconfirmed and strategy.position_size == 0 and ta.crossunder(fastEMA, slowEMA)
string quantityText = str.tostring(orderQuantity)
if enterLong
float plannedStop = close - stopPoints
float plannedTarget = close + targetPoints
string longMessage = "{\"ticker\":\"" + webhookTicker + "\",\"action\":\"buy\",\"quantity\":" + quantityText + ",\"orderType\":\"market\",\"stopLoss\":{\"type\":\"stop\",\"stopPrice\":" + str.tostring(plannedStop, format.mintick) + "},\"takeProfit\":{\"limitPrice\":" + str.tostring(plannedTarget, format.mintick) + "}}"
strategy.entry("Long", strategy.long, qty = orderQuantity, alert_message = longMessage)
if enterShort
float plannedStop = close + stopPoints
float plannedTarget = close - targetPoints
string shortMessage = "{\"ticker\":\"" + webhookTicker + "\",\"action\":\"sell\",\"quantity\":" + quantityText + ",\"orderType\":\"market\",\"stopLoss\":{\"type\":\"stop\",\"stopPrice\":" + str.tostring(plannedStop, format.mintick) + "},\"takeProfit\":{\"limitPrice\":" + str.tostring(plannedTarget, format.mintick) + "}}"
strategy.entry("Short", strategy.short, qty = orderQuantity, alert_message = shortMessage)
if strategy.position_size > 0
float longStop = strategy.position_avg_price - stopPoints
float longTarget = strategy.position_avg_price + targetPoints
string longExitMessage = "{\"ticker\":\"" + webhookTicker + "\",\"action\":\"exit\",\"cancel\":true}"
strategy.exit("Long Exit", from_entry = "Long", stop = longStop, limit = longTarget, alert_message = longExitMessage)
if strategy.position_size < 0
float shortStop = strategy.position_avg_price + stopPoints
float shortTarget = strategy.position_avg_price - targetPoints
string shortExitMessage = "{\"ticker\":\"" + webhookTicker + "\",\"action\":\"exit\",\"cancel\":true}"
strategy.exit("Short Exit", from_entry = "Short", stop = shortStop, limit = shortTarget, alert_message = shortExitMessage)
plot(fastEMA, "Fast EMA", color = color.aqua)
plot(slowEMA, "Slow EMA", color = color.orange)
The entry message carries a broker-side bracket, so the stop and target rest at the broker once the entry fills. The chart's strategy.exit() calls mirror that bracket, and their fill alerts send an exit as a backup. The exit message sets cancel to true so TradersPost cancels the resting bracket orders before it exits. If the broker's bracket has already closed the position, TradersPost rejects the later exit because there is no open position, which its documentation describes as a safeguard. The bracket prices are anchored to the signal bar's close, while the chart's exits use the average fill price, so the two can differ by the gap between that close and the fill.
Paste the corrected script into the Pine Editor and compile it before creating an alert. Confirm the chart uses the intended ES dataset, then replace the default webhook ticker when the explicit contract changes. Compilable code is only the beginning of the review, not proof that the order semantics are correct.
Common AI Futures Mistakes
Misusing alert_message
The alert_message expression is evaluated when its order executes, not when the script initially places the order. Other placeholders cannot be evaluated inside that argument, and an order without an alert_message supplies an empty custom message.1
Even a correct custom string remains unavailable unless the alert's Message field, or the //@strategy_alert_message annotation, contains {{strategy.order.alert_message}}. If either part is missing, the webhook body arrives empty instead of carrying your JSON.
Confusing Futures Sessions
For most futures contracts, the longer electronic trading session is the default named regular. Selecting RTH on a CME futures chart changes the active name to us_regular, while session.extended is equivalent to session.regular where no separate extended session exists.6
Therefore, code that tests session.regular to isolate daytime bars can include the electronic session instead. Ask the AI to inspect syminfo.session and define the intended named session explicitly.
Mishandling Continuous Contracts
On a continuous chart, syminfo.current_contract identifies the underlying contract ticker. Because its documented non-continuous fallback is inconsistent, test both na and an empty string before using it.
Do not infer rollover from a fixed number of days before expiration. TradingView's documented example switches GC1! from GCQ2022 to GCZ2022 on July 28, 2022, ahead of the August 29 expiration, based on the symbol's volume-derived switching rule.4 Explicit contract tickers make the intended destination unambiguous.
AI Pine Script Review Checklist
Compile And Alert Checks
- Compile in the Pine Editor under version 6.
- Remove deprecated syntax and unresolved type errors.
- Verify every dynamic value uses
str.tostring(). - Confirm the Message field contains
{{strategy.order.alert_message}}. - Inspect the resulting body as JSON, not merely as a Pine string.
Session And Symbol Checks
- Confirm whether the logic targets
regular,us_regular, or another named session. - Verify
syminfo.root,syminfo.mintick, andsyminfo.pointvaluewhere relevant. - Distinguish the chart symbol from the explicit contract sent in the webhook.
- Guard both
naand empty-string results before usingsyminfo.current_contract.
Order And Payload Checks
- Verify entry and exit actions match the receiving service's field specification.
- Confirm numeric JSON values are not accidentally quoted as strings.
- Test that fill alerts occur only for realtime executions, not historical orders.
- Delete and recreate the alert after changing code, inputs, symbol, or timeframe.
- Validate JSON before enabling automated submission.
Strategy fill notifications are generated for realtime executions, not orders on historical bars. A running alert also uses an independent server-side copy, so subsequent chart or input changes do not update it.7
From Chart To Live Execution
Create The Strategy Alert
Add the compiled strategy to the intended chart, open Create Alert, and select order fills, alert() calls, or both. For this example, select order fills and leave the Message field as {{strategy.order.alert_message}}. Pine code exposes alert events, but it does not create the running alert by itself.2
Enable webhook delivery in Notifications and enter the destination URL. TradingView sends the alert body with an application/json content type when the message is valid JSON; otherwise, it sends text/plain.8
Route The Webhook
TradersPost can parse the incoming ticker, action, and quantity, process the signal under the strategy subscription, and submit the resulting trade to the connected broker, exchange, or prop firm account. For futures, always send an explicit contract such as ESZ2026 instead of ES1!, for the reasons in the FAQ below. Review the signal record and resulting trade information to confirm that the payload was interpreted as intended.
Paper Trade First
Connect the alert to a paper account before risking capital. The TradersPost paper broker supports futures but has no futures data by default, so set a connected broker with live futures data, such as TradeStation, as its market data source. Webhook alerts require two-factor authentication, and TradingView publishes the source IP addresses used for webhook POST requests.8 During testing, watch the alert log for excessive activity. TradingView stops a strategy alert if it triggers more than 15 times within three minutes.7
Paper trade the corrected strategy through the complete webhook path. Compare the TradingView alert body, the receiving platform's parsed signal, and the paper account's order before enabling live submission.
Bottom Line
- Prompt for Pine Script v6, exact typed inputs, explicit futures symbols, and confirmed-bar conditions.
- Build
alert_messagevalues through concatenation andstr.tostring(). - Include
{{strategy.order.alert_message}}in the alert Message field or compiler annotation. - Treat
regular,us_regular, and continuous-contract rollover as futures-specific review items. - Recreate alerts after changes and verify the entire route with paper trading.
Use the broader Best AI for Pine Script framework to evaluate the assistant that generates your code, then paper trade this corrected strategy through a TradersPost webhook before considering live execution.
Frequently Asked Questions
What Does The Placeholder Do?
{{strategy.order.alert_message}} inserts the string supplied to the executed order's alert_message parameter. It must appear in the alert's Message field or the //@strategy_alert_message annotation. Without it, the custom string does not appear in the delivered notification.
Does strategy.close Support alert_message?
Yes. Pine Script v6 supports alert_message on strategy.close(), strategy.entry(), strategy.exit(), and strategy.order().1 It accepts a series string, so script values can be converted with str.tostring(). Do not embed alert placeholders such as {{close}} inside the argument.
Why Is My Message Missing?
Confirm that the Message field includes {{strategy.order.alert_message}} and that the order executed in realtime. If you changed the script, inputs, chart symbol, or timeframe after creating the alert, delete the old alert and create a new one.
Why Is Regular Session Wrong?
On many futures charts, regular identifies the default electronic session rather than daytime RTH. A CME futures chart configured for RTH reports us_regular. Session filters copied from equity scripts can therefore classify futures bars incorrectly.
Should Webhooks Use Continuous Symbols?
No. Our futures documentation recommends against continuous symbols such as NQ1!: we do not yet reliably resolve the contract behind them, and around roll periods our platform and TradingView may reference different contracts. Send an explicit contract ticker, such as NQZ2026, so there is no disagreement about which contract receives the order.
References
1 TradingView Pine Script Alerts
2 TradingView Pine Script Alerts FAQ
3 TradingView Chart Information
4 Continuous Futures Switching Dates
5 TradingView Pine Script Release Notes
6 TradingView Pine Script Sessions
7 TradingView Strategy Alerts
8 TradingView Webhook Configuration