Tutorials

How to Build an AI Pine Script Swing Trading Strategy

Prompt AI for a Pine Script v6 swing trading strategy, catch repainting and warm-up errors in the output, then route a confirmed signal to a broker.

Tom Hartman

Marketing

13 Min Read Reviewed by Tom Hartman Fact-checked by Tom Hartman
BluSky — The Future of Trading. Prop firm futures trading. Sign up at BluSky.pro.

An AI Pine Script swing trading strategy should be treated as a draft, even when it compiles. The main risks are higher-timeframe future leaks, unconfirmed bar values, missing warm-up checks, and indicator lengths that represent different calendar periods when the chart timeframe changes.

A reliable workflow starts with a precise Pine Script v6 prompt, continues with a manual code review, and ends with realtime alert testing before broker execution. The worked example below uses confirmed bars, a non-repainting higher-timeframe EMA, ATR-based exits, and explicit guards against undefined indicator values. For choosing the assistant itself, see our guide to the best AI for Pine Script.

What Counts as Swing Trading

Multi-Day Holds and Position State

Swing strategies hold positions across multiple bars or sessions. The script must therefore know whether a position is open, where its protective levels were established, and whether an exit has occurred as execution advances bar by bar.

Variables declared normally are initialized again on each execution. Adding the var keyword initializes a variable once and preserves its updates across subsequent bars.1 This is useful for storing a stop price, target price, entry state, or another value that must survive beyond the signal bar.

Intraday scripts also need correct state management, but persistent state becomes especially visible when a trade remains open through session boundaries. Whenever AI-generated code manually tracks a position, verify that the relevant variables use var or derive their state directly from strategy.position_size.

Higher-Timeframe Trend Filters

A swing system may trade signals on a lower chart timeframe while using a daily or weekly trend filter. Pine retrieves that context through request.security(). Scripts can read the chart timeframe through timeframe.period and compare timeframe durations through timeframe.in_seconds().2

This validation matters because a supposed higher-timeframe input can accidentally be set below the chart timeframe. It also helps control complexity. Pine permits up to 40 unique request.*() calls, or 64 on the Ultimate plan, so multiple symbols and filters can consume the allowance quickly.3

ATR-Based Stops for Swings

Average True Range measures volatility as an absolute price distance, not a percentage of price. An ATR value from a lower-priced instrument is therefore not directly comparable with one from a higher-priced instrument.4

ATR also incorporates its prior value when smoothing. Results depend partly on where the calculation begins, and StockCharts calculates at least 250 earlier periods to improve accuracy.4 An ATR multiple ties the stop distance to how far the instrument has recently been moving, rather than to a fixed tick count that means something different on every symbol.

Writing a Precise AI Prompt

Pin the Pine Script Version

Specify Pine Script v6. Version 6 removed the when parameter from applicable strategy.*() functions, changed default strategy margin percentages to 100, and enables dynamic request.*() calls by default.5 Without a pinned version, an AI assistant may return old syntax or logic whose behavior changed during migration.

Name Exact Inputs and Conditions

List every required input and define each condition. A useful request names the fast EMA length, slow EMA length, higher-timeframe EMA length, higher timeframe, ATR length, stop multiple, target multiple, entry rule, and discretionary exit rule.

Ask for documented input.* calls with numeric defaults and minimum values. Avoid prompts such as “make it trend-following.” If that style is your goal, define the mechanics or compare the result with a dedicated guide to AI trend-following strategies.

Require Confirmed-Bar Entry Logic

Require barstate.isconfirmed on entry and conditional exit signals. This restricts the condition to the bar's closing iteration, when prices are confirmed.6 Signals arrive later than logic based on a fluctuating realtime close, but historical and realtime behavior become more reproducible.

Worked Example: Prompt to Code

The Initial Prompt and Draft

Use a prompt with requirements that can be checked individually:

“Write a Pine Script v6 long-only swing strategy. Use configurable fast and slow EMAs on the chart for crossover entries. Require price to be above a configurable higher-timeframe EMA. Request only the last confirmed higher-timeframe EMA. Add configurable ATR stop and target multiples. Gate entries and crossover exits with barstate.isconfirmed. Reject a higher timeframe that is not above the chart timeframe. Add explicit na guards. Use named input.* calls and no calc_on_every_tick.”

A first draft will commonly contain a strategy() declaration, an EMA crossover, an HTF request, and a strategy.exit() call. Compilation only proves that the syntax is acceptable. It does not prove that historical data was available when shown or that realtime signals will match the strategy report.

Reviewing the Draft for Errors

  • Inspect every HTF request for lookahead_on without a [1] expression offset.
  • Check whether EMA and ATR values can be na during initialization.
  • Confirm that each fixed length is intentional for the selected chart timeframe.
  • Verify that entries and rule-based exits require a confirmed bar.

Using lookahead_on without offsetting the requested expression leaks future HTF information into historical bars.6 Early historical references can also return na when the referenced bar does not exist.1

The Corrected Version 6 Code

//@version=6
strategy("Confirmed HTF Swing Strategy", overlay = true,
     margin_long = 100, margin_short = 100,
     commission_type = strategy.commission.percent, commission_value = 0.05,
     slippage = 2)

int fastLength = input.int(20, "Fast EMA Length", minval = 1)
int slowLength = input.int(50, "Slow EMA Length", minval = 2)
int htfEmaLength = input.int(50, "HTF EMA Length", minval = 2)
string higherTimeframe = input.timeframe("1D", "Higher Timeframe")
int atrLength = input.int(14, "ATR Length", minval = 1)
float stopMultiple = input.float(2.0, "ATR Stop Multiple", minval = 0.1, step = 0.1)
float targetMultiple = input.float(3.0, "ATR Target Multiple", minval = 0.1, step = 0.1)

if timeframe.in_seconds(higherTimeframe) <= timeframe.in_seconds()
    runtime.error("Higher Timeframe must be above the chart timeframe.")

float fastEma = ta.ema(close, fastLength)
float slowEma = ta.ema(close, slowLength)
float atrValue = ta.atr(atrLength)

// Cross checks run on every bar, outside the lazily evaluated `and` chains below.
bool bullCross = ta.crossover(fastEma, slowEma)
bool bearCross = ta.crossunder(fastEma, slowEma)

float confirmedHtfEma = request.security(
     syminfo.tickerid,
     higherTimeframe,
     ta.ema(close, htfEmaLength)[1],
     lookahead = barmerge.lookahead_on)

bool indicatorsReady = not na(fastEma) and not na(slowEma) and not na(atrValue) and not na(confirmedHtfEma)
bool longEntry = barstate.isconfirmed and indicatorsReady and strategy.position_size == 0 and bullCross and close > confirmedHtfEma
bool ruleExit = barstate.isconfirmed and strategy.position_size > 0 and bearCross

string entryMessage = '{"ticker":"' + syminfo.ticker + '","action":"buy"}'
string exitMessage = '{"ticker":"' + syminfo.ticker + '","action":"exit"}'

var float activeStop = na
var float activeTarget = na

if longEntry
    activeStop := close - atrValue * stopMultiple
    activeTarget := close + atrValue * targetMultiple
    strategy.entry("Long", strategy.long, alert_message = entryMessage)
    strategy.exit("ATR Exit", "Long", stop = activeStop, limit = activeTarget, alert_message = exitMessage)

if barstate.isconfirmed and strategy.position_size > 0 and not na(activeStop) and not na(activeTarget)
    strategy.exit("ATR Exit", "Long", stop = activeStop, limit = activeTarget, alert_message = exitMessage)

if ruleExit
    strategy.close("Long", alert_message = exitMessage)

if strategy.position_size == 0 and strategy.position_size[1] > 0
    activeStop := na
    activeTarget := na

plot(fastEma, "Fast EMA", color = color.teal)
plot(slowEma, "Slow EMA", color = color.orange)
plot(confirmedHtfEma, "Confirmed HTF EMA", color = color.blue, linewidth = 2)
plot(activeStop, "Active Stop", color = color.red, style = plot.style_linebr)
plot(activeTarget, "Active Target", color = color.green, style = plot.style_linebr)

The crossovers are computed in the global scope because v6 evaluates and conditions lazily, so a history-dependent call placed late in a chain may not run on every bar.5 The declaration sets commission and slippage on purpose: with no commission arguments a strategy applies no commission, and slippage defaults to 0 ticks.7 Use values that match your broker. The HTF expression uses the documented combination of a one-bar offset and lookahead_on, returning the last confirmed HTF value across historical and realtime bars.6 The corrected strategy will signal later than a repainting draft. That delay is the expected cost of confirmation.

Why AI Misses Warm-Up Bars

What Warm-Up Bars Mean

A script starts at the first accessible bar and executes sequentially. A reference to unavailable history returns na. In TradingView's own example, a 20-bar ta.highest() call plots na on the first 19 bars, because it needs the current bar plus 19 earlier ones.1

Longer EMA, weekly, or smoothed ATR calculations require more history before their output should be trusted. AI often focuses on the signal expression and omits the initialization period entirely.

Guarding Against Early Bar Errors

Create a single readiness condition covering every entry input:

bool indicatorsReady = not na(fastEma) and not na(slowEma) and not na(atrValue) and not na(confirmedHtfEma)

Then require that condition before entry. For ATR research, include enough earlier data to let smoothing stabilize. StockCharts uses at least 250 earlier periods for its displayed ATR calculations.4 A productive follow-up prompt is: “What happens on the first N bars where each indicator has no valid value?”

Why AI Ties Length to Timeframe

Fixed Lengths Across Timeframes

A 20-bar length represents 100 minutes on a 5-minute chart but about four trading weeks on a daily chart. Changing the timeframe therefore changes the calendar span of the rule. ATR also changes with timeframe because each bar contributes a different range, while its absolute value remains dependent on the instrument's price scale.4

Scaling Inputs to Chart Timeframe

Use timeframe.period to inspect the chart interval and timeframe.in_seconds() to compare it with an expected interval.2 A timeframe string combines a multiplier and a unit letter, such as 1D for one day, and minutes are the one unit with no letter. There is no hour unit, so one hour is written 60; 1H is not valid.2

Ask AI either to scale lengths against a clearly defined base timeframe or reject charts outside an approved range. Validation is usually easier to audit because automatic scaling can change the intended strategy logic.

Why AI Leaks Future HTF Data

The Lookahead Future Leak

request.security() defaults to barmerge.lookahead_off. If AI changes it to lookahead_on without offsetting the expression, historical bars can receive HTF values before those values were knowable.3 This can make a strategy report appear unusually clean because the filter has advance knowledge of the completed HTF bar.

TradingView calls this dangerously misleading, and using lookahead to produce misleading results is not allowed in script publications.6

The Confirmed HTF Pattern

request.security(symbol, timeframe, expression[1], lookahead = barmerge.lookahead_on)

This is the pattern TradingView documents for requesting confirmed higher-timeframe values. The offset and lookahead_on work as a pair: together they return the last confirmed HTF value on historical and realtime bars. Merely switching to lookahead_off does not prevent an unconfirmed HTF value from changing on realtime bars and repainting after a restart.6

A Review Checklist for Swing Strategies

Repainting and Confirmation Checks

  • Every HTF request uses an expression offset with lookahead_on.
  • Entry and conditional exit signals require barstate.isconfirmed.
  • The strategy avoids unnecessary calc_on_every_tick and varip behavior.

Both tick-by-tick strategy calculations and varip state can create realtime behavior that historical bars cannot reproduce.6

Timeframe and Warm-Up Checks

  • Lengths are deliberately fixed, scaled, or restricted to an approved timeframe.
  • All entry indicators have explicit na guards.
  • The test range includes sufficient earlier history for ATR smoothing.

Execution Mechanics and Limits

  • strategy.exit() uses current v6 syntax without when.
  • Commission and slippage are set explicitly rather than left at zero.
  • Gap assumptions are reviewed in the strategy report.
  • The script remains within the applicable unique request.*() limit.

When an order level is crossed in the gap between bars, TradingView's broker emulator fills the order at the next bar's open rather than at the crossed level.7 Review those trades individually instead of assuming every simulated stop or target receives its specified price.

From Chart Alert to Live Execution

Generating the Alert Message

Use alert() for condition-driven events or the alert_message parameter on order-generating functions for order fill messages. Alerts trigger only on realtime bars, and dynamic messages can include calculated values through str.tostring().8

The example already builds its order messages as JSON with ticker and action fields, so the alert's Message field only needs the {{strategy.order.alert_message}} placeholder. Keep the payload aligned with the event it represents, and recreate the running TradingView alert after changing the script or its inputs because an existing alert retains a saved copy of its original configuration.8

Webhook Payload and Test Signals

A TradersPost signal is JSON sent to a strategy webhook. It can carry ticker, action, and quantity, plus optional instructions such as stopLoss and takeProfit. If the signal carries a quantity, the subscription's Allow signal override setting next to its quantity settings decides whether that value is used instead of the configured sizing.

{
  "ticker": "AAPL",
  "action": "buy",
  "quantity": 1,
  "test": true
}

When test is true, the signal is processed as a test and no order is sent to the broker. Use this mode to compare the TradingView Alert Log with the received webhook signal before enabling live submission. Then test with a paper trading account where available and monitor realtime alerts after deployment.

Bottom Line

  • Pin Pine Script v6 and name every input, condition, timeframe, and confirmation requirement.
  • Use expression[1] with lookahead_on for confirmed higher-timeframe data.
  • Guard EMA, ATR, and requested values against na during warm-up.
  • Validate fixed lengths against the chart timeframe and review gap fills.
  • Test JSON alerts before allowing any webhook signal to reach a broker.

AI can accelerate implementation, but it does not replace review. Use the broader AI for Pine Script guide to evaluate coding tools, then compare category-specific logic with guides to mean reversion strategies, breakout and opening range strategies, or momentum strategies.

After the strategy passes the checklist, connect its confirmed-bar alert to a broker through a webhook. Start with test:true, verify the received payload, and move to paper execution before considering live automated orders.

Frequently Asked Questions

Can AI Write a Working Strategy?

AI can produce Pine code that compiles, but compilation does not detect HTF future leaks, unconfirmed conditions, warm-up omissions, or timeframe-dependent assumptions. Treat the first response as a draft and review it against the checklist.

Why Does My Strategy Repaint?

Common causes are lookahead_on without an expression offset and conditions based on an unconfirmed realtime close, high, or low. Correcting either issue usually makes signals later because confirmed information arrives later.

Which Pine Version Should I Request?

Request Pine Script v6 explicitly. This prevents the assistant from relying on removed parameters or older request behavior and makes the generated code easier to compare with current documentation.

How Many Warm-Up Bars Are Needed?

The answer depends on the longest calculation, the requested timeframe, and the available dataset. Guard every required value against na. For stable ATR research, include substantial prior history rather than starting the test immediately when the minimum length becomes available.

How Do I Trade It Live?

Generate a realtime TradingView alert with JSON containing the required signal fields, send it to the webhook URL, and inspect the received signal. Process a test payload first, then use paper execution where available before enabling live broker submission.

References

1 TradingView: Execution Model
2 TradingView: Timeframes
3 TradingView: Other Timeframes and Data
4 StockCharts ChartSchool: Average True Range
5 TradingView: Migrating to Pine Script Version 6
6 TradingView: Repainting
7 TradingView: Strategies
8 TradingView: Alerts

All In Tutorials
  • Tutorials Oct 10, 2026

    AI Pine Script Moving Average Indicators

    Learn to prompt AI for working Pine Script v6 moving average, Hull MA, and Supertrend code, catch its common bugs, and turn the result into an alert.

  • Tutorials Oct 9, 2026

    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.

  • Tutorials Oct 9, 2026

    AI Pine Script DCA Strategy: Prompts That Work

    Learn to prompt AI for working Pine Script v6 DCA, grid, and scale-in code, plus the pyramiding, exit, and trimming mistakes to catch before you deploy it.

  • Tutorials Oct 9, 2026

    Coding an AI Pine Script Scalping Strategy

    Prompt an AI for a Pine Script v6 scalping strategy, then audit the backtest for the fill, cost, and bar-detail mistakes AI assistants make most.

Start trading at scale today. Sign up for free.

Free 7-day trial

Set-up in 3 minutes

Paper account for testing

By continuing, you agree to the TradersPost Terms of Service and Privacy Policy. This site is protected by reCAPTCHA.

TradersPost operates as a non-custodial automated trading platform, enabling users to connect alerts from their preferred trading platforms to their selected brokerage or exchange accounts. It abstains from the transmission, custody, or management of customer funds, covering both traditional and cryptocurrency assets. Typically, registration requirements set by regulatory entities such as the SEC, FINRA, or FinCEN apply to entities that hold or transmit customer funds. To ensure ongoing compliance, TradersPost regularly engages with regulatory authorities to confirm its adherence to all relevant local and federal laws.

TradersPost does not provide alerts, signals, research, analysis, or trading advice of any kind. It is designed to assist traders and investors in making their own trading decisions based on their alerts. The platform does not offer recommendations regarding securities to buy or sell, nor does it provide trading or investing advice. The platform and its features, capabilities, and tools are provided 'as-is' without any warranty.

Risk Disclosure: The use of automated trading systems involves inherent risks, including the potential for significant financial loss. These systems operate based on predetermined algorithms that may not fully adapt to changing market conditions, possibly making them unsuitable for some investors. Individuals are advised to thoroughly assess their financial situation and risk tolerance before using this platform.

Testimonials appearing on this website may not be representative of other clients or customers and is not a guarantee of future performance or success.

Broker, exchange, trading platform, company names, product names, service marks, trademarks, and logos appearing on this website are the property of their respective owners and are used solely to identify supported connections and technical compatibility. TradersPost is an independent third-party platform and is not affiliated with, endorsed by, sponsored by, or authorized by any of these organizations unless expressly stated. Technical compatibility does not imply a commercial partnership, sponsorship, endorsement, or authorization. See Important Disclosures for trademark information.

© 2026 TradersPost, Inc. All rights reserved.