# Backtest Viewer (/platform/ai-backtesting/backtest-viewer)



![](/images/ai-backtesting/backtest-viewer/backtest-viewer-intro.png)

Users have access to a wide variety of useful data and visualisations to evaluate a strategy trough the backtest viewer.

By default the backtest viewer will display backtest information from the most recent fetched strategy in the conversation.

<Tip>
  You can check backtest results from previously fetched strategies by simply clicking on the card holding the strategy
</Tip>

## Backtest Statistics [#backtest-statistics]

### Overview [#overview]

* Historical Net Profit
* Net Profit
* Trades
* Win Rate
  * Winning Trades (left)
  * Losing Trades (right)
* Max Drawdown
  * Max Drawdown %

### Performance [#performance]

![](/images/ai-backtesting/backtest-viewer/performance.png)

The **Performance** section includes two visualisations:

* Net Daily PNL: Net daily trades result (profit + loss)
* Weekday Performance: Sum of the profit and losses made during a specific day of the week

#### Table [#table]

A performance table is included listing statistics for all trades, only longs, and only shorts. The following statistics are returned:

* <Tooltip headline="Net Profit" tip="Net result after combining all realized profits and losses.">
    Net Profit
  </Tooltip>
* <Tooltip headline="CAGR" tip="Annualized compounded return over the full backtest period.">
    CAGR (Compounded annualized growth rate)
  </Tooltip>
* <Tooltip headline="Gross Profit" tip="Sum of profits from all winning trades before losses.">
    Gross Profit
  </Tooltip>
* <Tooltip headline="Gross Loss" tip="Sum of losses from all losing trades before gains.">
    Gross Loss
  </Tooltip>
* <Tooltip headline="Profit Factor" tip="Gross profit divided by gross loss. Higher values indicate stronger profitability.">
    Profit Factor
  </Tooltip>
* <Tooltip headline="Average P&L per Day" tip="Average net profit or loss generated per trading day.">
    Average P&L per Day
  </Tooltip>
* <Tooltip headline="Average P&L per Week" tip="Average net profit or loss generated per trading week.">
    Average P&L per Week
  </Tooltip>
* <Tooltip headline="Drawdown" tip="Largest drop in equity from a peak to a later low.">
    Drawdown
  </Tooltip>
  * <Tooltip headline="Drawdown Percent" tip="Largest peak-to-low equity decline expressed as a percentage.">
      Drawdown Percent
    </Tooltip>

### Trades Analysis [#trades-analysis]

![](/images/ai-backtesting/backtest-viewer/trades-analysis.png)

The **Trades Analysis** section includes two visualisations:

* P\&L Distribution: Distribution of trade profit and losses
* Winrate: Shows winrate, winning trades and losing trades
* Duration vs P\&L: Scatter plot showing trade duration (in bars) against the trade result

#### Table [#table-1]

A trades analysis table is included listing statistics for all trades, only longs, and only shorts. The following statistics are returned:

* <Tooltip headline="Closed Trades" tip="Total number of trades that have been opened and closed.">
    Closed Trades
  </Tooltip>
* <Tooltip headline="Winning Trades" tip="Number of closed trades with a positive result.">
    Winning Trades
  </Tooltip>
* <Tooltip headline="Losing Trades" tip="Number of closed trades with a negative result.">
    Losing Trades
  </Tooltip>
* <Tooltip headline="Win Rate" tip="Winning trades divided by total closed trades.">
    Win Rate
  </Tooltip>
* <Tooltip headline="Avg P&L" tip="Average profit or loss across all closed trades.">
    Avg P&L
  </Tooltip>
* <Tooltip headline="Avg Winning Trade" tip="Average profit across winning trades only.">
    Avg Winning Trade
  </Tooltip>
* <Tooltip headline="Avg Losing Trade" tip="Average loss across losing trades only.">
    Avg Losing Trade
  </Tooltip>
* <Tooltip headline="Largest Winning Trade" tip="Single closed trade with the highest profit.">
    Largest Winning Trade
  </Tooltip>
* <Tooltip headline="Largest Losing Trade" tip="Single closed trade with the largest loss.">
    Largest Losing Trade
  </Tooltip>

#### Duration [#duration]

![](/images/ai-backtesting/backtest-viewer/duration.png)

A duration table is included listing statistics for all trades, only longs, and only shorts. The following statistics are returned:

* <Tooltip headline="Avg Trade Duration (bars)" tip="Average number of bars a trade stays open.">
    Avg Trade Duration (bars)
  </Tooltip>
* <Tooltip headline="Avg Winning Trade Duration (bars)" tip="Average number of bars winning trades stay open.">
    Avg Winning Trade Duration (bars)
  </Tooltip>
* <Tooltip headline="Avg Losing Trade Duration (bars)" tip="Average number of bars losing trades stay open.">
    Avg Losing Trade Duration (bars)
  </Tooltip>
* <Tooltip headline="Avg Trades per Day" tip="Average number of closed trades per day.">
    Avg Trades per Day
  </Tooltip>
* <Tooltip headline="Avg Trades per Week" tip="Average number of closed trades per week.">
    Avg Trades per Week
  </Tooltip>
* <Tooltip headline="Longest Trade (bars)" tip="Greatest number of bars any single trade stayed open.">
    Longest Trade (bars)
  </Tooltip>
* <Tooltip headline="Shortest Trade (bars)" tip="Fewest number of bars any single trade stayed open.">
    Shortest Trade (bars)
  </Tooltip>
* <Tooltip headline="Longest Winning Streak (bars)" tip="Longest consecutive winning period, measured in bars.">
    Longest Winning Streak (bars)
  </Tooltip>
* <Tooltip headline="Longest Losing Streak (bars)" tip="Longest consecutive losing period, measured in bars.">
    Longest Losing Streak (bars)
  </Tooltip>

## Trades Log [#trades-log]

![](/images/ai-backtesting/backtest-viewer/trades-log.png)

A trades log table is included showing each closed trade in chronological order, including the trade number, trade side, entry, exit, net P\&L, and the running cumulative P\&L after each trade.

<Tip>
  Click on a column to sort it ascendingly or descendingly.
</Tip>

### Calendar View [#calendar-view]

![](/images/ai-backtesting/backtest-viewer/trades-calendar.png)

When the `View Mode` is switched to calendar view, trades are grouped by day in a monthly layout with month navigation at the top. Each day can show its net P\&L, number of trades, and win rate, while a summary below highlights the month net P\&L, best day, worst day, and average trades per day.

## Strategy Comparison View [#strategy-comparison-view]

When the Backtesting AI fetches multiple strategies at once, these will eb displayed in a comparison view, allowing for detailed comparisons.

### Comparison Table [#comparison-table]

![](/images/ai-backtesting/backtest-viewer/comparison-viewer.png)

The comparison table displays each strategy side by side, including the symbol and timeframe, strategy name, net profit, trades, win rate, max DD %, and profit factor.

### Comparison Scatter Plots [#comparison-scatter-plots]

The comparison scatter plots help visualize how strategies relate across key metrics, with views such as CAGR vs Max Drawdown %, Profit Factor vs Trade Count, Winrate vs Trades, and Normalized Avg Win vs Avg Duration.

Each point represents a strategy, with strong performers being located in the quadrant in green.

<Tip>
  Hovering over a strategy point in the graph shows a tooltip with the relevant strategy details.
</Tip>


# Fetching Information (/platform/ai-backtesting/fetching-information)



![](/images/ai-backtesting/fetching-information/smart_trail.png)

The AI Backtesting Assistant is able to fetch our product documentation to provide relevant information to you about the features used by a retrieved strategy or to simply help you learn more about our products.

The AI will automatically determine whether it requires fetching context from the documentation depending on the user query.

<Tip>
  You can force the AI to fetch information from the documentation by telling it to fetch the documentation.

  For example:

  > Tell me more about confirmation signals, use the documentation

  This can prevent the AI from giving partial information he might know from his existing context window.
</Tip>

## Detailed Strategy Descriptions Using Docs [#detailed-strategy-descriptions-using-docs]

![](/images/ai-backtesting/fetching-information/explanation.png)

We can request for the AI to provide important context about the conditions returned by a trading strategy.

## Using Docs to Get Tailored Strategies [#using-docs-to-get-tailored-strategies]

By asking the AI to learn more about the LuxAlgo features from the documentation, we can effectively get unique trading strategies by simply mentioning trading styles.

For example, we can use the following prompt:

> From the documentation search for the best features for swing trading, then find a strategy using those features that provides good results on crypto on the hourly timeframe


# Fetching Strategies (/platform/ai-backtesting/fetching-strategies)



![](/images/ai-backtesting/fetching-strategies/chat.png)

The primary role of the AI Backtesting Assistant is to help users find strategies quickly without having to manually iterate through various conditions, tickers, and timeframes on TradingView charts. Instead, users can simply ask the AI to look for the strategy that would best match the criteria they want.

The AI Backtesting Assistant has access to millions of unique strategies, each made using a wide variety of conditions.

<Warning>
  This AI tool’s backtesting data & analytics are for informational purposes only and do not constitute financial advice or recommendations to buy or sell any financial product, including but not limited to securities, derivatives, cryptocurrencies, or other investment instruments.

  This tool may contain errors, and past performance is not indicative of future results. Always conduct your own research and consult with a professional financial advisor.

  LuxAlgo is not liable for any decisions made based on any information given. Read our [full disclaimer](https://www.luxalgo.com/legal/disclaimer/).
</Warning>

## Strategies Configuration [#strategies-configuration]

Strategies are evaluated using a maximum of the most recent 20.000 bars, with an initial capital of 10.000 base currency, without commissions/slippage, and using default settings for all toolkits features.

Unit position sizing is used across all tickers.

When a strategy enters a trade, any previous trade is closed. As such, it is not possible for two positions to be open at the same time.

<Info>
  Strategies do not make use of stop losses or take profits.
</Info>

See the section below to learn more about the supported tickers, timeframes, and backtesting conditions.

### Schedule [#schedule]

In order to preserve ressources and avoid redundancy we run backtests three times a week:

* **Monday** 23:30 UTC
* **Wednesday** 23:30 UTC
* **Friday** 23:30 UTC

### Supported Tickers [#supported-tickers]

The following tickers are supported:

<Accordion title="Stocks (Cboe BZX)">
  * AAPL
  * AMD
  * AMZN
  * BABA
  * CELH
  * COIN
  * CRWD
  * DIS
  * DUOL
  * GME
  * GOOG
  * HOOD
  * INTC
  * MA
  * META
  * MSFT
  * MSTR
  * NFLX
  * NKE
  * NVDA
  * ORCL
  * PLTR
  * PYPL
  * RIVN
  * SMCI
  * SNOW
  * SOFI
  * TSLA
  * UBER
  * WMT
</Accordion>

<Accordion title="ETFs (Cboe BZX)">
  * ARKK
  * IWM
  * QQQ
  * SPY
  * VTI
</Accordion>

<Accordion title="Crypto">
  * AAVEUSDT (Binance)
  * ADAUSDT (Binance)
  * ATOMUSDT (Binance)
  * AVAXUSDT (Binance)
  * BCHUSDT (Binance)
  * BNBUSDT (Binance)
  * BTCUSDT (Binance)
  * DOGEUSDT (Binance)
  * DOTUSDT (Binance)
  * EOSUSDT (Binance)
  * ETHUSDT (Binance)
  * HBARUSDT (Binance)
  * HYPEUSD (Kucoin)
  * JUPUSDT (Binance)
  * KASUSD (Kucoin)
  * LINKUSDT (Binance)
  * LTCUSDT (Binance)
  * POLUSDT (Binance)
  * SHIBUSDT (Binance)
  * SOLUSDT (Binance)
  * SUIUSDT (Binance)
  * TONUSDT (Binance)
  * TRXUSDT (Binance)
  * UNIUSDT (Binance)
  * VETUSDT (Binance)
  * XLMUSDT (Binance)
  * XRPUSDT (Binance)
</Accordion>

<Accordion title="Forex">
  * AUDJPY
  * AUDUSD
  * EURAUD
  * EURGBP
  * EURUSD
  * EURJPY
  * GBPAUD
  * GBPJPY
  * GBPUSD
  * NZDUSD
  * USDCAD
  * USDCHF
  * USDJPY
</Accordion>

<Accordion title="Commodities">
  * XAGUSD
  * XAUUSD
  * UKOIL
</Accordion>

<Accordion title="Futures">
  * NQ
  * ES
  * RTY
  * YM
  * GC
  * SI
  * CL
  * NG
  * PL
  * ZC
  * ZW
  * ZS
  * 6E
  * 6J
</Accordion>

<Info>
  US equities (Stocks and ETFs) data is sourced from Cboe BZX and use regular trading hours.

  Futures are based on continuous contracts and use electronic trading hours, without adjustment for contracts changes. Additionally, unlike TradingView, Futures contracts roll on the day prior to expiration.
</Info>

### Supported Timeframes [#supported-timeframes]

5m, 15m, and 60m timeframes are supported.

## Strategy Entry and Exits Constructions [#strategy-entry-and-exits-constructions]

Strategy entry conditions can make use of multiple conditions from different toolkits together. We classify conditions as triggers and filters:

* Triggers: Triggers determine the initial conditions used by a strategy. These tend to be less persistent events on a chart, occurring for a single bar.
* Filters: Filters add precision to a strategy, potentially filtering out bad trades that would have been taken by simply using triggers. Filters tend to be more persistent, lasting for multiple bars on a chart.

Entry conditions contain a single trigger, and can contain up to two filters.

<Info>
  It is possible for a long/short entry condition to contain both bullish and bearish conditions
</Info>

Long and short entry conditions mirror each other, that is, if the long entry condition uses a bullish condition, the short entry condition will use the same condition but bearish.

Example:

> * long: **Confirmation Any Bullish** and **Bullish Smart Trail** and **Bearish Trend Tracer**
> * short: **Confirmation Any Bearish** and **Bearish Smart Trail** and **Bullish Trend Tracer**

Finally, it is possible that some strategies using conditions from the Signals & Overlays® toolkit use exits condition to close trades.

### Signals & Overlays® Conditions [#signals--overlays-conditions]

***

#### Triggers [#triggers]

<Accordion title="Bullish Triggers">
  * Confirmation Any Bullish
  * Confirmation Normal Bullish
  * Confirmation Strong Bullish
  * Contrarian Any Bullish
  * Contrarian Normal Bullish
  * Contrarian Strong Bullish
  * Smart Trail Switch Bullish
  * Price Cross Under Reversal Zones S1
  * Trend Tracer Switch Bullish
  * Trend Catcher Switch Bullish
  * Neo Cloud Switch Bullish
</Accordion>

<Accordion title="Bearish Triggers">
  * Confirmation Any Bearish
  * Confirmation Normal Bearish
  * Confirmation Strong Bearish
  * Contrarian Any Bearish
  * Contrarian Normal Bearish
  * Contrarian Strong Bearish
  * Smart Trail Switch Bearish
  * Price Cross Over Reversal Zones R1
  * Trend Tracer Switch Bearish
  * Trend Catcher Switch Bearish
  * Neo Cloud Switch Bearish
</Accordion>

#### Filters [#filters]

<Accordion title="Bullish Filters">
  * Confirmation Uptrend
  * Contrarian Uptrend
  * Bullish Smart Trail
  * Price Below Reversal Zones S1
  * Trend Tracer Bullish
  * Trend Catcher Bullish
  * Neo Cloud Bullish
</Accordion>

<Accordion title="Bearish Filters">
  * Confirmation Downtrend
  * Contrarian Downtrend
  * Bearish Smart Trail
  * Price Above Reversal Zones R1
  * Trend Tracer Bearish
  * Trend Catcher Bearish
  * Neo Cloud Bearish
</Accordion>

<Accordion title="Neutral Filters">
  * Trend Strength Trending
  * Trend Strength Ranging
</Accordion>

#### Exits [#exits]

Strategies using the Signals & Overlays® toolkit can make exit trades which are outlined below:

<Accordion title="Exit Conditions">
  * Any strategy using Confirmation Signals can make use of builtin-exits labelled as "Confirmation Built-in Exits".
  * Any strategy using Contrarian Signals can make use of builtin-exits labelled as "Contrarian Builtin Exits".
  * Any strategy using Reversal Zones can make use of the exit condition "Price Cross R1/S1 Average", which will exit a trade when price crosses the average of the Reversal Zones.
</Accordion>

### Price Action Concepts® Conditions [#price-action-concepts-conditions]

***

Currently only triggers are supported for the Price Action Concepts® toolkit.

#### Triggers [#triggers-1]

<Accordion title="Bullish Triggers">
  * Bullish CHoCH
  * Bullish BOS
  * Bullish OB Entered
  * Bullish OB Exited
  * New Bullish FVG
  * Bearish FVG Mitigated
  * New Bullish Opening Gap
  * Bearish Opening Gap Mitigated
  * New Bullish Volume Imbalance
  * Bearish Volume Imbalance Mitigated
  * Bullish Liquidity Grab
  * Bearish Trendline Break
</Accordion>

<Accordion title="Bearish Triggers">
  * Bearish CHoCH
  * Bearish BOS
  * Bearish OB Entered
  * Bearish OB Exited
  * New Bearish FVG
  * Bullish FVG Mitigated
  * New Bearish Opening Gap
  * Bullish Opening Gap Mitigated
  * New Bearish Volume Imbalance
  * Bullish Volume Imbalance Mitigated
  * Bearish Liquidity Grab
  * Bullish Trendline Break
</Accordion>

### Oscillator Matrix® Conditions [#oscillator-matrix-conditions]

***

#### Triggers [#triggers-2]

<Accordion title="Bullish Triggers">
  * New Bullish Overflow
  * Money Flow Crossing Over 50
  * HyperWave Oversold Bullish Signal
  * HyperWave Crossing Over 80
  * HyperWave Crossing Over 50
  * Reversal Any Up
  * New HyperWave Bullish Divergences
</Accordion>

<Accordion title="Bearish Triggers">
  * New Bearish Overflow
  * Money Flow Crossing Under 50
  * HyperWave Overbought Bearish Signal
  * HyperWave Crossing Under 20
  * HyperWave Crossing Under 50
  * Reversal Any Down
  * New HyperWave Bearish Divergences
</Accordion>

#### Filters [#filters-1]

<Accordion title="Bullish Filters">
  * Bullish Overflow
  * Money Flow Above 50
  * HyperWave Above 80
  * HyperWave Above 50
  * Strong Bullish Confluence
  * Weak Bullish Confluence
</Accordion>

<Accordion title="Bearish Filters">
  * Bearish Overflow
  * Money Flow Below 50
  * HyperWave Below 20
  * HyperWave Below 50
  * Strong Bearish Confluence
  * Weak Bearish Confluence
</Accordion>

## How To Fetch Strategies [#how-to-fetch-strategies]

![](/images/ai-backtesting/fetching-strategies/landing.png)

Interacting with the AI Backtesting Assistant is straightforward. All users need to do is type a prompt in the chat box located at the bottom of their screen.

Here are examples of prompts that will trigger the AI Backtesting Assistant to retrieve a trading strategy:

* What is the best performing strategy across all crypto tickers on the 5-minute timeframe?
* Give me three strategies using fair value gaps that have a winrate above 80%.
* Find a strategy with above average net profit but below average drawdown.

Users can also click on one of the suggested prompts in the search group located above the input prompt.

The AI can access the following backtesting results for strategies:

* Net Profit
* Max Net Profit
* Closed Trades
* Winning Trades
* Losing Trades
* Winrate
* Gross Profit
* Gross Loss
* Profit Factor
* Max Drawdown
* Max Drawdown Percent
* Average Trade
* Average Winning Trade
* Average Losing Trade

<Info>
  Due to calculations being sensitive to data differences, our Drawdowns calculations are based on realized profits/losses, which is not the case on TradingView, this can explain why certain differences can be observed.
</Info>

These results will be returned in a component in the chat.

![Strategy Results](/images/ai-backtesting/fetching-strategies/table.png)

If multiple strategies are returned, they will all be located within a table.

<Info>
  A maximum of 3 strategies can be returned in a table at once, while a maximum of three strategies can be returned from a single fetching operation.

  If the AI needs to fetch more than 3 strategies it will fetch strategies sequentially.
</Info>

The AI can also access to the following information from a strategy:

* Strategy begins at: When the strategy started being evaluated
* Strategy current position: Long, short, or flat if no positions are currently open

### Fetch Statistics Across The Entire Strategy Population [#fetch-statistics-across-the-entire-strategy-population]

![](/images/ai-backtesting/fetching-strategies/compare.png)

The AI can get general statistics and data across the whole population of strategies it has access to.

Example of prompts doing this are:

* What is the total net profit of all bitcoin strategies you have?
* On BTCUSD, are strategies using normal Confirmation signals better than the ones using strong Confirmation signals overall?
* What is total net profit of profitable strategies across stocks?

This can be useful to get big picture information about strategies.

<Info>
  Do note that such prompts can lead to high wait time for the results.
</Info>

### Analyzing strategy trades [#analyzing-strategy-trades]

A returned strategy by the AI Backtesting Assistant contains a list of trades, each having information on its side (long / short), entry time and price, closing time and price, and P\&L result.

The AI Backtesting Assistant may filter trades by their index, trade side, result (win / loss), holding time (in minutes), P\&L, opening and closing time. Then an operation may be applied to a specific trade information from the filtered results.

Available capabilities from the trade analysis include:

* Trade filtering by index, trade side, result (win / loss), holding time (in minutes), P\&L, opening and closing time
* Operations on filtered subset such as average, median, count, sum, percentage
* Winning/Losing streak analysis

Example of typical questions one could ask related to trades of a strategy may include:

* Which trade has been the most/least profitable one?
* What is the entry and exit price price of most profitable long trade?
* What is the average trade duration of winning trades?
* What is the average profit/loss of trades with a duration above 50 minutes?
* How many winning trades occurred after 14 Jan 2025?
* What is the percentage of trades amongst the first 50 ones that are winning trades?
* What is the losing streak length before the last win?
* What is the average winning streak length?
* How many trades have a profit superior to the average winning trade?

## Reproducing a Strategy on TradingView [#reproducing-a-strategy-on-tradingview]

Once you get a strategy you like, you may be inclined to add it to your chart to visualize it and further optimize its results on TradingView.

![](/images/ai-backtesting/fetching-strategies/replicate.png)

To do so, simply click on the TradingView logo located near the ticker symbol for the single strategy component, and to the right of a hovered row on a table containing strategies.

![](/images/ai-backtesting/fetching-strategies/dialog.png)

This will open a dialog box containing all the steps required to get the strategy on TradingView, as well as the scripted strategy which you must copy and paste to the relevant backtester.

<Note>
  A strategy using multiple conditions from at least two toolkits will require using LUCID connectors, learn more about LUCID connectors [here](/platform/ai-backtesting/backtesters/strategy-scripting/lucid-connectors).
</Note>

If replicating a strategy involves making use of LUCID conectors, **make sure you enable the relevant features**, for example, if a strategy using the **Trend Catcher** requires using the LUCID connector of the **Signals & Overlays** toolkit, make sure the Trend Catcher overlay is enabled in the toolkit settings.

<Warning>
  We compute strategies using data that might different from the one on TradingView, which can limit the reproducibility of strategies returned by the AI.

  Some users might not be able to reproduce exact strategy results due to the limitation on how much data certain users can access.
</Warning>

## Saving Strategies [#saving-strategies]

![](/images/ai-backtesting/fetching-strategies/save.png)

Strategies can be saved in the sidebar by clicking on the star icon located near the ticker symbol for the single strategy component, and to the right of a hovered row on a table containing strategies.

![](/images/ai-backtesting/fetching-strategies/csv.png)

Additionally, strategies returned in a table can be exported as a CSV file.

## Prompting Tips [#prompting-tips]

The AI can make mistakes when fetching strategies, as this is a very complex process depending on the user query.

We will always aim to improve the AI Backtesting Assistant over time; however, the following tips can help you get the most from our AI.

### Make Detailed Queries [#make-detailed-queries]

Make sure to detail every aspect of the strategy you want to fetch if you have a precise one in mind, for example:

| Original                                                     | Improved                                                                                                                     |
| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| Fetch a strategy that uses Confirmation Signals              | Fetch a strategy that only uses Confirmation Signals and no other conditions                                                 |
| I want a PAC strategy that goes long on a new FVG, and CHoCH | I want a PAC strategy that goes long when:<br /><br />- Step 1: a new bullish FVG occur<br />- Step 2: a bullish CHoCH occur |
| I want the best strategy                                     | I want the strategy with the highest net profit on BTCUSD 5 minute                                                           |

Context and details will help the AI avoid making guesses about your query.

### Encourage the AI to Improve [#encourage-the-ai-to-improve]

The AI Backtesting Assistant can be fairly self-aware if a fetched strategy doesn't meet a user criteria, and will then retry.

But if the AI response is clearly mistaken, then it can be good to let it know using a prompt encouraging re-evaluation of the fetching process:

> The strategy your returned is not the one I want. Correct the issue in your fetching process and retry to fetch the strategy in accordance with my query.

### Split Complex Queries Into Multiple Tasks [#split-complex-queries-into-multiple-tasks]

The AI will be more prone to mistakes if the query is complex, as such, it is best to do follow-up messages asking about the details of a strategy rather than asking the AI to give those alongside other information.

For example:

> Return a good strategy on BTCUSD, alongside the average net profit of all strategies on BTCUSD that use Confirmation signals, then tell me what the highest winrate strategy is, using the same conditions as the first one

The AI Backtesting Assistant might provide more relevant answers if we let context accumulate by splitting and re-ordering our initial query into multiple ones:

<Steps>
  <Step title="First Query">
    Return a good strategy on BTCUSD
  </Step>

  <Step title="Second Query">
    Find the strategy with the highest winrate that use the same exact entry conditions returned previously
  </Step>

  <Step title="Third Query">
    Return the average net profit of all strategies on BTCUSD that use Confirmation Signals.
  </Step>
</Steps>


# Introduction (/platform/ai-backtesting/introduction)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/PaYLlbjcW5U"
    frameBorder="0"
  />
</div>

<Card title="AI Backtesting Assistant" icon="sparkle" href="https://www.luxalgo.com/chat/">
  Access the AI Backtesting Assistant here.
</Card>

The [AI Backtesting Assistant](https://www.luxalgo.com/backtesting/chat/) is a conversational AI leveraging a variety of state of the arts large language models to scan and retrieve a wide variety of strategies built using core features from our Signals & Overlays®, Price Action Concepts®, and Oscillator Matrix® toolkits. It also can give contextual information about our products & markets.

<Info>
  The AI Backtesting Assistant is only available for Ultimate users.
</Info>

<video className="w-full aspect-video" src="/videos/ai-backtesting/introduction/chat_demo.mp4" />

To learn more about the capabilities of the AI Backtesting Assistant, see the pages below:

<CardGroup cols="2">
  <Card title="Fetching Strategies" icon="searchengin" href="/platform/ai-backtesting/fetching-strategies">
    Fetch strategies simply by using natural language
  </Card>

  <Card title="Viewing Backtests" icon="chart-bar" href="/platform/ai-backtesting/backtest-viewer">
    View detailed strategies backtset results
  </Card>

  <Card title="Fetching Information" icon="books" href="/platform/ai-backtesting/fetching-information">
    Learn more about LuxAlgo toolkits using our assistant
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/platform/ai-backtesting/troubleshooting">
    Common issues and solutions
  </Card>
</CardGroup>

## FAQ [#faq]

<Accordion title="How do I get access to AI BAcktesting?">
  All users can access AI Backtesting [here](https://www.luxalgo.com/chat). Users with Premium and Ultimate plans can benefit from higher usage limits.
</Accordion>

<Accordion title="What are credits?">
  Credits is the currency system used to control the usage amount a user has with our AI products. The credit balance of an user is shared across both AI Backtesting platform and Quant, our Pine Script® coding agent.

  User credits reset every first of the month.
</Accordion>

<Accordion title="What strategies are available?">
  The AI Backtesting Assistant has access to the backtesting results of over 10 million trading strategies using core features from the Signals & Overlays®, Price Action Concepts®, and Oscillator Matrix® toolkits.
</Accordion>

<Accordion title="What tickers and timeframes are backtested?">
  Strategies are evaluated for some of the most traded tickers across 3 timeframes: 5 minute, 15 minute, and 1 hour.

  **Included ticker symbols by market:**

  <Accordion title="Stocks (Cboe BZX)">
    * AAPL
    * AMD
    * AMZN
    * BABA
    * CELH
    * COIN
    * CRWD
    * DIS
    * DUOL
    * GME
    * GOOG
    * HOOD
    * INTC
    * MA
    * META
    * MSFT
    * MSTR
    * NFLX
    * NKE
    * NVDA
    * ORCL
    * PLTR
    * PYPL
    * RIVN
    * SMCI
    * SNOW
    * SOFI
    * TSLA
    * UBER
    * WMT
  </Accordion>

  <Accordion title="ETFs (Cboe BZX)">
    * ARKK
    * IWM
    * QQQ
    * SPY
    * VTI
  </Accordion>

  <Accordion title="Crypto">
    * AAVEUSDT (Binance)
    * ADAUSDT (Binance)
    * ATOMUSDT (Binance)
    * AVAXUSDT (Binance)
    * BCHUSDT (Binance)
    * BNBUSDT (Binance)
    * BTCUSDT (Binance)
    * DOGEUSDT (Binance)
    * DOTUSDT (Binance)
    * EOSUSDT (Binance)
    * ETHUSDT (Binance)
    * HBARUSDT (Binance)
    * HYPEUSDT (Kucoin)
    * JUPUSDT (Binance)
    * KASUSDT (Kucoin)
    * LINKUSDT (Binance)
    * LTCUSDT (Binance)
    * SHIBUSDT (Binance)
    * POLUSDT (Binance)
    * SOLUSDT (Binance)
    * SUIUSDT (Binance)
    * TONUSDT (Binance)
    * TRXUSDT (Binance)
    * UNIUSDT (Binance)
    * VETUSDT (Binance)
    * XLMUSDT (Binance)
    * XRPUSDT (Binance)
  </Accordion>

  <Accordion title="Forex">
    * AUDJPY
    * AUDUSD
    * EURAUD
    * EURGBP
    * EURUSD
    * EURJPY
    * GBPAUD
    * GBPJPY
    * GBPUSD
    * NZDUSD
    * USDCAD
    * USDCHF
    * USDJPY
  </Accordion>

  <Accordion title="Commodities">
    * XAGUSD
    * XAUUSD
    * UKOIL
  </Accordion>

  <Accordion title="Futures">
    * NQ
    * ES
    * RTY
    * YM
    * GC
    * SI
    * CL
    * NG
    * PL
    * ZC
    * ZW
    * ZS
    * 6E
    * 6J
  </Accordion>

  <Info>
    US equities (Stocks and ETFs) use regular trading hours and are based on the Cboe BZX exchange.

    Futures are based on continuous contracts and use electronic trading hours, without adjustment for contracts changes. Contract roll is performed taking into account a combination of volume and openinterest.

    Unlike TradingView, Futures contracts roll on the day prior to expiration.
  </Info>
</Accordion>

<Accordion title="Is there a limit to how much I can use the AI?">
  Yes, users have a token balance that determines how much they can use the AI, however, it is highly unlikely you will ever reach the end of your token balance. It resets at the first of each month and once it reaches 0, the AI will no longer be available until the balance is refreshed.
</Accordion>

<Accordion title="Can I save my strategies?">
  Yes, strategy results provided by the AI can be exported as a CSV file for easy access and analysis.
</Accordion>

<Accordion title="How are strategies evaluated?">
  Backtests are performed on all the tickers on Monday, Wednesday, and Friday at 23:30 UTC, using our own internal system on data from quality third party providers, ensuring users can reproduce the results given by the AI on TradingView if needed.

  Strategies starts being evaluated at a specific date, which can be returned by the AI assistant.
</Accordion>

<Accordion title="Are backtesting results updated live?">
  No, currently backtests are performed on Monday, Wednesday, and Friday at 23:30 UTC for all strategies. As a result you might potentially see some more recent trades once you replicate a given strategy on TradingView.

  We are actively working on updating strategies continuously.
</Accordion>

<Accordion title="Why am I seeing different strategy results on TradingView?">
  Due to the potential difference in market data and processes it is possible for backtest results to slightly differ from what you might observe on TradingView.
</Accordion>

## Disclaimer [#disclaimer]

<Warning>
  This AI tool’s backtesting data & analytics are for informational purposes only and do not constitute financial advice or recommendations to buy or sell any financial product, including but not limited to securities, derivatives, cryptocurrencies, or other investment instruments.

  This tool may contain errors, and past performance is not indicative of future results. Always conduct your own research and consult with a professional financial advisor.

  LuxAlgo is not liable for any decisions made based on any information given. Read our [full disclaimer](https://www.luxalgo.com/legal/disclaimer/).
</Warning>


# Troubleshooting (/platform/ai-backtesting/troubleshooting)



The AI Backtesting Assistant is an early feature, and such might face several limitations described in this page.

<Note>
  Don't hesitate to send your feedback at [support@luxalgo.com](mailto:support@luxalgo.com) or in our [Discord server](https://discord.com/invite/LUX).
</Note>

## I can't use the AI Backtesting Assistant [#i-cant-use-the-ai-backtesting-assistant]

The AI Backtesting assistant is only available for our Ultimate users. Users without a plan can purchase one [here](https://www.luxalgo.com/pricing/).

Users subscribed to the Premium plan will need to upgrade to the Ultimate plan to use the AI Backtesting Assistant.

## The AI is not responding to my message [#the-ai-is-not-responding-to-my-message]

To ensure fair and consistent access to the AI Backtesting Assistant, users are subject to rate limits defined in terms of concurrent requests.

If the maximum number of concurrent requests is exceeded, messages sent to the AI Backtesting Assistant will take longer to get answered.

Waiting for some time will generally resolve the issue.

## Reached maximum monthly usage for the AI Backtesting Assistant [#reached-maximum-monthly-usage-for-the-ai-backtesting-assistant]

Because of the amount of compute necessary to run our LLM and to prevent potential abuse, users have a limited usage of the AI Backtesting Assistant.

The amount of usage is determined by the amount of content returned by the AI, as such large messages can consume more usage than more frequent small messages.

The monthly usage is reset every first of the month.

<Tip>
  In order to significantly save on usage you can refresh the page to clear the chat, as generating new messages on large conversations will drain usage more quickly.

  You can also use a cheaper model to reduce usage, the cost of a model is indicated by the amount of $ signs present in the model selection.
</Tip>

## I am seeing very different strategy results on Tradingview [#i-am-seeing-very-different-strategy-results-on-tradingview]

We compute strategies using data that might be slightly different on TradingView.

The amount of data you have access to can also affect its ability to reproduce strategies, as strategies are backtested using 20.000 bars.

## Specific models do not work [#specific-models-do-not-work]

Some models can be subject to geo-restriction and as such might not be usable by specific users.

We always include one model that is not subject to this restriction, such as Qwen models.


# Access on TradingView (/platform/algos/access-on-tradingview)



Once you've signed up to any plan on [our website](https://luxalgo.com/pricing) -- you will be able to see a place to "Connect Accounts" and enter your TradingView username to get instant access to use our exclusive tools compatible with TradingView.

In order to use our exclusive tools, you need to open a chart on TradingView.

![](/images/setup-tradingview/image603.png)

Once your chart is open, click on **Indicators*&#x2A; (or type &#x2A;*/** ) to open the indicator menu, then click on "**Invite-only Scripts**". You should see the names of each Toolkit, Screener, or Backtester appear in this folder and you can click on one  them to add to your chart.

Remember, you don't need to use all of these tools at once. It is suggested for users to explore through our tools to find a couple features that fit most optimally in your trading.

![](/images/setup-tradingview/image571.png)

<Note>
  If you don't see the invite-only scripts folder or the locks appear red, please first try refreshing your browser/app. If this still doesn't have them show up, please email [support@luxalgo.com](mailto:support@luxalgo.com) with your TradingView username or contact live-chat on our website and our team will grant you access right away.
</Note>

If you want to quickly access our tools you can add them to your favorites by clicking on the star at the right of their names in the folder. You will then be able to load the starred indicators directly from your favorites tab while on your charts.

![](/images/setup-tradingview/image560.png)

## Troubleshooting [#troubleshooting]

<AccordionGroup>
  <Accordion title="The plug-ins aren't showing on TradingView">
    Double-check the username you entered in the Connect Accounts portal, then refresh TradingView and look under **Indicators → Invite-only scripts**.

    If you created your TradingView account moments before connecting it, give it 5 to 10 minutes to be indexed by TradingView, then reconnect. Still stuck? Tell our live support team your TradingView username and we will fix it. If you're speaking with the AI agent, just ask for a human.
  </Accordion>

  <Accordion title="Can I switch access to a different TradingView account?">
    Yes, we allow username changes in most scenarios as long as it's not too frequent. Send your new username alongside your old username to [support@luxalgo.com](mailto:support@luxalgo.com), or ask our live support team on the website. If you're speaking to an AI agent, request a human for this.
  </Accordion>

  <Accordion title="Do I need a paid TradingView plan?">
    No. The plug-ins are fully functional on TradingView's free plan. [You can sign up for free here](https://www.tradingview.com/gopro/?share_your_love=LuxAlgo).
  </Accordion>
</AccordionGroup>


# How To Get Updates (/platform/algos/how-to-update)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/BHW0kxavvqA"
    frameBorder="0"
  />
</div>

## Applying An Update [#applying-an-update]

On Tradingview updates are easy to apply. Once an update is available, refresh your chart, then a purple 🔄 icon will be visible near the indicator title, click on it and click on "Update to latest version".

![](/images/how-to-update/update.png)

You can also apply an update by following these steps:

1. Refresh your browser or mobile app
2. Remove the indicator from your chart
3. Re-add it from your invite-only indicators

The latest version of the indicator should appear.

<Tip>
  Make sure to save your **Chart Layout** on Tradingview after successfully installing an update so you can ensure it will stay
</Tip>

![](/images/how-to-update/image172.png)

## Receiving Updates Notifications [#receiving-updates-notifications]

We always announce our updates on our community Discord server & [X Account](https://x.com/LuxAlgo). You can also receive update notifications on your TradingView charts directly by going to:

`settings -> notifications -> SCRIPTS YOU'VE ADDED TO FAVORITES OR LIKED`

![](/images/how-to-update/image125.png)

<Tip>
  Make sure to follow the LuxAlgo account on [TradingView](https://www.tradingview.com/u/LuxAlgo/#published-scripts) to ensure you are notified when we push updates with new features to our exclusive tools!
</Tip>


# TradingView Alerts (/platform/algos/tradingview-alerts)



In this page, we show you the specifics of how to set up any alert on TradingView in general. If you want to learn how to set up specific alerts on our exclusive tools specifically, you can see the following [video](https://youtu.be/olKQq5XR5S0?t=38).

On the TradingView platform, alerts can be created to trigger for any user set condition, such as crosses between two indicators, price entering or exiting a channel...etc. Users can be notified when the condition is trigerred via phone notifications, emails, SMS, and more through webhooks.

## How To Create An Alert [#how-to-create-an-alert]

To create an alert you can:

* Click on the alert button at the top of your chart.

![](/images/setup-alerts/image510.png)

* From the alert manager

![](/images/setup-alerts/image370.png)

* Using hotkeys `ALT + A` (Windows) or `⌥ + A` (Mac)

## Trigger Conditions [#trigger-conditions]

Users can select the conditions used to trigger an alert in the condition menu, selecting the indicators and a set of rules used to trigger an alert. The second drop down menu controls the specific alert trigger conditions, while the other drop down controls the elements/plots to be used for conditions.

<Note>
  Indicators return plots, which are visual elements that can be used to set the alert condition. Plots are used to visualize series of values, conditions, etc. Note that indicators can return multiple plots.
</Note>

Available conditions to set custom alerts are explained below:

### Crossing/Crossing Up/Crossing Down [#crossingcrossing-upcrossing-down]

![](/images/setup-alerts/crossing_conditions.png)

Crossing conditions are commonly used and allow users to be alerted when two elements cross each other.

Three crossing conditions are available, **Crossing**, **Crossing Up**, **Crossing Down**.

![](/images/setup-alerts/crossing.png)

**Crossing** will alert on any crosses between two selected plots/values.

![](/images/setup-alerts/crossingUp.png)

**Crossing Up** will alert only when the first selected element crosses over the second one.

![](/images/setup-alerts/crossingDown.png)

**Crossing Down** will alert only when the first selected element crosses over the second one.

### Entering/Exiting Channel [#enteringexiting-channel]

**Entering/Exiting Channels** conditions will trigger when any selected element enter or exit user selected upper/lower bounds.

![](/images/setup-alerts/enteringChannel.png)

The **Entering Channel** condition will trigger when the element in the first drop-down menu cross-under the selected upper bound or cross-over the selected lower bound.

![](/images/setup-alerts/exitingChannel.png)

The **Exiting Channel** condition will trigger when the element in the first drop-down menu cross-over the selected upper bound or cross-under the selected lower bound.

### Outside/Inside Channel [#outsideinside-channel]

![](/images/setup-alerts/outsideChannel.png)

**Outside/Inside Channels** conditions will trigger when any selected element is outside or within selected upper/lower bounds.

![](/images/setup-alerts/insideChannel.png)

### Greater/Less Than [#greaterless-than]

![](/images/setup-alerts/greaterThan.png)

The **Greater Than** and **Less Than** conditions will trigger when a selected element is greater or less than a selected element or value.

![](/images/setup-alerts/lessThan.png)

## How To Use Placeholders [#how-to-use-placeholders]

![](/images/setup-alerts/image257.png)

Placeholders allow for the value of a variable to be returned in an alert message. This variable can be the closing price of a symbol (`{{close}}`), the time frame of the chart the alert is created on (`{{interval}}`), and even the value of an indicator. This is done using the `{{plot("Name")}}` placeholder, where `"Name"` is the name of the plot you want the value to be returned in the alert message. Note that only the first 22 plots in an indicator can be used in placeholders.

<Note>
  Note that `plot("Name")` must be part of the indicator selected in the alert condition.
</Note>

## Warning Message [#warning-message]

![](/images/setup-alerts/image276.png)

When you create an alert using certain indicators such as the Signals & Overlays® indicator, you might see a warning message, this is caused by internal Pine Script® functions used in the script that may have a repainting behavior when set in a specific way.

The alerts provided by the LuxAlgo indicators **do not repaint** and as such, you can click on **continue anyway**.

## Managing Alerts [#managing-alerts]

The alerts manager menu allows you to manage the alerts you have currently set. From there you can create, remove, pause, reactivate, clone, search, sort and edit your alerts. To access the alert manager menu click on the alerts icon represented by an alarm symbol at the top left of the chart.

![](/images/setup-alerts/image370.png)

If you want to remove all your current alerts click on the three horizontal dots at the right of the "add alert" icon, then select "remove all".

![](/images/setup-alerts/image476.png)

For more information on how to manage alerts go [here](https://www.tradingview.com/support/solutions/43000595311-manage-alerts/).

### Updating Alerts [#updating-alerts]

It is important to note that if one of the indicator settings is **changed after the alert is created**, then the alert will be **triggered using the old settings**. In order to update an alert go to the alert manager and click on edit.

![](/images/setup-alerts/image.png)

In the first drop-down menu of the **Condition** section, select the second indicator with the same name as the one first used to create the alert you want to update and save the alert.

![](/images/setup-alerts/image515.png)


# What Is TradingView? (/platform/algos/what-is-tradingview)



![](/images/tradingview/logo-preview-2.png)

[TradingView ](https://www.tradingview.com)is a free charting platform and social network where traders/investors can **look** at financial charts and use a wide variety of tools to spot opportunity in every market across Stocks, Forex, Crypto, Futures & Commodities. TradingView users can connect most brokers to trade directly on the platform and **leap** into opportunities as they are presented in real time from their charts.

<Note>
  This is the platform where we host our exclusive tools. You can create a free account on TradingView and then sign up on [our website](https://luxalgo.com/pricing) to receive instant-access to use our tools.
</Note>

## How To Create A TradingView Account [#how-to-create-a-tradingview-account]

In order to create a TradingView account, you need to go to the TradingView [homepage](https://www.tradingview.com). Click on "Sign in" at the top right of the homepage, then click on "Sign up" at the bottom of the current window. You can sign up using your social media profiles or an email address.

![](/images/tradingview/image581.png)

![](/images/tradingview/image637.png)

## TradingView Charts [#tradingview-charts]

### How To Open A Chart On TradingView [#how-to-open-a-chart-on-tradingview]

![](/images/tradingview/image538.png)

You can open a chart on TradingView by clicking on 'Chart', located at the top left of the website when you are logged in.

### How To Change Symbol [#how-to-change-symbol]

![](/images/tradingview/image50.png)

You can change the symbol of the chart by clicking on the top left section containing the current chart ticker.

<Note>
  You can also directly type the name of the symbol on your keyboard.
</Note>

Once you click on it, you will be able to search for specific symbols. You can also filter out symbols by markets or exchange.

![](/images/tradingview/image468.png)

![](/images/tradingview/image348.png)

### Change Chart Timeframe [#change-chart-timeframe]

![](/images/tradingview/image99.png)

Timeframes determine the frequency at which a new candle is displayed on the chart. To change the timeframe of your chart simply click on the section at the right of the ticker name and select a timeframe of interest.

<Note>
  You can also type the timeframe directly on your keyboard to change it.
</Note>

### Add Indicators To Your Chart [#add-indicators-to-your-chart]

![](/images/tradingview/image441.png)

Click on the **Indicators** button at the top of the chart. This will open a menu where you can search for the indicator and click on it to add it to your chart.

![](/images/tradingview/image296.png)

### Save A Chart Layout [#save-a-chart-layout]

You can create, delete and save chart layouts on TradingView if you wish to save your indicator & chart settings on specific tickers/timeframes. Once you make a change on a layout, you will need to save these changes, all you need to do is to click on the cloud symbol at the top right of the chart.

![](/images/tradingview/image356.png)

![](/images/tradingview/image225.png)

<Note>
  You can also save a chart layout by pressing `ctrl + s` or `cmd + s` for mac users, on your keyboard.
</Note>


# Features (/platform/quant/features)



Quant provides a comprehensive set of features designed to help you create, refine, and deploy Pine Script® indicators and strategies. This page covers the core capabilities available in the Quant interface.

## Code Generation [#code-generation]

Quant can generate complete Pine Script® code based on your natural language descriptions. When Quant generates code, it appears in a split editor view, allowing you to review the generated script alongside the chat interface. You can copy the generated code using the copy button and paste it directly into TradingView® to add the indicator to your charts.

![](/images/quant/code-generation-editor.png)

<Note>
  The code cannot be copied while Quant is actively generating code. Wait for the generation to complete before using the copy button.
</Note>

Quant leverages its access to both TradingView scripts and LuxAlgo's proprietary script library to reliably handle niche and complex concepts. This dual knowledge base enables Quant to generate more relevant and code of higher quality.

## Instant Visualization [#instant-visualization]

![](/images/quant/chart.png)

Once generated, indicators can be displayed in a chart for direct visual feedback. Strategy scripts can also be run and backtested on the chart — see [Backtesting](#backtesting).

<Note>
  Displayed results may differ from what could be seen on TradingView®.

  Not all indicators are compatible with our chart and may only be displayed on TradingView® charts.
</Note>

Users can access data from the following assets:

* Stocks
* Cryptocurrencies
* Forex
* Commodities

on the follwoing timeframes: 1 minute, 5 minutes, 15 minutes, 30 minutes, 1 hour, 4 hours, 1 day.

<Note>
  Due to data limitations, only completed candles are returned. Users may experience a one candle delay across assets
</Note>

## Image to Code [#image-to-code]

You can upload images of indicators or chart setups, by pasting them directly in the prompt input, uploading them from the upload image button, or by clicking on "Generate From Image", and Quant will analyze the visual representation to generate Pine Script® code that reproduces the indicator's behavior as closely as possible.

![](/images/quant/image-to-code-interface.png)

Additionally, you can send images during your conversation to provide more context or refine Quant's understanding of your requirements. This helps Quant better interpret complex visual patterns and generate more accurate code.

<Tip>
  You can attach chart images directly to the chat input prompt from the camera icon in the chart header and clicking on "Attach to chat".

  ![](/images/quant/attach.png)
</Tip>

## Backtesting [#backtesting]

![](/images/quant/backtest1.png)

Once a script uses a `strategy(...)` declaration, running it on the chart computes trade history and performance statistics in real time—no need to copy the code to TradingView® first.

### Converting Indicators to Backtests [#converting-indicators-to-backtests]

Indicators are not backtested as-is. To evaluate an indicator's logic as a strategy, use the **Backtest** button in the toolbar, or ask Quant to convert the current indicator into a backtestable strategy. Quant rewrites the script with the appropriate `strategy(...)` declaration and entry/exit logic while preserving the original concept.

<Tip>
  The toolbar **Backtest** action is only shown when the editor holds indicator code (not already a strategy).
</Tip>

### Running a Backtest [#running-a-backtest]

Click **Run** after your script is a strategy. The chart displays the strategy alongside a **Backtest Summary** strip below it, showing:

* Net Profit
* Trades
* Win Rate (with winning and losing trade counts)
* Max Drawdown (absolute and percentage)
* Profit Factor
* Historical net profit chart (toggleable)

Use the maximize button on the summary strip to open the full backtest viewer.

### Backtest Viewer [#backtest-viewer]

![](/images/quant/backtest2.png)

The maximized backtest viewer provides detailed analytics across three tabs:

* **Performance** — historical net profit chart, summary metrics, net daily P\&L, weekday performance, and a performance table (all trades, longs, and shorts)
* **Trades Analysis** — P\&L distribution, win rate breakdown, duration vs. P\&L scatter plot, and a trades analysis table
* **Trades Log** — a searchable trade log with entry, exit, net P\&L, and cumulative P\&L; switch to calendar view to review daily results

<Tip>
  From the maximized viewer you can change the symbol and timeframe to re-run the backtest on different market data without leaving Quant.
</Tip>

### Script Settings [#script-settings]

Open the settings gear in the toolbar to tune how a strategy runs:

* **Inputs** — adjust the script's declared `input.*` parameters. Changes apply immediately and re-run the backtest, similar to TradingView®.
* **Properties** — configure backtest settings such as initial capital, default order size (quantity, base currency, or % of equity), pyramiding, commission, slippage, and margin for long and short positions.

Properties apply only to strategy scripts. Indicator scripts expose the Inputs tab when they declare inputs.

### Saving Strategies [#saving-strategies]

Save a backtest using the star icon in the summary strip or maximized viewer. Saved strategies appear in the **Saved Strategies** sidebar and include the script code, chart symbol, timeframe, input overrides, and backtest properties from the run.

From the sidebar you can open a saved Quant strategy in a new conversation (**See in Quant**) to reproduce the same tuned backtest, or inspect its backtest configuration.

<Note>
  Backtest results may differ from what you observe on TradingView® due to differences in market data, fill logic, or processing.

  Backtests use the same assets and timeframes available in [Instant Visualization](#instant-visualization): stocks, cryptocurrencies, forex, and commodities on 1 minute, 5 minutes, 15 minutes, 30 minutes, 1 hour, 4 hours, and 1 day timeframes.
</Note>

## Bug Fixing [#bug-fixing]

Quant can automatically identify and fix errors and warnings in your Pine Script® code. To fix all errors and warnings present in your script, use the "Fix with Quant" button in the editor.

![](/images/quant/bug-fixing-interface.png)

<Note>
  Errors prevent the script from running, while warnings won't prevent execution but may cause unexpected results.
</Note>

The editor also includes error and warnings highlighting for users more used to this.

<Tip>
  You can hover on the error or warning button to get more information about them.
</Tip>

### Runtime Errors [#runtime-errors]

![](/images/quant/runtime.png)

Quant can also help fix detected runtime errors. Unlike syntax errors runtime errors only appears once running the indicator over historical and/or newly formed candles.

If a runtime error is present, a message will appear at the bottom part of the chart.

## Pine Script® Help [#pine-script-help]

Quant has access to the complete Pine Script® knowledge base and can assist you with questions about Pine Script® syntax, functions, best practices, and implementation patterns. Whether you're learning Pine Script® or need clarification on specific concepts, Quant can provide accurate, context-aware answers based on official Pine Script® documentation.

## Assisted Coding [#assisted-coding]

For more experienced Pine Script® developers, Quant can assist with repetitive tasks and complex implementations. The integrated code editor is fully interactive and supports syntax/error highlighting as well as syntax completion, providing a professional coding environment.

<Note>
  The editor cannot be used while Quant is generating code. Wait for code generation to complete before making manual edits.
</Note>

You can work collaboratively with Quant by making manual edits to the code, and Quant will learn from your changes to assist with subsequent iterations and refinements.


# Introduction (/platform/quant/introduction)



![](/images/quant/introduction-hero.png)

Quant is an AI coding agent specialized in generating and validating Pine Script® indicators and strategies for TradingView®. This documentation will help you understand Quant's capabilities, learn how to effectively prompt it, and troubleshoot common issues.

Quant leverages deep knowledge of Pine Script®, trading indicators, strategies, styling, and validation, built from our extensive library of tools and frameworks developed over 5+ years. This specialization makes Quant superior to generic AI chatbots for trading tool generation.

<Note>
  All users can access Quant [here](https://www.luxalgo.com/quant). Users with Premium and Ultimate plans benefit from higher usage limits.
</Note>

![](/images/quant/benchmark-chart.png)

> *All 100 indicators varied in complexity. An implementation is considered failed if it contains errors (syntax or runtime), return NaN or constant unexpected values, or if the AI did not comply with the user request due to infeasability. Benchmark done on 02/21/2026.*

The chart above represents the percentage of correct implementations out of 100 total indicators amongst multiple chatbots.

## Documentation [#documentation]

<CardGroup cols="2">
  <Card title="Features" icon="sparkles" href="/platform/quant/features" />

  <Card title="Prompting Tips" icon="lightbulb" href="/platform/quant/prompting-tips" />
</CardGroup>

## FAQ [#faq]

<Accordion title="How do I get access to Quant?">
  All users can access Quant [here](https://www.luxalgo.com/quant). Users with Premium and Ultimate plans can benefit from higher usage limits.
</Accordion>

<Accordion title="What are credits?">
  Credits is the currency system used to control the usage amount a user has with our AI products. The credit balance of an user is shared across both AI Backtesting platform and Quant, our Pine Script® coding agent.

  User credits reset every first of the month.
</Accordion>

<Accordion title="How does Quant differ from ChatGPT or generic AI?">
  Quant is fully specialized toward trading-tool generation, making it superior to existing AI chatbots that always generate bug-filled, generic indicators & strategies.

  We built Quant to work with a wizard-level knowledge of Pine Script®, trading indicators, strategies, styling, and validation which was only possible from our own extensive library of tools + frameworks we've publicly released & privately hosted over 5+ years.
</Accordion>

<Accordion title="Can I convert an image of a chart into an indicator?">
  Yes, you can upload chart screenshots to have Quant reverse-engineer any indicator or marked up strategy into a working replica, often first try. Users can also keep iterating on it in the same chat to generate more features on top as they wish, instantly.
</Accordion>

<Accordion title="Can Quant generate complete TradingView indicators?">
  Yes. Quant can generate complete functional TradingView indicators from simple concepts to entire toolkits, which are validated and refined so they are ready to use on your charts.
</Accordion>

<Accordion title="Do I need to know how to code in Pine Script®?">
  No. You don't need to know Pine Script® to use Quant. Quant handles all the coding for you based on your requests.
</Accordion>

<Accordion title="How to prevent large credits consumption">
  Credits are consumed faster on very long conversations with Quant. We recommend starting new conversations whenever reaching a total of 50 messages.
</Accordion>

## Disclaimer [#disclaimer]

<Warning>
  TradingView® and Pine Script® are registered trademarks of TradingView, Inc. LuxAlgo is not affiliated with TradingView. Please read our [full disclaimer](https://www.luxalgo.com/legal/disclaimer/).
</Warning>


# Prompting Tips (/platform/quant/prompting-tips)



Effective prompting is key to getting the best results from Quant. Follow these tips to generate high-quality Pine Script® code efficiently.

## Start Simple [#start-simple]

Say what you want to build in plain language.

**Examples:**

* "A RSI oscillator with signals."
* "An order block detector with breakout signals."

This gets you moving fast and gives the agent something concrete to scaffold.

## Be Specific About the "Result" [#be-specific-about-the-result]

Tell Quant what "done" looks like:

* Expected indicator behavior under specific scenarios
* How the indicator should look like, with explicit mention of plot/label/line styles, colors, linewidth...etc
* Tell Quant if the indicator should appear on the chart, or in its own pane

<Tip>
  You can use images to provide a more direct answer about what the output should look like.
</Tip>

## Break Big Work Into Small Steps [#break-big-work-into-small-steps]

Asking Quant to build a complex indicator/strategy/toolkit in a single shot will generally lead to poor results.

Instead of "build the whole toolkit," do:

* "Start with element A."
* "Add element B"
* "Add element C"
* "Add alerts"

Incremental prompts reduce bugs, and provide checkpoints you can comeback to.

## Ask the Agent to Plan When It's Complex [#ask-the-agent-to-plan-when-its-complex]

If you're not sure about the indicator architecture:

> "Break this indicator into steps and propose a plan to achieve it"

Then implement step-by-step.

## Iterate with Targeted Follow-ups [#iterate-with-targeted-follow-ups]

After the first output, request one change per message:

* "Add moving average to x."
* "Add alert on condition x."
* "Add tooltip to these inputs..."

This keeps iterations clean and predictable.


# FAQ (/platform/getting-started/faq)



## Access [#access]

<AccordionGroup>
  <Accordion title="How do I get access?">
    Sign up on [luxalgo.com](https://luxalgo.com) and log in. That's it: the platform runs on the web, access is instant, and Quant, AI Backtesting, and AI Strategy Alerts are ready the moment you're in.
  </Accordion>

  <Accordion title="Which plan should I choose?">
    **Premium** gives you Quant with 5,000 monthly credits to build indicators and strategies from plain language, plus the full set of our world-renowned algos: Signals & Overlays®, Oscillator Matrix®, and Price Action Concepts® with Screeners.

    **Ultimate** is the most popular choice: 5× the Quant credits (25,000), plus the full AI Backtesting platform, our specialized Backtesters, and deep optimization for the biggest possible advantage.

    Compare both anytime on our [pricing page](https://luxalgo.com/pricing).
  </Accordion>

  <Accordion title="What are credits?">
    Credits are the fuel behind our AI. One shared balance powers Quant and AI Backtesting, and it resets on the first of every month.
  </Accordion>

  <Accordion title="Do I need to know how to code?">
    No. Describe your logic in plain language and Quant builds it, and you refine it right in the platform. If you do write Pine Script®, Quant is fluent in it, auto-debugs it, and goes far beyond it.
  </Accordion>
</AccordionGroup>

## Products [#products]

<AccordionGroup>
  <Accordion title="What markets are supported?">
    Our AI platform supports 90+ major assets across stocks, futures, crypto, forex, and commodities, on every timeframe, for building, backtesting, and deploying strategies with alerts. Our premium algos go further: they work on any market, anywhere you chart.
  </Accordion>

  <Accordion title="What platforms does it work with?">
    Build, test, and run strategies on LuxAlgo, then deploy live to any bot, brokerage, or prop-firm platform that accepts webhooks. You can also replicate strategies to TradingView in one click with code we generate, and the [Library](https://luxalgo.com/library) includes indicators for MetaTrader, NinjaTrader, and Thinkorswim.
  </Accordion>

  <Accordion title="Can I automate my trades?">
    Yes, automation is what the platform is built for. Generate a backtested strategy, turn on [AI Strategy Alerts](/platform/ai-strategy-alerts/introduction), and stream live signals via webhooks to whatever executes your trades.
  </Accordion>

  <Accordion title="Can I win every trade using this?">
    No, and be suspicious of anyone who claims otherwise. No tool or strategy predicts markets with guaranteed accuracy, and ours make no such claim. LuxAlgo gives you discipline and evidence: strategies built exactly as you define them, tested honestly against history, and executed without emotion. Trading is risky, and past performance does not guarantee future results.
  </Accordion>

  <Accordion title="Is LuxAlgo beginner friendly?">
    Yes. If you can describe your idea, you can build it. Every plan includes tutorials and a highly active community with 24/7 peer support and daily analysis. Start simple, backtest honestly, and let the data lead.
  </Accordion>

  <Accordion title="How is the paid platform different from the free Library?">
    The [Library](https://luxalgo.com/library) is the world's largest collection of free indicators: every major trading concept implemented as code. It's also the intelligence our AI builds on.

    The paid platform is everything on top: Quant generates custom indicators and strategies from plain language, AI Backtesting validates and optimizes them against real data, AI Strategy Alerts automates them live, and the premium algo suites give you our most advanced charting systems.
  </Accordion>

  <Accordion title="When is a signal confirmed?">
    A signal is confirmed when its candle closes. AI Strategy Alerts only fire on confirmed events, so what reaches you is never a signal that could still change.
  </Accordion>

  <Accordion title="Do the algos repaint?">
    No. Nothing in our premium algo suites repaints. Signals appear in real time and are permanent once the candle they were generated on closes.
  </Accordion>
</AccordionGroup>

## Billing [#billing]

<AccordionGroup>
  <Accordion title="How do I cancel?">
    You can cancel your subscription anytime by logging directly into your account on [our website](https://luxalgo.com/account/) and accessing the billing page where you will find your subscription and a way to easily cancel it on your own.

    Once your subscription has been canceled, you will have access for the rest of the time in the plan you purchased and you will not be charged again.
  </Accordion>

  <Accordion title="How do I reactivate my subscription?">
    You can re-activate a subscription that you previously cancelled by purchasing a plan again on our [pricing page](https://luxalgo.com/pricing).

    Once you select the plan you want to subscribe back with, make sure to login to the same account you used with your previous subscription.
  </Accordion>

  <Accordion title="Can I change my plan anytime?">
    Yes. Upgrade, downgrade, or extend anytime from the [pricing page](https://luxalgo.com/pricing) while logged in. Upgrades convert your remaining time into days on the new plan, downgrades start when your current plan ends, and yearly plans can be extended up to three times.
  </Accordion>

  <Accordion title="How do discounts work?">
    For special limited time offers we do you can receive discounts which are all automatically applied, displayed on the pricing cards, and for the first payment of your subscription only.

    You can always get up to 50% off the total price you'd pay monthly by choosing to pay annually on our plans.
  </Accordion>

  <Accordion title="Is there a free trial?">
    LuxAlgo is free to start. We also offer a 30 day money back guarantee on paid plans, so you can try the full platform risk free.
  </Accordion>

  <Accordion title="Will the price of my subscription ever change?">
    No, the price you signed up for is the price you will keep forever as long as you stay signed up. We will **never** change the price of your subscription. All members are grandfathered into the price they first signed up at regardless of any price increases we may do in the future.

    Please keep in mind that all coupons used on LuxAlgo.com are for the first interval of your subscription only. All future billing intervals aside from the first one will be at the full price of the subscription you chose.
  </Accordion>

  <Accordion title="How long does it take to get access after purchasing?">
    Access is instant. Log in on [luxalgo.com](https://luxalgo.com) right after sign-up and the full platform is ready to use.
  </Accordion>

  <Accordion title="What is your refund policy?">
    If you're within 30 days of your first initial purchase, you can request a refund by getting in touch with our live human support team from contacting [support@luxalgo.com](mailto:support@luxalgo.com) or by requesting to speak to a human if you've already reached out on our website so a team member can help you with this directly.
  </Accordion>
</AccordionGroup>


# Quickstart (/platform/getting-started/introduction)



![](/images/brand.png)

## Getting Started with LuxAlgo [#getting-started-with-luxalgo]

Welcome to the LuxAlgo documentation. LuxAlgo is the AI trading platform: describe a strategy in plain language and Quant builds, backtests, and runs it. Start with Quant and AI Backtesting.

<CardGroup cols="2">
  <Card title="Quant" icon="sparkles" href="/platform/quant/introduction">
    The AI built to understand markets.
  </Card>

  <Card title="AI Backtesting" icon="hexagon-nodes" href="/platform/ai-backtesting/introduction">
    Generate and validate strategies with AI.
  </Card>

  <Card title="AI Strategy Alerts" icon="alarm-clock" href="/platform/ai-strategy-alerts/introduction">
    Live signals, notifications, and webhooks.
  </Card>

  <Card title="TradingView Plug-ins" icon="toolbox" href="/platform/algos/access-on-tradingview">
    Toolkits and screeners for your charts.
  </Card>
</CardGroup>

## Frequently Asked Questions [#frequently-asked-questions]

Need quick answers about the platform, products, or billing?

<Card title="FAQ" icon="clipboard-question" href="/platform/getting-started/faq">
  Read through our frequently asked questions
</Card>

## Join our Community [#join-our-community]

Need further support or want to give us some feedback? You can [join our community](https://discord.com/invite/LUX) or drop us an email at [support@luxalgo.com](mailto:support@luxalgo.com).


# What Is LuxAlgo? (/platform/getting-started/what-is-luxalgo)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/_2xVnhQXJuw"
    frameBorder="0"
  />
</div>

LuxAlgo is the AI trading platform. Describe a strategy in plain language and [Quant](/platform/quant/introduction) turns it into a built, backtested, running strategy. No code required.

Behind it: the industry's leading library of standardized trading logic, years of foundational open-source development, and our world-renowned premium algos. Free to start.

![](/images/what-is-luxalgo/what-is-luxalgo.png)

## The Platform at a Glance [#the-platform-at-a-glance]

1. **[Quant](/platform/quant/introduction)**: the AI that powers LuxAlgo. It builds indicators and strategies from plain language (or even chart screenshots), refines them with you, and is fluent in Pine Script® when you want code.
2. **[The Library](https://luxalgo.com/library)**: every trading concept, already implemented. The world's largest collection of indicators and algorithms, maintained as strategy-ready code that the rest of the platform builds on.
3. **[AI Backtesting](/platform/ai-backtesting/introduction)**: the strategy engine. It discovers, validates, and optimizes strategies against years of real market history before you risk anything.
4. **[AI Strategy Alerts](/platform/ai-strategy-alerts/introduction)**: live signals from your strategies, delivered as notifications or webhooks so they reach your bot, brokerage, or prop-firm platform the instant they fire.
5. **[Premium Algo Suites](/platform/algos/access-on-tradingview)**: Signals & Overlays®, Oscillator Matrix®, and Price Action Concepts® with Screeners, available as plug-ins wherever you chart.

## The Library: Every Concept, Already Code [#the-library-every-concept-already-code]

Most platforms hand you a charting tool and wish you luck. LuxAlgo maintains the world's largest library of trading indicators and algorithms: order blocks, liquidity, market structure, trend-following, mean reversion, volume profiles, and practically every other technical concept traders use, each implemented as tested, strategy-ready code.

That matters for two reasons:

* **You never start from zero.** Any concept you want to trade already exists as working code you can chart, combine, and backtest immediately.
* **It's the intelligence behind our AI.** Quant and AI Backtesting don't guess at what an "order block" is. They build on the same battle-tested implementations used by millions of traders.

## From Idea to Validated Strategy [#from-idea-to-validated-strategy]

The platform closes the loop that traders usually stitch together by hand:

* **Generate:** Describe a strategy in plain language, or pick concepts from the Library, and get a working strategy instantly.
* **Validate:** AI Backtesting iterates through parameters against real historical data to find statistically significant configurations, instead of leaving you to trial-and-error settings.
* **Automate:** Deploy strategies with live alerts and webhooks, so signals reach you or your execution platform the moment they trigger.

## Why It Works [#why-it-works]

The quality of the platform comes from the quality of what feeds it. Our algorithms are used by over 1 million traders and are built by a team holding the rare Pine Wizard distinction. The strategies you generate stand on the most widely used, stress-tested technical implementations in the world, not generic code.


# Creating Alerts (/platform/ai-strategy-alerts/creating-alerts)



![](/images/ai-strategy-alerts/alert-button.png)

Users can easily create alerts from strategies by requesting them to our [AI Assistant](/platform/ai-backtesting/introduction).

![](/images/ai-strategy-alerts/create-alert.png)

Once a strategy is fetched the user can create an alert from it by clicking on the alarm icon. This will open up a menu allowing to customize the alert. Once done the user can create the alert by clicking on the button "Create".

![](/images/ai-strategy-alerts/alert-from-saved-strategy.png)

Alerts can also be created from saved strategies in the sidebar.

<Note>
  Users can have a maximum of 100 alerts running.
</Note>

## Accessing Created Alerts [#accessing-created-alerts]

![](/images/ai-strategy-alerts/alert-sidebar.png)

Created alerts can be accessed from the sidebar. From there the user can edit, pause, or delete them.

### Editing Created Alerts [#editing-created-alerts]

![](/images/ai-strategy-alerts/edit-alert.png)

Alerts can be edited at any time by clicking on the kebab menu in the sidebar. Once edited and saved, the alert will use the new user-set configuration for future alerts.


# Introduction (/platform/ai-strategy-alerts/introduction)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/_2xVnhQXJuw"
    frameBorder="0"
  />
</div>

<Note>
  Strategy alerts within our AI platform are only available to our Ultimate users. [Upgrade](https://www.luxalgo.com/pricing) your subscription to start creating strategies & alerts.
</Note>

Any AI trading strategy you create out of millions on the LuxAlgo platform can now generate advanced alerts. This feature sends alerts triggered by events such as going long, short, exits, taken by the strategies available from using our [AI Assistant](/platform/ai-backtesting/introduction).

LuxAlgo users may use our alerts system to receive notifications in the form of in app notifications within our app, emails, or make use of our integrated webhooks for more advanced usage through third party platforms such as automated trading.

## Features [#features]

<CardGroup cols="3">
  <Card title="Creating Alerts" icon="alarm-clock" href="/platform/ai-strategy-alerts/creating-alerts" />

  <Card title="Notifications" icon="bell" href="/platform/ai-strategy-alerts/notifications" />

  <Card title="Webhooks" icon="webhook" href="/platform/ai-strategy-alerts/webhooks" />
</CardGroup>

## FAQ [#faq]

<Accordion title="How to create alerts?">
  See [this guide](/platform/ai-strategy-alerts/creating-alerts).

  Only users with an **Ultimate** subscription may create alerts.
</Accordion>

<Accordion title="How many alerts can I set?">
  Users with an **Ultimate** subscription are limited to 100 alerts.
</Accordion>

<Accordion title="Can I send emails from triggered alerts?">
  Yes, users can enable emails to be sent when an alert triggers.
</Accordion>

<Accordion title="Can I create custom alerts?">
  Currently alerts are limited to the supported tickers and timeframes, as well as the available strategies in our database.
</Accordion>

<Accordion title="Can I use webhooks?">
  Yes, we allow creating up to 4 webhooks for a single alert.
</Accordion>

<Accordion title="Can I auto trade with alerts?">
  Yes, it is possible to use our webhooks to integrate with third party automation platforms to place orders.

  We do not guarantee the proper execution of orders nor the arrival of requests to set webhooks.
</Accordion>

<Accordion title="Can alerts be delayed?">
  Yes, it is possible for alerts to be delayed depending on the load on our systems and other external factors.
</Accordion>

<Accordion title="Are alerts based on futures delayed by 15 minutes?">
  No, alerts created from strategies on futures use a real-time data feed.
</Accordion>

<Accordion title="What happens if I cancel/downgrade/pause my Ultimate subscription?">
  Canceling or downgrading your Ultimate subscription will keep your existing alerts active. You will be able to create alerts only until the end of the subscription.

  Once your Ultimate subscription ends, all your alerts will be stopped.

  If you pause your subscription, all of your alerts will stop immediately.
</Accordion>

<Accordion title="Are my alerts deleted if I cancel my Ultimate subscription?">
  All alerts will be immediately stopped but will remain tied to your account in the state they were set as.

  If you resubscribe, you will regain access to previously created alert and may reactivate them.
</Accordion>

## Disclaimer [#disclaimer]

<Warning>
  Alerts and notifications provided by LuxAlgo are intended **for informational and display purposes only**. They are **not designed or guaranteed for trading execution, investment decisions, or financial advice**.

  LuxAlgo does not guarantee the **availability, accuracy, reliability, or timely delivery** of any alerts, notifications, or webhooks. **Downtime or delivery failures may occur** at any time without prior notice.

  By using LuxAlgo alerts, you acknowledge that LuxAlgo **is not liable for any losses, damages, or outcomes** resulting from the use, interpretation, or reliance upon such alerts, notifications, or webhooks.

  Read our [full disclaimer](https://www.luxalgo.com/legal/disclaimer/).
</Warning>


# Notifications (/platform/ai-strategy-alerts/notifications)



![](/images/ai-strategy-alerts/notifications-menu.png)

Users can be notified of triggered alerts via in-app notifications and/or emails.

The default view when creating or editing an alert displays the notification settings by default and can be accessed at any time by clicking on "Notifications" in the alert menu sidebar.

## Events [#events]

The event toggles determine which strategy events (long, short, exit long, exit short) trigger an in-app or email notification (if enabled).

## Alert message [#alert-message]

The alert message field determines the content that appears in the in-app notification or email.

### Placeholders [#placeholders]

Users might want to include dynamic data in their alert message, such as which strategy event triggered the alert or the strategy's entry price.

This is possible using placeholders. The syntax is `[[placeholder_name]]`. The following placeholders are available.

| Placeholder                      | Description                                                                                                                                                                    |            |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
| \[\[currency]]                   | Currency of the asset the strategy is based on                                                                                                                                 |            |
| \[\[current\_contract\_myy]]     | For strategies set on futures, current contract traded by the strategy in the format `[ROOT][MONTH_CODE][2 digit YEAR]`, for example `ESZ25`                                   | `string`   |
| \[\[current\_contract\_myyyy]]   | For strategies set on futures, current contract traded by the strategy in the format `[ROOT][MONTH_CODE][4 digit YEAR]`, for example `ESZ2025`                                 | `string`   |
| \[\[exchange]]                   | Exchange where the asset is traded                                                                                                                                             |            |
| \[\[strategy\_event]]            | Event originating from the strategy, options include "long", "short", "exit long", "exit short"                                                                                |            |
| \[\[strategy\_id]]               | A unique identifier associated to the strategy                                                                                                                                 |            |
| \[\[strategy\_market\_position]] | Current position of the strategy, options include "long", "short", "flat"                                                                                                      |            |
| \[\[strategy\_name]]             | Name of the strategy                                                                                                                                                           |            |
| \[\[strategy\_order\_action]]    | Action taken by the strategy, options include "buy" and "sell". For this placeholder an exit long would lead to the value being "sell" while an exit short would lead to "buy" |            |
| \[\[strategy\_order\_price]]     | Price at which the order was executed                                                                                                                                          |            |
| \[\[ticker]]                     | Ticker symbol the strategy is based on                                                                                                                                         |            |
| \[\[time]]                       | ISO 8601 datetime (including milliseconds) of the opening time of the candle for which the strategy event occurred. Example `2025-11-17T23:14:52.123Z`                         | `datetime` |
| \[\[timeframe]]                  | Timeframe the strategy is based on                                                                                                                                             | `number`   |
| \[\[timenow]]                    | ISO 8601 datetime (including milliseconds) of the alert being triggered. Example `2025-11-17T23:14:52.123Z`                                                                    | `datetime` |

Example of a strategy alert message using placeholders:

```
Strategy "[[strategy_name]]" triggered a new [[strategy_event]] signal on [[ticker]] ([[timeframe]]m) at price [[strategy_order_price]].
```

<Tip>
  Placeholders may be inserted in the message directly through the "Placeholders" dropdown; click a placeholder to insert it.
</Tip>

## In-App Notifications [#in-app-notifications]

![](/images/ai-strategy-alerts/toasts.png)

In-app notifications display small cards indicating an alert has been triggered. On desktop, they appear at the bottom-right of the screen; on mobile, at the bottom.

![](/images/ai-strategy-alerts/toasts-expand.png)

Hovering over a card shows messages from older alerts. Removing a card displays the next oldest alert (if any) at the top of the list.

<Tip>
  Users can edit alerts directly from the cards by clicking the gear icon in the bottom-right.
</Tip>

## Emails [#emails]

![](/images/ai-strategy-alerts/email.png)

When a strategy alert is triggered, the user can receive an email at the address associated with their account.

To receive emails, enable "Send email" in the Notifications settings at the bottom-right.


# Webhooks (/platform/ai-strategy-alerts/webhooks)



Webhooks allow strategy alerts to be sent to third-party applications such as Discord, Telegram, automation platforms, or anywhere for advanced usage such as automated trading.

## Creating a webhook [#creating-a-webhook]

![](/images/ai-strategy-alerts/create-webhook.png)

To create a webhook, simply look at the alert menu sidebar and click `+ New` under the **WEBHOOKS** section. This will open the settings for the new webhook, which will also appear in the alert menu sidebar.

<Note>
  Users can create up to 4 webhooks.
</Note>

## Webhook Settings [#webhook-settings]

***

### Webhook URL (Required) [#webhook-url-required]

The URL of the webhook that will be reached when an alert is triggered. Only valid URLs are allowed.

Once a valid URL is entered, the webhook toggle can be enabled.

### Events [#events]

The event toggles determine for which strategy events (long, short, exit long, exit short) a webhook is triggered.

### Payload (Optional) [#payload-optional]

The payload field should contain the data sent to the webhook. The expected format for the payload is JSON.

> Learn more about the JSON format [here](https://www.geeksforgeeks.org/javascript/json-introduction).

![](/images/ai-strategy-alerts/invalid-payload.png)

If the payload is invalid it will be indicated.

<Note>
  If no payload is provided an empty JSON `{}` will be sent.
</Note>

#### Placeholders [#placeholders]

Users might want to include in their alert message dynamic data, such as which strategy event triggered the alert or the entry price of the strategy.

This is possible by using placeholders. The placeholder syntax is as follows `[[placeholder_name]]`. The following placeholders are available.

| Placeholder                      | Description                                                                                                                                                                    | Type       |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
| \[\[currency]]                   | Currency of the asset the strategy is based on                                                                                                                                 | `string`   |
| \[\[current\_contract\_myy]]     | For strategies set on futures, current contract traded by the strategy in the format `[ROOT][MONTH_CODE][2 digit YEAR]`, for example `ESZ25`                                   | `string`   |
| \[\[current\_contract\_myyyy]]   | For strategies set on futures, current contract traded by the strategy in the format `[ROOT][MONTH_CODE][4 digit YEAR]`, for example `ESZ2025`                                 | `string`   |
| \[\[exchange]]                   | Exchange where the asset is traded                                                                                                                                             | `string`   |
| \[\[strategy\_event]]            | Event originating by the strategy, options include "long", "short", "exit long", "exit short"                                                                                  | `string`   |
| \[\[strategy\_id]]               | A unique identifier associated to the strategy                                                                                                                                 | `string`   |
| \[\[strategy\_market\_position]] | Current position of the strategy, options include "long", "short", "flat"                                                                                                      | `string`   |
| \[\[strategy\_name]]             | Name of the strategy                                                                                                                                                           | `string`   |
| \[\[strategy\_order\_action]]    | Action taken by the strategy, options include "buy" and "sell". For this placeholder an exit long would lead to the value being "sell" while an exit short would lead to "buy" | `string`   |
| \[\[strategy\_order\_price]]     | Price at which the order was executed                                                                                                                                          | `number`   |
| \[\[ticker]]                     | Ticker symbol the strategy is based on                                                                                                                                         | `string`   |
| \[\[time]]                       | ISO 8601 datetime (including milliseconds) of the opening time of the candle for which the strategy event occurred. Example `2025-11-17T23:14:52.123Z`                         | `datetime` |
| \[\[timeframe]]                  | Timeframe the strategy is based on                                                                                                                                             | `number`   |
| \[\[timenow]]                    | ISO 8601 datetime (including milliseconds) of the alert being triggered. Example `2025-11-17T23:14:52.123Z`                                                                    | `datetime` |

<Note>
  Even though the result of a placeholder can have a specific type, we only consider placeholders valid if they are strings (in between double quotation marks) in the payload.
</Note>

Example of valid payload:

```json
{
  "id": "my_unique_id",
  "time": "[[timenow]]",
  "event": "[[strategy_event]]",
  "year": 2025,
  "strategy conditions":
  {
    "long": "Confirmation Any Bullish",
    "short": "Confirmation Any Bearish"
  }
}
```

<Tip>
  Placeholders may be inserted in the message directly through the "Placeholders" dropdown and clicking in the specific placeholder to insert. These will be inserted as a string directly.
</Tip>

## Creating Multiple Webhooks [#creating-multiple-webhooks]

![](/images/ai-strategy-alerts/multiple-webhooks.png)

Users can create up to 4 webhooks, one for different events, different URLs, or payloads.

This is useful if the URL or expected payload for specific events are different, or if a specific alert should be delivered to multiple platforms at the same time.

### Duplicating Webhooks [#duplicating-webhooks]

![](/images/ai-strategy-alerts/webhook-hover.png)

To easily preserve fields such as webhook URL and payload, users can duplicate the webhook by hovering on its corresponding item in the alert menu sidebar, clicking on the duplicate button will duplicate the webhook.

<Warning>
  Duplicated webhooks come with the webhook URL toggle disabled.
</Warning>

## Templates [#templates]

<Warning>
  Alerts and notifications provided by LuxAlgo are intended **for informational and display purposes only**. They are **not designed or guaranteed for trading execution, investment decisions, or financial advice**.

  LuxAlgo does not guarantee the **availability, accuracy, reliability, or timely delivery** of any alerts, notifications, or webhooks. **Downtime or delivery failures may occur** at any time without prior notice.

  By using LuxAlgo alerts, you acknowledge that LuxAlgo **is not liable for any losses, damages, or outcomes** resulting from the use, interpretation, or reliance upon such alerts, notifications, or webhooks.

  Read our [full disclaimer](https://www.luxalgo.com/legal/disclaimer/).
</Warning>

### TradersPost [#traderspost]

Create a new strategy on TradersPost, once created navigate to it, click on "Webhook" and copy the provided URL.

In the LuxAlgo strategy alert creation menu create a new webhook, add the provided webhook URL, and use the following as payload:

```json
{
    "ticker": "[[ticker]]",
    "action": "[[strategy_order_action]]",
    "sentiment": "[[strategy_market_position]]",
    "quantity": "1",
    "price": "[[strategy_order_price]]",
    "time": "[[timenow]]",
    "interval": "[[timeframe]]"
}
```

<Note>
  For `quantity` use your preffered position sizing based on your money management rules.
</Note>

See the following docs for more information about [TradersPost](https://docs.traderspost.io/docs).

#### For Futures [#for-futures]

When setting a strategy alert for futures you plan on automating TradersPost recommends for the current contract traded to be provided.

The futures contract should be in the format `[ROOT][MONTH CODE][4 digit YEAR]`. This can be provided using this template:

```json
{
    "ticker": "[[current_contract_myyyy]]",
    "action": "[[strategy_order_action]]",
    "sentiment": "[[strategy_market_position]]",
    "quantity": "1",
    "price": "[[strategy_order_price]]",
    "time": "[[timenow]]",
    "interval": "[[timeframe]]"
}
```

### 3commas [#3commas]

#### Single Webhook [#single-webhook]

Create a new signal bot on 3commas, select TradingView strategy, and copy the provided URL.

In the LuxAlgo strategy alert creation menu create a new webhook, add the provided webhook URL, and use the following as payload:

```json
{  
  "secret": "your_secret_token",
  "max_lag": "300",
  "timestamp": "[[timenow]]",
  "trigger_price": "[[strategy_order_price]]",
  "tv_exchange": "[[exchange]]",
  "tv_instrument": "[[ticker]]",
  "action": "[[strategy_order_action]]",
  "bot_uuid": "your_bot_uuid",
  "strategy_info": {
    "market_position": "[[strategy_market_position]]",
    "market_position_size": "1"
  },  
  "order": {    
    "amount": "1",
    "currency_type": "base"  
  }
}
```

Use your provided "secret" and "bot\_uuid".

<Note>
  For `amount` in `order` use your preffered position sizing based on your money management rules.
</Note>

See the following [docs](https://help.3commas.io/en/articles/8778456-signal-bot-tradingview-strategy-type) to learn more about 3commas strategy bots.

#### Multiple Webhooks [#multiple-webhooks]

You can also use multiple webhooks with 3commas to open specific positions.

Create a new signal bot on 3commas, select custom signal, select "Reversal" as direction, copy the provided URL and use the following payloads depending on the position:

<Tabs>
  <Tab title="Long">
    Only keep "long" enabled in the event tab.

    ```json
    {  
        "secret": "your_secret_token",
        "max_lag": "300",
        "timestamp": "[[timenow]]",
        "trigger_price": "[[strategy_order_price]]",
        "tv_exchange": "[[exchange]]",
        "tv_instrument": "[[ticker]]",
        "action": "enter_long",
        "bot_uuid": "your_bot_uuid"
    }
    ```
  </Tab>

  <Tab title="Exit Long">
    Only keep "exit long" enabled in the event tab.

    ```json
    {  
        "secret": "your_secret_token",
        "max_lag": "300",
        "timestamp": "[[timenow]]",
        "trigger_price": "[[strategy_order_price]]",
        "tv_exchange": "[[exchange]]",
        "tv_instrument": "[[ticker]]",
        "action": "exit_long",
        "bot_uuid": "your_bot_uuid"
    }
    ```
  </Tab>

  <Tab title="Short">
    Only keep "short" enabled in the event tab.

    ```json
    {  
        "secret": "your_secret_token",
        "max_lag": "300",
        "timestamp": "[[timenow]]",
        "trigger_price": "[[strategy_order_price]]",
        "tv_exchange": "[[exchange]]",
        "tv_instrument": "[[ticker]]",
        "action": "enter_short",
        "bot_uuid": "your_bot_uuid"
    }
    ```
  </Tab>

  <Tab title="Exit Short">
    Only keep "exit short" enabled in the event tab.

    ```json
    {  
        "secret": "your_secret_token",
        "max_lag": "300",
        "timestamp": "[[timenow]]",
        "trigger_price": "[[strategy_order_price]]",
        "tv_exchange": "[[exchange]]",
        "tv_instrument": "[[ticker]]",
        "action": "exit_short",
        "bot_uuid": "your_bot_uuid"
    }
    ```
  </Tab>
</Tabs>

Use your provided "secret" and "bot\_uuid".

See the following [docs](https://help.3commas.io/en/articles/8529406-signal-bot-custom-signal-type) to learn more about 3commas signal bots.

### Cornix [#cornix]

Go to your dashboard, and create a new bot by selecting "TradingView Bot".

<Steps>
  <Step title="Account Type">
    Select wether this bot should be for live or a demo trading account.
  </Step>

  <Step title="Settings">
    Configure the bot name, account, symbol, position size and the direction.
  </Step>

  <Step title="Create Bot">
    Click on create bot and choose in the new window if the bot should be activated or not after creation. If not activated you will need to activate it later on.
  </Step>

  <Step title="Webhook URL">
    Once created copy the Webhook URL for this bot and paste it in the webhook URL field in the LuxAlgo alert creation menu.
  </Step>

  <Step title="Events">
    Before creating the alert only keep the relevant event enabled based on the direction selected before, if you selected "Long" only keep the long event selected.
  </Step>
</Steps>

Repeat the above steps for the Short direction.

### Discord [#discord]

You can receive messages from AI strategy alerts directly in your Discord server, this can be useful to have a personal log for alerts or just a simple place to get notified on actions taken by strategies.

Make sure you have a server and a channel with a webhoook setup, to learn more about creating webhooks for Discord see [this](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) guide.

Copy the webhook URL, paste it in the webhook URL field in the LuxAlgo alert creation menu, and use the following payload:

```json
{  
  "username": "[[strategy_name]]",
  "content": "New [[strategy_event]] signal on [[ticker]] ([[timeframe]]m)"
}
```

You maye customize the message look using the fields described [here](https://discord.com/developers/docs/resources/webhook#execute-webhook-jsonform-params).


# Alerts (/platform/ai-backtesting/backtesters/alerts)



## Backtester Alerts [#backtester-alerts]

|                Buy Alerts                |                Sell Alerts                |
| :--------------------------------------: | :---------------------------------------: |
| ![](/images/backtesters/alerts/long.png) | ![](/images/backtesters/alerts/short.png) |

Users can get alerted for any action executed by a strategy, from opening positions to closing them. The way alerts work is the same across all the Backtester scripts.

The message field in the **Alert Messages** setting section of each Backtester allows for the strategy to send a custom alert message depending on the action taken by the strategy, if no messages are set the strategy will send default messages.

In order for alerts to return custom user set messages the `{{strategy.order.alert_message}}` should be set on the message field of the Tradingview alert menu.

![](/images/backtesters/alerts/alert.png)

<Note>
  Backtester (PAC) does not have Exit Long/Short TP/SL alerts, instead only having Exit Long/Short
</Note>

## Alerts Placeholders [#alerts-placeholders]

Users can use placeholders in the messages set from the **Alert Messages** setting section to return market data, take profit/stop losses values, chart ticker and timeframe...etc.

|                       Placeholder                      |                                                   Description                                                  |
| :----------------------------------------------------: | :------------------------------------------------------------------------------------------------------------: |
|    <div style="{ textAlign: 'left' }">`{open}`</div>   |                         <div style="{ textAlign: 'left' }">Current opening price</div>                         |
|    <div style="{ textAlign: 'left' }">`{high}`</div>   |                           <div style="{ textAlign: 'left' }">Current high price</div>                          |
|    <div style="{ textAlign: 'left' }">`{low}`</div>    |                           <div style="{ textAlign: 'left' }">Current low price</div>                           |
|   <div style="{ textAlign: 'left' }">`{close}`</div>   |                         <div style="{ textAlign: 'left' }">Current closing price</div>                         |
|   <div style="{ textAlign: 'left' }">`{volume}`</div>  |                             <div style="{ textAlign: 'left' }">Current volume</div>                            |
|    <div style="{ textAlign: 'left' }">`{time}`</div>   |    <div style="{ textAlign: 'left' }">UNIX timestamp (in milliseconds) of the bar <b>opening time</b></div>    |
|  <div style="{ textAlign: 'left' }">`{timenow}`</div>  |    <div style="{ textAlign: 'left' }">UNIX timestamp (in milliseconds) of the <b>current bar time</b></div>    |
|   <div style="{ textAlign: 'left' }">`{ticker}`</div>  |   <div style="{ textAlign: 'left' }">Chart ticker (e.g: for BINANCE:BTCUSDT the ticker name is BTCUSDT)</div>  |
|   <div style="{ textAlign: 'left' }">`{prefix}`</div>  | <div style="{ textAlign: 'left' }">Ticker prefix (e.g: for BINANCE:BTCUSDT the ticker prefix is BINANCE)</div> |
| <div style="{ textAlign: 'left' }">`{timeframe}`</div> |                            <div style="{ textAlign: 'left' }">Chart timeframe</div>                            |
|     <div style="{ textAlign: 'left' }">`{tp}`</div>    |                           <div style="{ textAlign: 'left' }">Take profit value</div>                           |
|     <div style="{ textAlign: 'left' }">`{sl}`</div>    |                            <div style="{ textAlign: 'left' }">Stop loss value</div>                            |

<Warning>
  In order to function properly, `{timenow}` requires the strategy to recalculate on everytick.

  This can be enabled from the strategy settings -> Properties -> RECALCULATE

  If not enabled `{timenow}` will act like `{time}`.
</Warning>

Example:

<Note>
  New long entry generated at `{time}` and price `{close}` on `{ticker}{timeframe}` with take profit `{tp}` and stop loss `{sl}`
</Note>


# Forecasting (/platform/ai-backtesting/backtesters/forecasting)



![](/images/backtesters/forecasting/settings.png)

Being able to anticipate where price might evolve after a trade can be useful to plan potential exit points, set take profits/stop loss as well as knowing when user set ones might get hit...etc.

All Backtesters include a forecasting tool able to provide forecasts from the most recent trade executed by the backtester.

## Using Forecasts [#using-forecasts]

![](/images/backtesters/forecasting/forecasting.png)

Forecasts can be enabled from the "FORECAST" settings group by enabling the "Show Forecast" toggle.

Each forecast is influenced by the price evolution made during previous respective long and short trades, as such forecasts for long positions are different from the ones of short positions.

Users can select the maximum length of forecast using the "Forecasting Length" setting.

<Note>
  Because of the forecasting algorithm used, forecasts can have a lower length than the one selected by the user depending on the average bars in trade.

  Very frequent trades will generally return short price forecasts due to the lack of available data.
</Note>

### Forecasting Area [#forecasting-area]

![](/images/backtesters/forecasting/area.png)

By default, forecasts are displayed alongside an area, indicating where future prices might evolve. The area extremities can help users potentially determines exit points with there interaction with the price, but can also help reference the performance of a current trade relative to past trades.

Areas extremities are representative of past trades performances. For example:

* For long positions: A lower extremity not significantly going below the entry price can indicate good past performance for long positions.
* For short positions: An upper extremity not significantly going above the entry price can indicate good past performance for short positions.

<Note>
  If an area is no longer visible in a point in time of the forecast it means that there is not enough data for that point in time.
</Note>

### Forecasting Memory [#forecasting-memory]

Users can control the influence older trades have on the forecast using the "Maximum Forecast Memory" setting, with lower values using a shorter term memory, discarding older information more quickly.

Low values of this setting allow obtaining more diverse forecasts for new trades, while higher values will return forecasts less subject to change over time.

### Take Profits Levels From Forecast [#take-profits-levels-from-forecast]

When forecasts are displayed, users can set take profits using the **nth** percentile of a returned forecast. This can be done from the "TPS & SLS" settings, by selecting "Forecast" in the "Long TP" and/or "Short TP".

The numerical setting selected at the right of the drop-down menus determines the percentile used, and should be set within the range \[0, 100] in order to work. Percentiles values can affect the returned levels as follows:

* In case of a long trade, a lower set percentile will return take profits closer to the entry price, potentially triggering sooner.
* In case of a short trade, a lower set percentile will return take profits further away from the entry price, potentially triggering later.

<Warning>
  Do note that because forecasts are based on the strategy entries, take profits levels based on it can be subject to repainting, except when using a backtesting window based on a date.
</Warning>


# Actions & Methods (/platform/algos/alert-scripting/actions-methods)



Actions are elements tied to a user set condition producing a specific effect when the assigned condition is true. These allow users to have more control over their scripted alert, from invalidating sequences, to plotting graphical elements on the chart.

<Info>
  Actions have the structure `@keyword()`, starting with `@`, with an identifier, and `()`.

  Some actions can have a condition assigned to them using `=`. When the assigned condition is true the action will be executed.
</Info>

Like functions in classical programing languages, actions can have arguments located within `()`, adding further degrees of control over the action.

On the other hand, **methods** work similarily but are tied to a UDP, changing its properties.

This page lists out the available actions and methods syntax as well as their utility.

***

## @alert [#alert]

The `@alert` action allows users to get alerted when the associated condition is true.

### Syntax [#syntax]

```js
@alert("message")
```

### Arguments [#arguments]

**message** (string): Optional. Alert message used by the alert. Data placeholders can be used, these will be replaced by their corresponding value when the alert triggers. If not provided will use the associated condition.

### Examples [#examples]

```js
// Send an alert when close > open
@alert("close is greater than open") = {close} > {open}
```

```js
// Send an alert when close > open, with alert message returning the closing price value
@alert("price is {close}") = {close} > {open}
```

### Remarks [#remarks]

<Note>
  In order for the alert to trigger, users must create an alert using as condition "any alert() function call".
</Note>

***

## @label [#label]

The `@label` action allows users to draw labels on the toolkit pane when the associated condition is true.

### Syntax [#syntax-1]

```js
@label(y, text, text_color, color, style, size)
```

### Arguments [#arguments-1]

**y** (placeholder or value): Optional. Location of the label. Accepts data placeholders as values. Default is `{close}` for toolkits appearing on the chart, and 50 for the &#x2A;*Oscillator Matrix®** toolkit.

**text** (string): Optional. text displayed on the label. Default text is the condition.

<Info>
  Placeholders can be used within the text, these will be replaced with the corresponding value. For example:

  ```
  @label(text="closing price: {close}")
  ```
</Info>

**text\_color** (color): color of the text displayed on the label. Possible values include:

```
"black", "silver", "gray", "white", "maroon", "purple", "fuchsia", "green", "red", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua", "orange", "transparent", "chart.bg_color", "chart.fg_color"
```

Default is the chart background color `"chart.bg_color"`.

**color** (color): Optional. color of the label. Possible values include:

```
"black", "silver", "gray", "white", "maroon", "purple", "fuchsia", "green", "red", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua", "orange", "transparent", "chart.bg_color", "chart.fg_color"
```

Default is the color to the chart background `"chart.fg_color"`.

**style** (string): Optional. Shape of the displayed label, possible values include:

* `"none"`
* `"xcross"`
* `"cross"`
* `"triangleup"`
* `"triangledown"`
* `"flag"`
* `"circle"`
* `"arrowup"`
* `"arrowdown"`
* `"label_up"`
* `"label_down"`
* `"label_left"`
* `"label_right"`
* `"label_lower_left"`
* `"label_lower_right"`
* `"label_upper_left"`
* `"label_upper_right"`
* `"label_center"`
* `"square"`
* `"diamond"`

Default is `"label_down"`

**size** (string): Optional. Size of the displayed label. Possible values include:

* `"auto"` (automatically size the label based on the screen resolution)
* `"tiny"`
* `"small"`
* `"normal"`
* `"large"`
* `"huge"`

### Examples [#examples-1]

```js
// Draw a label when close > open
@label(y={close}, text="Up", text_color="white", color="green", style="label_up", size="small") = {close} > {open}
```

***

## @line [#line]

The `@line` action allows users to draw lines on the toolkit pane when the associated condition is true.

### Syntax [#syntax-2]

```js
@line(x1, y1, x2, y2, color)
```

### Arguments [#arguments-2]

**x1** (integer): Optional. Data placeholder of the x1 coordinate of the line or a constant integer determining how far ahead/behind from the current bar the x1 coordinate is located. Accepts negative values. Default is `0`.

**y1** (placeholder): Optional. y1 coordinate value of the line. Accepts data placeholders as values. Default is `"{close}"` except for the &#x2A;*Oscillator Matrix®** which uses 50.

**x2** (integer): Optional. Data placeholder of the x2 coordinate of the line or a constant integer determining how far ahead/behind from the current bar the x2 coordinate is located. Default is `0`.

**y2** (placeholder): Optional. y2 coordinate value of the line. Accepts data placeholders as values. Default is `"{close}"` except for the &#x2A;*Oscillator Matrix®** which uses 50.

**color** (color): Optional. color of the label. Possible values include:

```
"black", "silver", "gray", "white", "maroon", "purple", "fuchsia", "green", "red", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua", "orange", "chart.bg_color", "chart.fg_color"
```

Default is `"blue"`.

### Examples [#examples-2]

```js
// Draw line if closing price cross over closing price 10 bars ago
@line(x1= -10, y1= {low}, x2= 10, y2= {high}, color= "green") = {close} crossover {close[10]}
```

```js
// Draw line if closing price cross over closing price 10 bars ago
@line(x1={barindex[10]}, y1={low}, x2={barindex}, y2={high}, color="green") = {close} crossover {close[10]}
```

***

## @valuewhen [#valuewhen]

Plot a user set value on the chart when the assigned condition is true.

### Syntax [#syntax-3]

```js
@valuewhen(source, reference)
```

### Arguments [#arguments-3]

**source** (placeholder): Optional. Value to plot when the assigned condition is true. Default is `{close}` except for the &#x2A;*Oscillator Matrix®** which uses 50.

**reference** (string): Optional. Placeholder string used to reference the value in the script.

### Examples [#examples-3]

```js
// Plot {high} when close crossover open
@valuewhen({high}) = {close} crossover {open}
```

```js
// Record the value of {high} when close crossover open 
// Reuse the value as y coordinate for label
@valuewhen({high}, "{cross_high}") = {close} crossover {open}

@label({cross_high}) = {close} > {open}
```

### Remarks [#remarks-1]

The plot for `@valuewhen` is accessible as input for other scripts with name "@Valuewhen", also allowing users to create alerts using it.

When multiple `@valuewhen` actions are defined the last one in the script will be ploted.

<Warning>
  When specifying a reference placeholder in `@valuewhen`, the value will not be ploted.
</Warning>

## Methods [#methods]

Methods are similar to actions however they directly affect specific user set UDP's, changing their properties. We say that methods are **attached** to UDP's.

<Info>
  Methods have the structure `{udp}.keyword()`, with an UDP followed by `.` then an identifier `keyword`, and `()`.

  Some methods can have a condition assigned to them using `=`. When the assigned condition is true the method will be executed.
</Info>

### `invalidate` [#invalidate]

Invalidate the UDP when the trigger condition is true.

#### Syntax [#syntax-4]

```js
{udp}.invalidate(step)
```

#### Arguments [#arguments-4]

**step** (integer, integer list): Optional. Step to consider for invalidation, supports multiple values. If no steps are provided all steps will be considered for invalidation.

#### Examples [#examples-4]

```js
// Invalidate sequence if opening price is lower than previous low
{my_sequence}.invalidate() = {open} < {low[1]}

// Define sequence:
// step 1: closing price is above opening price
// step 2: closing price is above previous high

{my_sequence} = {close} > {open} then {close} > {high[1]}
```

Example evaluating our invalidation condition on a specific step:

```js
// Invalidate sequence if closing price is lower than opening price
// when we are evaluating the second step (step=1)
{my_sequence}.invalidate(step=1) = {close} < {open}

// Define sequence:
// step 1: closing price is above opening price and volume is above 500
// step 2: closing price is above opening price and volume is above 1000
// step 3: closing price is under opening price

{my_sequence} = {close} > {open} and {volume} > 500 then {close} > {open} and {volume} > 1000 then {close} < {open}
```

Example evaluating our invalidation condition on multiple step:

```js
// Invalidate sequence if closing price is lower than opening price
// when we are evaluating the second, third, and fourth step (step=1,2,3)
{my_sequence}.invalidate(step=1,2,3) = {close} < {open}

// Define sequence:
// step 1: closing price is above opening price and volume is above 500
// step 2: closing price is above opening price and volume is above 1000
// step 3: closing price is above opening price and volume is above 1500
// step 4: closing price is above opening price and volume is above 2000
// step 5: closing price is under opening price

{my_sequence} = {close} > {open} and {volume} > 500 then {close} > {open} and {volume} > 1000 then {close} > {open} and {volume} > 1500 then {close} > {open} and {volume} > 2000 then {close} < {open}
```

#### Remarks [#remarks-2]

The `invalidate` method can work similarily to inline invalidation using the operator `!`.

Consider that the order at which the `invalidate` method is used matters, for example if the method is defined after the UDP it affects then the sequence can complete before invalidation takes place. On the other if you want invalidation to take place first the `invalidate` method should be defined before the affected UDP.

***

### `set_step` [#set_step]

Set the UDP sequence step to a user defined value, or increase/decrease the current set by a user set increment/decrement.

#### Syntax [#syntax-5]

```js
{udp}.set_step(value)
```

#### Arguments [#arguments-5]

**value** (integer): New value of the step. Users can also increase the current step value using `+` as prefix, e.g: `+1`, or decrease it using `-` as prefix, e.g: `-1`.

#### Example [#example]

```js
// Define UDPs
{bull} = {close} > {open}
{bear} = {close} < {open}
{bull+} = {close} > {high[1]} and {bull}
{bear+} = {close} < {low[1]} and {bear}

// Define sequence
{my_sequence} = {bull} then {bear} then {bull+} then {bear+}

// Reset sequence to corresponding step
{my_sequence}.set_value(0) = {bull} // Reset to step 1
{my_sequence}.set_value(1) = {bear} // Reset to step 2
{my_sequence}.set_value(2) = {bull+} // Reset to step 3
```

#### Remarks [#remarks-3]

Users cannot set a negative value for a step (e.g: `{udp}.set_step(-1)`), which would instead set the step to 0.

Consider that the order at which the `set_step` method is used matters, if you want to set a step value before the sequence evaluation the `set_step` method should be defined before the affected UDP.


# Alert Scripting (/platform/algos/alert-scripting/introduction)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/syWuQIl6U4A"
    frameBorder="0"
  />
</div>

Alert Scripting is a feature within each of our 3 exclusive toolkits that allows users to create extremely advanced alert conditions using our own scripting language. Alert Scripting is available near the bottom of the settings of each LuxAlgo toolkit, and allows using most of the features from the respective toolkits for creating alert combinations.

This feature is useful for users that feel limited by the custom alert creator.

To learn more about Alert Scripting, see the following pages:

<CardGroup cols="2">
  <Card title="Placeholders" icon="brackets-curly" href="./placeholders">
    Learn how to reference market data and toolkit features when scripting alerts.
  </Card>

  <Card title="Operators" icon="code" href="./operators">
    Learn about the operators you can use to compare data with each other.
  </Card>

  <Card title="Actions & Methods" icon="at" href="./actions-methods">
    Learn how to enhance scripted alerts trough actions and methods.
  </Card>

  <Card title="Usage" icon="display-code" href="./usage">
    Learn how to create simple and complex alerts.
  </Card>
</CardGroup>

## Migrations [#migrations]

In order to migrate from older versions to more recent versions see the following page:

<Card title="Migrations" icon="rotate-right" href="./migrations">
  Migrate your script to newer version of our scripting language.
</Card>


# Scripting Migrations (/platform/algos/alert-scripting/migrations)



<MigrationUtility />

## v1 to v2 [#v1-to-v2]

The version 2 of our scripting language comes with the following changes:

* Multiple sequences can now be defined.

* Sequences can now be used with strategy scripting.

* Sequences are no longer constructed from one condition per line, instead the new `then` operator should be used.

* New `{any_inactive_step}` placeholder returning true if any step but the active one is true, can only be used for invalidation

* Actions `@invalidate` and `@set_step` have been replaced with their corresponding [methods](./actions-methods).

* Actions no longer require to have the argument name specified:

before: `@label(y = "{close}", text = "hello")`

now: `@label({close}, "hello")`

<Warning>
  Do note that the order of arguments needs to be respected. It is still good practice to specify action arguments names to avoid confusion.
</Warning>

* Actions argument values no longer requires to be in between quotes/double quotes.

<Tip>
  It is still good practice to use quotes for strings.
</Tip>

* y argument for `@label` and `@line` actions now support historical referencing.

* Action `@valuewhen` now accepts a new `reference` argument allowing to reuse the captured value in other conditions/actions.

* Removed `@filter` action.

### Migrating from v1 to v2 [#migrating-from-v1-to-v2]

#### Defining Sequences [#defining-sequences]

Scripted alerts using a sequence previously separated steps by linebreaks:

```
{close} > {open} // Step 1

{close} crossover {close[9]} // Step 2

{volume} crossover 10000 // Step 3
```

Now sequences can be defined inline using the new `then` operator as follows:

```
{close} > {open} then {close} crossover {close[9]} then {volume} crossover 10000
```

#### Invalidating Sequences [#invalidating-sequences]

Previously sequences were invalidated using the `@invalidate` action:

```
{close} > {open} // Step 1

{close} crossover {close[9]} // Step 2

{volume} crossover 10000 // Step 3

@invalidate() = {close} < {open} // invalidate if closing price is lower than open
```

This can now be done inline using the new operator `!`:

```
{close} > {open} then {close} crossover {close[9]} then {volume} crossover 10000 ! {close} < {open} 
```

Or using the new method `invalidate` as long as an udp is defined for the sequence:

```
{my_sequence}.invalidate() = {close} < {open} // Invalidation of {my_sequence}

{my_sequence} = {close} > {open} then {close} crossover {close[9]} then {volume} crossover 10000
```

<Info>
  In V1 invalidation always took place first, as such we want `{my_sequence}.invalidate()` to appear first in the script.
</Info>

If your invalidation setup only affected specific steps such that you had:

```
@invalidate(step= 1,2) = {close} < {open} // invalidate if closing price is lower than open and active steps are the second one or third one
```

Then you can reuse a similar setup:

```
{my_sequence}.invalidate(step= 1,2) = {close} < {open} // Invalidation of {my_sequence}
```

#### Changing Step [#changing-step]

Previously users could change the active step of a sequence using the `@set_step` action:

```
{close} > {open} // Step 1

{close} crossover {close[9]} // Step 2

{volume} crossover 10000 // Step 3

@set_step(0) = {close} < {open} // Set step to 0 if closing price is lower than open
```

Now this can be done with the `set_step` action:

```
{my_sequence}.set_step(0) = {close} < {open} // Set step to 0 if closing price is lower than open

{my_sequence} = {close} > {open} then {close} crossover {close[9]} then {volume} crossover 10000
```

<Info>
  In V1 setting a sequence step always took place first, as such we want `{my_sequence}.set_step(0)` to appear first in the script.
</Info>

The value argument still accept increments and decrements.

#### Accessing a sequence active step [#accessing-a-sequence-active-step]

Previously users could access the active step of a sequence (the step subject to evaluation) using the `{step}` placeholder.

Due to the possibility with version 2 of hacing multipe sequences in a single script, the active step can be obtained from a defined UDP for the sequence:

```
{my_sequence} = {close} > {open} then {close} crossover {close[9]} then {volume} crossover 10000

// Get the active step for {my_sequence}
{my_sequence.step}.
```

#### Accessing a sequence elapsed bars since new step [#accessing-a-sequence-elapsed-bars-since-new-step]

Previously users could access the numbers of bars elapsed since a last step was true or invalidation of the sequence using the `{barssince_step}` placeholder.

Due to the possibility with version 2 of hacing multipe sequences in a single script, the active step can be obtained from a defined UDP for the sequence:

```
{my_sequence} = {close} > {open} then {close} crossover {close[9]} then {volume} crossover 10000

// Get the number of bars ellapsed since a last step was true or invalidation for {my_sequence}
{my_sequence.barssince_step}
```

#### Filtering each steps with @filter [#filtering-each-steps-with-filter]

Previously users could apply a condition to all steps of a sequence with a single usage of the `@filter` action as follows:

```
{close} > {open} // Step 1

{close} crossover {close[9]} // Step 2

{volume} crossover 10000 // Step 3

@filter() = {volume} > 5000 // Test for all steps if volume is above 5000
```

This can be done by having a UDP associated to this filter condition and using the and operators with it:

```
{vol_filter} = {volume} > 5000 
{my_sequence} = {close} > {open} and {vol_filter} then {close} crossover {close[9]} and {vol_filter} then {volume} crossover 10000 and {vol_filter}
```


# Operators (/platform/algos/alert-scripting/operators)



Operators are specific tokens (characters) performing an operation over multiple defined elements. Alert scripting supports three types of operators described below.

### Comparison Operators [#comparison-operators]

Comparison operators are used to compare two numerical values with each other.

#### `>` (Greater Than) [#-greater-than]

***

`True` if value to the left of `>` is greater than the value to its right.

```js Example
{close} > {open}
```

#### `<` (Less Than) [#-less-than]

***

`True` if value to the left of `<` is less than the value to its right.

```js Example
{close} < {open}
```

#### `>=` (Greater Than or Equal) [#-greater-than-or-equal]

***

`True` if value to the left of `>=` is greater or equal to the value to its right.

```js Example
{close} >= {open}
```

#### `<=` (Less Than or Equal) [#-less-than-or-equal]

***

`True` if value to the left of `<=` is greater or equal to the value to its right.

```js Example
{close} <= {open}
```

#### `==` (Equal) [#-equal]

***

`True` if value to the left of `==` is equal to the value to its right.

```js Example
{close} == {open}
```

#### `!=` (Not Equal) [#-not-equal]

***

`True` if value to the left of `!=` is not equal to the value to its right.

```js Example
{close} != {open}
```

#### `crossover` [#crossover]

***

`True` if series to the left of `crossover` is crossing over the series to its right. Also expressed as:

* `A crossover B = A > B and A[1] < B[1]`

```js Example
{close} crossover {open}
```

<Info>
  `crossover` is not a traditional operator but serves a comparative purpose for alert scripting.
</Info>

#### `crossunder` [#crossunder]

***

`True` if the series to the left of `crossunder` is crossing under the series to its right. Also expressed as:

* `A crossunder B = A < B and A[1] > B[1]`

```js Example
{close} crossunder {open}
```

<Info>
  `crossunder` is not a traditional operator but serves a comparative purpose for alert scripting.
</Info>

#### `cross` [#cross]

***

`True` if the series to the left of `cross` is crossing (either over or under) the series to its right. Also expressed as:

* `A cross B = A crossover B or A crossunder B`
* `A cross B = (A - B) * (A[1] - B[1]) < 0`

```js Example
{close} cross {open}
```

<Info>
  `cross` is not a traditional operator but serves a comparative purpose for alert scripting.
</Info>

### Logical Operators [#logical-operators]

Logical operators link two expressions together forming a more complex condition being either `True` or `False`.

#### `and` [#and]

***

The `and` operator return true if both expressions to its side are `True`.

```js Example
{close} > {open} and {volume} > 1000
```

The above return `True` if the closing price is greater than the opening price while the volume is greater than 1000.

#### `or` [#or]

***

The `or` operator return true if either one of the expressions to its side are `True`.

```js Example
{close} > {open} or {close} > {hl2}
```

The above return `True` if the closing price is greater than the opening price or if the closing price is greater than the median price `hl2`.

It is important to note that `or` takes precedence over `and`, for example:

```js
{close} > {open} and {volume} > 1000 or {close} > {hl2} and {volume} > 1000
```

In the above condition `{close} > {open} and {volume} > 1000` and `{close} > {hl2} and {volume} > 1000` would be evaluated first.

#### `not` [#not]

The `not` operator test if a following condition is true or false, returning `True` if the condition is `False`.

```js
not {close} > {open}
```

The above return `True` if the closing price is not greater than the opening price.

### History Referencing Operator `[]` [#history-referencing-operator-]

Users can refer to previous values of a series using the historical referencing operator `[]`. This operator is placed to the right of a placeholder within the curly brackets:

```python Syntax
{A[n]}
```

Where `n` is a numerical value determining the number of bars back to go fetching `{A}`.

For example, if we want to get the previous value of the closing price we can use `{close[1]}`.

```python Example
{close} > {close[1]}
```

The above example return `True` if the current closing price is greater than value of the closing price one bar back.

### Sequence Operators [#sequence-operators]

Sequence related operators allow defining and controling sequences.

<Info>
  A sequence is a structure made of steps. When one step is triggered we switch to the next step on the next bar.

  If there are no more steps to evaluate we say that the sequence is complete and start from the first step again.
</Info>

#### `then` [#then]

***

Operator defining a sequence. Each condition in between `then` is a step, with the first step being the leftmost condition before `then`.

```python Syntax
{a} then {b} then {c}
```

Where `{a}`, `{b}`, `{c}` are the step conditions.

```js Example
{close} > {open} then {close} < {open} then {close} > {open} and {volume} > 1000
```

In the above example we have a sequence with 3 steps:

* Step 1: `{close} > {open}&#x60; &#x2A;(is the closing price greater than the open price)*
* Step 2: `{close} < {open}&#x60; &#x2A;(is the closing price lower than the open price)*
* Step 3: `{close} > {open} and {volume} > 1000&#x60; &#x2A;(is the closing price greater than the open price and volume above 1000)*

We first evaluate step 1, if it is true we will start evaluating step 2 starting at the next bar.

Once step 2 is true, we will finally start evaluating step 3 sarting at the next bar.

Step 3 is the last step of our sequence, once it is true the sequence will be complete and will restart evaluating from step 1 at the next bar.

#### `!` (Inline Invalidation) [#-inline-invalidation]

***

Operator invalidating a sequence if the assocated condition is true. Inline invalidation always takes place **before** the evaluation of a sequence step.

<Info>
  Invalidation reffer to restarting a sequence prematurely before it is complete.
</Info>

```python Syntax
{a} then {b} then {c} ! {d}
```

Where `{a}`, `{b}`, `{c}` are the step conditions and `{d}` is the invalidation condition.

```js Example
{close} > {open} then {close} > {open} then {close} > {open} ! {close} < {open}
```

In the above example we have a 3 step sequence using the same conditions (closing price greater than the opening price), and an inline invalidation condition defined after the invalidation operator `!`.

Before evaluating any step from the sequence, the invalidation condition will be checked first, if it is true then we reset the sequence to step 1, after this we immediately evaluate the sequence from step 1.


# Placeholders (/platform/algos/alert-scripting/placeholders)



Placeholders are strings that refer to a specific series of values or events, and have the form `{placeholder_title}`, where `placeholder_title` is the name of the placeholder to access and `{}` makes sure the string is recognized as a placeholder.

Placeholders allow accessing market data such as ohlc prices, volume, but also specific features from their respective toolkit such as signals, indicators...etc.

## Common Placeholders [#common-placeholders]

The placeholders below can be used in all the toolkits.

### Market Data Placeholders [#market-data-placeholders]

Market data placeholder allows referencing typical market data such as ohlc prices and volume.

These placeholders are available for all toolkits.

| Placeholder | Description                                                   |
| ----------- | ------------------------------------------------------------- |
| `{open}`    | Open price of the bar                                         |
| `{high}`    | High price of the bar                                         |
| `{low}`     | Low price of the bar                                          |
| `{close}`   | Close price of the bar                                        |
| `{hl2}`     | Median price `(high + low) / 2`                               |
| `{ohlc4}`   | Average price `(open + high + low + close) / 4`               |
| `{hlc3}`    | Typical price `(high + low + close) / 3`                      |
| `{hlcc4}`   | Weighted average of hlc prices `(high + low + 2 * close) / 4` |
| `{volume}`  | Volume for the bar                                            |
| `{range}`   | Difference between High and Low prices                        |
| `{tr}`      | True Range                                                    |

### Time Related Placeholders [#time-related-placeholders]

Time placeholder allows referencing time related information such as the bar hour and minute, day, bar index...etc.

These placeholders are available for all toolkits.

| Placeholder                   | Description                                                                                                                                                   |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{barindex}`                  | Index of the current bar                                                                                                                                      |
| `{last_barindex}`             | Index of the most recent bar                                                                                                                                  |
| `{second}`                    | Current bar second                                                                                                                                            |
| `{minute}`                    | Current bar minute                                                                                                                                            |
| `{hour}`                      | Current bar hour                                                                                                                                              |
| `{dayofweek}`                 | Current bar day of the week ranging from 1 to 7, with 1 for sunday                                                                                            |
| `{dayofmonth}`                | Current bar day of the month                                                                                                                                  |
| `{weekofyear}`                | Current bar week of the year                                                                                                                                  |
| `{month}`                     | Current bar month of the year                                                                                                                                 |
| `{year}`                      | Current bar year (with timezone)                                                                                                                              |
| `{time}`                      | Unix timestamp of the bar                                                                                                                                     |
| `{unix_ts}`                   | Unix timestamp of the bar (Deprecated, use `{time}`)                                                                                                          |
| `{timeframe}`                 | Chart timeframe in minutes                                                                                                                                    |
| `{hhmm-hhmm}`                 | Session placeholder, `True` when the bar is located within the specific session interval, see [here](./placeholders#session-placeholder) for more information |
| `{YYYY-MM-DDThh:mm:ss±hh:mm}` | Datestring placeholder, equal to the timestamp of the specified date, see [here](./placeholders#datestring-placeholder) for more information                  |

<Note>
  Note that `{dayofweek}` returns the day based on the time of the bar's open. For overnight sessions (e.g. EURUSD, where Monday session starts on Sunday, 17:00) this value can be lower by 1 than the day of the trading day.
</Note>

The timezone for the time related placeholders (with the exception of `time`, and the datestring placeholder) is defined by the `Timezone` dropdown setting, defining the timezone using UTC. Default value is `exchange`, which uses the exchange timezones to get the value of the time related placeholders.

#### Datestring Placeholder [#datestring-placeholder]

You can easily define dates and optionally th time and timezone using the **Datestring** placeholder, following the ISO 8601 standard. The syntax of the placeholder is as follows:

```text Example
{YYYY-MM-DDThh:mm:ss±hh:mm}
```

* `YYYY`: Year
* `MM`: Month
* `DD`: Day
* `hh`: Hour
* `mm`: Minute
* `±hh:mm`: Timezone offset, default timezone is UTC-0 (optional)

<Note>
  The **Datestring** placeholder is not affected by the **Timezone** setting
</Note>

This placeholder can be useful alongside `{time}` to constraint the evaluation of conditions to a specific date interval. For example:

```text Example
{close} > {open} and {time} >= {2025-01-01}
```

The above script will only trigger if the closing price is above the opening price while the time is on or after January 1st, 2025.

Dates and times are separated by `T`, for example `{2025-01-01T04:00}`. `±hh:mm` can be used to offset the timezones, as such if the timezone of interest is the New York timezone (UTC-4), we would use:

```text Example
{2025-01-01T04:00-04:00}
```

#### Session Placeholder [#session-placeholder]

You can easily detect if the current bar is within a specific session (time interval) by using the session placeholder with pattern `{hhmm-hhmm}`, where `hh` is the hour and `mm` the minute.

The value of this placeholder will be `True` when the bar time is located between the start time `{hhmm-` and end time `-hhmm}`.

For example, if we want to detect when the bar is located in the session starting at 14:30 and ending at 16:45 we would simply use:

```python Example
{1430-1645}
```

### External Placeholders [#external-placeholders]

![](/images/alert-scripting/placeholders/external.png)

Users can refer to a series of values outside the toolkit by using external placeholder. A maximum of 5 external placeholders can be used:

* `{external1}`
* `{external2}`
* `{external3}`
* `{external4}`
* `{external5}`

In order to use external placeholders, users must first select the external source from the provided dropdowns located in the Alert Scripting settings. Once selected, external sources can be referred from their respective placeholders.

### Custom Alert Creator Placeholders [#custom-alert-creator-placeholders]

Placeholders referencing events from the custom alert creator in order to potentially create more complex alerts.

These can either be `True` or `False`.

| Placeholder           | Description                                              |
| --------------------- | -------------------------------------------------------- |
| `{custom_alert_step}` | Set conditions from the custom alert creator (except OR) |
| `{custom_alert_or}`   | OR condition from the custom alert creator               |

### Sequencing Placeholders [#sequencing-placeholders]

Alert scripting includes multiple placeholders helping user create and refine a sequence of conditions.

| Placeholder            | Type    | Description                                                                                                |
| ---------------------- | ------- | ---------------------------------------------------------------------------------------------------------- |
| `{udp.step}`           | Data    | Current active step in the ongoing sequence of conditions from udp `{udp}`, with 0 being the first step    |
| `{udp.barssince_step}` | Data    | Numbers of bars elapsed since last step or invalidation of the sequence associated to `{udp}`              |
| `{any_active_step}`    | Trigger | True if any of the inactive conditions from a sequence is true. Can only be used as invalidation condition |

For more information, see [here](./usage#sequencing-and-steps).

## User Defined Placeholders (UDP) [#user-defined-placeholders-udp]

User defined placeholders (UDPs) are custom placeholders defined by users and assigned to a specific condition set by the user. These placeholders can later be referenced as part of other conditions or UDPs.

<Info>
  We strongly recommend using UDPs to lower the complexity of conditions.

  You can easily breakdown one complex condition into two simpler UDPs.
</Info>

To define an UDP a line must start with a placeholder with a unique name, followed by an equal sign `=`, then followed by the condition we want to assign to this placeholder, for example `{my_placeholder} = {close} > {open}`

Let's take a more complex example and say we want to be alerted on a new bullish fair value gaps while we also had one on the previous bar.

Doing this could involve a long and messy script, to make things easier we can create an UDP that refers to the condition of a bullish fair value gaps, and reuse it for our alert condition:

```
{bull_fvg} = {low} > {high[2]} and {close[1]} > {high[2]}

{bull_fvg} and {bull_fvg[1]}
```

## Toolkit Placeholders [#toolkit-placeholders]

The placeholders below are unique to their respective toolkit, and refer to the toolkit features.

### Signals & Overlays Placeholders [#signals--overlays-placeholders]

#### Data Series Placeholders [#data-series-placeholders]

| Placeholder               | Description                                                                                            |
| ------------------------- | ------------------------------------------------------------------------------------------------------ |
| `{barcolor}`              | Bar color value for confirmation signals, with 1 for green, -1 for red, and 0 for purple               |
| `{ml_classifier}`         | ML Classifier value for signals, ranging from 1 to 4                                                   |
| `{smart_trail}`           | Smart Trail value                                                                                      |
| `{smart_trail_extremity}` | Smart Trail extremity value                                                                            |
| `{rz_r3}`                 | Reversal Zones Resistance 3 value                                                                      |
| `{rz_r2}`                 | Reversal Zones Resistance 2 value                                                                      |
| `{rz_r1}`                 | Reversal Zones Resistance 1 value                                                                      |
| `{rz_s1}`                 | Reversal Zones Support 1 value                                                                         |
| `{rz_s2}`                 | Reversal Zones Support 2 value                                                                         |
| `{rz_s3}`                 | Reversal Zones Support 3 value                                                                         |
| `{catcher}`               | Trend Catcher value                                                                                    |
| `{os_catcher}`            | Value associated to the trend detected by the Trend Catcher, with 1 for uptrends, and 0 for downtrends |
| `{tracer}`                | Trend Tracer value                                                                                     |
| `{os_tracer}`             | Value associated to the trend detected by the Trend Tracer, with 1 for uptrends, and 0 for downtrends  |
| `{neo_lead}`              | Neo Cloud lead line value                                                                              |
| `{neo_lag}`               | Neo Cloud lagging line value                                                                           |
| `{tp2}`                   | Take profit 2 level value                                                                              |
| `{tp1}`                   | Take profit 1 level value                                                                              |
| `{sl1}`                   | Stop loss 1 level value                                                                                |
| `{sl2}`                   | Stop loss 2 level value                                                                                |
| `{optimal_sensitivity}`   | Optimal sensitivity value                                                                              |
| `{trend_strength}`        | Trend Strength percentage value                                                                        |
| `{volatility}`            | Lux Volatility percentage value                                                                        |
| `{squeeze}`               | Squeeze Index percentage value                                                                         |
| `{volume_sentiment}`      | Volume Sentiment percentage value                                                                      |

#### Trigger Placeholders [#trigger-placeholders]

| Placeholder                    | Description                                                                        |
| ------------------------------ | ---------------------------------------------------------------------------------- |
| `{bullish_confirmation}`       | `True` on a new normal bullish confirmation signal                                 |
| `{bullish_confirmation+}`      | `True` on a new strong bullish confirmation signal                                 |
| `{bullish_confirmation_any}`   | `True` on a any new bullish confirmation signal                                    |
| `{bullish_confirmation_turn+}` | `True` when a normal bullish confirmation signal turns strong                      |
| `{bearish_confirmation}`       | `True` on a new normal bearish confirmation signal                                 |
| `{bearish_confirmation+}`      | `True` on a new strong bearish confirmation signal                                 |
| `{bearish_confirmation_any}`   | `True` on any new bearish confirmation signal                                      |
| `{bearish_confirmation_turn+}` | `True` when a normal bearish confirmation signal turns strong                      |
| `{bullish_contrarian}`         | `True` on a new normal bullish contrarian signal                                   |
| `{bullish_contrarian+}`        | `True` on a new strong bullish contrarian signal                                   |
| `{bullish_contrarian_any}`     | `True` on any new bullish contrarian signal                                        |
| `{bearish_contrarian}`         | `True` on a new normal bearish contrarian signal                                   |
| `{bearish_contrarian+}`        | `True` on a new strong bearish contrarian signal                                   |
| `{bearish_contrarian_any}`     | `True` on any new bearish contrarian signal                                        |
| `{exit_buy}`                   | `True` on a new exit buy signal                                                    |
| `{exit_sell}`                  | `True` on a new exit sell signal                                                   |
| `{bullish_smart_trail}`        | `True` if Smart Trail is up-trending                                               |
| `{bearish_smart_trail}`        | `True` if Smart Trail is down-trending                                             |
| `{switch_bullish_smart_trail}` | `True` if Smart Trail switches to bullish                                          |
| `{switch_bearish_smart_trail}` | `True` if Smart Trail switches to bearish                                          |
| `{price_above_rzr1}`           | `True` if the closing price is above the lower Reversal Zones resistance R1        |
| `{price_below_rzs1}`           | `True` if the closing price is below the upper Reversal Zones support S1           |
| `{price_co_rzr1}`              | `True` if the closing price cross over the lower Reversal Zones resistance R1      |
| `{price_cu_rzs1}`              | `True` if the closing price cross under the upper Reversal Zones support S1        |
| `{bullish_catcher}`            | `True` if Trend Catcher is up-trending                                             |
| `{bearish_catcher}`            | `True` if Trend Catcher is down-trending                                           |
| `{switch_bullish_catcher}`     | `True` if Trend Catcher switches to bullish                                        |
| `{switch_bearish_catcher}`     | `True` if Trend Catcher switches to bearish                                        |
| `{bullish_tracer}`             | `True` if Trend Tracer is up-trending                                              |
| `{bearish_tracer}`             | `True` if Trend Tracer is down-trending                                            |
| `{switch_bullish_tracer}`      | `True` if Trend Tracer switches to bullish                                         |
| `{switch_bearish_tracer}`      | `True` if Trend Tracer switches to bearish                                         |
| `{bullish_neo}`                | `True` if Neo Cloud is up-trending                                                 |
| `{bearish_neo}`                | `True` if Neo Cloud is down-trending                                               |
| `{switch_bullish_neo}`         | `True` if Neo Cloud switches to bullish                                            |
| `{switch_bearish_neo}`         | `True` if Neo Cloud switches to bearish                                            |
| `{trend_strength_trending}`    | `True` if the Trend Strength indicates a trending market (Trend Strength above 50) |
| `{trend_strength_ranging}`     | `True` if the Trend Strength indicates a ranging market (Trend Strength below 50)  |

### Price Action Concepts Placeholders [#price-action-concepts-placeholders]

#### Data Series Placeholders [#data-series-placeholders-1]

| Placeholder           | Description                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{internal_high}`     | Internal high levels value                                                                                                                                                                                                                                                                                                                                                                    |
| `{internal_low}`      | Internal low levels value                                                                                                                                                                                                                                                                                                                                                                     |
| `{swing_high}`        | Swing high levels value                                                                                                                                                                                                                                                                                                                                                                       |
| `{swing_low}`         | Swing low levels value                                                                                                                                                                                                                                                                                                                                                                        |
| `{itrend}`            | The trend value is determined by internal market structures, with positive values for uptrends and negative values for downtrends. In an uptrend, an Internal Change of Character (I-CHoCH) has a value of +1, while an Internal Break of Structure (I-BOS) has a value of +2. The same logic applies to downtrends but uses negative values instead (−1 for an I-CHoCH and −2 for an I-BOS). |
| `{strend}`            | The trend value is determined by swing market structures, with positive values for uptrends and negative values for downtrends. In an uptrend, a Swing Change of Character (S-CHoCH) has a value of +1, while a Swing Break of Structure (S-BOS) has a value of +2. The same logic applies to downtrends but uses negative values instead (−1 for an S-CHoCH and −2 for an S-BOS).            |
| `{ob_buy_volume}`     | Most recent order block buy volume                                                                                                                                                                                                                                                                                                                                                            |
| `{ob_sell_volume}`    | Most recent order block sell volume                                                                                                                                                                                                                                                                                                                                                           |
| `{ob_total_volume}`   | Most recent order block total volume                                                                                                                                                                                                                                                                                                                                                          |
| `{ob_per_volume}`     | Most recent order block percentage volume                                                                                                                                                                                                                                                                                                                                                     |
| `{prev_day_high}`     | Previous day high value                                                                                                                                                                                                                                                                                                                                                                       |
| `{prev_day_low}`      | Previous day low value                                                                                                                                                                                                                                                                                                                                                                        |
| `{prev_week_high}`    | Previous week high value                                                                                                                                                                                                                                                                                                                                                                      |
| `{prev_week_low}`     | Previous week low value                                                                                                                                                                                                                                                                                                                                                                       |
| `{prev_month_high}`   | Previous month high value                                                                                                                                                                                                                                                                                                                                                                     |
| `{prev_month_low}`    | Previous month low value                                                                                                                                                                                                                                                                                                                                                                      |
| `{prev_quarter_high}` | Previous quarter high value                                                                                                                                                                                                                                                                                                                                                                   |
| `{prev_quarter_low}`  | Previous quarter low value                                                                                                                                                                                                                                                                                                                                                                    |

#### Trigger Placeholders [#trigger-placeholders-1]

| Placeholder                     | Description                                         |
| ------------------------------- | --------------------------------------------------- |
| `{bullish_ichoch}`              | `True` on a new internal bullish CHoCH              |
| `{bullish_ichoch+}`             | `True` on a new internal bullish CHoCH+             |
| `{bullish_ibos}`                | `True` on a new internal bullish BOS                |
| `{bearish_ichoch}`              | `True` on a new internal bearish CHoCH              |
| `{bearish_ichoch+}`             | `True` on a new internal bearish CHoCH+             |
| `{bearish_ibos}`                | `True` on a new internal bearish BOS                |
| `{bullish_schoch}`              | `True` on a new swing bullish CHoCH                 |
| `{bullish_schoch+}`             | `True` on a new swing bullish CHoCH+                |
| `{bullish_sbos}`                | `True` on a new swing bullish BOS                   |
| `{bearish_schoch}`              | `True` on a new swing bearish CHoCH                 |
| `{bearish_schoch+}`             | `True` on a new swing bearish CHoCH+                |
| `{bearish_sbos}`                | `True` on a new swing bearish BOS                   |
| `{bullish_ob}`                  | `True` on a new bullish order block                 |
| `{bearish_ob}`                  | `True` on a new bearish order block                 |
| `{bullish_bb}`                  | `True` on a new bullish breaker block               |
| `{bearish_bb}`                  | `True` on a new bearish breaker block               |
| `{bullish_ob_mitigated}`        | `True` when a bullish order block is mitigated      |
| `{bearish_ob_mitigated}`        | `True` when a bearish order block is mitigated      |
| `{bullish_ob_within}`           | `True` when price is within a bullish order block   |
| `{bearish_ob_within}`           | `True` when price is within a bearish order block   |
| `{bullish_ob_entered}`          | `True` when price enters a bullish order block      |
| `{bearish_ob_entered}`          | `True` when price enters a bearish order block      |
| `{bullish_ob_exit}`             | `True` when price exits a bullish order block       |
| `{bearish_ob_exit}`             | `True` when price exits a bearish order block       |
| `{bullish_imbalance}`           | `True` on a new bullish imbalance                   |
| `{bearish_imbalance}`           | `True` on a new bearish imbalance                   |
| `{bullish_imbalance_mitigated}` | `True` when a bullish imbalance is mitigated        |
| `{bearish_imbalance_mitigated}` | `True` when a bearish imbalance is mitigated        |
| `{bullish_imbalance_within}`    | `True` when price is within a bullish imbalance     |
| `{bearish_imbalance_within}`    | `True` when price is within a bearish imbalance     |
| `{bullish_imbalance_entered}`   | `True` when price enters a bullish imbalance        |
| `{bearish_imbalance_entered}`   | `True` when price enters a bearish imbalance        |
| `{bullish_imbalance_exit}`      | `True` when price exits a bullish imbalance         |
| `{bearish_imbalance_exit}`      | `True` when price exits a bearish imbalance         |
| `{bullish_trendline_new}`       | `True` on a new bullish trendline                   |
| `{bullish_trendline_update}`    | `True` on a bullish trendline update                |
| `{bullish_trendline_break}`     | `True` on a bullish trendline break                 |
| `{bearish_trendline_new}`       | `True` on a new bearish trendline                   |
| `{bearish_trendline_update}`    | `True` on a bearish trendline update                |
| `{bearish_trendline_break}`     | `True` on a bearish trendline break                 |
| `{bullish_grab}`                | `True` on a new bullish liquidity grab              |
| `{bearish_grab}`                | `True` on a new bearish liquidity grab              |
| `{premium}`                     | `True` when price is located within a premium zone  |
| `{discount}`                    | `True` when price is located within a discount zone |
| `{custom_alert_step}`           | `True` on a custom alert step completion            |
| `{custom_alert_or}`             | `True` on a custom alert OR condition completion    |

### Oscillator Matrix Placeholders [#oscillator-matrix-placeholders]

#### Data Series Placeholders [#data-series-placeholders-2]

| Placeholder            | Description                                                                                            |
| ---------------------- | ------------------------------------------------------------------------------------------------------ |
| `{moneyflow}`          | Money Flow oscillator value                                                                            |
| `{overflow}`           | Overflow value                                                                                         |
| `{upper_mf_threshold}` | Upper Money Flow threshold                                                                             |
| `{lower_mf_threshold}` | Lower Money Flow threshold                                                                             |
| `{hyperwave}`          | HyperWave oscillator value                                                                             |
| `{hyperwave_signal}`   | HyperWave signal line value                                                                            |
| `{bullish_confluence}` | Bullish confluence numerical value, 0 for no confluence, 1 for low confluence, 2 for strong confluence |
| `{bearish_confluence}` | Bearish confluence numerical value, 0 for no confluence, 1 for low confluence, 2 for strong confluence |
| `{confluence_meter}`   | Confluence meter value                                                                                 |

#### Trigger Placeholders [#trigger-placeholders-2]

| Placeholder                             | Description                                         |
| --------------------------------------- | --------------------------------------------------- |
| `{regular_bullish_hyperwave_signal}`    | `True` on a new regular bullish HyperWave signal    |
| `{regular_bearish_hyperwave_signal}`    | `True` on a new regular bearish HyperWave signal    |
| `{oversold_bullish_hyperwave_signal}`   | `True` on a new oversold bullish HyperWave signal   |
| `{overbought_bearish_hyperwave_signal}` | `True` on a new overbought bearish HyperWave signal |
| `{hyperwave_co_80}`                     | `True` when HyperWave crosses over 80               |
| `{hyperwave_cu_20}`                     | `True` when HyperWave crosses under 20              |
| `{hyperwave_co_50}`                     | `True` when HyperWave crosses over 50               |
| `{hyperwave_cu_50}`                     | `True` when HyperWave crosses under 50              |
| `{hyperwave_above_80}`                  | `True` when HyperWave is above 80                   |
| `{hyperwave_below_20}`                  | `True` when HyperWave is below 20                   |
| `{hyperwave_above_50}`                  | `True` when HyperWave is above 50                   |
| `{hyperwave_below_50}`                  | `True` when HyperWave is below 50                   |
| `{moneyflow_co_50}`                     | `True` when Money Flow crosses over 50              |
| `{moneyflow_cu_50}`                     | `True` when Money Flow crosses under 50             |
| `{moneyflow_above_50}`                  | `True` when Money Flow is above 50                  |
| `{moneyflow_below_50}`                  | `True` when Money Flow is below 50                  |
| `{new_bullish_overflow}`                | `True` on a new bullish Overflow signal             |
| `{new_bearish_overflow}`                | `True` on a new bearish Overflow signal             |
| `{bullish_overflow}`                    | `True` if bullish Overflow is active                |
| `{bearish_overflow}`                    | `True` if bearish Overflow is active                |
| `{reversal_up-}`                        | `True` on a new minor bullish reversal signal       |
| `{reversal_up+}`                        | `True` on a new major bullish reversal signal       |
| `{reversal_down-}`                      | `True` on a new minor bearish reversal signal       |
| `{reversal_down+}`                      | `True` on a new major bearish reversal signal       |
| `{reversal_any_up}`                     | `True` on any new bullish reversal signal           |
| `{reversal_any_down}`                   | `True` on any new bearish reversal signal           |
| `{bullish_divergence}`                  | `True` on a new bullish divergence                  |
| `{bearish_divergence}`                  | `True` on a new bearish divergence                  |
| `{weak_bullish_confluence}`             | `True` on a new weak bullish confluence             |
| `{weak_bearish_confluence}`             | `True` on a new weak bearish confluence             |
| `{strong_bullish_confluence}`           | `True` on a new strong bullish confluence           |
| `{strong_bearish_confluence}`           | `True` on a new strong bearish confluence           |
| `{custom_alert_step}`                   | `True` on a custom alert step completion            |
| `{custom_alert_or}`                     | `True` on a custom alert OR condition completion    |


# Pre-made Templates (/platform/algos/alert-scripting/readymade-scripts)



You can find the pre-configured scripts below adapted for various use cases. To use them, simply copy the script code and paste them in the "Condition Input Field" setting.

## S\&O Toolkit [#so-toolkit]

### Confirmation Signals during New York Session [#confirmation-signals-during-new-york-session]

```
{bullish_confirmation_any} and {0900-1700} or {bearish_confirmation_any} and {0900-1700}
```

Use UTC-5 as timezone.

### Optimal Sensitivity Change [#optimal-sensitivity-change]

```
{optimal_sensitivity} != {optimal_sensitivity[1]}
```

### Early short signal, weak trend confirmation above smart trail [#early-short-signal-weak-trend-confirmation-above-smart-trail]

```
{bearish_confirmation_any} and {ml_classifier} < 3 and {bullish_smart_trail}
```

### Reversal setup, Confirmation exit into strong trend change [#reversal-setup-confirmation-exit-into-strong-trend-change]

```
{confirmation_exits} then {bearish_confirmation_any} then {barcolor} == -1
```

### Contrarian inside upper Reversal Zone [#contrarian-inside-upper-reversal-zone]

```
{bearish_contrarian_any} and {close} > {rz_r2} and {close} < {rz_r3}
```

### Price Retesting Smart Trail with bearish confirmation [#price-retesting-smart-trail-with-bearish-confirmation]

```
{bearish_confirmation_any} and {bearish_smart_trail} or {high} crossover {smart_trail_extremity} and {bearish_smart_trail}
```

### Strong Buy Signal with Volume Confirmation [#strong-buy-signal-with-volume-confirmation]

```
{bullish_confirmation+} and {volume} > {volume[1]} and {trend_strength} > 60
```

### Smart Trail Trend Change with Confirmation [#smart-trail-trend-change-with-confirmation]

```
{bullish_smart_trail} and not {bullish_smart_trail[1]} and {bullish_confirmation_any}
```

### Exit During High Volatility & Squeeze [#exit-during-high-volatility--squeeze]

```
{exit_sell} and {volatility} > 75 and {squeeze} > 80
```

### Strong Trend with Multiple Indicators [#strong-trend-with-multiple-indicators]

```
{bullish_catcher} and {bullish_tracer} and {bullish_neo} and {trend_strength} > 70
```

### Neo Cloud Breakout with Volume [#neo-cloud-breakout-with-volume]

```
{close} cross {neo_lead} and {volume} > {volume[1]}
```

### Take Profit Reached [#take-profit-reached]

```
{close} cross {tp1} or {close} cross {tp2}
```

### Stop Loss Reached [#stop-loss-reached]

```
{close} cross {sl1} or {close} cross {sl2}
```

### Strong Trend Shift [#strong-trend-shift]

```
{bullish} = {bullish_confirmation_any} and {bearish_smart_trail[1]} and {volatility} > 50
{bearish} = {bearish_confirmation_any} and {bullish_smart_trail[1]} and {volatility} > 50
{bullish} or {bearish}
```

## PAC Toolkit [#pac-toolkit]

### Bearish Order Block Creation with Bullish Volume [#bearish-order-block-creation-with-bullish-volume]

```
{bear_ob} and {ob_buy_volume} > {ob_sell_volume}
```

### Swing / Internal CHOCH Confluence [#swing--internal-choch-confluence]

```
{bull_schoch} and {bull_ichoch} or {bull_schoch+} and {bull_ichoch+}
```

### Order Block Mitigation Setup [#order-block-mitigation-setup]

```
{bull_ob_mitigated} and {bullish_grab}
```

### Premium Zone Trading [#premium-zone-trading]

```
{premium} and {bull_imbalance} and {swing_high} < {close}
```

### Multiple Block Confluence [#multiple-block-confluence]

```
{bull_ob_within} and {bull_imbalance_within}
```

### Trendline Break with Structure [#trendline-break-with-structure]

```
{trendline_up_break} and {bear_ichoch}
```

### High-Probability Reversal [#high-probability-reversal]

```
{bear_ob_entered} and {bearish_grab} and {prev_week_high} < {close}
```

### Key Bullish Level Break [#key-bullish-level-break]

```
{any_choch} = {bullish_ichoch} or {bullish_ichoch+} or {bullish_schoch} or {bullish_schoch+}
{close} > {prev_day_high} and {any_choch}
```

### Complex Structure Analysis [#complex-structure-analysis]

```
{bull_bb} and {bull_imbalance_entered} and {trendline_up_new}
```

## Oscillator Matrix Toolkit [#oscillator-matrix-toolkit]

### Strong Reversal Signal [#strong-reversal-signal]

```
{reversal_up+} and {bullish_divergence} and {hyperwave} > {hyperwave_signal}
```

### High Confluence Setup [#high-confluence-setup]

```
{bullish_confluence} == 2 and {moneyflow} > 60 and {overflow} > 70
```

### Oversold Bounce [#oversold-bounce]

```
{oversold_bullish_hyperwave_signal} and {bullish_divergence} and {confluence_meter} > 70
```

### Multiple Indicator Confirmation [#multiple-indicator-confirmation]

```
{regular_bullish_hyperwave_signal} and {reversal_up+} and {moneyflow} crossover 50
```

### Divergence with Momentum [#divergence-with-momentum]

```
{bullish_divergence} and {hyperwave} crossover {hyperwave_signal} and {moneyflow} > 50
```

### Strong Trend Confirmation [#strong-trend-confirmation]

```
{bullish_confluence} > 0 and {overflow} > 60 and {moneyflow} > {moneyflow[1]}
```

### HyperWave Signal with Confluence [#hyperwave-signal-with-confluence]

```
{regular_bullish_hyperwave_signal} and {bullish_confluence} == 2 and {confluence_meter} > 80
```

### Major Reversal Pattern [#major-reversal-pattern]

```
{reversal_up+} and {moneyflow} > 70 and {hyperwave} > {hyperwave[1]}
```

### Early Trend Detection [#early-trend-detection]

```
{regular_bullish_hyperwave_signal} and {moneyflow} crossover 30 and {overflow} > 50
```

### High Probability Setup [#high-probability-setup]

```
{bullish_divergence} and {reversal_up+} and {bullish_confluence} == 2 and {confluence_meter} > 75
```


# Usage (/platform/algos/alert-scripting/usage)



Alert scripting is a good way to create more complex alerts without feeling limited by the number of conditions or their limited reusability.

This page covers the syntax of scripted alerts as well as showcasing typical use cases for alert scripting.

## Building Simple Conditions [#building-simple-conditions]

Users can define a simple condition over a single line in the text area "Condition Input Field".

In order to build effective conditions, we will need to reference specific data using [placeholders](./placeholders) and compare this data with each other using [operators](./operators).

For example, we could define the following condition:

```
{close} > {close[14]}
```

Here we have the following elements:

* `{close}` placeholder, referencing the current closing price value
* Greater than operator `>`, testing if the value to its left is greater than the value to its right
* `{close[14]}` refer to the value of the closing price (`{close}`) 14 bars ago (defined by the historical referencing operator `[14]`)

This simple condition is `True` if the current closing price is higher than the closing price 14 bars ago.

### Combining Conditions [#combining-conditions]

Creating slightly more complex conditions can involve combining individual ones into a single one, allowing us to retain more precise events or on the contrary, loosen our condition rules.

#### Combination using `and` [#combination-using-and]

Using the operator `and` allows to require more than one "sub-conditions" to be valid for the whole to be true.

For example, we could reuse our previous condition but be more strict such that we only want to know when the current closing price is greater than the closing price 14 bars ago when it is between 1pm and 3pm (exchange timezone). To do this we can simply add another sub-condition leading to:

```
{close} > {close[14]} and {hour} >= 13 and {hour} <= 15
```

<Tip>
  `and` is similar to using the custom alert creator and having multiple enabled conditions using the same step number.
</Tip>

Another example where we can combine multiple conditions is to detect FVG's, for the case of a bullish FVG we have the following requirements:

* current low price is higher than the high price two bars ago
* previous closing price is higher than the high price two bars ago

We can use alert scripting to detect bullish FVG's from this combination of conditions as follows:

```
{low} > {high[2]} and {close[1]} > {high[2]}
```

#### Combination using `or` [#combination-using-or]

Sometimes we might be interested in getting alerted about two specific different conditions, without having to create two specific alerts. This is where `or` can be useful, requiring only one of the defined sub-conditions to be true in order for the alert to trigger.

For example, we could want to be alerted either on a strong bullish confirmation signal, or when the Smart Trail turns bullish, this could be easily achieved using the following script:

```
{bullish_confirmation+} or {bullish_smart_trail} and not {bullish_smart_trail[1]}
```

Here `{bullish_confirmation+}` is true when we have a strong bullish confirmation signal.

`{bullish_smart_trail}` when the Smart Trail is bullish, with the whole `{bullish_smart_trail} and not {bullish_smart_trail[1]}` being true when the Smart Trail is currently bullish while the previous Smart trail is **not** bullish.

<Info>
  `or` takes precedence over `and`. You can think of this as a way to split a whole condition into multiple sub-conditions.

  For example if we have `{A} and {B} or {C} > 0 or {D} and {E}`, the precedence of or would split this conditions leading to `{A} and {B}`, `{C} > 0`, and `{D} and {E}`.

  Each of these individual sub-conditions would be evaluated individually, if one of them is true, then our whole condition is true.
</Info>

## Sequencing and Steps [#sequencing-and-steps]

Users can create sequences of conditions, triggering an alert when the last step in a sequence is true.

Sequences are constructed using the `then` operator, with each conditions seperated by `then` being a step. The first step is the leftmost one before `then`. When a step is valid, the next step condition will be evaluated at the next bar.

Once the last step is evaluated and is true, the sequence is completed which can trigger an alert (if set) and will switch to the first defined step on the next bar.

<Info>
  This is similar in practice to how the custom alert creator works.
</Info>

For example, we can define a sequence consisting of the following conditions:

* Step 1: High price is lower than the previous high price
* Step 2: Low price is lower than the previous low price
* Step 3: Closing price is lower than the opening price

Leading to the following script:

```
{my_sequence} = {high} < {high[1]} then {low} < {low[1]} then {close} < {open}
```

<Tip>
  For clarity you can assign conditions over a single line to a UDP defined at the start.

  ```
  {condition1} = ...
  {condition2} = ...
  {condition3} = ...

  {my_sequence} = {condition1} then {condition2} then {condition3}
  ```
</Tip>

### Invalidation [#invalidation]

Invalidation refers to re-evaluating from the first step of a sequence of conditions when one or more specific conditions are true.

<Note>
  Invalidation takes place prior to evaluating steps.
</Note>

Invalidation conditions can be defined for a sequence using the inline invalidation operator `!`:

```
// Invalidate sequence when {close} is above {open}
{my_sequence} = {high} < {high[1]} then {low} < {low[1]} then {close} < {open} ! {close} > {open}
```

If the condition after `!` is true, we go back to evaluating the sequence of conditions from step 1.

Another way to perform invalidation is using the method `invalidate()`:

```
// Invalidate sequence when {close} is above {open}, evaluate invalidation first
{my_sequence}.invalidate() = {close} > {open}

// Define sequence
{my_sequence} = {high} < {high[1]} then {low} < {low[1]} then {close} < {open}
```

#### Step Specific Invalidation [#step-specific-invalidation]

It is possible to apply invalidation conditions to specific steps, this is done using the `step` parameter in the `@invalidate` action.

To illustrate this, we can reuse our previous sequence of conditions:

```
{my_sequence} = {high} < {high[1]} then {low} < {low[1]} then {close} < {open}
```

Maybe that we don't want the closing price to be greater than the opening price as long as the second step is the one being evaluated, this can be achieved as follows:

```
{my_sequence} = {high} < {high[1]} then {low} < {low[1]} then {close} < {open} ! {close} > {open} and {my_sequence.step} == 1
```

Here `{my_sequence.step} == 1` refer to the second step of \{my\_sequence} needing to be the one being evaluated in the sequence to apply the invalidation condition `{close} > {open}`.

<Info>
  With steps, 0 refers to the first step because it is 0 indexed.
</Info>

Another way of doing this using the `invalidate` method can be done as follows:

```
// Invalidate sequence 
{my_sequence}.invalidate(step=1) = {close} > {open}

// Define sequence
{my_sequence} = {high} < {high[1]} then {low} < {low[1]} then {close} < {open}
```

Here `invalidate(step=1)` indicates that invalidation only takes place if the active step is the second one.

#### Invalidate on Step Evaluation Duration [#invalidate-on-step-evaluation-duration]

Sometimes we might not want to keep evaluating one or multiple steps for an excessive number of bars, and instead prefer having each step being triggered with a short interval in between.

This can easily be done using invalidation and the placeholder `barssince_step`, which quantify the number of bars that elapsed since a step has started being evaluated or since invalidation took place.

For example, we can invalidate a sequence of conditions if the amount elapsed since a step is greater than 5 bars:

```
{my_sequence} = {high} < {high[1]} then {low} < {low[1]} then {close} < {open} ! {my_sequence.barssince_step} > 5
```

## Visualizing Scripted Alerts Conditions [#visualizing-scripted-alerts-conditions]

![](/images/alert-scripting/usage/highlight.png)

In order to make sure the scripted alert corresponds to the desired result and that it triggers as expected, users can visualize when the alert would have triggered using the "Highlight On Chart" setting.

Enabling this setting will display a series of squares located at the top or bottom of the chart. When the last UDP condition specified in the script is true, the square will be colored in blue or the selected color from the setting.

<Tip>
  Users can control the location of this highlighter on the chart trough the dropdown menu.

  "Top" and "Bottom" locations are available, while the Oscillator Matrix toolkit also includes a "Center" option.

  "None" will hide the scripted alert highlighting.
</Tip>

## Setting Scripted Alerts [#setting-scripted-alerts]

![](/images/alert-scripting/usage/alert_toggle.png)

Once a scripted alert is defined, we can create an alert using it as a condition by enabling the "Scripted Alert" setting in the "Any alert() Function Call" settings group located at the bottom of the toolkit settings.

![](/images/alert-scripting/usage/alert.png)

Then we can create the alert by clicking on the alert menu, selecting the toolkit in the condition dropdown, and making sure the dropdown below has "any alert() function call" selected.

The alert will trigger whenever the last defined UDP condition is true.

<Tip>
  More alerts can be created via scripting using the `@alert()` action, see [here](./actions#@alert) for more information.
</Tip>

## Comments [#comments]

To make a scripted alert easier to read, users can add comments by starting a line with `//`.

Comments will be ignored when a scripted alert is evaluated.

For example:

```
// Step 1: candle color becomes green
{barcolor} > 0 and not {barcolor[1]} > 0

// Step 2: candle color is red
{barcolor} < 0
```

Comments can also be set inline:

```
// Step 1: candle color becomes green
{green_color} = {barcolor} > 0 and not {barcolor[1]} > 0 

@label() = {green_color} // Label when candle color becomes green
```


# Alerts (/platform/algos/oscillator-matrix/alerts)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/olKQq5XR5S0"
    frameBorder="0"
  />
</div>

The Oscillator Matrix® can alert users for multiple events relevant to the existing features in the toolkit. To learn more about how to set alerts in general see [how to create alerts on tradingview](/platform/algos/tradingview-alerts).

<Note>
  In order for an alert to work in the toolkit make sure the related feature is enabled
</Note>

## Any alert() function call [#any-alert-function-call]

<div align="center">
  ![](/images/lux-oscillator/alerts/any-alert.png)
</div>

It possible to set only one alert alerting for multiple selected conditions by the users.

Conditions can be selected in the **Any alert() function call** group in the indicator settings. Then the user can set an alert by selecting "any alert() function call" as condition.

<Note>
  Alert conditions for the "any alert() function call" use a `once per bar close` frequency
</Note>

### Any Alert() Function Call Message [#any-alert-function-call-message]

Users can set a custom message that will be used when an any alert() function call condition is triggered in the message field below the list of conditions. Users can use placeholders to customize messages and return useful information, the following placeholders are supported:

#### Symbol Placeholders [#symbol-placeholders]

| Placeholder  | Description                                             | Example                 |
| ------------ | ------------------------------------------------------- | ----------------------- |
| `{default}`  | Default message associated with the trigerred condition | `"Bullish Divergence"`  |
| `{ticker}`   | Symbol ticker without the exchange prefix               | `"MSFT"`                |
| `{exchange}` | Symbol exchange/data source                             | `"COINBASE"`            |
| `{sector}`   | Sector of the symbol, `na` if the symbol has no sector  | `"Technology Services"` |
| `{market}`   | Symbol market type                                      | `"Stocks"`              |

#### Time Placeholders [#time-placeholders]

| Placeholder | Description                                                                            | Example           |
| ----------- | -------------------------------------------------------------------------------------- | ----------------- |
| `{tf}`      | Chart timeframe, includes "S" for seconds, "D" for days, "W" for weeks, "M" for months | `"15"`            |
| `{time}`    | Unix time in milliseconds in exchange timezone                                         | `"1705331385158"` |
| `{year}`    | Current year in exchange timezone                                                      | `"2024"`          |
| `{month}`   | Current month in exchange timezone                                                     | `"7"`             |
| `{day}`     | Current day of the month in exchange timezone                                          | `"21"`            |

#### Data Placeholders [#data-placeholders]

| Placeholder | Description    | Example    |
| ----------- | -------------- | ---------- |
| `{open}`    | Opening price  | `"1.2145"` |
| `{high}`    | High price     | `"1.2357"` |
| `{low}`     | Low price      | `"1.1983"` |
| `{close}`   | Closing price  | `"1.0569"` |
| `{volume}`  | Current volume | `"45216"`  |

#### Indicators Placeholders [#indicators-placeholders]

| Placeholder          | Description                                                                                                      | Example   |
| -------------------- | ---------------------------------------------------------------------------------------------------------------- | --------- |
| `{moneyflow}`        | Money Flow oscillator value                                                                                      | `"56.89"` |
| `{bull_overflow}`    | Bullish Overflow value                                                                                           | `"51.63"` |
| `{bear_overflow}`    | Bearish Overflow value                                                                                           | `"46.52"` |
| `{hyperwave}`        | HyperWave oscillator value                                                                                       | `"78.44"` |
| `{hyperwave_signal}` | HyperWave oscillator signal line value                                                                           | `"72.61"` |
| `{bull_confluence}`  | Bullish confluence zone value. 2 if exhibiting strong confluence, 1 for weak confluence, and 0 for no confluence | `"2"`     |
| `{bear_confluence}`  | Bearish confluence zone value. 2 if exhibiting strong confluence, 1 for weak confluence, and 0 for no confluence | `"0"`     |
| `{confluence_meter}` | Confluence meter value                                                                                           | `"89.73"` |

### Format as JSON [#format-as-json]

Use the following message template to receive "any alert() function call" messages in the JSON format:

```json
{
  "alert": "{default}",
  "ticker": "{ticker}",
  "tf": "{tf}",
  "ohlcv":{
    "open": {open},
    "high": {high},
    "low": {low},
    "close": {close},
    "volume": {volume}
  },
  "bartime": {time}
}
```

## Custom Alert Creator [#custom-alert-creator]

![](/images/lux-oscillator/alerts/custom.png)

Users wishing to construct more complex alerts using features within the toolkit and/or external indicators can use the **Custom Alert Creator**.

The custom alert creator allows alerting when various user set conditions are met or when a sequence of conditions is complete.

<Tip>
  A user wishing to be alerted when the money flow is bullish (above 50) while the HyperWave is overbought (above 80) could do it as follows:

  ![](/images/lux-oscillator/alerts/custom_example.png)
</Tip>

A custom alert can be triggered as an *any alert() function call* if the setting is selected or as a regular alert when selected in the Tradingview alert creator menu.

### Steps [#steps]

Steps allow creating a sequence of conditions, which when met trigger an alert. When a condition with a step equal to 1 is met the next condition associated with step 2 will be evaluated (if enabled) and so on for higher steps (if any).

Conditions with the same associated step will require for both conditions to be true in order for the condition associated to a higher step (if any) to be evaluated. This allows certain conditions to act as filters.

<Tip>
  A user wishing to be alerted when the HyperWave cross under 80 after any bearish reversal signal could do it as follows:

  ![](/images/lux-oscillator/alerts/step_example.png)
</Tip>

<Warning>
  Make sure steps are set such that they form a linear sequence. For example if the lowest step is 2 or if the set steps are only 1 and 3 an error will be returned.
</Warning>

#### OR Step Operator [#or-step-operator]

The "OR" step operator can be used to get alerted for any additional condition using "OR" as step, in addition to other set custom alert conditions.

Conditions using the "OR" step can be highlighted using a different color, which can be set from the Custom Alert Creator settings.

#### All Step Operator [#all-step-operator]

The "All" step operator can be used to filter any set "Step" condition, including "OR" and "Invalidate".

This step operator can be useful if all the steps in a sequence needs to obey a specific rule.

<Tip>
  If a user has 2 steps set, and requires both of these to occur at a specific time interval such as 9am to 1pm, then we can use a "Session" condition using the "All" step, requiring our step 1 and step 2 to be occur between 9am and 1pm.
</Tip>

#### Invalidate Step [#invalidate-step]

The "Invalidate" step allows to set a condition as an "invalidation condition". When this condition is triggered while a sequence of conditions is incomplete, the sequence will restart at step 1.

If multiple steps are set as "Invalidate" any of the conditions being true will reset the sequence of conditions, as such not all of them are required to be true for the sequence of conditions to reset.

<Card title="Example">
  Let's take an example where we use 2 regular steps and one "Invalidate" step. The alert will trigger when step 2 trigger after step 1 has been triggered.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Invalidation Step">
      Invalidation step condition trigger, we reset the sequence and evaluate step 1 just after.
    </Step>
  </Steps>
</Card>

### Invalidation Behaviors [#invalidation-behaviors]

Invalidation behaviors allows adding more restrictions to a sequence of conditions, users can use two different invalidation behaviors described below:

#### Invalidate On Step 1 [#invalidate-on-step-1]

The "Invalidate On Step 1" behavior allows to reset an incomplete sequence of conditions when the condition on step 1 trigger. This prevents the first step condition from happening in between other steps of the condition sequence.

This behavior is useful when the first step of our sequence of conditions needs to never be repeated during the sequence.

<Card title="Example">
  Let's take an example where we use 3 regular steps.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Step 2">
      Step 2 condition trigger, we will now evaluate step 3 from now on.
    </Step>

    <Step title="Step 1 Trigger">
      Step 1 condition trigger, we start evaluating step 2 from now.
    </Step>
  </Steps>
</Card>

#### Invalidate On Any Repeated Step [#invalidate-on-any-repeated-step]

The "Invalidate On Any Repeated Step" behavior allows to reset an incomplete sequence of conditions when a step is triggered such that it does not respect the set order of conditions.

This behavior is useful when we want a perfectly ordered sequence of conditions to complete, without any step repeating itself.

<Card title="Example">
  Let's take an example where we use 3 regular steps.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Step 2">
      Step 2 condition trigger, we will now evaluate step 3 from now on.
    </Step>

    <Step title="Step 1 or 2 Trigger">
      Step 1 or 2 conditions trigger, we start evaluating step 1 from now.
    </Step>
  </Steps>
</Card>

### Maximum Step Interval [#maximum-step-interval]

Users can determine what is the maximum allowed horizontal distance (in bars) between two steps by enabling the "Maximum Step Interval" setting. This allows restricting steps separated by a large amount of bars.

If the amount of bars since a step exceed the set threshold, then the conditions sequence is reset, and we start evaluating from step 1 again.

### Highlight On Chart [#highlight-on-chart]

When a custom alert condition is set, a visual element is displayed at the bottom of the chart in order to more easily visualize when the custom alert conditions occur (displayed by default). Users can choose the location of this visual element (top, bottom of the oscillator or centered).

## Alerts Reference [#alerts-reference]

***

### Pre-Set Alerts [#pre-set-alerts]

| Alert                     | Description                                                           |
| ------------------------- | --------------------------------------------------------------------- |
| HyperWave Signal Up       | Triggers on the event of an HyperWave signal up                       |
| HyperWave OS Signal Up    | Triggers on the event of an HyperWave oversold signal up              |
| HyperWave Signal Down     | Triggers on the event of an HyperWave signal down                     |
| HyperWave OB Signal Down  | Triggers on the event of an HyperWave overbought signal down          |
| Reversal Up               | Triggers on the event of a detected reversal up signal                |
| Reversal Up +             | Triggers on the event of a detected reversal up + signal              |
| Reversal Down             | Triggers on the event of a detected reversal down signal              |
| Reversal Down +           | Triggers on the event of a detected reversal down + signal            |
| Bullish Divergence        | Triggers on the event of a detected bullish divergence                |
| Bearish Divergence        | Triggers on the event of a detected bearish divergence                |
| Strong Bullish Confluence | Triggers when a strong bullish confluence is detected                 |
| Weak Bullish Confluence   | Triggers when a weak bullish confluence is detected                   |
| Strong Bearish Confluence | Triggers when a strong bearish confluence is detected                 |
| Weak Bearish Confluence   | Triggers when a weak bearish confluence is detected                   |
| Bullish Overflow          | Triggers when bullish overflow is detected                            |
| Bearish Overflow          | Triggers when bearish overflow is detected                            |
| Custom Alert Condition    | Triggers on the event of a user set custom alert condition being true |

### Any alert() Function Call [#any-alert-function-call-1]

| Alert                | Description                                                           | Default                                                                    |
| -------------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| HWO Signal Up        | Triggers on the event of an HyperWave signal up                       | <Exclamation tooltip="Hyper Wave oscillator upward signal" />              |
| Oversold             | Triggers on the event of an HyperWave oversold signal up              | <Exclamation tooltip="Oversold Hyper Wave oscillator upward signal" />     |
| HWO Signal Down      | Triggers on the event of an HyperWave signal down                     | <Exclamation tooltip="Hyper Wave oscillator downward signal" />            |
| Overbought           | Triggers on the event of an HyperWave overbought signal down          | <Exclamation tooltip="Overbought Hyper Wave oscillator downward signal" /> |
| Reversal Up          | Triggers on the event of a detected reversal up signal                | <Exclamation tooltip="Bullish reversal" />                                 |
| +                    | Triggers on the event of a detected reversal up + signal              | <Exclamation tooltip="Bullish reversal +" />                               |
| Reversal Down        | Triggers on the event of a detected reversal down signal              | <Exclamation tooltip="Bearish reversal" />                                 |
| +                    | Triggers on the event of a detected reversal down + signal            | <Exclamation tooltip="Bearish reversal +" />                               |
| Bullish Overflow     | Triggers when bullish overflow is detected                            | <Exclamation tooltip="Bullish overflow" />                                 |
| Bearish Overflow     | Triggers when bearish overflow is detected                            | <Exclamation tooltip="Bearish overflow" />                                 |
| Bullish Divergence   | Triggers on the event of a detected bullish divergence                | <Exclamation tooltip="Bullish divergence" />                               |
| Bearish Divergence   | Triggers on the event of a detected bearish divergence                | <Exclamation tooltip="Bearish divergence" />                               |
| Bullish Confluence   | Triggers when a weak bullish confluence is detected                   | <Exclamation tooltip="Weak bullish confluence" />                          |
| Strong               | Triggers when a strong bullish confluence is detected                 | <Exclamation tooltip="Strong bullish confluence" />                        |
| Bearish Confluence   | Triggers when a weak bearish confluence is detected                   | <Exclamation tooltip="Weak bearish confluence" />                          |
| Strong               | Triggers when a strong bearish confluence is detected                 | <Exclamation tooltip="Strong bearish confluence" />                        |
| Custom Alert Creator | Triggers on the event of a user set custom alert condition being true | <Exclamation tooltip="Custom step condition triggered" />                  |


# Confluence (/platform/algos/oscillator-matrix/confluence)



<div align="center">
  ![](/images/lux-oscillator/confluence/settings.png)
</div>

Confluence is an important aspect when dealing with multiple indicators. In this toolkit two features allows the users to easily assess if the confluence between each toolkit element in high or low.

## Confluence Zones [#confluence-zones]

![](/images/lux-oscillator/confluence/zones.png)

Users can see on the oscillators pane one upper (above 100) and lower (under 0) zone, measuring the bullish/bearish confluence between the Hyper Wave and Money Flow oscillators.

When both the Hyper Wave and Money Flow oscillators are indicating an uptrend the upper zone is colored with a darker green. If only one of the oscillators is indicating an uptrend then the confluence zone will have a brighter green. This logic applies to the lower zone in the case of a downtrend.

<Note>
  Confluence zones have an associated numerical that can be used in external scripts using the output "Upper Confluence Value" for the upper confluence zone and "Lower Confluence Value" for the lower confluence zone, returned values include:

  * 2: Strong confluence
  * 1: Weak confluence
  * 0: No confluence
</Note>

## Confluence Meter [#confluence-meter]

![](/images/lux-oscillator/confluence/meter.png)

The confluence meter quantifies the amount of confluence between each indication returned by the oscillator, elements taken into account are:

* Trend indicated by the Hyper Wave relative to its signal line
* Trend indicated by the Hyper Wave
* Trend indicated by the Money Flow
* Overflow
* Divergences

The meter giving a higher measurement would be indicative of a more significant bullish confluence, that is most elements are indicative of a bullish trend, while a lower measurement would be indicative of a more significant bearish confluence.

<Note>
  The confluence meter had an associated numerical that can be used in external scripts using the output "Confluence Meter Value"
</Note>


# HyperWave (/platform/algos/oscillator-matrix/hyperwave)



<div align="center">
  ![](/images/lux-oscillator/hyper-wave/settings.png)
</div>

The HyperWave is a normalized adaptive oscillator aiming to precisely reflect price trends without returning a high amount of noise.

It is the primary oscillator used to detect trends/reversals within the LuxAlgo Oscillator Matrix®.

This oscillator is directly controlled by the **Main Length** setting, with higher values returning a smoother result reflecting longer term trends.

<Note>
  The HyperWave and every associated components (Signal line, turning points, divergences) can be disabled by toggling off "Show HyperWave"
</Note>

## Signal Line [#signal-line]

![](/images/lux-oscillator/hyper-wave/signal.png)

The Hyper Wave includes a signal line allowing to reactively detect trend directions. The area between the Hyper Wave and its signal line is displayed to easily assess the current trend direction, with a color determined by the position of the Hyper Wave relative to its signal line.

Users can use two different algorithms for computing the signal line:

* SMA : A simple moving average of the Hyper Wave is used as signal line.
* TS : A trailing stop of the Hyper Wave is used as signal line. This algorithm makes the signal line less sensitive to noisy variations of the Hyper Wave.

| SMA                                            | TS                                            |
| ---------------------------------------------- | --------------------------------------------- |
| ![](/images/lux-oscillator/hyper-wave/sma.png) | ![](/images/lux-oscillator/hyper-wave/ts.png) |

The users can select the length of the signal line using the numerical input at the right of the **Signal** drop-down menu.

<Tip>
  It can be more interesting to use a higher signal line length when the **Main Length** setting is high.
</Tip>

## Turning Points [#turning-points]

![](/images/lux-oscillator/hyper-wave/turning-points.png)

Crosses made between the HyperWave and its signal line are highlighted by dots on the oscillators pane, these are **turning points**.

By default, a Hyper Wave crossing over the signal line is highlighted in green while a Hyper Wave crossing under the signal line is highlighted in red.

### Overbought/Oversold Turning Points [#overboughtoversold-turning-points]

The displayed turning points exist in different sizes, with bigger dots occurring when the crosses are made within an overbought/oversold area.

* Oversold Turning Point : Occurs when the Hyper Wave cross over its signal line while the Hyper Wave is under 20
* Overbought Turning Point : Occurs when the Hyper Wave cross under its signal line while the Hyper Wave is over 80

## Real Time Divergences [#real-time-divergences]

<div align="center">
  ![](/images/lux-oscillator/hyper-wave/divergences-settings.png)
</div>

Divergences between the price and the HyperWave oscillator are detected in real-time. The users can return longer term divergences by decreasing the &#x2A;*Divergence Sensitivity %**, with lower values of this setting making divergence detection more sensitive to noisy variations of the Hyper Wave.

![](/images/lux-oscillator/hyper-wave/divergences.png)

Divergences are highlighted by a blue line for bullish divergences and by a red line for bearish divergences.

### Divergences On Price Chart [#divergences-on-price-chart]

In order to reference the price points returning the divergence with the HyperWave, divergences lines are directly displayed on the user price chart thanks to "Show Divergences On Chart".

It is important to note that, unlike the divergence lines that appear in the oscillator pane, the divergence lines on the user chart do not appear exactly where the divergences are detected; rather, they are set retrospectively.

<Note>
  Because the amount of lines is limited to a maximum of 500, disabling "Show Divergences On Chart" can increase the amount of historical divergences highlighted on the oscillator pane.
</Note>


# Introduction (/platform/algos/oscillator-matrix/introduction)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/EyDeWIsduhs"
    frameBorder="0"
  />
</div>

Oscillator Matrix® is an all-in-one indicator that incorporates 6+ unique components designed for interoperability & confluence with one another to provide a powerful trend following & reversal detection experience.

Users can create various ways to utilize the indicator's features together such as looking for excess money flowing into the market alongside strong reversal signals appearing, or getting real-time divergence signals alongside a lack of money flowing into the market to predict upcoming trends.

This indicator is described as an 'Oscillator Matrix' as it's made up of many different components that can create a systematic approach to analyzing markets alone, however, the indicator also can serve as a great secondary piece to a user's primary analysis with or without our other LuxAlgo Premium indicators.

## Features [#features]

Oscillator Matrix® toolkit includes the following features:

<CardGroup cols="2">
  <Card title="HyperWave" icon="wave-pulse" href="/platform/algos/oscillator-matrix/hyperwave" />

  <Card title="Smart Money Flow" icon="chart-area" href="/platform/algos/oscillator-matrix/smart-money-flow" />

  <Card title="Reversal Signals" icon="sort" href="/platform/algos/oscillator-matrix/reversal-signals" />

  <Card title="Confluence" icon="infinity" href="/platform/algos/oscillator-matrix/confluence" />
</CardGroup>

## Alerts [#alerts]

Oscillator Matrix® can alert the user for multiple conditions, learn more about the provided alert conditions/setups here:

<Card title="Alerts" icon="bell" href="/platform/algos/oscillator-matrix/alerts" />

## Settings Reference [#settings-reference]

Oscillator Matrix® includes various settings affecting the appearance and behavior of the toolkit, most of which are specific to the feature they affect.

Users can access the list of user settings and their descriptions in the following page:

<Card title="Settings Reference" icon="gear" href="/platform/algos/oscillator-matrix/settings" />


# Reversal Signals (/platform/algos/oscillator-matrix/reversal-signals)



<div align="center">
  ![](/images/lux-oscillator/reversal-signals/settings.png)
</div>

Oscillator Matrix® comes with a complete trend reversals detection system, allowing users to timely assess when a trend is changing direction.

## Classification [#classification]

![](/images/lux-oscillator/reversal-signals/reversals.png)

Different kind of reversals are returned on the oscillators pane, minor (-) and major (+) reversals signals.

Minor reversals are frequent signals often indicative of shorter term reversals, such as the top/bottom of an impulse (thus announcing a retracement). Minor reversals are highlighted by circles.

Major reversals can occur later in a trend, and can be indicative of a more significant reversals. Major reversals are highlighted by triangles.


# Settings Reference (/platform/algos/oscillator-matrix/settings)



<Info>
  To open the settings menu, place your mouse pointer over the indicator title on your chart, you will then see a list of icons appear, and click on the gear ⚙️ icon.

  You can also open the settings panel by double-clicking any element displayed by the toolkit.
</Info>

## Smart Money Flow [#smart-money-flow]

***

### Show Money Flow [#show-money-flow]

<ResponseField name="Toggle" type="default: true" />

Display Smart Money Flow oscillator

### Money Flow [#money-flow]

<ResponseField name="Numerical Input" type="default: 35" />

Money flow oscillator length, higher values will highlight longer term money flow.

### Smooth [#smooth]

<ResponseField name="Numerical Input" type="default: 6" />

Money flow oscillator smoothing amount, higher values will return a smoother Money Flow oscillator.

## HyerWave [#hyerwave]

***

### Show HyerWave [#show-hyerwave]

<ResponseField name="Toggle" type="default: true" />

Display HyperWave oscillator

### HyperWave Length [#hyperwave-length]

<ResponseField name="Numerical Input" type="default: 7" />

HyperWave length setting. Higher values will highlight longer term variations from the HyperWave oscillator.

### Signal [#signal]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="SMA" />

    <ResponseField name="Trailing Stop" />
  </Expandable>
</ResponseField>

Type of signal line displayed alongside the HyperWave.

<ResponseField name="Numerical Input" type="default: 3" />

Length of the signal method applied to the HyperWave oscillator, higher values will return longer term signals returned from the HyperWave.

### Colors [#colors]

<ResponseField name="Numerical Input" type="default: 80" />

Transparency of the area between the HyperWave and its signal plot.

## HyperWave Divergences [#hyperwave-divergences]

***

### Divergences Sensitivity % [#divergences-sensitivity-]

<ResponseField name="Numerical Input" type="default: 10" />

Sensitivity of the divergences detection algorithm, with lower values will return fewer and longer-term divergences.

### Show Divergences [#show-divergences]

<ResponseField name="Toggle" type="default: true" />

Display divergences between the price and HyperWave oscillator on the oscillator pane.

### Show Divergences On Chart [#show-divergences-on-chart]

<ResponseField name="Toggle" type="default: true" />

Display divergences between the price and HyperWave oscillator on the price chart.

## Reversals [#reversals]

***

### Reversal Factor [#reversal-factor]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />
  </Expandable>
</ResponseField>

Controls the frequency at which reversals are detected by the HyperWave, with higher values returning less frequent and longer term potential reversals.

### Show Reversals [#show-reversals]

<ResponseField name="Toggle" type="default: true" />

Display reversal signals at the top/bottom of the indicator pane.

## Confluence [#confluence]

***

### Upper Confluence [#upper-confluence]

<ResponseField name="Toggle" type="default: true" />

Display upper confluence area.

### Lower Confluence [#lower-confluence]

<ResponseField name="Toggle" type="default: true" />

Display lower confluence area.

### Show Confluence Meter [#show-confluence-meter]

<ResponseField name="Toggle" type="default: false" />

Display confluence meter.

### Meter Width [#meter-width]

<ResponseField name="Numerical" type="default: 3" />

Width of the confluence meter.

## Custom Alert Creator [#custom-alert-creator]

***

### Money Flow [#money-flow-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Money Flow condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the current Money Flow value with the inline numerical input.

<ResponseField name="Numerical Input" type="default: 50" />

Value used to compare the Money Flow with.

#### Step [#step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Money Flow condition step in the custom alert creator sequence.

### Overflow [#overflow]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Overflow condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Equal" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the current Overflow value with the inline numerical input.

<ResponseField name="Numerical Input" type="default: 50" />

Value used to compare the Overflow with.

#### Step [#step-1]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Overflow condition step in the custom alert creator sequence.

### HyperWave [#hyperwave]

<ResponseField name="Toggle" type="default: false" />

Enable the selected HyperWave condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Any" />

    <ResponseField name="Any Bullish" />

    <ResponseField name="Regular Bullish" />

    <ResponseField name="Oversold Bullish" />

    <ResponseField name="Any Bearish" />

    <ResponseField name="Regular Bearish" />

    <ResponseField name="Overbought Bearish" />

    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />
  </Expandable>
</ResponseField>

HyperWave conditions. Includes comparison operators used to compare the current HyperWave value with the inline numerical input.

<ResponseField name="Numerical Input" type="default: 50" />

Value used to compare the HyperWave with if a comparison operator is selected in the dropdown conditions.

#### Step [#step-2]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

HyperWave condition step in the custom alert creator sequence.

### Reversals [#reversals-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Reversal condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Any Reversal" />

    <ResponseField name="Any Reversal Up" />

    <ResponseField name="Reversal Up -" />

    <ResponseField name="Reversal Up +" />

    <ResponseField name="Any Reversal Down" />

    <ResponseField name="Reversal Down -" />

    <ResponseField name="Reversal Down +" />
  </Expandable>
</ResponseField>

Reversal conditions.

#### Step [#step-3]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Reversal condition step in the custom alert creator sequence.

### Divergences [#divergences]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Divergences condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Any" />

    <ResponseField name="Bullish" />

    <ResponseField name="Bearish" />
  </Expandable>
</ResponseField>

Divergences conditions.

#### Step [#step-4]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Divergences condition step in the custom alert creator sequence.

### Confluence [#confluence-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Confluence condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Strong Bullish" />

    <ResponseField name="Weak Bullish" />

    <ResponseField name="Strong Bearish" />

    <ResponseField name="Weak Bearish" />
  </Expandable>
</ResponseField>

Confluence conditions.

#### Step [#step-5]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Confluence condition step in the custom alert creator sequence.

### Confluence Meter [#confluence-meter]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Confluence Meter condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Equal" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the current Confluence Meter value with the inline numerical input.

<ResponseField name="Numerical Input" type="default: 50" />

Value used to compare the Confluence Meter with.

#### Confluence Meter Step [#confluence-meter-step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Confluence Meter condition step in the custom alert creator sequence.

### External Condition (1) [#external-condition-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected External condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

First source input.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />

    <ResponseField name="Equal" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the two selected sources.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

Second source input.

#### External Step (1) [#external-step-1]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

First external source condition step in the custom alert creator sequence.

### External Condition (2) [#external-condition-2]

<ResponseField name="Toggle" type="default: false" />

Enable the selected External condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

First source input.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />

    <ResponseField name="Equal" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the two selected sources.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

Second source input.

#### External Step (2) [#external-step-2]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Second external source condition step in the custom alert creator sequence.

### External Condition (3) [#external-condition-3]

<ResponseField name="Toggle" type="default: false" />

Enable the selected External condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

Source input.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />

    <ResponseField name="Equal" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the selected source to the selected numerical value.

<ResponseField name="Numerical Input" type="default:0" />

Numerical input.

#### External Step (3) [#external-step-3]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Third external source condition step in the custom alert creator sequence.

### Invalidation Behavior [#invalidation-behavior]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="None">
      No invalidation behavior.
    </ResponseField>

    <ResponseField name="Invalidate On Step 1">
      The "Invalidate On Step 1" behavior allows to reset an incomplete sequence of conditions when the condition on step 1 trigger. This prevents the first step condition from happening in between other steps of the condition sequence.

      This behavior is useful when the first step of our sequence of conditions needs to never be repeated during the sequence.

      <Card title="Example">
        Let's take an example where we use 3 regular steps.

        <Steps>
          <Step title="Step 1">
            Step 1 condition trigger, we will now evaluate step 2 from now on.
          </Step>

          <Step title="Step 2">
            Step 2 condition trigger, we will now evaluate step 3 from now on.
          </Step>

          <Step title="Step 1 Trigger">
            Step 1 condition trigger, we start evaluating step 2 from now.
          </Step>
        </Steps>
      </Card>
    </ResponseField>

    <ResponseField name="Invalidate On Any Repeated Step">
      The "Invalidate On Any Repeated Step" behavior allows to reset an incomplete sequence of conditions when a step is triggered such that it does not respect the user-set order of conditions.

      This behavior is useful when we want a perfectly ordered sequence of conditions to complete, without any step repeating itself.

      <Card title="Example">
        Let's take an example where we use 3 regular steps.

        <Steps>
          <Step title="Step 1">
            Step 1 condition trigger, we will now evaluate step 2 from now on.
          </Step>

          <Step title="Step 2">
            Step 2 condition trigger, we will now evaluate step 3 from now on.
          </Step>

          <Step title="Step 1 or 2 Trigger">
            Step 1 or 2 conditions trigger, we start evaluating step 1 from now.
          </Step>
        </Steps>
      </Card>
    </ResponseField>
  </Expandable>
</ResponseField>

Invalidation behaviors allows adding more restrictions to a sequence of conditions, users can use two different invalidation behaviors described below.

### Maximum Step Interval [#maximum-step-interval]

<ResponseField name="Numerical Input" type="default:10" />

Maximum allowed horizontal distance (in bars) between two steps.

If the amount of bars since a step exceed the selected threshold, then the conditions sequence is reset, and we start evaluating from step 1 again.

### Highlight Custom Alert Triggers [#highlight-custom-alert-triggers]

<ResponseField name="Toggle" type="default:true" />

When a custom alert condition is set, a visual element is displayed at the bottom of the chart in order to more easily visualize when the custom alert conditions occurs (displayed by default).

## Calculation [#calculation]

***

### Calculated Bars [#calculated-bars]

<ResponseField name="Numerical" type="default:10000" />

Amount of most recent bars used for the calculation of the toolkit. Lower values allow returning faster computation times.


# Smart Money Flow (/platform/algos/oscillator-matrix/smart-money-flow)



<div align="center">
  ![](/images/lux-oscillator/smart-money-flow/settings.png)
</div>

Money Flow and liquidity are core concepts with the Oscillator Matrix®. Various elements centered around this concept are provided to detect longer term trends, assess market participants behaviours, and detect reversals.

<Note>
  The Money Flow and every associated components (overflow, thresholds) can be disabled by toggling off "Show Money Flow"
</Note>

## Money Flow [#money-flow]

![](/images/lux-oscillator/smart-money-flow/money-flow.png)

The toolkit include one money flow indicator that aims to detect trends based on market participants activity.

Compared to the Hyper Wave the Money Flow oscillator can return longer term indications and be used to support shorter term indications.

## Overflow [#overflow]

![](/images/lux-oscillator/smart-money-flow/overflow.png)

Overflow occurs when excessive liquidity relative to a trend length enters the market, this can highlight late trend following participants entering the market, and can announce potential reversals.

### Money Flow Thresholds [#money-flow-thresholds]

![](/images/lux-oscillator/smart-money-flow/thresholds.png)

Users can gauge market activity by using the provided Money Flow thresholds, two extremities displayed above/under the central 50 levels.

When the Money Flow oscillator is above the upper threshold there is significant one-sided bullish activity on the market. When the Money Flow oscillator is under the lower threshold there is significant one-sided bearish activity on the market.

Money flow fluctuating within the thresholds can highlight a more balanced market.


# Alerts (/platform/algos/price-action-concepts/alerts)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/olKQq5XR5S0"
    frameBorder="0"
  />
</div>

The Price Action Concepts® can alert users for multiple events relevant to the existing features in the toolkit. To learn more about how to set alerts in general see [how to create alerts on tradingview](/platform/algos/tradingview-alerts).

<Note>
  In order for an alert to work in the toolkit make sure the related feature is enabled
</Note>

## Any Alert() Function Call [#any-alert-function-call]

Users can create a single alert for multiple conditions using the *any alert() function call* alert condition. Conditions users want to be alerted for are grouped by the type of features they affect (structures, volumetric order blocks, imbalances...etc) and are located at the bottom of the toolkit settings.

Once conditions are toggled on users only need to set an alert using the *any alert() function call* as condition.

![](/images/lux-pa/alerts/set.png)

### Any Alert() Function Call Message [#any-alert-function-call-message]

Users can set a custom message that will be used when an any alert() function call condition is triggered in the message field below the list of conditions. Users can use placeholders to customize messages and return useful information, the following placeholders are supported:

#### Symbol Placeholders [#symbol-placeholders]

| Placeholder  | Description                                             | Example                         |
| ------------ | ------------------------------------------------------- | ------------------------------- |
| `{default}`  | Default message associated with the trigerred condition | `"Mitigated Bullish Imbalance"` |
| `{ticker}`   | Symbol ticker without the exchange prefix               | `"MSFT"`                        |
| `{exchange}` | Symbol exchange/data source                             | `"COINBASE"`                    |
| `{sector}`   | Sector of the symbol, `na` if the symbol has no sector  | `"Technology Services"`         |
| `{market}`   | Symbol market type                                      | `"Stocks"`                      |

#### Time Placeholders [#time-placeholders]

| Placeholder | Description                                                                            | Example           |
| ----------- | -------------------------------------------------------------------------------------- | ----------------- |
| `{tf}`      | Chart timeframe, includes "S" for seconds, "D" for days, "W" for weeks, "M" for months | `"15"`            |
| `{time}`    | Unix time in milliseconds in exchange timezone                                         | `"1705331385158"` |
| `{year}`    | Current year in exchange timezone                                                      | `"2024"`          |
| `{month}`   | Current month in exchange timezone                                                     | `"7"`             |
| `{day}`     | Current day of the month in exchange timezone                                          | `"21"`            |

#### Data Placeholders [#data-placeholders]

| Placeholder        | Description                                   | Example    |
| ------------------ | --------------------------------------------- | ---------- |
| `{open}`           | Opening price                                 | `"1.2145"` |
| `{high}`           | High price                                    | `"1.2357"` |
| `{low}`            | Low price                                     | `"1.1983"` |
| `{close}`          | Closing price                                 | `"1.0569"` |
| `{volume}`         | Current volume                                | `"45216"`  |
| `{ob_buy_volume}`  | Most recent order block internal buy volume   | `"560"`    |
| `{ob_sell_volume}` | Most recent order block internal sell volume  | `"480"`    |
| `{ob_volume}`      | Most recent order block total internal volume | `"1040"`   |

### Format as JSON [#format-as-json]

Use the following message template to receive "any alert() function call" messages in the JSON format:

```json
{
  "alert": "{default}",
  "ticker": "{ticker}",
  "tf": "{tf}",
  "ohlcv":{
    "open": {open},
    "high": {high},
    "low": {low},
    "close": {close},
    "volume": {volume},
    "order block buy volume": {ob_buy_volume},
    "order block sell volume": {ob_sell_volume},
    "order block total volume": {ob_volume}
  },
  "bartime": {time}
}
```

## Custom Alert Creator [#custom-alert-creator]

![](/images/lux-pa/alerts/custom.png)

Users wishing to construct more complex alerts using features within the toolkit and/or external indicators can use the **Custom Alert Creator**.

The custom alert creator allows alerting when various user-set conditions are met or when a sequence of conditions is complete.

<Tip>
  A user wishing to be alerted when any internal market structure occurs within 1AM and 5AM of the symbol timezone could do it as follows:

  ![](/images/lux-pa/alerts/custom_example.png)
</Tip>

A custom alert can be triggered as an *any alert() function call* if the setting is selected or as a regular alert when selected in the Tradingview alert creator menu.

### Steps [#steps]

Steps allow creating a sequence of conditions, which when met trigger an alert. When a condition with a step equal to 1 is met the next condition associated with step 2 will be evaluated (if enabled) and so on for higher steps (if any).

Conditions with the same associated step will require both conditions to be true in order for the condition associated with a higher step (if any) to be evaluated. This allows certain conditions to act as filters.

<Tip>
  A user wishing to be alerted when the price enters a bullish imbalance after any internal bullish market structure could do it as follows:

  ![](/images/lux-pa/alerts/step_example.png)

  *Make sure internal market structures and imbalances are enabled*.
</Tip>

<Warning>
  Make sure steps are set such that they form a linear sequence. For example, if the lowest step is 2 or if the set steps are only 1 and 3 an error will be returned.
</Warning>

#### OR Step Operator [#or-step-operator]

The "OR" step operator can be used to get alerted for any additional condition using "OR" as a step, in addition to other set custom alert conditions.

Conditions using the "OR" step can be highlighted using a different color, which can be set from the Custom Alert Creator settings. Users can choose the location of this visual element (top or bottom of the chart).

#### All Step Operator [#all-step-operator]

The "All" step operator can be used to filter any set "Step" condition, including "OR" and "Invalidate".

This step operator can be useful if all the steps in a sequence needs to obey a specific rule.

<Tip>
  If a user has 2 steps set, and requires both of these to occur at a specific time interval such as 9am to 1pm, then we can use a "Session" condition using the "All" step, requiring our step 1 and step 2 to be occur between 9am and 1pm.
</Tip>

#### Invalidate Step [#invalidate-step]

The "Invalidate" step allows to set a condition as an "invalidation condition". When this condition is triggered while a sequence of conditions is incomplete, the sequence will restart at step 1.

If multiple steps are set as "Invalidate" any of the conditions being true will reset the sequence of conditions, as such not all of them are required to be true for the sequence of conditions to reset.

<Card title="Example">
  Let's take an example where we use 2 regular steps and one "Invalidate" step. The alert will trigger when step 2 trigger after step 1 has been triggered.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Invalidation Step">
      Invalidation step condition trigger, we reset the sequence and evaluate step 1 just after.
    </Step>
  </Steps>
</Card>

### Invalidation Behaviors [#invalidation-behaviors]

Invalidation behaviors allows adding more restrictions to a sequence of conditions, users can use two different invalidation behaviors described below:

#### Invalidate On Step 1 [#invalidate-on-step-1]

The "Invalidate On Step 1" behavior allows to reset an incomplete sequence of conditions when the condition on step 1 trigger. This prevents the first step condition from happening in between other steps of the condition sequence.

This behavior is useful when the first step of our sequence of conditions needs to never be repeated during the sequence.

<Card title="Example">
  Let's take an example where we use 3 regular steps.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Step 2">
      Step 2 condition trigger, we will now evaluate step 3 from now on.
    </Step>

    <Step title="Step 1 Trigger">
      Step 1 condition trigger, we start evaluating step 2 from now.
    </Step>
  </Steps>
</Card>

#### Invalidate On Any Repeated Step [#invalidate-on-any-repeated-step]

The "Invalidate On Any Repeated Step" behavior allows to reset an incomplete sequence of conditions when a step is triggered such that it does not respect the set order of conditions.

This behavior is useful when we want a perfectly ordered sequence of conditions to complete, without any step repeating itself.

<Card title="Example">
  Let's take an example where we use 3 regular steps.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Step 2">
      Step 2 condition trigger, we will now evaluate step 3 from now on.
    </Step>

    <Step title="Step 1 or 2 Trigger">
      Step 1 or 2 conditions trigger, we start evaluating step 1 from now.
    </Step>
  </Steps>
</Card>

### Maximum Step Interval [#maximum-step-interval]

Users can determine what is the maximum allowed horizontal distance (in bars) between two steps by enabling the "Maximum Step Interval" setting. This allows restricting steps separated by a large amount of bars.

If the amount of bars since a step exceed the set threshold, then the conditions sequence is reset, and we start evaluating from step 1 again.

### Highlight On Chart [#highlight-on-chart]

When a custom alert condition is set, a visual element is displayed at the bottom of the chart in order to more easily visualize when the custom alert condition occurs (displayed by default).

## Alerts Reference [#alerts-reference]

***

### Pre-Set Alerts [#pre-set-alerts]

| Alert                       | Description                                                                        |
| --------------------------- | ---------------------------------------------------------------------------------- |
| Bullish I-BOS               | Triggers on the event of an internal bullish break of structure (BOS)              |
| Bullish I-CHOCH             | Triggers on the event of an internal bullish change of character (CHOCH)           |
| Bullish I-CHOCH+            | Triggers on the event of a Supported internal bullish change of character (CHOCH+) |
| Bearish I-BOS               | Triggers on the event of an internal bearish break of structure (BOS)              |
| Bearish I-CHOCH             | Triggers on the event of an internal bearish change of character (CHOCH)           |
| Bearish I-CHOCH+            | Triggers on the event of a Supported internal bearish change of character (CHOCH+) |
| Bullish S-BOS               | Triggers on the event of a swing bullish break of structure (BOS)                  |
| Bullish S-CHOCH             | Triggers on the event of a swing bullish change of character (CHOCH)               |
| Bullish S-CHOCH+            | Triggers on the event of a Supported swing bullish change of character (CHOCH+)    |
| Bearish S-BOS               | Triggers on the event of a swing bearish break of structure (BOS)                  |
| Bearish S-CHOCH             | Triggers on the event of a swing bearish change of character (CHOCH)               |
| Bearish S-CHOCH+            | Triggers on the event of a Supported swing bearish change of character (CHOCH+)    |
| Equal Highs                 | Triggers on the event of an equal high (EQH)                                       |
| Equal Lows                  | Triggers on the event of an equal low (EQL)                                        |
| Bullish OB Created          | Triggers when a new bullish order block is created                                 |
| Bearish OB Created          | Triggers when a new bearish order block is created                                 |
| Bullish OB Mitigated        | Triggers when a bullish order block (visible or not) is mitigated                  |
| Bearish OB Mitigated        | Triggers when a bearish order block (visible or not) is mitigated                  |
| Bullish Breaker             | Triggers when a bullish order block (visible or not) becomes a breaker block       |
| Bearish Breaker             | Triggers when a bearish order block (visible or not) becomes a breaker block       |
| Within Bullish OB           | Triggers when the price is located within a bullish order block (visible or not)   |
| Within Bearish OB           | Triggers when the price is located within a bearish order block (visible or not)   |
| Bullish OB Entered          | Triggers when the price enters a bullish order block (visible or not)              |
| Bearish OB Entered          | Triggers when the price enters a bearish order block (visible or not)              |
| Bullish Imbalance           | Triggers when a new bullish imbalance is detected                                  |
| Bearish Imbalance           | Triggers when a new bearish imbalance is detected                                  |
| Bullish Imbalance Mitigated | Triggers when a bullish imbalance (visible or not) is mitigated                    |
| Bearish Imbalance Mitigated | Triggers when a bearish imbalance (visible or not) is mitigated                    |
| Broken Uptrendline          | Triggers when an upward trendline is broken                                        |
| Broken Downtrendline        | Triggers when a downward trendline is broken                                       |
| Bullish Grab                | Triggers when a new bullish liquidity grab is detected                             |
| Bearish Grab                | Triggers when a new bearish liquidity grab is detected                             |
| Custom Alert Condition      | Triggers on the event of a user set custom alert condition being true              |

### Any alert() Function Call [#any-alert-function-call-1]

***

#### Custom Alert Creator [#custom-alert-creator-1]

| Alert                                | Description                                                           | Default                                                   |
| ------------------------------------ | --------------------------------------------------------------------- | --------------------------------------------------------- |
| Trigger as any alert() function call | Triggers on the event of a user set custom alert condition being true | <Exclamation tooltip="Custom step condition triggered" /> |

#### Market Structures [#market-structures]

| Alert            | Description                                                                        | Default                                                  |
| ---------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------- |
| Bullish I-BOS    | Triggers on the event of an internal bullish break of structure (BOS)              | <Exclamation tooltip="Internal bullish BOS formed" />    |
| Swing BOS        | Triggers on the event of a swing bullish break of structure (BOS)                  | <Exclamation tooltip="Bullish BOS formed" />             |
| Bearish I-BOS    | Triggers on the event of an internal bearish break of structure (BOS)              | <Exclamation tooltip="Internal bearish BOS formed" />    |
| Swing BOS        | Triggers on the event of a swing bearish break of structure (BOS)                  | <Exclamation tooltip="Bearish BOS formed" />             |
| Bullish I-CHOCH  | Triggers on the event of an internal bullish change of character (CHOCH)           | <Exclamation tooltip="Internal bullish CHoCH formed" />  |
| Swing CHOCH      | Triggers on the event of a swing bullish change of character (CHOCH)               | <Exclamation tooltip="Bullish CHoCH formed" />           |
| Bullish I-CHOCH+ | Triggers on the event of a Supported internal bullish change of character (CHOCH+) | <Exclamation tooltip="Internal bullish CHoCH+ formed" /> |
| Swing CHOCH+     | Triggers on the event of a Supported swing bullish change of character (CHOCH+)    | <Exclamation tooltip="Bullish CHoCH+ formed" />          |
| Bearish I-CHOCH  | Triggers on the event of an internal bearish change of character (CHOCH)           | <Exclamation tooltip="Internal bearish CHoCH formed" />  |
| Swing CHOCH      | Triggers on the event of a swing bearish change of character (CHOCH)               | <Exclamation tooltip="Bearish CHoCH formed" />           |
| Bearish I-CHOCH+ | Triggers on the event of a Supported internal bearish change of character (CHOCH+) | <Exclamation tooltip="Internal bearish CHoCH+ formed" /> |
| Swing CHOCH+     | Triggers on the event of a Supported swing bearish change of character (CHOCH+)    | <Exclamation tooltip="Bearish CHoCH+ formed" />          |

#### Volumetric Order Blocks [#volumetric-order-blocks]

| Alert                | Description                                                                      | Default                                                               |
| -------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| Bullish OB           | Triggers when a new bullish order block is created                               | <Exclamation tooltip="New Bullish Order Block [top, bottom]" />       |
| Bearish OB           | Triggers when a new bearish order block is created                               | <Exclamation tooltip="New Bearish Order Block [top, bottom]" />       |
| Bullish OB Mitigated | Triggers when a bullish order block (visible or not) is mitigated                | <Exclamation tooltip="Bullish Order Block Mitigated [top, bottom]" /> |
| Bearish OB Mitigated | Triggers when a bearish order block (visible or not) is mitigated                | <Exclamation tooltip="Bearish Order Block Mitigated [top, bottom]" /> |
| Bullish Breaker      | Triggers when a bullish order block (visible or not) becomes a breaker block     | <Exclamation tooltip="New Bullish Breaker Block [top, bottom]" />     |
| Bearish Breaker      | Triggers when a bearish order block (visible or not) becomes a breaker block     | <Exclamation tooltip="New Bearish Breaker Block [top, bottom]" />     |
| Within Bullish OB    | Triggers when the price is located within a bullish order block (visible or not) | <Exclamation tooltip="Within Bullish Block [top, bottom]" />          |
| Within Bearish OB    | Triggers when the price is located within a bearish order block (visible or not) | <Exclamation tooltip="Within Bearish Block [top, bottom]" />          |
| Bullish OB Entered   | Triggers when the price enters a bullish order block (visible or not)            | <Exclamation tooltip="Bullish Block Entered [top, bottom]" />         |
| Bearish OB Entered   | Triggers when the price enters a bearish order block (visible or not)            | <Exclamation tooltip="Bearish Block Entered [top, bottom]" />         |
| Bullish OB Exited    | Triggers when the price exits a bullish order block                              | <Exclamation tooltip="Exit Bullish Block [top, bottom]" />            |
| Bearish OB Exited    | Triggers when the price exits a bearish order block                              | <Exclamation tooltip="Exit Bearish Block [top, bottom]" />            |

#### Imbalances [#imbalances]

| Alert                       | Description                                                              | Default                                               |
| --------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------- |
| Any New Imbalance           | Triggers when a bullish or bearish imbalance is identified               | <Exclamation tooltip="Any New Bullish Imbalance" />   |
| Bullish New Imbalance       | Triggers when a bullish imbalance is identified                          | <Exclamation tooltip="Bullish Imbalance" />           |
| Bearish New Imbalance       | Triggers when a bearish imbalance is identified                          | <Exclamation tooltip="Bearish Imbalance" />           |
| Any Imbalance Mitigated     | Triggers when a bullish or bearish imbalance is mitigated                | <Exclamation tooltip="Mitigated imbalance" />         |
| Bullish Imbalance Mitigated | Triggers when a bullish imbalance is mitigated                           | <Exclamation tooltip="Mitigated bullish imbalance" /> |
| Bearish Imbalance Mitigated | Triggers when a bearish imbalance is mitigated                           | <Exclamation tooltip="Mitigated bearish imbalance" /> |
| Within Any Imbalance        | Triggers when the price is located within a bullish or bearish imbalance | <Exclamation tooltip="Within imbalance" />            |
| Within Bullish Imbalance    | Triggers when the price is located within a bullish imbalance            | <Exclamation tooltip="Within bullish imbalance" />    |
| Within Bearish Imbalance    | Triggers when the price is located within a bearish imbalance            | <Exclamation tooltip="Within bearish imbalance" />    |
| Any Imbalance Entered       | Triggers when the price enters a bullish or bearish imbalance            | <Exclamation tooltip="Entered imbalance" />           |
| Bullish Imbalance Entered   | Triggers when the price enters a bullish imbalance                       | <Exclamation tooltip="Entered bullish imbalance" />   |
| Bearish Imbalance Entered   | Triggers when the price enters a bearish imbalance                       | <Exclamation tooltip="Entered bearish imbalance" />   |
| Bullish Imbalance Exited    | Triggers when the price exits a bullish imbalance                        | <Exclamation tooltip="Exit bullish imbalance" />      |
| Bearish Imbalance Exited    | Triggers when the price exits a bearish imbalance                        | <Exclamation tooltip="Exit bearish imbalance" />      |

#### Liquidity Concepts [#liquidity-concepts]

| Alert                | Description                                               | Default                                              |
| -------------------- | --------------------------------------------------------- | ---------------------------------------------------- |
| Up-Trendline Break   | Triggers when the price breaks above an upward trendline  | <Exclamation tooltip="Price broke up-trendline" />   |
| Down-Trendline Break | Triggers when the price breaks below a downward trendline | <Exclamation tooltip="Price broke down-trendline" /> |
| Detected Pattern     | Triggers when a user-set chart pattern is identified      | <Exclamation tooltip="Pattern detected" />           |
| Equal High           | Triggers when a new equal high is detected                | <Exclamation tooltip="Equal highs formed" />         |
| Equal Low            | Triggers when a new equal low is detected                 | <Exclamation tooltip="Equal lows formed" />          |
| Bullish Grab         | Triggers when a bullish liquidity grab occurs             | <Exclamation tooltip="Bullish grab" />               |
| Bearish Grab         | Triggers when a bearish liquidity grab occurs             | <Exclamation tooltip="Bearish grab" />               |


# Fibonacci Retracements (/platform/algos/price-action-concepts/fibonacci)



![](/images/lux-pa/retracements/settings.png)

Fibonacci retracements are levels displayed from two price points using Fibonacci ratios, specific numbers obtained from the Fibonacci sequence.

Users can display up to 5 customizable Fibonacci retracements from one upper and one lower extremities users can select from a wide variety of feature included in the toolkit.

## Possible Extremities [#possible-extremities]

The upper/lower extremities used to construct the Fibonacci retracements can be selected from the "Fibonacci Top" or "Fibonacci Bottom" dropdown settings, and can be obtained from the following features within the Price Action Concepts® toolkit:

<AccordionGroup>
  <Accordion title="Swing Points">
    * Internal High
    * Internal Low
    * Swing High
    * Swing Low
  </Accordion>

  <Accordion title="Premium/Discount">
    * Discount
    * Equilibrium Average
    * Premium
  </Accordion>

  <Accordion title="CHoCH/BOS">
    * Bullish I-CHoCH
    * Bearish I-CHoCH
    * Bullish I-BOS
    * Bearish I-BOS
  </Accordion>

  <Accordion title="Orderblock">
    * Highest OB Top
    * Lowest  OB Bottom
  </Accordion>

  <Accordion title="Imbalance">
    * Imbalance Top
    * Imbalance Bottom
    * Imbalance Average
  </Accordion>

  <Accordion title="Previous H/L">
    * Previous Day High
    * Previous Day Low
    * Previous Week High
    * Previous Week Low
    * Previous Month High
    * Previous Month Low
    * Previous Quarterly High
    * Previous Quarterly Low
  </Accordion>
</AccordionGroup>

Selecting "None" for "Fibonacci Top" or "Fibonacci Bottom" will hide the retracements.

## Ratios [#ratios]

![](/images/lux-pa/retracements/levels.png)

Users can control which levels are displayed as well as the ratio used for each level. Ratios are also visible to the right side of each retracement displayed.

## Anchor To Origin [#anchor-to-origin]

![](/images/lux-pa/retracements/origin.png)

By default, the levels are anchored from the most recent bar and extend 20 bars ahead. By enabling "Anchor To Origin" users can anchor retracement levels to a point of origin related to the selected extremities in "Fibonacci Top" and "Fibonacci Bottom".

Based on the selected extremity, the anchor point will be the following:

| Extremity                                | Anchor                                                   |
| ---------------------------------------- | -------------------------------------------------------- |
| Swing High                               | Swing High Location                                      |
| Swing Low                                | Swing Low Location                                       |
| Internal High                            | Internal High Location                                   |
| Internal Low                             | Internal Low Location                                    |
| Premium / Equilibrium Average / Discount | Oldest anchor between Swing High and Swing Low Locations |

<Info>
  Only the above extremities are compatible with "Anchor To Origin".
</Info>

If "Fibonacci Top" and "Fibonacci Bottom" are different, the anchor point will be the oldest location between the two.


# Imbalance Concepts (/platform/algos/price-action-concepts/imbalances)



![](/images/lux-pa/imbalances/settings.png)

Imbalances in market prices arise when there is a great disparity between supply and demand, this can lead to inefficiencies where an asset does not trade at its fair value.

It is common for the price to seek balance after an imbalance, thus returning to the price area where the imbalance occurred. These areas of imbalances can also be used as support and resistance areas.

The toolkit is able to detect the following imbalances:

* Fair Value Gaps (FVG)
* Inverse Fair Value Gaps (Inverse FVG)
* Double Fair Value Gaps (Double FVG)
* Volume Imbalance
* Opening Gap

Each method is covered in the sections below.

## Fair Value Gaps [#fair-value-gaps]

![](/images/lux-pa/imbalances/fvg.png)

Fair Value Gaps (FVG) are market imbalances formed from a sequence of three candles where the outer candle wicks fail to overlap the central candle body, the range between the wicks highlight a Fair Value Gap.

Bullish fair value gaps occur when the current price low is higher than the price high two bars ago. Bearish fair gaps occur when the current price high is lower than the price low two bars ago.

## Inverse FVG [#inverse-fvg]

![](/images/lux-pa/imbalances/ifvg.png)

Inverse fair value gaps are essentially mitigated fair value gaps, these can be used to provide retests areas.

A mitigated bullish FVG will lead to a bearish inverse FVG, where we can expect price to retrace upward and retest the area, while a mitigated bearish FVG will lead to a bullish inverse FVG, where we can expect price to retrace downward and retest the area.

<Info>
  For the sake of efficiency, inverse FVG's are always based on the mitigation of the most recent detected FVG, disregarding any previous historical FVG that might get mitigated.
</Info>

## Double FVG [#double-fvg]

![](/images/lux-pa/imbalances/bpr.png)

Double Fair Value Gaps, also called balanced price ranges occur when the areas of two Fair Value Gaps overlap. The overlapping areas highlight a new area of imbalance.

A bullish Balanced Price Range is determined by a new bullish Fair Value Gap area overlapping a previous bearish Fair Value Gap area, while a bearish Balanced Price Range is determined by a new bearish Fair Value Gap area overlapping a previous bullish Fair Value Gap area.

## Volume Imbalance [#volume-imbalance]

![](/images/lux-pa/imbalances/vi.png)

Volume Imbalances are market imbalances characterised by two adjacent candles with non overlapping candle bodies, while their wicks overlaps. These imbalances are more commonly found in stocks or second timeframes of cryptocurrencies and forex pairs.

## Opening Gap [#opening-gap]

![](/images/lux-pa/imbalances/og.png)

Opening Gaps are market imbalances characterised by two adjacent candles with non overlapping candle wicks, highlighting an empty area. These imbalances are more commonly found in stocks or second timeframes of cryptocurrencies and forex pairs.

## Mitigation Methods [#mitigation-methods]

Once price breaks a highlighted imbalance area it is said to have been "mitigated" and will automatically disappear. Within the Price Action Concepts® indicator, users can determine the condition for any imbalance to be considered mitigated trough the **Mitigation Method** setting, available options include:

|    Method   |                                                                                               Description                                                                                               |
| :---------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|  **Close**  |           Mitigates an imbalance once price close cross above the imbalance upper extremity in the case of a bearish imbalance, and under its lower extremity in case of a bullish imbalance.           |
|   **Wick**  | Mitigates an imbalance once price high cross above the imbalance upper extremity in the case of a bearish imbalance, and when price low cross under its lower extremity in case of a bullish imbalance. |
| **Average** |                                                                Mitigates an imbalance once price cross the imbalance area average level.                                                                |
|   **None**  |                                                                                  Will not remove mitigated imbalances.                                                                                  |

<Note>
  This toolkit makes use of a buffer allowing for a maximum of 200 imbalances. Any newly detected imbalance making the buffer exceed this number will cause the toolkit to discard the oldest detected imbalance.
</Note>

## Volatility Threshold [#volatility-threshold]

The volatility threshold allows filtering out less significant imbalances, with higher values preserving imbalances with a larger range.

<Note>
  The volatility threshold is determined from a volatility estimator, the threshold act as a multiplier.

  Increments of 1 will return visible results. Floating points can be used.
</Note>

## Alerts [#alerts]

Users can be alerted on the detection of a bullish or bearish imbalance, as well as when an imbalance area is mitigated.


# Introduction (/platform/algos/price-action-concepts/introduction)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/rD776uLM4P0"
    frameBorder="0"
  />
</div>

Price Action Concepts® is an exclusive exclusive toolkit of trading algorithms that automate a wide variety of concepts such as support & resistance, trend lines, patterns, and much more.

This toolkit is great for beginners or professionals alike who wish to automate price action, the essential part of your charts. Each feature included can be seen in the pages below.

## Features [#features]

<CardGroup cols="3">
  <Card title="Market Structure" icon="florin-sign" href="/platform/algos/price-action-concepts/market-structures" />

  <Card title="Volumetric Order Blocks" icon="bars-progress" href="/platform/algos/price-action-concepts/order-blocks" />

  <Card title="Liquidity Concepts" icon="droplet" href="/platform/algos/price-action-concepts/liquidity" />

  <Card title="Imbalance Concepts" icon="scale-unbalanced" href="/platform/algos/price-action-concepts/imbalances" />

  <Card title="Premium/Discount Zones" icon="distribute-spacing-vertical" href="/platform/algos/price-action-concepts/pdzones" />

  <Card title="Previous Highs/Lows" icon="bars-staggered" href="/platform/algos/price-action-concepts/previous-high-low" />
</CardGroup>

## Alerts [#alerts]

Most concepts covered in the toolkit include built-in alerts. Users can also be alerted for multiple conditions from on alert using **any alert() function call** conditions. Finally, users who wish to set complex alerts conditions can do so using our "Custom Alert Creator".

<Card title="Alerts" icon="bell" href="/platform/algos/price-action-concepts/alerts" />

## Settings Reference [#settings-reference]

Price Action Concepts® includes various settings affecting the appearance and behavior of the toolkit, most of which are specific to the feature they affect.

Users can access the list of user settings and their descriptions in the following page:

<Card title="Settings Reference" icon="gear" href="/platform/algos/price-action-concepts/settings" />


# Liquidity Concepts (/platform/algos/price-action-concepts/liquidity)



![](/images/lux-pa/liquidity/settings.png)

Price Action Concepts® covers a variety of concepts related to liquidity. Each of these concepts can be found in the **Liquidity Concepts** settings section and are described in the sections below.

## Liquidity Trendlines [#liquidity-trendlines]

![](/images/lux-pa/liquidity/trendlines.png)

Liquidity Trendlines displays linear zones constructed by taking into account the amount of liquidity at certain price levels, with zones being displayed only if market participants found liquidity in them. The returned zones can give support areas (in blue) during up-trends or resistance areas (in red) during down-trends.

Once price breaks the extremity of a zone we can expect a trend reversal.

<Note>
  Once the conditions for a trendline to be displayed are met, the indicator will display it retrospectively.
</Note>

The user can determine the amount of historical up-trendlines and down-trendlines to display on the chart to avoid clutter using the provided numerical input setting.

<Note>
  Builtins alerts for trendlines breakouts are available to the users.
</Note>

## Patterns [#patterns]

![](/images/lux-pa/liquidity/image530.png)

Price Action Concepts® includes an Automatic Pattern Detection feature able to detect various chart patterns as well as a dashboard to give users information about any detected pattern.

The Pattern Detection feature is able to detect the following patterns:

* Ascending/Descending Triangles
* Ascending/Descending Broadening Wedges
* Ascending/Descending/Symmetrical Triangles
* Double Tops/Bottoms
* Head & Shoulders
* Inverted Head & Shoulders

as well as providing general supports and resistances if any of the above patterns are not detected.

![](/images/lux-pa/liquidity/dashboard.png)

When a pattern is detected, it will be displayed on the dashboard located at the top-right of the chart. Solid lines will highlight the patterns. When no patterns are detected, dashed lines will be displayed that can be used as support and resistance.

![](/images/lux-pa/liquidity/image602.png)

Users can detect longer term chart patterns using the provided numerical drop-down menu setting, with higher values detecting longer term patterns.

<Note>
  Builtins alerts for detected patterns are available to the users.
</Note>

## Equal Highs & Lows [#equal-highs--lows]

![](/images/lux-pa/liquidity/eqhl.png)

The toolkit returns historical equal high's & low's constructed from swing points. Equal high's & low's can be indicative of reversals, and incoming market structure such as CHoCH or BOS.

Users can display longer term equal high's/low's by increasing the numerical input at the right of the toggle (3 by default).

<Note>
  Equal high's & low's requires the detected swing points to be confirmed, which takes an amount of bars equal to the value set in the numerical input. As such it is important to understand these are displayed retrospectively.
</Note>

## Liquidity Grabs [#liquidity-grabs]

![](/images/lux-pa/liquidity/grabs.png)

Liquidity Grabs highlight occurrences where trading activity took place in more liquid areas. These are highlighted by the toolkit with colored borders. A liquidity grab can be identified as a bullish grab if the activity took place in a demand area, or a bearish grab if this activity took place in a supply area.

Bullish liquidity grabs are highlighted in blue with borders ranging from the price low to candle body minimum and indicates a potential bullish reversal.

Bearish liquidity grabs are highlighted in red with borders ranging from the price high to candle body maximum and indicates a potential bearish reversal.

The occurrence of both bullish and bearish liquidity grabs at the same time can highlight market indecision's.

<Note>
  Built-in alerts for liquidity grabs are available to users within Price Action Concepts®.
</Note>


# Market Structure (/platform/algos/price-action-concepts/market-structures)



![](/images/lux-pa/structures/settings.png)

Market structure is a core component of the price action. The Price Action Concepts® toolkit automatically detects and highlights real time market structure on your chart, allowing for a clean picture of market trends without introducing traditional forms of technical analysis like moving averages.

## Type Of Market Structures [#type-of-market-structures]

![Type of market structures](/images/lux-pa/structures/type-structures.png)

Market structure allows traders to understand trend reversals as well as trend continuations in the market through two distinct identifications:

* Change of Character (CHoCH)
* Break of Structure (BOS)

These are further described in the subsections below.

### Change Of Character (CHoCH) [#change-of-character-choch]

![Type of CHoCH's](/images/lux-pa/structures/CHoCH.png)

A change of character (CHoCH), sometimes also referred as 'market structure shift' is characterized by price breaking a prior swing low during an uptrend (bullish CHoCH) or a prior swing high during a downtrend (bearish CHoCH), and thus indicates a potential market reversal.

The Price Action Concepts® toolkits  labels two distinct type of CHoCHs:

* Leading CHoCH (labelled as **CHoCH**)
* Supported CHoCH (labelled as &#x2A;*CHoCH+**)

The difference between each is based on the relative position of prior swing highs/lows. A leading CHoCH is characterized by price not having prior signs of a reversal, such as a failed higher high (lower high) during an uptrend or a failed lower low (higher low) during a downtrend.

On the other hand, a supported CHoCH (&#x2A;*CHoCH+**) is preceded by an early sign of market reversal, such as a failed higher high (lower high) during an uptrend or a failed lower low (higher low) during a downtrend.

<Note>
  Users can show allow the toolkit to show all or only specific types of CHoCH's from the drop-down menus within the **Market Structure** settings section in the toolkit.
</Note>

### Break Of Structure (BOS) [#break-of-structure-bos]

![](/images/lux-pa/structures/BOS.png)

A break of structure (BOS) can be thought of primarily as a trend continuation label. This structure is the opposite of the CHoCH, with a BOS being when price breaks a prior swing high during an uptrend (bullish BOS) thus forming a new higher high, or when price breaks a prior swing low during a downtrend (bearish BOS) thus forming a new lower low. This structure only can occur after a CHoCH.

Since BOS's indicate trend continuations, it is common to observe successive BOS's.

## Swing & Internal Structure [#swing--internal-structure]

![](/images/lux-pa/structures/structures.png)

The Price Action Concepts® toolkit offers two distinct dimensions when it comes to market structure:

* Internal Structure
* Swing Structure

Internal structure are constructed from shorter term swing high/low points, while swing structure are constructed from longer term ones. Users can select the lookback used for the swing point detection for both the internal and swing market structures construction.

Internal structures can use a lookback in range (5, 49). Swing structures can use a lookback in the range (50, 100).

The toolkit highlights internal structure with dashed lines and labels with a smaller text size.

<Note>
  The size of the labels for internal and swing structure can be changed from the **General Styling** settings section.
</Note>

## Candle Coloring [#candle-coloring]

![](/images/lux-pa/structures/coloring.png)

Users can color candles depending on the detected internal market structure. This is useful if users want to be able to determine a detected internal market structure without having labels and lines on their chart, freeing up space on the chart to use other indicators.

Candle coloring is affected by the colors settings selected by the user for the internal market structure. Candle coloring is determined as follows:

* Darker bullish color : Bullish CHoCH active
* Regular bullish color : Bullish BOS active
* Darker bearish color : Bearish CHoCH active
* Regular bearish color : Bearish BOS active

<Note>
  Users can make highlighted structure and candle colors monochrome from the **General Styling** settings by using the *monochrome* option in the **Theme** drop down menu.
</Note>

## Swing High/Low [#swing-highlow]

Users can display swing high/low by enabling the "Show Swing High/Low" setting, highlighting past points where a reversal occurred. Displayed swing points can be denoted as:

* HH (Higher high)
* HL (Higher low)
* LH (Lower high)
* LL (Lower low)

The displayed swing points are affected by the period selected for the internal market structures, with higher values returning longer term swing points.

<Warning>
  Swing points are displayed retrospectively, and as such are not detected where they are located. They should not be used for real-time applications.
</Warning>

## Strong/Weak High/Low [#strongweak-highlow]

![](/images/lux-pa/structures/strongweak.png)

Strong/Weak High/Lows highlight maximum and minimum levels made from medium term swings. A relative percentage derived between the trading volume that occurred on both swings is also highlighted, and is used to determine if a maxima/minima is strong or weak.

The displayed swing points are affected by the period selected for the internal market structures, with higher values returning longer term swing points.

## Alerts [#alerts]

Built-in alerts are available for each supported market structure, including swing structures, internal structures, and equal highs/lows.


# Volumetric Order Blocks (/platform/algos/price-action-concepts/order-blocks)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/fM-tgGv58Jk"
    frameBorder="0"
  />
</div>

Volumetric Order Blocks highlight price areas where more informed market participants accumulate orders and can be used as potential areas of support or resistance. These are automatically highlighted by the toolkit and will disappear once mitigated.

![](/images/lux-pa/order-blocks/settings.png)

These order Blocks are separated into two types, bullish Volumetric Order Blocks and bearish Volumetric Order Blocks. Bullish Volumetric Order Blocks initial locations are near swing low's and are used as a potential support. On the other hand Bearish Volumetric Order Blocks initial locations are near swing high's and are used as a potential resistance.

Users can show longer term Volumetric Order Blocks by increasing the **Length** setting in the Order Blocks section. This setting control the swing point detection lookback used to construct Volumetric Order Blocks.

<Tip>
  You can hide overlapping Volumetric Order Blocks by using the **Hide Overlap** setting, if two Volumetric Order Blocks overlap the most recent one will be conserved.
</Tip>

## Breaker Blocks [#breaker-blocks]

![](/images/lux-pa/order-blocks/breakers.png)

Breaker Blocks show previous Volumetric Order Blocks that got mitigated (broken by price). These zones can be revisited by the price and provide support/resistance areas.

Bullish breaker blocks disappear once price goes above the breaker block upper extremity, while bearish breaker blocks disappear once price goes under the breaker block lower extremity.

Users can enable the option of showing breaker blocks by enabling the "Show Breakers" setting.

<Note>
  Breaker blocks are differentiated from Volumetric Order Blocks by their non-solid background color.
</Note>

## Internal Activity & Metrics [#internal-activity--metrics]

![](/images/lux-pa/order-blocks/activity.png)

The toolkit is able to return various information regarding an order block. Internal activity highlight the bullish and bearish activity within the interval used to construct the order block, with green bars highlighting bullish activity, and red bars highlighting bearish activity.

This allows users to see if the activity associated to an order block is in accordance with its type, or if the activity is uniform. Users can utilize this to estimate when bulls or bears are potentially exhausted at certain order blocks in the market, as such it can be used to further understand if price is likely to break or respect an order block.

<Note>
  Internal activity and metrics both require volume data in order to be displayed.
</Note>

### Metrics [#metrics]

![](/images/lux-pa/order-blocks/metrics.png)

Metrics are displayed to the right of an order block near the most recent historical price bar. This information represent the accumulated volume within the interval used to construct the order block, and can be useful to determine how significant an order block is, with larger volume indicating a more significant order block.

The percentage to the right indicates how much the volume of an order block account for the total accumulated volume of all Volumetric Order Blocks displayed on the chart, this allows to quickly determine which Volumetric Order Blocks can be more interesting to look for.

<Tip>
  You can change the size of the metrics from the **OB Metrics Size** setting within the **GENERAL STYLING** settings section, available options include: *tiny*, *small* (default), *normal*, and *auto*.
</Tip>

### Processing Internal Activity Data [#processing-internal-activity-data]

![](/images/lux-pa/order-blocks/processing.png)

Users interested in accessing and highlighting occurrence of specific conditions related to the internal activity of the most recent order block can use the "OB Volume" condition creator in the [custom alert creator](./alerts#custom-alert-creator).

This condition creator includes the following conditions:

|           Condition           |                                                                                  Description                                                                                 |
| :---------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|         **Buy Volume**        |                                                             Buy volume associated to the most recent order block.                                                            |
|        **Sell Volume**        |                                                            Sell volume associated to the most recent order block.                                                            |
|        **Total Volume**       |                                                            Total volume associated to the most recent order block.                                                           |
|         **Percentage**        |                                                         Volume percentage associated to the most recent order block.                                                         |
|  **Buy Volume > Sell Volume** |                                         Return true if the buy volume is greater than the sell volume of the most recent order block.                                        |
| **Buy Volume \< Sell Volume** |                                          Return true if the buy volume is lower than the sell volume of the most recent order block.                                         |
|    **Positive Association**   | Return true if buy volume is greater than sell volume in the case of a bullish order block, or if buy volume is lower than sell volume in the case of a bullish order block. |
|    **Negative Association**   | Return true if buy volume is lower than sell volume in the case of a bullish order block, or if buy volume is greater than sell volume in the case of a bullish order block. |

<Note>
  This data is always associated with the most recent order block, regardless of its mitigation status.
</Note>

When "Buy Volume", "Sell Volume", "Total Volume", or "Percentage" are selected we want to compare these values to a user set value as reference using the following comparison operators:

* Greater Than
* Lower Than
* Equal

## Mitigation Methods [#mitigation-methods]

Once price break an order block this order block is said to have been "mitigated", and will automatically disappear. Users can determine the condition for an order block to be considered mitigated through the **Mitigation Method** setting, each method is described below:

|    Method   |                                                                                             Description                                                                                             |
| :---------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|  **Close**  |           Mitigates an order block once price close cross above its upper extremity in the case of a bearish order block, and under its lower extremity in case of a bullish order block.           |
|   **Wick**  | Mitigates an order block once price high cross above its upper extremity in the case of a bearish order block, and when price low cross under its lower extremity in case of a bullish order block. |
| **Average** |                                                                     Mitigates an order block once price cross its average level.                                                                    |

<Note>
  Builtins alerts for Volumetric Order Blocks mitigation are available to the users.
</Note>

## MTF Order Blocks [#mtf-order-blocks]

![15m chart with 1h Volumetric Order Blocks (left) and 1h chart with 1h Volumetric Order Blocks (right)](/images/lux-pa/order-blocks/mtf_ob.png)

The user can display internal and swing Volumetric Order Blocks of a high or lower chart timeframe on the chart using the **Timeframe** settings. The displayed Volumetric Order Blocks will have the same price values and volume data than the ones of the selected timeframe.

<Warning>
  The time location of an order block of a different timeframe can differ from the location on its chart timeframe, these behaviors is proper to the tools used to retrieve Volumetric Order Blocks data on different timeframe.

  This can also affect when an order block data is retrieved, potentially causing mitigated Volumetric Order Blocks to appear unmitigated.
</Warning>


# Premium & Discount Zones (/platform/algos/price-action-concepts/pdzones)



![](/images/lux-pa/zones/settings.png)

Premium & Discount zones highlight three specific price areas, one upper area (premium), one central area (equilibrium), and one lower area (discount).

![](/images/lux-pa/zones/premium-discount.png)

These areas can be used to know when an event is occurring at a premium, discount, or within an equilibrium zone.

<Note>
  If a condition indicative of an uptrend occur within a discount zone then it would have an higher chance of being the cause of a reversal, this also applies for a condition indicative of a downtrend occurring within the premium zone.
</Note>

Additionally, each area can be used as a support or resistance.


# Highs & Lows MTF (/platform/algos/price-action-concepts/previous-high-low)



![](/images/lux-pa/previous-hl/settings.png)

It is common for traders to use the maximum and minimum price point reached in a previous day, week, month, or on specific periods as support and resistances levels.

The toolkit is able to return levels from the maximum and minimum price made during:

* The previous day
* The previous week
* The previous month
* The previous quarter

![](/images/lux-pa/previous-hl/previous-hl.png)

Each can be displayed from the **HIGHS & LOWS MTF** setting section.

## Day of Week High / Low [#day-of-week-high--low]

![](/images/lux-pa/previous-hl/previous-dow.png)

Users can display the maximum and minimum price point reached during a user-selected day of the week (Monday by default).

If the current day is the one selected, the returned level will track the highest and lowest prices reached during the day.

<Note>
  Some days of the week may be associated to closed markets for specific assets. As such certain days may not return levels when selected.
</Note>


# Settings Reference (/platform/algos/price-action-concepts/settings)



<Info>
  To open the settings menu, place your mouse pointer over the indicator title on your chart, you will then see a list of icons appear, and click on the gear ⚙️ icon.

  You can also open the settings panel by double-clicking any element displayed by the toolkit.
</Info>

## Market Structure [#market-structure]

***

### Internal [#internal]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="All">
      Show all types of internal market structures.
    </ResponseField>

    <ResponseField name="CHoCH (All)">
      Show all types of change of characters (CHoCH).
    </ResponseField>

    <ResponseField name="CHoCH+">
      Only show "supported" change of characters (CHoCH+).
    </ResponseField>

    <ResponseField name="CHoCH">
      Only show "leading" change of characters (CHoCH).
    </ResponseField>

    <ResponseField name="BOS">
      Only show break of structures (BOS).
    </ResponseField>

    <ResponseField name="None">
      Show no internal market structure.
    </ResponseField>
  </Expandable>
</ResponseField>

Type of internal market structures to display.

<ResponseField name="Numerical" type="default: 5">
  Determines the sensitivity of the swing detection algorithm.

  Higher values will return longer-term swing points, and as such longer-term internal market structures.
</ResponseField>

### Swing [#swing]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="All">
      Show all types of swing market structures.
    </ResponseField>

    <ResponseField name="CHoCH (All)">
      Show all types of change of characters (CHoCH).
    </ResponseField>

    <ResponseField name="CHoCH+">
      Only show "supported" change of characters (CHoCH+).
    </ResponseField>

    <ResponseField name="CHoCH">
      Only show "leading" change of characters (CHoCH).
    </ResponseField>

    <ResponseField name="BOS">
      Only show break of structures (BOS).
    </ResponseField>

    <ResponseField name="None">
      Show no swing market structure.
    </ResponseField>
  </Expandable>
</ResponseField>

Type of swing market structures to display.

<ResponseField name="Numerical" type="default: 50">
  Determines the sensitivity of the swing detection algorithm.

  Higher values will return longer-term swing points, and as such longer-term swing market structures.
</ResponseField>

### Timeframe [#timeframe]

<ResponseField name="Timeframe" type="default: Chart">
  Chart timeframe used for the detection of internal and swing market structures.
</ResponseField>

### Show Swing High/Low [#show-swing-highlow]

<ResponseField name="Toggle" type="default: false">
  Display detected swing points when enabled.

  <Note>
    Detected swing points are displayed retrospectively, and as such do not appear in real-time.
  </Note>
</ResponseField>

### Show Strong/Weak HL [#show-strongweak-hl]

<ResponseField name="Toggle" type="default: false">
  Display strong/weak swing levels when enabled.
</ResponseField>

### Color Candles [#color-candles]

<ResponseField name="Toggle" type="default: false">
  Color user chart candles based on the trend detected from internal market structures as follows:

  * Bullish CHoCH: Dark Green
  * Bullish BOS: Bright Green
  * Bearish BOS: Bright Red
  * Bearish CHoCH: Dark Red
</ResponseField>

## Volumetric Order Blocks [#volumetric-order-blocks]

***

### Show Last [#show-last]

<ResponseField name="Toggle" type="default: true">
  Display volumetric order blocks.
</ResponseField>

<ResponseField name="Numerical" type="default: 5">
  Determines how many of the most recent volumetric order blocks to display on the chart.
</ResponseField>

### Internal Buy/Sell Activity [#internal-buysell-activity]

<ResponseField name="Toggle" type="default: true">
  Display volumetric order blocks.
</ResponseField>

### Show Breakers [#show-breakers]

<ResponseField name="Toggle" type="default: false">
  Display breaker blocks.
</ResponseField>

### Length [#length]

<ResponseField name="Numerical" type="default: 5">
  Determines the sensitivity of the swing detection algorithm used to detect order blocks.

  Higher values will return longer-term swing points, and as such longer-term order blocks.
</ResponseField>

### Mitigation Method [#mitigation-method]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Close">
      Mitigate order blocks using the closing price.
    </ResponseField>

    <ResponseField name="Wick">
      Mitigate order blocks using candle wicks (high/low price).
    </ResponseField>

    <ResponseField name="Average">
      Mitigate order blocks using the crossing from the closing price and the order block area average level.
    </ResponseField>
  </Expandable>
</ResponseField>

Determines the method used to mitigate order blocks.

### Timeframe [#timeframe-1]

<ResponseField name="Timeframe" type="default: Chart">
  Chart timeframe used for the detection of order blocks.
</ResponseField>

### Show Metrics [#show-metrics]

<ResponseField name="Toggle" type="default: true">
  Display order block metrics such as total volume and percentage located at the right of the chart.
</ResponseField>

### Show Mid-Line [#show-mid-line]

<ResponseField name="Toggle" type="default: true">
  Display order block area average level (dashed line).
</ResponseField>

### Hide Overlap [#hide-overlap]

<ResponseField name="Toggle" type="default: true">
  Hide overlapping order blocks, conserving the most recent one.
</ResponseField>

## Liquidity Concepts [#liquidity-concepts]

***

### Trend Lines [#trend-lines]

<ResponseField name="Toggle" type="default: false">
  Display trend lines when enabled.
</ResponseField>

<ResponseField name="Numerical" type="default: 5">
  Determines how many of the most recent respective bullish/bearish trend lines to display on the user chart.
</ResponseField>

### Patterns [#patterns]

<ResponseField name="Toggle" type="default: false">
  Display detected pattern when enabled.
</ResponseField>

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />
  </Expandable>
</ResponseField>

Determines the sensitivity of the swing detection algorithm used to detect patterns.

Higher values will return longer-term swing points, and as such longer-term patterns.

### Show Patterns Zones [#show-patterns-zones]

<ResponseField name="Toggle" type="default: true">
  Display zones around the pattern lines.
</ResponseField>

### Equal H\&L [#equal-hl]

<ResponseField name="Toggle" type="default: false">
  Display equal highs/lows on the chart.
</ResponseField>

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Short-Term">
      Use short term swing point detection for detecting equal highs/lows, returning shorter term EQHL as a result.
    </ResponseField>

    <ResponseField name="Long-Term">
      Use long term swing point detection for detecting equal highs/lows, returning longer term EQHL as a result.
    </ResponseField>
  </Expandable>
</ResponseField>

Determines the sensitivity of the swing detection algorithm used to detect equal highs/lows.

### Liquidity Grabs [#liquidity-grabs]

<ResponseField name="Toggle" type="default: false">
  Display liquidity grabs on the chart.
</ResponseField>

## Imbalance Concepts [#imbalance-concepts]

***

<ResponseField name="Toggle" type="default: false">
  Display the selected imbalance on the chart.
</ResponseField>

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="FVG">
      Detect fair value gaps (FVG) when selected.
    </ResponseField>

    <ResponseField name="Inverse FVG">
      Detect inverse fair value gaps when selected.
    </ResponseField>

    <ResponseField name="Double FVG">
      Detect double fair value gaps when selected.
    </ResponseField>

    <ResponseField name="Volume Imbalance">
      Detect volume imbalances when selected.
    </ResponseField>

    <ResponseField name="Opening Gap">
      Detect opening gaps when selected.
    </ResponseField>
  </Expandable>
</ResponseField>

Determines which imbalance to detect.

### Mitigation Method [#mitigation-method-1]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Close">
      Mitigate imbalances using the closing price.
    </ResponseField>

    <ResponseField name="Wick">
      Mitigate imbalancess using candle wicks (high/low price).
    </ResponseField>

    <ResponseField name="Average">
      Mitigate imbalances using the crossing from the closing price and the imbalance area average level.
    </ResponseField>
  </Expandable>
</ResponseField>

Determines the method used to mitigate imbalances.

### Timeframe [#timeframe-2]

<ResponseField name="Timeframe" type="default: Chart">
  Chart timeframe used for the detection of imbalances.
</ResponseField>

### Extend Imbalance [#extend-imbalance]

<ResponseField name="Numerical" type="default: 10">
  Extend the imbalance area to the right by the selected amount of bars.
</ResponseField>

### Volatility Threshold [#volatility-threshold]

<ResponseField name="Numerical" type="default: 0">
  Threshold used to filter out imbalances with a low area height. Higher values will preserver more volatility imbalances.
</ResponseField>

## Premium & Discount Zones [#premium--discount-zones]

### Premium/Discount Zones [#premiumdiscount-zones]

<ResponseField name="Toggle" type="default: false">
  Display premium/discount and equilibrium zones on the chart when enabled.
</ResponseField>

***

## Highs & Lows MTF [#highs--lows-mtf]

***

### Daily [#daily]

<ResponseField name="Toggle" type="default: false">
  Display previous daily high/low on the chart when enabled.
</ResponseField>

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="⎯⎯⎯">
      Use solid line style when selected.
    </ResponseField>

    <ResponseField name="----">
      Use dashed line style when selected.
    </ResponseField>

    <ResponseField name="····">
      Use dotted line style when selected.
    </ResponseField>
  </Expandable>
</ResponseField>

Level line style.

### Weekly [#weekly]

<ResponseField name="Toggle" type="default: false">
  Display previous weekly high/low on the chart when enabled.
</ResponseField>

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="⎯⎯⎯">
      Use solid line style when selected.
    </ResponseField>

    <ResponseField name="----">
      Use dashed line style when selected.
    </ResponseField>

    <ResponseField name="····">
      Use dotted line style when selected.
    </ResponseField>
  </Expandable>
</ResponseField>

Level line style.

### Monthly [#monthly]

<ResponseField name="Toggle" type="default: false">
  Display previous monthly high/low on the chart when enabled.
</ResponseField>

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="⎯⎯⎯">
      Use solid line style when selected.
    </ResponseField>

    <ResponseField name="----">
      Use dashed line style when selected.
    </ResponseField>

    <ResponseField name="····">
      Use dotted line style when selected.
    </ResponseField>
  </Expandable>
</ResponseField>

Level line style.

### Quarterly [#quarterly]

<ResponseField name="Toggle" type="default: false">
  Display previous quarterly high/low on the chart when enabled.
</ResponseField>

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="⎯⎯⎯">
      Use solid line style when selected.
    </ResponseField>

    <ResponseField name="----">
      Use dashed line style when selected.
    </ResponseField>

    <ResponseField name="····">
      Use dotted line style when selected.
    </ResponseField>
  </Expandable>
</ResponseField>

Level line style.

### Day of week [#day-of-week]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Monday" />

    <ResponseField name="Tuesday" />

    <ResponseField name="Wednesday" />

    <ResponseField name="Thursday" />

    <ResponseField name="Friday" />

    <ResponseField name="Saturday" />

    <ResponseField name="Sunday" />
  </Expandable>
</ResponseField>

Returns the high/low price levels made during the selected day of the week.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="⎯⎯⎯">
      Use solid line style when selected.
    </ResponseField>

    <ResponseField name="----">
      Use dashed line style when selected.
    </ResponseField>

    <ResponseField name="····">
      Use dotted line style when selected.
    </ResponseField>
  </Expandable>
</ResponseField>

Level line style.

## Fibonacci Retracements [#fibonacci-retracements]

***

### Fibonacci Top [#fibonacci-top]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="None" />

    <ResponseField name="Internal High" />

    <ResponseField name="Internal Low" />

    <ResponseField name="Swing High" />

    <ResponseField name="Swing Low" />

    <ResponseField name="Discount" />

    <ResponseField name="Equilibrium Average" />

    <ResponseField name="Premium" />

    <ResponseField name="Bullish I-CHoCH" />

    <ResponseField name="Bearish I-CHoCH" />

    <ResponseField name="Bullish I-BOS" />

    <ResponseField name="Bearish I-BOS" />

    <ResponseField name="Highest OB Top" />

    <ResponseField name="Lowest  OB Bottom" />

    <ResponseField name="Imbalance Top" />

    <ResponseField name="Imbalance Bottom" />

    <ResponseField name="Imbalance Average" />

    <ResponseField name="Previous Day High" />

    <ResponseField name="Previous Day Low" />

    <ResponseField name="Previous Week High" />

    <ResponseField name="Previous Week Low" />

    <ResponseField name="Previous Month High" />

    <ResponseField name="Previous Month Low" />

    <ResponseField name="Previous Quarterly High" />

    <ResponseField name="Previous Quarterly Low" />
  </Expandable>
</ResponseField>

Upper value used to calculate the retracements.

### Fibonacci Bottom [#fibonacci-bottom]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="None" />

    <ResponseField name="Internal High" />

    <ResponseField name="Internal Low" />

    <ResponseField name="Swing High" />

    <ResponseField name="Swing Low" />

    <ResponseField name="Discount" />

    <ResponseField name="Equilibrium Average" />

    <ResponseField name="Premium" />

    <ResponseField name="Bullish I-CHoCH" />

    <ResponseField name="Bearish I-CHoCH" />

    <ResponseField name="Bullish I-BOS" />

    <ResponseField name="Bearish I-BOS" />

    <ResponseField name="Highest OB Top" />

    <ResponseField name="Lowest  OB Bottom" />

    <ResponseField name="Imbalance Top" />

    <ResponseField name="Imbalance Bottom" />

    <ResponseField name="Imbalance Average" />

    <ResponseField name="Previous Day High" />

    <ResponseField name="Previous Day Low" />

    <ResponseField name="Previous Week High" />

    <ResponseField name="Previous Week Low" />

    <ResponseField name="Previous Month High" />

    <ResponseField name="Previous Month Low" />

    <ResponseField name="Previous Quarterly High" />

    <ResponseField name="Previous Quarterly Low" />
  </Expandable>
</ResponseField>

Lower value used to calculate the retracements.

### Ratios [#ratios]

<ResponseField name="Toggle" type="default: true">
  Display the level with the associated ratio when enabled.
</ResponseField>

<ResponseField name="Numerical">
  Numerical input determining the ratio used to construct the retracement.
</ResponseField>

<Note>
  These settings are available for 5 ratios
</Note>

### Show Top/Bottom Levels [#show-topbottom-levels]

<ResponseField name="Toggle" type="default: true">
  Display the top/bottom extremities used to construct the retracements.
</ResponseField>

### Anchor To Origin [#anchor-to-origin]

<ResponseField name="Toggle" type="default: true">
  Set the left anchor of the retracement to the appropriate coordinate depending on the selected extremities. Else the most recent bar will be used as anchor.
</ResponseField>

## General Styling [#general-styling]

***

### Internal Label Style [#internal-label-style]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Tiny" />

    <ResponseField name="Small" />

    <ResponseField name="Normal" />
  </Expandable>
</ResponseField>

Determine the size of the internal market structures labels.

### Swing Label Style [#swing-label-style]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Tiny" />

    <ResponseField name="Small" />

    <ResponseField name="Normal" />
  </Expandable>
</ResponseField>

Determine the size of the swing market structures labels.

### Structures Theme [#structures-theme]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Colored">
      Use user set internal market structures colors as palette for market structures graphical elements.
    </ResponseField>

    <ResponseField name="Monochrome">
      Use white/gray tones as palette for market structures graphical elements.
    </ResponseField>
  </Expandable>
</ResponseField>

Determine the color theme of market structures related graphical elements such as levels, labels, and candle coloring.

### EQHL Label Style [#eqhl-label-style]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Tiny" />

    <ResponseField name="Small" />

    <ResponseField name="Normal" />
  </Expandable>
</ResponseField>

Determine the size of the equal highs/lows labels.

### OB Metrics Size [#ob-metrics-size]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Tiny" />

    <ResponseField name="Small" />

    <ResponseField name="Normal" />

    <ResponseField name="Auto">
      Automatically adjust text size to fit in the order block area.
    </ResponseField>
  </Expandable>
</ResponseField>

Determine the size of the swing market structures labels.

## Custom Alert Creator [#custom-alert-creator]

***

### Market Structure [#market-structure-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Market Structure condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Any Internal" />

    <ResponseField name="Any Internal Bullish" />

    <ResponseField name="Any Internal Bearish" />

    <ResponseField name="Any Internal Bullish CHoCH" />

    <ResponseField name="Any Internal Bearish CHoCH" />

    <ResponseField name="Internal Bullish CHoCH+" />

    <ResponseField name="Internal Bearish CHoCH+" />

    <ResponseField name="Internal Bullish CHoCH" />

    <ResponseField name="Internal Bearish CHoCH" />

    <ResponseField name="Bullish BOS" />

    <ResponseField name="Bearish BOS" />
  </Expandable>
</ResponseField>

List of market structure conditions to be used in the custom alert creator.

#### Market Structure Step [#market-structure-step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="11" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Market structure conditions step in the custom alert creator sequence.

### Order Block [#order-block]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Order Block condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Bullish Order Block" />

    <ResponseField name="Bearish Order Block" />

    <ResponseField name="Bullish OB Mitigation" />

    <ResponseField name="Bearish OB Mitigation" />

    <ResponseField name="Bullish Breaker" />

    <ResponseField name="Bearish Breaker" />

    <ResponseField name="Within Bullish OB" />

    <ResponseField name="Within Bearish OB" />

    <ResponseField name="Entered Bullish OB" />

    <ResponseField name="Entered Bearish OB" />

    <ResponseField name="Exit Bullish OB" />

    <ResponseField name="Exit Bearish OB" />
  </Expandable>
</ResponseField>

List of Order Block conditions to be used in the custom alert creator.

#### Order Block Step [#order-block-step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="11" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Order Block conditions step in the custom alert creator sequence.

### Imbalance [#imbalance]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Imbalance condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Any New Imbalance" />

    <ResponseField name="Bullish New Imbalance" />

    <ResponseField name="Bearish New Imbalance" />

    <ResponseField name="Any Imbalance Mitigated" />

    <ResponseField name="Bullish Imbalance Mitigated" />

    <ResponseField name="Bearish Imbalance Mitigated" />

    <ResponseField name="Within Any Imbalance" />

    <ResponseField name="Within Bullish Imbalance" />

    <ResponseField name="Within Bearish Imbalance" />

    <ResponseField name="Any Imbalance Entered" />

    <ResponseField name="Bullish Imbalance Entered" />

    <ResponseField name="Bearish Imbalance Entered" />

    <ResponseField name="Any Imbalance Exited" />

    <ResponseField name="Bullish Imbalance Exited" />

    <ResponseField name="Bearish Imbalance Exited" />
  </Expandable>
</ResponseField>

List of Imbalance conditions to be used in the custom alert creator.

#### Imbalance Step [#imbalance-step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="11" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Imbalance condition step in the custom alert creator sequence.

### Trendline [#trendline]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Trendline condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Up-Trendline Break" />

    <ResponseField name="Down-Trendline Break" />
  </Expandable>
</ResponseField>

List of Trendline conditions to be used in the custom alert creator.

### Trendline Step [#trendline-step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="11" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Trendline condition step in the custom alert creator sequence.

### Pattern [#pattern]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Pattern condition to be used in the custom alert creator.

<Expandable title="Expand">
  <ResponseField name="Ascending Triangle" />

  <ResponseField name="Descending Triangle" />

  <ResponseField name="Symmetrical Triangle" />

  <ResponseField name="Rising Wedge" />

  <ResponseField name="Falling Wedge" />

  <ResponseField name="Ascending Broadening Wedge" />

  <ResponseField name="Descending Broadening Wedge" />

  <ResponseField name="H&S" />

  <ResponseField name="Inverted H&S" />

  <ResponseField name="Double Top" />

  <ResponseField name="Double Bottom" />
</Expandable>

List of Pattern conditions to be used in the custom alert creator.

#### Pattern Step [#pattern-step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="11" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Pattern condition step in the custom alert creator sequence.

### Liquidity Grabs [#liquidity-grabs-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Liquidity Grabs condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Any Grab" />

    <ResponseField name="Bullish Grab" />

    <ResponseField name="Bearish Grab" />
  </Expandable>
</ResponseField>

List of Liquidity Grabs conditions to be used in the custom alert creator.

#### Liquidity Grabs Step [#liquidity-grabs-step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="11" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Liquidity Grabs condition step in the custom alert creator sequence.

### Premium/Discount [#premiumdiscount]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Premium/Discount condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Premium" />

    <ResponseField name="Discount" />
  </Expandable>
</ResponseField>

List of Premium/Discount conditions to be used in the custom alert creator.

#### Premium/Discount Step [#premiumdiscount-step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="11" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Premium/Discount condition step in the custom alert creator sequence.

### Session [#session]

<ResponseField name="Toggle" type="default: false" />

Enable session condition in the custom alert creator.

<ResponseField name="Session" type="default: 00:00" />

Session times used in the custom alert creator.

#### Session Step [#session-step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="11" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Session condition step in the custom alert creator sequence.

### OB Volume [#ob-volume]

<ResponseField name="Toggle" type="default: false" />

Enable the selected OB Volume condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Buy Volume (Numerical)" />

    <ResponseField name="Sell Volume (Numerical)" />

    <ResponseField name="Total Volume (Numerical)" />

    <ResponseField name="Percentage (Numerical)" />

    <ResponseField name="Buy Volume > Sell Volume (Bool)" />

    <ResponseField name="Buy Volume < Sell Volume (Bool)" />

    <ResponseField name="Positive Association (Bool)" />

    <ResponseField name="Negative Association (Bool)" />
  </Expandable>
</ResponseField>

List of OB Volume data sources to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Equal" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the selected OB data source to the selected numerical input.

<ResponseField name="Numerical Input" type="default:0" />

Numerical input.

#### Ob Volume Step [#ob-volume-step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="11" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

OB Volume condition step in the custom alert creator sequence.

### External Condition (1) [#external-condition-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected External condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

First source input.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />

    <ResponseField name="Equal" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the two selected sources.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

Second source input.

#### External Step (1) [#external-step-1]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="11" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

First external source condition step in the custom alert creator sequence.

### External Condition (2) [#external-condition-2]

<ResponseField name="Toggle" type="default: false" />

Enable the selected External condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

First source input.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />

    <ResponseField name="Equal" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the two selected sources.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

Second source input.

#### External Step (2) [#external-step-2]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Second external source condition step in the custom alert creator sequence.

### External Condition (3) [#external-condition-3]

<ResponseField name="Toggle" type="default: false" />

Enable the selected External condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

Source input.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />

    <ResponseField name="Equal" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the selected source to the selected numerical value.

<ResponseField name="Numerical Input" type="default:0" />

Numerical input.

#### External Step (3) [#external-step-3]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="11" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Third external source condition step in the custom alert creator sequence.

### Invalidation Behavior [#invalidation-behavior]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="None">
      No invalidation behavior.
    </ResponseField>

    <ResponseField name="Invalidate On Step 1">
      The "Invalidate On Step 1" behavior allows to reset an incomplete sequence of conditions when the condition on step 1 trigger. This prevents the first step condition from happening in between other steps of the condition sequence.

      This behavior is useful when the first step of our sequence of conditions needs to never be repeated during the sequence.

      <Card title="Example">
        Let's take an example where we use 3 regular steps.

        <Steps>
          <Step title="Step 1">
            Step 1 condition trigger, we will now evaluate step 2 from now on.
          </Step>

          <Step title="Step 2">
            Step 2 condition trigger, we will now evaluate step 3 from now on.
          </Step>

          <Step title="Step 1 Trigger">
            Step 1 condition trigger, we start evaluating step 2 from now.
          </Step>
        </Steps>
      </Card>
    </ResponseField>

    <ResponseField name="Invalidate On Any Repeated Step">
      The "Invalidate On Any Repeated Step" behavior allows to reset an incomplete sequence of conditions when a step is triggered such that it does not respect the user-set order of conditions.

      This behavior is useful when we want a perfectly ordered sequence of conditions to complete, without any step repeating itself.

      <Card title="Example">
        Let's take an example where we use 3 regular steps.

        <Steps>
          <Step title="Step 1">
            Step 1 condition trigger, we will now evaluate step 2 from now on.
          </Step>

          <Step title="Step 2">
            Step 2 condition trigger, we will now evaluate step 3 from now on.
          </Step>

          <Step title="Step 1 or 2 Trigger">
            Step 1 or 2 conditions trigger, we start evaluating step 1 from now.
          </Step>
        </Steps>
      </Card>
    </ResponseField>
  </Expandable>
</ResponseField>

Invalidation behaviors allow adding more restrictions to a sequence of conditions; users can use two different invalidation behaviors described below.

### Maximum Step Interval [#maximum-step-interval]

<ResponseField name="Numerical Input" type="default:10" />

Maximum allowed horizontal distance (in bars) between two steps.

If the number of bars since a step exceeds the selected threshold, then the condition sequence is reset, and we start evaluating from step 1 again.

### Highlight Custom Alert Triggers [#highlight-custom-alert-triggers]

<ResponseField name="Toggle" type="default:true" />

When a custom alert condition is set, a visual element is displayed at the bottom of the chart in order to more easily visualize when the custom alert conditions occurs (displayed by default).

## Calculation [#calculation]

### Calculated Bars [#calculated-bars]

<ResponseField name="Numerical" type="default:10 000" />

Limits the initial calculation of the toolkit to the specified amount of most recent bars. Lower values will return a faster computation time. A value of 0 will compute the toolkit over the entire data history.


# Alerts (/platform/algos/signals-overlays/alerts)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/olKQq5XR5S0"
    frameBorder="0"
  />
</div>

Signals & Overlays® can alert users for multiple events relevant to the existing features in the toolkit. To learn more about how to set alerts in general see [how to create alerts on tradingview](/platform/algos/tradingview-alerts).

<Note>
  In order for an alert to work in the toolkit make sure the related feature is enabled
</Note>

## Using Placeholders With Confirmation/Contrarian Alerts [#using-placeholders-with-confirmationcontrarian-alerts]

![](/images/lux-algo/alerts/image620.png)

When creating an alert using 'Any Confirmation' or 'Any Contrarian' it is possible to use placeholders so that the alert message returns the type of signal that triggered the alert.

In order to do so, paste the following line in the message field:

```
Bullish : {{plot("Bullish")}} | Bearish : {{plot("Bearish")}} | Bullish+: {{plot("Bullish+")}} | Bearish+ : {{plot("Bearish+")}}
```

![](/images/lux-algo/alerts/image31.png)

## Any Alert() Function Call [#any-alert-function-call]

The *any alert() function call* alert condition allows to create one alert using various conditions, this allows to bypass the limit of one alert for free users as well as making the process of updating many alerts faster and easier.

To create such an alert go to the indicator settings, and scroll down to the '**ANY ALERT() FUNCTION CALL CONDITIONS**' section.

![](/images/lux-algo/alerts/conditions.png)

Toggle on the conditions of interest. Now create a new alert, in the first **Condition** drop-down menu select the **LuxAlgo Premium** indicator, in the second drop-down menu select **Any alert() function call**, you can then create the alert.

<Note>
  This alert would use **once per bar close** by default.
</Note>

![](/images/lux-algo/alerts/image117.png)

<Note>
  All-in-one alerts do not differ from regular alerts, they will be using the same indicator settings as the one used during their creation. Note however that you cannot set a custom message for all-in-one alerts, the message displayed when the selected conditions trigger will be the same as the one used in the same individual alert conditions.
</Note>

### Any Alert() Function Call Message [#any-alert-function-call-message]

Users can set a custom message that will be used when an "any alert() function call" condition is triggered in the message field below the list of conditions. Users can use placeholders to customize messages and return useful information, the following placeholders are supported:

#### Symbol Placeholders [#symbol-placeholders]

| Placeholder  | Description                                             | Example                         |
| ------------ | ------------------------------------------------------- | ------------------------------- |
| `{default}`  | Default message associated with the triggered condition | `"Bullish Confirmation Signal"` |
| `{ticker}`   | Symbol ticker without the exchange prefix               | `"MSFT"`                        |
| `{exchange}` | Symbol exchange/data source                             | `"COINBASE"`                    |
| `{sector}`   | Sector of the symbol, `na` if the symbol has no sector  | `"Technology Services"`         |
| `{market}`   | Symbol market type                                      | `"Stocks"`                      |

#### Time Placeholders [#time-placeholders]

| Placeholder | Description                                                                            | Example           |
| ----------- | -------------------------------------------------------------------------------------- | ----------------- |
| `{tf}`      | Chart timeframe, includes "S" for seconds, "D" for days, "W" for weeks, "M" for months | `"15"`            |
| `{time}`    | Unix time in milliseconds in exchange timezone                                         | `"1705331385158"` |
| `{year}`    | Current year in exchange timezone                                                      | `"2024"`          |
| `{month}`   | Current month in exchange timezone                                                     | `"7"`             |
| `{day}`     | Current day of the month in exchange timezone                                          | `"21"`            |

#### Data Placeholders [#data-placeholders]

| Placeholder | Description    | Example    |
| ----------- | -------------- | ---------- |
| `{open}`    | Opening price  | `"1.2145"` |
| `{high}`    | High price     | `"1.2357"` |
| `{low}`     | Low price      | `"1.1983"` |
| `{close}`   | Closing price  | `"1.0569"` |
| `{volume}`  | Current volume | `"45216"`  |

#### Indicators Placeholders [#indicators-placeholders]

| Placeholder     | Description                               | Example    |
| --------------- | ----------------------------------------- | ---------- |
| `{smart_trail}` | Smart Trail overlay value                 | `"0.8321"` |
| `{rz_r3}`       | Reversal Zones overlay resistance 3 value | `"3.4102"` |
| `{rz_r2}`       | Reversal Zones overlay resistance 2 value | `"1.0293"` |
| `{rz_r1}`       | Reversal Zones overlay resistance 1 value | `"5.1234"` |
| `{rz_s1}`       | Reversal Zones overlay support 1 value    | `"2.5647"` |
| `{rz_s2}`       | Reversal Zones overlay support 2 value    | `"7.8910"` |
| `{rz_s3}`       | Reversal Zones overlay support 3 value    | `"6.2345"` |
| `{catcher}`     | Trend Catcher overlay value               | `"4.3456"` |
| `{tracer}`      | Trend Tracer overlay value                | `"3.7812"` |
| `{neo_lead}`    | Neo Cloud overlay lead value              | `"1.9087"` |
| `{neo_lag}`     | Neo Cloud overlay lag value               | `"0.4571"` |
| `{tp1}`         | Take Profit 1 value                       | `"2.7690"` |
| `{sl1}`         | Stop Loss 1 value                         | `"8.1203"` |
| `{tp2}`         | Take Profit 2 value                       | `"9.6584"` |
| `{sl2}`         | Stop Loss 2                               | `"3.1246"` |

### Format as JSON [#format-as-json]

Use the following message template to receive "any alert() function call" messages in the JSON format:

```json
{
  "alert": "{default}",
  "ticker": "{ticker}",
  "tf": "{tf}",
  "ohlcv":{
    "open": {open},
    "high": {high},
    "low": {low},
    "close": {close},
    "volume": {volume}
  },
  "bartime": {time}
}
```

### Updating Alerts Using Any Alert() Function Call [#updating-alerts-using-any-alert-function-call]

If you already set an *any alert() function call* alert and want to change the indicator settings or want to toggle on another condition all you need to do is edit the alert you want to update, select the second instance of the indicator in the first **Condition** drop-down menu and then click on save.

![](/images/lux-algo/alerts/image345.png)

## Custom Alert Creator [#custom-alert-creator]

![](/images/lux-algo/alerts/custom.png)

Users wishing to construct more complex alerts using features within the toolkit and/or external indicators can use the **Custom Alert Creator**.

The custom alert creator allows alerting when various user-set conditions are met or when a sequence of conditions is complete.

<Tip>
  A user wishing to be alerted when any bullish signal occurs while the smart trail is bullish could do it as follows:

  ![](/images/lux-algo/alerts/custom_example.png)
</Tip>

A custom alert can be triggered as an *any alert() function call* if the setting is selected or as a regular alert when selected in the Tradingview alert creator menu.

### Steps [#steps]

Steps allow creating a sequence of conditions, which when met trigger an alert. When a condition with a step equal to 1 is met the next condition associated with step 2 will be evaluated (if enabled) and so on for higher steps (if any).

Conditions with the same associated step will require both conditions to be true in order for the condition associated with a higher step (if any) to be evaluated. This allows certain conditions to act as filters.

<Tip>
  A user wishing to be alerted for any bullish confirmation signal classified as 1 or 2 by the ML Classifier occurring after the smart trail turned bullish could do it as follows:

  ![](/images/lux-algo/alerts/step_example.png)

  *Make sure the ML Classifier is enabled*.
</Tip>

<Warning>
  Make sure steps are set such that they form a linear sequence. For example, if the lowest step is 2 or if the set steps are only 1 and 3 an error will be returned.
</Warning>

#### OR Step Operator [#or-step-operator]

The "OR" step operator can be used to get alerted for any additional condition using "OR" as step, in addition to other set custom alert conditions.

Conditions using the "OR" step can be highlighted using a different color, which can be set from the Custom Alert Creator settings. Users can choose the location of this visual element (top or bottom of the chart).

#### All Step Operator [#all-step-operator]

The "All" step operator can be used to filter any set "Step" condition, including "OR" and "Invalidate".

This step operator can be useful if all the steps in a sequence needs to obey a specific rule.

<Tip>
  If a user has 2 steps set, and requires both of these to occur at a specific time interval such as 9am to 1pm, then we can use a "Session" condition using the "All" step, requiring our step 1 and step 2 to be occur between 9am and 1pm.
</Tip>

#### Invalidate Step [#invalidate-step]

The "Invalidate" step allows to set a condition as an "invalidation condition". When this condition is triggered while a sequence of conditions is incomplete, the sequence will restart at step 1.

If multiple steps are set as "Invalidate" any of the conditions being true will reset the sequence of conditions, as such not all of them are required to be true for the sequence of conditions to reset.

<Card title="Example">
  Let's take an example where we use 2 regular steps and one "Invalidate" step. The alert will trigger when step 2 trigger after step 1 has been triggered.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Invalidation Step">
      Invalidation step condition trigger, we reset the sequence and evaluate step 1 just after.
    </Step>
  </Steps>
</Card>

### Invalidation Behaviors [#invalidation-behaviors]

Invalidation behaviors allows adding more restrictions to a sequence of conditions, users can use two different invalidation behaviors described below:

#### Invalidate On Step 1 [#invalidate-on-step-1]

The "Invalidate On Step 1" behavior allows to reset an incomplete sequence of conditions when the condition on step 1 trigger. This prevents the first step condition from happening in between other steps of the condition sequence.

This behavior is useful when the first step of our sequence of conditions needs to never be repeated during the sequence.

<Card title="Example">
  Let's take an example where we use 3 regular steps.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Step 2">
      Step 2 condition trigger, we will now evaluate step 3 from now on.
    </Step>

    <Step title="Step 1 Trigger">
      Step 1 condition trigger, we start evaluating step 2 from now.
    </Step>
  </Steps>
</Card>

#### Invalidate On Any Repeated Step [#invalidate-on-any-repeated-step]

The "Invalidate On Any Repeated Step" behavior allows to reset an incomplete sequence of conditions when a step is triggered such that it does not respect the set order of conditions.

This behavior is useful when we want a perfectly ordered sequence of conditions to complete, without any step repeating itself.

<Card title="Example">
  Let's take an example where we use 3 regular steps.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Step 2">
      Step 2 condition trigger, we will now evaluate step 3 from now on.
    </Step>

    <Step title="Step 1 or 2 Trigger">
      Step 1 or 2 conditions trigger, we start evaluating step 1 from now.
    </Step>
  </Steps>
</Card>

### Maximum Step Interval [#maximum-step-interval]

Users can determine what is the maximum allowed horizontal distance (in bars) between two steps by enabling the "Maximum Step Interval" setting. This allows restricting steps separated by a large amount of bars.

If the amount of bars since a step exceed the set threshold, then the conditions sequence is reset, and we start evaluating from step 1 again.

### Highlight On Chart [#highlight-on-chart]

When a custom alert condition is set, a visual element is displayed at the bottom of the chart in order to more easily visualize when the custom alert conditions occurs (displayed by default).

## Alerts Reference [#alerts-reference]

***

### Pre-Set Alerts [#pre-set-alerts]

| Alert                     | Description                                                                                      |
| ------------------------- | ------------------------------------------------------------------------------------------------ |
| Any alert() function call | Return when an alert when a user toggled any alert() condition is triggered (see sections below) |
| Any Confirmation          | Triggers when any Confirmation signal (bullish/bearish normal/+) is displayed                    |
| Any Bullish Confirmation  | Triggers when any bullish Confirmation signal (normal/+) is displayed                            |
| Any Bearish Confirmation  | Triggers when any bearish Confirmation signal (normal/+) is displayed                            |
| Any Confirmation+         | Triggers when any Confirmation + signal (bullish/bearish) is displayed                           |
| Any Contrarian            | Triggers when any Contrarian signal (normal/+ bullish/bearish) is displayed                      |
| Any Bullish Contrarian    | Triggers when any bullish Contrarian signal (normal/+) is displayed                              |
| Any Bearish Contrarian    | Triggers when any bearish Contrarian signal (normal/+) is displayed                              |
| Bullish Confirmation      | Triggers when a normal bullish Confirmation signal is displayed                                  |
| Bearish Confirmation      | Triggers when a normal bearish Confirmation signal is displayed                                  |
| Bullish Confirmation+     | Triggers when a bullish Confirmation+ signal is displayed                                        |
| Bearish Confirmation+     | Triggers when a bearish Confirmation+ signal is displayed                                        |
| Bullish Exit              | Triggers when a bullish exit signal is displayed                                                 |
| Bearish Exit              | Triggers when a bearish exit signal is displayed                                                 |
| Trend Catcher Down        | Triggers once the Trend Catcher starts decreasing (switch from green to red)                     |
| Trend Catcher Up          | Triggers once the Trend Catcher starts rising (switch from red to green)                         |
| Trend Tracer Down         | Triggers once the Trend Tracer starts decreasing (switch from green to red)                      |
| Trend Tracer Up           | Triggers once the Trend Tracer starts rising (switch from red to green)                          |
| Custom Alert Condition    | Triggers on the event of a user set custom alert condition being true                            |

### Any alert() Function Call [#any-alert-function-call-1]

| Alert                  | Description                                                                     | Default                                                   |
| ---------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------- |
| Bullish Confirmation   | Triggers when a normal bullish Confirmation signal is displayed                 | <Exclamation tooltip="Bullish Confirmation" />            |
| Bearish Confirmation   | Triggers when a normal bearish Confirmation signal is displayed                 | <Exclamation tooltip="Bearish Confirmation" />            |
| Bullish +              | Triggers when a strong bullish Confirmation signal is displayed                 | <Exclamation tooltip="Bullish Confirmation+" />           |
| Bearish +              | Triggers when a strong bearish Confirmation signal is displayed                 | <Exclamation tooltip="Bearish Confirmation+" />           |
| Bullish Contrarian     | Triggers when a normal bullish Contrarian signal is displayed                   | <Exclamation tooltip="Bullish Contrarian" />              |
| Bearish Contrarian     | Triggers when a normal bearish Contrarian signal is displayed                   | <Exclamation tooltip="Bearish Contrarian" />              |
| Bullish +              | Triggers when a strong bullish Contrarian signal is displayed                   | <Exclamation tooltip="Bullish Contrarian+" />             |
| Bearish +              | Triggers when a strong bearish Contrarian signal is displayed                   | <Exclamation tooltip="Bearish Contrarian+" />             |
| Bullish Exit           | Triggers when a bullish exit signal is displayed                                | <Exclamation tooltip="Bullish Exit" />                    |
| Bearish Exit           | Triggers when a bearish exit signal is displayed                                | <Exclamation tooltip="Bearish Exit" />                    |
| Take Profit            | Triggers when a take profit is reached (TP1 or TP2)                             | <Exclamation tooltip="TP(1/2) Reached" />                 |
| Stop Loss              | Triggers when a stop loss is reached (SL1 or SL2)                               | <Exclamation tooltip="SL(1/2) Reached" />                 |
| Bullish Turn +         | Triggers when a weak bullish trend turns strong (purple candle switch to green) | <Exclamation tooltip="Bullish Turn +" />                  |
| Bearish Turn +         | Triggers when a weak bearish trend turns strong (purple candle switch to red)   | <Exclamation tooltip="Bearish Turn +" />                  |
| Custom Alert Condition | Triggers on the event of a user set custom alert condition being true           | <Exclamation tooltip="Custom step condition triggered" /> |


# Candle Coloring (/platform/algos/signals-overlays/candle-coloring)



The candle coloring feature allows the user to change the body color of the candles/bars when using candlesticks or bar charts. Three coloring modes are available, with two of them being based on the previously described signal methodologies.

Candle coloring also works on charts using Heikin-Ashi, Renko, Kagi, line break, point and figure, and range candles.

Each mode can be enabled from the settings in the **candle color mode** dropdown menu.

![](/images/lux-algo/candle-coloring/image547.png)

In order to use candle coloring as properly as possible make sure to disable the **borders** setting in your chart settings.

![](/images/lux-algo/candle-coloring/image72.png)

### Confirmation Simple [#confirmation-simple]

The **confirmation simple** color mode is based on the methodology used by confirmation signals and is the one enabled by default. This mode uses three colors, each one can be interpreted as follows:

* **Green candles:** Strong bullish signal active.
* **Red candles:** Strong bearish signal active.
* **Purple candles:** Normal bullish or bearish signal active.

![Confirmation Simple color mode](/images/lux-algo/candle-coloring/image398.png)

<Tip>
  If you need to set an alert based on candle coloring or need to return information on what is the current candle color on an alert message simply use the **Bar Color Value** variable. This variable as three possible values each indicating the following:

  * 1: Candle color is green
  * 0: Candle color is purple
  * -1: Candle color is red

  This variable can be used as input for external indicators as well.
</Tip>

### Confirmation Gradient [#confirmation-gradient]

The **confirmation gradient** color mode is also based on the algorithm that generates confirmation signals, but will slightly differ from the previously described **confirmation simple** color mode.

Lighter green colors indicate strong bullish variations, while lighter red colors indicate strong bearish variations. Colors closer to purple indicate that the current variation might be a retracement or that the market is ranging.

![Confirmation Gradient color mode](/images/lux-algo/candle-coloring/image93.png)

### Contrarian Gradient [#contrarian-gradient]

The **contrarian gradient** color mode is based on the contrarian signals algorithm. Stronger green colors indicate an oversold market and a potential reversal, while stronger red colors indicate an overbought market.

![Contrarian Gradient color mode](/images/lux-algo/candle-coloring/contrarian.png)


# Dashboard (/platform/algos/signals-overlays/dashboard)



![](/images/lux-algo/dashboard/image601.png)

The dashboard displays the most relevant metrics from within our premium features. This feature is extremely useful to easily obtain pertinent information regarding the market and can be used in confluence with confirmation or contrarian signals.

## Dashboard Elements [#dashboard-elements]

### Optimal Sensitivity [#optimal-sensitivity]

![](/images/lux-algo/dashboard/image576.png)

The optimal sensitivity is an estimate of the optimal **sensitivity** setting. Depending on the selected signal mode, the recommended sensitivity for confirmation or contrarian signals will be displayed. When **None** is selected, no optimal sensitivity will be displayed. This value is obtained through grid search optimization by using the most recent 250 bars and varies in a range between 10 and 20.

The cell color is based on the signal returned by the signal algorithm using the returned optimal setting, with a green color indicating a bullish signal and a red color indicating a bearish signal.

<Warning>
  Note that this metric is not indicative of future results.
</Warning>

When **Autopilot Sensitivity** is enabled, the recommended sensitivity will not appear on the dashboard and is replaced with an indication notifying the user that autopilot is enabled.

![](/images/lux-algo/dashboard/image544.png)

### Trend Strength [#trend-strength]

![](/images/lux-algo/dashboard/image107.png)

The trend strength quantifies the strength of an underlying trend as a percentage and can inform the trader whether the market is trending or ranging. This indication can help a trader know whether a signal has a higher chance of being false or not. The 🔥 sign is used with trending markets while ❄️ is used for ranging markets.

The cell color is based on the currently displayed signal, with a green color indicating a bullish signal and a red color indicating a sell signal.

A trend strength plot is available allowing users to set alerts based on it, using placeholders `{{plot("Trend Strength"}}` in the alert message.

<Note>
  A percentage superior to 50% would indicate a trending market, while inferior percentages would indicate a ranging market.
</Note>

### Lux Volatility [#lux-volatility]

The Lux volatility appearing on the dashboard is based on the **Lux Volatility** indicator and returns either **Stable** during periods of low volatility or **Volatile** during periods of high volatility. When volatility is rising the 📈 sign will appear, while 📉 appears when volatility is declining.

![](/images/lux-algo/dashboard/image585.png)

The Lux volatility can also display with a warning sign, this happens when the volatility is either too low or too high and indicates extra precaution.

### Squeeze [#squeeze]

Squeeze is a measure of price compression. Higher values of this metric indicate that the market is being compressed into a tighter area, suggesting market prices are subject to damping.

![](/images/lux-algo/dashboard/squeeze.png)

Higher values of this metric are mostly returned during ranging markets.

### Volume Sentiment [#volume-sentiment]

The volume sentiment metric returns an estimate of whether most volume came from buying or selling participants. This measure is expressed as a percentage in the range \[-100 / 100].

![](/images/lux-algo/dashboard/volume.png)

The value of this metric can sometimes be high or low even if a candle does not reflect this sentiment, thus potentially indicating a reversal.

## Dashboard Settings [#dashboard-settings]

![](/images/lux-algo/dashboard/image591.png)

Enabling/disabling the dashboard can be done from the **Indicator Overlay** section. Settings affecting the dashboard visual style can be found in the **Advanced Settings** section.

### Dashboard Location [#dashboard-location]

![](/images/lux-algo/dashboard/image610.png)

Determines the corner where the dashboard is displayed on the chart. Selecting "Hidden" will hide the dashboard.

### Dashboard Size [#dashboard-size]

![](/images/lux-algo/dashboard/image584.png)

Determines the size of the dashboard on the chart, options include **Tiny**, **Small**, **Normal**, and **Large**.


# Indicator Overlay (/platform/algos/signals-overlays/indicator-overlay)



![](/images/lux-algo/indicator-overlay/image634.png)

Signals & Overlays® Indicator allows the user to display various technical indicators directly on their chart. These can be used in confluence with the confirmation/contrarian signals or optionally used as a standalone.

Each overlay has an associated setting affecting its output. These settings range from 1 to 5, with higher values allowing the overlay to return indications for longer-term trends.

<Note>
  All overlays use a default sensitivity of 3 except the Neo Cloud which uses a sensitivity of 2.
</Note>

## Smart Trail [#smart-trail]

![](/images/lux-algo/indicator-overlay/smart.png)

The Smart Trail is a trailing stop that diverges from the price in the presence of unwanted price variations in a trend.

An up-trend is indicated when the price is above the trailing stop, and a down-trend when the price is below the trailing stop.

The Smart Trail also displays support (in blue) and resistance (in red) areas and aims to indicate when the price might reverse when entering the area.

## Reversal Zones [#reversal-zones]

When enabled, one upper and one lower zone will be displayed on your chart, each zone is designed to help the user find tops and bottoms whether the market is ranging or trending (however the indicator will still be more effective during ranging markets).

![](/images/lux-algo/indicator-overlay/reversal.png)

These zones can be great areas to take profit or find early entries to use alongside the confirmation or contrarian signals.

<Note>
  During periods of increased volatility, the price may move outside the zones, you can wait for the price to go back within each zone to take a decision.
</Note>

## Trend Tracer [#trend-tracer]

![](/images/lux-algo/indicator-overlay/tracer.png)

The Trend Tracer is a trend-following algorithm that aims to estimate the underlying trend in the price. When the indicator indicates an uptrend its color will be blue, while orange in the case of a downtrend.

<Note>
  The Trend Tracer can be used as a trailing support/resistance tool, you can also use it in confluence with the confirmation signals, either by waiting for the price to reach the trend tracer in order to enter a trade corresponding to the current confirmation signal or by filtering shorter-term signals by only trading those in accordance by the trend estimated by the trend tracer.
</Note>

## Trend Catcher [#trend-catcher]

![](/images/lux-algo/indicator-overlay/catcher.png)

The Trend Catcher is a similar algorithm to the previous **Trend Tracer**, however, it aims to detect very early trends, and is thus more reactive.

Like with the **Trend Tracer**, the color of the indicator determines the detected trend.

## Neo Cloud [#neo-cloud]

![](/images/lux-algo/indicator-overlay/neo.png)

When enabled, a simple cloud-like indicator will be displayed on your chart. The Neo Cloud is similar in effect to the Ichimoku Cloud, and can determine the current trend direction.

The cloud is filled with a gradient color showing how new a detected trend is, with brighter colors indicating older trends.

This cloud can provide a support when an uptrend is detected or resistance when a downtrend is detected. This makes it perfect for entries into precise trades.


# Introduction (/platform/algos/signals-overlays/introduction)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/j-o4jJcQfF4"
    frameBorder="0"
  />
</div>

**Signals & Overlays®** is the most popular toolkit provided by LuxAlgo which contains a variety of signal modes, indicator overlays, and advanced dynamic features to help users make improved decisions when trading.

This toolkit is great for beginners & serious traders alike who want the most powerful signals & dynamic features possible for their charts.

Provided below are pages describing all features included.

<CardGroup cols="3">
  <Card title="Signal Modes" icon="sort" href="/platform/algos/signals-overlays/signals" />

  <Card title="Signal Settings" icon="wrench" href="/platform/algos/signals-overlays/signal-settings" />

  <Card title="TP/SL Points" icon="bullseye-arrow" href="/platform/algos/signals-overlays/tp-sl-points" />

  <Card title="Indicator Overlays" icon="function" href="/platform/algos/signals-overlays/indicator-overlay" />

  <Card title="Candle Coloring" icon="chart-candlestick" href="/platform/algos/signals-overlays/candle-coloring" />

  <Card title="Dashboard" icon="table-rows" href="/platform/algos/signals-overlays/dashboard" />
</CardGroup>

## Alerts [#alerts]

Most features included within the Signals & Overlays® toolkit include built-in alerts.

Users can also utilize the "any alert() function call" conditions at the bottom of the settings to be alerted for multiple conditions at once. Further, the Signals & Overlays® toolkit allows users to set extremely complex alerts with its “Custom Alert Creator” feature at the bottom of the settings menu.

<Card title="Alerts" icon="bell" href="/platform/algos/signals-overlays/alerts" />

## Settings Reference [#settings-reference]

Signals & Overlays® includes various settings affecting the appearance and behavior of the toolkit, most of which are specific to the feature they affect.

Users can access the list of user settings and their descriptions in the following page:

<Card title="Settings Reference" icon="gear" href="/platform/algos/signals-overlays/settings" />

Users can access the list of user settings and their descriptions in the following page:

<Card title="Signal Settings" icon="gear-complex-code" href="/platform/algos/signals-overlays/signal-settings" />


# Settings Reference (/platform/algos/signals-overlays/settings)



<Info>
  To open the settings menu, place your mouse pointer over the indicator title on your chart, you will then see a list of icons appear, and click on the gear ⚙️ icon.

  You can also open the settings panel by double-clicking any element displayed by the toolkit.
</Info>

## Basic Settings [#basic-settings]

***

### Presets / Filters [#presets--filters]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="None" type="none">
      No presets/filter enabled.
    </ResponseField>

    <ResponseField name="Trend Trader" type="preset">
      * Confirmation Signals
      * Smart Trail
      * Trend Catcher
    </ResponseField>

    <ResponseField name="Scalper" type="preset">
      * Confirmation Signals
      * Confirmation gradient coloring
      * Reversal Zones
      * Take Profit/Stop Losses
    </ResponseField>

    <ResponseField name="Swing Trader" type="preset">
      * Confirmation Signals
      * Long-Term Frequency Autopilot
      * Simple confirmation candle coloring
      * Neo Cloud
    </ResponseField>

    <ResponseField name="Contrarian Trader" type="preset">
      * Contrarian Signals
      * Mid-Term Frequency Autopilot
      * Contrarian candle coloring
      * Reversal Zones
    </ResponseField>

    <ResponseField name="Smart Trail" type="filter">
      Show bullish signals when price is above the Smart Trail, and bearish signals when price is under the Smart Trail.
    </ResponseField>

    <ResponseField name="Trend Tracer" type="filter">
      Show bullish signals when Trend Tracer is indicating an up-trend, and bearish signals when Trend Tracer is indicating a down-trend.
    </ResponseField>

    <ResponseField name="Trend Catcher" type="filter">
      Show bullish signals when Trend Catcher is indicating an up-trend, and bearish signals when Trend Catcher is indicating a down-trend.
    </ResponseField>

    <ResponseField name="Neo Cloud" type="filter">
      Show bullish signals above Neo cloud, and bearish signals under Neo Cloud.
    </ResponseField>

    <ResponseField name="Trend Strength" type="filter">
      Show bullish/bearish signals when the Trend strength is above 50%.
    </ResponseField>
  </Expandable>
</ResponseField>

Presets allows users to easily enable multiple features at once from a selected option.

<Note>
  Note that when a preset is selected some settings such as the toggles of the displayed overlay indicators can become inactive/not usable. If you wish to further customize the Signals & Overlays® toolkit, we suggest disabling the Preset and manually customizing the settings.
</Note>

On the other hand, filters allow users to display confirmation/contrarian signals when a specific overlay or metric behavior is respected.

### Signal Mode [#signal-mode]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Confirmation + Exits">
      Confirmation signals alongside exit signals.
    </ResponseField>

    <ResponseField name="Contrarian + Exits">
      Contrarian signals alongside exit signals.
    </ResponseField>

    <ResponseField name="None">
      No signals.
    </ResponseField>
  </Expandable>
</ResponseField>

Controls the type of signal generation algorithm to use.

### ML Signal Classifier [#ml-signal-classifier]

<ResponseField name="Toggle" type="default: false" />

Enable the ML Classifier for the returned signals. Signals will be classified between groups 1, 2, 3, and 4, with 1/2 indicating replacements and 3/4 signals in accordance with a main trend.

### Signals Sensitivity [#signals-sensitivity]

<ResponseField name="Numerical Input" type="default: 12" />

The sensitivity setting controls how sensitive the signals generation algorithms are to shorter term price variations, with higher values of the setting returning longer term signals.

To learn more about signal settings see the following [page](/platform/algos/signals-overlays/settings).

### Candle Coloring [#candle-coloring]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Confirmation Simple">
      Color candles using three shades based on the Confirmation signal algorithm.
    </ResponseField>

    <ResponseField name="Confirmation Gradient">
      Color candles using a gradient based on the Confirmation signal algorithm.
    </ResponseField>

    <ResponseField name="Contrarian Gradient">
      Color candles using a gradient based on the Contrarian signal algorithm.
    </ResponseField>

    <ResponseField name="None" />
  </Expandable>
</ResponseField>

Change the color of the candle/bars on the user chart based on the selected option.

To learn more about candle coloring see the following [page](/platform/algos/signals-overlays/candle-coloring).

## Indicator Overlay [#indicator-overlay]

***

### Smart Trail [#smart-trail]

<ResponseField name="Toggle" type="default: true" />

Enable the Smart Trail overlay on the chart when toggled on.

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />
  </Expandable>
</ResponseField>

Smart Trail numerical period. Greater values will allow the Smart Trail to return indications of longer term trends.

### Reversal Zones [#reversal-zones]

<ResponseField name="Toggle" type="default: false" />

Enable the Reversal Zones overlay on the chart when toggled on.

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />
  </Expandable>
</ResponseField>

Reversal Zones numerical period. Greater values will allow the Reversal Zones to return indications of longer term trends.

### Trend Catcher [#trend-catcher]

<ResponseField name="Toggle" type="default: false" />

Enable the Trend Catcher overlay on the chart when toggled on.

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />
  </Expandable>
</ResponseField>

Trend Catcher numerical period. Greater values will allow the Trend Catcher to return indications of longer term trends.

### Trend Tracer [#trend-tracer]

<ResponseField name="Toggle" type="default: false" />

Enable the Trend Tracer overlay on the chart when toggled on.

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />
  </Expandable>
</ResponseField>

Trend Tracer numerical period. Greater values will allow the Trend Tracer to return indications of longer term trends.

### Neo Cloud [#neo-cloud]

<ResponseField name="Toggle" type="default: false" />

Enable the Neo Cloud overlay on the chart when toggled on.

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />
  </Expandable>
</ResponseField>

Neo Cloud numerical period. Greater values will allow the Neo Cloud to return indications of longer term trends.

## Take Profits/Stop Losses [#take-profitsstop-losses]

***

### TP/SL Levels [#tpsl-levels]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="None">
      No take profits/stop losses levels are displayed.
    </ResponseField>

    <ResponseField name="Signals">
      Return take profits/stop losses levels based on Confirmation/Contrarian signals.
    </ResponseField>

    <ResponseField name="Smart Trail">
      Return take profits/stop losses levels based on Smart Trail sentiment changes.
    </ResponseField>

    <ResponseField name="Reversal Zones">
      Return take profits/stop losses levels based on the contact between the price and Reversal Zones extremities.
    </ResponseField>

    <ResponseField name="Trend Catcher">
      Return take profits/stop losses levels based on the Trend Catcher sentiment changes.
    </ResponseField>

    <ResponseField name="Trend Tracer">
      Return take profits/stop losses levels based on the Trend Tracer sentiment changes.
    </ResponseField>

    <ResponseField name="Neo Cloud">
      Return take profits/stop losses levels based on the Neo Cloud sentiment changes.
    </ResponseField>

    <ResponseField name="Custom Alert Creator">
      Return take profits/stop losses levels based on the user set custom alert creator condition triggering. Unlike other options this one assumes a bullish sentiment, returning take profits above the price and stop losses below the price.
    </ResponseField>
  </Expandable>
</ResponseField>

### Distance [#distance]

<ResponseField name="Numerical Input" type="default: 5" />

Control how far away take profits/stop losses levels are located from the price, with higher values returning levels located farther away from the price.

## Advanced Settings [#advanced-settings]

***

### ML Classifier [#ml-classifier]

<ResponseField name="Numerical Input" type="default: 1234" />

ML Classifier filter. This input allows filtering out classified signals that are not specified in the input.

For example if the input only contains `12` then only signals classified as either 1 or 2 will be displayed.

### Autopilot Sensitivity [#autopilot-sensitivity]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Off">
      Disable the autopilot sensitivity algorithm.
    </ResponseField>

    <ResponseField name="Short-Term">
      Enable the autopilot sensitivity algorithm to return shorter term signals.
    </ResponseField>

    <ResponseField name="Medium-Term">
      Enable the autopilot sensitivity algorithm to return medium term signals.
    </ResponseField>

    <ResponseField name="Long-Term">
      Enable the autopilot sensitivity algorithm to return longer term signals.
    </ResponseField>
  </Expandable>
</ResponseField>

Allows enabling autopilot sensitivity for the selected signal algorithm. When autopilot is enabled changes in the `sensitivity` settings will not affect signals.

## Dashboard [#dashboard]

***

### Dashboard Location [#dashboard-location]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Hidden">
      Hide the dashboard on the chart.
    </ResponseField>

    <ResponseField name="Top Right">
      Display the dashboard to the top right of the chart.
    </ResponseField>

    <ResponseField name="Bottom Right">
      Display the dashboard to the bottom right of the chart.
    </ResponseField>

    <ResponseField name="Bottom Left">
      Display the dashboard to the bottom left of the chart.
    </ResponseField>
  </Expandable>
</ResponseField>

Dashboard location on the chart.

### Dashboard Size [#dashboard-size]

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Tiny" />

    <ResponseField name="Small" />

    <ResponseField name="Normal" />

    <ResponseField name="Large" />
  </Expandable>
</ResponseField>

Size of the dashboard on the chart.

### Trend Strength [#trend-strength]

<ResponseField name="Toggle" type="default: true" />

Display Trend Strength metric on the dashboard.

### Volatility [#volatility]

<ResponseField name="Toggle" type="default: false" />

Display the Lux Volatility metric on the dashboard.

### Squeeze [#squeeze]

<ResponseField name="Toggle" type="default: false" />

Display the squeeze index on the dashboard.

### Volume Sentiment [#volume-sentiment]

<ResponseField name="Toggle" type="default: false" />

Display the Volume Sentiment on the dashboard.

## Custom Alert Creator [#custom-alert-creator]

***

### Signal [#signal]

<ResponseField name="Toggle" type="default: false" />

Enable the selected signal condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Any Bullish Confirmation" />

    <ResponseField name="Any Bearish Confirmation" />

    <ResponseField name="Bullish Confirmation" />

    <ResponseField name="Bullish Confirmation+" />

    <ResponseField name="Bearish Confirmation" />

    <ResponseField name="Bearish Confirmation+" />

    <ResponseField name="Confirmation Uptrend" />

    <ResponseField name="Confirmation Downtrend" />

    <ResponseField name="Any Bullish Contrarian" />

    <ResponseField name="Any Bearish Contrarian" />

    <ResponseField name="Bullish Contrarian" />

    <ResponseField name="Bullish Contrarian+" />

    <ResponseField name="Bearish Contrarian" />

    <ResponseField name="Bearish Contrarian+" />

    <ResponseField name="Contrarian Uptrend" />

    <ResponseField name="Contrarian Downtrend" />
  </Expandable>
</ResponseField>

Specific type of signal used in the custom alert creator.

#### Step [#step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Signal condition step in the custom alert creator sequence.

### ML Classifier [#ml-classifier-1]

<ResponseField name="Toggle" type="default: false" />

Enable the ML Classifier to be used in the custom alert creator.

<ResponseField name="Numerical Input" type="default: 1234" />

Determines to which group a signal should be classified.

#### Step [#step-1]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

ML Classifier step in the custom alert creator sequence.

### Smart Trail [#smart-trail-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Smart Trail condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Smart Trail Bullish" />

    <ResponseField name="Smart Trail Bearish" />

    <ResponseField name="Smart Trail Switch Bullish" />

    <ResponseField name="Smart Trail Switch Bearish" />
  </Expandable>
</ResponseField>

Specific Smart Trail condition used in the custom alert creator.

#### Step [#step-2]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Smart Trail condition step in the custom alert creator sequence.

***

### Trend Tracer [#trend-tracer-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Trend Tracer condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Trend Tracer Bullish" />

    <ResponseField name="Trend Tracer Bearish" />

    <ResponseField name="Trend Tracer Switch Bullish" />

    <ResponseField name="Trend Tracer Switch Bearish" />
  </Expandable>
</ResponseField>

Specific Trend Tracer condition used in the custom alert creator.

#### Step [#step-3]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Trend Tracer condition step in the custom alert creator sequence.

***

### Trend Catcher [#trend-catcher-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Trend Catcher condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Trend Catcher Bullish" />

    <ResponseField name="Trend Catcher Bearish" />

    <ResponseField name="Trend Catcher Switch Bullish" />

    <ResponseField name="Trend Catcher Switch Bearish" />
  </Expandable>
</ResponseField>

Specific Trend Catcher condition used in the custom alert creator.

#### Step [#step-4]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Trend Catcher condition step in the custom alert creator sequence.

***

### Neo Cloud [#neo-cloud-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Neo Cloud condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Neo Cloud Bullish" />

    <ResponseField name="Neo Cloud Bearish" />

    <ResponseField name="Neo Cloud Switch Bullish" />

    <ResponseField name="Neo Cloud Switch Bearish" />

    <ResponseField name="Neo Cloud Lead Step Up" />

    <ResponseField name="Neo Cloud Lead Step Down" />

    <ResponseField name="Neo Cloud Lag Step Up" />

    <ResponseField name="Neo Cloud Lag Step Down" />
  </Expandable>
</ResponseField>

Specific Neo Cloud condition used in the custom alert creator.

#### Step [#step-5]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Neo Cloud condition step in the custom alert creator sequence.

### Reversal Zones [#reversal-zones-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Reversal Zones condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Within Upper Zone [R1-R3]" />

    <ResponseField name="Within Lower Zone [S1-S3]" />

    <ResponseField name="Entering Upper Zone [R1-R3]" />

    <ResponseField name="Entering Lower Zone [S1-S3]" />
  </Expandable>
</ResponseField>

Specific Reversal Zones condition used in the custom alert creator.

#### Step [#step-6]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Reversal Zones condition step in the custom alert creator sequence.

### Trend Strength [#trend-strength-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected Trend Strength condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the current Trend Strength value with the inline numerical input.

<ResponseField name="Numerical Input" type="default: 50" />

Value used to compare Trend Strength with.

#### Trend Strength Step [#trend-strength-step]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Trend Strength condition step in the custom alert creator sequence.

### External Condition (1) [#external-condition-1]

<ResponseField name="Toggle" type="default: false" />

Enable the selected External condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

First source input.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />

    <ResponseField name="Equal" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the two selected sources.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

Second source input.

#### External Step (1) [#external-step-1]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

First external source condition step in the custom alert creator sequence.

### External Condition (2) [#external-condition-2]

<ResponseField name="Toggle" type="default: false" />

Enable the selected External condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

First source input.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />

    <ResponseField name="Equal" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the two selected sources.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

Second source input.

#### External Step (2) [#external-step-2]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Second external source condition step in the custom alert creator sequence.

### External Condition (3) [#external-condition-3]

<ResponseField name="Toggle" type="default: false" />

Enable the selected External condition to be used in the custom alert creator.

<ResponseField name="Dropdown" type="Source">
  <Expandable title="Expand">
    <ResponseField name="open" />

    <ResponseField name="high" />

    <ResponseField name="low" />

    <ResponseField name="close" />

    ...

    <ResponseField name="Others">
      Other external indicator outputs can be used as source.
    </ResponseField>
  </Expandable>
</ResponseField>

Source input.

<ResponseField name="Dropdown" type="String">
  <Expandable title="Expand">
    <ResponseField name="Greater Than" />

    <ResponseField name="Lower Than" />

    <ResponseField name="Crossing Over" />

    <ResponseField name="Crossing Under" />

    <ResponseField name="Crossing" />

    <ResponseField name="Equal" />
  </Expandable>
</ResponseField>

Comparison operator used to compare the selected source to the selected numerical value.

<ResponseField name="Numerical Input" type="default:0" />

Numerical input.

#### External Step (3) [#external-step-3]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="1" />

    <ResponseField name="2" />

    <ResponseField name="3" />

    <ResponseField name="4" />

    <ResponseField name="5" />

    <ResponseField name="6" />

    <ResponseField name="7" />

    <ResponseField name="8" />

    <ResponseField name="9" />

    <ResponseField name="10" />

    <ResponseField name="All" />

    <ResponseField name="Invalidate" />

    <ResponseField name="OR" />
  </Expandable>
</ResponseField>

Third external source condition step in the custom alert creator sequence.

### Invalidation Behavior [#invalidation-behavior]

<ResponseField name="Dropdown" type="Numerical">
  <Expandable title="Expand">
    <ResponseField name="None">
      No invalidation behavior.
    </ResponseField>

    <ResponseField name="Invalidate On Step 1">
      The "Invalidate On Step 1" behavior allows to reset an incomplete sequence of conditions when the condition on step 1 trigger. This prevents the first step condition from happening in between other steps of the condition sequence.

      This behavior is useful when the first step of our sequence of conditions needs to never be repeated during the sequence.

      <Card title="Example">
        Let's take an example where we use 3 regular steps.

        <Steps>
          <Step title="Step 1">
            Step 1 condition trigger, we will now evaluate step 2 from now on.
          </Step>

          <Step title="Step 2">
            Step 2 condition trigger, we will now evaluate step 3 from now on.
          </Step>

          <Step title="Step 1 Trigger">
            Step 1 condition trigger, we start evaluating step 2 from now.
          </Step>
        </Steps>
      </Card>
    </ResponseField>

    <ResponseField name="Invalidate On Any Repeated Step">
      The "Invalidate On Any Repeated Step" behavior allows to reset an incomplete sequence of conditions when a step is triggered such that it does not respect the user-set order of conditions.

      This behavior is useful when we want a perfectly ordered sequence of conditions to complete, without any step repeating itself.

      <Card title="Example">
        Let's take an example where we use 3 regular steps.

        <Steps>
          <Step title="Step 1">
            Step 1 condition trigger, we will now evaluate step 2 from now on.
          </Step>

          <Step title="Step 2">
            Step 2 condition trigger, we will now evaluate step 3 from now on.
          </Step>

          <Step title="Step 1 or 2 Trigger">
            Step 1 or 2 conditions trigger, we start evaluating step 1 from now.
          </Step>
        </Steps>
      </Card>
    </ResponseField>
  </Expandable>
</ResponseField>

Invalidation behaviors allows adding more restrictions to a sequence of conditions, users can use two different invalidation behaviors described below.

### Maximum Step Interval [#maximum-step-interval]

<ResponseField name="Numerical Input" type="default:10" />

Maximum allowed horizontal distance (in bars) between two steps.

If the amount of bars since a step exceed the selected threshold, then the conditions sequence is reset, and we start evaluating from step 1 again.

### Highlight Custom Alert Triggers [#highlight-custom-alert-triggers]

<ResponseField name="Toggle" type="default:true" />

When a custom alert condition is set, a visual element is displayed at the bottom of the chart in order to more easily visualize when the custom alert conditions occurs (displayed by default).


# Signal Settings (/platform/algos/signals-overlays/signal-settings)



The settings affecting the signal generation algorithm within Signals & overlays plays an important role in using the signals effectively. We give our users the possibility to adjust the settings so that the signals can fit their trading style but we also provide ways to easily adjust settings by using a wide variety of methods.

Each method for adjusting signal settings is designed to fit specific user needs, one method will not necessarily be more accurate than another.

<Warning>
  It's critical to understand Confirmation Signals are generally meant to be used as trend confirmations, not direct entries & exits. Focusing on ease of use with the signal settings & using other features within our toolkits will likely be the best decision for you rather than trying to find the "holy grail" of optimized signal settings & following the signals alone.
</Warning>

## Manual Adjustments [#manual-adjustments]

Manual adjustment of signals settings allow the user to fit the signals to their trading style and allows a greater degree of customizability.

### Confirmation Signals [#confirmation-signals]

![](/images/lux-algo/signal-settings/image565.png)

Confirmation signals are affected by the **Signals Sensitivity** setting. Increasing this setting will allow the trader to make decisions for longer-term trends.

### Method For Manually Adjusting Confirmation Signals [#method-for-manually-adjusting-confirmation-signals]

First, identify which price movements you are looking to trade (regardless of timeframes). Depending on how you are on your charts (on any timeframe) you can adjust the sensitivity value higher or lower to catch the price movements you are looking for. If you are looking for shorter term price movements on those timeframes, you should consider using a lower sensitivity. If you are looking for longer term price movements on those timeframes you should consider using a higher sensitivity.

### Contrarian Signals [#contrarian-signals]

Contrarian signals are also affected by the **Signals Sensitivity** setting, increasing it will aim to make contrarian signals spot longer term tops and bottoms.

## Automatic Settings Estimation [#automatic-settings-estimation]

### Autopilot Signal Settings [#autopilot-signal-settings]

The downside of adjusting signal settings is that this task needs to be constantly repeated due to ever-changing market conditions. **Autopilot** is a feature that allows the user to avoid adjusting signal settings completely. Instead, the currently selected signal generation algorithm (Confirmation or Contrarian) adjusts dynamically to the market conditions in order to provide the best signals possible.

This setting can be enabled from the **Advanced Settings** settings section.

<Note>
  Compared to other automatic methods the "Autopilot" feature is the most user-friendly since it avoids adjusting inputs within the indicator entirely.
</Note>

[](/images/lux-algo/signal-settings/autopilotfreq.png)

The **Autopilot Sensitivity** advanced setting allows autopilot to adjust to shorter or longer term trends depending on user preference.

### Dashboard Optimal Sensitivity [#dashboard-optimal-sensitivity]

The Optimal Sensitivity is a value displayed on the Lux Dashboard showing the optimal **Sensitivity** setting when using Confirmation or Contrarian signal modes. This optimization is based on a grid search algorithm and as such will backtest every generated signals using a sensitivity ranging from 10 to 20 over the most recent 250 bars.

This feature works on any ticker & timeframe as long as at least 250 bars of historical data are available.

## Choosing The Right Method [#choosing-the-right-method]

While the optimal sensitivity provided by the dashboard is the most precise method since it's performing grid search, there is no "best" method for optimizing signals as it's up to the user to understand their style of trading and the price variations they want to trade (regardless of timeframes).

### Manual Adjustments [#manual-adjustments-1]

Do you specifically want to trade **shorter**-term trends on the timeframes you use?

* Consider using lower sensitivity values such as 5-10

Do you specifically want to trade **longer**-term trends on the timeframes you use?

* Consider using higher sensitivity values such as 20+

Alternatively, you can use the default of 12 for trading medium-term trends. This provides simple and responsive signals.

### Automatic Adjustments [#automatic-adjustments]

Do you rely a lot on optimizing signals and are you ok with updating these manually over time?

* Use the Optimal Sensitivity provided by the dashboard to find more precise signal settings.

Do you completely dislike the process of adjusting signal settings altogether?

* Use the Autopilot sensitivity to allow the signals to adjust dynamically and work without the need of any user interaction.

<Note>
  Using Autopilot or the default settings are overall the simplest user experience as it allows you to completely avoid adjusting settings altogether.
</Note>


# Signal Modes (/platform/algos/signals-overlays/signals)



The Signals & Overlays® toolkit displays signals that can indicate the current trend direction in the price and aim to support the analysis of a trader. There are two main signal modes available, each one fitting a different trading style, **Confirmation** signals, and **Contrarian** signals. The following sections on this page describe these two methodologies in detail.

<Note>
  Focusing on using other features within our toolkit alongside the provided signals will likely be the best decision for you rather than using them alone.
</Note>

## Confirmation + Exits [#confirmation--exits]

![Confirmation Signals](/images/lux-algo/signals/image470.png)

Confirmation signals are based on a performance adaptive trend following methodology. As the name suggests, they aim to confirm any potential action a trader might take and are more effective when used in confluence with other features like our indicator overlays.

There are two types of Confirmation signals, **normal** signals, and &#x2A;*strong (+)** signals. Strong (+) signals are signals that are in accordance with the current estimated trend, while normal signals can be caused by a retracement.

![](/images/lux-algo/signals/image316.png)

The Confirmation signals mode includes exit signals (represented by colored crosses). A bullish exit signal is displayed as a blue cross, while a bearish exit is displayed as an orange cross.

![](/images/lux-algo/signals/image592.png)

These exits signals aim to help traders find areas to consider exiting a position based on a previous confirmation signal at the highest price possible. Also, note that these exits would always exit a previous confirmation signal on a win.

<Note>
  Successive exit signals can occur during longer-term trends, as such the user can do partial exits instead of exiting an entire position. These exit signals can also be used within a user's analysis as potential reversal points, as such they do not necessarily have to be used as take profits using the Signals & Overlays toolkit.
</Note>

## Contrarian + Exits [#contrarian--exits]

![](/images/lux-algo/signals/image607.png)

Contrarian signals by design go in opposition to the current trend and aim to deliver extremely fast decision timing. More simply put, Contrarian signals aim to spot potential tops and bottoms.

The advantage of Contrarian methodologies is that they are less subject to lag than most trend-following methodologies, thus allowing for potentially higher profits, however, it is important to note that going against the trend exposes a trader to larger price variations, which means potentially **higher losses**.

![](/images/lux-algo/signals/image588.png)

Like Confirmation signals, some Contrarian signals can also be labeled as &#x2A;*strong (+)**. These strong signals occur when the price is excessively overbought or oversold and can have more of a chance of indicating a potential reversal.

<Note>
  Extra confluence such as declining volume or the occurrence of the signal near a significant support/resistance can be useful to tell when a signal is indicative of a reversal.

  You can also use the Contrarian signals more safely as an alternative exit point for a position. Timing tops/bottoms is very challenging, however, these can be useful for the natural reversal trader in combination with other features within the Signals & Overlays toolkit.
</Note>

The Contrarian signals mode also includes exit signals. A bullish exit signal is displayed as a blue cross, while a bearish exit is displayed as an orange cross.

![](/images/lux-algo/signals/contrarian_exits.png)

Like Confirmation signals, Contrarian exit signals would always exit a previous confirmation signal on a win. Do note that successive exits will not occur for Contrarian signals.

<Tip>
  Contrarian Exits can be particularly useful for when price temporarily moves in accordance with a displayed Contrarian signal before reversing, rendering the signal as false for a larger trend reversal.

  If the user is confident that a larger reversal might still occur in accordance with a previously displayed Contrarian signal, the exit can be ignored or could lead to a partial exit only for a "scalp" trade.
</Tip>

## ML Classifier [#ml-classifier]

![](/images/lux-algo/signals/classifier.png)

For users who want a deeper interpretation of signals, an adaptive threshold classifier is built-in to optionally enable at the top of the settings menu that automatically classifies both Confirmation & Contrarian signals between a range of 1 to 4 directly on the signal labels.

The displayed number indicates a degree of strength to your signals & can give user a deeper interpretation rather than just showing the base layer of normal vs strong. Do note the base layer of detecting normal vs strong trends is still displayed within the Confirmation Simple candle coloring mode which is on by default.

For Confirmation signals, lower-numbered signals (1-2) can be indicative of a potential reversal or retracement, while higher-numbered signals (3-4) can be more indicative of a trend continuation.

For Contrarian signals lower numbered signals can be indicative of an early trend with a lower chance of a larger reversal. Whereas higher numbers (3-4) will tend to occur on more developed trends, with a higher chance of larger trend reversal.

### ML Classifier Filter [#ml-classifier-filter]

![](/images/lux-algo/signals/classifier_settings.png)

Users can take their analysis even deeper by filtering out specific numbered signals from the advanced settings by entering the numbers that should stay on the chart. By default it shows "1234" as this would show all signals with the ML Classifier.

As an example, users could type into this box "12" to only display 1's and 2's on their chart. The same with "134" to only display 1's, 3's, and 4's on their chart depending on their preference for filtering specific signals.

## Display [#display]

It is possible to choose what type of signals you want to display here as well as displaying no signals at all, this is done from the settings menu in the **Signal Mode** drop-down menu.

![](/images/lux-algo/signals/image630.png)

By default, **Confirmation + Exits** is selected. **None** will not display any signal on the chart.

<Warning>
  Signals are confirmed at the opening of the next candle, if you see a signal displayed on the most recent candle remember that it might be subject to change.

  *CFTC Rule 4.41 - Hypothetical or Simulated performance results have certain limitations, unlike an actual performance record, simulated results do not represent actual trading. Also, since the trades have not been executed, the results may have under-or-over compensated for the impact, if any, of certain market factors, such as lack of liquidity. Simulated trading programs in general are also subject to the fact that they are designed with the benefit of hindsight. No representation is being made that any account will or is likely to achieve profit or losses similar to those shown.*
</Warning>


# TP/SL Points (/platform/algos/signals-overlays/tp-sl-points)



![](/images/lux-algo/tp-sl/settings.png)

The TP/SL feature displays 4 take profits/stop losses levels and aims to help the user set stop losses and take profits by estimating points of potential support/resistance from an initial user-selected condition.

![](/images/lux-algo/tp-sl/tpsl.png)

Take-profits/stop-loss levels are based on the option selected in the "TP/SL Levels" dropdown menu. Options include the following:

| Option                 | Description                                                                                                                                                                                                                                                                 |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Signals`              | Will calculate new take-profits/stop-losses when a new Confirmation or Contrarian signal is detected. If the signal is bullish, take profits will be located above the price and stop losses below; if bearish, the opposite applies.                                       |
| `Smart Trail`          | Will calculate new take-profits/stop-losses when the Smart Trail turns bullish or bearish. If it turns bullish, take profits will be located above the price and stop losses below; if bearish, the opposite applies.                                                       |
| `Reversal Zones`       | Will calculate new take-profits/stop-losses when the price reaches the Reversal Zones outer bands. If the price goes below the bottom zone, take profits will be located below the price and stop losses above; if the price goes above the top zone, the opposite applies. |
| `Trend Catcher`        | Will calculate new take-profits/stop-losses when the Trend Catcher turns bullish or bearish. If it turns bullish, take profits will be located above the price and stop losses below; if bearish, the opposite applies.                                                     |
| `Trend Tracer`         | Will calculate new take-profits/stop-losses when the Trend Tracer turns bullish or bearish. If it turns bullish, take profits will be located above the price and stop losses below; if bearish, the opposite applies.                                                      |
| `Neo Cloud`            | Will calculate new take-profits/stop-losses when the Neo Cloud turns bullish or bearish. If it turns bullish, take profits will be located above the price and stop losses below; if bearish, the opposite applies.                                                         |
| `Custom Alert Creator` | Will calculate new take-profits/stop-losses when the user-set custom alert creator condition is true.                                                                                                                                                                       |

When the price significantly deviates from the highest take profit or lowest stop loss, these will update, returning new relevant take profits and stop losses.

<Info>
  Users can notice when a tp/sl has updated based on where their levels start, if the levels start where the initial condition is located then it hasn't updated.
</Info>

![](/images/lux-algo/tp-sl/tpsl_dist.png)

Users can control the distance between the price and the take profits/stop losses using the "Distance" setting, with higher values returning take profits/stop losses farther away from the price.

<Tip>
  Make sure to adjust this setting according to the price variations you are trading. If you are trading large price variations, use a higher setting value.
</Tip>

### Alerts [#alerts]

Users can be alerted when the price reaches a take profit or stop loss level using the "Take Profit" and/or "Stop Loss" "any alert() function call" conditions. This alert triggers once per bar.

#### TP/SL Placeholders [#tpsl-placeholders]

Plots for accessing take profits and stop losses levels are available in order for users to set alerts based on them.

Use the placeholder `{{plot("Take Profit")}}` for accessing the nearest take profit value and `{{plot("Stop Loss")}}` for accessing the nearest stop loss value.

<Warning>
  *CFTC Rule 4.41 - Hypothetical or Simulated performance results have certain limitations, unlike an actual performance record, simulated results do not represent actual trading. Also, since the trades have not been executed, the results may have under-or-over compensated for the impact, if any, of certain market factors, such as lack of liquidity. Simulated trading programs in general are also subject to the fact that they are designed with the benefit of hindsight. No representation is being made that any account will or is likely to achieve profit or losses similar to those shown.*
</Warning>


# Entry Rules (/platform/ai-backtesting/backtesters/osc/entry)



![](/images/backtesters/osc/entry/settings.png)

Backtester (OSC) allows users to set their own long/short entry rules from features of the Oscillator Matrix® as well as external indicators.

Entry rules can be created separately for both long and short entries, and can be set using up to 8 different user set conditions. Each can be enabled by toggling them on using the toggle input on the left of each condition, if the enabled conditions are valid, the strategy will open a corresponding market order.

## Oscillator Matrix® Conditions [#oscillator-matrix-conditions]

Conditions for 6 features of the Oscillator Matrix® are included. These include:

* Money Flow conditions
* OverFlow conditions
* HyperWave conditions
* Reversal conditions
* Divergences conditions
* Confluence conditions

The first dropdown determines the specific condition used for that feature, these can include generic conditional operators such as `Greater Than` or `Crossing`, or conditions proper to certain features such as `Any Bullish` in the HyperWave conditions.

The second input setting allows the user to enter a numerical values, which is used when `Greater Than`, `Lower Than`, `Equal`, `Crossing Over`, `Crossing Under`, or `Crossing` are used.

For example, if the user wants to go **Long** when the HyperWave oscillator crosses over 80, the following settings would be used:

![](/images/backtesters/osc/entry/example-osc.png)

## External Conditions [#external-conditions]

![](/images/backtesters/osc/entry/externals.png)

Users can use the output of external indicators on the chart as input to set entry conditions.

The second dropdown of external condition settings determines which conditional operator to use on both external outputs. These include:

* Greater Than
* Lower Than
* Equal
* Crossing Over
* Crossing Under
* Crossing

The third external condition works similarly, but instead of comparing two external outputs, the condition will compare an external output specified in the condition first dropdown with a user set value in the third dropdown using a conditional operator set on the second dropdown. This last condition is particularly useful for oscillators.

Here is an example of the third external condition being set for an RSI crossing over 50.

![](/images/backtesters/osc/entry/example-external.png)

## Using Conditions Together [#using-conditions-together]

More complex entry rules can be created by using multiple conditions together, this is done thanks to the **Step** dropdown setting on the right of each condition (below each condition for conditions 4 and 5).

The *Step* setting is directly related to the Step & Match algorithm and work in two ways:

* When two or more conditions have the same step number, both conditions are evaluated. Used to test matching conditions.
* When two or more conditions have different step numbers, each conditions will be evaluated in order, testing for the first step and switching to the next step once the previous one is true. When the final step is true, the strategy will open a market order. Used to create a sequence of conditions.

This operation is complementary, as you can create a sequence of conditions with one step consisting of two or more matching conditions as long as they have the same step number.

<Tip>
  A user wanting to go long when a bullish divergence occurs after any bullish reversal while money flow was bullish (over 50) could do it as follows:

  ![](/images/backtesters/pac/entry/example-stepmatch.png)
</Tip>

### Invalidate Step [#invalidate-step]

The "Invalidate" step allows setting a condition as an "invalidation condition". When this condition is triggered while a sequence of conditions is incomplete, the sequence will restart at step 1.

If multiple steps are set as "Invalidate" any of the conditions being true will reset the sequence of conditions, as such not all of them are required to be true for the sequence of conditions to reset.

<Card title="Example">
  Let's take an example where we use 2 regular steps and one "Invalidate" step. The position will be opened when step 2 trigger after step 1 has been triggered.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Invalidation Step">
      Invalidation step condition trigger, we reset the sequence and evaluate step 1 just after.
    </Step>
  </Steps>
</Card>

### Invalidation Behaviors [#invalidation-behaviors]

Invalidation behaviors allow adding more restrictions to a sequence of conditions; users can use two different invalidation behaviors described below:

#### Invalidate On Step 1 [#invalidate-on-step-1]

The "Invalidate On Step 1" behavior allows resetting an incomplete sequence of conditions when the condition on step 1 triggers. This prevents the first step condition from happening in between other steps of the condition sequence.

This behavior is useful when the first step of our sequence of conditions needs to never be repeated during the sequence.

<Card title="Example">
  Let's take an example where we use 3 regular steps.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Step 2">
      Step 2 condition trigger, we will now evaluate step 3 from now on.
    </Step>

    <Step title="Step 1 Trigger">
      Step 1 condition trigger, we start evaluating step 2 from now.
    </Step>
  </Steps>
</Card>

#### Invalidate On Any Repeated Step [#invalidate-on-any-repeated-step]

The "Invalidate On Any Repeated Step" behavior allows to reset an incomplete sequence of conditions when a step is triggered such that it does not respect the set order of conditions.

This behavior is useful when we want a perfectly ordered sequence of conditions to complete, without any step repeating itself.

<Card title="Example">
  Let's take an example where we use 3 regular steps.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Step 2">
      Step 2 condition trigger, we will now evaluate step 3 from now on.
    </Step>

    <Step title="Step 1 or 2 Trigger">
      Step 1 or 2 conditions trigger, we start evaluating step 1 from now.
    </Step>
  </Steps>
</Card>

## Limit / Stop Orders [#limit--stop-orders]

Limit, stop, and stop-limit orders provide better control over the price at which a trade is executed.

These can be enabled for long and short positions respectively.

### Limit Orders [#limit-orders]

![](/images/backtesters/limit-stop/limit.png)

Limit orders execute at a specified price or better. For long positions, a limit order is generally placed below the current market price; for short positions, it is placed higher.

Limit orders can be enabled by toggling the "Long Limit Value" setting.

<Tip>
  Limit orders are represented by a dotted line.

  A dot is displayed to indicate when a long condition is true, thus updating the limit order price.
</Tip>

### Stop Orders [#stop-orders]

![](/images/backtesters/limit-stop/stop.png)

Stop orders execute at a specified price or worse. For long positions, a stop order is generally placed above the current market price; for short positions, it is placed lower.

Stop orders can be enabled by toggling the "Long Stop Value" setting.

<Tip>
  Stop orders are represented by a dashed line.

  A dot is displayed to indicate when a long condition is true, thus updating the stop order price.
</Tip>

### Stop-Limit Orders [#stop-limit-orders]

![](/images/backtesters/limit-stop/stop-limit.png)

Stop-limit orders use both limit and stop orders together, providing more control over the price at which a trade is executed.

In a stop-limit order, when the specified stop price is reached, the limit order becomes active. Once this limit price is reached, the trade is executed.

### Limit / Stop Orders Price [#limit--stop-orders-price]

![](/images/backtesters/limit-stop/long-settings.png)
![](/images/backtesters/limit-stop/short-settings.png)

The price of limit and stop orders is specified in "Long Limit Value"/"Short Limit Value" and "Long Stop Value"/"Short Stop Value," respectively. These settings are located at the bottom of the Long/Short Conditions settings groups.

Similar to TP/SL, various options to determine the order price are available:

|  Option  |                                                                         Description                                                                        |
| :------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------: |
|   Price  |                                          <div align="left">Set the limit/stop orders at the specified price.</div>                                         |
| Currency |                <div align="left">Set the limit/stop orders a specified amount of currency away from where the entry condition is met.</div>                |
|   Ticks  |                  <div align="left">Set the limit/stop orders a specified amount of ticks away from where the entry condition is met.</div>                 |
|     %    |                    <div align="left">Set the limit/stop orders a specified percentage away from where the entry condition is met.</div>                    |
|    ATR   | <div align="left">Set the limit/stop orders *N* average true ranges away from where the entry condition is met, where *N* is a specified multiplier.</div> |
| Forecast |          <div align="left">Use a percentile of a returned forecast to the order price. Forecasts must be enabled in order for this to work.</div>          |

For convenience, all options (except "Price") are adjusted based on the order type; for example, limit orders will be below the current price for longs and above for shorts, while stop orders will be above the price for longs and below for shorts. These can always be inverted using a negative value.

<Info>
  The price of a limit/stop order is updated every time the specified long/short condition is true.
</Info>

## No Existing Positions Requirement [#no-existing-positions-requirement]

Users can enable the **Don't Allow Trades Until Closed** setting in order to only open trade when no existing positions are open. This setting allows waiting for a position to be closed before one can be opened.

<Note>
  Enabling **Don't Allow Trades Until Closed** for long positions will prevent opening shorts as long as a long position is opened.

  If this same setting is not enabled for short conditions then shorts can effectively be closed by new long positions.
</Note>


# Exit Conditions (/platform/ai-backtesting/backtesters/osc/exits)



![](/images/backtesters/osc/exits/settings.png)

Users can close any position on a profit or loss using specific crossing conditions obtained from external sources. Exits conditions can be enabled in the **EXIT CONDITIONS** settings section.

<Note>
  Oscillators are intended to be used as external source when it comes to TP/SL conditions, but users can directly create scripts returning scaled outputs from overlay indicators.
</Note>

### Exit Take Profit [#exit-take-profit]

Long/Short take profits exits conditions will exit a long or short position when a set condition is met such that the position exit on a profit.

Take profits conditions are triggered if they are toggled on for a specific order (Long/Short) and if the selected external source is either **Crossing**, **Crossing Up**, or **Crossing Down**, **Greater Than**, **Lower Than**, or **Equal** to the numerical value specified by the user.

### Exit Stop Losses [#exit-stop-losses]

Long/Short stop losses exits conditions will exit a long or short order when a set condition is met such that the position exit on a loss.

Stop losses conditions are triggered if they are toggled on for a specific order (Long/Short) and if the selected external source is either **Crossing**, **Crossing Up**, or **Crossing Down**, **Greater Than**, **Lower Than**, or **Equal** to the numerical value specified by the user.

### Time Exits [#time-exits]

Time exits allow users to exit trade from a specific time of the day. Note that trades are still exited at the opening of the next bar.

<Note>
  The timezone is the same as the one of the chart symbol.
</Note>

### Placeholders [#placeholders]

![Exit Long Take Profit when closing price cross the upper Bollinger band extremity](/images/backtesters/osc/exits/placeholder.png)

Users who do not wish to use a numerical value to evaluate a condition with an external source may use a placeholder instead, which allows using prices. The following placeholders are supported:

* `open` : Opening price
* `high` : High price
* `low` : Low price
* `close` : Closing price
* `hl2` : <Tooltip tip="(high + low) / 2"> Median price </Tooltip>
* `ohlc4` : <Tooltip tip="(open + high + low + close) / 4"> Average price </Tooltip>
* `hlc3` : <Tooltip tip="(high + low + close) / 3"> Typical price </Tooltip>
* `hlcc4` : <Tooltip tip="(high + low + 2 * close) / 4"> Weighted price </Tooltip>

## Exit on Entry [#exit-on-entry]

Enabling the **Exit On Entry** setting will close any existing position when an entry condition leading to a new opposite position is met.

## Partial Exits [#partial-exits]

![](/images/backtesters/partial.png)

Users can exit a percentage of an existing position by specifying a percentage lower than 100%. This can lead to sequential exit, with reduced exposure each time an exit is triggered.

## Only Exit On A Profit [#only-exit-on-a-profit]

Enabling "Only Exit On A Profit" allows only triggering an exit if this exit leads to a profit, effectively preventing any exit that would lead to a loss.

This can be useful to increase winrates of strategies.

<Tip>
  Enabling this setting alongside "Dont allow trades until closed" can prevent any losing trade from being exited, leading to a theoretical 100% winrate without a stop-loss.

  Do note however that a unprofitable trade can be held for an unlimited amount of time, leading to unrealistic scenarios.
</Tip>


# Introduction (/platform/ai-backtesting/backtesters/osc/introduction)



Backtester (OSC) is a script that allows users to perform backtests using features from our [Oscillator Matrix®](/platform/algos/oscillator-matrix/introduction) toolkit. Users can create their own entry & exit conditions as well as control take-profit and stop-loss placement. Each core component of the backtester are described in the following pages:

<CardGroup cols="3">
  <Card title="Entry" icon="arrow-up-arrow-down" href="./entry" />

  <Card title="Exits" icon="xmark" href="./exits" />

  <Card title="Take Profits/Stop Losses" icon="shield" href="./tpsl" />
</CardGroup>

# Backtest Parameters [#backtest-parameters]

The Backtester (OSC) use the following default properties:

| Property                  | Default          | Description                                                                                                                                                                                                                                                                                                                              |
| ------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Backtesting Window        | Bars             | Determines how the backtesting interval is set. If `Bars` is selected the most recent specified amount (in `Window (Bars)`) will be used to backtest the strategy. If `Date` is selected the interval will be set using the specified starting and ending dates, while using "Entire History" will use all the available historical data |
| Window (Bars)             | 2000             | Amount of the most recent bars used to backtest the strategy if `Bars` is selected in `Backtesting Window`                                                                                                                                                                                                                               |
| Window Start              | 2024-01-01 00:00 | Starting date of the backtesting strategy if `Date` is selected in `Backtesting Window`                                                                                                                                                                                                                                                  |
| Window End                | 2024-01-01 00:00 | Ending date of the backtesting strategy if `Date` is selected in `Backtesting Window` and if the `Window End` toggle is enabled                                                                                                                                                                                                          |
| Initial Capital           | 10 000           | Initial amount of funds available at the start of the backtest                                                                                                                                                                                                                                                                           |
| Base Currency             | Default          | Currency used for performing the backtest, backtest statistics will be expressed using this curreny. If `Default` is selected the symbol currency is used                                                                                                                                                                                |
| Order Size                | 1 Contract       | Determines the amount of contracts/currency to buy or sell, can be expressed in contracts, currency, or percent of equity                                                                                                                                                                                                                |
| Commission                | 0%               | Fees paid per clotured trades, can be expressed as currency per contracts, currency per order, or % of the total transation value                                                                                                                                                                                                        |
| Margin for long position  | 0%               | Equity percentage required to fund a position                                                                                                                                                                                                                                                                                            |
| Margin for short position | 0%               | Equity percentage required to fund a position                                                                                                                                                                                                                                                                                            |

These should be adjusted to return more precise and accurate results of the real performance of a trading strategy.

# Disclaimer [#disclaimer]

<Warning>
  Backtests are not indicative of future results. Backtesting strategies on synthetic data does not return representative results of a strategy. Backtests should be performed on charts returning real closing prices. See [here](https://www.tradingview.com/script/q9laJNG9-Backtesting-on-Non-Standard-Charts-Caution-PineCoders-FAQ/) for more information.

  *CFTC Rule 4.41 - Hypothetical or Simulated performance results have certain limitations, unlike an actual performance record, simulated results do not represent actual trading. Also, since the trades have not been executed, the results may have under-or-over compensated for the impact, if any, of certain market factors, such as lack of liquidity. Simulated trading programs in general are also subject to the fact that they are designed with the benefit of hindsight. No representation is being made that any account will or is likely to achieve profit or losses similar to those shown.*
</Warning>


# Take Profit & Stop Loss (/platform/ai-backtesting/backtesters/osc/tpsl)



![](/images/backtesters/tpsl.png)

Users can set take profits and stop losses orders allowing a certain degree of money management in the backtest. Take profits and stop loss orders can be enabled and adjusted from the **FIXED TPS & SLS** settings section.

<Note>
  Take profits and stop losses orders are placed relative to the price preceding a trade, and might not be filled at the exact price set by the user.
</Note>

## TP/SL Options [#tpsl-options]

Take profits and stop losses for specific orders can be enabled from the toggles present in their settings section. Take profits and stop losses can be set using various methods described below:

|     Option    |                                                                                            Description                                                                                           |
| :-----------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|     Price     |                                                       <div align="left">Set the take profit/stop loss orders at the specified price.</div>                                                       |
|    Currency   |                <div align="left">Set the take profit/stop loss orders a specified amount of currency away from the closing price preceding the entry of a long/short order.</div>                |
|     Ticks     |                  <div align="left">Set the take profit/stop loss orders a specified amount of ticks away from the closing price preceding the entry of a long/short order.</div>                 |
|       %       |                    <div align="left">Set the take profit/stop loss orders a specified percentage away from the closing price preceding the entry of a long/short order.</div>                    |
|      ATR      | <div align="left">Set the take profit/stop loss orders *N* average true ranges away from the closing price preceding the entry of a long/short order, where *N* is a specified multiplier.</div> |
|    Forecast   |        <div align="left">(Only available for take profits orders) Use a percentile of a returned forecast to set take profits. Forecasts must be enabled in order for this to work.</div>        |
| Trailing Stop |                <div align="left">(Only available for stop loss orders) Enable a percentage based trailing stop. A position will be exited if price reach the trailing stop.</div>                |

<Note>
  The average true range used to set take profits/stop losses orders make use of a period of 200 bars.
</Note>

### Trailing Stop [#trailing-stop]

![](/images/backtesters/trailing.png)

Users can use a percentage based trailing stop to exit positions. The trailing stop will move in the direction of interest if its distance with the low price(in case of long positions) or high price (in case of short positions) is greater than the specified percentage.

### Take Profits Levels From Forecast [#take-profits-levels-from-forecast]

When forecasts are displayed, users can set take profits using the **nth** percentile of a returned forecast. This can be done from the "TPS & SLS" settings, by selecting "Forecast" in the "Long TP" and/or "Short TP".

The numerical setting selected at the right of the drop-down menus determines the percentile used, and should be set within the range \[0, 100] in order to work. Percentiles values can affect the returned levels as follows:

* In case of a long trade, a lower set percentile will return take profits closer to the entry price, potentially triggering sooner.
* In case of a short trade, a lower set percentile will return take profits further away from the entry price, potentially triggering later.

<Warning>
  Do note that because forecasts are based on the strategy entries, take profits levels based on it can be subject to repainting, except when using a backtesting window based on a date.
</Warning>


# Entry Rules (/platform/ai-backtesting/backtesters/pac/entry)



Backtester (PAC) allows users to set their own long/short entry rules from price action related concepts as well as external indicators. Entry rules can be built as sequences of conditions, matching conditions, or a mix of both. More details are provided in the section below.

Entry rules can be created separately for both long and short entries, and can be set using up to 6 different user set conditions. Each can enabled by toggling them on using the toggle input on the left of each condition, if the enabled conditions are valid, the strategy will open a corresponding market order.

Three types of distinct conditions are available, each described in the sections below.

## Price Action Conditions [#price-action-conditions]

![](/images/backtesters/pac/entry/pac-settings.png)

Conditions 1, 2, and 3 work the same way, and allow using conditions from market structures, volumetric order blocks, imbalances, and liquidity grabs.

The first dropdown includes options: *Bullish* and *Bearish*, and determines the origin of the selected condition in the second dropdown.

The second dropdown includes the list of price action related conditions, these include:

|      Condition      |                                                                        Description                                                                        |
| :-----------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------: |
|     CHoCH or BOS    |                                                  Triggered on a change of character or break of structure                                                 |
|        CHoCH        |                                                             Triggered on a change of character                                                            |
|         BOS         |                                                             Triggered on a break of structure                                                             |
|     OB Mitigated    |                                                       Triggered once price mitigates an order block                                                       |
|      OB Within      |                                                  Triggered if price is within the area of an order block                                                  |
|      OB Entered     |                                                   Triggered once price enters the area of an order block                                                  |
|      OB Exited      |                                                   Triggered once price exits the area of an order block                                                   |
|  Imbalance Entered  |                                                    Triggered once price enters the area of an imbalance                                                   |
|   Imbalance Within  |                                                   Triggered if price is within the area of an imbalance                                                   |
| Imbalance Mitigated |                                                        Triggered once price mitigates an imbalance                                                        |
|   Imbalance Exited  |                                                          Triggered once price exits an imbalance                                                          |
|    Liquidity Grab   |                                                        Triggered once a liquidity grab is detected                                                        |
|   Trendline Break   | Triggered once a trendline is broken. Note that a bullish trendlinereffers to an uptrending trendline, while Bearish reffers to a downtrending trendline. |

For example, if the user wants to go **Long** when a new **Bullish** **CHoCH** the following settings would be used:

![](/images/backtesters/pac/entry/example-pac.png)

<Note>
  User can change the settings of each price action concept by scrolling down the settings, each concept has its own setting group. To learn more about each concept see:

  [Market Structures](/platform/algos/price-action-concepts/market-structures)

  [Order Blocks](/platform/algos/price-action-concepts/order-blocks)

  [Imbalances](/platform/algos/price-action-concepts/imbalances)

  [Liquidity Grabs](/platform/algos/price-action-concepts/liquidity)
</Note>

## External Conditions [#external-conditions]

![](/images/backtesters/pac/entry/external-settings.png)

Users can use the output of external indicators on the chart as input to set entry conditions.

The second dropdown of external condition settings determines which conditional operator to use on both external outputs. These include:

* Greater Than
* Lower Than
* Equal
* Crossing Over
* Crossing Under
* Crossing

The third external condition works similarly but instead of comparing two external outputs the condition will compare an external output specified in the condition first dropdown with a user set value in the third dropdown using a conditional operator set on the second dropdown. This last condition is particularly useful for oscillators.

Here is an example of the third external condition being set for price crossing over a simple moving average.

![](/images/backtesters/pac/entry/example-external.png)

## Session Condition [#session-condition]

![](/images/backtesters/pac/entry/session.png)

Condition 6 is a session condition, with an active status if current time is within the specified session interval.

<Note>
  Session timezone is the same as the one of the chart symbol exchange (UTC for cryptocurrencies)
</Note>

## Using Conditions Together [#using-conditions-together]

More complex entry rules can be created by using multiple conditions together, this is done thanks to the **Step** dropdown setting on the right of each condition (below each condition for conditions 4 and 5).

The *Step* setting is directly related to the Step & Match algorithm and work in two ways:

* When two or more conditions have the same step number, both conditions are evaluated. Used to test matching conditions.
* When two or more conditions have different step numbers, each conditions will be evaluated in order, testing for the first step and switching to the next step once the previous one is true. When the final step is true the strategy will open a market order. Used to create sequence of conditions.

This operation is complementary, as you can create a sequence of conditions with one step consisting of two or more matching conditions as long as they have the same step number.

<Tip>
  A user wanting to go long when a bullish break of structure occurs after price mitigated a bearish order block while price was above its 20 period moving average could do it as follows:

  ![](/images/backtesters/pac/entry/example-stepmatch.png)
</Tip>

### Invalidate Step [#invalidate-step]

The "Invalidate" step allows to set a condition as an "invalidation condition". When this condition is triggered while a sequence of conditions is incomplete, the sequence will restart at step 1.

If multiple steps are set as "Invalidate" any of the conditions being true will reset the sequence of conditions, as such not all of them are required to be true for the sequence of conditions to reset.

<Card title="Example">
  Let's take an example where we use 2 regular steps and one "Invalidate" step. The position will be opened when step 2 trigger after step 1 has been triggered.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Invalidation Step">
      Invalidation step condition trigger, we reset the sequence and evaluate step 1 just after.
    </Step>
  </Steps>
</Card>

### Invalidation Behaviors [#invalidation-behaviors]

Invalidation behaviors allows adding more restrictions to a sequence of conditions, users can use two different invalidation behaviors described below:

#### Invalidate On Step 1 [#invalidate-on-step-1]

The "Invalidate On Step 1" behavior allows to reset an incomplete sequence of conditions when the condition on step 1 trigger. This prevents the first step condition from happening in between other steps of the condition sequence.

This behavior is useful when the first step of our sequence of conditions needs to never be repeated during the sequence.

<Card title="Example">
  Let's take an example where we use 3 regular steps.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Step 2">
      Step 2 condition trigger, we will now evaluate step 3 from now on.
    </Step>

    <Step title="Step 1 Trigger">
      Step 1 condition trigger, we start evaluating step 2 from now.
    </Step>
  </Steps>
</Card>

#### Invalidate On Any Repeated Step [#invalidate-on-any-repeated-step]

The "Invalidate On Any Repeated Step" behavior allows to reset an incomplete sequence of conditions when a step is triggered such that it does not respect the set order of conditions.

This behavior is useful when we want a perfectly ordered sequence of conditions to complete, without any step repeating itself.

<Card title="Example">
  Let's take an example where we use 3 regular steps.

  <Steps>
    <Step title="Step 1">
      Step 1 condition trigger, we will now evaluate step 2 from now on.
    </Step>

    <Step title="Step 2">
      Step 2 condition trigger, we will now evaluate step 3 from now on.
    </Step>

    <Step title="Step 1 or 2 Trigger">
      Step 1 or 2 conditions trigger, we start evaluating step 1 from now.
    </Step>
  </Steps>
</Card>

## Limit / Stop Orders [#limit--stop-orders]

Limit, stop, and stop-limit orders provide better control over the price at which a trade is executed.

These can be enabled for long and short positions respectively.

### Limit Orders [#limit-orders]

![](/images/backtesters/limit-stop/limit.png)

Limit orders execute at a specified price or better. For long positions, a limit order is generally placed below the current market price; for short positions, it is placed higher.

Limit orders can be enabled by toggling the "Long Limit Value" setting.

<Tip>
  Limit orders are represented by a dotted line.

  A dot is displayed to indicate when a long condition is true, thus updating the limit order price.
</Tip>

### Stop Orders [#stop-orders]

![](/images/backtesters/limit-stop/stop.png)

Stop orders execute at a specified price or worse. For long positions, a stop order is generally placed above the current market price; for short positions, it is placed lower.

Stop orders can be enabled by toggling the "Long Stop Value" setting.

<Tip>
  Stop orders are represented by a dashed line.

  A dot is displayed to indicate when a long condition is true, thus updating the stop order price.
</Tip>

### Stop-Limit Orders [#stop-limit-orders]

![](/images/backtesters/limit-stop/stop-limit.png)

Stop-limit orders use both limit and stop orders together, providing more control over the price at which a trade is executed.

In a stop-limit order, when the specified stop price is reached, the limit order becomes active. Once this limit price is reached, the trade is executed.

### Limit / Stop Orders Price [#limit--stop-orders-price]

![](/images/backtesters/limit-stop/long-settings.png)
![](/images/backtesters/limit-stop/short-settings.png)

The price of limit and stop orders is specified in "Long Limit Value"/"Short Limit Value" and "Long Stop Value"/"Short Stop Value," respectively. These settings are located at the bottom of the Long/Short Conditions settings groups.

Similar to TP/SL, various options to determine the order price are available:

|  Option  |                                                                         Description                                                                        |
| :------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------: |
|   Price  |                                          <div align="left">Set the limit/stop orders at the specified price.</div>                                         |
| Currency |                <div align="left">Set the limit/stop orders a specified amount of currency away from where the entry condition is met.</div>                |
|   Ticks  |                  <div align="left">Set the limit/stop orders a specified amount of ticks away from where the entry condition is met.</div>                 |
|     %    |                    <div align="left">Set the limit/stop orders a specified percentage away from where the entry condition is met.</div>                    |
|    ATR   | <div align="left">Set the limit/stop orders *N* average true ranges away from where the entry condition is met, where *N* is a specified multiplier.</div> |
| Forecast |          <div align="left">Use a percentile of a returned forecast to the order price. Forecasts must be enabled in order for this to work.</div>          |

For convenience, all options (except "Price") are adjusted based on the order type; for example, limit orders will be below the current price for longs and above for shorts, while stop orders will be above the price for longs and below for shorts. These can always be inverted using a negative value.

<Info>
  The price of a limit/stop order is updated every time the specified long/short condition is true.
</Info>

## No Existing Positions Requirement [#no-existing-positions-requirement]

Users can enable the **Don't Allow Trades Until Closed** setting in order to only open trade when no existing positions are open. This setting allows waiting for a position to be closed before one can be opened.

<Note>
  Enabling **Don't Allow Trades Until Closed** for long positions will prevent opening shorts as long as a long position is opened.

  If this same setting is not enabled for short conditions then shorts can effectively be closed by new long positions.
</Note>


# Exit Rules (/platform/ai-backtesting/backtesters/pac/exits)



The strategy can include custom long/short orders exits rules. These can be created in a similar way than entry rules by using two available conditions, and can evaluate price action features as well as the output of external indicators on your chart.

## Price Action Conditions [#price-action-conditions]

![](/images/backtesters/pac/exits/settings.png)

Conditions *Exit Long 1* and *Exit Short 1* allow using conditions from market structures, volumetric order blocks, and imbalances.

The first dropdown includes options: *Bullish* and *Bearish* and determines the origin of the selected condition in the second dropdown.

The second dropdown includes the list of price action related conditions, these include:

|      Condition      |                        Description                       |
| :-----------------: | :------------------------------------------------------: |
|     CHoCH or BOS    | Triggered on a change of character or break of structure |
|        CHoCH        |            Triggered on a change of character            |
|         BOS         |             Triggered on a break of structure            |
|     Mitigated OB    |       Triggered once price mitigates an order block      |
|      Within OB      |  Triggered if price is within the area of an order block |
|      Entered OB     |  Triggered once price enters the area of an order block  |
|  Entered Imbalance  |   Triggered once price enters the area of an imbalance   |
|   Within Imbalance  |   Triggered if price is within the area of an imbalance  |
| Mitigated Imbalance |        Triggered once price mitigates an imbalance       |

## External Conditions [#external-conditions]

Conditions *Exit Long 2* and *Exit Short 2*  allow the user to use the output of external indicators on the chart as input for setting exit conditions.

These allow setting a condition based on two different external indicators output, with The first and third dropdowns determining which external output to use.

The second dropdown determines which conditional operator to use on both external outputs. These include:

* Greater Than
* Lower Than
* Equal
* Crossing Over
* Crossing Under
* Crossing

## Using Conditions Together [#using-conditions-together]

More complex exit rules can be created by using multiple conditions together, this is done thanks to the **Step** dropdown setting on the right of each condition (below each condition for *Exit Long 2* and *Exit Short 2*).

The *Step* setting is directly related to the Step & Match algorithm and works in two ways:

* When two or more conditions have the same step number, both conditions are evaluated. Used to test matching conditions.
* When two or more conditions have different step numbers, each condition will be evaluated in order, testing for the first step and switching to the next step once the previous one is true. When the final step is true the strategy will open a market order. Used to create sequence of conditions.

This operation is complementary, as you can create a sequence of conditions with one step consisting of two or more matching conditions as long as they have the same step number.

## Time Exits [#time-exits]

Time exits allow users to exit trade from a specific time of the day. Note that trades are still exited at the opening of the next bar.

<Note>
  The timezone is the same as the one of the chart symbol.
</Note>

## Exit on Entry [#exit-on-entry]

Enabling the **Exit On Entry** setting will close any existing position when an entry condition leading to a new opposite position is met.

## Partial Exits [#partial-exits]

![](/images/backtesters/partial.png)

Users can exit a percentage of an existing position by specifying a percentage lower than 100%. This can lead to sequential exit, with reduced exposure each time an exit is triggered.

## Only Exit On A Profit [#only-exit-on-a-profit]

Enabling "Only Exit On A Profit" allows only triggering an exit if this exit leads to a profit, effectively preventing any exit that would lead to a loss.

This can be useful to increase winrates of strategies.

<Tip>
  Enabling this setting alongside "Dont allow trades until closed" can prevent any losing trade from being exited, leading to a theoretical 100% winrate without a stop-loss.

  Do note however that a unprofitable trade can be held for an unlimited amount of time, leading to unrealistic scenarios.
</Tip>


# Introduction (/platform/ai-backtesting/backtesters/pac/introduction)



Backtester (PAC) is a script that allows users to perform advanced backtests on all features from our [Price Action Concepts®](/platform/algos/price-action-concepts/introduction) toolkit. Users can create their own entry & exit conditions as well as control take-profit & stop-loss placement. Each core components of the system is described on the following pages:

<CardGroup cols="3">
  <Card title="Entry" icon="arrow-up-arrow-down" href="./entry" />

  <Card title="Exits" icon="xmark" href="./exits" />

  <Card title="Take Profits/Stop Losses" icon="shield" href="./tpsl" />
</CardGroup>

# Backtest Parameters [#backtest-parameters]

The Backtester (PAC) uses the following default properties:

| Property                  | Default          | Description                                                                                                                                                                                                                                                                                                                              |
| ------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Backtesting Window        | Bars             | Determines how the backtesting interval is set. If `Bars` is selected the most recent specified amount (in `Window (Bars)`) will be used to backtest the strategy. If `Date` is selected the interval will be set using the specified starting and ending dates, while using "Entire History" will use all the available historical data |
| Window (Bars)             | 2000             | Amount of the most recent bars used to backtest the strategy if `Bars` is selected in `Backtesting Window`                                                                                                                                                                                                                               |
| Window Start              | 2024-01-01 00:00 | Starting date of the backtesting strategy if `Date` is selected in `Backtesting Window`                                                                                                                                                                                                                                                  |
| Window End                | 2024-01-01 00:00 | Ending date of the backtesting strategy if `Date` is selected in `Backtesting Window` and if the `Window End` toggle is enabled                                                                                                                                                                                                          |
| Initial Capital           | 10 000           | Initital amount of funds available at the start of the backtest                                                                                                                                                                                                                                                                          |
| Base Currency             | Default          | Currency used for performing the backtest, backtest statistics will be expressed using this curreny. If `Default` is selected the symbol currency is used                                                                                                                                                                                |
| Order Size                | 1 Contract       | Determines the amount of contracts/currency to buy or sell, can be expressed in contracts, currency, or percent of equity                                                                                                                                                                                                                |
| Commission                | 0%               | Fees paid per clotured trades, can be expressed as currency per contracts, currency per order, or % of the total transaction value                                                                                                                                                                                                       |
| Margin for long position  | 0%               | Equity percentage required to fund a position                                                                                                                                                                                                                                                                                            |
| Margin for short position | 0%               | Equity percentage required to fund a position                                                                                                                                                                                                                                                                                            |

These should be adjusted to return more precise and accurate results of the real performance of a trading strategy.

# Disclaimer [#disclaimer]

<Warning>
  Backtests are not indicative of future results. Backtesting strategies on synthetic data does not return representative results of a strategy. Backtests should be performed on charts returning real closing prices. See [here](https://www.tradingview.com/script/q9laJNG9-Backtesting-on-Non-Standard-Charts-Caution-PineCoders-FAQ/) for more information.

  *CFTC Rule 4.41 - Hypothetical or Simulated performance results have certain limitations, unlike an actual performance record, simulated results do not represent actual trading. Also, since the trades have not been executed, the results may have under-or-over compensated for the impact, if any, of certain market factors, such as lack of liquidity. Simulated trading programs in general are also subject to the fact that they are designed with the benefit of hindsight. No representation is being made that any account will or is likely to achieve profit or losses similar to those shown.*
</Warning>


# Take Profit & Stop Loss (/platform/ai-backtesting/backtesters/pac/tpsl)



![](/images/backtesters/tpsl.png)

Users can set take profits and stop losses orders allowing a certain degree of money management in the backtest. Take profits and stop loss orders can be enabled and adjusted from the **FIXED TPS & SLS** settings section.

<Note>
  Take profits and stop losses orders are placed relative to the price preceding a trade, and might not be filled at the exact price set by the user.
</Note>

## TP/SL Options [#tpsl-options]

Take profits and stop losses for specific orders can be enabled from the toggles present in their settings section. Take profits and stop losses can be set using various methods described below:

|     Option    |                                                                                            Description                                                                                           |
| :-----------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|     Price     |                                                       <div align="left">Set the take profit/stop loss orders at the specified price.</div>                                                       |
|    Currency   |                <div align="left">Set the take profit/stop loss orders a specified amount of currency away from the closing price preceding the entry of a long/short order.</div>                |
|     Ticks     |                  <div align="left">Set the take profit/stop loss orders a specified amount of ticks away from the closing price preceding the entry of a long/short order.</div>                 |
|       %       |                    <div align="left">Set the take profit/stop loss orders a specified percentage away from the closing price preceding the entry of a long/short order.</div>                    |
|      ATR      | <div align="left">Set the take profit/stop loss orders *N* average true ranges away from the closing price preceding the entry of a long/short order, where *N* is a specified multiplier.</div> |
|    Forecast   |        <div align="left">(Only available for take profits orders) Use a percentile of a returned forecast to set take profits. Forecasts must be enabled in order for this to work.</div>        |
| Trailing Stop |                <div align="left">(Only available for stop loss orders) Enable a percentage based trailing stop. A position will be exited if price reach the trailing stop.</div>                |

<Note>
  The average true range used to set take profits/stop losses orders make use of a period of 200 bars.
</Note>

### Trailing Stop [#trailing-stop]

![](/images/backtesters/trailing.png)

Users can use a percentage based trailing stop to exit positions. The trailing stop will move in the direction of interest if its distance with the low price(in case of long positions) or high price (in case of short positions) is greater than the specified percentage.

### Take Profits Levels From Forecast [#take-profits-levels-from-forecast]

When forecasts are displayed, users can set take profits using the **nth** percentile of a returned forecast. This can be done from the "TPS & SLS" settings, by selecting "Forecast" in the "Long TP" and/or "Short TP".

The numerical setting selected at the right of the drop-down menus determines the percentile used, and should be set within the range \[0, 100] in order to work. Percentiles values can affect the returned levels as follows:

* In case of a long trade, a lower set percentile will return take profits closer to the entry price, potentially triggering sooner.
* In case of a short trade, a lower set percentile will return take profits further away from the entry price, potentially triggering later.

<Warning>
  Do note that because forecasts are based on the strategy entries, take profits levels based on it can be subject to repainting, except when using a backtesting window based on a date.
</Warning>


# Entry Rules (/platform/ai-backtesting/backtesters/s-o/entry)



![](/images/backtesters/s-o/entry/settings.png)

The Signals & Overlays (S\&O) Backtester makes use of various features within the Signals & Overlays® toolkit, such as [Confirmation and Contrarian Signals](/platform/algos/signals-overlays/signals) or [Overlays indicators](/platform/algos/signals-overlays/indicator-overlay), to specify entry rules for your strategy.

<Tip>
  More information regarding signal settings can be found [here](/platform/algos/signals-overlays/signal-settings)
</Tip>

By default, backtesting is performed using the most recent 2,000 bars. This value can be changed from the &#x2A;*Backtest Window (Bars)** setting. Users can also perform a backtest between two specified dates using the "Date" options in "Backtesting Window", or use all available chart history via "Entire History" (note that this will disable the optimizer).

## Long / Short Entry Conditions [#long--short-entry-conditions]

Users can specify the long and short entry rules for their strategy from the **LONG CONDITIONS** and **SHORT CONDITIONS** settings sections, using the following available options:

|       Condition      |                                                                                                   Options                                                                                                   |                                                                                                            Options (2)                                                                                                            |                                                                                                                                                                                                                                              Description                                                                                                                                                                                                                                              |
| :------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|      Signal Type     |                                                               <ul style="{ textAlign: 'left' }"><li>Confirmation</li><li>Contrarian</li></ul>                                                               | <ul style="{ textAlign: 'left' }"><li>Any Bullish</li><li>Any Bearish</li><li>Normal Bullish</li><li>Normal Bearish</li><li>Strong Bullish</li><li>Strong Bearish</li><li>Bullish Classifier</li><li>Bearish Classifier</li></ul> | <div align="left"> Enter a position on the occurrence of the specified signal. Users can select between the Confirmation and Contrarian signals algorithm (Long/Short conditions can use different algorithms), which are affected by the selected Sensitivity and Autopilot frequency settings.<br /><br />Selecting "Bullish Classifier" or "Bearish Classifier" will open positions on the occurrence of a signal with the same classification as the one specified in the rightmost input. </div> |
|    Reversal Zones    |     Cross Over R3, Cross Over R2, Cross Over R1, Cross Under S1, Cross Under S2, Cross Under S3, Above R3, Within \[R3-R2], Within \[R2-R1], Within \[R1-S1], Within \[S1-S2], Within \[S2-S3], Under S3    |                                                               <div align="left">Enter a position if price is above/under or within the specified extremities.</div>                                                               |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
|      Smart Trail     |                                            <ul style="{ textAlign: 'left' }"><li>Bullish</li><li>Bearish</li><li>Switch Bullish</li><li>Switch Bearish</li></ul>                                            |                                                                                                                                                                                                                                   |                                                                                                                                                                                    <div align="left"> Enter a position if the Smart Trail switches bullish/bearish or if it is currently bullish or bearish.</div>                                                                                                                                                                                    |
|     Trend Tracer     |                                            <ul style="{ textAlign: 'left' }"><li>Bullish</li><li>Bearish</li><li>Switch Bullish</li><li>Switch Bearish</li></ul>                                            |                                                                                                                                                                                                                                   |                                                                                                                                                                                    <div align="left"> Enter a position if the Trend Tracer switches bullish/bearish or if it is currently bullish or bearish.</div>                                                                                                                                                                                   |
|     Trend Catcher    |                                            <ul style="{ textAlign: 'left' }"><li>Bullish</li><li>Bearish</li><li>Switch Bullish</li><li>Switch Bearish</li></ul>                                            |                                                                                                                                                                                                                                   |                                                                                                                                                                                   <div align="left"> Enter a position if the Trend Catcher switches bullish/bearish or if it is currently bullish or bearish.</div>                                                                                                                                                                                   |
|       Neo Cloud      | <ul style="{ textAlign: 'left' }"><li>Bullish</li><li>Bearish</li><li>Switch Bullish</li><li>Switch Bearish</li><li>Lead Step Up</li><li>Lead Step Down</li><li>Lag Step Up</li><li>Lag Step Down</li></ul> |                                                                                                                                                                                                                                   |                                                                                                                                                                                     <div align="left"> Enter a position if the Neo Cloud switches bullish/bearish or if it is currently bullish or bearish.</div>                                                                                                                                                                                     |
|    Trend Strength    |                                                               <ul style="{ textAlign: 'left' }"><li>Greater Than</li><li>Lower Than</li></ul>                                                               |                                                                                                                                                                                                                                   |                                                                                                                                                                                             <div align="left"> Enter a position if the Trend Strength is greater or lower than the specified value.</div>                                                                                                                                                                                             |
|        Session       |                                                                                               Session interval                                                                                              |                                                                                                                                                                                                                                   |                                                                                                                                                                                                       <div align="left"> Enter a position if it is within the specified session interval.</div>                                                                                                                                                                                                       |
| External Condition 1 |                         <ul style="{ textAlign: 'left' }"><li>Greater Than</li><li>Lower Than</li><li>Equal</li><li>Crossing Over</li><li>Crossing Under</li><li>Crossing</li></ul>                         |                                                                                                                                                                                                                                   |                                                                                                                                                                                      <div align="left"> Enter a position if the condition between the first and second specified external sources is true.</div>                                                                                                                                                                                      |
| External Condition 2 |                         <ul style="{ textAlign: 'left' }"><li>Greater Than</li><li>Lower Than</li><li>Equal</li><li>Crossing Over</li><li>Crossing Under</li><li>Crossing</li></ul>                         |                                                                                                                                                                                                                                   |                                                                                                                                                                                      <div align="left"> Enter a position if the condition between the first and second specified external sources is true.</div>                                                                                                                                                                                      |
| External Condition 3 |                         <ul style="{ textAlign: 'left' }"><li>Greater Than</li><li>Lower Than</li><li>Equal</li><li>Crossing Over</li><li>Crossing Under</li><li>Crossing</li></ul>                         |                                                                                                                                                                                                                                   |                                                                                                           <div align="left"> Enter a position if the condition between the first and second specified external sources is true. Users can use price placeholders such as open, high, low, close, hl2, ohlc4, hlc3, and hlcc4 instead of a numerical value in the second source input.</div>                                                                                                           |

Multiple conditions can be enabled at the same time, in which case the backtester requires all conditions to be true in order to execute an order.

<Note>
  If the **Exit On Signal** setting is enabled, existing positions will be exited on the occurrence of a confirmation/contrarian signal.
</Note>

## Limit / Stop Orders [#limit--stop-orders]

Limit, stop, and stop-limit orders provide better control over the price at which a trade is executed.

These can be enabled for long and short positions respectively.

### Limit Orders [#limit-orders]

![](/images/backtesters/limit-stop/limit.png)

Limit orders execute at a specified price or better. For long positions, a limit order is generally placed below the current market price; for short positions, it is placed higher.

Limit orders can be enabled by toggling the "Long Limit Value" setting.

<Tip>
  Limit orders are represented by a dotted line.

  A dot is displayed to indicate when a long condition is true, thus updating the limit order price.
</Tip>

### Stop Orders [#stop-orders]

![](/images/backtesters/limit-stop/stop.png)

Stop orders execute at a specified price or worse. For long positions, a stop order is generally placed above the current market price; for short positions, it is placed lower.

Stop orders can be enabled by toggling the "Long Stop Value" setting.

<Tip>
  Stop orders are represented by a dashed line.

  A dot is displayed to indicate when a long condition is true, thus updating the stop order price.
</Tip>

### Stop-Limit Orders [#stop-limit-orders]

![](/images/backtesters/limit-stop/stop-limit.png)

Stop-limit orders use both limit and stop orders together, providing more control over the price at which a trade is executed.

In a stop-limit order, when the specified stop price is reached, the limit order becomes active. Once this limit price is reached, the trade is executed.

### Limit / Stop Orders Price [#limit--stop-orders-price]

![](/images/backtesters/limit-stop/long-settings.png)
![](/images/backtesters/limit-stop/short-settings.png)

The price of limit and stop orders is specified in "Long Limit Value"/"Short Limit Value" and "Long Stop Value"/"Short Stop Value," respectively. These settings are located at the bottom of the Long/Short Conditions settings groups.

Similar to TP/SL, various options to determine the order price are available:

|  Option  |                                                                         Description                                                                        |
| :------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------: |
|   Price  |                                          <div align="left">Set the limit/stop orders at the specified price.</div>                                         |
| Currency |                <div align="left">Set the limit/stop orders a specified amount of currency away from where the entry condition is met.</div>                |
|   Ticks  |                  <div align="left">Set the limit/stop orders a specified amount of ticks away from where the entry condition is met.</div>                 |
|     %    |                    <div align="left">Set the limit/stop orders a specified percentage away from where the entry condition is met.</div>                    |
|    ATR   | <div align="left">Set the limit/stop orders *N* average true ranges away from where the entry condition is met, where *N* is a specified multiplier.</div> |
| Forecast |          <div align="left">Use a percentile of a returned forecast to the order price. Forecasts must be enabled in order for this to work.</div>          |

For convenience, all options (except "Price") are adjusted based on the order type; for example, limit orders will be below the current price for longs and above for shorts, while stop orders will be above the price for longs and below for shorts. These can always be inverted using a negative value.

<Info>
  The price of a limit/stop order is updated every time the specified long/short condition is true.
</Info>

## No Existing Positions Requirement [#no-existing-positions-requirement]

Users can enable the **Don't Allow Trades Until Closed** setting to only open trades when no existing positions are active. This setting allows waiting for a position to be closed before another can be opened.

<Note>
  Enabling **Don't Allow Trades Until Closed** for long positions will prevent opening shorts as long as a long position is open.

  If this same setting is not enabled for short conditions, then shorts can effectively be closed by new long positions.
</Note>


# Exit Conditions (/platform/ai-backtesting/backtesters/s-o/exits)



![](/images/backtesters/s-o/exits/settings.png)

Users can close any position on a profit or loss using specific crossing conditions obtained from a user-selected external source and/or by using our builtins exits. Exit conditions can be enabled in the **EXIT CONDITIONS** settings section.

## External Exits [#external-exits]

### Exit Take Profit [#exit-take-profit]

Long/Short take profits exit conditions will exit a long or short position when a set condition is met such that the position exits on a profit.

Take profits conditions are triggered if they are toggled on for a specific order (Long/Short) and if the selected external source is either **Crossing**, **Crossing Up**, or **Crossing Down**, **Greater Than**, **Lower Than**, or **Equal** to the numerical value specified by the user.

### Exit Stop Losses [#exit-stop-losses]

Long/Short stop-losses exit conditions will exit a long or short order when a set condition is met such that the position exits on a loss.

Stop losses conditions are triggered if they are toggled on for a specific order (Long/Short) and if the selected external source is either **Crossing**, **Crossing Up**, or **Crossing Down**, **Greater Than**, **Lower Than**, or **Equal** to the numerical value specified by the user.

### Placeholders [#placeholders]

![Exit Long Take Profit when closing price cross the upper Bollinger band extremity](/images/backtesters/s-o/exits/placeholder.png)

Users who do not wish to use a numerical value to evaluate a condition with an external source may use a placeholder instead, which allows price data to be used. The following placeholders are supported:

* `open` : Opening price
* `high` : High price
* `low` : Low price
* `close` : Closing price
* `hl2` : <Tooltip tip="(high + low) / 2"> Median price </Tooltip>
* `ohlc4` : <Tooltip tip="(open + high + low + close) / 4"> Average price </Tooltip>
* `hlc3` : <Tooltip tip="(high + low + close) / 3"> Typical price </Tooltip>
* `hlcc4` : <Tooltip tip="(high + low + 2 * close) / 4"> Weighted price </Tooltip>

## Builtins Exits [#builtins-exits]

Built-in exits are included for the selected signal algorithm, these are the same as the ones present in the Signals & Overlays® toolkit and can be used in the Backtester to exit positions.

<Note>
  Built-in exits will always exit a position on a profit.
</Note>

## Time Exits [#time-exits]

Time exits allow users to exit trade at a specific time of the day. Note that trades are still exited at the opening of the next bar.

<Note>
  The timezone is the same as the one on the chart symbol.
</Note>

## Exit On Signal [#exit-on-signal]

Enabling the **Exit On Signal** setting will close any position once a new confirmation or contrarian signal is generated (if enabled).

## Exit on Entry [#exit-on-entry]

Enabling the **Exit On Entry** setting will close any existing position when an entry condition leading to a new opposite position is met.

## Partial Exits [#partial-exits]

![](/images/backtesters/partial.png)

Users can exit a percentage of an existing position by specifying a percentage lower than 100%.

This can lead to sequential exits, with reduced exposure each time an exit is triggered.


# Introduction (/platform/ai-backtesting/backtesters/s-o/introduction)



<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/TjN1-SjB9X8"
    frameBorder="0"
  />
</div>

Backtester (S\&O) is a script that allows users to perform backtests using features from our [Signals & Overlays®](/platform/algos/signals-overlays/introduction) toolkit. Many options are included to provide more complete & diverse backtests to the user, and each core component of the system are described in the following pages:

<CardGroup cols="2">
  <Card title="Entry" icon="arrow-up-arrow-down" href="./entry" />

  <Card title="Exits" icon="xmark" href="./exits" />

  <Card title="Take Profits/Stop Losses" icon="shield" href="./tpsl" />

  <Card title="Optimization" icon="flask-gear" href="./optimization" />
</CardGroup>

# Backtest Parameters [#backtest-parameters]

The Backtester (S\&O) use the following default properties:

| Property                  | Default          | Description                                                                                                                                                                                                                                                                                                                              |
| ------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Backtesting Window        | Bars             | Determines how the backtesting interval is set. If `Bars` is selected the most recent specified amount (in `Window (Bars)`) will be used to backtest the strategy. If `Date` is selected the interval will be set using the specified starting and ending dates, while using "Entire History" will use all the available historical data |
| Window (Bars)             | 2000             | Amount of the most recent bars used to backtest the strategy if `Bars` is selected in `Backtesting Window`                                                                                                                                                                                                                               |
| Window Start              | 2024-01-01 00:00 | Starting date of the backtesting strategy if `Date` is selected in `Backtesting Window`                                                                                                                                                                                                                                                  |
| Window End                | 2024-01-01 00:00 | Ending date of the backtesting strategy if `Date` is selected in `Backtesting Window` and if the `Window End` toggle is enabled                                                                                                                                                                                                          |
| Initial Capital           | 10 000           | Initial amount of funds available at the start of the backtest                                                                                                                                                                                                                                                                           |
| Base Currency             | Default          | Currency used for performing the backtest, backtest statistics will be expressed using this curreny. If `Default` is selected the symbol currency is used                                                                                                                                                                                |
| Order Size                | 1 Contract       | Determines the amount of contracts/currency to buy or sell, can be expressed in contracts, currency, or percent of equity                                                                                                                                                                                                                |
| Commission                | 0%               | Fees paid per closed trades, can be expressed as currency per contracts, currency per order, or % of the total transation value                                                                                                                                                                                                          |
| Margin for long position  | 0%               | Equity percentage required to fund a position                                                                                                                                                                                                                                                                                            |
| Margin for short position | 0%               | Equity percentage required to fund a position                                                                                                                                                                                                                                                                                            |

These should be adjusted to return more precise and accurate results of the real performance of a trading strategy.

# Disclaimer [#disclaimer]

<Warning>
  Backtests are not indicative of future results. Backtesting strategies on synthetic data does not return representative results of a strategy. Backtests should be performed on charts returning real closing prices. See [here](https://www.tradingview.com/script/q9laJNG9-Backtesting-on-Non-Standard-Charts-Caution-PineCoders-FAQ/) for more information.

  *CFTC Rule 4.41 - Hypothetical or Simulated performance results have certain limitations, unlike an actual performance record, simulated results do not represent actual trading. Also, since the trades have not been executed, the results may have under-or-over compensated for the impact, if any, of certain market factors, such as lack of liquidity. Simulated trading programs in general are also subject to the fact that they are designed with the benefit of hindsight. No representation is being made that any account will or is likely to achieve profit or losses similar to those shown.*
</Warning>


# Optimization (/platform/ai-backtesting/backtesters/s-o/optimization)



![](/images/backtesters/s-o/optimization/optimization.png)

When signals are used to perform a backtest, the Backtester is able to return the **Sensitivity** setting that maximized a user specified statistic such as net profit, winrate, profit factor, or maximum drawdown.

<Note>
  Certain settings will disable the optimization procedure, such as disabling the backtesting window or using autopilot.
</Note>

## Procedure [#procedure]

![](/images/backtesters/s-o/optimization/settings.png)

Users can choose to optimize net profit, winrate, profit factor, potential ratio $(\varphi)$ or maximum drawdown depending on the options selected from the **Optimize** drop-down menu within the **OPTIMIZER** settings section.

<Tabs>
  <Tab title="Net Profit">
    The overall profit or loss achieved (higher is better).
  </Tab>

  <Tab title="Winrate">
    The percentage of winning trades (higher is better).
  </Tab>

  <Tab title="Profit Factor">
    The gross profits over gross losses generated by the strategy (higher is better). A value higher than 1 suggest that the strategy generated more profits than losses.
  </Tab>

  <Tab title="Maximum Drawdown">
    The greatest decline of the strategy relative to the highest net profits (Lower is better).

    A lower value is indicative of a strategy less subject to large downside variability.
  </Tab>

  <Tab title="Potential Ratio (φ)">
    The ratio between the sum of maximum potential profits you could have made in your trades over the sum of maximum potential losses you could have made in your trades (higher is better).

    Higher values indicates that the entries generated by the strategy have the potential to lead to larger profits compared to losses. Lower values however might be indicative of a more risky strategy, with more frequent exposure to larger potential losses.

    If the backtest with the highest potential ratio is not the one with the highest potential profit then it can be indicative of a strategy with more potential with a tendency to exit trades potentially too late.
  </Tab>
</Tabs>

Optimization can be performed on any given range for the **Sensitivity** setting.

<Warning>
  Optimizing the **Sensitivity** setting using a large range of values and/or using a large backtest window can increase computation time, potentially returning time-out errors.
</Warning>

<Note>
  Results from the optimizer can vary from the backtest results returned by the strategy, this is due to the difference between how the Tradingview backtesting engine opens positions compared to the internal engine used to perform optimization within the script. Using different signal algorithms for long/short entry rules can return very different results from the optimizer compared to backtesting results.

  Take profits and/or stop losses orders are taken into account by the internal engine if these are enabled, however, there can still exist a great degree of disparity between results from the backtest and results returned by the optimizer.
</Note>

### Warmup Period [#warmup-period]

The warmup period allows the script to compute the signal algorithms *n* bars prior to the backtesting window, where *n* is the selected warmup period. This allows to backtest signals that are closer to the ones returned by the Signals & Overlays® toolkit, returning more accurate optimized settings as a result.

## Optimization Dashboard [#optimization-dashboard]

![](/images/backtesters/s-o/optimization/dashboard.png)

Users can see the backtests results for sensitivity values within the specified optimization range on a dashboard shown on the top-right of their chart. This dashboard presents results in descending order, with results associated to the best optimized statistic appearing on top.

### Simple [#simple]

![](/images/backtesters/s-o/optimization/simple.png)

Users that do not wish to have a large dashboard on the chart can use the **Simple** optimization dashboard. This dashboard will only return the **Sensitivity** setting that optimize the selected statistic alongside the statistic of interest.

### Display [#display]

![](/images/backtesters/s-o/optimization/display.png)

Users can control the location as well as hide the optimization dashboard on the chart from the **DISPLAY** settings section.

A dashboard location set to **None** will hide it from the chart as well as disable the optimizer.


# Take Profit & Stop Loss (/platform/ai-backtesting/backtesters/s-o/tpsl)



![](/images/backtesters/tpsl.png)

Users can set take profits and stop losses orders allowing a certain degree of money management in the backtest. Take profits and stop loss orders can be enabled and adjusted from the **FIXED TPS & SLS** settings section.

<Note>
  Take profits and stop losses orders are placed relative to the price preceding a trade, and might not be filled at the exact price set by the user.
</Note>

## TP/SL Options [#tpsl-options]

Take profits and stop losses for specific orders can be enabled from the toggles in their settings section. Take profits and stop losses can be set using various methods described below:

|     Option    |                                                                                            Description                                                                                           |
| :-----------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|     Price     |                                                       <div align="left">Set the take profit/stop loss orders at the specified price.</div>                                                       |
|    Currency   |                <div align="left">Set the take profit/stop loss orders a specified amount of currency away from the closing price preceding the entry of a long/short order.</div>                |
|     Ticks     |                  <div align="left">Set the take profit/stop loss orders a specified amount of ticks away from the closing price preceding the entry of a long/short order.</div>                 |
|       %       |                    <div align="left">Set the take profit/stop loss orders a specified percentage away from the closing price preceding the entry of a long/short order.</div>                    |
|      ATR      | <div align="left">Set the take profit/stop loss orders *N* average true ranges away from the closing price preceding the entry of a long/short order, where *N* is a specified multiplier.</div> |
|    Forecast   |        <div align="left">(Only available for take profits orders) Use a percentile of a returned forecast to set take profits. Forecasts must be enabled in order for this to work.</div>        |
| Trailing Stop |             <div align="left">(Only available for stop loss orders) Enable a percentage-based trailing stop. A position will be exited if the price reaches the trailing stop.</div>             |

<Note>
  The average true range used to set take profits/stop losses orders uses a period of 200 bars.
</Note>

### Trailing Stop [#trailing-stop]

![](/images/backtesters/trailing.png)

Users can use a percentage-based trailing stop to exit positions. The trailing stop will move in the direction of interest if its distance with the low price (in case of long positions) or high price (in case of short positions) is greater than the specified percentage.

### Take Profits Levels From Forecast [#take-profits-levels-from-forecast]

When forecasts are displayed, users can set take profits using the **nth** percentile of a returned forecast. This can be done from the "TPS & SLS" settings, by selecting "Forecast" in the "Long TP" and/or "Short TP".

The numerical setting selected at the right of the drop-down menus determines the percentile used, and should be set within the range \[0, 100] in order to work. Percentiles values can affect the returned levels as follows:

* In case of a long trade, a lower set percentile will return take profits closer to the entry price, potentially triggering sooner.
* In case of a short trade, a lower set percentile will return take profits further away from the entry price, potentially triggering later.

<Warning>
  Do note that because forecasts are based on the strategy entries, take profits levels based on it can be subject to repainting, except when using a backtesting window based on a date.
</Warning>


# Actions (/platform/ai-backtesting/backtesters/strategy-scripting/actions)



**Actions** are elements tied to a user set condition producing a specific effect when the assigned condition is true.

<Info>
  Actions have the structure `@keyword()`, starting with `@`, with an identifier, and `()`.

  Some actions can have a condition assigned to them using `=`. When the assigned condition is true the action will trigger.
</Info>

Like functions in classical programming languages, actions can have arguments located within `()`, adding further degrees of control over the action.

Actions used for scripted alerts are also available for scripted strategies, however, this page only lists the actions that are exclusively used for scripted strategies.

To see a list of other actions, see [scripted alerts actions](/platform/toolkits/alert-scripting/actions-methods)

***

## @long [#long]

The `@long` action allows entering a long position when the associated condition is true.

### Syntax [#syntax]

```js
@long(limit, stop, alert_message)
```

### Arguments [#arguments]

**limit** (float): Optional. Limit price for the order. Accepts data placeholders as values. If specified, a limit order is created.

**stop** (float): Optional. Stop price for the order. Accepts data placeholders as values. If specified, a stop order is created.

<Note>
  If both `limit` and `stop` are specified, a stop-limit order is created.
</Note>

**alert\_message** (string): Optional. Alert message sent when the order is opened. Data placeholders can be used within the message, these will be replaced by their corresponding value when the alert triggers.

### Examples [#examples]

```js
// Go long when the price is greater than the opening price
@long() = {close} > {open}
```

```js
// Go long with a limit order at the previous high
@long(limit = {high[1]}) = {close} > {open}
```

```js
// Go long with a stop order at the previous low
@long(stop = {low[1]}) = {close} > {open}
```

```js
// Go long with a stop-limit order
@long(limit = {high[1]}, stop = {low[1]}) = {close} > {open}
```

```js
// Go long with an alert message containing the entry price
@long(alert_message = "Long entry at {close}") = {close} > {open}
```

### Remarks [#remarks]

<Note>
  By default a long position is entered at the opening of the next bar. Using "fill on bar close" from the settings in **PROPERTIES** → **FILL ORDERS** will open a position at the close of the bar the condition is true.
</Note>

***

## @short [#short]

The `@short` action allows entering a short position when the associated condition is true.

### Syntax [#syntax-1]

```js
@short(limit, stop, alert_message)
```

### Arguments [#arguments-1]

**limit** (float): Optional. Limit price for the order. Accepts data placeholders as values. If specified, a limit order is created.

**stop** (float): Optional. Stop price for the order. Accepts data placeholders as values. If specified, a stop order is created.

<Note>
  If both `limit` and `stop` are specified, a stop-limit order is created.
</Note>

**alert\_message** (string): Optional. Alert message sent when the order is opened. Data placeholders can be used within the message, these will be replaced by their corresponding value when the alert triggers.

### Examples [#examples-1]

```js
// Go short when the price is lower than the opening price
@short() = {close} < {open}
```

```js
// Go short with a limit order at the previous low
@short(limit = {low[1]}) = {close} < {open}
```

```js
// Go short with a stop order at the previous high
@short(stop = {high[1]}) = {close} < {open}
```

```js
// Go short with a stop-limit order
@short(limit = {low[1]}, stop = {high[1]}) = {close} < {open}
```

```js
// Go short with an alert message containing the entry price
@short(alert_message = "Short entry at {close}") = {close} < {open}
```

### Remarks [#remarks-1]

<Note>
  By default a short position is entered at the opening of the next bar. Using "fill on bar close" from the settings in **PROPERTIES** -> **FILL ORDERS** will open a position at the close of the bar the condition is true.
</Note>

***

## @exit\_long [#exit_long]

The `@exit_long` action closes all open **long** positions when the associated condition is true.

### Syntax [#syntax-2]

```js
@exit_long(qty, alert_message)
```

### Arguments [#arguments-2]

**qty** (float): Optional. Number of contracts/lots/shares/units to close on exit for a long position. If the character `%` is present, the specified % of the long position is closed. If not provied the entire position is closed.

**alert\_message** (string): Optional. Alert message sent when the order is closed. Data placeholders can be used within the message, these will be replaced by their corresponding value when the alert triggers.

### Examples [#examples-2]

```js
// Exit long position when price is below the price 14 bars ago
@exit_long() = {close} < {close[14]}
```

```js
// Exit 50 contracts/lots/shares/units from the long position 
// when price is below the price 14 bars ago
@exit_long(50) = {close} < {close[14]}
```

```js
// Exit 50% of the long position 
// when price is below the price 14 bars ago
@exit_long(50%) = {close} < {close[14]}
```

```js
// Exit long position with an alert message containing the exit price
@exit_long(alert_message = "Long exit at {close}") = {close} < {close[14]}
```

### Remarks [#remarks-2]

<Note>
  By default, a long position is exited at the opening of the next bar. You can enable "fill on bar close" from **PROPERTIES** → **FILL ORDERS** to trigger the exit at the bar close instead.
</Note>

***

## @exit\_short [#exit_short]

The `@exit_short` action closes all open **short** positions when the associated condition is true.

### Syntax [#syntax-3]

```js
@exit_short(qty, alert_message)
```

### Arguments [#arguments-3]

**qty** (float): Optional. Number of contracts/lots/shares/units to close on exit for a short position. If the character `%` is present, the specified % of the short position is closed. If not provied the entire position is closed.

**alert\_message** (string): Optional. Alert message sent when the order is closed. Data placeholders can be used within the message, these will be replaced by their corresponding value when the alert triggers.

### Examples [#examples-3]

```js
// Exit short position when the high price is above previous low price
@exit_short() = {high} > {low[1]}
```

```js
// Exit 50 contracts/lots/shares/units from the short position 
// when the high price is above previous low price
@exit_short(50) = {high} > {low[1]}
```

```js
// Exit 50% of the short position 
// when the high price is above previous low price
@exit_short(50%) = {high} > {low[1]}
```

```js
// Exit short position with an alert message containing the exit price
@exit_short(alert_message = "Short exit at {close}") = {high} > {low[1]}
```

### Remarks [#remarks-3]

<Note>
  By default, a short position is exited at the opening of the next bar. To exit at the close of the bar where the condition is true, enable "fill on bar close" from **PROPERTIES** → **FILL ORDERS**.
</Note>

***

## @exit\_all [#exit_all]

The `@exit_all` action closes **all open positions** (long or short and scripted/unscripted) when the associated condition is true.

### Syntax [#syntax-4]

```js
@exit_all()
```

### Examples [#examples-4]

```js
// Exit all positions when the closing price is above the previous high price
@exit_all() = {close} > {high[1]}
```

### Remarks [#remarks-4]

<Note>
  This exits both scripted long and short positions, but also any other long/short condition you may have set.

  By default, the exit occurs at the next bar’s open unless "fill on bar close" is enabled in **PROPERTIES** → **FILL ORDERS**.
</Note>

***

## @start\_date [#start_date]

The `@start_date` action determines the starting date of the scripted strategy evaluation using a Datestring placeholder.

### Syntax [#syntax-5]

```js
@start_date({YYYY-MM-DDThh:mm:ss±hh:mm})
```

### Arguments [#arguments-4]

**datestring**: Datestring placeholder, must comply with the ISO 8061 standard. The time and timezone are optional.

### Examples [#examples-5]

```js
@long = {close} > {open}
@short = {close} < {open}

// Only evaluate the strategy starting 2025
@start_date({2025-01-01})
```

### Remarks [#remarks-5]

<Note>
  The **Timezone** setting does not affect this placeholder. Default timezone for this placeholder is UTC-0.
</Note>

***

## @end\_date [#end_date]

The `@end_date` action determines the starting date of the scripted strategy evaluation using a Datestring placeholder.

### Syntax [#syntax-6]

```js
@end_date({YYYY-MM-DDThh:mm:ss±hh:mm})
```

### Arguments [#arguments-5]

**datestring**: Datestring placeholder, must comply with the ISO 8061 standard. The time and timezone are optional.

### Examples [#examples-6]

```js
@long = {close} > {open}
@short = {close} < {open}

// Only evaluate the strategy prior to 2025
@end_date({2025-01-01})
```

### Remarks [#remarks-6]

<Note>
  The **Timezone** setting does not affect this placeholder. Default timezone for this placeholder is UTC-0.
</Note>

***

## @only\_scripted [#only_scripted]

The `@only_scripted` action determines if only scripted entries/exits should be considered during the backtest. If `true` it will ignore any other entry condition set in the other Backtester settings.

### Syntax [#syntax-7]

```js
@only_scripted()
```

### Examples [#examples-7]

```js
// Only use the scripted strategy for the backtest
@only_scripted() = true
```


# Strategy Scripting (/platform/ai-backtesting/backtesters/strategy-scripting/introduction)



Strategy Scripting is a feature within each of our 3 Backtesters that allows users to create and backtests strategies using our own scripting language. This feature works just like [alert scripting](/platform/toolkits/alert-scripting/introduction) but is adjusted toward backtesting.

To better learn about Strategy Scripting, see the following pages:

<CardGroup cols="2">
  <Card title="Placeholders" icon="brackets-curly" href="./placeholders">
    Learn how to reference market and backtesting data when scripting strategies.
  </Card>

  <Card title="Operators" icon="code" href="/platform/toolkits/alert-scripting/operators">
    Learn about the operators you can use to compare data with each other.
  </Card>

  <Card title="Actions" icon="at" href="./actions">
    Learn how to open, exit trades, and more using actions.
  </Card>

  <Card title="LUCID Connectors" icon="right-left-large" href="./lucid-connectors">
    Use any condition from any external LuxAlgo toolkit in your scripted strategy with LUCID connectors.
  </Card>
</CardGroup>


# LUCID Connectors (/platform/ai-backtesting/backtesters/strategy-scripting/lucid-connectors)



![](/images/backtesters/strategy-scripting/lucid-connectors/settings.png)

LUCID (LuxAlgo Universal Conditions Identifier) connectors are identifiers used to share the **trigger placeholders** from toolkits directly to any Backtester by simply using them as a source in the appropriate inputs.

One usage of LUCID connectors is being able to backtest strategies based on both the Signals & Overlays™ indicators and Oscillator Matrix™ using the PAC Backtester.

<Warning>
  LUCID Connectors are only available starting from the following toolkit versions

  * LuxAlgo® - Signals & Overlays™: 7.0
  * LuxAlgo® - Price Action Concepts™: 3.0
  * LuxAlgo® - Oscillator Matrix™: 7.0

  And can be used only starting from the following Backtester versions:

  * Backtester (S\&O): 3.0
  * Backtester (PAC): 2.0
  * Backtester (OSC): 2.0
</Warning>

## Connectors [#connectors]

Each toolkit connector can be identified from their name:

* **"LuxAlgo® - Signals & Overlays™: LUCID Connector"**
* **"LuxAlgo® - Price Action Concepts™: LUCID Connector"**
* **"LuxAlgo® - Oscillator Matrix™: LUCID Connector"**

## Available Placeholders [#available-placeholders]

Only the trigger placeholders of each toolkit are supported, to get a list of those placeholders see:

* [Signals & Overlays™ trigger placeholders](/platform/toolkits/alert-scripting/placeholders#trigger-placeholders)
* [Price Action Concepts™ trigger placeholders](/platform/toolkits/alert-scripting/placeholders#trigger-placeholders-2)
* [Oscillator Matrix™ trigger placeholders](/platform/toolkits/alert-scripting/placeholders#trigger-placeholders-3)

## How To Use [#how-to-use]

Let's suppose that we want to backtest a strategy with the following entry conditions:

* long: bullish CHoCH while the neo cloud is bullish and the market is trending
* short: bearish CHoCH while the neo cloud is bearish and the market is trending

For this example, we will use the PAC Backtester, as such we need the Signals & Overlays™ toolkit to share information about the Neo Cloud and Trend Strength indicators.

![](/images/backtesters/strategy-scripting/lucid-connectors/in-chart.png)

First, we can add the PAC Backtester and Signals & Overlays™ toolkit to our chart.

![](/images/backtesters/strategy-scripting/lucid-connectors/is-enabled.png)

Once done, let's make sure the Neo Cloud overlay is enabled in the Signals & Overlays™ toolkit.

![](/images/backtesters/strategy-scripting/lucid-connectors/lucid-selection.png)

Once the Neo Cloud is enabled, go to the PAC Backtester settings, locate the &#x2A;*CONDITIONS (ADVANCED)** section, then locate the &#x2A;*LUCID Connector (S\&O)*&#x2A; setting, open its dropdown and select &#x2A;*"LuxAlgo® - Signals & Overlays™: LUCID Connector"**.

<Warning>
  When using the LUCID connector of a specific toolkit make sure to select it from the correct input setting.

  For example having in the &#x2A;*LUCID Connector (S\&O)*&#x2A; setting the LUCID connector for the Price Action Concepts™ toolkit &#x2A;*"LuxAlgo® - Price Action Concepts™: LUCID Connector"** will result in unexpected results.
</Warning>

Now we simply need to script our strategies. THe PAC Backtester already includes placeholders for bullish and bearish CHoCH's, `{bullish_choch}` and `{bearish_choch}`. For the Signals & Overlays, the trigger placeholders of interest are `{bullish_neo}`, `{bearish_neo}`, and `{trend_strength_trending}`,

This leads to the following scripted strategy:

```js
// Add your long entry condition below
@long() = {bullish_choch} and {bullish_neo} and {trend_strength_trending}

// Add your short entry condition below
@short() = {bearish_choch} and {bearish_neo} and {trend_strength_trending}
```

![](/images/backtesters/strategy-scripting/lucid-connectors/trades.png)

As we can see above we are indeed filtering CHoCH's with the help of the Signals & Overlays.


# Placeholders (/platform/ai-backtesting/backtesters/strategy-scripting/placeholders)



Placeholders are strings that refer to a specific series of values or events, and have the form `{placeholder_title}`, where `placeholder_title` is the name of the placeholder to access and `{}` makes sure the string is recognized as a placeholder.

With scripted strategies, placeholders allow accessing market data such as ohlc prices, volume, but also strategy data such as net profit, winrate, entry prices...etc.

## Market Data Placeholders [#market-data-placeholders]

Market data placeholder allows referencing typical market data such as ohlc prices and volume.

These placeholders are available for all toolkits.

| Placeholder | Description                                                   |
| ----------- | ------------------------------------------------------------- |
| `{open}`    | Open price of the bar                                         |
| `{high}`    | High price of the bar                                         |
| `{low}`     | Low price of the bar                                          |
| `{close}`   | Close price of the bar                                        |
| `{hl2}`     | Median price `(high + low) / 2`                               |
| `{ohlc4}`   | Average price `(open + high + low + close) / 4`               |
| `{hlc3}`    | Typical price `(high + low + close) / 3`                      |
| `{hlcc4}`   | Weighted average of hlc prices `(high + low + 2 * close) / 4` |
| `{volume}`  | Volume for the bar                                            |
| `{range}`   | Difference between High and Low prices                        |
| `{tr}`      | True Range                                                    |

## Time Related Placeholders [#time-related-placeholders]

Time placeholder allows referencing time related information such as the bar hour and minute, day, bar index...etc.

These placeholders are available for all toolkits.

| Placeholder                   | Description                                                                                                                                                   |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{barindex}`                  | Index of the current bar                                                                                                                                      |
| `{last_barindex}`             | Index of the most recent bar                                                                                                                                  |
| `{second}`                    | Current bar second                                                                                                                                            |
| `{minute}`                    | Current bar minute                                                                                                                                            |
| `{hour}`                      | Current bar hour                                                                                                                                              |
| `{dayofweek}`                 | Current bar day of the week ranging from 1 to 7, with 1 for sunday                                                                                            |
| `{dayofmonth}`                | Current bar day of the month                                                                                                                                  |
| `{weekofyear}`                | Current bar week of the year                                                                                                                                  |
| `{month}`                     | Current bar month of the year                                                                                                                                 |
| `{year}`                      | Current bar year (with timezone)                                                                                                                              |
| `{time}`                      | Unix timestamp of the bar                                                                                                                                     |
| `{unix_ts}`                   | Unix timestamp of the bar (Deprecated, use `{time}`)                                                                                                          |
| `{timeframe}`                 | Chart timeframe in minutes                                                                                                                                    |
| `{hhmm-hhmm}`                 | Session placeholder, `True` when the bar is located within the specific session interval, see [here](./placeholders#session-placeholder) for more information |
| `{YYYY-MM-DDThh:mm:ss±hh:mm}` | Datestring placeholder, equal to the timestamp of the specified date, see [here](./placeholders#datestring-placeholder) for more information                  |

<Note>
  Note that `{dayofweek}` returns the day based on the time of the bar's open. For overnight sessions (e.g. EURUSD, where Monday session starts on Sunday, 17:00) this value can be lower by 1 than the day of the trading day.
</Note>

The timezone for the time related placeholders (with the exception of `time`, and the datestring placeholder) is defined by the `Timezone` dropdown setting, defining the timezone using UTC. Default value is `exchange`, which uses the exchange timezones to get the value of the time related placeholders.

### Datestring Placeholder [#datestring-placeholder]

You can easily define dates and optionally th time and timezone using the **Datestring** placeholder, following the ISO 8601 standard. The syntax of the placeholder is as follows:

```text Example
{YYYY-MM-DDThh:mm:ss±hh:mm}
```

* `YYYY`: Year
* `MM`: Month
* `DD`: Day
* `hh`: Hour
* `mm`: Minute
* `±hh:mm`: Timezone offset, default timezone is UTC-0 (optional)

<Note>
  The **Datestring** placeholder is not affected by the **Timezone** setting
</Note>

This placeholder can be useful alongside `{time}` to constraint the evaluation of conditions to a specific date interval. For example:

```text Example
{close} > {open} and {time} >= {2025-01-01}
```

The above script will only trigger if the closing price is above the opening price while the time is on or after January 1st, 2025.

Dates and times are separated by `T`, for example `{2025-01-01T04:00}`. `±hh:mm` can be used to offset the timezones, as such if the timezone of interest is the New York timezone (UTC-4), we would use:

```text Example
{2025-01-01T04:00-04:00}
```

### Session Placeholder [#session-placeholder]

You can easily detect if the current bar is within a specific session (time interval) by using the session placeholder with pattern `{hhmm-hhmm}`, where `hh` is the hour and `mm` the minute.

The value of this placeholder will be `True` when the bar time is located between the start time `{hhmm-` and end time `-hhmm}`.

For example, if we want to detect when the bar is located in the session starting at 14:30 and ending at 16:45 we would simply use:

```python Example
{1430-1645}
```

## Strategy Statistics Placeholders [#strategy-statistics-placeholders]

It is possible to use the actual strategy results made over time as part of our entry or exit conditions using the following data placeholders:

| Placeholder              | Description                                 |
| ------------------------ | ------------------------------------------- |
| `{entry_price}`          | Average entry price of the current position |
| `{net_profit}`           | Total net profit from all closed trades     |
| `{equity}`               | Current equity including open trades        |
| `{open_profit}`          | Unrealized profit/loss of open trades       |
| `{gross_profit}`         | Total gross profit from winning trades      |
| `{gross_loss}`           | Total gross loss from losing trades         |
| `{gross_profit_percent}` | Gross profit expressed as a percentage      |
| `{gross_loss_percent}`   | Gross loss expressed as a percentage        |
| `{profit_factor}`        | Ratio of gross profit to gross loss         |
| `{winrate}`              | Percentage of closed trades that were wins  |
| `{closed_trades}`        | Number of closed trades                     |
| `{open_trades}`          | Number of currently open trades             |
| `{win_trades}`           | Number of winning closed trades             |
| `{loss_trades}`          | Number of losing closed trades              |
| `{avg_trade}`            | Average profit/loss per trade               |
| `{avg_winning_trade}`    | Average profit of winning trades            |
| `{avg_losing_trade}`     | Average loss of losing trades               |
| `{max_drawdown}`         | Maximum drawdown in account value           |
| `{max_drawdown_percent}` | Max drawdown expressed as a percentage      |
| `{max_runup}`            | Maximum runup in account value              |
| `{max_runup_percent}`    | Max runup expressed as a percentage         |
| `{position_size}`        | Current position size                       |

## External Placeholders [#external-placeholders]

![](/images/alert-scripting/placeholders/external.png)

Users can refer to a series of values outside the toolkit by using external placeholder. A maximum of 3 external placeholders can be used:

* `{external1}`
* `{external2}`
* `{external3}`

In order to use external placeholders, users must first select the external source from the provided dropdowns located in the Strategy Scripting settings. Once selected, external sources can be referred from their respective placeholders.

## User Defined Placeholders (UDP) [#user-defined-placeholders-udp]

User defined placeholders (UDPs) are custom placeholders defined by users and assigned to a specific condition set by the user. These placeholders can later be referenced as part of other conditions or UDPs.

To define an UDP a line must start with a placeholder with a unique name, followed by an equal sign `=`, then followed by the condition we want to assign to this placeholder, for example `{my_placeholder} = {close} > {open}`

Let's take a more complex example and say we want to go long on a new bullish fair value gap while we also had one on the previous bar.

Doing this could involve a long and messy script, to make things easier we can create an UDP that refers to the condition of a bullish fair value gaps:

```
{bull_fvg} = {low} > {high[2]} and {close[1]} > {high[2]}
@long() = {bull_fvg} and {bull_fvg[1]}
```

## Backtester Trigger Placeholders [#backtester-trigger-placeholders]

The trigger placeholders below are unique to their respective Backtesters, and refer to their respective features.

### Backtester (S\&O) Trigger Placeholders [#backtester-so-trigger-placeholders]

| Placeholder                    | Description                                                                   |
| ------------------------------ | ----------------------------------------------------------------------------- |
| `{bullish_confirmation}`       | `True` on a new normal bullish confirmation signal                            |
| `{bullish_confirmation+}`      | `True` on a new strong bullish confirmation signal                            |
| `{bullish_confirmation_any}`   | `True` on a any new bullish confirmation signal                               |
| `{bullish_confirmation_turn+}` | `True` when a normal bullish confirmation signal turns strong                 |
| `{bearish_confirmation}`       | `True` on a new normal bearish confirmation signal                            |
| `{bearish_confirmation+}`      | `True` on a new strong bearish confirmation signal                            |
| `{bearish_confirmation_any}`   | `True` on any new bearish confirmation signal                                 |
| `{bearish_confirmation_turn+}` | `True` when a normal bearish confirmation signal turns strong                 |
| `{bullish_contrarian}`         | `True` on a new normal bullish contrarian signal                              |
| `{bullish_contrarian+}`        | `True` on a new strong bullish contrarian signal                              |
| `{bullish_contrarian_any}`     | `True` on any new bullish contrarian signal                                   |
| `{bearish_contrarian}`         | `True` on a new normal bearish contrarian signal                              |
| `{bearish_contrarian+}`        | `True` on a new strong bearish contrarian signal                              |
| `{bearish_contrarian_any}`     | `True` on any new bearish contrarian signal                                   |
| `{confirmation_exits}`         | `True` on a new exit buy or sell confirmation signal                          |
| `{contrarian_exits}`           | `True` on a new exit buy or sell contrarian signal                            |
| `{exit_sell}`                  | `True` on a new exit sell signal                                              |
| `{bullish_smart_trail}`        | `True` if Smart Trail is up-trending                                          |
| `{bearish_smart_trail}`        | `True` if Smart Trail is down-trending                                        |
| `{switch_bullish_smart_trail}` | `True` if Smart Trail switches to bullish                                     |
| `{switch_bearish_smart_trail}` | `True` if Smart Trail switches to bearish                                     |
| `{price_above_rzr1}`           | `True` if the closing price is above the lower Reversal Zones resistance R1   |
| `{price_below_rzs1}`           | `True` if the closing price is below the upper Reversal Zones support S1      |
| `{price_co_rzr1}`              | `True` if the closing price cross over the lower Reversal Zones resistance R1 |
| `{price_cu_rzs1}`              | `True` if the closing price cross under the upper Reversal Zones support S1   |
| `{bullish_catcher}`            | `True` if Trend Catcher is up-trending                                        |
| `{bearish_catcher}`            | `True` if Trend Catcher is down-trending                                      |
| `{switch_bullish_catcher}`     | `True` if Trend Catcher switches to bullish                                   |
| `{switch_bearish_catcher}`     | `True` if Trend Catcher switches to bearish                                   |
| `{bullish_tracer}`             | `True` if Trend Tracer is up-trending                                         |
| `{bearish_tracer}`             | `True` if Trend Tracer is down-trending                                       |
| `{switch_bullish_tracer}`      | `True` if Trend Tracer switches to bullish                                    |
| `{switch_bearish_tracer}`      | `True` if Trend Tracer switches to bearish                                    |
| `{bullish_neo}`                | `True` if Neo Cloud is up-trending                                            |
| `{bearish_neo}`                | `True` if Neo Cloud is down-trending                                          |
| `{switch_bullish_neo}`         | `True` if Neo Cloud switches to bullish                                       |
| `{switch_bearish_neo}`         | `True` if Neo Cloud switches to bearish                                       |
| `{confirmation_uptrend}`       | `True` when confirmation signal indicates an uptrend                          |
| `{confirmation_downtrend}`     | `True` when confirmation signal indicates a downtrend                         |
| `{contrarian_uptrend}`         | `True` when contrarian signal indicates an uptrend                            |
| `{contrarian_downtrend}`       | `True` when contrarian signal indicates a downtrend                           |
| `{trend_strength_trending}`    | `True` when trend strength indicates a trending market (above 50)             |
| `{trend_strength_ranging}`     | `True` when trend strength indicates a ranging market (under 50)              |
| `{confirmation_exits}`         | `True` when confirmation-based exit condition is met                          |
| `{contrarian_exits}`           | `True` when contrarian-based exit condition is met                            |
| `{price_cross_rz_avg}`         | `True` when price crosses the average of The Reversal Zones                   |

### Backtester (PAC) Trigger Placeholders [#backtester-pac-trigger-placeholders]

| Placeholder                 | Description                                               |
| --------------------------- | --------------------------------------------------------- |
| `{bullish_choch}`           | `True` when a bullish Change of Character (CHoCH) occurs  |
| `{bearish_choch}`           | `True` when a bearish Change of Character (CHoCH) occurs  |
| `{bullish_bos}`             | `True` when a bullish Break of Structure (BOS) occurs     |
| `{bearish_bos}`             | `True` when a bearish Break of Structure (BOS) occurs     |
| `{bullish_trend}`           | `True` when market structure trend is bullish             |
| `{bearish_trend}`           | `True` when market structure trend is bearish             |
| `{bullish_ob_exited}`       | `True` when price exits a bullish order block             |
| `{bearish_ob_exited}`       | `True` when price exits a bearish order block             |
| `{bullish_new_fvg}`         | `True` on detection of a new bullish Fair Value Gap (FVG) |
| `{bearish_new_fvg}`         | `True` on detection of a new bearish Fair Value Gap (FVG) |
| `{bullish_fvg_mitigated}`   | `True` when bullish FVG is mitigated                      |
| `{bearish_fvg_mitigated}`   | `True` when bearish FVG is mitigated                      |
| `{bullish_fvg_entered}`     | `True` when price enters bullish FVG                      |
| `{bearish_fvg_entered}`     | `True` when price enters bearish FVG                      |
| `{bullish_fvg_within}`      | `True` when price is within bullish FVG                   |
| `{bearish_fvg_within}`      | `True` when price is within bearish FVG                   |
| `{bullish_fvg_exited}`      | `True` when price exits bullish FVG                       |
| `{bearish_fvg_exited}`      | `True` when price exits bearish FVG                       |
| `{bullish_new_ifvg}`        | `True` on detection of a new bullish Inverse FVG          |
| `{bearish_new_ifvg}`        | `True` on detection of a new bearish Inverse FVG          |
| `{bullish_ifvg_mitigated}`  | `True` when bullish Inverse FVG is mitigated              |
| `{bearish_ifvg_mitigated}`  | `True` when bearish Inverse FVG is mitigated              |
| `{bullish_ifvg_entered}`    | `True` when price enters bullish Inverse FVG              |
| `{bearish_ifvg_entered}`    | `True` when price enters bearish Inverse FVG              |
| `{bullish_ifvg_within}`     | `True` when price is within bullish Inverse FVG           |
| `{bearish_ifvg_within}`     | `True` when price is within bearish Inverse FVG           |
| `{bullish_ifvg_exited}`     | `True` when price exits bullish Inverse FVG               |
| `{bearish_ifvg_exited}`     | `True` when price exits bearish Inverse FVG               |
| `{bullish_new_dfvg}`        | `True` on detection of a new bullish Double FVG           |
| `{bearish_new_dfvg}`        | `True` on detection of a new bearish Double FVG           |
| `{bullish_dfvg_mitigated}`  | `True` when bullish Double FVG is mitigated               |
| `{bearish_dfvg_mitigated}`  | `True` when bearish Double FVG is mitigated               |
| `{bullish_dfvg_entered}`    | `True` when price enters bullish Double FVG               |
| `{bearish_dfvg_entered}`    | `True` when price enters bearish Double FVG               |
| `{bullish_dfvg_within}`     | `True` when price is within bullish Double FVG            |
| `{bearish_dfvg_within}`     | `True` when price is within bearish Double FVG            |
| `{bullish_dfvg_exited}`     | `True` when price exits bullish Double FVG                |
| `{bearish_dfvg_exited}`     | `True` when price exits bearish Double FVG                |
| `{bullish_new_vi}`          | `True` on detection of a new bullish Volume Imbalance     |
| `{bearish_new_vi}`          | `True` on detection of a new bearish Volume Imbalance     |
| `{bullish_vi_mitigated}`    | `True` when bullish Volume Imbalance is mitigated         |
| `{bearish_vi_mitigated}`    | `True` when bearish Volume Imbalance is mitigated         |
| `{bullish_vi_entered}`      | `True` when price enters bullish Volume Imbalance         |
| `{bearish_vi_entered}`      | `True` when price enters bearish Volume Imbalance         |
| `{bullish_vi_within}`       | `True` when price is within bullish Volume Imbalance      |
| `{bearish_vi_within}`       | `True` when price is within bearish Volume Imbalance      |
| `{bullish_vi_exited}`       | `True` when price exits bullish Volume Imbalance          |
| `{bearish_vi_exited}`       | `True` when price exits bearish Volume Imbalance          |
| `{bullish_new_og}`          | `True` on detection of a new bullish Opening Gap          |
| `{bearish_new_og}`          | `True` on detection of a new bearish Opening Gap          |
| `{bullish_og_mitigated}`    | `True` when bullish Opening Gap is mitigated              |
| `{bearish_og_mitigated}`    | `True` when bearish Opening Gap is mitigated              |
| `{bullish_og_entered}`      | `True` when price enters bullish Opening Gap              |
| `{bearish_og_entered}`      | `True` when price enters bearish Opening Gap              |
| `{bullish_og_within}`       | `True` when price is within bullish Opening Gap           |
| `{bearish_og_within}`       | `True` when price is within bearish Opening Gap           |
| `{bullish_og_exited}`       | `True` when price exits bullish Opening Gap               |
| `{bearish_og_exited}`       | `True` when price exits bearish Opening Gap               |
| `{bullish_liquidity_grab}`  | `True` on detection of a bullish liquidity grab           |
| `{bearish_liquidity_grab}`  | `True` on detection of a bearish liquidity grab           |
| `{bullish_trendline_break}` | `True` on a bullish trendline break                       |
| `{bearish_trendline_break}` | `True` on a bearish trendline break                       |

### Backtester (OSC) Trigger Placeholders [#backtester-osc-trigger-placeholders]

| Placeholder                             | Description                                         |
| --------------------------------------- | --------------------------------------------------- |
| `{regular_bullish_hyperwave_signal}`    | `True` on a new regular bullish HyperWave signal    |
| `{regular_bearish_hyperwave_signal}`    | `True` on a new regular bearish HyperWave signal    |
| `{oversold_bullish_hyperwave_signal}`   | `True` on a new oversold bullish HyperWave signal   |
| `{overbought_bearish_hyperwave_signal}` | `True` on a new overbought bearish HyperWave signal |
| `{hyperwave_co_80}`                     | `True` when HyperWave crosses over 80               |
| `{hyperwave_cu_20}`                     | `True` when HyperWave crosses under 20              |
| `{hyperwave_co_50}`                     | `True` when HyperWave crosses over 50               |
| `{hyperwave_cu_50}`                     | `True` when HyperWave crosses under 50              |
| `{hyperwave_above_80}`                  | `True` when HyperWave is above 80                   |
| `{hyperwave_below_20}`                  | `True` when HyperWave is below 20                   |
| `{hyperwave_above_50}`                  | `True` when HyperWave is above 50                   |
| `{hyperwave_below_50}`                  | `True` when HyperWave is below 50                   |
| `{moneyflow_co_50}`                     | `True` when Money Flow crosses over 50              |
| `{moneyflow_cu_50}`                     | `True` when Money Flow crosses under 50             |
| `{moneyflow_above_50}`                  | `True` when Money Flow is above 50                  |
| `{moneyflow_below_50}`                  | `True` when Money Flow is below 50                  |
| `{new_bullish_overflow}`                | `True` on a new bullish Overflow signal             |
| `{new_bearish_overflow}`                | `True` on a new bearish Overflow signal             |
| `{bullish_overflow}`                    | `True` if bullish Overflow is active                |
| `{bearish_overflow}`                    | `True` if bearish Overflow is active                |
| `{reversal_any_up}`                     | `True` on any new bullish reversal signal           |
| `{reversal_any_down}`                   | `True` on any new bearish reversal signal           |
| `{bullish_divergence}`                  | `True` on a new bullish divergence                  |
| `{bearish_divergence}`                  | `True` on a new bearish divergence                  |
| `{weak_bullish_confluence}`             | `True` on a new weak bullish confluence             |
| `{weak_bearish_confluence}`             | `True` on a new weak bearish confluence             |
| `{strong_bullish_confluence}`           | `True` on a new strong bullish confluence           |
| `{strong_bearish_confluence}`           | `True` on a new strong bearish confluence           |


# Elements (/platform/algos/screeners/osc/elements)



The Screener (OSC) supports various core features from the &#x2A;*Oscillator Matrix®**, each listed out below.

## Screener Elements [#screener-elements]

| Element    | Description                                                                                                                                                       | Default  |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| Price      | Most recent price value                                                                                                                                           | Enabled  |
| Volume     | Most recent volume                                                                                                                                                | Disabled |
| %Change    | Most recent price percent change                                                                                                                                  | Enabled  |
| Change     | Most recent price change                                                                                                                                          | Disabled |
| Rating     | Most recent ticker rating. The rating is given depending on the percentage of bullish information returned by all enabled screener elements for a specific ticker | Enabled  |
| HWO Signal | Most recent HyperWave signal on the ticker                                                                                                                        | Enabled  |
| Money Flow | Most recent Money Flow oscillator value on the ticker                                                                                                             | Enabled  |
| Overflow   | Most recent Overflow value on the ticker                                                                                                                          | Enabled  |
| HyperWave  | Most recent Overflow value on the ticker                                                                                                                          | Enabled  |
| Reversal   | Most recent Reversal signal detected on the ticker                                                                                                                | Enabled  |
| Divergence | Most recent divergence detected between the associated ticker price and associated ticker HyperWave                                                               | Enabled  |
| Confluence | Most recent confluence status detected on the ticker                                                                                                              | Enabled  |

Each element can be disabled/enabled from the screener settings.

## Ratings [#ratings]

The screener ratings indicate the confluence of all returned screener data for each individual ticker by measuring the percentage of bullish indications returned by the enabled screener elements. Ratings are determined as follows:

* ▲ Strong Bullish (more than 80% of the returned information is bullish)
* △ Bullish (60% to 80% of the returned information is bullish)
* ― Neutral (40% to 60% of the returned information is bullish)
* ▽ Bearish (20% to 60% of the returned information is bullish)
* ▼ Strong Bearish (less than 20% of the returned information is bullish)

## Indicator Settings [#indicator-settings]

Users can control the settings of the included indicators in the screener from their respective settings group, these include:

#### Smart Money Flow [#smart-money-flow]

* Main Length
* Smooth

#### HyperWave [#hyperwave]

* Main Length
* Signal & Signal Length
* Divergence Sensitivity

#### Reversals [#reversals]

* Reversal Factor

## Colors [#colors]

Users are able to change the color of the information returned by each element from the "Screener Elements" settings.


# Filtering (/platform/algos/screeners/osc/filtering)



<Frame caption="OSC Screener with △ Bullish Rating filter enabled">
  <img src="/images/screeners/osc/filtering/filter.png" />
</Frame>

From the "Screener Elements" settings users are able to filter out undesired information returned by the screener. This is done from the dropdown setting located on each "Screener Elements" settings.

Certain elements returning specific information from a list of supported options will have a filter allowing to select a precise option, while elements returning a value on the screener will make use of conditional operators (Above/Below).

Users can set a filtering condition on multiple elements, allowing them to display the tickers that respect the user set filtering conditions.


# Introduction (/platform/algos/screeners/osc/introduction)



![](/images/screeners/osc/introduction/screener.png)

The Screener (OSC) is a useful tool able to return recent information from various features in the &#x2A;*Oscillator Matrix®** toolkit for up to 10 user set tickers with customizable timeframe.

In order to use the screener the best way possible it is first advised to fully understand the [Oscillator Matrix®](/platform/algos/oscillator-matrix/introduction) toolkit.

<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/DMq2twlYpB4"
    frameBorder="0"
  />
</div>

Provided below are pages describing all key features included.

<CardGroup cols="3">
  <Card title="Elements" icon="puzzle-piece" href="/screeners/osc/elements" />

  <Card title="Filtering" icon="filter" href="/screeners/osc/filtering" />

  <Card title="Sorting" icon="arrow-up-wide-short" href="/screeners/osc/sorting" />
</CardGroup>

<Info>
  Having access to the &#x2A;*Oscillator Matrix®** will automatically grant you access to the OSC screener.
</Info>

## Tickers [#tickers]

Users can change tickers as well as their respective timeframes from the screener settings. Default tickers include:

* BTCUSDT
* ETHUSDT
* SOLUSDT
* ADAUSDT
* SPX
* AAPL
* TSLA
* NVDA
* EURUSD
* GBPUSD

The default timeframe used for all is the user chart timeframe. Supported tickers includes all tickers available on Tradingview (note that this availability can be affected by your Tradingview plan).

Users can also use the same ticker but different timeframes, allowing to perform multi timeframe analysis of the &#x2A;*Oscillator Matrix®** toolkit. However, duplicated tickers with the same timeframe will result in the duplicate ticker to not appear on the screener.

## Display [#display]

Users are offered a high degree of freedom when it comes to the placement of the screener. By default, the screener is located on a different pane, with the options to select the following positions:

* Top Center
* Top Right
* Top Left
* Bottom Center
* Bottom Right
* Bottom Left

It is also common for more compact screeners to be directly on the price chart, this is possible with this screener by drag and dropping it to the user chart, or by moving it to the existing pane above assuming the screener is in a pane below the price chart.

![](/images/screeners/pane.png)

## Notes [#notes]

Tradingview is not a friendly environment when it comes to running more complete screeners, this cause various limitations showcased below:

### Computation [#computation]

The computation of a screener on Tradingview is very intensive, as such you can expect rare calculations timeouts. When this occurs simply force the screener recalculation by modifying one setting or adding the screener again to your chart.

### Inconsistency [#inconsistency]

It is possible to observe inconsistencies when observing the screener on a chart with a specific symbol due to screener tickers having being on different market.

The crypto market operates 24/7 while stock markets have opening and closing times, this can cause the screener to return different information when observing it from a cryptocurrency or stock/currency pair.


# Sorting (/platform/algos/screeners/osc/sorting)



![](/images/screeners/sort.png)

Users can sort certain elements returning/or with an associated numerical information in either a descending or ascending order. This can be useful to see specific tickers first or last depending on the sorted data on the screener.

Supported elements for sorting include:

* Rating
* Money Flow
* HyperWave
* None

<Note>
  When "None" is selected the screener tickers appear as per their order in the Tickers settings.
</Note>

![Tickers sorted by rating in a descending order](/images/screeners/osc/sorting/sorted.png)


# Elements (/platform/algos/screeners/pac/elements)



The Screener (OSC) supports various core features from the &#x2A;*Price Action Concepts®**, each listed out below.

## Screener Elements [#screener-elements]

| Element         | Description                                                                                                                                                       | Default  |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| Price           | Most recent price value                                                                                                                                           | Enabled  |
| Volume          | Most recent volume                                                                                                                                                | Disabled |
| %Change         | Most recent price percent change                                                                                                                                  | Enabled  |
| Change          | Most recent price change                                                                                                                                          | Disabled |
| Rating          | Most recent ticker rating. The rating is given depending on the percentage of bullish information returned by all enabled screener elements for a specific ticker | Enabled  |
| Structure       | Most recent market structure returned on the ticker                                                                                                               | Enabled  |
| Order Block     | Price position relative to the most recent Order Block area detected on the ticker                                                                                | Enabled  |
| Buy OB Volume   | Buy volume associated to the most recently detected Order Block on the ticker                                                                                     | Disabled |
| Sell OB Volume  | Sell volume associated to the most recently detected Order Block on the ticker                                                                                    | Disabled |
| OB Volume       | Total volume associated to the most recently detected Order Block on the ticker                                                                                   | Enabled  |
| Imbalances      | Status of the most recent detected imbalance                                                                                                                      | Enabled  |
| P\&D Zones      | Price position relative to the most recent premium/equilibirum/discount zones on the ticker                                                                       | Disabled |
| Liquidity Grabs | Most recent detected liquidity grab                                                                                                                               | Enabled  |
| EQHL            | Most recent detected equal high/low                                                                                                                               | Disabled |

Each element can be disabled/enabled from the screener settings.

## Ratings [#ratings]

The screener ratings indicate the confluence of all returned screener data for each individual ticker by measuring the percentage of bullish indications returned by the enabled screener elements. Ratings are determined as follows:

* ▲ Strong Bullish (more than 80% of the returned information is bullish)
* △ Bullish (60% to 80% of the returned information is bullish)
* ― Neutral (40% to 60% of the returned information is bullish)
* ▽ Bearish (20% to 60% of the returned information is bullish)
* ▼ Strong Bearish (less than 20% of the returned information is bullish)

## Indicator Settings [#indicator-settings]

Users can control the settings of the included indicators in the screener from the "Indicator Settings" settings group, these include:

* Market Structure Length
* Order Block Length
* Imbalance Type
* EQHL Period

## Colors [#colors]

Users are able to change the color of the information returned by each element from the "Screener Elements" settings.


# Filtering (/platform/algos/screeners/pac/filtering)



<Frame caption="PAC Screener with △ Bullish Rating filter enabled">
  <img src="/images/screeners/pac/filtering/filter.png" />
</Frame>

From the "Screener Elements" settings users are able to filter out undesired information returned by the screener. This is done from the dropdown setting located on each "Screener Elements" settings.

Certain elements returning specific information from a list of supported options will have a filter allowing to select a precise option, while elements returning a value on the screener will make use of conditional operators (Above/Below).

Users can set a filtering condition on multiple elements, allowing them to display the tickers that respect the user set filtering conditions.


# Introduction (/platform/algos/screeners/pac/introduction)



![](/images/screeners/pac/introduction/screener.png)

The Screener (PAC) is a useful tool able to return recent information from various features in the &#x2A;*Price Action Concepts®** toolkit for up to 10 user set tickers with customizable timeframe.

In order to use the screener the best way possible it is first advised to fully understand the [Price Action Concepts®](/platform/algos/price-action-concepts/introduction) toolkit.

<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/DMq2twlYpB4"
    frameBorder="0"
  />
</div>

Provided below are pages describing all key features included.

<CardGroup cols="3">
  <Card title="Elements" icon="puzzle-piece" href="/screeners/pac/elements" />

  <Card title="Filtering" icon="filter" href="/screeners/pac/filtering" />

  <Card title="Sorting" icon="arrow-up-wide-short" href="/screeners/pac/sorting" />
</CardGroup>

<Info>
  Having access to the &#x2A;*Price Action Concepts®** will automatically grant you access to the PAC screener.
</Info>

## Tickers [#tickers]

Users can change tickers as well as their respective timeframes from the screener settings. Default tickers include:

* BTCUSDT
* ETHUSDT
* SOLUSDT
* ADAUSDT
* SPX
* AAPL
* TSLA
* NVDA
* EURUSD
* GBPUSD

The default timeframe used for all is the user chart timeframe. Supported tickers includes all tickers available on Tradingview (note that this availability can be affected by your Tradingview plan).

Users can also use the same ticker but different timeframes, allowing to perform multi timeframe analysis of the &#x2A;*Price Action Concepts®** toolkit. However, duplicated tickers with the same timeframe will result in the duplicate ticker to not appear on the screener.

## Display [#display]

Users are offered a high degree of freedom when it comes to the placement of the screener. By default, the screener is located on a different pane, with the options to select the following positions:

* Top Center
* Top Right
* Top Left
* Bottom Center
* Bottom Right
* Bottom Left

It is also common for more compact screeners to be directly on the price chart, this is possible with this screener by drag and dropping it to the user chart, or by moving it to the existing pane above assuming the screener is in a pane below the price chart.

![](/images/screeners/pane.png)

## Notes [#notes]

Tradingview is not a friendly environment when it comes to running more complete screeners, this cause various limitations showcased below:

### Computation [#computation]

The computation of a screener on Tradingview is very intensive, as such you can expect rare calculations timeouts. When this occurs simply force the screener recalculation by modifying one setting or adding the screener again to your chart.

### Inconsistency [#inconsistency]

It is possible to observe inconsistencies when observing the screener on a chart with a specific symbol due to screener tickers having being on different market.

The crypto market operates 24/7 while stock markets have opening and closing times, this can cause the screener to return different information when observing it from a cryptocurrency or stock/currency pair.


# Sorting (/platform/algos/screeners/pac/sorting)



![](/images/screeners/sort.png)

Users can sort certain elements returning/or with associated numerical information in either a descending or ascending order. This can be useful to see specific tickers first or last depending on the sorted data on the screener.

Supported elements for sorting include:

* Rating
* Structure
* Structure (Absolute)
* OB Volume
* OB Buy Volume
* OB Sell Volume
* None

<Note>
  When "None" is selected the screener tickers appear as per their order in the Tickers settings.
</Note>

![Tickers sorted by rating in a descending order](/images/screeners/osc/sorting/sorted.png)

## Structure Sorting [#structure-sorting]

By default, sorting by **Structure** orders rows from the strongest **bearish** structures to the strongest **bullish** ones (and inversely if "Ascending" is toggled on).

The ranking used, from strongest to weakest, is:

* Higher consecutive BOS
* CHoCH+
* CHoCH

This same ranking is applied in both bearish and bullish directions.

### Structure (Absolute) Sorting [#structure-absolute-sorting]

The &#x2A;*Structure (Absolute)** option ignores the direction (bullish or bearish) and sorts all structures purely based on the previous ranking.


# Elements (/platform/algos/screeners/s-o/elements)



The Screener (S\&O) supports various core features from the &#x2A;*Signals & Overlays®**, each listed out below.

## Screener Elements [#screener-elements]

| Element          | Description                                                                                                                                                       | Default  |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| Price            | Most recent price value                                                                                                                                           | Enabled  |
| Volume           | Most recent volume                                                                                                                                                | Disabled |
| %Change          | Most recent price percent change                                                                                                                                  | Enabled  |
| Change           | Most recent price change                                                                                                                                          | Disabled |
| Rating           | Most recent ticker rating. The rating is given depending on the percentage of bullish information returned by all enabled screener elements for a specific ticker | Enabled  |
| Signal           | Most recent confirmation or contrarian signal returned on the ticker                                                                                              | Enabled  |
| Exits            | Number of exits signals returned since the most recent signal                                                                                                     | Enabled  |
| Smart Trail      | Current Smart Trail Status                                                                                                                                        | Enabled  |
| Reversal Zones   | Current price position relative to Reversal Zones                                                                                                                 | Disabled |
| Trend Catcher    | Current Trend Catcher Status                                                                                                                                      | Enabled  |
| Trend Tracer     | Current Trend Tracer Status                                                                                                                                       | Enabled  |
| Neo Cloud        | Current Neo Cloud Status                                                                                                                                          | Disabled |
| Trend Strength   | Current Trend Strength value                                                                                                                                      | Enabled  |
| Lux Volatility   | Current Lux Volatility value                                                                                                                                      | Enabled  |
| Squeeze Index    | Current Squeeze Index value                                                                                                                                       | Enabled  |
| Volume Sentiment | Current Volume Sentiment value                                                                                                                                    | Disabled |

Each element can be disabled/enabled from the screener settings.

## Ratings [#ratings]

The screener ratings indicate the confluence of all returned screener data for each individual ticker by measuring the percentage of bullish indications returned by the enabled screener elements. Ratings are determined as follows:

* ▲ Strong Bullish (more than 80% of the returned information is bullish)
* △ Bullish (60% to 80% of the returned information is bullish)
* ― Neutral (40% to 60% of the returned information is bullish)
* ▽ Bearish (20% to 60% of the returned information is bullish)
* ▼ Strong Bearish (less than 20% of the returned information is bullish)

## Signal Settings [#signal-settings]

The most recent signal returned by the screener is affected by the "Signal" settings group. These settings allow changing the sensitivity of the signals as well as the signal algorithm, with users having the option to select Confirmation or Contrarian signals.

## Overlays Settings [#overlays-settings]

Overlay indicators screener elements are affected by their respective settings located in the "Overlays" settings group.

Each overlay has an associated setting affecting their output. These settings range from 1 to 5, with higher values allowing the overlay to return indications for longer-term trends.

## Colors [#colors]

Users are able to change the color of the information returned by each element from the "Screener Elements" settings.


# Filtering (/platform/algos/screeners/s-o/filtering)



<Frame caption="S&O Screener with △ Bullish Rating filter enabled">
  <img src="/images/screeners/s-o/filtering/filter.png" />
</Frame>

From the "Screener Elements" settings users are able to filter out undesired information returned by the screener. This is done from the dropdown setting located on each "Screener Elements" settings.

Certain elements returning specific information from a list of supported options will have a filter allowing to select a precise option, while elements returning a value on the screener will make use of conditional operators (Above/Below), except the Lux Volatility.

Users can set a filtering condition on multiple elements, allowing them to display the tickers that respect the user set filtering conditions.


# Introduction (/platform/algos/screeners/s-o/introduction)



![](/images/screeners/s-o/introduction/screener.png)

The Screener (S\&O) is a useful tool able to return recent information from various features in the &#x2A;*Signals & Overlays®** toolkit for up to 10 user set tickers with customizable timeframe.

In order to use the screener the best way possible it is first advised to fully understand the [Signals & Overlays®](/platform/algos/signals-overlays/introduction) toolkit.

<div style="{ position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden' }">
  <iframe
    style="{
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
  }"
    src="https://www.youtube.com/embed/DMq2twlYpB4"
    frameBorder="0"
  />
</div>

Provided below are pages describing all key features included.

<CardGroup cols="3">
  <Card title="Elements" icon="puzzle-piece" href="/screeners/s-o/elements" />

  <Card title="Filtering" icon="filter" href="/screeners/s-o/filtering" />

  <Card title="Sorting" icon="arrow-up-wide-short" href="/screeners/s-o/sorting" />
</CardGroup>

<Info>
  Having access to the &#x2A;*Signals & Overlays®** will automatically grant you access to the S\&O screener.
</Info>

## Tickers [#tickers]

Users can change tickers as well as their respective timeframes from the screener settings. Default tickers include:

* BTCUSDT
* ETHUSDT
* SOLUSDT
* ADAUSDT
* SPX
* AAPL
* TSLA
* NVDA
* EURUSD
* GBPUSD

The default timeframe used for all is the user chart timeframe. Supported tickers includes all tickers available on Tradingview (note that this availability can be affected by your Tradingview plan).

Users can also use the same ticker but different timeframes, allowing to perform multi timeframe analysis of the &#x2A;*Signals & Overlays®** toolkit. However, duplicated tickers with the same timeframe will result in the duplicate ticker to not appear on the screener.

## Display [#display]

Users are offered a high degree of freedom when it comes to the placement of the screener. By default, the screener is located on a different pane, with the options to select the following positions:

* Top Center
* Top Right
* Top Left
* Bottom Center
* Bottom Right
* Bottom Left

It is also common for more compact screeners to be directly on the price chart, this is possible with this screener by drag and dropping it to the user chart, or by moving it to the existing pane above assuming the screener is in a pane below the price chart.

![](/images/screeners/pane.png)

## Notes [#notes]

Tradingview is not a friendly environment when it comes to running more complete screeners, this cause various limitations showcased below:

### Computation [#computation]

The computation of a screener on Tradingview is very intensive, as such you can expect rare calculations timeouts. When this occurs simply force the screener recalculation by modifying one setting or adding the screener again to your chart.

### Inconsistency [#inconsistency]

It is possible to observe inconsistencies when observing the screener on a chart with a specific symbol due to screener tickers having being on different market.

The crypto market operates 24/7 while stock markets have opening and closing times, this can cause the screener to return different information when observing it from a cryptocurrency or stock/currency pair.


# Sorting (/platform/algos/screeners/s-o/sorting)



![](/images/screeners/sort.png)

Users can sort certain elements returning/or with an associated numerical information in either a descending or ascending order. This can be useful to see specific tickers first or last depending on the sorted data on the screener.

Supported elements for sorting include:

* Rating
* Trend Strength
* Lux Volatility
* Squeeze Index
* Volume Sentiment
* Exits
* None

<Note>
  When "None" is selected the screener tickers appear as per their order in the Tickers settings.
</Note>

![Tickers sorted by rating in a descending order](/images/screeners/s-o/sorting/sorted.png)


# Alerts (/developers/pinets/alerts)



PineTS supports Pine Script's `alert()` and `alertcondition()` functions, allowing indicators and strategies to emit alert events that your application can capture and act on — for example, sending webhook notifications, triggering trades, or logging signals.

## Table of Contents [#table-of-contents]

* [Overview](#overview)
* [alert()](#alert)
* [alertcondition()](#alertcondition)
* [Capturing Alerts](#capturing-alerts)
* [Alert Modes](#alert-modes)
* [Complete Examples](#complete-examples)

***

## Overview [#overview]

| Function                                    | Purpose                                              | Fires when                      |
| ------------------------------------------- | ---------------------------------------------------- | ------------------------------- |
| `alert(message, freq)`                      | Imperative — fires when the code path executes it    | Code reaches the `alert()` call |
| `alertcondition(condition, title, message)` | Declarative — fires when a boolean condition is true | `condition` evaluates to `true` |

By default, alerts only fire on the **last (realtime) bar**, matching TradingView's behavior. Use `setAlertMode('all')` to fire on every bar for backtesting.

***

## alert() [#alert]

Fires an alert event when executed. Supports frequency gating to control how often the alert triggers.

### Pine Script Syntax [#pine-script-syntax]

```javascript
//@version=6
indicator("EMA Cross Alert", overlay=true)

ema9 = ta.ema(close, 9)
ema21 = ta.ema(close, 21)

if ta.crossover(ema9, ema21)
    alert("Bullish EMA cross! Price: " + str.tostring(close), alert.freq_once_per_bar)

if ta.crossunder(ema9, ema21)
    alert("Bearish EMA cross! Price: " + str.tostring(close), alert.freq_once_per_bar)

plot(ema9, "EMA 9", color.blue)
plot(ema21, "EMA 21", color.red)
```

### Frequency Constants [#frequency-constants]

| Constant                        | Behavior                                                            |
| ------------------------------- | ------------------------------------------------------------------- |
| `alert.freq_all`                | Every `alert()` call triggers (including multiple calls per bar)    |
| `alert.freq_once_per_bar`       | Only the **first** `alert()` call per bar triggers (default)        |
| `alert.freq_once_per_bar_close` | Only triggers on the bar's **final tick** (when it closes/confirms) |

If no frequency is specified, `alert.freq_once_per_bar` is used.

***

## alertcondition() [#alertcondition]

Registers a named alert condition that fires when a boolean expression is true. Unlike `alert()`, it doesn't support frequency gating — it fires once per bar when the condition is met.

### Pine Script Syntax [#pine-script-syntax-1]

```javascript
//@version=6
indicator("RSI Alert Conditions")

rsi = ta.rsi(close, 14)

alertcondition(rsi > 70, "Overbought", "RSI crossed above 70!")
alertcondition(rsi < 30, "Oversold", "RSI dropped below 30!")

plot(rsi, "RSI")
hline(70, "Overbought")
hline(30, "Oversold")
```

### Parameters [#parameters]

| Parameter   | Type           | Required | Description                         |
| ----------- | -------------- | -------- | ----------------------------------- |
| `condition` | `series bool`  | Yes      | When `true`, the alert fires        |
| `title`     | `const string` | No       | Name of the alert condition         |
| `message`   | `const string` | No       | Message included in the alert event |

***

## Capturing Alerts [#capturing-alerts]

### With run() (Promise-based) [#with-run-promise-based]

After execution, alerts are available on the returned context object:

```typescript
import { PineTS, Provider } from 'pinets';

const pine = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

const code = `
//@version=6
indicator("Alert Demo")
if ta.crossover(ta.ema(close, 9), ta.ema(close, 21))
    alert("Bullish cross detected!", alert.freq_once_per_bar)
alertcondition(ta.rsi(close, 14) > 70, "Overbought", "RSI > 70")
plot(close)
`;

const ctx = await pine.run(code);

// Access all alert events
for (const a of ctx.alerts) {
    console.log(`[${a.type}] ${a.message} (bar ${a.bar_index})`);
}
```

### With stream() (Event-based) [#with-stream-event-based]

In streaming mode, alerts are emitted as `'alert'` events in real time:

```typescript
import { PineTS, Provider } from 'pinets';

const pine = new PineTS(Provider.Binance, 'BTCUSDT', '1m', 100);

const code = `
//@version=6
indicator("Live Alert")
if ta.crossover(close, ta.sma(close, 20))
    alert("Price crossed above SMA 20!", alert.freq_once_per_bar)
plot(close)
`;

const evt = pine.stream(code, { live: true, interval: 5000 });

evt.on('alert', (alert) => {
    console.log('ALERT:', alert.message);
    // Send to webhook, trigger trade, send notification, etc.
    // sendWebhook(alert.message);
});

evt.on('data', (ctx) => {
    // Normal data processing
});

evt.on('error', (err) => {
    console.error('Error:', err);
});
```

### Alert Event Structure [#alert-event-structure]

Each alert event (whether from `alert()` or `alertcondition()`) has this shape:

```typescript
{
    type: 'alert' | 'alertcondition',
    message: string,          // The alert message
    title?: string,           // Title (alertcondition only)
    freq?: string,            // Frequency constant (alert only)
    bar_index: number,        // Bar index where the alert fired
    time: number              // Bar open time (ms timestamp)
}
```

***

## Alert Modes [#alert-modes]

By default, PineTS matches TradingView behavior: alerts only fire on the **last (realtime) bar**. Historical bars are silently skipped. This is the correct behavior for live trading.

For **backtesting**, you can switch to `'all'` mode to fire alerts on every bar:

```typescript
const pine = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 365);

// Backtest mode — fire alerts on ALL bars
pine.setAlertMode('all');

const code = `
//@version=6
indicator("Backtest Alerts")
if ta.crossover(ta.ema(close, 9), ta.ema(close, 21))
    alert("Buy signal", alert.freq_once_per_bar)
if ta.crossunder(ta.ema(close, 9), ta.ema(close, 21))
    alert("Sell signal", alert.freq_once_per_bar)
plot(close)
`;

const ctx = await pine.run(code);

console.log(`Total alerts over ${ctx.marketData.length} bars: ${ctx.alerts.length}`);
for (const a of ctx.alerts) {
    const date = new Date(a.time).toISOString().slice(0, 10);
    console.log(`  ${date}: ${a.message}`);
}
```

| Mode                   | Behavior                         | Use case                     |
| ---------------------- | -------------------------------- | ---------------------------- |
| `'realtime'` (default) | Alerts fire only on the last bar | Live trading, webhooks       |
| `'all'`                | Alerts fire on every bar         | Backtesting, signal analysis |

***

## Complete Examples [#complete-examples]

### Example 1: Webhook-Ready Live Alerts [#example-1-webhook-ready-live-alerts]

```typescript
import { PineTS, Provider } from 'pinets';

const pine = new PineTS(Provider.Binance, 'BTCUSDT', '5', 200);

const strategy = `
//@version=6
indicator("Scalp Alerts", overlay=true)

fast = ta.ema(close, 8)
slow = ta.ema(close, 21)
rsi = ta.rsi(close, 14)

// Multi-condition alert
if ta.crossover(fast, slow) and rsi > 50
    alert("LONG entry: EMA cross + RSI > 50 at " + str.tostring(close), alert.freq_once_per_bar)

if ta.crossunder(fast, slow) and rsi < 50
    alert("SHORT entry: EMA cross + RSI < 50 at " + str.tostring(close), alert.freq_once_per_bar)

plot(fast, "Fast EMA", color.green)
plot(slow, "Slow EMA", color.red)
`;

const evt = pine.stream(strategy, { live: true, interval: 10000 });

evt.on('alert', async (alert) => {
    console.log(`[${new Date().toISOString()}] ${alert.message}`);
    // await fetch('https://your-webhook.com/alerts', {
    //     method: 'POST',
    //     body: JSON.stringify(alert)
    // });
});

evt.on('data', (ctx) => {
    const close = ctx.marketData[ctx.idx]?.close;
    console.log(`Bar ${ctx.idx}: close=${close}`);
});
```

### Example 2: Backtest Signal Analysis [#example-2-backtest-signal-analysis]

```typescript
import { PineTS, Provider } from 'pinets';

async function backtestAlerts() {
    const pine = new PineTS(Provider.Binance, 'BTCUSDT', 'D', undefined, new Date('2024-01-01').getTime(), new Date('2024-12-31').getTime());

    pine.setAlertMode('all');

    const code = `
//@version=6
indicator("Backtest Signals")
rsi = ta.rsi(close, 14)
alertcondition(rsi < 30, "Oversold", "RSI oversold — potential buy")
alertcondition(rsi > 70, "Overbought", "RSI overbought — potential sell")
plot(rsi)
    `;

    const ctx = await pine.run(code);

    const buys = ctx.alerts.filter((a) => a.title === 'Oversold');
    const sells = ctx.alerts.filter((a) => a.title === 'Overbought');

    console.log(`Oversold signals: ${buys.length}`);
    console.log(`Overbought signals: ${sells.length}`);

    for (const signal of ctx.alerts) {
        const date = new Date(signal.time).toISOString().slice(0, 10);
        console.log(`  ${date}: [${signal.title}] ${signal.message}`);
    }
}

backtestAlerts();
```

### Example 3: Custom Data with Alerts [#example-3-custom-data-with-alerts]

```typescript
import { PineTS } from 'pinets';

const data = [
    { openTime: Date.now() - 86400000 * 5, open: 100, high: 110, low: 95, close: 105, volume: 1000, closeTime: Date.now() - 86400000 * 4 },
    { openTime: Date.now() - 86400000 * 4, open: 105, high: 115, low: 100, close: 112, volume: 1200, closeTime: Date.now() - 86400000 * 3 },
    { openTime: Date.now() - 86400000 * 3, open: 112, high: 120, low: 108, close: 98, volume: 800, closeTime: Date.now() - 86400000 * 2 },
    { openTime: Date.now() - 86400000 * 2, open: 98, high: 105, low: 90, close: 92, volume: 1500, closeTime: Date.now() - 86400000 * 1 },
    { openTime: Date.now() - 86400000 * 1, open: 92, high: 100, low: 88, close: 95, volume: 900, closeTime: Date.now() },
];

const pine = new PineTS(data);
pine.setAlertMode('all');

const code = `
//@version=6
indicator("Custom Alerts")
if close < open
    alert("Bearish bar at " + str.tostring(close))
plot(close)
`;

const ctx = await pine.run(code);
console.log('Alerts:', ctx.alerts.length);
```


# Data Providers (/developers/pinets/data-providers)



PineTS supports multiple market data providers out of the box. Each provider fetches OHLCV candle data and symbol metadata, which PineTS uses to run your indicators.

***

## Overview [#overview]

There are two ways to use a provider:

**1. Singleton** (from the `Provider` registry):

```typescript
import { PineTS, Provider } from 'pinets';

const pine = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);
```

**2. Direct instantiation** (for provider-specific config):

```typescript
import { PineTS, BinanceProvider } from 'pinets';

const binance = new BinanceProvider();
const pine = new PineTS(binance, 'BTCUSDT', 'D', 100);
```

Providers that require API keys must be configured before use, either via `.configure()` on the singleton or by passing config to the constructor.

### Timeframe Aggregation [#timeframe-aggregation]

If a provider doesn't natively support a requested timeframe, PineTS automatically selects the best sub-timeframe and aggregates candles. For example, requesting `'45'` (45 min) from Binance will fetch `'15'` minute candles and aggregate 3:1. This is handled transparently by the `BaseProvider` class.

***

## Binance [#binance]

The Binance provider fetches cryptocurrency market data. &#x2A;*No API key required.**

### Quick Start [#quick-start]

```typescript
import { PineTS, Provider } from 'pinets';

// Fetch 100 daily BTC candles and compute SMA
const pine = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

const { result, plots } = await pine.run(`
//@version=6
indicator("BTC SMA Cross")
sma20 = ta.sma(close, 20)
sma50 = ta.sma(close, 50)
plot(sma20, "SMA 20", color.blue)
plot(sma50, "SMA 50", color.red)
`);
```

### Symbols [#symbols]

| Format   | Example              | Market |
| -------- | -------------------- | ------ |
| `SYMBOL` | `BTCUSDT`, `ETHUSDC` | Spot   |

### Supported Timeframes [#supported-timeframes]

`'1'`, `'3'`, `'5'`, `'15'`, `'30'`, `'60'`, `'120'`, `'240'`, `'D'`, `'W'`, `'M'`

Timeframes not in this list (e.g., `'45'`, `'180'`) are automatically aggregated from smaller candles.

### Features [#features]

* **No API key** required
* **Auto-pagination** for fetching >1000 candles
* **5-minute cache** for repeated identical requests
* **Dual endpoint fallback**: tries `api.binance.com`, falls back to `api.binance.us`

### Examples [#examples]

```typescript
import { PineTS, Provider, BinanceProvider } from 'pinets';

// Using the singleton
const pine1 = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

// Direct instantiation (equivalent)
const binance = new BinanceProvider();
const pine2 = new PineTS(binance, 'ETHUSDT', '1W', 50);

// With date range
const sDate = new Date('2024-01-01').getTime();
const eDate = new Date('2024-06-30').getTime();
const pine3 = new PineTS(Provider.Binance, 'BTCUSDT', 'D', undefined, sDate, eDate);
```

***

## FMP (Financial Modeling Prep) [#fmp-financial-modeling-prep]

The FMP provider fetches stock, forex, and crypto data from [Financial Modeling Prep](https://financialmodelingprep.com/). &#x2A;*Requires an API key.**

### Quick Start [#quick-start-1]

```typescript
import { PineTS, Provider } from 'pinets';

// Configure with your API key
Provider.FMP.configure({ apiKey: 'your-api-key' });

// Fetch 200 daily AAPL candles
const pine = new PineTS(Provider.FMP, 'AAPL', 'D', 200);

const { result } = await pine.run(`
//@version=6
indicator("AAPL RSI")
rsi = ta.rsi(close, 14)
plot(rsi, "RSI")
hline(70, "Overbought")
hline(30, "Oversold")
`);
```

### Configuration [#configuration]

```typescript
// Option 1: Configure the singleton
Provider.FMP.configure({ apiKey: 'your-api-key' });

// Option 2: Direct instantiation with config
import { FMPProvider } from 'pinets';
const fmp = new FMPProvider({ apiKey: 'your-api-key' });
const pine = new PineTS(fmp, 'AAPL', 'D', 200);
```

| Option    | Type     | Required | Description               |
| --------- | -------- | -------- | ------------------------- |
| `apiKey`  | `string` | Yes      | Your FMP API key          |
| `baseUrl` | `string` | No       | Override the API base URL |

### Symbols [#symbols-1]

| Format | Example               | Market         |
| ------ | --------------------- | -------------- |
| Ticker | `AAPL`, `MSFT`, `SPY` | US Stocks      |
| Crypto | `BTCUSD`, `ETHUSD`    | Cryptocurrency |
| Forex  | `EURUSD`              | Currency pairs |

### Supported Timeframes [#supported-timeframes-1]

| Tier     | Timeframes                                    |
| -------- | --------------------------------------------- |
| **Free** | `'D'` (daily only)                            |
| **Paid** | `'1'`, `'5'`, `'15'`, `'30'`, `'60'`, `'240'` |

### Features [#features-1]

* **Exchange-aware session times**: maps exchanges to IANA timezones (e.g., NYSE = `America/New_York`, `0930-1600`)
* **Session-aware closeTime**: correctly computes bar close times including early-close days
* **Profile caching**: symbol metadata is fetched once and cached

### Examples [#examples-1]

```typescript
import { PineTS, Provider, FMPProvider } from 'pinets';

// Configure singleton
Provider.FMP.configure({ apiKey: 'your-api-key' });

// US Stock
const pine1 = new PineTS(Provider.FMP, 'AAPL', 'D', 200);

// Crypto via FMP
const pine2 = new PineTS(Provider.FMP, 'BTCUSD', 'D', 100);

// Forex
const pine3 = new PineTS(Provider.FMP, 'EURUSD', 'D', 100);

// Direct instantiation (separate instance with its own config)
const fmp = new FMPProvider({ apiKey: 'different-api-key' });
const pine4 = new PineTS(fmp, 'MSFT', 'D', 100);
```

***

## Alpaca [#alpaca]

The Alpaca provider fetches US stock and crypto data from [Alpaca Markets](https://alpaca.markets/). &#x2A;*Requires an API key and secret.**

### Quick Start [#quick-start-2]

```typescript
import { PineTS, Provider } from 'pinets';

// Configure with your credentials
Provider.Alpaca.configure({
    apiKey: 'PK...',
    apiSecret: '...',
    paper: true,
    feed: 'iex',
});

// Fetch 200 daily AAPL candles
const pine = new PineTS(Provider.Alpaca, 'AAPL', 'D', 200);

const { result } = await pine.run(`
//@version=6
indicator("AAPL Bollinger Bands")
[middle, upper, lower] = ta.bb(close, 20, 2)
plot(middle, "Middle", color.blue)
plot(upper, "Upper", color.green)
plot(lower, "Lower", color.red)
`);
```

### Configuration [#configuration-1]

```typescript
// Option 1: Configure the singleton
Provider.Alpaca.configure({
    apiKey: 'PK...',
    apiSecret: '...',
    paper: true,
    feed: 'sip',
});

// Option 2: Direct instantiation
import { AlpacaProvider } from 'pinets';
const alpaca = new AlpacaProvider({
    apiKey: 'PK...',
    apiSecret: '...',
    paper: true,
    feed: 'iex',
});
const pine = new PineTS(alpaca, 'AAPL', 'D', 200);
```

| Option       | Type             | Required | Default | Description                                                      |
| ------------ | ---------------- | -------- | ------- | ---------------------------------------------------------------- |
| `apiKey`     | `string`         | Yes      | -       | Alpaca API key ID (`PK...`)                                      |
| `apiSecret`  | `string`         | Yes      | -       | Alpaca secret key                                                |
| `paper`      | `boolean`        | No       | `true`  | `true` for paper trading, `false` for live                       |
| `feed`       | `'sip' \| 'iex'` | No       | `'sip'` | `'sip'` = full market data (paid), `'iex'` = IEX exchange (free) |
| `dataUrl`    | `string`         | No       | -       | Override the market data API URL                                 |
| `tradingUrl` | `string`         | No       | -       | Override the trading/asset API URL                               |

### Symbols [#symbols-2]

| Format | Example               | Market                          |
| ------ | --------------------- | ------------------------------- |
| Ticker | `AAPL`, `MSFT`, `SPY` | US Stocks                       |
| Crypto | `BTC/USD`, `ETH/USD`  | Cryptocurrency (note the slash) |

> **Note:** Crypto symbols use slash notation (`BTC/USD`), not concatenated (`BTCUSD`).

### Supported Timeframes [#supported-timeframes-2]

`'1'`, `'3'`, `'5'`, `'15'`, `'30'`, `'45'`, `'60'`, `'120'`, `'180'`, `'240'`, `'D'`, `'W'`, `'M'`

Alpaca natively supports all common timeframes including `'45'` and `'180'` which other providers aggregate.

### Features [#features-2]

* **Calendar-aware closeTime** for stocks: uses Alpaca's trading calendar API for exact session times (handles early closes and DST)
* **Fixed-duration closeTime** for crypto: 24/7 markets use computed period boundaries
* **Auto-pagination**: handles large requests (10,000+ candles) via page tokens
* **Asset and calendar caching**: symbol info and trading calendar fetched once per session

### Data Feed Options [#data-feed-options]

| Feed    | Cost            | Coverage                                            |
| ------- | --------------- | --------------------------------------------------- |
| `'sip'` | Paid ($9/month) | Full consolidated market data from all US exchanges |
| `'iex'` | Free            | IEX exchange data only (may have gaps or lag)       |

### Examples [#examples-2]

```typescript
import { PineTS, Provider, AlpacaProvider } from 'pinets';

// Configure singleton
Provider.Alpaca.configure({
    apiKey: 'PK...',
    apiSecret: '...',
    paper: true,
    feed: 'iex',
});

// US Stock
const pine1 = new PineTS(Provider.Alpaca, 'AAPL', 'D', 200);

// Crypto (note the slash notation)
const pine2 = new PineTS(Provider.Alpaca, 'BTC/USD', 'D', 100);

// Direct instantiation
const alpaca = new AlpacaProvider({
    apiKey: 'PK...',
    apiSecret: '...',
    feed: 'sip',
});
const pine3 = new PineTS(alpaca, 'MSFT', '60', 500);
```

***

## Custom Data [#custom-data]

You can bypass providers entirely by passing your own OHLCV data array:

```typescript
import { PineTS } from 'pinets';

const customData = [
    {
        openTime: new Date('2024-01-01').getTime(),
        open: 42000,
        high: 43000,
        low: 41500,
        close: 42500,
        volume: 1234.56,
        closeTime: new Date('2024-01-02').getTime(),
    },
    {
        openTime: new Date('2024-01-02').getTime(),
        open: 42500,
        high: 44000,
        low: 42000,
        close: 43800,
        volume: 2345.67,
        closeTime: new Date('2024-01-03').getTime(),
    },
    // ... more candles
];

const pine = new PineTS(customData);
const { result } = await pine.run(`
//@version=6
indicator("Custom Data SMA")
plot(ta.sma(close, 10))
`);
```

### Data Format [#data-format]

Each candle object must include:

| Field       | Type     | Required | Description                   |
| ----------- | -------- | -------- | ----------------------------- |
| `open`      | `number` | Yes      | Opening price                 |
| `high`      | `number` | Yes      | Highest price                 |
| `low`       | `number` | Yes      | Lowest price                  |
| `close`     | `number` | Yes      | Closing price                 |
| `volume`    | `number` | Yes      | Trading volume                |
| `openTime`  | `number` | No       | Bar open time (ms timestamp)  |
| `closeTime` | `number` | No       | Bar close time (ms timestamp) |

If `openTime` / `closeTime` are omitted, time-dependent functions (`time`, `hour`, `dayofmonth`, etc.) will not work correctly.

***

## Custom Providers [#custom-providers]

You can create your own provider by implementing the `IProvider` interface or extending `BaseProvider`:

```typescript
import { BaseProvider, PineTS } from 'pinets';
import type { ISymbolInfo, Kline } from 'pinets';

class MyProvider extends BaseProvider {
    constructor() {
        super({ requiresApiKey: false, providerName: 'MyProvider' });
    }

    protected getSupportedTimeframes() {
        return new Set(['1', '5', '15', '60', 'D']);
    }

    protected async _getMarketDataNative(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<Kline[]> {
        // Fetch data from your API
        const response = await fetch(`https://my-api.com/candles?symbol=${tickerId}&tf=${timeframe}`);
        const data = await response.json();

        return data.map((candle) => ({
            openTime: candle.time,
            open: candle.o,
            high: candle.h,
            low: candle.l,
            close: candle.c,
            volume: candle.v,
            closeTime: candle.time + 86400000, // next bar open
        }));
    }

    async getSymbolInfo(tickerId: string): Promise<ISymbolInfo> {
        // Return symbol metadata
        return { ticker: tickerId /* ... */ } as ISymbolInfo;
    }
}

// Use it
const provider = new MyProvider();
const pine = new PineTS(provider, 'AAPL', 'D', 100);
```

By extending `BaseProvider`, you get automatic timeframe aggregation for free. If you request a timeframe not in `getSupportedTimeframes()`, the base class will automatically fetch a supported sub-timeframe and aggregate.

You can also add your provider to the global registry for convenience:

```typescript
import { Provider } from 'pinets';

Provider['MyProvider'] = new MyProvider();
// Now available as Provider.MyProvider
```

### Chart-Type Modifiers (Extended Tickers) [#chart-type-modifiers-extended-tickers]

A ticker may carry a chart-type modifier suffix — `"BTCUSDT;heikinashi"` (see [Non-Standard Chart Types](/developers/pinets/initialization-and-usage#non-standard-chart-types-heikin-ashi)). The contract at the provider boundary:

* **Providers extending `BaseProvider`** never see the modifier in `getMarketData()` — the base class strips it, because a venue API serves standard candles only. Strip it likewise at the top of your `getSymbolInfo()` (all bundled providers do): `tickerId = stripTickerModifier(tickerId)` (exported from `pinets`).
* **A source that OWNS a chart-type transform** (typically an embedding host implementing `IProvider` directly, not via `BaseProvider`) receives the extended ticker verbatim and must serve the derived bars for `"SYM;heikinashi"` and raw bars for `"SYM"` — the modifier is the only thing distinguishing the two requests.
* **PineTS itself never transforms bars** — the modifier is routing metadata, end to end.

***

## Timeframe Reference [#timeframe-reference]

Standard timeframe strings used across all providers:

| String            | Description | Period   |
| ----------------- | ----------- | -------- |
| `'1'`             | 1 minute    | Intraday |
| `'3'`             | 3 minutes   | Intraday |
| `'5'`             | 5 minutes   | Intraday |
| `'15'`            | 15 minutes  | Intraday |
| `'30'`            | 30 minutes  | Intraday |
| `'45'`            | 45 minutes  | Intraday |
| `'60'`            | 1 hour      | Intraday |
| `'120'`           | 2 hours     | Intraday |
| `'180'`           | 3 hours     | Intraday |
| `'240'` or `'4H'` | 4 hours     | Intraday |
| `'D'` or `'1D'`   | 1 day       | Daily    |
| `'W'` or `'1W'`   | 1 week      | Weekly   |
| `'M'` or `'1M'`   | 1 month     | Monthly  |

### Provider Support Matrix [#provider-support-matrix]

| Timeframe | Binance    | FMP (Free) | FMP (Paid) | Alpaca |
| --------- | ---------- | ---------- | ---------- | ------ |
| `'1'`     | Native     | -          | Native     | Native |
| `'3'`     | Native     | -          | -          | Native |
| `'5'`     | Native     | -          | Native     | Native |
| `'15'`    | Native     | -          | Native     | Native |
| `'30'`    | Native     | -          | Native     | Native |
| `'45'`    | Aggregated | -          | -          | Native |
| `'60'`    | Native     | -          | Native     | Native |
| `'120'`   | Native     | -          | -          | Native |
| `'180'`   | Aggregated | -          | -          | Native |
| `'240'`   | Native     | -          | Native     | Native |
| `'D'`     | Native     | Native     | Native     | Native |
| `'W'`     | Native     | -          | -          | Native |
| `'M'`     | Native     | -          | -          | Native |

**Native** = provider fetches directly. **Aggregated*&#x2A; = PineTS fetches smaller candles and combines them. &#x2A;*-** = not available.


# Getting Started (/developers/pinets/getting-started)



## Getting Started with PineTS [#getting-started-with-pinets]

PineTS is a JavaScript/TypeScript runtime that enables execution of Pine Script indicators in JavaScript environments. It supports two input formats:

1. **Native Pine Script v5/v6** - Run original Pine Script code directly
2. **PineTS Syntax** - A JavaScript/TypeScript syntax that closely mirrors Pine Script

It preserves the original functionality and behavior while providing robust handling of time-series data processing, technical analysis calculations, and Pine Script's distinctive scoping mechanisms.

## Installation [#installation]

```bash
npm install pinets
```

## Usage Examples [#usage-examples]

### Option 1: Run Native Pine Script Directly [#option-1-run-native-pine-script-directly]

Starting with v0.7.0, you can run original Pine Script code directly without conversion:

```javascript
import { PineTS, Provider } from 'pinets';

// Initialize with market data
const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

// Run native Pine Script directly - no conversion needed!
const pineScriptCode = `
//@version=5
indicator('My EMA Cross Strategy')

ema9 = ta.ema(close, 9)
ema18 = ta.ema(close, 18)

bull_bias = ema9 > ema18
bear_bias = ema9 < ema18

prev_close = close[1]
diff_close = close - prev_close

plot(ema9, title = '9 EMA', color = color.yellow)
plot(ema18, title = '18 EMA', color = color.red)
`;

const { result, plots } = await pineTS.run(pineScriptCode);
// Access results: result.ema9, result.ema18, result.bull_bias, result.bear_bias
```

### Option 3: Runtime Inputs [#option-3-runtime-inputs]

You can pass dynamic values to your indicator's `input()` functions at runtime using the `Indicator` class. This allows you to change parameters without modifying the code.

```javascript
import { PineTS, Provider, Indicator } from 'pinets';

const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

const code = `
//@version=5
indicator("Dynamic MA")
len = input.int(14, "Length")
src = input.source(close, "Source")
plot(ta.sma(src, len))
`;

// Override default input values
const indicator = new Indicator(code, {
    Length: 50, // Use 50 instead of 14
});

const { result } = await pineTS.run(indicator);
```

> **⚠️ Note**: Native Pine Script support is experimental. Some indicators may fail if they use API features not yet implemented. Check the [API Coverage](/developers/pinets/../api-coverage) pages to verify compatibility.

***

### Option 2: Use PineTS Syntax [#option-2-use-pinets-syntax]

If you prefer or need more control, you can use PineTS syntax, which is a JavaScript/TypeScript version with minimal differences from Pine Script.

#### Converting Pine Script to PineTS [#converting-pine-script-to-pinets]

Original Pine Script:

```javascript
//@version=5
indicator('My EMA Cross Strategy');

ema9 = ta.ema(close, 9);
ema18 = ta.ema(close, 18);

bull_bias = ema9 > ema18;
bear_bias = ema9 < ema18;

prev_close = close[1];
diff_close = close - prev_close;

_oo = open;
_oo = math.abs(open[1] - close[2]);
```

Equivalent PineTS syntax:

```javascript
const ema9 = ta.ema(close, 9);
const ema18 = ta.ema(close, 18);

const bull_bias = ema9 > ema18;
const bear_bias = ema9 < ema18;

const prev_close = close[1];
const diff_close = close - prev_close;

let _oo = open;
_oo = math.abs(open[1] - close[2]);
```

#### Running PineTS Syntax Code [#running-pinets-syntax-code]

```javascript
import { PineTS, Provider } from 'pinets';

// Initialize with market data
const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

// Run your indicator
const { result } = await pineTS.run((context) => {
    const ta = context.ta;
    const math = context.math;
    const { close, open } = context.data;

    const ema9 = ta.ema(close, 9);
    const ema18 = ta.ema(close, 18);

    const bull_bias = ema9 > ema18;
    const bear_bias = ema9 < ema18;

    const prev_close = close[1];
    const diff_close = close - prev_close;

    let _oo = open;
    _oo = math.abs(open[1] - close[2]);
});
```

## Streaming Data and Pagination [#streaming-data-and-pagination]

For processing large datasets efficiently or handling live data streams, use the `stream()` method:

```javascript
import { PineTS, Provider } from 'pinets';

// Initialize with a provider
const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', '1m', 100);

// Start streaming
const evt = pineTS.stream(
    (context) => {
        const { close } = context.data;
        const sma = context.ta.sma(close, 14);
        return { close, sma };
    },
    {
        pageSize: 50, // Process in chunks of 50
        live: true, // Continue with live data
        interval: 2000, // Poll every 2 seconds
    },
);

// Listen for updates
evt.on('data', (ctx) => {
    const { close, sma } = ctx.result;
    console.log(`Update: Close=${close[close.length - 1]}, SMA=${sma[sma.length - 1]}`);
});

evt.on('error', (err) => console.error(err));

// To stop streaming later
// evt.stop();
```

**Benefits:**

* Memory efficient for large datasets
* Event-based interface is easier to integrate
* Automatically streams live market data
* Perfect for real-time trading bots and dashboards

📖 &#x2A;*For complete pagination and streaming documentation, see [Pagination & Live Streaming](/developers/pinets/../pagination).**

## Key Differences: PineTS Syntax vs Native Pine Script [#key-differences-pinets-syntax-vs-native-pine-script]

When using **PineTS syntax** (Option 2), note these differences from native Pine Script:

1. **Variable Declaration**: Use JavaScript's `const`, `let`, and `var` instead of Pine Script's implicit declaration
2. **Function Syntax**: JavaScript arrow functions and standard function syntax
3. **Module System**: Import Pine Script namespaces from context: `const { ta, math } = context; const { close, open } = context.data;`
4. **Object Syntax**: Use JavaScript object notation for parameters: `plot(value, { title: 'My Plot', color: color.yellow })`
5. **Scoping Rules**: Maintains Pine Script's series behavior through runtime transformation
6. **Return Syntax**: Can return an object with indicator results for easy access in JavaScript

When using **Native Pine Script** (Option 1), write code exactly as you would in TradingView - no conversion needed!

## Data Providers [#data-providers]

PineTS includes built-in providers for multiple data sources:

| Provider           | Market                | API Key      |
| ------------------ | --------------------- | ------------ |
| `Provider.Binance` | Crypto                | Not required |
| `Provider.FMP`     | Stocks, forex, crypto | Required     |
| `Provider.Alpaca`  | US stocks, crypto     | Required     |

You can also pass your own OHLCV data array instead of using a provider. See the &#x2A;*[Data Providers](/developers/pinets/../data-providers)** page for configuration details and examples.

## Core Components [#core-components]

### PineTS Class [#pinets-class]

The main class that handles:

* Market data management
* Series calculations
* Built-in variables (open, high, low, close, volume, etc.)
* Runtime execution context

### Namespaces [#namespaces]

* **Core**: Essential Pine Script functionality and base operations
* **TechnicalAnalysis**: Comprehensive set of technical indicators and analysis functions
* **PineMath**: Mathematical operations and precision handling
* **Input**: Parameter and input management system
* **Syminfo**: Symbol information and market data helpers

## Alerts [#alerts]

PineTS supports `alert()` and `alertcondition()` from Pine Script. Capture alert events via `context.alerts` after `run()`, or listen for real-time `'alert'` events with `stream()`. Use `setAlertMode('all')&#x60; for backtesting. See the &#x2A;*[Alerts](/developers/pinets/../alerts)** page for details.

## Project Status [#project-status]

For the current implementation status:

* See [Language Coverage](/developers/pinets/../lang-coverage) for language features
* See [API Coverage](/developers/pinets/../api-coverage) for API functions and methods

## Next Steps [#next-steps]

After getting started, try exploring our demo indicators:

* [WillVixFix Indicator](/developers/pinets/../indicators/willvixfix)
* [Squeeze Momentum Indicator](/developers/pinets/../indicators/sqzmom)

Or contribute to the project on [GitHub](https://github.com/alaa-eddine/PineTS)!


# PineTS (/developers/pinets)



PineTS is a TypeScript implementation of the Pine Script language, allowing you to write trading indicators and strategies using TypeScript while leveraging the power of Pine Script's execution model and functions.

With PineTS, you can create, test and visualize technical indicators using modern web technologies while maintaining compatibility with the Pine Script language features.

<CardGroup cols="2">
  <Card title="Getting Started" icon="rocket" href="/developers/pinets/getting-started">
    Learn the basics of converting Pine Script to PineTS and understand key differences.
  </Card>

  <Card title="Initialization & Usage" icon="play" href="/developers/pinets/initialization-and-usage">
    How to initialize PineTS and run indicators: constructor parameters, run options, and return formats.
  </Card>

  <Card title="Indicator" icon="chart-line" href="/developers/pinets/indicator">
    The `Indicator` class: cached transpile, schema introspection, and runtime input/prop overrides.
  </Card>

  <Card title="Syntax Guide" icon="code" href="/developers/pinets/syntax-guide">
    Writing PineTS code equivalent to Pine Script: declarations, series access, and control structures.
  </Card>

  <Card title="Alerts" icon="bell" href="/developers/pinets/alerts">
    The `alert()` and `alertcondition()` functions and how alert events are collected.
  </Card>

  <Card title="Pagination & Live Streaming" icon="refresh-cw" href="/developers/pinets/pagination">
    Incremental processing of historical data and live bar updates.
  </Card>

  <Card title="Strategy Namespace" icon="target" href="/developers/pinets/strategy">
    `strategy.*` order primitives, trade collections, risk management, and known divergences.
  </Card>

  <Card title="Data Providers" icon="database" href="/developers/pinets/data-providers">
    Built-in market data providers and how to implement your own.
  </Card>

  <Card title="Architecture" icon="layers" href="/developers/pinets/architecture">
    How PineTS works internally: transpiler, runtime, and namespace implementations.
  </Card>

  <Card title="Language Coverage" icon="list-checks" href="/developers/pinets/lang-coverage">
    Pine Script language features implemented in PineTS: execution model, time series, and more.
  </Card>

  <Card title="API Coverage" icon="table" href="/developers/pinets/api-coverage">
    Implementation status of Pine Script API functions and methods in PineTS.
  </Card>

  <Card title="View on GitHub" icon="github" href="https://github.com/LuxAlgo/PineTS">
    Star the repo, report issues, or contribute to PineTS.
  </Card>
</CardGroup>


# Indicator (/developers/pinets/indicator)



`Indicator` is the SSOT wrapper around a Pine/JS script. It owns the source code, the transpiled function, the parsed input schema, and the parsed declaration-prop schema. You typically pass an `Indicator` to `pine.run()` / `pine.stream()` instead of a raw string — that gives you:

* Lazy, cached transpile (re-running the same Indicator across multiple `pine.run()` calls compiles the script **once**)
* A title-keyed `.input` view to read or override input defaults
* A name-keyed `.prop` view to read or override `indicator()` / `strategy()` declaration arguments
* Schema introspection (`getInputsMeta()`, `getPropsMeta()`) for UI builders

Every code snippet on this page is exercised by the verification harness at `PineTS/.scratchpad/indicator-doc-examples.cjs` (kept in lock-step with the docs during authoring). If something doesn't behave as documented, the harness fails.

## Table of Contents [#table-of-contents]

* [Quickstart](#quickstart)
* [Constructor](#constructor)
* [Indicator.from()](#indicatorfrom)
* [Visible-range detection](#visible-range-detection)
* [Inputs — `.input` and `getInputsMeta()`](#inputs--input-and-getinputsmeta)
* [Declaration props — `.prop` and `getPropsMeta()`](#declaration-props--prop-and-getpropsmeta)
* [Reusing the same Indicator across runs](#reusing-the-same-indicator-across-runs)
* [JS-function source](#js-function-source)
* [Backward compatibility — constructor `inputs` map](#backward-compatibility--constructor-inputs-map)

***

## Quickstart [#quickstart]

The simplest path — wrap a Pine string in `new Indicator(...)`, pass it to `pine.run()`:

```javascript
const { PineTS, Indicator } = require('pinets');

const code = `
//@version=6
indicator("SMA Demo")
len = input.int(14, "Length")
plot(ta.sma(close, len))
`;

const ind = new Indicator(code);
const pine = new PineTS(/* market data or provider */);
const ctx = await pine.run(ind);
```

Raw strings and raw functions also work — they get wrapped in a throwaway Indicator internally:

```javascript
await pine.run(code); // raw Pine string
await pine.run(($) => {
    /* ... */
}); // raw JS function
```

You'd choose the explicit `Indicator` form when you want to:

* Override input or prop values before the run.
* Reuse the same compiled script across multiple `pine.run()` calls.
* Inspect the script's schema from the host UI.

***

## Constructor [#constructor]

```typescript
new Indicator(source: string | Function, inputs?: Record<string, unknown>)
```

| Argument | Type                                             | Required | Notes                                                                                 |
| -------- | ------------------------------------------------ | -------- | ------------------------------------------------------------------------------------- |
| `source` | `string` (Pine code) or `Function` (JS callback) | Yes      | The script body.                                                                      |
| `inputs` | `Record<string, unknown>`                        | No       | **Legacy** — title-keyed override map. Kept for back-compat; prefer the `.input` API. |

Both forms accepted:

```javascript
// Pine string source
const a = new Indicator(`
//@version=6
indicator("X")
plot(close)
`);

// JS function source
const b = new Indicator(($) => {
    const { indicator, plot } = $.pine;
    indicator('JS X');
    plot($.data.close);
});
```

***

## Indicator.from() [#indicatorfrom]

A static normalizer used by `pine.run()` and friends. Accepts any of the three input shapes and returns an `Indicator`:

```javascript
Indicator.from(code); // wraps the raw Pine string
Indicator.from(($) => {
    /* ... */
}); // wraps the raw function
Indicator.from(existingInd) === existingInd; // pass-through
```

Use it when you write code that needs to operate on Indicators regardless of how the user provided the source.

***

## Visible-range detection [#visible-range-detection]

Statically tags whether the script references any host-bound built-in (`chart.left_visible_bar_time`, `chart.right_visible_bar_time`, …). Used by `pine.update()` to skip re-runs for non-viewport scripts.

```javascript
const vr = new Indicator(`
//@version=6
indicator("VR", overlay=true)
left = chart.left_visible_bar_time
plot(close)
`);

const noVR = new Indicator(`//@version=6\nindicator("Plain")\nplot(close)`);

vr.usesVisibleRange(); // → true
noVR.usesVisibleRange(); // → false
```

See &#x2A;*[Initialization and Usage → Host Environment (Visible Range)](/developers/pinets/initialization-and-usage#host-environment-visible-range)** for the full visible-range flow.

***

## Inputs — `.input` and `getInputsMeta()` [#inputs--input-and-getinputsmeta]

### Reading and writing values [#reading-and-writing-values]

`.input&#x60; is a Proxy keyed primarily by &#x2A;*`varId`** — the variable each input is assigned to (`length = input.int(…)` → `"length"`). **Keying by varId is the recommended way to read and override inputs*&#x2A;: the variable name is always present and always unique, so it works even when titles are empty or duplicated. The input's &#x2A;*`title`** also resolves as a *secondary alias* for the common case. The container is frozen — you mutate values per key, but you can't replace the whole object.

```javascript
const code = `
//@version=6
indicator("Demo")
length = input.int(14, "Length", minval=2, maxval=200)
src    = input.source(close, "Source")
maType = input.string("EMA", "MA Type", options=["EMA", "SMA", "WMA"])
plot(close)
`;
const ind = new Indicator(code);

// Recommended: key by varId (the variable name).
ind.input['length']; // → 14         (default from source)
ind.input['src'];    // → "close"
ind.input['maType']; // → "EMA"

ind.input['length'] = 50; // ✓ valid override
ind.input['length'];      // → 50

// Title still works as a fallback alias (same underlying slot).
ind.input['Length'];      // → 50
ind.input['MA Type'] = 'SMA';
```

> **varId takes priority.** Resolution tries the varId first, then falls back to title. If a varId and some other input's title happen to collide, the varId wins. `Object.keys(ind.input)` enumerates varIds; titles resolve but aren't listed.

When you pass the Indicator to `pine.run()`, the override flows into the runtime:

```javascript
const code = `
//@version=6
indicator("E2E")
len = input.int(14, "Length")
plot(ta.sma(close, len))
`;
const ind = new Indicator(code);
ind.input['len'] = 5;              // by varId (recommended)
const ctx = await pine.run(ind);
// ctx.inputs.len === 5            (overrides are forwarded under the varId)
// the SMA in the script was computed with length=5
```

### Empty or duplicated titles [#empty-or-duplicated-titles]

Because varId is the primary key, inputs that are awkward to address by title still have a stable handle:

```javascript
const code = `
//@version=6
indicator("Dup")
fast = input.int(10, "Length")
slow = input.int(20, "Length")   // same title as 'fast'
hidden = input.int(5, "")        // empty title
plot(close)
`;
const ind = new Indicator(code);

ind.input['slow']   = 50;  // ✓ targets the second input precisely
ind.input['hidden'] = 7;   // ✓ overridable despite the empty title
ind.input['Length'];       // → 10  (a shared title aliases the FIRST input, 'fast')

ind.getInputsMeta().length; // → 3  (duplicates and empty-titled inputs all appear)
```

### Validation [#validation]

Writes are validated against the input's schema. Failures throw immediately with a tailored message:

```javascript
ind.input['nope'] = 1;      // ✗ Error: [Indicator.input] unknown input key "nope". Known: length, src, maType, Length, Source, MA Type
ind.input['length'] = 1;    // ✗ Error: [Indicator.input] "length" value 1 is below minval 2
ind.input['maType'] = 'HMA';// ✗ Error: [Indicator.input] "maType" value "HMA" is not one of: "EMA", "SMA", "WMA"
ind.input = { foo: 1 };     // ✗ Error: [Indicator.input] .input cannot be replaced — mutate individual keys (…)
```

### Schema introspection — `getInputsMeta()` [#schema-introspection--getinputsmeta]

Returns the parsed schema as an array of `IPineInput`. Useful for UI builders that need to render input controls.

```javascript
const meta = ind.getInputsMeta();
// [
//   { type: 'int',    varId: 'length', title: 'Length',  defval: 14, minval: 2, maxval: 200 },
//   { type: 'source', varId: 'src',    title: 'Source',  defval: 'close' },
//   { type: 'string', varId: 'maType', title: 'MA Type', defval: 'EMA', options: ['EMA','SMA','WMA'] },
//   { type: 'color',  varId: 'lineCol',title: 'Line',    defval: '#F23645FF' },
// ]
```

Each entry carries everything the scanner harvested — `type`, `defval&#x60;, &#x2A;*`varId`**, `title`, `tooltip`, `group`, `inline`, `display`, `options`, `minval`, `maxval`, `step`, `active`, `confirm`. `varId` (the assigned variable name) is present for every scanned input and is the primary `.input` override key; `title` may be empty or repeated across inputs, but `varId` is unique. The exported types `IPineInput`, `PineInputType`, and `PineInputDisplay` describe the shape.

**Color defaults are normalized.** `color`-typed inputs report `defval` as a canonical &#x2A;*8-digit RGBA hex string `#RRGGBBAA`** (uppercase; `FF` = fully opaque), regardless of how the source wrote it:

* `#RRGGBB` / `#RRGGBBAA` hex literals
* a named constant — `color.red` → `#F23645FF`
* the constructor calls `color.new(col, transp)` and `color.rgb(r, g, b, transp?)`, statically evaluated (Pine's `transp` is 0–100 transparency, so `color.new(#26a69a, 50)` → `#26A69A80`, `color.rgb(6, 162, 47)` → `#06A22FFF`)

Reading the same key back via `.input['Line']` returns the identical normalized string. This gives UI color pickers a single, parseable format to bind to. (The normalization is presentational — it doesn't affect what the running script computes when the input isn't overridden.) A color default that can't be resolved statically — e.g. one built from a runtime variable or `color.from_gradient(...)` — is reported as `undefined`.

**Constant arguments are resolved.** When an input argument references a top-level constant or simple variable rather than a literal, the scanner resolves it to the declared value — so `defval`, `group`, `inline`, `tooltip`, `options`, etc. report the value, not the variable name. This composes with the enum, color, and chained-const logic.

```javascript
const string GRP = "Indicator Settings"
const int    DEF = 14
len = input.int(DEF, "Length", group = GRP)
// → { type: 'int', varId: 'len', title: 'Length', defval: 14, group: 'Indicator Settings' }
```

References that can't be resolved to a static value (e.g. a computed series like `ta.sma(close, 5)`) fall back to the bare variable name.

***

## Declaration props — `.prop` and `getPropsMeta()` [#declaration-props--prop-and-getpropsmeta]

The script's `indicator(...)` or `strategy(...)` declaration takes its own set of arguments — `overlay`, `precision`, `initial_capital`, `pyramiding`, `currency`, etc. The `.prop` view exposes those for read/override, mirroring the `.input` API but keyed by **arg name** (each arg name is unique, so there's no varId/title distinction here).

### Reading source-code defaults [#reading-source-code-defaults]

When you construct an Indicator, the source's declaration call is parsed and its arg values seed `.prop`. Spec defaults from the Pine docs apply to args the source didn't set.

```javascript
const code = `
//@version=6
strategy("Demo Strat", overlay=true, initial_capital=50000, pyramiding=3,
         currency=currency.EUR, default_qty_type=strategy.percent_of_equity,
         commission_type=strategy.commission.cash_per_order)
plot(close)
`;
const ind = new Indicator(code);

ind.prop['overlay']; // → true         (from source)
ind.prop['initial_capital']; // → 50000        (from source)
ind.prop['pyramiding']; // → 3            (from source)
ind.prop['currency']; // → "EUR"        (enum resolved to runtime string)
ind.prop['default_qty_type']; // → "percent_of_equity"
ind.prop['commission_type']; // → "cash_per_order"  (nested namespace resolved)
ind.prop['slippage']; // → 0            (spec default — source didn't set it)
```

Namespaced enum constants (`currency.EUR`, `strategy.percent_of_equity`, `strategy.commission.cash_per_order`) are resolved to the bare runtime string — matching what TradingView's `str.tostring()` would print.

### Writing overrides [#writing-overrides]

```javascript
ind.prop['initial_capital'] = 75000;
ind.prop['pyramiding'] = 4;

const ctx = await pine.run(ind);
// ctx.strategy.config.initial_capital === 75000   (override wins)
// ctx.strategy.config.pyramiding      === 4
// ctx.strategy.config.currency        === "EUR"   (source-only value still applies)
```

Indicator scripts work the same way against `ctx.indicator`:

```javascript
const code = `//@version=6\nindicator("E2E", overlay=false, precision=2)\nplot(close)`;
const ind = new Indicator(code);
ind.prop['overlay'] = true;
ind.prop['precision'] = 6;
const ctx = await pine.run(ind);
// ctx.indicator.overlay   === true
// ctx.indicator.precision === 6
```

### Validation [#validation-1]

Same strictness as `.input`:

```javascript
ind.prop['nope'] = 1; // ✗ Error: [Indicator.prop] unknown key "nope". Known: …
ind.prop['currency'] = 'XYZ'; // ✗ Error: [Indicator.prop] "currency" value "XYZ" is not one of: …
ind.prop['title'] = 'X'; // ✗ Error: [Indicator.prop] unknown key "title". Known: …   (title is mutable: false)
ind.prop = { foo: 1 }; // ✗ Error: [Indicator] .prop cannot be replaced — (…)
```

### Schema introspection — `getPropsMeta()` [#schema-introspection--getpropsmeta]

Returns an `IPineProp[]` filtered to props applicable to the detected script type (`indicator` vs `strategy`). Includes `title` and `shorttitle` with `mutable: false` so UI builders can render them even though they're not writable.

```javascript
const ind = new Indicator(`//@version=6\nstrategy("X")\nplot(close)`);
const meta = ind.getPropsMeta();
// Each entry: { name, type, defval, options?, minval?, maxval?, mutable, appliesTo, version? }

const currency = meta.find((m) => m.name === 'currency');
// {
//   name: "currency",
//   type: "enum",
//   defval: "USD",
//   options: ["AED", "ARS", "AUD", ..., "USDT", ..., "ZAR"],
//   mutable: true,
//   appliesTo: "strategy",
// }
```

`getDeclarationType()` reports which schema the Indicator is using:

```javascript
new Indicator(`//@version=6\nstrategy("X")\nplot(close)`).getDeclarationType(); // → 'strategy'
new Indicator(`//@version=6\nindicator("X")\nplot(close)`).getDeclarationType(); // → 'indicator'
new Indicator(`//@version=6\nplot(close)`).getDeclarationType(); // → null  (falls back to indicator schema)
```

### Exported schemas for UI builders [#exported-schemas-for-ui-builders]

The complete prop schemas are also exported as top-level constants. UI code that wants the full list (independent of any Indicator instance) can import them directly:

```javascript
const { INDICATOR_PROPS, STRATEGY_PROPS, propsForDeclaration } = require('pinets');

INDICATOR_PROPS; // IPineProp[]   — 17 entries (incl. title/shorttitle)
STRATEGY_PROPS; // IPineProp[]   — 33 entries
propsForDeclaration('strategy'); // → STRATEGY_PROPS
propsForDeclaration(null); // → INDICATOR_PROPS (fallback)
```

Every enum-typed entry in [`propsSchema.ts`](https://github.com/LuxAlgo/PineTS/blob/main/src/Indicator/propsSchema.ts) has a one-line comment enumerating accepted values and pointing to the corresponding exported Pine enum (`enum format`, `enum scale`, `enum currency`, …) in [`src/namespaces/Types.ts`](https://github.com/LuxAlgo/PineTS/blob/main/src/namespaces/Types.ts) so you can import the source-of-truth values rather than hard-coding strings in UI code.

***

## Reusing the same Indicator across runs [#reusing-the-same-indicator-across-runs]

The transpiled function is cached on the Indicator instance, so passing the same Indicator to multiple `pine.run()` calls compiles the script **once**. Live overrides on `.input` / `.prop` between runs are picked up automatically.

```javascript
const code = `
//@version=6
indicator("Reusable")
len = input.int(14, "Length")
plot(ta.sma(close, len))
`;
const ind = new Indicator(code);
ind.input['Length'] = 7;

const ctxA = await new PineTS(dataA).run(ind); // first run: transpiles + scans
const ctxB = await new PineTS(dataB).run(ind); // second run: reuses prepared artifact
// Both runs computed ta.sma(close, 7)
```

This matters for hosts that run the same script across many datasets (e.g. a chart with multiple symbols, or a backtest sweep over parameter ranges).

***

## JS-function source [#js-function-source]

When the source is a JS callback, the AST scan for inputs returns `[]` (Pine inputs are written in Pine syntax; the JS form expresses them differently). The `.input` view is empty and writes throw "unknown input key". The legacy `inputs` constructor map still works for runtime overrides via the JS path.

```javascript
const fn = ($) => {
    const { indicator, plot } = $.pine;
    indicator('JS', { overlay: true, precision: 4 });
    plot($.data.close);
};
const ind = new Indicator(fn);

ind.getInputsMeta(); // → []
ind.getDeclarationType(); // → 'indicator'    (acorn parse of the function body)
ind.prop['overlay']; // → true           (from the JS call)
ind.prop['precision']; // → 4              (from the JS call)
```

Declaration detection works for the JS path because the function body is parsed with `acorn`; the scanner walks for `CallExpression(indicator | strategy, ...)` regardless of whether it's destructured from `$.pine` first.

***

## Backward compatibility — constructor `inputs` map [#backward-compatibility--constructor-inputs-map]

The legacy second constructor argument (a **title-keyed** override map) is still honored. It's most useful when you build an Indicator and want to ship a default override set up front, without touching `.input` later.

```javascript
const code = `
//@version=6
indicator("BC")
len = input.int(14, "Length")
plot(ta.sma(close, len))
`;
const ind = new Indicator(code, { Length: 21 });   // keyed by title
const ctx = await pine.run(ind);
// ctx.inputs.Length === 21    (the runtime resolves it via the title fallback)
```

Precedence when both are set: a `.input` write (varId-keyed) wins over the constructor map (title-keyed), because the runtime resolves **varId before title**. Both entries coexist in `ctx.inputs` — they're distinct keys — and the varId one is chosen:

```javascript
const ind = new Indicator(code, { Length: 21 });
ind.input['len'] = 3;            // varId override
const ctx = await pine.run(ind);
// ctx.inputs === { Length: 21, len: 3 }
// resolveInput checks varId 'len' first → the script computes ta.sma(close, 3)
```

New code should prefer the **varId**-keyed `.input` API. The constructor `inputs` argument (and title-keyed `.input` access) stay for callers that have existing code relying on them.

***

## Related [#related]

* **[Initialization and Usage](/developers/pinets/initialization-and-usage)** — full surface of `pine.run()`, `pine.stream()`, `pine.update()`.
* **[Strategy Namespace](/developers/pinets/strategy)** — `strategy.*` API surface (the Pine side of strategy props).
* **[API Coverage](/developers/pinets/api-coverage)** — implementation status of every Pine `input.*` and namespace.


# Initialization and Usage (/developers/pinets/initialization-and-usage)



This guide explains how to initialize PineTS and run indicators or strategies with detailed documentation of all available options and return values.

## Table of Contents [#table-of-contents]

* [Installation](#installation)
* [PineTS Constructor](#pinets-constructor)
* [Initialization Options](#initialization-options)
* [The run() Method](#the-run-method)
* [The stream() Method](#the-stream-method)
* [The update() Method](#the-update-method)
* [Host Environment (Visible Range)](#host-environment-visible-range)
* [Non-Standard Chart Types (Heikin Ashi)](#non-standard-chart-types-heikin-ashi)
* [Context Object](#context-object)
* [Return Values](#return-values)
* [Alerts](#alerts)
* [Complete Examples](#complete-examples)

***

## Installation [#installation]

```bash
npm install pinets
```

***

## PineTS Constructor [#pinets-constructor]

The `PineTS` class is the main entry point for working with indicators and strategies.

### Syntax [#syntax]

```typescript
const pineTS = new PineTS(
    source: IProvider | any[],
    tickerId?: string,
    timeframe?: string,
    limit?: number,
    sDate?: number,
    eDate?: number
);
```

### Parameters [#parameters]

| Parameter   | Type                 | Required | Description                                                                          |
| ----------- | -------------------- | -------- | ------------------------------------------------------------------------------------ |
| `source`    | `IProvider \| any[]` | Yes      | Either a data provider instance (e.g., `Provider.Binance`) or an array of OHLCV data |
| `tickerId`  | `string`             | No\*     | The trading pair symbol (e.g., `'BTCUSDT'`). Required when using a provider          |
| `timeframe` | `string`             | No\*     | The timeframe/interval for the data. Required when using a provider                  |
| `limit`     | `number`             | No       | Maximum number of candles to fetch (default: provider-specific, max 5000)            |
| `sDate`     | `number`             | No       | Start date in milliseconds timestamp. Used for date range queries                    |
| `eDate`     | `number`             | No       | End date in milliseconds timestamp. Used for date range queries                      |

\* Required when using a provider, optional when passing an array of data

### Understanding Candle Fetching and Ordering [#understanding-candle-fetching-and-ordering]

#### How `limit` Works [#how-limit-works]

When you specify a `limit` without date ranges, PineTS fetches the **most recent candles working backwards** from the current time:

```typescript
// Fetches the last 100 daily candles (most recent)
const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);
// Result: 100 candles from ~100 days ago until now
```

**Important notes:**

* Data is fetched from **newest to oldest** from the exchange
* Maximum limit is **5000 candles** (hard cap, might be changed in the future as we optimize the runtime performance)
* If no limit is specified, the provider's default is used (varies by provider)

#### How Date Ranges Work [#how-date-ranges-work]

When you specify `sDate` and `eDate`, PineTS fetches all candles within that date range:

```typescript
const startDate = new Date('2024-01-01').getTime(); // Start: Jan 1, 2024
const endDate = new Date('2024-12-31').getTime(); // End: Dec 31, 2024

const pineTS = new PineTS(
    Provider.Binance,
    'BTCUSDT',
    'D',
    undefined, // No limit - use date range instead
    startDate,
    endDate
);
// Result: All daily candles from Jan 1 to Dec 31, 2024
```

**Date range behavior:**

* Fetches **all candles** between `sDate` and `eDate`
* If the date range spans more than 1000 candles, PineTS automatically handles pagination
* Still subject to the 5000 candle maximum
* Data is ordered chronologically (oldest to newest)

#### Priority and Combinations [#priority-and-combinations]

| Scenario                           | Behavior                                           |
| ---------------------------------- | -------------------------------------------------- |
| Only `limit` specified             | Fetches the last `limit` candles from now          |
| Only `sDate` and `eDate` specified | Fetches all candles in the date range (up to 5000) |
| Both `limit` and date range        | Date range is used, `limit` is ignored             |
| Neither specified                  | Uses provider default (typically 500-1000 candles) |

#### Data Ordering After Fetching [#data-ordering-after-fetching]

Regardless of how data is fetched, PineTS ensures the data is in **chronological order**:

```typescript
// After initialization, data is ordered: [oldest ... newest]
const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

await pineTS.run((context) => {
    const { close } = context.data;

    // close[0] = current bar (most recent)
    // close[1] = previous bar
    // close[2] = 2 bars ago
    // ... and so on

    console.log('Current close:', close[0]);
    console.log('Previous close:', close[1]);
});
```

**Time series indexing:**

* `[0]` = current/most recent bar
* `[1]` = previous bar
* `[2]` = 2 bars ago
* This matches Pine Script's time series behavior

#### Examples of Different Fetching Scenarios [#examples-of-different-fetching-scenarios]

```typescript
// Example 1: Last 100 candles (from now backwards)
const recent = new PineTS(Provider.Binance, 'BTCUSDT', '1h', 100);
// Gets: ~100 hours of data up to current time

// Example 2: Specific date range (all candles in range)
const historical = new PineTS(Provider.Binance, 'ETHUSDT', 'D', undefined, new Date('2023-01-01').getTime(), new Date('2023-12-31').getTime());
// Gets: All daily candles in 2023 (365 candles)

// Example 3: Large limit (will be capped at 5000)
const maxData = new PineTS(Provider.Binance, 'BTCUSDT', '1h', 10000);
// Gets: Only 5000 most recent hourly candles (max cap)

// Example 4: No limit (provider default)
const defaultData = new PineTS(Provider.Binance, 'BTCUSDT', 'D');
// Gets: Provider default amount (typically 500-1000 candles)
```

***

## Initialization Options [#initialization-options]

### Option 1: Using a Data Provider [#option-1-using-a-data-provider]

The easiest way to initialize PineTS is using a built-in data provider:

```typescript
import { PineTS, Provider } from 'pinets';

// Basic initialization with limit
const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

// With date range
const startDate = new Date('2024-01-01').getTime();
const endDate = new Date('2024-12-31').getTime();
const pineTSWithDateRange = new PineTS(
    Provider.Binance,
    'ETHUSDT',
    '1h',
    undefined, // no limit
    startDate,
    endDate
);
```

#### Available Providers [#available-providers]

| Provider           | Usage                 | API Key                 | Best For       |
| ------------------ | --------------------- | ----------------------- | -------------- |
| `Provider.Binance` | Crypto market data    | Not required            | Cryptocurrency |
| `Provider.FMP`     | Stocks, forex, crypto | Required                | US/intl stocks |
| `Provider.Alpaca`  | US stocks, crypto     | Required (key + secret) | US equities    |

Providers that require API keys must be configured before use. See the &#x2A;*[Data Providers](/developers/pinets/../data-providers)** page for detailed setup instructions, configuration options, and examples for each provider.

#### Supported Timeframes [#supported-timeframes]

All providers support the standard timeframe strings: `'1'`, `'3'`, `'5'`, `'15'`, `'30'`, `'60'`, `'120'`, `'240'`, `'D'`, `'W'`, `'M'&#x60;. Timeframes not natively supported by a provider are automatically aggregated from smaller candles. See the &#x2A;*[Timeframe Reference](/developers/pinets/../data-providers#timeframe-reference)** for the full support matrix.

### Option 2: Using Custom Data [#option-2-using-custom-data]

You can also provide your own OHLCV data as an array:

```typescript
import { PineTS } from 'pinets';

const customData = [
    {
        openTime: 1640995200000,
        open: 46000,
        high: 47000,
        low: 45500,
        close: 46500,
        volume: 1234.56,
        closeTime: 1641081599999,
    },
    // ... more candles
];

const pineTS = new PineTS(customData);
```

#### Custom Data Format [#custom-data-format]

Each data point in the array must include:

| Field       | Type     | Required | Description                           |
| ----------- | -------- | -------- | ------------------------------------- |
| `open`      | `number` | Yes      | Opening price                         |
| `high`      | `number` | Yes      | Highest price                         |
| `low`       | `number` | Yes      | Lowest price                          |
| `close`     | `number` | Yes      | Closing price                         |
| `volume`    | `number` | Yes      | Trading volume                        |
| `openTime`  | `number` | No       | Opening time (milliseconds timestamp) |
| `closeTime` | `number` | No       | Closing time (milliseconds timestamp) |

***

## The run() Method [#the-run-method]

The `run()` method executes your indicator or strategy code across all candles in the dataset.

### Syntax [#syntax-1]

```typescript
const context = await pineTS.run(
    pineTSCode: Indicator | Function | String,
    n?: number
): Promise<Context>
```

### Parameters [#parameters-1]

| Parameter    | Type                              | Default     | Description                                                                              |
| ------------ | --------------------------------- | ----------- | ---------------------------------------------------------------------------------------- |
| `pineTSCode` | `Indicator \| Function \| String` | Required    | The indicator/strategy function to execute. Use `Indicator` class to pass runtime inputs |
| `n`          | `number`                          | All periods | Number of most recent periods to process. If not specified, processes all available data |

### Running with Runtime Inputs [#running-with-runtime-inputs]

To pass custom input values to your indicator at runtime, wrap the source in an `Indicator` instance. Two equivalent forms — pick whichever is more ergonomic:

```typescript
import { PineTS, Provider, Indicator } from 'pinets';

// Your indicator code (Native Pine Script or PineTS syntax)
const code = `
//@version=5
indicator("My Indicator")
len = input.int(14, "Length")
src = input.source(close, "Source")
plot(ta.sma(src, len))
`;

// Initialize PineTS
const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

// (a) Per-key override via .input — preferred, validates against the input's schema
const ind = new Indicator(code);
ind.input['Length'] = 50;
const { result } = await pineTS.run(ind);

// (b) Constructor overrides map — legacy, still supported
const ind2 = new Indicator(code, { Length: 50 });
```

In both forms, the keys must match the &#x2A;*`title`** argument of the corresponding `input.*` call in the source.

The `Indicator` class also lets you override `indicator()` / `strategy()` declaration arguments (e.g. `initial_capital`, `pyramiding`, `overlay`) via `ind.prop["name"] = value`, and exposes the script's schema for UI builders via `getInputsMeta()` / `getPropsMeta()&#x60;. See &#x2A;*[Indicator](/developers/pinets/indicator)** for the complete API surface.

### Return Value [#return-value]

Returns a `Promise<Context>` object containing:

* `result`: The computed indicator values
* `data`: Market data arrays (open, high, low, close, volume, etc.)
* `plots`: Any plot data generated
* Additional context properties

***

## The stream() Method [#the-stream-method]

The `stream()` method provides an event-based interface for handling live data streams, making it easy to integrate with real-time applications.

### Syntax [#syntax-2]

```typescript
const evt = pineTS.stream(
    pineTSCode: Indicator | Function | String,
    options?: {
        pageSize?: number,
        live?: boolean,
        interval?: number
    }
);
```

### Options [#options]

| Option     | Type      | Default           | Description                                                                                    |
| ---------- | --------- | ----------------- | ---------------------------------------------------------------------------------------------- |
| `pageSize` | `number`  | `undefined` (all) | Number of bars per chunk. If not specified, processes all available historical data in one go. |
| `live`     | `boolean` | `true`            | Whether to continue fetching live data after processing historical data.                       |
| `interval` | `number`  | `1000`            | Polling interval in milliseconds for live data.                                                |

### Usage [#usage]

The method returns an object with `on()` and `stop()` methods:

```typescript
// Start streaming
const evt = pineTS.stream(indicator, { pageSize: 1, live: true, interval: 2000 });

// Handle data updates
evt.on('data', (context) => {
    // Process new data
    console.log('New data:', context.result);
});

// Handle alert events (from alert() and alertcondition() calls)
evt.on('alert', (alert) => {
    console.log('Alert:', alert.message);
});

// Handle runtime warnings (non-blocking, e.g. array OOB)
evt.on('warning', (warning) => {
    console.warn('Warning:', warning.message);
});

// Handle errors
evt.on('error', (error) => {
    console.error('Stream error:', error);
});

// Stop streaming
// evt.stop();
```

**Available events:**

| Event       | Payload                                             | Description                   |
| ----------- | --------------------------------------------------- | ----------------------------- |
| `'data'`    | `Context`                                           | New bar data processed        |
| `'alert'`   | `{ type, message, title?, freq?, bar_index, time }` | Alert or alertcondition fired |
| `'warning'` | `{ message, method?, bar }`                         | Non-blocking runtime warning  |
| `'error'`   | `Error`                                             | Fatal error (script halted)   |

***

## The update() Method [#the-update-method]

`update()` is a smart wrapper around `run()` that skips execution when the
output cannot have changed. Use it instead of `run()` in event-driven flows
(viewport changes, settings tweaks, etc.) so non-affected indicators are
free to call.

### Syntax [#syntax-3]

```typescript
const context = await pineTS.update(
    pineTSCode?: Indicator | Function | String,
): Promise<Context>
```

### Behavior [#behavior]

| Call                                                                                  | Action                                                           |
| ------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| First call (no cached result)                                                         | Executes — equivalent to `run()`. `pineTSCode` is required here. |
| Subsequent call, script **does not** use visible-range built-ins                      | Returns the cached `Context` immediately (no work).              |
| Subsequent call, script uses visible-range AND viewport changed since last cached run | Re-executes against the new viewport, returns fresh `Context`.   |
| Subsequent call, viewport unchanged                                                   | Returns the cached `Context`.                                    |

The `pineTSCode` argument is optional after the first call — the previously
seen code is reused. Pass it again only when the script source itself
changes.

### Example [#example]

```typescript
const pine = new PineTS(Provider.Binance, 'BTCUSDT', '1W', 500);

// First call: behaves like run()
await pine.update(code);

// User pans the chart → host computes new visible range
pine.setVisibleRange(t1, t2);
await pine.update();   // re-runs ONLY if the script uses visible-range

// User pans again, but to the same range
await pine.update();   // returns cached result, no compute
```

***

## Host Environment (Visible Range) [#host-environment-visible-range]

PineTS is renderer-agnostic — it has no UI. But Pine Script has a small set
of built-ins whose values come from the chart's UI state (the user's current
zoom/pan), notably:

| Pine built-in                  | PineTS behavior                                                                 |
| ------------------------------ | ------------------------------------------------------------------------------- |
| `chart.left_visible_bar_time`  | Defaults to `marketData[0].openTime`; host can override via `setVisibleRange()` |
| `chart.right_visible_bar_time` | Defaults to `marketData[marketData.length - 1].openTime`; host can override     |

For most scripts these are unused, and the defaults are "the full loaded
range is the viewport" — perfectly defensible since PineTS does compute over
everything it loaded. For scripts that *do* reference them (e.g. LuxAlgo's
Supply-and-Demand Visible Range), a host like QFChart can wire its actual
viewport in.

### `setVisibleRange(left: number, right: number)` [#setvisiblerangeleft-number-right-number]

Stores host viewport values. The setter only updates internal state; it
does not trigger a re-run by itself. Call `update()` afterwards to apply.

```typescript
pine.setVisibleRange(
    new Date('2024-01-01').getTime(),
    new Date('2024-06-30').getTime(),
);
await pine.update(code);
```

### `usesVisibleRange(): boolean` [#usesvisiblerange-boolean]

Static-analysis flag set during transpile. Returns `true` if the loaded
script references any visible-range built-in.

Use this to short-circuit fan-out logic across many indicators on one
chart — only viewport-dependent indicators need re-runs on user zoom:

```typescript
function onChartPan(left, right) {
    for (const p of indicators) {
        if (!p.usesVisibleRange()) continue;   // skip — output unaffected
        p.setVisibleRange(left, right);
        chart.clear();                          // QFChart helper
        const ctx = await p.update();
        chart.addIndicator(p.id, ctx.plots);
    }
}
```

Detection is performed by scanning the transpiled function body (comments
are stripped during pine2js, so accidental references inside comments do
not flip the flag).

### `visibleRangeLeft` / `visibleRangeRight` getters [#visiblerangeleft--visiblerangeright-getters]

Read back the current values stored by `setVisibleRange()`. Return
`undefined` when the setter has never been called (the default-falls-back
case).

### Streaming integration [#streaming-integration]

`stream()` already handles continuous data input. Combining `stream()`
with `setVisibleRange()` is a planned follow-up — for the batch path,
use `run()` / `update()`.

***

## Non-Standard Chart Types (Heikin Ashi) [#non-standard-chart-types-heikin-ashi]

**The chart type is the ticker.** To run a script "on a Heikin Ashi chart", construct PineTS with an **extended ticker** — the plain symbol plus a chart-type modifier suffix:

```javascript
const pineTS = new PineTS(myHaAwareSource, 'BTCUSDT;heikinashi', 'D', 500);
```

Everything derives from that one setting:

* `chart.is_heikinashi` is `true` (and `chart.is_standard` is `false`);
* `syminfo.tickerid` carries the modifier (`"BINANCE:BTCUSDT;heikinashi"`), while `syminfo.ticker` stays clean;
* `request.security(syminfo.tickerid, tf, expr)` requests **chart-typed** data from the data source, and `request.security(ticker.standard(syminfo.tickerid), tf, expr)` explicitly requests **standard** data — including at the chart's own timeframe.

**PineTS never transforms bars.** The extended ticker is a *routing marker*, not a conversion request:

* A **host data source that owns the transform** (e.g. a charting library embedding PineTS and serving Heikin Ashi views) receives the extended ticker verbatim from `getMarketData()` / `getSymbolInfo()` and must serve the derived bars for it — and raw bars for the plain symbol.
* PineTS' **bundled providers** (Binance, Alpaca, FMP, Mock) strip the modifier at their boundary and always serve standard candles — so on a bundled provider the extended ticker is a documented no-op (the chart *reports* Heikin Ashi but runs on standard data).

Consequently, a consistent standalone Heikin Ashi run requires a data source that distinguishes `"SYM;heikinashi"` from `"SYM"`. Renko / Kagi / Line Break / Point & Figure have no such source and remain unsupported (`chart.is_renko` etc. are always `false`; `ticker.renko()` etc. return the plain symbol).

***

## Context Object [#context-object]

The context object is passed to your indicator function and contains all the data and utilities needed for calculations.

### Available Properties [#available-properties]

```typescript
interface Context {
    // Market data (time-series arrays)
    data: {
        open: number[]; // Opening prices
        high: number[]; // Highest prices
        low: number[]; // Lowest prices
        close: number[]; // Closing prices
        volume: number[]; // Volume data
        hl2: number[]; // (high + low) / 2
        hlc3: number[]; // (high + low + close) / 3
        ohlc4: number[]; // (open + high + low + close) / 4
        openTime: number[]; // Opening timestamps
        closeTime: number[]; // Closing timestamps
    };

    // Pine Script namespaces
    ta: TechnicalAnalysis; // Technical analysis functions
    math: PineMath; // Mathematical operations
    input: Input; // Input parameters
    request: PineRequest; // Data requests
    array: PineArray; // Array operations
    core: {
        plot: Function; // Plot data
        plotchar: Function; // Plot characters
        na: Function; // Not-a-number handling
        nz: Function; // Replace NaN with zero
        color: any; // Color utilities
    };

    // Execution state
    idx: number; // Current bar index
    NA: any; // Not-a-number constant (NaN)

    // Variable scopes (for Pine Script compatibility)
    params: any; // Parameter variables
    const: any; // Constant variables
    var: any; // Var-scoped variables
    let: any; // Let-scoped variables

    // Results
    result: any; // Computed results
    plots: any; // Plot data
    alerts: any[]; // Alert events from alert() and alertcondition()
    warnings: any[]; // Runtime warnings (e.g. array OOB)

    // Market context
    marketData: any[]; // Raw market data
    source: IProvider | any[]; // Data source
    tickerId: string; // Trading pair
    timeframe: string; // Timeframe
    limit: number; // Data limit
    sDate: number; // Start date
    eDate: number; // End date
}
```

### Quick Access to Common Data [#quick-access-to-common-data]

```typescript
const { result } = await pineTS.run((context) => {
    // Destructure commonly used items
    const { ta, math, core } = context;
    const { close, open, high, low, volume } = context.data;

    // Your indicator logic here
    const ema9 = ta.ema(close, 9);
    const ema21 = ta.ema(close, 21);

    return { ema9, ema21 };
});
```

***

## Return Values [#return-values]

The `run()` method returns different formats depending on what your indicator returns:

### Single Value Return [#single-value-return]

If your indicator returns a single value, `context.result` will be an array:

```typescript
const { result } = await pineTS.run((context) => {
    const { ta } = context;
    const { close } = context.data;

    const sma = ta.sma(close, 20);
    return sma; // Single value
});

// result is an array of numbers
console.log(result); // [45123.5, 45234.2, 45345.8, ...]
```

### Object Return (Multiple Values) [#object-return-multiple-values]

If your indicator returns an object, `context.result` will be an object with arrays:

```typescript
const { result } = await pineTS.run((context) => {
    const { ta } = context;
    const { close } = context.data;

    const ema9 = ta.ema(close, 9);
    const ema21 = ta.ema(close, 21);
    const rsi = ta.rsi(close, 14);

    return { ema9, ema21, rsi }; // Object with multiple values
});

// result is an object with arrays
console.log(result.ema9); // [45123.5, 45234.2, ...]
console.log(result.ema21); // [44987.3, 45098.7, ...]
console.log(result.rsi); // [65.4, 67.2, ...]
```

### Accessing the Full Context [#accessing-the-full-context]

You can access the entire context object for more information:

```typescript
const context = await pineTS.run((context) => {
    const { ta } = context;
    const { close } = context.data;

    const ema = ta.ema(close, 9);
    return { ema };
});

console.log(context.result); // The indicator results
console.log(context.data); // Market data
console.log(context.tickerId); // 'BTCUSDT'
console.log(context.timeframe); // 'D'
console.log(context.marketData); // Raw OHLCV data
```

***

## Complete Examples [#complete-examples]

### Example 1: Simple Moving Average [#example-1-simple-moving-average]

```typescript
import { PineTS, Provider } from 'pinets';

async function runSMA() {
    // Initialize with 200 daily candles
    const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 200);

    // Calculate 20-period SMA
    const { result } = await pineTS.run((context) => {
        const { ta } = context;
        const { close } = context.data;

        const sma20 = ta.sma(close, 20);
        return sma20;
    });

    console.log('SMA(20):', result);
}

runSMA();
```

### Example 2: Multiple Indicators [#example-2-multiple-indicators]

```typescript
import { PineTS, Provider } from 'pinets';

async function runMultipleIndicators() {
    const pineTS = new PineTS(Provider.Binance, 'ETHUSDT', '4H', 500);

    const { result } = await pineTS.run((context) => {
        const { ta, math } = context;
        const { close, high, low } = context.data;

        // Calculate multiple indicators
        const rsi = ta.rsi(close, 14);
        const [macd, signal, histogram] = ta.macd(close, 12, 26, 9);
        const [upperBand, middleBand, lowerBand] = ta.bb(close, 20, 2);
        const atr = ta.atr(high, low, close, 14);

        // Return all results
        return {
            rsi,
            macd,
            signal,
            histogram,
            upperBB: upperBand,
            middleBB: middleBand,
            lowerBB: lowerBand,
            atr,
        };
    });

    console.log('RSI:', result.rsi);
    console.log('MACD:', result.macd);
    console.log('ATR:', result.atr);
}

runMultipleIndicators();
```

### Example 3: With Date Range [#example-3-with-date-range]

```typescript
import { PineTS, Provider } from 'pinets';

async function runWithDateRange() {
    const startDate = new Date('2024-01-01').getTime();
    const endDate = new Date('2024-06-30').getTime();

    const pineTS = new PineTS(
        Provider.Binance,
        'BTCUSDT',
        'D',
        undefined, // No limit, use date range
        startDate,
        endDate
    );

    const { result } = await pineTS.run((context) => {
        const { ta } = context;
        const { close } = context.data;

        const ema50 = ta.ema(close, 50);
        const ema200 = ta.ema(close, 200);

        return {
            ema50,
            ema200,
            bullish: ema50 > ema200,
        };
    });

    console.log('EMA50:', result.ema50);
    console.log('EMA200:', result.ema200);
    console.log('Bullish signals:', result.bullish);
}

runWithDateRange();
```

### Example 4: Custom Data [#example-4-custom-data]

```typescript
import { PineTS } from 'pinets';

async function runWithCustomData() {
    const customData = [
        { open: 100, high: 105, low: 99, close: 103, volume: 1000, openTime: Date.now() - 86400000 * 99, closeTime: Date.now() - 86400000 * 98 },
        { open: 103, high: 108, low: 102, close: 107, volume: 1200, openTime: Date.now() - 86400000 * 98, closeTime: Date.now() - 86400000 * 97 },
        // ... more data
    ];

    const pineTS = new PineTS(customData);

    const { result } = await pineTS.run((context) => {
        const { ta } = context;
        const { close } = context.data;

        const sma10 = ta.sma(close, 10);
        return { sma10 };
    });

    console.log('SMA(10):', result.sma10);
}

runWithCustomData();
```

### Example 5: Processing Last N Periods Only [#example-5-processing-last-n-periods-only]

```typescript
import { PineTS, Provider } from 'pinets';

async function runLastNPeriods() {
    // Fetch 1000 candles
    const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 1000);

    // But only process the last 100
    const { result } = await pineTS.run((context) => {
        const { ta } = context;
        const { close } = context.data;

        const rsi = ta.rsi(close, 14);
        return { rsi };
    }, 100); // Only process last 100 periods

    console.log('RSI (last 100 periods):', result.rsi);
}

runLastNPeriods();
```

### Example 6: Using TA Cache for Performance [#example-6-using-ta-cache-for-performance]

```typescript
import { PineTS, Provider } from 'pinets';

async function runWithCache() {
    const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', '1h', 5000);

    // Enable TA cache for better performance on large datasets
    const { result } = await pineTS.run(
        (context) => {
            const { ta } = context;
            const { close } = context.data;

            const ema20 = ta.ema(close, 20);
            const ema50 = ta.ema(close, 50);

            return { ema20, ema50 };
        },
        undefined,
        true
    ); // Enable cache

    console.log('Results computed with caching enabled');
}

runWithCache();
```

### Example 7: Complex Strategy [#example-7-complex-strategy]

```typescript
import { PineTS, Provider } from 'pinets';

async function runComplexStrategy() {
    const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 365);

    const context = await pineTS.run((ctx) => {
        const { ta, math } = ctx;
        const { close, high, low, volume } = ctx.data;

        // Multiple indicator calculation
        const rsi = ta.rsi(close, 14);
        const [macd, signal, _] = ta.macd(close, 12, 26, 9);
        const atr = ta.atr(high, low, close, 14);
        const volumeSMA = ta.sma(volume, 20);

        // Generate signals
        const buySignal = rsi < 30 && macd > signal && volume > volumeSMA;
        const sellSignal = rsi > 70 && macd < signal;

        // Calculate stop loss and take profit levels
        const stopLoss = close - atr * 2;
        const takeProfit = close + atr * 3;

        return {
            rsi,
            macd,
            signal,
            atr,
            buySignal,
            sellSignal,
            stopLoss,
            takeProfit,
            price: close,
        };
    });

    // Access results
    const { result } = context;

    // Find trading opportunities
    console.log('Last RSI:', result.rsi[result.rsi.length - 1]);
    console.log('Last MACD:', result.macd[result.macd.length - 1]);

    // Count signals
    const buyCount = result.buySignal.filter(Boolean).length;
    const sellCount = result.sellSignal.filter(Boolean).length;
    console.log(`Buy signals: ${buyCount}, Sell signals: ${sellCount}`);
}

runComplexStrategy();
```

***

## Tips and Best Practices [#tips-and-best-practices]

### 1. Waiting for Data to Load [#1-waiting-for-data-to-load]

Always use `await` with `pineTS.run()` since data fetching is asynchronous:

```typescript
// ✅ Correct
const { result } = await pineTS.run((context) => { ... });

// ❌ Wrong - will not work properly
const { result } = pineTS.run((context) => { ... }); // Missing await
```

### 2. Destructuring for Cleaner Code [#2-destructuring-for-cleaner-code]

Destructure the context for more readable code:

```typescript
const { result } = await pineTS.run((context) => {
    // Destructure for cleaner access
    const { ta, math } = context;
    const { close, open, high, low } = context.data;

    // Now you can use them directly
    const sma = ta.sma(close, 20);
    return sma;
});
```

### 3. Return Objects for Multiple Values [#3-return-objects-for-multiple-values]

When calculating multiple indicators, return them as an object:

```typescript
// ✅ Return multiple values as object
return { sma, ema, rsi };

// ❌ Less convenient - only returns one value
return sma;
```

### 4. Performance Optimization [#4-performance-optimization]

For large datasets or complex calculations:

```typescript
// Enable TA cache
const { result } = await pineTS.run(indicatorFn, undefined, true);

// Or process fewer periods
const { result } = await pineTS.run(indicatorFn, 100); // Last 100 periods only
```

### 5. Error Handling [#5-error-handling]

Always wrap your PineTS code in try-catch blocks:

```typescript
try {
    const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);
    const { result } = await pineTS.run((context) => {
        // Your indicator logic
    });
    console.log(result);
} catch (error) {
    console.error('Error running indicator:', error);
}
```

***

## Alerts [#alerts]

PineTS supports `alert()` and `alertcondition()` from Pine Script. Alerts are captured as events that your application can act on — send webhooks, trigger trades, or log signals.

### Quick Example [#quick-example]

```typescript
const pine = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

const code = `
//@version=6
indicator("EMA Cross Alert")
if ta.crossover(ta.ema(close, 9), ta.ema(close, 21))
    alert("Bullish cross!", alert.freq_once_per_bar)
plot(close)
`;

// With run() — alerts on context
const ctx = await pine.run(code);
console.log(ctx.alerts); // [{type: 'alert', message: 'Bullish cross!', ...}]

// With stream() — real-time alert events
const evt = pine.stream(code, { live: true });
evt.on('alert', (a) => console.log('ALERT:', a.message));
```

### Backtest Mode [#backtest-mode]

By default, alerts only fire on the last (realtime) bar. For backtesting, enable alerts on all bars:

```typescript
pine.setAlertMode('all'); // Fire alerts on every bar
```

For full documentation including frequency constants, alert modes, and complete examples, see the &#x2A;*[Alerts](/developers/pinets/../alerts)** page.

***

## Next Steps [#next-steps]

* Check [API Coverage](/developers/pinets/../api-coverage) to see all available technical analysis functions
* Explore [Language Coverage](/developers/pinets/../lang-coverage) to understand Pine Script compatibility
* Try our demo indicators: [WillVixFix](/developers/pinets/../indicators/willvixfix) and [Squeeze Momentum](/developers/pinets/../indicators/sqzmom)
* Contribute on [GitHub](https://github.com/alaa-eddine/PineTS)


# Language Coverage (/developers/pinets/lang-coverage)



## PineTS Language Coverage [#pinets-language-coverage]

This document lists the features of the PineTS language and their status.

| Feature                | Status      | Comments                                                                |
| ---------------------- | ----------- | ----------------------------------------------------------------------- |
| Execution model        | done        | Core Pine Script language features, execution, and transpiler           |
| Time series            | done        | Variables behave like Pine Script time series (array annotation access) |
| Script structure       | done        | Syntax closely matches Pine Script where possible                       |
| Identifiers            | native      | Uses native JS/TS identifiers                                           |
| Operators              | native      | Uses native JS/TS operators                                             |
| Variables declarations | done        | Supports time series and variable syntaxes                              |
| Conditional structures | done        | if implemented / switch needs testing                                   |
| Loops                  | done        | for done / while missing / for in missing                               |
| Type system            | native      | Uses native JS/TS types                                                 |
| Builtins               | done        | Implemented open, close, high, low, hl2, hlc3, ohlc4                    |
| Functions              | done        | Check [api-coverage](/developers/pinets/api-coverage) for details       |
| UDT                    | done        | Check [api-coverage](/developers/pinets/api-coverage) for details       |
| Objects                | in progress | Check [api-coverage](/developers/pinets/api-coverage) for details       |
| Enums                  | done        | Check [api-coverage](/developers/pinets/api-coverage) for details       |
| Methods                | in progress | Check [api-coverage](/developers/pinets/api-coverage) for details       |
| Arrays                 | done        | Check [api-coverage](/developers/pinets/api-coverage) for details       |
| Matrices               | done        | Check [api-coverage](/developers/pinets/api-coverage) for details       |
| Maps                   | done        | Check [api-coverage](/developers/pinets/api-coverage) for details       |
| Imports                | planned     |                                                                         |


# Pagination & Live Streaming (/developers/pinets/pagination)



## Overview [#overview]

PineTS supports **pagination** and **live streaming** for processing large datasets and real-time market data. The primary and recommended way to handle this is via the `stream()` method, which provides a convenient event-based interface.

This enables you to:

* Process historical data in manageable chunks
* Build responsive UIs that show progress
* Stream live market data as new candles form
* Build real-time trading bots and monitoring systems

## Using `PineTS.stream` (Recommended) [#using-pinetsstream-recommended]

The `stream()` method unifies pagination and live streaming into a single, easy-to-use interface.

### Basic Usage [#basic-usage]

```typescript
import { PineTS, Provider } from 'pinets';

const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', '1m');

const evt = pineTS.stream(
    (context) => {
        const { close } = context.data;
        const sma = context.ta.sma(close, 20);
        return { close, sma };
    },
    {
        pageSize: 50, // Process data in chunks of 50 bars
        live: true, // Continue with live updates after history
        interval: 1000, // Check for updates every 1 second
    }
);

evt.on('data', (ctx) => {
    // This callback runs for every historical page AND every live update
    const { close, sma } = ctx.result;
    console.log(`Received data. Latest Close: ${close[close.length - 1]}`);
});

evt.on('error', (err) => {
    console.error('Stream error:', err);
});

// To stop the stream manually
// evt.stop();
```

### Processing Historical Pages Only [#processing-historical-pages-only]

If you only want to process historical data in pages (e.g., for memory efficiency or progress bars) without staying connected for live updates, set `live: false`.

```typescript
const evt = pineTS.stream(indicator, {
    pageSize: 100,
    live: false, // Stop after processing all historical data
});

evt.on('data', (ctx) => {
    console.log(`Processed page with ${ctx.result.close.length} bars`);
    // Update progress bar...
});
```

### Live Streaming [#live-streaming]

By default (`live: true`), `stream()` will seamlessly transition from processing historical data to fetching live updates.

1. **Historical Phase**: `data` events are emitted for each page of historical data (size determined by `pageSize`).
2. **Live Phase**: Once history is caught up, `data` events are emitted whenever new market data is available, polled at `interval` milliseconds.

```typescript
const evt = pineTS.stream(indicator, {
    pageSize: 100,
    live: true,
    interval: 2000, // Poll every 2 seconds
});

evt.on('data', (ctx) => {
    // Check if this is a historical chunk or a live update
    const currentCandle = ctx.marketData[ctx.idx];
    const isHistorical = currentCandle.closeTime < Date.now();

    if (isHistorical) {
        console.log('Processing history...');
    } else {
        console.log('⚡ LIVE UPDATE:', ctx.result);
    }
});
```

### Options [#options]

| Option     | Type      | Default           | Description                                                                                    |
| :--------- | :-------- | :---------------- | :--------------------------------------------------------------------------------------------- |
| `pageSize` | `number`  | `undefined` (all) | Number of bars per chunk. If not specified, processes all available historical data in one go. |
| `live`     | `boolean` | `true`            | Whether to continue fetching live data after processing historical data.                       |
| `interval` | `number`  | `1000`            | Polling interval in milliseconds for live data.                                                |

***

## Alternative: Using Iterator with `PineTS.run` (Advanced) [#alternative-using-iterator-with-pinetsrun-advanced]

If you need more granular control over the execution flow (e.g., using `await` inside the loop before processing the next chunk), you can use the `run()` method with an iterator.

### Basic Iterator Usage [#basic-iterator-usage]

```typescript
// Process 100 candles in pages of 10
const iterator = pineTS.run(indicator, 100, 10);

for await (const page of iterator) {
    console.log(`Received ${page.result.sma.length} results`);
    // The loop pauses here until you process this page
    // Useful if you need to perform async operations for each page
}
```

### Manual Live Streaming with Iterator [#manual-live-streaming-with-iterator]

When using `run()` with a provider and no end date, the iterator will automatically enter live mode. You must handle the polling wait time yourself when `null` is yielded.

```typescript
const iterator = pineTS.run(indicator, 50, 10);

for await (const page of iterator) {
    if (page === null) {
        // No new data yet, YOU must control the polling interval
        await new Promise((r) => setTimeout(r, 1000));
        continue;
    }

    const { sma } = page.result;
    console.log(`New data: SMA=${sma[sma.length - 1]}`);
}
```

### Comparison [#comparison]

| Feature            | `stream()` (Recommended)          | `run()` Iterator              |
| :----------------- | :-------------------------------- | :---------------------------- |
| **Interface**      | Event-based (`.on('data')`)       | Async Iterator (`for await`)  |
| **Control Flow**   | Non-blocking (callbacks)          | Blocking (awaits processing)  |
| **Live Polling**   | Automatic (handled by `interval`) | Manual (must implement sleep) |
| **Error Handling** | `.on('error')`                    | `try/catch` block             |
| **Complexity**     | Low                               | Medium/High                   |

## Use Cases [#use-cases]

### Real-Time Trading Bot [#real-time-trading-bot]

Use `stream()` with `live: true`. The event-driven nature fits perfectly with reacting to market changes.

### Backtesting Progress Bar [#backtesting-progress-bar]

Use `stream()` with `live: false` and `pageSize`. Update your UI's progress bar in the `data` callback.

### Complex Async Processing [#complex-async-processing]

Use `run()` iterator if you need to perform complex asynchronous operations (like database writes or expensive calculations) for each page and want to prevent new data from arriving until the current page is fully processed.

## Understanding Open vs Closed Candles [#understanding-open-vs-closed-candles]

### What Are Open Candles? [#what-are-open-candles]

In live trading, the **current candle** is still forming - its high, low, close, and volume are constantly changing. This is called an **open candle**.

### How PineTS Handles Open Candles [#how-pinets-handles-open-candles]

When live streaming (both `stream()` and `run()`), PineTS:

1. **Always recalculates the last candle** when fetching new data
2. **Updates OHLCV values** with the latest market data
3. **Recalculates all indicators** for the updated candle
4. **Returns updated results** so your indicators stay current

```typescript
// Watching a candle form in real-time
const evt = pineTS.stream(indicator, { live: true });
let previousClose = null;

evt.on('data', (ctx) => {
    const currentClose = ctx.result.close[ctx.result.close.length - 1];

    if (previousClose !== null && currentClose !== previousClose) {
        console.log('⚡ Candle updated:', currentClose);
    }
    previousClose = currentClose;
});
```

## Best Practices [#best-practices]

### ✅ Do's [#-dos]

* **Use `stream()`** for most use cases - it's simpler and less error-prone.
* **Use pagination (`pageSize`)** for large datasets (>1000 candles) to save memory.
* **Handle errors** by listening to the `error` event.
* **Stop streams** using `evt.stop()` when they are no longer needed (e.g., component unmount).

### ❌ Don'ts [#-donts]

* **Don't block the event loop** inside `data` callbacks. If you have heavy processing, consider offloading it.
* **Don't set `interval` too low** (e.g., \< 100ms) to avoid rate limiting from data providers.


# Strategy Namespace (/developers/pinets/strategy)



PineTS implements Pine Script's full `strategy.*` surface so you can run TradingView strategies — entries, exits, position management, performance metrics — locally against your own data.

This page is the developer reference. For the surface checklist (every `strategy.*&#x60; entry mapped to its implementation status) see the &#x2A;*[Strategy API coverage page](/developers/pinets/api-coverage/strategy)**.

Every example below is exercised by a verification harness at `PineTS/.scratchpad/strategy-doc-examples.cjs` (kept in lock-step with the docs during authoring). If something doesn't behave as documented, the harness fails.

## Table of Contents [#table-of-contents]

* [Quickstart](#quickstart)
* [The `strategy()` declaration](#the-strategy-declaration)
* [Order primitives](#order-primitives)
  * [`strategy.order()`](#strategyorder)
  * [`strategy.entry()`](#strategyentry)
  * [`strategy.exit()`](#strategyexit)
  * [`strategy.close()` / `close_all()`](#strategyclose--close_all)
  * [`strategy.cancel()` / `cancel_all()`](#strategycancel--cancel_all)
* [The `context.strategy` object](#the-contextstrategy-object)
* [Trade collections](#trade-collections)
* [Read-only getters](#read-only-getters)
* [Risk-adjusted performance (Sharpe / Sortino)](#risk-adjusted-performance-sharpe--sortino)
* [Compound annual growth rate (CAGR)](#compound-annual-growth-rate-cagr)
* [Benchmark - Buy-and-hold return](#benchmark---buy-and-hold-return)
* [Constants](#constants)
* [Risk management](#risk-management)
* [Conversion helpers](#conversion-helpers)
* [Known divergences](#known-divergences)

***

## Quickstart [#quickstart]

```javascript
const { PineTS } = require('pinets');

const data = [/* …OHLCV bars… */];
const pine = new PineTS(data);

const ctx = await pine.run(($) => {
    const { strategy } = $.pine;

    // 1. Declare the strategy ONCE per run.
    strategy('My Long-Only', {
        overlay: true,
        initial_capital: 100000,
        default_qty_type: 'percent_of_equity',
        default_qty_value: 10,
    });

    // 2. Place orders based on bar conditions.
    //    Always wrap bodies in { ... } — see the brace quirk below.
    if ($.idx === 1) {
        strategy.entry('long', strategy.long, 1);
    }
    if ($.idx === 10) {
        strategy.close('long');
    }
});

// 3. Inspect the final state.
const s = ctx.strategy;
console.log('Net profit:', s.netprofit);
console.log('Closed trades:', s.closedtrades.length);
console.log('First trade profit:', s.closedtrades[0].profit);
```

The same script in native Pine syntax also works — pass the source to `run()`:

```javascript
const code = `
//@version=5
strategy("My Long-Only", overlay=true, initial_capital=100000,
         default_qty_type=strategy.percent_of_equity, default_qty_value=10)
if bar_index == 1
    strategy.entry("long", strategy.long, 1)
if bar_index == 10
    strategy.close("long")
`;

// (a) Bare string — simplest form.
const ctx = await pine.run(code);

// (b) Wrapped in Indicator() — required when you need to override input.* values
//     at runtime (the keys must match each input's title argument).
const { Indicator } = require('pinets');
const ctx2 = await pine.run(new Indicator(code));                    // no overrides
const ctx3 = await pine.run(new Indicator(code, { Qty: 5 }));        // override input.int(1, "Qty")
```

The `Indicator()&#x60; wrapper is the same one used for indicator scripts — strategies and indicators share the runtime, so there's nothing strategy-specific to do. See &#x2A;*[Indicator](/developers/pinets/indicator)** for the full API (per-key overrides via `ind.input[...]` / `ind.prop[...]&#x60;, schema introspection, etc.), or &#x2A;*[Initialization and Usage → Running with Runtime Inputs](/developers/pinets/initialization-and-usage#running-with-runtime-inputs)** for a quick overview.

***

## The `strategy()` declaration [#the-strategy-declaration]

A single `strategy(title, options)` call initializes the strategy state on `context.strategy`. It's safe to call on every bar — only the first call initializes; subsequent calls update `config` only. Field names mirror Pine's `strategy()` parameters exactly.

```javascript
await pine.run(($) => {
    const { strategy } = $.pine;
    strategy('My Strategy', {
        overlay: true,
        initial_capital: 50000,
        default_qty_type: 'percent_of_equity',  // 'fixed' | 'percent_of_equity' | 'cash'
        default_qty_value: 25,
        commission_type:  'percent',            // 'percent' | 'cash_per_order' | 'cash_per_contract'
        commission_value: 0.075,
        slippage: 2,                            // in ticks of syminfo.mintick
        pyramiding: 3,
    });
});
```

After the call, `context.strategy.config` holds the merged options (defaults + your overrides) and the rest of `context.strategy` is initialized — see the [object reference below](#the-contextstrategy-object).

**Supported options** (every field is `StrategyConfig` in [`PineTS/src/namespaces/strategy/types.ts`](https://github.com/alaa-eddine/PineTS/blob/main/src/namespaces/strategy/types.ts)):

| Field                                                                              | Type      | Default     | Notes                                                                                          |
| ---------------------------------------------------------------------------------- | --------- | ----------- | ---------------------------------------------------------------------------------------------- |
| `title`                                                                            | `string`  | `''`        | First positional arg                                                                           |
| `overlay`                                                                          | `boolean` | `false`     |                                                                                                |
| `initial_capital`                                                                  | `number`  | `1000000`   |                                                                                                |
| `currency`                                                                         | `string`  | `'USD'`     |                                                                                                |
| `pyramiding`                                                                       | `number`  | `1`         | Cap on same-direction open trades                                                              |
| `default_qty_type`                                                                 | `string`  | `'fixed'`   | Qty unit for unspecified `entry()` qty                                                         |
| `default_qty_value`                                                                | `number`  | `1`         |                                                                                                |
| `commission_type`                                                                  | `string`  | `'percent'` |                                                                                                |
| `commission_value`                                                                 | `number`  | `0`         |                                                                                                |
| `slippage`                                                                         | `number`  | `0`         | Ticks against trade direction                                                                  |
| `margin_long` / `margin_short`                                                     | `number`  | `100`       | Margin % (used by `margin_liquidation_price`)                                                  |
| `risk_free_rate`                                                                   | `number`  | `2`         | Annual %, denominator input for [Sharpe / Sortino](#risk-adjusted-performance-sharpe--sortino) |
| `process_orders_on_close`                                                          | `boolean` | `false`     | Affects `strategy.close({immediately: true})`                                                  |
| `max_lines_count` / `max_labels_count` / `max_boxes_count` / `max_polylines_count` | `number`  | `50`        | Pass-through to drawing engine                                                                 |

***

## Order primitives [#order-primitives]

PineTS implements the same order lifecycle as TradingView:

1. **You place an order on bar N** — it goes onto `state.pending_orders`.
2. **On bar N+1's open, the engine processes it** — fills it if market, or checks limit/stop conditions. Slippage and commissions are applied at fill time.
3. **Position-mutating events** (open / close / reverse) are mirrored on the flat scalars (`position_size`, `position_avg_price`, `position_entry_name`).

Internal lifecycle is handled by `processStrategyOrders()` and `processExitOrders()` in [`strategy/utils.ts`](https://github.com/alaa-eddine/PineTS/blob/main/src/namespaces/strategy/utils.ts), invoked at the start of every bar.

### `strategy.order()` [#strategyorder]

The lowest-level primitive — no pyramiding cap, no auto-reverse, no risk filters. Use when you need exact control.

```javascript
await pine.run(($) => {
    const { strategy } = $.pine;
    strategy('Order Test', { overlay: true, initial_capital: 10000 });
    if ($.idx === 1) {
        // Pine: strategy.order(id, direction, qty, limit?, stop?, ...)
        strategy.order('long1', strategy.long, 1);
    }
});
```

After this run, `context.strategy.opentrades[0]` is `{ entry_id: 'long1', size: 1, entry_price: <bar-2 open>, ... }`.

### `strategy.entry()` [#strategyentry]

Same shape as `order()`, but adds two behaviors:

* **Pyramiding cap** — if the `strategy()` declaration sets `pyramiding: N`, additional same-direction entries become no-ops once `N` trades are open in that direction.
* **Auto-reversal** — entering opposite the current position closes the existing position and opens the new one in a single market order. The pending order's qty is automatically inflated to `|current position| + requested qty`.

```javascript
// Pyramiding cap
strategy('Pyramid', { pyramiding: 2 });
if ($.idx === 1)  { strategy.entry('L1', strategy.long, 1); }
if ($.idx === 5)  { strategy.entry('L2', strategy.long, 1); }
if ($.idx === 10) { strategy.entry('L3', strategy.long, 1); }  // no-op once L1+L2 are open

// Auto-reversal
if ($.idx === 1) { strategy.entry('go-long',  strategy.long,  1); }
if ($.idx === 5) { strategy.entry('go-short', strategy.short, 1); }
// after bar 5+1: closedtrades=[go-long], opentrades=[go-short], position_size=-1
```

### `strategy.exit()` [#strategyexit]

Attaches conditional exit orders (TP / SL / trailing stop) to an existing entry. Multiple legs on a single `exit()` call are treated OCO — the first to trigger fires and removes the rest.

```javascript
if ($.idx === 1) {
    strategy.entry('long', strategy.long, 1);
    strategy.exit('tp/sl', {
        from_entry: 'long',      // empty/undefined = attach to ALL open entries
        profit: 10000,           // TP in TICKS of syminfo.mintick
        loss: 5000,              // SL in TICKS
        // or absolute prices:
        // limit: 120,           // TP price level
        // stop:  90,            // SL price level
        // trailing:
        // trail_price: 110,     // arm trailing once price hits this absolute level
        // trail_points: 500,    // OR arm once price moves N ticks above entry
        // trail_offset: 200,    // ride N ticks behind the running peak
    });
}
```

Trigger evaluation runs each bar against the bar's intra-bar high/low (not close), matching TV's "tick-fast" semantics — a TP/SL can fire mid-bar even if the close doesn't reach the level.

### `strategy.close()` / `close_all()` [#strategyclose--close_all]

`close(id)` flattens any position opened by the entry-id `id` (FIFO across multiple trades sharing the id). `close_all()` flattens **all** open trades regardless of id.

```javascript
if ($.idx === 1) {
    strategy.entry('A', strategy.long, 1);
    strategy.entry('B', strategy.long, 1);
}
if ($.idx === 5) { strategy.close('A'); }
// after: closedtrades=[A], opentrades=[B]

// or
if ($.idx === 5) { strategy.close_all(); }
// after: closedtrades=[A, B], opentrades=[]
```

Both accept an `immediately: true` option that requires `process_orders_on_close: true` in the declaration and fills at the **current** bar's close instead of the next bar's open.

### `strategy.cancel()` / `cancel_all()` [#strategycancel--cancel_all]

Removes pending orders. `cancel(id)` drops orders whose `id` matches; `cancel_all()` empties `pending_orders`. Already-filled trades are unaffected.

```javascript
if ($.idx === 1) {
    strategy.entry('lim', strategy.long, 1, /*limit=*/ 1);  // limit far below market → never fills
    strategy.cancel('lim');                                 // remove it
}
```

***

## The `context.strategy` object [#the-contextstrategy-object]

After `strategy()` is declared, `context.strategy` exposes the live state — the same object every getter reads from. It's documented in [`StrategyState`](https://github.com/alaa-eddine/PineTS/blob/main/src/namespaces/strategy/types.ts):

```typescript
interface StrategyState {
    config: StrategyConfig;             // merged declaration options

    // Trade collections (arrays, indexable, .length = Pine's count)
    opentrades:     Trade[];
    closedtrades:   Trade[];
    pending_orders: Order[];

    // Position info — FLAT scalars matching Pine's data model
    position_size:        number;       // SIGNED — positive long, negative short
    position_avg_price:   number;       // NaN when flat
    position_entry_name:  string;       // entry_id that opened the current position

    // Account / aggregate P&L
    initial_capital:  number;
    account_currency: string;
    equity:           number;
    netprofit:        number;           // realized only
    grossprofit:      number;
    grossloss:        number;
    openprofit:       number;           // unrealized P&L of open positions

    // Peaks
    max_drawdown:     number;
    max_runup:        number;
    equity_peak:      number;           // internal high-water mark
    equity_trough:    number;

    // Risk-adjusted performance — computed ONCE at end-of-run from the
    // monthly equity curve (report-only; see the section below). These are
    // NOT Pine built-in variables, so they're read off context.strategy only.
    sharpe_ratio:     number;
    sortino_ratio:    number;

    // Capital efficiency
    cagr:             number;           // annualized return %, report-only (NaN if window < 1 day)

    // Buy-and-hold benchmark — report-only, computed ONCE at end-of-run.
    // Models a long position bought with the entire initial capital at the
    // first trade's (slippage-adjusted) entry price and held to the last bar.
    buy_and_hold_pnl:        number;    // initial_capital × per_gain / 100
    buy_and_hold_per_gain:   number;    // (last_close − first_entry) / first_entry × 100
    strategy_outperformance: number;    // netprofit − buy_and_hold_pnl

    // Trade-stat counters
    wintrades:                number;
    losstrades:               number;
    eventrades:               number;
    wintrades_total_profit:   number;
    losstrades_total_loss:    number;

    // Position-size peaks
    max_contracts_held_all:   number;
    max_contracts_held_long:  number;
    max_contracts_held_short: number;

    // Risk rules + halt flag (set by strategy.risk.*)
    risk_rules: { /* see Risk Management */ };
    risk_halted: boolean;
}
```

Each `Trade` (in `opentrades` / `closedtrades`):

```typescript
interface Trade {
    id:               string;           // internal unique id
    entry_id:         string;           // id passed to strategy.entry()
    entry_price:      number;
    entry_bar_index:  number;
    entry_time:       number;           // ms timestamp
    entry_comment?:   string;
    exit_id?:         string;           // set on close
    exit_price?:      number;
    exit_bar_index?:  number;
    exit_time?:       number;
    exit_comment?:    string;
    size:             number;           // SIGNED — positive long, negative short
    profit?:          number;           // realized P&L on close (commission-netted)
    commission?:      number;           // total commission charged on this trade
    max_drawdown?:    number;           // per-trade peak adverse excursion (in dollars)
    max_runup?:       number;           // per-trade peak favorable excursion
    status:           'open' | 'closed';
}
```

Each `Order` (in `pending_orders`):

```typescript
interface Order {
    id:         string;
    direction:  number;                  // +1 long, -1 short
    qty:        number;                  // unsigned
    type:       'market' | 'limit' | 'stop' | 'stop-limit';
    limit?:     number;
    stop?:      number;
    bar:        number;                  // bar index where the order was placed
    time:       number;                  // ms timestamp at placement
    category?:  'entry' | 'exit';        // 'exit' for TP/SL/trailing orders
    status:     'pending' | 'filled' | 'cancelled';
    fill_price?: number;
    fill_bar?:   number;
    fill_time?:  number;

    // Exit-specific fields (when category === 'exit')
    profit?:        number;              // TP in ticks
    loss?:          number;              // SL in ticks
    trail_price?:   number;
    trail_offset?:  number;
    trail_points?:  number;
    from_entry?:    string;              // '' = all open entries
    qty_percent?:   number;
    // ... comments + alert texts per leg
    oca_name?:      string;
    oca_type?:      'cancel' | 'reduce' | 'none';
}
```

***

## Trade collections [#trade-collections]

`strategy.closedtrades` and `strategy.opentrades` serve a dual role in Pine — both as a count (series int) and as a namespace for per-trade getters (`profit(idx)`, `entry_price(idx)`, etc.). PineTS preserves both.

**From inside the run callback (or in Pine syntax)** — the transpiler auto-calls the namespace, so you can write Pine-style code:

```javascript
await pine.run(($) => {
    const { strategy, plot } = $.pine;
    strategy('Getter Test', { overlay: true, initial_capital: 100000 });
    if ($.idx === 1) { strategy.entry('e1', strategy.long, 1); }
    if ($.idx === 5) { strategy.close('e1'); }

    // Used as a count (auto-coerces to int via valueOf):
    plot(strategy.closedtrades);

    // Used as a namespace (per-trade getter):
    // const lastProfit = strategy.closedtrades.profit(0);
});
```

**From plain JS code outside the callback** (and on the returned context), it's an array — use index access:

```javascript
const ctx = await pine.run(/* ... */);
const s = ctx.strategy;

console.log('Closed count:', s.closedtrades.length);
console.log('First profit:', s.closedtrades[0].profit);
console.log('First trade size:', s.closedtrades[0].size);     // signed
console.log('First trade was a win:', s.closedtrades[0].profit > 0);
```

Per-trade getter methods (`profit(idx)`, `size(idx)`, `entry_price(idx)`, `max_drawdown_percent(idx)`, etc.) are listed in the [API coverage table](/developers/pinets/api-coverage/strategy#closed-trades).

***

## Read-only getters [#read-only-getters]

Every Pine `strategy.*` scalar getter is implemented as a property on the `strategy` namespace inside the run callback, and is also available as a field on `context.strategy` after the run:

| Getter                                                                         | Source                                           |
| ------------------------------------------------------------------------------ | ------------------------------------------------ |
| `strategy.equity`                                                              | `state.equity` (initial + realized + unrealized) |
| `strategy.netprofit` / `netprofit_percent`                                     | `state.netprofit` (realized only)                |
| `strategy.openprofit` / `openprofit_percent`                                   | `state.openprofit` (unrealized)                  |
| `strategy.grossprofit` / `grossprofit_percent`                                 | `state.grossprofit`                              |
| `strategy.grossloss` / `grossloss_percent`                                     | `state.grossloss`                                |
| `strategy.position_size`                                                       | signed; positive long, negative short            |
| `strategy.position_avg_price`                                                  | NaN when flat                                    |
| `strategy.position_entry_name`                                                 | entry id that opened current position            |
| `strategy.wintrades` / `losstrades` / `eventrades`                             | trade outcome counters                           |
| `strategy.avg_trade` / `avg_winning_trade` / `avg_losing_trade` (+ `_percent`) | derived averages                                 |
| `strategy.max_drawdown` / `max_drawdown_percent`                               | equity-curve trough                              |
| `strategy.max_runup` / `max_runup_percent`                                     | equity-curve peak above start                    |
| `strategy.max_contracts_held_all` / `_long` / `_short`                         | running peaks                                    |
| `strategy.initial_capital` / `account_currency`                                | from declaration                                 |
| `strategy.margin_liquidation_price`                                            | computed from position + margin                  |

After a run, the same values are on `context.strategy` (the names are 1:1):

```javascript
const ctx = await pine.run(/* ... */);
const s = ctx.strategy;

console.log('Equity:', s.equity);
console.log('Net profit:', s.netprofit);
console.log('Win rate:', s.wintrades / s.closedtrades.length);
console.log('Max drawdown:', s.max_drawdown);
console.log('Max contracts held (long):', s.max_contracts_held_long);
```

***

## Risk-adjusted performance (Sharpe / Sortino) [#risk-adjusted-performance-sharpe--sortino]

PineTS reports the two risk-adjusted ratios from TradingView's **Risk-adjusted performance** panel:

```javascript
const ctx = await pine.run(/* ... */);
const s = ctx.strategy;

console.log('Sharpe ratio:',  s.sharpe_ratio);
console.log('Sortino ratio:', s.sortino_ratio);
```

**These are report-only fields, not getters.** Unlike `netprofit` or `max_drawdown`, Sharpe and Sortino are **not** Pine built-in variables — TradingView surfaces them only in the Strategy Tester report (and xlsx export), never to scripts. So there is no `strategy.sharpe_ratio` accessor inside the run callback; the values live on `context.strategy` after the run and nowhere else.

They are computed **once at the end of the run** (in `finalizeStrategyRun`), from the strategy's monthly equity curve.

### How they're calculated [#how-theyre-calculated]

Matching TradingView's documented methodology:

1. **Sample equity monthly.** The mark-to-market `strategy.equity` is captured at the last bar of each calendar month.
2. **Monthly returns.** Simple returns `rᵢ = Eᵢ / Eᵢ₋₁ − 1`, anchored at `initial_capital` (the first return runs from initial capital to the first month-end).
3. **Risk-free rate.** `RFR = risk_free_rate / 100 / 12` — the annual `risk_free_rate` declaration option (default **2**, i.e. 2%) converted to a monthly figure.
4. **Ratios** (no annualization):

   ```
   Sharpe  = (mean(r) − RFR) / SD
   Sortino = (mean(r) − RFR) / DD

   SD = √( Σ (rᵢ − mean(r))² / N )           — population standard deviation
   DD = √( Σ min(0, rᵢ − RFR)²   / N )        — downside deviation over all N returns, target = RFR
   ```

Edge cases: with fewer than two monthly returns both ratios are `0`; a zero-variance (flat) curve yields Sharpe `0`; a curve with no downside (every return above the RFR) yields Sortino `0`.

Set the risk-free rate via the declaration:

```javascript
strategy('RFR example', { initial_capital: 100000, risk_free_rate: 4 });  // 4% annual
```

### Accuracy [#accuracy]

The formula matches TradingView's exactly, but the ratios are **derivatives of the bar-by-bar equity curve** — so their fidelity rides on the engine's mark-to-market accuracy, not on the formula. Across the TV oracle datasets they land within roughly the second decimal (most within \~0.01; strategies with heavy margin-call activity diverge a little more, since their intra-month equity path is where PineTS and TV differ most even when final net profit matches). They are intended as a faithful summary metric, **not** a cent-exact reproduction like `netprofit`.

***

## Capital Efficiency - Compound annual growth rate (CAGR) [#capital-efficiency---compound-annual-growth-rate-cagr]

PineTS also reports the &#x2A;*annualized return (%)** of strategy equity over the whole backtest window:

```javascript
const ctx = await pine.run(/* ... */);
const s = ctx.strategy;

console.log('Strategy CAGR:', s.cagr, '%');
```

Like Sharpe / Sortino, `strategy.cagr` is a **report-only field, not a Pine built-in** — it lives on `context.strategy` after the run, computed once in `finalizeStrategyRun`.

### How it's calculated [#how-its-calculated]

Mirrors TradingView calculations:

```
daysBetween = (lastBarTime − firstBarTime) / 86_400_000   // ms per day
years       = daysBetween / 365
CAGR%       = 100 × ( (initial_capital + netprofit) / initial_capital ) ^ ((1 / years) − 1 )
```

`firstBarTime` / `lastBarTime` are the open times of the first and last loaded bars (matching Pine's `var int firstTime = time` and `last_bar_time`).

Edge cases: the result is `NaN` when the window is shorter than one day, when there is no loaded data, or when the capital figures are non-finite.

***

## Benchmark - Buy-and-hold return [#benchmark---buy-and-hold-return]

PineTS reports how the strategy fared against simply **buying and holding** the instrument over the same window:

```javascript
const ctx = await pine.run(/* ... */);
const s = ctx.strategy;

console.log('Buy & hold P&L:',     s.buy_and_hold_pnl);        // currency
console.log('Buy & hold % gain:',  s.buy_and_hold_per_gain);   // percent
console.log('Outperformance:',     s.strategy_outperformance); // netprofit − buy&hold
```

Like CAGR / Sharpe / Sortino these are **report-only fields, not Pine built-ins** — computed once in `finalizeStrategyRun` and read off `context.strategy` after the run.

### How it's calculated [#how-its-calculated-1]

The benchmark models a single **long** position bought with the **entire initial capital** at the **first trade's entry price** and held open through the last bar — it is never sold:

```
price_start   = first trade's entry price   // slippage already applied at fill
price_end     = last bar's close
qty           = initial_capital / price_start
buy_and_hold_pnl        = qty × (price_end − price_start)
                        = initial_capital × (price_end − price_start) / price_start
buy_and_hold_per_gain   = (price_end − price_start) / price_start × 100
strategy_outperformance = netprofit − buy_and_hold_pnl
```

Because the anchor is the first trade's actual fill price, the benchmark **is affected by the `slippage` property** (slippage shifts that fill). Since the position is never closed, there is **no exit leg**: commissions never apply and `price_end` carries no slippage. The figures are `NaN` until the first trade opens.

***

## Constants [#constants]

PineTS exposes Pine's three sets of `strategy` constants. Inside the run callback they appear as bare strings; from plain JS you call them as factories.

**Top-level constants** (resolve to themselves — the engine accepts these names verbatim):

```javascript
strategy.long              === 'long'
strategy.short             === 'short'
strategy.cash              === 'cash'
strategy.fixed             === 'fixed'
strategy.percent_of_equity === 'percent_of_equity'
```

**Nested constant namespaces** — the transpiler auto-calls them inside the run callback. From plain JS, you call the namespace yourself:

```javascript
// Inside run callback (Pine-style):
strategy.direction.long    // 'long'
strategy.oca.cancel        // 'cancel'
strategy.commission.percent  // 'percent'

// From plain JS:
const dir = strategy.direction();   // { long: 'long', short: 'short', all: 'all' }
const oca = strategy.oca();         // { none: 'none', cancel: 'cancel', reduce: 'reduce' }
const com = strategy.commission();  // { percent: 'percent', cash_per_order: 'cash_per_order',
                                    //   cash_per_contract: 'cash_per_contract' }
```

***

## Risk management [#risk-management]

`strategy.risk` is a nested namespace with 6 setter functions. Each one configures a pre-trade filter on `state.risk_rules`. Filters are checked by the engine before every fill; once a hard-stop rule triggers, `state.risk_halted` is set and further entries are blocked for the remainder of the run.

```javascript
await pine.run(($) => {
    const { strategy } = $.pine;
    strategy('Risk-Managed', { overlay: true, initial_capital: 100000 });
    if ($.idx === 0) {
        // Inside the run callback (Pine-style auto-call works too — strategy.risk.x()):
        const r = strategy.risk();
        r.allow_entry_in('long');           // 'long' | 'short' | 'all'
        r.max_position_size(5);             // cap contracts at 5
        r.max_drawdown(20000, 'cash');      // halt entries if equity drops $20k from peak
        r.max_intraday_loss(5000, 'cash');  // halt for the day if loss exceeds $5k
        r.max_intraday_filled_orders(10);   // halt for the day after 10 fills
        r.max_cons_loss_days(3);            // halt after 3 consecutive losing days
    }
});

// Inspect after the run:
const ctx = /* ... */;
console.log(ctx.strategy.risk_rules);
// { allow_entry_in: 'long', max_position_size: 5,
//   max_drawdown: { value: 20000, type: 'cash' }, ... }
console.log(ctx.strategy.risk_halted);  // true if any hard-stop fired
```

***

## Conversion helpers [#conversion-helpers]

Three utility functions for currency / qty math. In PineTS today the conversion functions are identity passthroughs for same-currency strategies (the engine doesn't currently fetch FX rates); `default_entry_qty` is fully functional and computes the qty a `strategy.entry()` would size given the current `default_qty_type` / `default_qty_value` and a hypothetical fill price.

```javascript
await pine.run(($) => {
    const { strategy } = $.pine;
    strategy('Conversion', { overlay: true, currency: 'USD',
                             default_qty_type: 'percent_of_equity', default_qty_value: 10 });

    const inUsd  = strategy.convert_to_account(100);  // 100 (passthrough for same-currency)
    const inSym  = strategy.convert_to_symbol(100);   // 100
    const qty    = strategy.default_entry_qty(50000); // (equity * 10%) / 50000
});
```

***

## Known divergences [#known-divergences]

The strategy namespace's surface is implemented 1:1 with TV. A subset of strategies produce values that diverge from TV in specific fields — these are tracked iteration items, **not** missing surface:

* **`strategy.margin_liquidation_price`** — PineTS uses an "equity hits zero" approximation; TV's broker liquidation formula differs.
* **`strategy.convert_to_account` / `convert_to_symbol`** — identity passthrough for same-currency cases. TV may return `na` when symbol/account currencies differ even nominally (e.g. `BTCUSDC`'s USDC vs USD).
* **OCA enforcement** — order objects carry `oca_name` / `oca_type` fields, but the engine doesn't yet auto-cancel or reduce siblings on fill. Deferred Phase 7.
* **Commission rounding** — per-leg charges may differ from TV by sub-cent rounding in edge cases.
* **Per-trade `max_drawdown` / `max_runup`** — PineTS tracks intra-bar high/low excursions, but TV's accounting differs for trades that open and close in adjacent bars.
* **`strategy.sharpe_ratio` / `strategy.sortino_ratio`** — the formula matches TV exactly, but the ratios are computed off the monthly equity curve and therefore inherit any bar-by-bar mark-to-market path difference. They match TV to \~2 decimals (most datasets within \~0.01), not to the cent. See [Risk-adjusted performance](#risk-adjusted-performance-sharpe--sortino).

For the complete checklist (every entry mapped to its implementation status) and the list of TV oracle scripts in use, see the &#x2A;*[Strategy API coverage page](/developers/pinets/api-coverage/strategy)**.


# Syntax Guide (/developers/pinets/syntax-guide)



## PineTS Syntax Guide [#pinets-syntax-guide]

This guide explains how to write PineTS code that is equivalent to Pine Script. PineTS is designed to be written in JavaScript/TypeScript but behaves like Pine Script's runtime execution model.

## Variable Declarations [#variable-declarations]

PineTS distinguishes between `let` and `var` declarations to mimic Pine Script's behavior. This is a critical difference from standard JavaScript.

### `let` vs `var` [#let-vs-var]

| Feature               | Pine Script         | PineTS (JS/TS)  | Behavior                                                                             |
| :-------------------- | :------------------ | :-------------- | :----------------------------------------------------------------------------------- |
| **Re-initialization** | `float x = close`   | `let x = close` | Variable is re-initialized/calculated **on every bar**.                              |
| **State Persistence** | `var float x = 0.0` | `var x = 0.0`   | Variable is initialized **only once** (first bar) and retains its value across bars. |

**⚠️ Important for JS Developers:** In PineTS, `var` does **not** behave like standard JavaScript `var`. It adopts Pine Script's `var` semantics (persistent state). If you need standard JS function-scoped variables that reset every time, use `let`.

#### Example: State Persistence [#example-state-persistence]

**Pine Script:**

```javascript
// 'sum' retains its value across bars
var float sum = 0.0
sum := sum + close
```

**PineTS:**

```javascript
// 'sum' retains its value across bars
var sum = 0.0;
sum = sum + close;
```

## Loops [#loops]

PineTS supports standard JavaScript loops, which map to Pine Script's loops.

| Feature        | Pine Script       | PineTS (JS/TS)                  |
| :------------- | :---------------- | :------------------------------ |
| **For Loop**   | `for i = 0 to 10` | `for (let i = 0; i <= 10; i++)` |
| **While Loop** | `while i < 10`    | `while (i < 10)`                |

#### Example: For Loop [#example-for-loop]

**Pine Script:**

```javascript
float sum = 0.0
for i = 0 to 9
    sum := sum + close[i]
```

**PineTS:**

```javascript
let sum = 0.0;
for (let i = 0; i < 10; i++) {
    sum += close[i];
}
```

## Control Structures [#control-structures]

### Switch Statement [#switch-statement]

PineTS supports the JavaScript `switch` statement, which is equivalent to Pine Script's `switch`.

**Pine Script:**

```javascript
switch type
    "ema" => ta.ema(close, len)
    "sma" => ta.sma(close, len)
    => ta.rma(close, len)
```

**PineTS:**

```javascript
switch (type) {
    case 'ema':
        return ta.ema(close, len);
    case 'sma':
        return ta.sma(close, len);
    default:
        return ta.rma(close, len);
}
```

## Functions [#functions]

User-defined functions in PineTS are written as standard JavaScript functions.

**Pine Script:**

```javascript
f_ma(source, length) =>
    ta.sma(source, length)
```

**PineTS:**

```javascript
function f_ma(source, length) {
    return ta.sma(source, length);
}
```

## Tuples and Multiple Return Values [#tuples-and-multiple-return-values]

Pine Script allows functions to return multiple values (tuples). PineTS handles this using array destructuring.

**Pine Script:**

```javascript
[macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9)
```

**PineTS:**

```javascript
const [macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9);
```

## Series and History Access [#series-and-history-access]

Accessing historical values is done using the `[]` operator in Pine Script. In PineTS, array access syntax is supported and transpiled to safe series access.

| Feature            | Pine Script | PineTS (JS/TS) | Notes                               |
| :----------------- | :---------- | :------------- | :---------------------------------- |
| **Current Value**  | `close`     | `close`        | References the current bar's value. |
| **Previous Value** | `close[1]`  | `close[1]`     | References the value 1 bar ago.     |
| **History Access** | `close[10]` | `close[10]`    | References the value 10 bars ago.   |

**PineTS:**

```javascript
// Calculate momentum
let mom = close - close[10];
```

## Conditional Logic [#conditional-logic]

PineTS supports standard JavaScript control flow, which maps to Pine Script's execution model.

| Feature          | Pine Script                   | PineTS (JS/TS)                             | Notes               |
| :--------------- | :---------------------------- | :----------------------------------------- | :------------------ |
| **If Statement** | `if condition`<br />    `...` | `if (condition) {`<br />    `...`<br />`}` | Standard JS syntax. |
| **Ternary**      | `cond ? val1 : val2`          | `cond ? val1 : val2`                       | Standard JS syntax. |

#### Example: Trend Direction [#example-trend-direction]

**Pine Script:**

```javascript
if close > open
    direction := 1
else
    direction := -1
```

**PineTS:**

```javascript
if (close > open) {
    direction = 1;
} else {
    direction = -1;
}
```

### Always brace your `if` / `for` / `while` bodies [#always-brace-your-if--for--while-bodies]

> **The rule:** In the JS callback form, &#x2A;*every control-flow body must be wrapped in `{ ... }`** — even single statements.

JavaScript itself allows brace-less single-statement bodies (`if (cond) stmt;`). PineTS does not. If the body contains any Pine namespace call (`strategy.entry`, `ta.sma`, `plot`, `line.new`, `label.new`, ...), brace-less bodies produce silent bugs.

**Why.** The PineTS transpiler rewrites Pine-style calls into a "compute first, use second" form — `strategy.entry('e', strategy.long, 1)` becomes a few `const p_N = strategy.param(...)` declarations followed by `const temp_N = strategy.entry(...)`. Those `pN` / `temp_N` declarations are **hoisted**: they must be emitted *before* the statement that needs them, and they must land in the smallest enclosing `BlockStatement` so that they execute only when that block does.

The transpiler's hoisting walker only has a hook for `BlockStatement`. When the body of an `if` / `for` / `while` is a plain single statement (brace-less), there is no inner block to catch the hoisted declarations — they fall through to the next enclosing block (typically the parent function body) and end up executing on every bar, ignoring the condition or the loop counter.

Pine source isn't affected because Pine's indentation-scoped bodies are *always* emitted as a `BlockStatement` by the Pine → JS codegen, regardless of how many statements they contain.

#### Example 1 — `if` body [#example-1--if-body]

```javascript
// ❌ WRONG — strategy.entry() fires on EVERY bar.
if ($.idx === 1) strategy.entry('e', strategy.long, 1);

// ✅ CORRECT
if ($.idx === 1) { strategy.entry('e', strategy.long, 1); }
```

What the transpiler emits in the wrong case:

```javascript
const temp_3 = strategy.entry(p2, temp_2, p3);   // ← runs every bar
if ($.idx === 1) temp_3;                          // ← reads value, no-op
```

#### Example 2 — `for` body [#example-2--for-body]

```javascript
// ❌ WRONG — strategy.order() runs ONCE, not 3 times.
for (let k = 0; k < 3; k++) strategy.order('o' + k, strategy.long, 1);

// ✅ CORRECT
for (let k = 0; k < 3; k++) { strategy.order('o' + k, strategy.long, 1); }
```

What the transpiler emits in the wrong case:

```javascript
const p2 = strategy.param('o' + k, ...);          // ← `k` not in scope yet — ReferenceError
const temp_3 = strategy.order(p2, temp_2, p3);    // ← runs once, before the loop
for (let k = 0; k < 3; k++) temp_3;               // ← loop reads the same temp
```

The same applies to `while` bodies and brace-less `else` branches.

> If a single-statement body contains *no* Pine namespace call (pure JS arithmetic, plain variable assignment, a `console.log`, etc.), no hoisting happens and brace-less is harmless. The safe default is to always use braces.

## Built-in Variables [#built-in-variables]

PineTS exposes Pine Script's built-in variables through the `context` object, but usually, you destructure them for easier access.

| Variable        | Pine Script | PineTS (JS/TS)                    |
| :-------------- | :---------- | :-------------------------------- |
| **Close Price** | `close`     | `close` (from `context.data`)     |
| **Open Price**  | `open`      | `open` (from `context.data`)      |
| **High Price**  | `high`      | `high` (from `context.data`)      |
| **Low Price**   | `low`       | `low` (from `context.data`)       |
| **Volume**      | `volume`    | `volume` (from `context.data`)    |
| **Bar Index**   | `bar_index` | `bar_index` (from `context.pine`) |

**PineTS Setup:**

```javascript
const { close, high, low } = context.data;
const { bar_index } = context.pine;
```

## Functions and Namespaces [#functions-and-namespaces]

PineTS organizes built-in functions into namespaces similar to Pine Script v5.

| Namespace              | Pine Script | PineTS (JS/TS) | Example                 |
| :--------------------- | :---------- | :------------- | :---------------------- |
| **Technical Analysis** | `ta.*`      | `ta.*`         | `ta.sma(close, 14)`     |
| **Math**               | `math.*`    | `math.*`       | `math.max(high, low)`   |
| **Request**            | `request.*` | `request.*`    | `request.security(...)` |

**PineTS Setup:**

```javascript
const { ta, math } = context.pine;
// Usage
const sma = ta.sma(close, 14);
```

## Full Example: Parabolic SAR [#full-example-parabolic-sar]

This example demonstrates `var` for state, `if/else` logic, and history access.

**Pine Script:**

```javascript
pine_sar(start, inc, max) =>
    var float result = na
    var float maxMin = na
    var float acceleration = na
    var bool isBelow = false
    bool isFirstTrendBar = false

    if bar_index == 1
        if close > close[1]
            isBelow := true
            maxMin := high
            result := low[1]
        else
            isBelow := false
            maxMin := low
            result := high[1]
        isFirstTrendBar := true
        acceleration := start

    // ... logic continues ...
    result
```

**PineTS:**

```javascript
function pine_sar(start, inc, max) {
    // Use 'var' for state variables (persistent)
    var result = na;
    var maxMin = na;
    var acceleration = na;
    var isBelow = false;

    // Use 'let' for temporary variables (reset every bar)
    let isFirstTrendBar = false;

    if (bar_index == 1) {
        if (close > close[1]) {
            isBelow = true;
            maxMin = high;
            result = low[1];
        } else {
            isBelow = false;
            maxMin = low;
            result = high[1];
        }
        isFirstTrendBar = true;
        acceleration = start;
    }

    // ... logic continues ...

    return result;
}
```


# Array (/developers/pinets/api-coverage/array)



### Creation & Initialization [#creation--initialization]

| Function               | Status | Description                  |
| ---------------------- | ------ | ---------------------------- |
| `array.copy()`         | ✅      | Create copy of array         |
| `array.from()`         | ✅      | Create array from arguments  |
| `array.new_bool()`     | ✅      | Create boolean array         |
| `array.new_box()`      |        | Create box array             |
| `array.new_color()`    |        | Create color array           |
| `array.new_float()`    | ✅      | Create float array           |
| `array.new_int()`      | ✅      | Create int array             |
| `array.new_label()`    |        | Create label array           |
| `array.new_line()`     |        | Create line array            |
| `array.new_linefill()` |        | Create linefill array        |
| `array.new_string()`   | ✅      | Create string array          |
| `array.new_table()`    |        | Create table array           |
| `array.new<type>()`    | ✅      | Create typed array (generic) |

### Element Access [#element-access]

| Function        | Status | Description        |
| --------------- | ------ | ------------------ |
| `array.first()` | ✅      | Get first element  |
| `array.get()`   | ✅      | Get value at index |
| `array.last()`  | ✅      | Get last element   |
| `array.set()`   | ✅      | Set value at index |

### Modification [#modification]

| Function          | Status | Description                  |
| ----------------- | ------ | ---------------------------- |
| `array.clear()`   | ✅      | Remove all elements          |
| `array.fill()`    | ✅      | Fill array with value        |
| `array.insert()`  | ✅      | Insert element at index      |
| `array.pop()`     | ✅      | Remove last element          |
| `array.push()`    | ✅      | Append element to end        |
| `array.remove()`  | ✅      | Remove element at index      |
| `array.reverse()` | ✅      | Reverse order                |
| `array.shift()`   | ✅      | Remove first element         |
| `array.unshift()` | ✅      | Prepend element to beginning |

### Size & Shape [#size--shape]

| Function         | Status | Description        |
| ---------------- | ------ | ------------------ |
| `array.concat()` | ✅      | Concatenate arrays |
| `array.size()`   | ✅      | Get array size     |
| `array.slice()`  | ✅      | Extract subarray   |

### Search & Query [#search--query]

| Function                          | Status | Description               |
| --------------------------------- | ------ | ------------------------- |
| `array.binary_search()`           | ✅      | Binary search             |
| `array.binary_search_leftmost()`  | ✅      | Binary search (leftmost)  |
| `array.binary_search_rightmost()` | ✅      | Binary search (rightmost) |
| `array.includes()`                | ✅      | Check if value exists     |
| `array.indexof()`                 | ✅      | Find first index of value |
| `array.lastindexof()`             | ✅      | Find last index of value  |

### Statistical [#statistical]

| Function             | Status | Description         |
| -------------------- | ------ | ------------------- |
| `array.avg()`        | ✅      | Average of elements |
| `array.covariance()` | ✅      | Covariance          |
| `array.max()`        | ✅      | Maximum value       |
| `array.median()`     | ✅      | Median value        |
| `array.min()`        | ✅      | Minimum value       |
| `array.mode()`       | ✅      | Mode value          |
| `array.range()`      | ✅      | Range of values     |
| `array.stdev()`      | ✅      | Standard deviation  |
| `array.sum()`        | ✅      | Sum of elements     |
| `array.variance()`   | ✅      | Variance            |

### Percentiles [#percentiles]

| Function                                  | Status | Description               |
| ----------------------------------------- | ------ | ------------------------- |
| `array.percentile_linear_interpolation()` | ✅      | Percentile (Linear)       |
| `array.percentile_nearest_rank()`         | ✅      | Percentile (Nearest Rank) |
| `array.percentrank()`                     | ✅      | Percentile rank           |

### Transformation [#transformation]

| Function               | Status | Description          |
| ---------------------- | ------ | -------------------- |
| `array.abs()`          | ✅      | Absolute values      |
| `array.join()`         | ✅      | Join to string       |
| `array.sort()`         | ✅      | Sort array           |
| `array.sort_indices()` | ✅      | Get sorted indices   |
| `array.standardize()`  | ✅      | Standardize elements |

### Logical [#logical]

| Function        | Status | Description                  |
| --------------- | ------ | ---------------------------- |
| `array.every()` | ✅      | Check if all elements match  |
| `array.some()`  | ✅      | Check if any element matches |


# Barstate (/developers/pinets/api-coverage/barstate)



### Bar State Flags [#bar-state-flags]

| Function                          | Status | Description                   |
| --------------------------------- | ------ | ----------------------------- |
| `barstate.isconfirmed`            | ✅      | Bar is confirmed              |
| `barstate.isfirst`                | ✅      | First bar of dataset          |
| `barstate.ishistory`              | ✅      | Bar is historical             |
| `barstate.islast`                 | ✅      | Last bar of dataset           |
| `barstate.islastconfirmedhistory` | ✅      | Last confirmed historical bar |
| `barstate.isnew`                  | ✅      | New bar                       |
| `barstate.isrealtime`             | ✅      | Bar is real-time              |


# Box (/developers/pinets/api-coverage/box)



### Management [#management]

| Function       | Status | Description          |
| -------------- | ------ | -------------------- |
| `box.all`      | ✅      | All boxes collection |
| `box.copy()`   | ✅      | Copy box             |
| `box.delete()` | ✅      | Delete box           |
| `box.new()`    | ✅      | Create new box       |

### Getters [#getters]

| Function           | Status | Description           |
| ------------------ | ------ | --------------------- |
| `box.get_bottom()` | ✅      | Get bottom coordinate |
| `box.get_left()`   | ✅      | Get left coordinate   |
| `box.get_right()`  | ✅      | Get right coordinate  |
| `box.get_top()`    | ✅      | Get top coordinate    |

### Setters [#setters]

| Function                       | Status | Description                   |
| ------------------------------ | ------ | ----------------------------- |
| `box.set_bgcolor()`            | ✅      | Set background color          |
| `box.set_border_color()`       | ✅      | Set border color              |
| `box.set_border_style()`       | ✅      | Set border style              |
| `box.set_border_width()`       | ✅      | Set border width              |
| `box.set_bottom()`             | ✅      | Set bottom coordinate         |
| `box.set_bottom_right_point()` | ✅      | Set bottom-right point        |
| `box.set_extend()`             | ✅      | Set extend mode               |
| `box.set_left()`               | ✅      | Set left coordinate           |
| `box.set_lefttop()`            | ✅      | Set left-top point            |
| `box.set_right()`              | ✅      | Set right coordinate          |
| `box.set_rightbottom()`        | ✅      | Set right-bottom point        |
| `box.set_text()`               | ✅      | Set text                      |
| `box.set_text_color()`         | ✅      | Set text color                |
| `box.set_text_font_family()`   | ✅      | Set text font family          |
| `box.set_text_formatting()`    | ✅      | Set text formatting           |
| `box.set_text_halign()`        | ✅      | Set text horizontal alignment |
| `box.set_text_size()`          | ✅      | Set text size                 |
| `box.set_text_valign()`        | ✅      | Set text vertical alignment   |
| `box.set_text_wrap()`          | ✅      | Set text wrap                 |
| `box.set_top()`                | ✅      | Set top coordinate            |
| `box.set_top_left_point()`     | ✅      | Set top-left point            |
| `box.set_xloc()`               | ✅      | Set x-location                |


# Builtin (/developers/pinets/api-coverage/builtin)



### Variables [#variables]

| Function          | Status | Description                            |
| ----------------- | ------ | -------------------------------------- |
| `bar_index`       | ✅      | Current bar index                      |
| `close`           | ✅      | Close price                            |
| `high`            | ✅      | High price                             |
| `hl2`             | ✅      | Average of high and low                |
| `hlc3`            | ✅      | Average of high, low, and close        |
| `hlcc4`           | ✅      | Average of high, low, close, and close |
| `last_bar_index`  | ✅      | Index of last bar                      |
| `last_bar_time`   | ✅      | Time of last bar                       |
| `low`             | ✅      | Low price                              |
| `na`              | ✅      | Not a number (NaN)                     |
| `ohlc4`           | ✅      | Average of open, high, low, and close  |
| `open`            | ✅      | Open price                             |
| `timenow`         | ✅      | Current time                           |
| `volume`          | ✅      | Volume                                 |
| `ask`             |        | Ask price                              |
| `bid`             |        | Bid price                              |
| `dayofmonth`      | ✅      | Day of month                           |
| `dayofweek`       | ✅      | Day of week                            |
| `hour`            | ✅      | Hour                                   |
| `minute`          | ✅      | Minute                                 |
| `month`           | ✅      | Month                                  |
| `second`          | ✅      | Second                                 |
| `time`            | ✅      | Bar time                               |
| `time_close`      | ✅      | Bar close time                         |
| `time_tradingday` | ✅      | Trading day time                       |
| `weekofyear`      | ✅      | Week of year                           |
| `year`            | ✅      | Year                                   |

### Constants [#constants]

| Function | Status | Description   |
| -------- | ------ | ------------- |
| `false`  | ✅      | Boolean false |
| `true`   | ✅      | Boolean true  |

### Functions [#functions]

| Function           | Status | Description           |
| ------------------ | ------ | --------------------- |
| `indicator()`      | ✅      | Indicator declaration |
| `input()`          | ✅      | Input parameter       |
| `na()`             | ✅      | Check if value is NaN |
| `nz()`             | ✅      | Replace NaN with zero |
| `alert()`          | ✅      | Alert function        |
| `alertcondition()` | ✅      | Alert condition       |
| `bool()`           | ✅      | Boolean conversion    |
| `box()`            | ✅      | Box object            |
| `color()`          | ✅      | Color object          |
| `dayofmonth()`     | ✅      | Day of month function |
| `dayofweek()`      | ✅      | Day of week function  |
| `fill()`           | ✅      | Fill function         |
| `fixnan()`         | ✅      | Fix NaN values        |
| `float()`          | ✅      | Float conversion      |
| `hline()`          | ✅      | Horizontal line       |
| `hour()`           | ✅      | Hour function         |
| `int()`            | ✅      | Integer conversion    |
| `label()`          | ✅      | Label object          |
| `library()`        |        | Library declaration   |
| `line()`           | ✅      | Line object           |
| `linefill()`       | ✅      | Linefill object       |
| `max_bars_back()`  |        | Maximum bars back     |
| `minute()`         | ✅      | Minute function       |
| `month()`          | ✅      | Month function        |
| `second()`         | ✅      | Second function       |
| `strategy()`       |        | Strategy declaration  |
| `string()`         | ✅      | String conversion     |
| `table()`          | ✅      | Table object          |
| `time()`           | ✅      | Time function         |
| `time_close()`     | ✅      | Time close function   |
| `timestamp()`      | ✅      | Timestamp function    |
| `weekofyear()`     | ✅      | Week of year function |
| `year()`           | ✅      | Year function         |
| `runtime.error()`  |        | Runtime error         |


# Chart (/developers/pinets/api-coverage/chart)



### Chart Properties [#chart-properties]

| Function         | Status | Description      |
| ---------------- | ------ | ---------------- |
| `chart.bg_color` | ✅      | Background color |
| `chart.fg_color` | ✅      | Foreground color |

### Chart Type Detection [#chart-type-detection]

| Function              | Status | Description                                   |
| --------------------- | ------ | --------------------------------------------- |
| `chart.is_heikinashi` | ✅      | True on a Heikin Ashi chart (see note below)  |
| `chart.is_kagi`       | ⚠️     | Always `false` — chart type not representable |
| `chart.is_linebreak`  | ⚠️     | Always `false` — chart type not representable |
| `chart.is_pnf`        | ⚠️     | Always `false` — chart type not representable |
| `chart.is_range`      | ⚠️     | Always `false` — chart type not representable |
| `chart.is_renko`      | ⚠️     | Always `false` — chart type not representable |
| `chart.is_standard`   | ✅      | True unless the chart is a non-standard type  |

### Visible Range [#visible-range]

| Function                       | Status | Description            |
| ------------------------------ | ------ | ---------------------- |
| `chart.left_visible_bar_time`  | ✅      | Left visible bar time  |
| `chart.right_visible_bar_time` | ✅      | Right visible bar time |

### Chart Point [#chart-point]

| Function                   | Status | Description             |
| -------------------------- | ------ | ----------------------- |
| `chart.point.copy()`       | ✅      | Copy chart point        |
| `chart.point.from_index()` | ✅      | Create point from index |
| `chart.point.from_time()`  | ✅      | Create point from time  |
| `chart.point.new()`        | ✅      | Create new chart point  |
| `chart.point.now()`        | ✅      | Get current chart point |

### Chart Point Fields [#chart-point-fields]

| Field               | Status | Description        |
| ------------------- | ------ | ------------------ |
| `chart.point.index` | ✅      | Bar index of point |
| `chart.point.price` | ✅      | Price of point     |
| `chart.point.time`  | ✅      | Timestamp of point |

### Notes [#notes]

* **`is_heikinashi` / `is_standard` — THE CHART TYPE IS THE TICKER.** A host declares a Heikin Ashi chart by constructing PineTS with an **extended ticker**: `new PineTS(source, 'BTCUSDT;heikinashi', 'D', …)`. The chart type is derived from that ticker's `;heikinashi` modifier — there is no separate setting. On such a chart, `chart.is_heikinashi` is `true`, `chart.is_standard` is `false`, and `syminfo.tickerid` carries the modifier (so `request.security(syminfo.tickerid, …)` routes chart-typed data requests to the data source — see [Ticker](/developers/pinets/api-coverage/ticker)). **PineTS never transforms bars**: the data source of an extended ticker is expected to serve the chart-type view already (an embedding host that owns the transform, e.g. a charting library); PineTS' bundled providers strip the modifier and serve standard candles.
* **`is_kagi` / `is_renko` / `is_linebreak` / `is_pnf` / `is_range`** — these chart types have no data source that can construct their bars, so the predicates are hardcoded `false`.
* These predicates are Pine **variables** (bare member access, `chart.is_heikinashi` — no call), exposed as getters like the visible-range built-ins below.
* **`left_visible_bar_time` / `right_visible_bar_time`** — In TradingView these reflect the user's UI viewport (what's scrolled into view). PineTS is renderer-agnostic, so by default they fall back to the first/last bar of the loaded `marketData` (i.e. "the full loaded range is the viewport"). Hosts that render PineTS output and want to model a true zoom/pan can override via `PineTS.setVisibleRange(left, right)`. Use `PineTS.usesVisibleRange()` to skip viewport-change re-runs for indicators that don't reference these built-ins; `PineTS.update(code)` is a smart re-run helper that gates on this tag automatically.


# Color (/developers/pinets/api-coverage/color)



### Predefined Colors [#predefined-colors]

| Function        | Status | Description   |
| --------------- | ------ | ------------- |
| `color.aqua`    | ✅      | Aqua color    |
| `color.black`   | ✅      | Black color   |
| `color.blue`    | ✅      | Blue color    |
| `color.fuchsia` | ✅      | Fuchsia color |
| `color.gray`    | ✅      | Gray color    |
| `color.green`   | ✅      | Green color   |
| `color.lime`    | ✅      | Lime color    |
| `color.maroon`  | ✅      | Maroon color  |
| `color.navy`    | ✅      | Navy color    |
| `color.olive`   | ✅      | Olive color   |
| `color.orange`  | ✅      | Orange color  |
| `color.purple`  | ✅      | Purple color  |
| `color.red`     | ✅      | Red color     |
| `color.silver`  | ✅      | Silver color  |
| `color.teal`    | ✅      | Teal color    |
| `color.white`   | ✅      | White color   |
| `color.yellow`  | ✅      | Yellow color  |

### Color Functions [#color-functions]

| Function                | Status | Description                  |
| ----------------------- | ------ | ---------------------------- |
| `color.from_gradient()` | ✅      | Create color from gradient   |
| `color.new()`           | ✅      | Create new color             |
| `color.rgb()`           | ✅      | Create color from RGB values |
| `color.b()`             | ✅      | Get blue component           |
| `color.g()`             | ✅      | Get green component          |
| `color.r()`             | ✅      | Get red component            |
| `color.t()`             | ✅      | Get transparency component   |


# Constants Audit (/developers/pinets/api-coverage/constants-audit)



## Constants Audit — TV vs PineTS String Values [#constants-audit--tv-vs-pinets-string-values]

> Generated 2026-03-01 via `diag/types_audit.pine` on BTCUSDC Weekly

## Mismatched (28) [#mismatched-28]

### shape (12) — TV uses `shape_` prefix + underscored words [#shape-12--tv-uses-shape_-prefix--underscored-words]

| Constant             | TV Value              | PineTS Value   |
| -------------------- | --------------------- | -------------- |
| `shape.arrowdown`    | `shape_arrow_down`    | `arrowdown`    |
| `shape.arrowup`      | `shape_arrow_up`      | `arrowup`      |
| `shape.circle`       | `shape_circle`        | `circle`       |
| `shape.cross`        | `shape_cross`         | `cross`        |
| `shape.diamond`      | `shape_diamond`       | `diamond`      |
| `shape.flag`         | `shape_flag`          | `flag`         |
| `shape.labeldown`    | `shape_label_down`    | `labeldown`    |
| `shape.labelup`      | `shape_label_up`      | `labelup`      |
| `shape.square`       | `shape_square`        | `square`       |
| `shape.triangledown` | `shape_triangle_down` | `triangledown` |
| `shape.triangleup`   | `shape_triangle_up`   | `triangleup`   |
| `shape.xcross`       | `shape_xcross`        | `xcross`       |

### location (5) — TV uses PascalCase [#location-5--tv-uses-pascalcase]

| Constant            | TV Value   | PineTS Value |
| ------------------- | ---------- | ------------ |
| `location.abovebar` | `AboveBar` | `abovebar`   |
| `location.belowbar` | `BelowBar` | `belowbar`   |
| `location.absolute` | `Absolute` | `absolute`   |
| `location.bottom`   | `Bottom`   | `bottom`     |
| `location.top`      | `Top`      | `top`        |

### xloc (2) — TV uses abbreviations [#xloc-2--tv-uses-abbreviations]

| Constant         | TV Value | PineTS Value |
| ---------------- | -------- | ------------ |
| `xloc.bar_index` | `bi`     | `bar_index`  |
| `xloc.bar_time`  | `bt`     | `bar_time`   |

### yloc (3) — TV uses abbreviations [#yloc-3--tv-uses-abbreviations]

| Constant        | TV Value | PineTS Value |
| --------------- | -------- | ------------ |
| `yloc.price`    | `pr`     | `price`      |
| `yloc.abovebar` | `ab`     | `abovebar`   |
| `yloc.belowbar` | `bl`     | `belowbar`   |

### font (2) — TV drops `family_` prefix [#font-2--tv-drops-family_-prefix]

| Constant                | TV Value    | PineTS Value       |
| ----------------------- | ----------- | ------------------ |
| `font.family_default`   | `default`   | `family_default`   |
| `font.family_monospace` | `monospace` | `family_monospace` |

### text (5) — TV drops `align_`/`wrap_` prefix [#text-5--tv-drops-align_wrap_-prefix]

| Constant            | TV Value | PineTS Value   |
| ------------------- | -------- | -------------- |
| `text.align_left`   | `left`   | `align_left`   |
| `text.align_center` | `center` | `align_center` |
| `text.align_right`  | `right`  | `align_right`  |
| `text.wrap_auto`    | `auto`   | `wrap_auto`    |
| `text.wrap_none`    | `none`   | `wrap_none`    |

## Correct (11) [#correct-11]

### size (6) — match as-is [#size-6--match-as-is]

| Constant      | TV Value | PineTS Value |
| ------------- | -------- | ------------ |
| `size.auto`   | `auto`   | `auto`       |
| `size.tiny`   | `tiny`   | `tiny`       |
| `size.small`  | `small`  | `small`      |
| `size.normal` | `normal` | `normal`     |
| `size.large`  | `large`  | `large`      |
| `size.huge`   | `huge`   | `huge`       |

### format (5) — match as-is [#format-5--match-as-is]

| Constant         | TV Value  | PineTS Value |
| ---------------- | --------- | ------------ |
| `format.inherit` | `inherit` | `inherit`    |
| `format.mintick` | `mintick` | `mintick`    |
| `format.percent` | `percent` | `percent`    |
| `format.price`   | `price`   | `price`      |
| `format.volume`  | `volume`  | `volume`     |

## Not tested (opaque / numeric types) [#not-tested-opaque--numeric-types]

| Namespace   | Reason                                                             |
| ----------- | ------------------------------------------------------------------ |
| `display`   | Opaque `plot_simple_display` type — `str.tostring()` not supported |
| `order`     | Numeric `sort_order` type — TV test errors                         |
| `barmerge`  | Opaque `barmerge_gaps`/`barmerge_lookahead` types — TV test errors |
| `currency`  | Already passing all tests                                          |
| `dayofweek` | Already passing all tests                                          |


# API Coverage (/developers/pinets/api-coverage)



## Pine Script API Implementation Progress [#pine-script-api-implementation-progress]

This is a list of all the functions in the Pine Script API and their implementation status.

## Key [#key]

* ✅ Implemented and Tested
* ✔️ Implemented but needs testing
* ❌ Not implemented
* ⏳ In progress

***

* [Input](/developers/pinets/api-coverage/input)
* [Math](/developers/pinets/api-coverage/math)
* [Technical Analysis](/developers/pinets/api-coverage/ta)
* [Array](/developers/pinets/api-coverage/array)
* [Box](/developers/pinets/api-coverage/box)
* [Chart](/developers/pinets/api-coverage/chart)
* [Color](/developers/pinets/api-coverage/color)
* [Label](/developers/pinets/api-coverage/label)
* [Line](/developers/pinets/api-coverage/line)
* [Linefill](/developers/pinets/api-coverage/linefill)
* [Log](/developers/pinets/api-coverage/log)
* [Map](/developers/pinets/api-coverage/map)
* [Matrix](/developers/pinets/api-coverage/matrix)
* [Plot](/developers/pinets/api-coverage/plots)
* [Request](/developers/pinets/api-coverage/request)
* [String](/developers/pinets/api-coverage/str)
* [Strategy](/developers/pinets/api-coverage/strategy)
* [Table](/developers/pinets/api-coverage/table)
* [Syminfo](/developers/pinets/api-coverage/syminfo)
* [Runtime](/developers/pinets/api-coverage/runtime)
* [Polyline](/developers/pinets/api-coverage/polyline)
* [Others](/developers/pinets/api-coverage/others)


# Input (/developers/pinets/api-coverage/input)



### Input Types [#input-types]

| Function            | Status | Description       |
| ------------------- | ------ | ----------------- |
| `input.bool()`      | ✅      | Boolean input     |
| `input.color()`     | ✅      | Color input       |
| `input.enum()`      | ✅      | Enumeration input |
| `input.float()`     | ✅      | Float input       |
| `input.int()`       | ✅      | Integer input     |
| `input.price()`     | ✅      | Price input       |
| `input.session()`   | ✅      | Session input     |
| `input.source()`    | ✅      | Source input      |
| `input.string()`    | ✅      | String input      |
| `input.symbol()`    | ✅      | Symbol input      |
| `input.text_area()` | ✅      | Text area input   |
| `input.time()`      | ✅      | Time input        |
| `input.timeframe()` | ✅      | Timeframe input   |


# Label (/developers/pinets/api-coverage/label)



### Styles [#styles]

| Function                        | Status | Description             |
| ------------------------------- | ------ | ----------------------- |
| `label.style_arrowdown`         | ✅      | Arrow down style        |
| `label.style_arrowup`           | ✅      | Arrow up style          |
| `label.style_circle`            | ✅      | Circle style            |
| `label.style_cross`             | ✅      | Cross style             |
| `label.style_diamond`           | ✅      | Diamond style           |
| `label.style_flag`              | ✅      | Flag style              |
| `label.style_label_center`      | ✅      | Label center style      |
| `label.style_label_down`        | ✅      | Label down style        |
| `label.style_label_left`        | ✅      | Label left style        |
| `label.style_label_lower_left`  | ✅      | Label lower left style  |
| `label.style_label_lower_right` | ✅      | Label lower right style |
| `label.style_label_right`       | ✅      | Label right style       |
| `label.style_label_up`          | ✅      | Label up style          |
| `label.style_label_upper_left`  | ✅      | Label upper left style  |
| `label.style_label_upper_right` | ✅      | Label upper right style |
| `label.style_none`              | ✅      | No style                |
| `label.style_square`            | ✅      | Square style            |
| `label.style_text_outline`      | ✅      | Text outline style      |
| `label.style_triangledown`      | ✅      | Triangle down style     |
| `label.style_triangleup`        | ✅      | Triangle up style       |
| `label.style_xcross`            | ✅      | X-cross style           |

### Management [#management]

| Function         | Status | Description                                                                          |
| ---------------- | ------ | ------------------------------------------------------------------------------------ |
| `label.all`      | ✅      | All labels collection                                                                |
| `label`          | ✅      | Casts na to label                                                                    |
| `label.copy()`   | ✅      | Copy label                                                                           |
| `label.delete()` | ✅      | Delete label                                                                         |
| `label.new()`    | ✅      | Create new label (supports both `x, y` and `point` signatures, with `force_overlay`) |

### Getters [#getters]

| Function           | Status | Description      |
| ------------------ | ------ | ---------------- |
| `label.get_text()` | ✅      | Get label text   |
| `label.get_x()`    | ✅      | Get x coordinate |
| `label.get_y()`    | ✅      | Get y coordinate |

### Setters [#setters]

| Function                       | Status | Description             |
| ------------------------------ | ------ | ----------------------- |
| `label.set_color()`            | ✅      | Set label color         |
| `label.set_point()`            | ✅      | Set label point         |
| `label.set_size()`             | ✅      | Set label size          |
| `label.set_style()`            | ✅      | Set label style         |
| `label.set_text()`             | ✅      | Set label text          |
| `label.set_text_font_family()` |        | Set text font family    |
| `label.set_text_formatting()`  |        | Set text formatting     |
| `label.set_textalign()`        | ✅      | Set text alignment      |
| `label.set_textcolor()`        | ✅      | Set text color          |
| `label.set_tooltip()`          | ✅      | Set tooltip             |
| `label.set_x()`                | ✅      | Set x coordinate        |
| `label.set_xloc()`             | ✅      | Set x-location          |
| `label.set_xy()`               | ✅      | Set x and y coordinates |
| `label.set_y()`                | ✅      | Set y coordinate        |
| `label.set_yloc()`             | ✅      | Set y-location          |


# Line (/developers/pinets/api-coverage/line)



### Styles [#styles]

| Function                 | Status | Description       |
| ------------------------ | ------ | ----------------- |
| `line.style_arrow_both`  | ✅      | Arrow both style  |
| `line.style_arrow_left`  | ✅      | Arrow left style  |
| `line.style_arrow_right` | ✅      | Arrow right style |
| `line.style_dashed`      | ✅      | Dashed style      |
| `line.style_dotted`      | ✅      | Dotted style      |
| `line.style_solid`       | ✅      | Solid style       |

### Management [#management]

| Function        | Status | Description                                                                         |
| --------------- | ------ | ----------------------------------------------------------------------------------- |
| `line.all`      | ✅      | All lines collection                                                                |
| `line()`        | ✅      | Casts na to line                                                                    |
| `line.copy()`   | ✅      | Copy line                                                                           |
| `line.delete()` | ✅      | Delete line                                                                         |
| `line.new()`    | ✅      | Create new line (supports both `x, y` and `point` signatures, with `force_overlay`) |

### Getters [#getters]

| Function           | Status | Description       |
| ------------------ | ------ | ----------------- |
| `line.get_price()` | ✅      | Get line price    |
| `line.get_x1()`    | ✅      | Get x1 coordinate |
| `line.get_x2()`    | ✅      | Get x2 coordinate |
| `line.get_y1()`    | ✅      | Get y1 coordinate |
| `line.get_y2()`    | ✅      | Get y2 coordinate |

### Setters [#setters]

| Function                  | Status | Description         |
| ------------------------- | ------ | ------------------- |
| `line.set_color()`        | ✅      | Set line color      |
| `line.set_extend()`       | ✅      | Set extend mode     |
| `line.set_first_point()`  | ✅      | Set first point     |
| `line.set_second_point()` | ✅      | Set second point    |
| `line.set_style()`        | ✅      | Set line style      |
| `line.set_width()`        | ✅      | Set line width      |
| `line.set_x1()`           | ✅      | Set x1 coordinate   |
| `line.set_x2()`           | ✅      | Set x2 coordinate   |
| `line.set_xloc()`         | ✅      | Set x-location      |
| `line.set_xy1()`          | ✅      | Set xy1 coordinates |
| `line.set_xy2()`          | ✅      | Set xy2 coordinates |
| `line.set_y1()`           | ✅      | Set y1 coordinate   |
| `line.set_y2()`           | ✅      | Set y2 coordinate   |


# Linefill (/developers/pinets/api-coverage/linefill)



### Management [#management]

| Function            | Status | Description              |
| ------------------- | ------ | ------------------------ |
| `linefill.all`      | ✅      | All linefills collection |
| `linefill()`        | ✅      | Casts na to linefill     |
| `linefill.delete()` | ✅      | Delete linefill          |
| `linefill.new()`    | ✅      | Create new linefill      |

### Getters [#getters]

| Function               | Status | Description     |
| ---------------------- | ------ | --------------- |
| `linefill.get_line1()` | ✅      | Get first line  |
| `linefill.get_line2()` | ✅      | Get second line |

### Setters [#setters]

| Function               | Status | Description        |
| ---------------------- | ------ | ------------------ |
| `linefill.set_color()` | ✅      | Set linefill color |


# Log (/developers/pinets/api-coverage/log)



| Function        | Status | Description         |
| --------------- | ------ | ------------------- |
| `log.error()`   | ✅      | Log error message   |
| `log.info()`    | ✅      | Log info message    |
| `log.warning()` | ✅      | Log warning message |


# Map (/developers/pinets/api-coverage/map)



| Function               | Status |
| ---------------------- | ------ |
| `map.clear()`          | ✅      |
| `map.contains()`       | ✅      |
| `map.copy()`           | ✅      |
| `map.get()`            | ✅      |
| `map.keys()`           | ✅      |
| `map.new<type,type>()` | ✅      |
| `map.put()`            | ✅      |
| `map.put_all()`        | ✅      |
| `map.remove()`         | ✅      |
| `map.size()`           | ✅      |
| `map.values()`         | ✅      |

***


# Math (/developers/pinets/api-coverage/math)



## Math Functions [#math-functions]

### Constants [#constants]

| Function    | Status | Description                           |
| ----------- | ------ | ------------------------------------- |
| `math.e`    | ✅      | Euler's number (≈2.71828)             |
| `math.phi`  | ✅      | Golden ratio (≈1.61803)               |
| `math.pi`   | ✅      | Pi constant (≈3.14159)                |
| `math.rphi` | ✅      | Reciprocal of golden ratio (≈0.61803) |

### Basic Operations [#basic-operations]

| Function                  | Status | Description                    |
| ------------------------- | ------ | ------------------------------ |
| `math.abs()`              | ✅      | Absolute value                 |
| `math.ceil()`             | ✅      | Round up to nearest integer    |
| `math.floor()`            | ✅      | Round down to nearest integer  |
| `math.round()`            | ✅      | Round to nearest integer       |
| `math.round_to_mintick()` | ✅      | Round to nearest mintick       |
| `math.sign()`             | ✅      | Sign of a number (-1, 0, or 1) |

### Trigonometric [#trigonometric]

| Function      | Status | Description                   |
| ------------- | ------ | ----------------------------- |
| `math.acos()` | ✔️     | Arc cosine (inverse cosine)   |
| `math.asin()` | ✔️     | Arc sine (inverse sine)       |
| `math.atan()` | ✔️     | Arc tangent (inverse tangent) |
| `math.cos()`  | ✔️     | Cosine                        |
| `math.sin()`  | ✔️     | Sine                          |
| `math.tan()`  | ✔️     | Tangent                       |

### Statistical [#statistical]

| Function     | Status | Description                    |
| ------------ | ------ | ------------------------------ |
| `math.avg()` | ✔️     | Average of all arguments       |
| `math.max()` | ✅      | Maximum value of all arguments |
| `math.min()` | ✅      | Minimum value of all arguments |
| `math.sum()` | ✔️     | Sum of all arguments           |

### Exponential & Logarithmic [#exponential--logarithmic]

| Function       | Status | Description                    |
| -------------- | ------ | ------------------------------ |
| `math.exp()`   | ✔️     | Exponential function (e^x)     |
| `math.log()`   | ✔️     | Natural logarithm (base e)     |
| `math.log10()` | ✅      | Base-10 logarithm              |
| `math.pow()`   | ✔️     | Power function (base^exponent) |
| `math.sqrt()`  | ✔️     | Square root                    |

### Utilities [#utilities]

| Function           | Status | Description                |
| ------------------ | ------ | -------------------------- |
| `math.random()`    | ✔️     | Random number generator    |
| `math.todegrees()` |        | Convert radians to degrees |
| `math.toradians()` |        | Convert degrees to radians |


# Matrix (/developers/pinets/api-coverage/matrix)



### Modification [#modification]

| Function                | Status |
| ----------------------- | ------ |
| `matrix.add_col()`      | ✅      |
| `matrix.add_row()`      | ✅      |
| `matrix.fill()`         | ✅      |
| `matrix.remove_col()`   | ✅      |
| `matrix.remove_row()`   | ✅      |
| `matrix.reverse()`      | ✅      |
| `matrix.swap_columns()` | ✅      |
| `matrix.swap_rows()`    | ✅      |

### Statistical [#statistical]

| Function          | Status |
| ----------------- | ------ |
| `matrix.avg()`    | ✅      |
| `matrix.max()`    | ✅      |
| `matrix.median()` | ✅      |
| `matrix.min()`    | ✅      |
| `matrix.mode()`   | ✅      |
| `matrix.sum()`    | ✅      |

### Element Access [#element-access]

| Function       | Status |
| -------------- | ------ |
| `matrix.col()` | ✅      |
| `matrix.get()` | ✅      |
| `matrix.row()` | ✅      |
| `matrix.set()` | ✅      |

### Size & Shape [#size--shape]

| Function                  | Status |
| ------------------------- | ------ |
| `matrix.columns()`        | ✅      |
| `matrix.elements_count()` | ✅      |
| `matrix.reshape()`        | ✅      |
| `matrix.rows()`           | ✅      |
| `matrix.submatrix()`      | ✅      |

### Operations [#operations]

| Function          | Status |
| ----------------- | ------ |
| `matrix.concat()` | ✅      |
| `matrix.diff()`   | ✅      |
| `matrix.kron()`   | ✅      |
| `matrix.mult()`   | ✅      |
| `matrix.pow()`    | ✅      |

### Creation & Initialization [#creation--initialization]

| Function             | Status |
| -------------------- | ------ |
| `matrix.copy()`      | ✅      |
| `matrix.new<type>()` | ✅      |

### Linear Algebra [#linear-algebra]

| Function                | Status |
| ----------------------- | ------ |
| `matrix.det()`          | ✅      |
| `matrix.eigenvalues()`  | ✅      |
| `matrix.eigenvectors()` | ✅      |
| `matrix.inv()`          | ✅      |
| `matrix.pinv()`         | ✅      |
| `matrix.rank()`         | ✅      |
| `matrix.trace()`        | ✅      |
| `matrix.transpose()`    | ✅      |

### Properties [#properties]

| Function                    | Status |
| --------------------------- | ------ |
| `matrix.is_antidiagonal()`  | ✅      |
| `matrix.is_antisymmetric()` | ✅      |
| `matrix.is_binary()`        | ✅      |
| `matrix.is_diagonal()`      | ✅      |
| `matrix.is_identity()`      | ✅      |
| `matrix.is_square()`        | ✅      |
| `matrix.is_stochastic()`    | ✅      |
| `matrix.is_symmetric()`     | ✅      |
| `matrix.is_triangular()`    | ✅      |
| `matrix.is_zero()`          | ✅      |

### Sorting [#sorting]

| Function        | Status |
| --------------- | ------ |
| `matrix.sort()` | ✅      |


# Others (/developers/pinets/api-coverage/others)



## Date/Time Functions [#datetime-functions]

| Function                   | Status |
| -------------------------- | ------ |
| `dayofmonth()`             |        |
| `dayofweek()`              |        |
| `hour()`                   |        |
| `minute()`                 |        |
| `month()`                  |        |
| `time()`                   |        |
| `time_close()`             |        |
| `timeframe.change()`       |        |
| `timeframe.from_seconds()` |        |
| `timeframe.in_seconds()`   |        |
| `timestamp()`              |        |
| `weekofyear()`             |        |
| `year()`                   |        |

***

## Draw Functions [#draw-functions]

| Function      | Status |
| ------------- | ------ |
| `fill()`      |        |
| `fixnan()`    |        |
| `float()`     |        |
| `hline()`     | ✅      |
| `indicator()` |        |

***

## Alerts [#alerts]

| Function           | Status |
| ------------------ | ------ |
| `alert()`          |        |
| `alertcondition()` |        |


# Plots (/developers/pinets/api-coverage/plots)



| Function       | Status | Description                      |
| -------------- | ------ | -------------------------------- |
| `plot()`       | ✅      | Plot a series                    |
| `plotchar()`   | ✅      | Plot character markers           |
| `plotarrow()`  | ✅      | Plot arrow markers               |
| `plotbar()`    | ✅      | Plot bar chart                   |
| `plotcandle()` | ✅      | Plot candlestick chart           |
| `plotshape()`  | ✅      | Plot shape markers               |
| `barcolor()`   | ✅      | Set bar color                    |
| `bgcolor()`    | ✅      | Set background color             |
| `hline()`      | ✅      | Plot horizontal line             |
| `fill()`       | ✅      | Fill between two plots or hlines |

***

### Plot Title Collisions [#plot-title-collisions]

When multiple `plot()` (or `hline()`) calls share the same `title`, PineTS disambiguates them by appending a `#N` suffix to the plot key. The first plot keeps the plain title, and subsequent collisions are numbered sequentially:

* First `plot(close, "SMA")` → plot key `"SMA"`
* Second `plot(open, "SMA")` → plot key `"SMA#1"`
* Third `plot(high, "SMA")` → plot key `"SMA#2"`

<Warning>
  Using duplicate plot titles is **not recommended**. The `#N` suffix ordering depends on execution order and may lead to fragile references. Always prefer unique titles for each plot. A more elegant solution for this case will be provided in a future version of PineTS.
</Warning>


# Polyline (/developers/pinets/api-coverage/polyline)



### Other [#other]

| Function            | Status | Description              |
| ------------------- | ------ | ------------------------ |
| `polyline.all`      | ✅      | All polylines collection |
| `polyline.delete()` | ✅      | Delete polyline          |
| `polyline.new()`    | ✅      | Create new polyline      |


# Request (/developers/pinets/api-coverage/request)



### Data Requests [#data-requests]

| Function                      | Status | Description                  |
| ----------------------------- | ------ | ---------------------------- |
| `request.security()`          | ✅      | Request security data        |
| `request.security_lower_tf()` | ✅      | Request lower timeframe data |
| `request.currency_rate()`     |        | Request currency rate        |
| `request.dividends()`         |        | Request dividends data       |
| `request.earnings()`          |        | Request earnings data        |
| `request.economic()`          |        | Request economic data        |
| `request.financial()`         |        | Request financial data       |
| `request.quandl()`            |        | Request Quandl data          |
| `request.seed()`              |        | Request seed data            |
| `request.splits()`            |        | Request splits data          |

### Notes [#notes]

* **Chart-type routing (extended tickers)** — the symbol argument may carry a chart-type modifier (`"BTCUSDT;heikinashi"`, built by `ticker.heikinashi()` or inherited from `syminfo.tickerid` on a Heikin Ashi chart; see [Ticker](/developers/pinets/api-coverage/ticker)). The modifier rides through to the data source untouched: a host data source that owns the transform serves derived bars; PineTS' bundled providers strip it and serve standard candles. An empty-string symbol (`""`) resolves to the chart's own ticker, **modifier included**.
* **Same-timeframe shortcut is chart-type aware** — `request.security(sym, tf, expr)` with the chart's own symbol *and* timeframe evaluates the expression against the chart's series directly (no secondary context). "Same symbol" requires the chart-type modifier to match too: on a Heikin Ashi chart, `security(syminfo.tickerid, <chart tf>, close)` shortcuts to the chart's (Heikin Ashi) series, while `security(ticker.standard(syminfo.tickerid), <chart tf>, close)` builds a secondary context that fetches STANDARD data.


# Runtime (/developers/pinets/api-coverage/runtime)



| Function          | Status |
| ----------------- | ------ |
| `runtime.error()` |        |


# Session (/developers/pinets/api-coverage/session)



### Session Flags [#session-flags]

| Function                     | Status | Description                  |
| ---------------------------- | ------ | ---------------------------- |
| `session.isfirstbar`         |        | First bar of session         |
| `session.isfirstbar_regular` |        | First bar of regular session |
| `session.islastbar`          |        | Last bar of session          |
| `session.islastbar_regular`  |        | Last bar of regular session  |
| `session.ismarket`           |        | Market session               |
| `session.ispostmarket`       |        | Post-market session          |
| `session.ispremarket`        |        | Pre-market session           |

### Session Constants [#session-constants]

| Function           | Status | Description               |
| ------------------ | ------ | ------------------------- |
| `session.extended` |        | Extended session constant |
| `session.regular`  |        | Regular session constant  |


# String (/developers/pinets/api-coverage/str)



### Query [#query]

| Function           | Status | Description                        |
| ------------------ | ------ | ---------------------------------- |
| `str.contains()`   | ✅      | Check if string contains substring |
| `str.endswith()`   | ✅      | Check if string ends with suffix   |
| `str.length()`     | ✅      | Get string length                  |
| `str.match()`      | ✅      | Match string against regex         |
| `str.pos()`        | ✅      | Find position of substring         |
| `str.startswith()` | ✅      | Check if string starts with prefix |

### Formatting [#formatting]

| Function            | Status | Description                  |
| ------------------- | ------ | ---------------------------- |
| `str.format()`      | ✅      | Format string with arguments |
| `str.format_time()` | ❌      | Format time value            |

### Transformation [#transformation]

| Function            | Status | Description              |
| ------------------- | ------ | ------------------------ |
| `str.lower()`       | ✅      | Convert to lowercase     |
| `str.repeat()`      | ✅      | Repeat string            |
| `str.replace()`     | ✅      | Replace first occurrence |
| `str.replace_all()` | ✅      | Replace all occurrences  |
| `str.trim()`        | ✅      | Remove whitespace        |
| `str.upper()`       | ✅      | Convert to uppercase     |

### Parsing [#parsing]

| Function          | Status | Description             |
| ----------------- | ------ | ----------------------- |
| `str.split()`     |        | Split string into array |
| `str.substring()` |        | Extract substring       |

### Conversion [#conversion]

| Function         | Status | Description             |
| ---------------- | ------ | ----------------------- |
| `str.tonumber()` | ✅      | Parse string to number  |
| `str.tostring()` | ✅      | Convert value to string |


# Strategy (/developers/pinets/api-coverage/strategy)



### Account Info [#account-info]

| Function                            | Status | Description              |
| ----------------------------------- | ------ | ------------------------ |
| `strategy.account_currency`         | ✅      | Account currency         |
| `strategy.equity`                   | ✅      | Account equity           |
| `strategy.grossloss`                | ✅      | Gross loss               |
| `strategy.grossloss_percent`        | ✅      | Gross loss percentage    |
| `strategy.grossprofit`              | ✅      | Gross profit             |
| `strategy.grossprofit_percent`      | ✅      | Gross profit percentage  |
| `strategy.initial_capital`          | ✅      | Initial capital          |
| `strategy.margin_liquidation_price` | ✅      | Margin liquidation price |
| `strategy.netprofit`                | ✅      | Net profit               |
| `strategy.netprofit_percent`        | ✅      | Net profit percentage    |

### Trade Statistics [#trade-statistics]

| Function                             | Status | Description                   |
| ------------------------------------ | ------ | ----------------------------- |
| `strategy.avg_losing_trade`          | ✅      | Average losing trade          |
| `strategy.avg_losing_trade_percent`  | ✅      | Average losing trade percent  |
| `strategy.avg_trade`                 | ✅      | Average trade                 |
| `strategy.avg_trade_percent`         | ✅      | Average trade percent         |
| `strategy.avg_winning_trade`         | ✅      | Average winning trade         |
| `strategy.avg_winning_trade_percent` | ✅      | Average winning trade percent |
| `strategy.closedtrades`              | ✅      | Number of closed trades       |
| `strategy.eventrades`                | ✅      | Number of even trades         |
| `strategy.losstrades`                | ✅      | Number of losing trades       |
| `strategy.opentrades`                | ✅      | Number of open trades         |
| `strategy.wintrades`                 | ✅      | Number of winning trades      |

### Closed Trades [#closed-trades]

| Function                                       | Status | Description              |
| ---------------------------------------------- | ------ | ------------------------ |
| `strategy.closedtrades.first_index`            | ✅      | First closed trade index |
| `strategy.closedtrades.commission()`           | ✅      | Get commission           |
| `strategy.closedtrades.entry_bar_index()`      | ✅      | Get entry bar index      |
| `strategy.closedtrades.entry_comment()`        | ✅      | Get entry comment        |
| `strategy.closedtrades.entry_id()`             | ✅      | Get entry ID             |
| `strategy.closedtrades.entry_price()`          | ✅      | Get entry price          |
| `strategy.closedtrades.entry_time()`           | ✅      | Get entry time           |
| `strategy.closedtrades.exit_bar_index()`       | ✅      | Get exit bar index       |
| `strategy.closedtrades.exit_comment()`         | ✅      | Get exit comment         |
| `strategy.closedtrades.exit_id()`              | ✅      | Get exit ID              |
| `strategy.closedtrades.exit_price()`           | ✅      | Get exit price           |
| `strategy.closedtrades.exit_time()`            | ✅      | Get exit time            |
| `strategy.closedtrades.max_drawdown()`         | ✅      | Get max drawdown         |
| `strategy.closedtrades.max_drawdown_percent()` | ✅      | Get max drawdown percent |
| `strategy.closedtrades.max_runup()`            | ✅      | Get max runup            |
| `strategy.closedtrades.max_runup_percent()`    | ✅      | Get max runup percent    |
| `strategy.closedtrades.profit()`               | ✅      | Get profit               |
| `strategy.closedtrades.profit_percent()`       | ✅      | Get profit percent       |
| `strategy.closedtrades.size()`                 | ✅      | Get size                 |

### Drawdown & Runup [#drawdown--runup]

| Function                            | Status | Description                |
| ----------------------------------- | ------ | -------------------------- |
| `strategy.max_contracts_held_all`   | ✅      | Max contracts held (all)   |
| `strategy.max_contracts_held_long`  | ✅      | Max contracts held (long)  |
| `strategy.max_contracts_held_short` | ✅      | Max contracts held (short) |
| `strategy.max_drawdown`             | ✅      | Maximum drawdown           |
| `strategy.max_drawdown_percent`     | ✅      | Maximum drawdown percent   |
| `strategy.max_runup`                | ✅      | Maximum runup              |
| `strategy.max_runup_percent`        | ✅      | Maximum runup percent      |

### Position Info [#position-info]

| Function                       | Status | Description            |
| ------------------------------ | ------ | ---------------------- |
| `strategy.openprofit`          | ✅      | Open profit            |
| `strategy.openprofit_percent`  | ✅      | Open profit percent    |
| `strategy.position_avg_price`  | ✅      | Position average price |
| `strategy.position_entry_name` | ✅      | Position entry name    |
| `strategy.position_size`       | ✅      | Position size          |

### Open Trades [#open-trades]

| Function                                     | Status | Description              |
| -------------------------------------------- | ------ | ------------------------ |
| `strategy.opentrades.capital_held`           | ✅      | Capital held             |
| `strategy.opentrades.commission()`           | ✅      | Get commission           |
| `strategy.opentrades.entry_bar_index()`      | ✅      | Get entry bar index      |
| `strategy.opentrades.entry_comment()`        | ✅      | Get entry comment        |
| `strategy.opentrades.entry_id()`             | ✅      | Get entry ID             |
| `strategy.opentrades.entry_price()`          | ✅      | Get entry price          |
| `strategy.opentrades.entry_time()`           | ✅      | Get entry time           |
| `strategy.opentrades.max_drawdown()`         | ✅      | Get max drawdown         |
| `strategy.opentrades.max_drawdown_percent()` | ✅      | Get max drawdown percent |
| `strategy.opentrades.max_runup()`            | ✅      | Get max runup            |
| `strategy.opentrades.max_runup_percent()`    | ✅      | Get max runup percent    |
| `strategy.opentrades.profit()`               | ✅      | Get profit               |
| `strategy.opentrades.profit_percent()`       | ✅      | Get profit percent       |
| `strategy.opentrades.size()`                 | ✅      | Get size                 |

### Constants [#constants]

| Function                     | Status | Description                |
| ---------------------------- | ------ | -------------------------- |
| `strategy.cash`              | ✅      | Cash constant              |
| `strategy.fixed`             | ✅      | Fixed constant             |
| `strategy.long`              | ✅      | Long constant              |
| `strategy.percent_of_equity` | ✅      | Percent of equity constant |
| `strategy.short`             | ✅      | Short constant             |

### Commission [#commission]

| Function                                | Status | Description        |
| --------------------------------------- | ------ | ------------------ |
| `strategy.commission.cash_per_contract` | ✅      | Cash per contract  |
| `strategy.commission.cash_per_order`    | ✅      | Cash per order     |
| `strategy.commission.percent`           | ✅      | Commission percent |

### Direction [#direction]

| Function                   | Status | Description     |
| -------------------------- | ------ | --------------- |
| `strategy.direction.all`   | ✅      | All directions  |
| `strategy.direction.long`  | ✅      | Long direction  |
| `strategy.direction.short` | ✅      | Short direction |

### OCA [#oca]

| Function              | Status | Description |
| --------------------- | ------ | ----------- |
| `strategy.oca.cancel` | ✅      | OCA cancel  |
| `strategy.oca.none`   | ✅      | OCA none    |
| `strategy.oca.reduce` | ✅      | OCA reduce  |

### Order Management [#order-management]

| Function                | Status | Description       |
| ----------------------- | ------ | ----------------- |
| `strategy.cancel()`     | ✅      | Cancel order      |
| `strategy.cancel_all()` | ✅      | Cancel all orders |

### Position Management [#position-management]

| Function               | Status | Description         |
| ---------------------- | ------ | ------------------- |
| `strategy.close()`     | ✅      | Close position      |
| `strategy.close_all()` | ✅      | Close all positions |
| `strategy.entry()`     | ✅      | Enter position      |
| `strategy.exit()`      | ✅      | Exit position       |
| `strategy.order()`     | ✅      | Place order         |

### Conversion [#conversion]

| Function                        | Status | Description            |
| ------------------------------- | ------ | ---------------------- |
| `strategy.convert_to_account()` | ✅      | Convert to account     |
| `strategy.convert_to_symbol()`  | ✅      | Convert to symbol      |
| `strategy.default_entry_qty()`  | ✅      | Default entry quantity |

### Risk Management [#risk-management]

| Function                                     | Status | Description                |
| -------------------------------------------- | ------ | -------------------------- |
| `strategy.risk.allow_entry_in()`             | ✅      | Allow entry in             |
| `strategy.risk.max_cons_loss_days()`         | ✅      | Max consecutive loss days  |
| `strategy.risk.max_drawdown()`               | ✅      | Max drawdown               |
| `strategy.risk.max_intraday_filled_orders()` | ✅      | Max intraday filled orders |
| `strategy.risk.max_intraday_loss()`          | ✅      | Max intraday loss          |
| `strategy.risk.max_position_size()`          | ✅      | Max position size          |

### Notes [#notes]

The entire `strategy.*` namespace is implemented and matches TV's surface 1:1. Of the 31 TV oracle scripts in `Automations/PineTS/pinescripts/strategy/`, 12 currently match TradingView to comparator-epsilon (precision 3, eps 0.001001). The remaining 18 produce valid output that diverges from TV in specific numeric fields — these are precision/semantics differences that need iterative refinement:

* **`strategy.margin_liquidation_price`** — PineTS uses a simple "equity hits zero" approximation; TV's broker liquidation formula differs in detail. Affects `account_props.pine`.
* **`strategy.convert_to_account` / `convert_to_symbol`** — identity passthrough for same-currency cases. TV may return `na` when symbol/account currencies differ even nominally (e.g. BTCUSDC's USDC vs USD). Affects `conversion.pine`.
* **OCA enforcement (cancel/reduce semantics)** — order objects carry `oca_name` / `oca_type` fields, but the engine does not yet auto-cancel or reduce siblings on fill. Deferred. Affects `oca_groups.pine`.
* **Commission rounding** — per-leg charges may differ from TV by sub-cent rounding in edge cases. Affects `commission_slippage.pine` and `commission_types.pine` in the second-decimal place.
* **Per-trade max\_drawdown / max\_runup** — PineTS tracks intra-bar high/low excursions, but TV's accounting includes the entry price's pre-fill open and exit-bar close in subtly different ways for trades that open and close in adjacent bars. Affects `closedtrades_full.pine`, `opentrades_full.pine`.

`oca_groups.pine` is the explicit Phase 7 deferral. All others are iteration items for follow-up work.


# Syminfo (/developers/pinets/api-coverage/syminfo)



Symbol information namespace providing metadata about the current trading symbol.

### Symbol Identification [#symbol-identification]

| Constant                   | Status | Description                                                                                                                                               |
| -------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `syminfo.current_contract` | ✅      | Contract name for futures (e.g., "Perpetual")                                                                                                             |
| `syminfo.description`      | ✅      | Human-readable description (e.g., "BTC / USDT Perpetual")                                                                                                 |
| `syminfo.isin`             | ✅      | ISIN code (empty for crypto)                                                                                                                              |
| `syminfo.main_tickerid`    | ✅      | Main ticker identifier                                                                                                                                    |
| `syminfo.prefix`           | ✅      | Exchange identifier (e.g., "BINANCE")                                                                                                                     |
| `syminfo.root`             | ✅      | Base asset/root symbol (e.g., "BTC")                                                                                                                      |
| `syminfo.ticker`           | ✅      | Symbol name (e.g., "BTCUSDT", "BTCUSDT.P")                                                                                                                |
| `syminfo.tickerid`         | ✅      | Exchange:Symbol format (e.g., "BINANCE:BTCUSDT"); carries the chart-type modifier on a non-standard chart (e.g., "BINANCE:BTCUSDT;heikinashi" — see note) |
| `syminfo.type`             | ✅      | Instrument type ("crypto" or "futures")                                                                                                                   |
| `syminfo.prefix()`         |        | Prefix function                                                                                                                                           |
| `syminfo.ticker()`         |        | Ticker function                                                                                                                                           |

### Currency & Location [#currency--location]

| Constant               | Status | Description                            |
| ---------------------- | ------ | -------------------------------------- |
| `syminfo.basecurrency` | ✅      | Base currency (e.g., "BTC")            |
| `syminfo.currency`     | ✅      | Quote currency (e.g., "USDT")          |
| `syminfo.timezone`     | ✅      | Timezone (always "Etc/UTC" for crypto) |
| `syminfo.country`      | ✅      | Country code (empty for crypto)        |

### Price & Contract Info [#price--contract-info]

| Constant              | Status | Description                                    |
| --------------------- | ------ | ---------------------------------------------- |
| `syminfo.mintick`     | ✅      | Minimum price increment (from Binance filters) |
| `syminfo.pricescale`  | ✅      | Price scale (inverse of tick size)             |
| `syminfo.minmove`     | ✅      | Minimum movement (always 1)                    |
| `syminfo.pointvalue`  | ✅      | Point value/contract multiplier                |
| `syminfo.mincontract` | ✅      | Minimum contract size (from Binance filters)   |

### Session & Market [#session--market]

| Constant                  | Status | Description                                                    |
| ------------------------- | ------ | -------------------------------------------------------------- |
| `syminfo.session`         | ✅      | Trading session (always "24x7" for crypto)                     |
| `syminfo.volumetype`      | ✅      | Volume type (always "base" for crypto)                         |
| `syminfo.expiration_date` | ✅      | Contract expiration (0 for perpetuals, timestamp for delivery) |

### Company Data [#company-data]

| Constant                           | Status | Description                                        |
| ---------------------------------- | ------ | -------------------------------------------------- |
| `syminfo.employees`                | ✅      | Number of employees (N/A for crypto, returns 0)    |
| `syminfo.industry`                 | ✅      | Industry sector (N/A for crypto, returns "")       |
| `syminfo.sector`                   | ✅      | Market sector (N/A for crypto, returns "")         |
| `syminfo.shareholders`             | ✅      | Number of shareholders (N/A for crypto, returns 0) |
| `syminfo.shares_outstanding_float` | ✅      | Float shares (N/A for crypto, returns 0)           |
| `syminfo.shares_outstanding_total` | ✅      | Total shares (N/A for crypto, returns 0)           |

### Analyst Ratings [#analyst-ratings]

| Constant                              | Status | Description                                             |
| ------------------------------------- | ------ | ------------------------------------------------------- |
| `syminfo.recommendations_buy`         | ✅      | Buy recommendations (N/A for crypto, returns 0)         |
| `syminfo.recommendations_buy_strong`  | ✅      | Strong buy recommendations (N/A for crypto, returns 0)  |
| `syminfo.recommendations_date`        | ✅      | Recommendations date (N/A for crypto, returns 0)        |
| `syminfo.recommendations_hold`        | ✅      | Hold recommendations (N/A for crypto, returns 0)        |
| `syminfo.recommendations_sell`        | ✅      | Sell recommendations (N/A for crypto, returns 0)        |
| `syminfo.recommendations_sell_strong` | ✅      | Strong sell recommendations (N/A for crypto, returns 0) |
| `syminfo.recommendations_total`       | ✅      | Total recommendations (N/A for crypto, returns 0)       |

### Price Targets [#price-targets]

| Constant                         | Status | Description                                      |
| -------------------------------- | ------ | ------------------------------------------------ |
| `syminfo.target_price_average`   | ✅      | Average price target (N/A for crypto, returns 0) |
| `syminfo.target_price_date`      | ✅      | Price target date (N/A for crypto, returns 0)    |
| `syminfo.target_price_estimates` | ✅      | Number of estimates (N/A for crypto, returns 0)  |
| `syminfo.target_price_high`      | ✅      | High price target (N/A for crypto, returns 0)    |
| `syminfo.target_price_low`       | ✅      | Low price target (N/A for crypto, returns 0)     |
| `syminfo.target_price_median`    | ✅      | Median price target (N/A for crypto, returns 0)  |

## Implementation Notes [#implementation-notes]

### Chart-Type Modifier in `tickerid` [#chart-type-modifier-in-tickerid]

On a non-standard chart — declared by constructing PineTS with an extended ticker, e.g. `new PineTS(source, 'BTCUSDT;heikinashi', 'D', …)` — `syminfo.tickerid` carries the chart-type suffix (`"BINANCE:BTCUSDT;heikinashi"`), matching TradingView's behavior of encoding the chart type into the ticker id. `syminfo.ticker` stays clean. `ticker.standard(syminfo.tickerid)` strips the modifier; passing the modified `tickerid` to `request.security` routes a chart-typed data request to the data source (see [Ticker](/developers/pinets/api-coverage/ticker) and [Chart](/developers/pinets/api-coverage/chart)).

### Binance Provider [#binance-provider]

The `BinanceProvider` implementation supports:

* **Spot Markets**: Standard symbols like `BTCUSDT`, `ETHUSDT`
* **Perpetual Futures**: Symbols with `.P` suffix like `BTCUSDT.P`, `ETHUSDT.P`
* **Delivery Futures**: Symbols with `_` like `BTCUSD_210625`

### Market Type Detection [#market-type-detection]

* Symbols ending with `.P` → Perpetual futures (uses `fapi.binance.com`)
* Symbols containing `_` → Delivery futures (uses `dapi.binance.com`)
* Other symbols → Spot market (uses `api.binance.com`)


# Technical Analysis (/developers/pinets/api-coverage/ta)



### Moving Averages [#moving-averages]

| Function      | Status | Description                              |
| ------------- | ------ | ---------------------------------------- |
| `ta.vwap`     | ✅      | Volume Weighted Average Price (variable) |
| `ta.alma()`   | ✅      | Arnaud Legoux Moving Average             |
| `ta.ema()`    | ✅      | Exponential Moving Average               |
| `ta.hma()`    | ✅      | Hull Moving Average                      |
| `ta.linreg()` | ✅      | Linear Regression                        |
| `ta.rma()`    | ✅      | Rolling/Running Moving Average           |
| `ta.sma()`    | ✅      | Simple Moving Average                    |
| `ta.swma()`   | ✅      | Symmetrically Weighted Moving Average    |
| `ta.vwap()`   | ✅      | Volume Weighted Average Price (function) |
| `ta.vwma()`   | ✅      | Volume Weighted Moving Average           |
| `ta.wma()`    | ✅      | Weighted Moving Average                  |

### Oscillators & Momentum [#oscillators--momentum]

| Function      | Status | Description                           |
| ------------- | ------ | ------------------------------------- |
| `ta.cci()`    | ✅      | Commodity Channel Index               |
| `ta.change()` | ✅      | Price Change                          |
| `ta.cmo()`    | ✅      | Chande Momentum Oscillator            |
| `ta.cog()`    | ✅      | Center of Gravity                     |
| `ta.macd()`   | ✅      | Moving Average Convergence Divergence |
| `ta.mfi()`    | ✅      | Money Flow Index                      |
| `ta.mom()`    | ✅      | Momentum                              |
| `ta.roc()`    | ✅      | Rate of Change                        |
| `ta.rsi()`    | ✅      | Relative Strength Index               |
| `ta.stoch()`  | ✅      | Stochastic Oscillator                 |
| `ta.tsi()`    | ✅      | True Strength Index                   |
| `ta.wpr()`    | ✅      | Williams %R                           |

### Volatility & Range [#volatility--range]

| Function        | Status | Description             |
| --------------- | ------ | ----------------------- |
| `ta.tr`         | ✅      | True Range (variable)   |
| `ta.atr()`      | ✅      | Average True Range      |
| `ta.bb()`       | ✅      | Bollinger Bands         |
| `ta.bbw()`      | ✅      | Bollinger Bands Width   |
| `ta.dev()`      | ✅      | Mean Absolute Deviation |
| `ta.kc()`       | ✅      | Keltner Channels        |
| `ta.kcw()`      | ✅      | Keltner Channels Width  |
| `ta.range()`    | ✅      | Range                   |
| `ta.stdev()`    | ✅      | Standard Deviation      |
| `ta.tr()`       | ✅      | True Range (function)   |
| `ta.variance()` | ✅      | Variance                |

### Volume Indicators [#volume-indicators]

| Function     | Status | Description                                            |
| ------------ | ------ | ------------------------------------------------------ |
| `ta.accdist` | ✅      | Accumulation/Distribution (variable)                   |
| `ta.iii`     | ✅      | Intraday Intensity Index (variable)                    |
| `ta.nvi`     | ✅      | Negative Volume Index (variable)                       |
| `ta.obv`     | ✅      | On-Balance Volume (variable)                           |
| `ta.pvi`     | ✅      | Positive Volume Index (variable)                       |
| `ta.pvt`     | ✅      | Price-Volume Trend (variable)                          |
| `ta.wad`     | ✅      | Williams Accumulation/Distribution (variable)          |
| `ta.wvad`    | ✅      | Williams Variable Accumulation/Distribution (variable) |

### Utility Functions [#utility-functions]

| Function         | Status | Description              |
| ---------------- | ------ | ------------------------ |
| `ta.barssince()` | ✅      | Bars Since Condition     |
| `ta.cum()`       | ✅      | Cumulative Sum           |
| `ta.rci()`       | ✅      | Rank Correlation Index   |
| `ta.valuewhen()` | ✅      | Value When Condition Met |

### Statistical Functions [#statistical-functions]

| Function                               | Status | Description               |
| -------------------------------------- | ------ | ------------------------- |
| `ta.correlation()`                     | ✅      | Correlation Coefficient   |
| `ta.highest()`                         | ✅      | Highest Value             |
| `ta.highestbars()`                     | ✅      | Bars Since Highest        |
| `ta.lowest()`                          | ✅      | Lowest Value              |
| `ta.lowestbars()`                      | ✅      | Bars Since Lowest         |
| `ta.max()`                             | ✅      | Maximum Value             |
| `ta.median()`                          | ✅      | Median Value              |
| `ta.min()`                             | ✅      | Minimum Value             |
| `ta.mode()`                            | ✅      | Mode Value                |
| `ta.percentile_linear_interpolation()` | ✅      | Percentile (Linear)       |
| `ta.percentile_nearest_rank()`         | ✅      | Percentile (Nearest Rank) |
| `ta.percentrank()`                     | ✅      | Percentile Rank           |

### Trend Analysis [#trend-analysis]

| Function          | Status | Description                        |
| ----------------- | ------ | ---------------------------------- |
| `ta.cross()`      | ✅      | Cross Detection (either direction) |
| `ta.crossover()`  | ✅      | Crossover Detection                |
| `ta.crossunder()` | ✅      | Crossunder Detection               |
| `ta.dmi()`        | ✅      | Directional Movement Index         |
| `ta.falling()`    | ✅      | Falling Trend Detection            |
| `ta.rising()`     | ✅      | Rising Trend Detection             |
| `ta.sar()`        | ✅      | Parabolic SAR                      |
| `ta.supertrend()` | ✅      | SuperTrend Indicator               |

### Support & Resistance [#support--resistance]

| Function                  | Status | Description          |
| ------------------------- | ------ | -------------------- |
| `ta.pivot_point_levels()` | ✅      | Pivot Point Levels   |
| `ta.pivothigh()`          | ✅      | Pivot High Detection |
| `ta.pivotlow()`           | ✅      | Pivot Low Detection  |


# Table (/developers/pinets/api-coverage/table)



### Cell Operations [#cell-operations]

| Function                            | Status | Description                    |
| ----------------------------------- | ------ | ------------------------------ |
| `table.cell()`                      | ✅      | Get cell                       |
| `table.cell_set_bgcolor()`          | ✅      | Set cell background color      |
| `table.cell_set_height()`           | ✅      | Set cell height                |
| `table.cell_set_text()`             | ✅      | Set cell text                  |
| `table.cell_set_text_color()`       | ✅      | Set cell text color            |
| `table.cell_set_text_font_family()` | ✅      | Set cell text font family      |
| `table.cell_set_text_formatting()`  |        | Set cell text formatting       |
| `table.cell_set_text_halign()`      | ✅      | Set cell text horizontal align |
| `table.cell_set_text_size()`        | ✅      | Set cell text size             |
| `table.cell_set_text_valign()`      | ✅      | Set cell text vertical align   |
| `table.cell_set_tooltip()`          | ✅      | Set cell tooltip               |
| `table.cell_set_width()`            | ✅      | Set cell width                 |
| `table.merge_cells()`               | ✅      | Merge cells                    |

### Management [#management]

| Function         | Status | Description           |
| ---------------- | ------ | --------------------- |
| `table()`        |        | Casts na to table     |
| `table.all`      | ✅      | All tables collection |
| `table.clear()`  | ✅      | Clear table           |
| `table.delete()` | ✅      | Delete table          |
| `table.new()`    | ✅      | Create new table      |

### Table Settings [#table-settings]

| Function                   | Status | Description          |
| -------------------------- | ------ | -------------------- |
| `table.set_bgcolor()`      | ✅      | Set background color |
| `table.set_border_color()` | ✅      | Set border color     |
| `table.set_border_width()` | ✅      | Set border width     |
| `table.set_frame_color()`  | ✅      | Set frame color      |
| `table.set_frame_width()`  | ✅      | Set frame width      |
| `table.set_position()`     | ✅      | Set table position   |


# Ticker (/developers/pinets/api-coverage/ticker)



### Ticker Functions [#ticker-functions]

| Function               | Status | Description                  |
| ---------------------- | ------ | ---------------------------- |
| `ticker.heikinashi()`  | ✅      | Create Heikin Ashi ticker    |
| `ticker.inherit()`     | ✅      | Inherit ticker               |
| `ticker.kagi()`        | ✅      | Create Kagi ticker           |
| `ticker.linebreak()`   | ✅      | Create Line Break ticker     |
| `ticker.modify()`      | ✅      | Modify ticker                |
| `ticker.new()`         | ✅      | Create new ticker            |
| `ticker.pointfigure()` | ✅      | Create Point & Figure ticker |
| `ticker.renko()`       | ✅      | Create Renko ticker          |
| `ticker.standard()`    | ✅      | Create standard ticker       |

### Notes [#notes]

#### Extended tickers — the chart-type modifier [#extended-tickers--the-chart-type-modifier]

Non-standard chart types travel **in the ticker id** as a `";modifier"` suffix (the *extended ticker*): `"BINANCE:BTCUSDT;heikinashi"`. This is PineTS' equivalent of TradingView's encoded chart-type ticker IDs, and it is the **single source of truth** for the chart type (see [Chart](/developers/pinets/api-coverage/chart)):

* **`ticker.heikinashi(sym)`** → `"sym;heikinashi"` (idempotent). Passed to `request.security`, the extended ticker rides through to the data source: a host data source that owns the Heikin Ashi transform serves derived bars; PineTS' **bundled providers strip the modifier** and serve standard candles (standalone no-op — PineTS never synthesises bars).
* **`ticker.standard(sym?)`** → strips any chart-type modifier. On a Heikin Ashi chart, `ticker.standard(syminfo.tickerid)` turns `"…;heikinashi"` back into the plain ticker, so the request fetches STANDARD candles (the opt-out).
* **`ticker.inherit(from, sym)`** → propagates the chart-type modifier from `from` onto `sym` (`inherit(syminfo.tickerid, "ETHUSDT")` on an HA chart → `"ETHUSDT;heikinashi"`). Other modifier kinds (session, adjustment) are dropped, as before.

#### Other notes [#other-notes]

* **`inherit`, `new`, `modify`, `standard`** — return ticker-ID strings that match TradingView's output exactly for the common "no extra modifiers" case (the dominant real-world usage). When non-default `adjustment` / `backadjustment` / `settlement_as_close` values are passed, TV emits an encoded `={"adjustment":"…","symbol":"…"}` form; PineTS returns the plain `prefix:ticker` since the underlying providers don't honour those modifiers anyway. Documented in [`src/namespaces/Ticker.ts`](/developers/pinets/../src/namespaces/Ticker.ts).
* **`renko`, `kagi`, `linebreak`, `pointfigure`** — accepted and chainable into `request.security` / `request.security_lower_tf` without errors, but they return the plain symbol rather than an encoded "alternative chart type" ticker ID. No data source we route to can construct those bars, so requests resolve to standard data. Use `chart.is_*` to detect this and branch in scripts that depend on actual Renko/Kagi bars.


# Timeframe (/developers/pinets/api-coverage/timeframe)



### Timeframe Type Checks [#timeframe-type-checks]

| Function               | Status | Description                   |
| ---------------------- | ------ | ----------------------------- |
| `timeframe.isdaily`    | ✅      | Check if daily timeframe      |
| `timeframe.isdwm`      | ✅      | Check if daily/weekly/monthly |
| `timeframe.isintraday` | ✅      | Check if intraday timeframe   |
| `timeframe.isminutes`  | ✅      | Check if minutes timeframe    |
| `timeframe.ismonthly`  | ✅      | Check if monthly timeframe    |
| `timeframe.isseconds`  | ✅      | Check if seconds timeframe    |
| `timeframe.isticks`    | ✅      | Check if ticks timeframe      |
| `timeframe.isweekly`   | ✅      | Check if weekly timeframe     |

### Timeframe Properties [#timeframe-properties]

| Function                | Status | Description              |
| ----------------------- | ------ | ------------------------ |
| `timeframe.main_period` | ✅      | Main period of timeframe |
| `timeframe.multiplier`  | ✅      | Timeframe multiplier     |
| `timeframe.period`      | ✅      | Timeframe period         |

### Timeframe Functions [#timeframe-functions]

| Function                   | Status | Description                   |
| -------------------------- | ------ | ----------------------------- |
| `timeframe.change()`       | ✅      | Change timeframe              |
| `timeframe.from_seconds()` | ✅      | Create timeframe from seconds |
| `timeframe.in_seconds()`   | ✅      | Convert timeframe to seconds  |


# Types (/developers/pinets/api-coverage/types)



### Currency [#currency]

| Function        | Status | Description        |
| --------------- | ------ | ------------------ |
| `currency.AED`  | ✅      | UAE Dirham         |
| `currency.ARS`  | ✅      | Argentine Peso     |
| `currency.AUD`  | ✅      | Australian Dollar  |
| `currency.BDT`  | ✅      | Bangladeshi Taka   |
| `currency.BHD`  | ✅      | Bahraini Dinar     |
| `currency.BRL`  | ✅      | Brazilian Real     |
| `currency.BTC`  | ✅      | Bitcoin            |
| `currency.CAD`  | ✅      | Canadian Dollar    |
| `currency.CHF`  | ✅      | Swiss Franc        |
| `currency.CLP`  | ✅      | Chilean Peso       |
| `currency.CNY`  | ✅      | Chinese Yuan       |
| `currency.COP`  | ✅      | Colombian Peso     |
| `currency.CZK`  | ✅      | Czech Koruna       |
| `currency.DKK`  | ✅      | Danish Krone       |
| `currency.EGP`  | ✅      | Egyptian Pound     |
| `currency.ETH`  | ✅      | Ethereum           |
| `currency.EUR`  | ✅      | Euro               |
| `currency.GBP`  | ✅      | British Pound      |
| `currency.HKD`  | ✅      | Hong Kong Dollar   |
| `currency.HUF`  | ✅      | Hungarian Forint   |
| `currency.IDR`  | ✅      | Indonesian Rupiah  |
| `currency.ILS`  | ✅      | Israeli Shekel     |
| `currency.INR`  | ✅      | Indian Rupee       |
| `currency.ISK`  | ✅      | Icelandic Króna    |
| `currency.JPY`  | ✅      | Japanese Yen       |
| `currency.KES`  | ✅      | Kenyan Shilling    |
| `currency.KRW`  | ✅      | South Korean Won   |
| `currency.KWD`  | ✅      | Kuwaiti Dinar      |
| `currency.LKR`  | ✅      | Sri Lankan Rupee   |
| `currency.MAD`  | ✅      | Moroccan Dirham    |
| `currency.MXN`  | ✅      | Mexican Peso       |
| `currency.MYR`  | ✅      | Malaysian Ringgit  |
| `currency.NGN`  | ✅      | Nigerian Naira     |
| `currency.NOK`  | ✅      | Norwegian Krone    |
| `currency.NONE` | ✅      | No currency        |
| `currency.NZD`  | ✅      | New Zealand Dollar |
| `currency.PEN`  | ✅      | Peruvian Sol       |
| `currency.PHP`  | ✅      | Philippine Peso    |
| `currency.PKR`  | ✅      | Pakistani Rupee    |
| `currency.PLN`  | ✅      | Polish Złoty       |
| `currency.QAR`  | ✅      | Qatari Riyal       |
| `currency.RON`  | ✅      | Romanian Leu       |
| `currency.RSD`  | ✅      | Serbian Dinar      |
| `currency.RUB`  | ✅      | Russian Ruble      |
| `currency.SAR`  | ✅      | Saudi Riyal        |
| `currency.SEK`  | ✅      | Swedish Krona      |
| `currency.SGD`  | ✅      | Singapore Dollar   |
| `currency.THB`  | ✅      | Thai Baht          |
| `currency.TND`  | ✅      | Tunisian Dinar     |
| `currency.TRY`  | ✅      | Turkish Lira       |
| `currency.TWD`  | ✅      | New Taiwan Dollar  |
| `currency.USD`  | ✅      | US Dollar          |
| `currency.USDT` | ✅      | Tether             |
| `currency.VES`  | ✅      | Venezuelan Bolívar |
| `currency.VND`  | ✅      | Vietnamese Đồng    |
| `currency.ZAR`  | ✅      | South African Rand |

### Dayofweek [#dayofweek]

| Function              | Status | Description |
| --------------------- | ------ | ----------- |
| `dayofweek.friday`    | ✅      | Friday      |
| `dayofweek.monday`    | ✅      | Monday      |
| `dayofweek.saturday`  | ✅      | Saturday    |
| `dayofweek.sunday`    | ✅      | Sunday      |
| `dayofweek.thursday`  | ✅      | Thursday    |
| `dayofweek.tuesday`   | ✅      | Tuesday     |
| `dayofweek.wednesday` | ✅      | Wednesday   |

### Display [#display]

| Function                | Status | Description              |
| ----------------------- | ------ | ------------------------ |
| `display.all`           | ✅      | Display all              |
| `display.data_window`   | ✅      | Display in data window   |
| `display.none`          | ✅      | Display none             |
| `display.pane`          | ✅      | Display in pane          |
| `display.pine_screener` | ✅      | Display in Pine Screener |
| `display.price_scale`   | ✅      | Display in price scale   |
| `display.status_line`   | ✅      | Display in status line   |

### Extend [#extend]

| Function       | Status | Description  |
| -------------- | ------ | ------------ |
| `extend.both`  | ✅      | Extend both  |
| `extend.left`  | ✅      | Extend left  |
| `extend.none`  | ✅      | Extend none  |
| `extend.right` | ✅      | Extend right |

### Font [#font]

| Function                | Status | Description           |
| ----------------------- | ------ | --------------------- |
| `font.family_default`   | ✅      | Default font family   |
| `font.family_monospace` | ✅      | Monospace font family |

### Format [#format]

| Function         | Status | Description    |
| ---------------- | ------ | -------------- |
| `format.inherit` | ✅      | Inherit format |
| `format.mintick` | ✅      | Mintick format |
| `format.percent` | ✅      | Percent format |
| `format.price`   | ✅      | Price format   |
| `format.volume`  | ✅      | Volume format  |

### Hline [#hline]

| Function             | Status | Description                  |
| -------------------- | ------ | ---------------------------- |
| `hline.style_dashed` | ✅      | Dashed horizontal line style |
| `hline.style_dotted` | ✅      | Dotted horizontal line style |
| `hline.style_solid`  | ✅      | Solid horizontal line style  |

### Location [#location]

| Function            | Status | Description        |
| ------------------- | ------ | ------------------ |
| `location.abovebar` | ✅      | Above bar location |
| `location.absolute` | ✅      | Absolute location  |
| `location.belowbar` | ✅      | Below bar location |
| `location.bottom`   | ✅      | Bottom location    |
| `location.top`      | ✅      | Top location       |

### Order [#order]

| Function           | Status | Description      |
| ------------------ | ------ | ---------------- |
| `order.ascending`  | ✅      | Ascending order  |
| `order.descending` | ✅      | Descending order |

### Plot [#plot]

| Function                      | Status | Description                 |
| ----------------------------- | ------ | --------------------------- |
| `plot.linestyle_dashed`       | ✅      | Dashed line style           |
| `plot.linestyle_dotted`       | ✅      | Dotted line style           |
| `plot.linestyle_solid`        | ✅      | Solid line style            |
| `plot.style_area`             | ✅      | Area plot style             |
| `plot.style_areabr`           | ✅      | Area break plot style       |
| `plot.style_circles`          | ✅      | Circles plot style          |
| `plot.style_columns`          | ✅      | Columns plot style          |
| `plot.style_cross`            | ✅      | Cross plot style            |
| `plot.style_histogram`        | ✅      | Histogram plot style        |
| `plot.style_line`             | ✅      | Line plot style             |
| `plot.style_linebr`           | ✅      | Line break plot style       |
| `plot.style_stepline`         | ✅      | Stepline plot style         |
| `plot.style_stepline_diamond` | ✅      | Stepline diamond plot style |
| `plot.style_steplinebr`       | ✅      | Stepline break plot style   |

### Position [#position]

| Function                 | Status | Description            |
| ------------------------ | ------ | ---------------------- |
| `position.bottom_center` | ✅      | Bottom center position |
| `position.bottom_left`   | ✅      | Bottom left position   |
| `position.bottom_right`  | ✅      | Bottom right position  |
| `position.middle_center` | ✅      | Middle center position |
| `position.middle_left`   | ✅      | Middle left position   |
| `position.middle_right`  | ✅      | Middle right position  |
| `position.top_center`    | ✅      | Top center position    |
| `position.top_left`      | ✅      | Top left position      |
| `position.top_right`     | ✅      | Top right position     |

### Scale [#scale]

| Function      | Status | Description |
| ------------- | ------ | ----------- |
| `scale.left`  | ✅      | Left scale  |
| `scale.none`  | ✅      | No scale    |
| `scale.right` | ✅      | Right scale |

### Settlement\_as\_close [#settlement_as_close]

| Function                      | Status | Description                 |
| ----------------------------- | ------ | --------------------------- |
| `settlement_as_close.inherit` | ✅      | Inherit settlement as close |
| `settlement_as_close.off`     | ✅      | Settlement as close off     |
| `settlement_as_close.on`      | ✅      | Settlement as close on      |

### Shape [#shape]

| Function             | Status | Description         |
| -------------------- | ------ | ------------------- |
| `shape.arrowdown`    | ✅      | Arrow down shape    |
| `shape.arrowup`      | ✅      | Arrow up shape      |
| `shape.circle`       | ✅      | Circle shape        |
| `shape.cross`        | ✅      | Cross shape         |
| `shape.diamond`      | ✅      | Diamond shape       |
| `shape.flag`         | ✅      | Flag shape          |
| `shape.labeldown`    | ✅      | Label down shape    |
| `shape.labelup`      | ✅      | Label up shape      |
| `shape.square`       | ✅      | Square shape        |
| `shape.triangledown` | ✅      | Triangle down shape |
| `shape.triangleup`   | ✅      | Triangle up shape   |
| `shape.xcross`       | ✅      | X-cross shape       |

### Size [#size]

| Function      | Status | Description |
| ------------- | ------ | ----------- |
| `size.auto`   | ✅      | Auto size   |
| `size.huge`   | ✅      | Huge size   |
| `size.large`  | ✅      | Large size  |
| `size.normal` | ✅      | Normal size |
| `size.small`  | ✅      | Small size  |
| `size.tiny`   | ✅      | Tiny size   |

### Splits [#splits]

| Function             | Status | Description       |
| -------------------- | ------ | ----------------- |
| `splits.denominator` | ✅      | Split denominator |
| `splits.numerator`   | ✅      | Split numerator   |

### Text [#text]

| Function             | Status | Description           |
| -------------------- | ------ | --------------------- |
| `text.align_bottom`  | ✅      | Bottom text alignment |
| `text.align_center`  | ✅      | Center text alignment |
| `text.align_left`    | ✅      | Left text alignment   |
| `text.align_right`   | ✅      | Right text alignment  |
| `text.align_top`     | ✅      | Top text alignment    |
| `text.format_bold`   | ✅      | Bold text format      |
| `text.format_italic` | ✅      | Italic text format    |
| `text.format_none`   | ✅      | No text format        |
| `text.wrap_auto`     | ✅      | Auto text wrap        |
| `text.wrap_none`     | ✅      | No text wrap          |

### Xloc [#xloc]

| Function         | Status | Description          |
| ---------------- | ------ | -------------------- |
| `xloc.bar_index` | ✅      | Bar index x-location |
| `xloc.bar_time`  | ✅      | Bar time x-location  |

### Yloc [#yloc]

| Function        | Status | Description          |
| --------------- | ------ | -------------------- |
| `yloc.abovebar` | ✅      | Above bar y-location |
| `yloc.belowbar` | ✅      | Below bar y-location |
| `yloc.price`    | ✅      | Price y-location     |

### Dividends [#dividends]

| Function                    | Status | Description            |
| --------------------------- | ------ | ---------------------- |
| `dividends.future_amount`   | ✅      | Future dividend amount |
| `dividends.future_ex_date`  | ✅      | Future ex-date         |
| `dividends.future_pay_date` | ✅      | Future pay date        |
| `dividends.gross`           | ✅      | Gross dividend         |
| `dividends.net`             | ✅      | Net dividend           |

### Earnings [#earnings]

| Function                          | Status | Description            |
| --------------------------------- | ------ | ---------------------- |
| `earnings.future_eps`             | ✅      | Future EPS             |
| `earnings.future_period_end_time` | ✅      | Future period end time |
| `earnings.future_revenue`         | ✅      | Future revenue         |
| `earnings.future_time`            | ✅      | Future time            |
| `earnings.actual`                 | ✅      | Actual earnings        |
| `earnings.estimate`               | ✅      | Estimated earnings     |
| `earnings.standardized`           | ✅      | Standardized earnings  |

### Adjustment [#adjustment]

| Function               | Status | Description          |
| ---------------------- | ------ | -------------------- |
| `adjustment.dividends` | ✅      | Dividends adjustment |
| `adjustment.none`      | ✅      | No adjustment        |
| `adjustment.splits`    | ✅      | Splits adjustment    |

### Alert [#alert]

| Function                        | Status | Description                        |
| ------------------------------- | ------ | ---------------------------------- |
| `alert.freq_all`                | ✅      | Alert frequency all                |
| `alert.freq_once_per_bar`       | ✅      | Alert frequency once per bar       |
| `alert.freq_once_per_bar_close` | ✅      | Alert frequency once per bar close |

### Backadjustment [#backadjustment]

| Function                 | Status | Description            |
| ------------------------ | ------ | ---------------------- |
| `backadjustment.inherit` | ✅      | Inherit backadjustment |
| `backadjustment.off`     | ✅      | Backadjustment off     |
| `backadjustment.on`      | ✅      | Backadjustment on      |

### Barmerge [#barmerge]

| Function                 | Status | Description   |
| ------------------------ | ------ | ------------- |
| `barmerge.gaps_off`      | ✅      | Gaps off      |
| `barmerge.gaps_on`       | ✅      | Gaps on       |
| `barmerge.lookahead_off` | ✅      | Lookahead off |
| `barmerge.lookahead_on`  | ✅      | Lookahead on  |


# Best Practices (/developers/pinets/architecture/best-practices)



## Best Practices and Common Pitfalls [#best-practices-and-common-pitfalls]

This guide covers common mistakes and recommended patterns when working with PineTS.

## Common Pitfalls [#common-pitfalls]

### ⚠️ Pitfall 1: Confusing Storage Order vs Access Order [#️-pitfall-1-confusing-storage-order-vs-access-order]

**Problem:**

```javascript
// Thinking arrays are stored in reverse
let lastValue = context.data.close[0]; // ❌ Actually OLDEST bar!
let currentValue = context.data.close[context.data.close.length - 1]; // ✅ Current bar
```

**Explanation:**

* **Storage**: Forward order (oldest at `[0]`, newest at `[length-1]`)
* **Access via `$.get()` or Series**: Pine Script semantics (0 = current, 1 = previous)

**Solution:**

```javascript
// ✅ Always use $.get() or Series for Pine Script semantics
let currentValue = context.get(close, 0); // Current
let previousValue = context.get(close, 1); // Previous

// Or with Series
let currentValue = Series.from(close).get(0);
let previousValue = Series.from(close).get(1);

// ❌ Direct array access gives chronological order
close[0]; // Oldest bar (not current!)
```

### ⚠️ Pitfall 2: Modifying Transpiler Without Understanding Scope [#️-pitfall-2-modifying-transpiler-without-understanding-scope]

**Problem:**
Changing variable transformation logic can break scope isolation and cause variable collisions.

```javascript
// ❌ Bad: Removing scope prefixes
$.let.x = $.init($.let.x, value); // Collisions possible!

// ✅ Good: Keep scope prefixes
$.let.glb1_x = $.init($.let.glb1_x, value);
$.let.fn2_x = $.init($.let.fn2_x, value); // Different variable
```

**Solution:**

* Always test with the transpiler test suite
* Understand the scope tree before modifying
* Respect the ScopeManager's variable naming conventions

### ⚠️ Pitfall 3: Not Handling NaN Properly [#️-pitfall-3-not-handling-nan-properly]

**Problem:**

```javascript
// ❌ JavaScript NaN behavior
if (value == NaN) { ... }  // Will NEVER be true in JavaScript
value === NaN;  // Always false
```

**Solution:**

```javascript
// ✅ The transpiler automatically converts == to $.math.__eq()
if (value == NaN) { ... }  // Transpiled to $.math.__eq(value, NaN)

// ✅ Or use isNaN()
if (isNaN(value)) { ... }

// ✅ In TA functions, check before updating state
const current = Series.from(source).get(0);
if (!isNaN(current)) {
    state.sum += current;  // Avoid NaN corruption
}
```

### ⚠️ Pitfall 4: Sharing State Across Function Calls [#️-pitfall-4-sharing-state-across-function-calls]

**Problem:**

```javascript
// Two calls with same parameters should be independent
let ema1 = ta.ema(close, 9);
let ema2 = ta.ema(close, 9); // ❌ Could share state without call IDs
```

**Explanation:**
Without unique call IDs, both calls would use the same state key (`ema_9`), causing them to return identical values.

**Solution:**

```javascript
// ✅ Transpiler automatically injects unique call IDs
// Transpiled:
// ta.ema(close, 9, '_ta0')  // First call
// ta.ema(close, 9, '_ta1')  // Second call

// ✅ In TA function implementation, ALWAYS use _callId
export function ema(context: any) {
    return (source: any, period: any, _callId?: string) => {
        const stateKey = _callId || `ema_${period}`; // Use unique ID
        // ...
    };
}
```

### ⚠️ Pitfall 5: Not Initializing Variables with $.init() [#️-pitfall-5-not-initializing-variables-with-init]

**Problem:**

```javascript
// ❌ Direct assignment bypasses Series initialization
$.let.var = someValue; // Won't work as time-series
```

**Solution:**

```javascript
// ✅ Always use $.init() for assignments
$.let.var = $.init($.let.var, someValue);

// Note: Transpiler handles this automatically for user code
```

### ⚠️ Pitfall 6: Incorrect Tuple Return Format [#️-pitfall-6-incorrect-tuple-return-format]

**Problem:**

```javascript
// ❌ Returning plain array (ambiguous with time-series)
export function myFunc(context: any) {
    return (source: any) => {
        return [value1, value2]; // Is this a tuple or time-series?
    };
}
```

**Solution:**

```javascript
// ✅ Wrap tuple in double brackets
export function myFunc(context: any) {
    return (source: any) => {
        return [[value1, value2]]; // Clear tuple marker
    };
}
```

### ⚠️ Pitfall 7: Forgetting context.precision() [#️-pitfall-7-forgetting-contextprecision]

**Problem:**

```javascript
// ❌ Returning raw floating point (inconsistent precision)
return sum / period; // 14.666666666666667
```

**Solution:**

```javascript
// ✅ Use context.precision() for consistent rounding
return context.precision(sum / period); // 14.6666666667 (10 decimals)
```

### ⚠️ Pitfall 8: Recalculating Instead of Incremental Updates [#️-pitfall-8-recalculating-instead-of-incremental-updates]

**Problem:**

```javascript
// ❌ Inefficient: Recalculate entire history every bar
export function sma(context: any) {
    return (source: any, period: any) => {
        let sum = 0;
        for (let i = 0; i < period; i++) {
            sum += Series.from(source).get(i); // O(n) per bar
        }
        return sum / period;
    };
}
```

**Solution:**

```javascript
// ✅ Efficient: Incremental calculation with state
export function sma(context: any) {
    return (source: any, period: any, _callId?: string) => {
        const stateKey = _callId || `sma_${period}`;

        if (!context.taState[stateKey]) {
            context.taState[stateKey] = { window: [], sum: 0 };
        }

        const state = context.taState[stateKey];
        const current = Series.from(source).get(0);

        state.window.push(current);
        state.sum += current;

        if (state.window.length > period) {
            state.sum -= state.window.shift(); // O(1) per bar
        }

        return state.window.length >= period ? context.precision(state.sum / period) : NaN;
    };
}
```

## Best Practices [#best-practices]

### ✅ Best Practice 1: Always Use the Transpiler [#-best-practice-1-always-use-the-transpiler]

**Don't write transpiled code manually.**

```javascript
// ❌ Bad: Manually writing transpiled code
const ema = (context) => {
    $.let.glb1_ema = $.init($.let.glb1_ema, ta.ema(...));
};

// ✅ Good: Write user code, let transpiler transform it
const ema = (context) => {
    let ema = ta.ema(close, 9);
};
```

### ✅ Best Practice 2: Test with Multiple Scenarios [#-best-practice-2-test-with-multiple-scenarios]

When modifying the transpiler or implementing TA functions, test with:

* Simple variable assignments
* Complex nested expressions
* Multiple function calls with same parameters
* Array operations and lookback
* Conditional logic (if/else)
* Loops (for/while)
* Tuple returns
* Edge cases (NaN, empty data, single bar)

### ✅ Best Practice 3: Understand the Context [#-best-practice-3-understand-the-context]

Before debugging, understand what the context contains:

```javascript
// Check context state
console.log('Variables:', context.let);
console.log('Parameters:', context.params);
console.log('TA State:', context.taState);
console.log('Current Index:', context.idx);
console.log('Market Data Length:', context.data.close.length);
```

### ✅ Best Practice 4: Respect the Scope Manager [#-best-practice-4-respect-the-scope-manager]

The ScopeManager tracks:

* Variable scopes and renaming
* Context-bound variables
* Loop variables
* Array pattern elements
* Parameter and cache ID generation

**Don't bypass it or modify its state inconsistently.**

### ✅ Best Practice 5: Implement Incremental TA Functions [#-best-practice-5-implement-incremental-ta-functions]

**Key Points:**

* Use `_callId` for unique state per function call
* Extract values from Series using `.get(0)` or `Series.from()`
* Maintain internal state (window, sum, etc.) for efficiency
* Return `NaN` during initialization period (Pine Script behavior)
* Use `context.precision()` for consistent decimal precision

**Template:**

```typescript
export function myIndicator(context: any) {
    return (source: any, period: any, _callId?: string) => {
        // 1. Extract values
        const periodValue = Series.from(period).get(0);
        const currentValue = Series.from(source).get(0);

        // 2. Initialize state
        const stateKey = _callId || `myInd_${periodValue}`;
        if (!context.taState[stateKey]) {
            context.taState[stateKey] = {
                // Initial state
            };
        }

        const state = context.taState[stateKey];

        // 3. Handle NaN inputs
        if (isNaN(currentValue)) {
            return NaN;
        }

        // 4. Update state incrementally
        // ... your calculation logic ...

        // 5. Return with precision
        return context.precision(result);
    };
}
```

### ✅ Best Practice 6: Handle Initialization Periods [#-best-practice-6-handle-initialization-periods]

```javascript
// ✅ Return NaN until enough data is available
if (state.window.length < period) {
    return NaN; // Pine Script behavior
}

// Calculate and return
return context.precision(state.sum / period);
```

### ✅ Best Practice 7: Use Namespace-Specific param() [#-best-practice-7-use-namespace-specific-param]

```javascript
// ✅ Each namespace has its own param
ta.param(value, index, 'p0'); // For TA functions
math.param(value, index, 'p1'); // For math functions
request.param(value, index, 'p2'); // For request functions

// ❌ Don't use context.param for namespace functions
context.param(value, index, 'p0'); // Use namespace-specific one instead
```

### ✅ Best Practice 8: Document Complex Transformations [#-best-practice-8-document-complex-transformations]

```javascript
// ✅ Add comments explaining non-obvious behavior
export function complexIndicator(context: any) {
    return (source: any, _callId?: string) => {
        // State stores a rolling window of 14 bars for efficiency.
        // We maintain both sum and sum-of-squares to calculate
        // standard deviation in O(1) time per bar.
        const state = context.taState[_callId];
        // ...
    };
}
```

### ✅ Best Practice 9: Use TypeScript Types [#-best-practice-9-use-typescript-types]

```typescript
// ✅ Type your state structures
interface EMAState {
    prevEma: number | null;
    initSum: number;
    initCount: number;
}

export function ema(context: any) {
    return (source: any, period: any, _callId?: string): number => {
        const state: EMAState = context.taState[_callId];
        // ... TypeScript will catch mistakes
    };
}
```

### ✅ Best Practice 10: Clean Up Temporary Files [#-best-practice-10-clean-up-temporary-files]

If you create temporary files for debugging:

```javascript
// After development, remove:
// - Debug console.log statements
// - Temporary test files
// - Commented-out code
// - Unused imports
```

### ✅ Best Practice 11: Implement All Namespace Members as Methods [#-best-practice-11-implement-all-namespace-members-as-methods]

**Everything in namespaces is a method** - even constants. The transpiler handles the conversion from property access to method calls:

```typescript
// ✅ CORRECT: Indicator with optional parameter
export function tr(context: any) {
    return (handle_na?: any) => {
        const handleNa = handle_na !== undefined ? Series.from(handle_na).get(0) : true;
        // ... implementation
    };
}

// ✅ CORRECT: Constant (zero-parameter method)
export function pi(context: any) {
    return () => Math.PI;
}

// ❌ WRONG: Don't use JavaScript getters
// (These belong in getters/ directory which is deprecated)
```

**Key Points:**

* Always implement in `methods/` directory
* Use optional parameters when needed
* The transpiler automatically converts `ta.tr` to `ta.tr()`
* No special cases - everything follows the same pattern
* Simpler to maintain and extend

## Performance Best Practices [#performance-best-practices]

### 1. Use Incremental Calculations [#1-use-incremental-calculations]

**Avoid:** O(n) per bar
&#x2A;*Prefer:** O(1) per bar with state

### 2. Cache Expensive Computations [#2-cache-expensive-computations]

```javascript
// ✅ Cache in context
if (!context.cache[cacheKey]) {
    context.cache[cacheKey] = expensiveCalculation();
}
return context.cache[cacheKey];
```

### 3. Avoid Redundant Series Wrapping [#3-avoid-redundant-series-wrapping]

```javascript
// ❌ Wasteful
for (let i = 0; i < 1000; i++) {
    Series.from(source).get(0); // Creates 1000 Series objects
}

// ✅ Efficient
const series = Series.from(source);
for (let i = 0; i < 1000; i++) {
    series.get(0); // Reuses Series object
}
```

### 4. Limit State Size [#4-limit-state-size]

```javascript
// ✅ Store only what you need
state.window = state.window.slice(-period); // Keep fixed size

// ❌ Don't store entire history
state.history.push(value); // Grows indefinitely
```

## Code Organization [#code-organization]

### Structure for New TA Functions [#structure-for-new-ta-functions]

```
src/namespaces/ta/methods/
    myindicator.ts          ← Implementation
tests/namespaces/
    ta.myindicator.test.ts  ← Unit tests
```

### Regenerate Barrel Files [#regenerate-barrel-files]

```bash
# After adding new method
npm run generate:ta-index
npm run generate:math-index
npm run generate:array-index
# etc.
```

## Testing Checklist [#testing-checklist]

Before submitting TA function implementations:

* [ ] Returns `NaN` during initialization period
* [ ] Uses `_callId` for state isolation
* [ ] Uses `context.precision()` for output
* [ ] Handles `NaN` inputs gracefully
* [ ] Implements incremental calculation (if applicable)
* [ ] Includes unit tests with expected values
* [ ] Tested with multiple calls (same parameters)
* [ ] Tested with edge cases (single bar, all NaN, etc.)
* [ ] Regenerated barrel file (`npm run generate:*-index`)


# Debugging Guide (/developers/pinets/architecture/debugging)



This guide covers practical debugging techniques for PineTS development.

## Viewing Transpiled Code [#viewing-transpiled-code]

To see what the transpiler generates:

```javascript
const transformer = transpile.bind(context);
const transpiledFn = transformer(userCode);
console.log(transpiledFn.toString());
```

This shows the actual JavaScript that will execute, helping identify transformation issues.

## Inspecting Context State [#inspecting-context-state]

### Market Data (Forward Storage) [#market-data-forward-storage]

```javascript
// Check raw market data (forward arrays: oldest to newest)
const close = context.data.close;
console.log('Close array:', close);
console.log('Current close:', close[close.length - 1]); // Last element = current
console.log('Previous close:', close[close.length - 2]); // Second-to-last = previous
console.log('Oldest close:', close[0]); // First element = oldest
```

### Using $.get() (Pine Script Semantics) [#using-get-pine-script-semantics]

```javascript
// Pine Script-style access (reverse indexing)
console.log('close[0] (current):', context.get(close, 0));
console.log('close[1] (previous):', context.get(close, 1));
console.log('close[2] (2 bars ago):', context.get(close, 2));
```

### User Variables [#user-variables]

```javascript
// Check user-defined variables
console.log('All let variables:', context.let);
console.log('All const variables:', context.const);
console.log('All var variables:', context.var);

// Check specific variable history (forward array)
console.log('ema9 series:', context.let.glb1_ema9);
console.log('ema9 current:', context.let.glb1_ema9[context.let.glb1_ema9.length - 1]);
```

### Function State [#function-state]

```javascript
// Check TA function internal state
console.log('All TA state:', context.taState);
console.log('Specific EMA state:', context.taState['_ta0']);
```

### Parameter Cache [#parameter-cache]

```javascript
// Check param() transformed values
console.log('All params:', context.params);
console.log('Specific param:', context.params['p0']);
```

## Inspecting Series Objects [#inspecting-series-objects]

```javascript
// Check Series wrapper details
const series = context.ta.param(close, 1, 'test');
console.log('Series data (forward array):', series.data);
console.log('Series offset (lookback):', series.offset);
console.log('Series.get(0):', series.get(0)); // Value with offset applied
console.log('Actual index accessed:', series.data.length - 1 - series.offset);
```

## Common Debug Patterns [#common-debug-patterns]

### Pattern 1: Value Mismatch [#pattern-1-value-mismatch]

**Problem:** Calculated value doesn't match expected.

**Debug:**

```javascript
// In your TA function
export function myIndicator(context: any) {
    return (source: any, period: any, _callId?: string) => {
        const currentValue = Series.from(source).get(0);
        console.log(`[${_callId}] Current value:`, currentValue);

        const periodValue = Series.from(period).get(0);
        console.log(`[${_callId}] Period:`, periodValue);

        const state = context.taState[_callId];
        console.log(`[${_callId}] State:`, state);

        // ... calculation
        const result = /* ... */;
        console.log(`[${_callId}] Result:`, result);
        return result;
    };
}
```

### Pattern 2: Incorrect Array Access [#pattern-2-incorrect-array-access]

**Problem:** Getting wrong historical values.

**Debug:**

```javascript
const close = context.data.close;

// Forward storage check
console.log('Total bars:', close.length);
console.log('Last 5 (newest to oldest):', [
    close[close.length - 1], // Current
    close[close.length - 2], // Previous
    close[close.length - 3],
    close[close.length - 4],
    close[close.length - 5],
]);

// Pine Script access check
console.log('Last 5 via $.get():', [
    context.get(close, 0), // Current
    context.get(close, 1), // Previous
    context.get(close, 2),
    context.get(close, 3),
    context.get(close, 4),
]);

// These should match!
```

### Pattern 3: State Corruption [#pattern-3-state-corruption]

**Problem:** Function state becomes `NaN` or corrupted.

**Debug:**

```javascript
export function ema(context: any) {
    return (source: any, period: any, _callId?: string) => {
        const stateKey = _callId || `ema_${period}`;
        const state = context.taState[stateKey];

        const currentValue = Series.from(source).get(0);

        // Debug NaN propagation
        if (isNaN(currentValue)) {
            console.warn(`[${stateKey}] NaN input detected at idx ${context.idx}`);
        }

        if (state && isNaN(state.prevEma)) {
            console.error(`[${stateKey}] State corrupted! prevEma is NaN`);
            console.log('State:', state);
        }

        // ... rest of calculation
    };
}
```

### Pattern 4: Scope Issues [#pattern-4-scope-issues]

**Problem:** Variable not found or accessing wrong scope.

**Debug:**

```javascript
// Check what variables exist in each scope
console.log(
    'Global variables (glb1_*):',
    Object.keys(context.let).filter((k) => k.startsWith('glb1_'))
);
console.log(
    'Function scope variables (fn*):',
    Object.keys(context.let).filter((k) => k.match(/^fn\d+_/))
);
console.log(
    'If scope variables (if*):',
    Object.keys(context.let).filter((k) => k.match(/^if\d+_/))
);
```

## Debugging Transpiler Issues [#debugging-transpiler-issues]

### Check Scope Manager State [#check-scope-manager-state]

```javascript
// In transformer code
console.log('Current scope:', scopeManager.currentScope);
console.log('Scope tree:', scopeManager.scopeTree);
console.log('Variable mapping:', scopeManager.variableMap);
console.log('Context-bound vars:', scopeManager.contextBoundVariables);
```

### Trace AST Transformation [#trace-ast-transformation]

```javascript
// Add logging to transformer
transform(node: any) {
    console.log('Transforming:', node.type, node);
    const result = this.transformNode(node);
    console.log('Result:', result);
    return result;
}
```

## Performance Debugging [#performance-debugging]

### Measure Execution Time [#measure-execution-time]

```javascript
const startTime = performance.now();
const result = await pineTS.run(code);
const endTime = performance.now();
console.log(`Execution time: ${endTime - startTime}ms`);
```

### Check Memory Usage [#check-memory-usage]

```javascript
const memBefore = process.memoryUsage();
const result = await pineTS.run(code);
const memAfter = process.memoryUsage();

console.log('Memory delta:', {
    heapUsed: (memAfter.heapUsed - memBefore.heapUsed) / 1024 / 1024,
    external: (memAfter.external - memBefore.external) / 1024 / 1024,
});
```

### Profile TA Function Calls [#profile-ta-function-calls]

```javascript
// Wrap TA function with timing
const originalEma = context.ta.ema;
const calls = { count: 0, totalTime: 0 };

context.ta.ema = (...args) => {
    const start = performance.now();
    const result = originalEma(...args);
    const elapsed = performance.now() - start;

    calls.count++;
    calls.totalTime += elapsed;

    return result;
};

// After execution
console.log(`EMA called ${calls.count} times, avg: ${calls.totalTime / calls.count}ms`);
```

## Common Issues and Solutions [#common-issues-and-solutions]

### Issue: `NaN` Results [#issue-nan-results]

**Symptoms:** Function returns `NaN` unexpectedly.

**Debug:**

```javascript
// Check inputs
console.log('Source:', Series.from(source).get(0));
console.log('Is NaN?', isNaN(Series.from(source).get(0)));

// Check state
console.log('State:', context.taState[_callId]);

// Check initialization
if (!state.initialized) {
    console.log('Still initializing, need more data');
}
```

**Common Causes:**

* Not enough data for initialization (e.g., EMA needs `period` bars)
* State corruption from `NaN` input
* Missing null/undefined checks

### Issue: Wrong Historical Values [#issue-wrong-historical-values]

**Symptoms:** `close[1]` doesn't return previous bar.

**Debug:**

```javascript
// Verify storage order
console.log(
    'Is forward storage?',
    close[0] < close[close.length - 1] // Should be true (oldest < newest)
);

// Verify Series offset
const series = Series.from(close);
console.log('Offset:', series.offset); // Should be 0 for close
console.log('get(1) index:', series.data.length - 1 - (series.offset + 1));
```

**Common Causes:**

* Using direct array access instead of `$.get()`
* Confusing forward storage with reverse access
* Incorrect offset in Series wrapper

### Issue: State Shared Between Calls [#issue-state-shared-between-calls]

**Symptoms:** Two `ta.ema(close, 9)` calls return identical values.

**Debug:**

```javascript
// Check call IDs
console.log('Call 1 ID:', _callId1);
console.log('Call 2 ID:', _callId2);
console.log('Are different?', _callId1 !== _callId2); // Should be true

// Check state keys
console.log('State keys:', Object.keys(context.taState));
```

**Common Causes:**

* Missing `_callId` parameter in function signature
* Not using `_callId` in state key
* Transpiler not injecting unique IDs

## Testing Strategies [#testing-strategies]

### Unit Test Pattern [#unit-test-pattern]

```javascript
it('should calculate correctly', async () => {
    const pineTS = new PineTS(Provider.Mock, 'BTCUSDC', 'D', null, startDate, endDate);

    const sourceCode = (context) => {
        const { close } = context.data;
        const { ta } = context.pine;

        const sma = ta.sma(close, 20);

        // Debug output
        console.log('Context at idx', context.idx);
        console.log('close:', context.get(close, 0));
        console.log('sma:', context.get(sma, 0));

        return { sma };
    };

    const { result } = await pineTS.run(sourceCode);

    // Assertions
    expect(result.sma).toBeDefined();
    expect(result.sma[result.sma.length - 1]).toBeCloseTo(expectedValue);
});
```

### Snapshot Testing [#snapshot-testing]

```javascript
// Capture full context state
const snapshot = {
    idx: context.idx,
    close: context.data.close.slice(-5), // Last 5 bars
    variables: { ...context.let },
    taState: { ...context.taState },
};

// Compare with expected
expect(snapshot).toMatchSnapshot();
```

## Visual Debugging Tools [#visual-debugging-tools]

### Plot All Values [#plot-all-values]

```javascript
const sourceCode = (context) => {
    const { close } = context.data;
    const { ta, plotchar } = context.pine;

    const sma = ta.sma(close, 20);
    const ema = ta.ema(close, 20);

    // Plot for visual inspection
    plotchar(close, 'close', { color: 'blue' });
    plotchar(sma, 'sma', { color: 'red' });
    plotchar(ema, 'ema', { color: 'green' });

    return { sma, ema };
};
```

### Export to CSV [#export-to-csv]

```javascript
const { result, plots } = await pineTS.run(sourceCode);

const csv = plots['sma'].data.map((p) => `${new Date(p.time).toISOString()},${p.value}`).join('\n');

fs.writeFileSync('debug.csv', 'time,sma\n' + csv);
```


# Architecture (/developers/pinets/architecture)



## Architecture Guide [#architecture-guide]

This section provides a deep dive into the internal architecture of PineTS. It is intended for **contributors** and **developers** who need to understand the transpilation pipeline, runtime execution model, and core design decisions to extend or debug the engine.

## Overview [#overview]

PineTS bridges the gap between Pine Script's unique time-series semantics and standard JavaScript execution through a three-layer architecture:

```
┌─────────────────────────────────────────────────┐
│          User PineTS Code (Input)           │
│    (Looks like Pine Script, uses JS syntax) │
└────────────────┬────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────┐
│           Transpiler Layer                  │
│  • AST Parsing (acorn)                      │
│  • Scope Analysis                           │
│  • Code Transformation                      │
│  • Context Variable Management              │
└────────────────┬────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────┐
│         Runtime Execution Layer             │
│  • Context Object ($)                       │
│  • Series Management                        │
│  • Namespace Functions (ta, math, etc.)     │
│  • State Management                         │
└─────────────────────────────────────────────────┘
```

### Main Components [#main-components]

1. **PineTS Class**: Orchestrates market data loading, execution, and pagination
2. **Context Class**: Maintains execution state, series data, and namespaces
3. **Transpiler**: Transforms PineTS code into executable JavaScript
4. **Namespaces**: Provide Pine Script functions (ta.ema, math.abs, etc.)

## Documentation Structure [#documentation-structure]

### Core Architecture [#core-architecture]

* [Transpiler Architecture](/developers/pinets/architecture/transpiler): Details on AST parsing, scope analysis, and code transformation.
  * [Scope Manager](/developers/pinets/architecture/transpiler/scope-manager): Variable renaming and unique ID generation
  * [Transformers](/developers/pinets/architecture/transpiler/transformers): AST transformation logic
  * [Real Examples](/developers/pinets/architecture/transpiler/examples): Actual transpilation output examples
* [Runtime Engine](/developers/pinets/architecture/runtime): How the `Context`, `Series`, and execution loop work.
  * [Context Class](/developers/pinets/architecture/runtime/context): The global state object
  * [Series Class](/developers/pinets/architecture/runtime/series): Forward storage with reverse access
  * [Execution Flow](/developers/pinets/architecture/runtime/execution-flow): Run loop and pagination
* [Namespaces](/developers/pinets/architecture/namespaces): Implementation of `ta`, `math`, `request`, etc.
  * [Technical Analysis (ta)](/developers/pinets/architecture/namespaces/ta): TA functions implementation
  * [Math (math)](/developers/pinets/architecture/namespaces/math): Mathematical operations
  * [Array (array)](/developers/pinets/architecture/namespaces/array): Array operations
  * [Request (request)](/developers/pinets/architecture/namespaces/request): Multi-timeframe analysis
  * [Input (input)](/developers/pinets/architecture/namespaces/input): User input handling
* [Critical Implementation Details](/developers/pinets/architecture/specifics): Deep dives into tuple handling, `request.security`, and other complex features.
  * [Tuple Handling](/developers/pinets/architecture/specifics/tuples): Double bracket convention
  * [Request Security](/developers/pinets/architecture/specifics/request-security): Secondary context architecture

### Developer Resources [#developer-resources]

* [Debugging Guide](/developers/pinets/architecture/debugging): Practical debugging techniques and common issues
* [Best Practices](/developers/pinets/architecture/best-practices): Common pitfalls and recommended patterns
* [Syntax Evolution](/developers/pinets/architecture/syntax-evolution): Migration from old to new syntax

## Key Design Principles [#key-design-principles]

1. **Series-First**: Everything is a time-series. The `Series` class is the fundamental data unit.
2. **Forward Storage, Reverse Access**: Data is stored chronologically (push to end) but accessed with reverse indexing (`close[1]` is previous).
3. **Incremental Calculation**: Functions utilize state to calculate values O(1) per bar rather than recalculating history.
4. **State Isolation**: Unique IDs ensure that multiple calls to the same function maintain separate internal states.


# Syntax Evolution (/developers/pinets/architecture/syntax-evolution)



## Syntax Evolution and Deprecation [#syntax-evolution-and-deprecation]

PineTS is evolving to provide a more unified and cleaner API surface. While older syntax patterns remain supported for backward compatibility, they may trigger runtime warnings.

## The Unified Namespace Pattern [#the-unified-namespace-pattern]

### ✅ Recommended (New Syntax) [#-recommended-new-syntax]

Destructure all Pine Script namespaces and built-in functions from `context.pine`. This creates a cleaner import section that mirrors Pine Script's unified environment.

```javascript
(context) => {
    const { close, open, high, low, hlc3, volume } = context.data;
    // Import everything from context.pine - unified namespace
    const { plotchar, color, plot, na, nz, ta, math } = context.pine;

    const sma = ta.sma(close, 20);
    const abs = math.abs(sma - close);
};
```

**Transpiled Output:**

```javascript
($) => {
    const { close, open, high, low, hlc3, volume } = $.data;
    const { plotchar, color, plot, na, nz, ta, math } = $.pine;
    // ... rest of transpiled code
};
```

### ⚠️ Deprecated (Old Syntax) [#️-deprecated-old-syntax]

Accessing namespaces directly from the context root or splitting imports between `context.core` and direct assignments is considered legacy behavior and may trigger warnings.

```javascript
(context) => {
    const { close, open, high, low, hlc3, volume } = context.data;
    const { plotchar, color, plot, na, nz } = context.core; // ⚠️ Legacy: context.core
    const ta = context.ta; // ⚠️ Legacy: Direct access
    const math = context.math; // ⚠️ Legacy: Direct access

    const sma = ta.sma(close, 20);
    const abs = math.abs(sma - close);
};
```

**Transpiled Output:**

```javascript
($) => {
    const { close, open, high, low, hlc3, volume } = $.data;
    const { plotchar, color, plot, na, nz } = $.core; // ⚠️ Deprecated
    const ta = $.ta; // ⚠️ Deprecated
    const math = $.math; // ⚠️ Deprecated
    // ... rest of transpiled code
};
```

## Key Differences [#key-differences]

1. **`context.pine` vs `context.core`**: Use `context.pine` as the single source of truth for Pine Script functions and variables. The `context.core` namespace is deprecated.

2. **Namespace Access**: Prefer destructuring `ta`, `math`, `array`, etc., from `context.pine` rather than accessing them directly from `context.ta`, `context.math`, etc.

3. **Consistency**: The new syntax provides a single, consistent pattern that's easier to understand and maintain.

## Migration Guide [#migration-guide]

### Before (Old Syntax) [#before-old-syntax]

```javascript
(context) => {
    const { close, open } = context.data;
    const { plot, color } = context.core;
    const ta = context.ta;
    const math = context.math;
    const array = context.array;

    // Your indicator logic
};
```

### After (New Syntax) [#after-new-syntax]

```javascript
(context) => {
    const { close, open } = context.data;
    const { plot, color, ta, math, array } = context.pine;

    // Your indicator logic (unchanged)
};
```

## Backward Compatibility [#backward-compatibility]

* **Old syntax still works**: Existing indicators using the old syntax will continue to function correctly.
* **Runtime warnings**: Indicators using deprecated syntax may log warnings to help developers migrate.
* **No breaking changes**: The transpiler handles both syntaxes identically after normalization.
* **Gradual migration**: You can migrate indicators at your own pace.

## Why the Change? [#why-the-change]

1. **Simplification**: One unified namespace (`context.pine`) instead of multiple access patterns.
2. **Clarity**: Clear distinction between market data (`context.data`) and Pine Script functions (`context.pine`).
3. **Maintainability**: Easier to document and understand for new users.
4. **Future-proofing**: Provides a cleaner foundation for future API enhancements.

## Complete Example [#complete-example]

### Old Style (Still Works) [#old-style-still-works]

```javascript
(context) => {
    // Scattered imports from different sources
    const { close, open, high, low } = context.data;
    const { plotchar, plot, color } = context.core;
    const ta = context.ta;
    const math = context.math;

    // Indicator logic
    let ema = ta.ema(close, 9);
    let diff = math.abs(ema - close);
    plotchar(diff, 'diff', { color: color.red });
};
```

### New Style (Recommended) [#new-style-recommended]

```javascript
(context) => {
    // Clean, unified imports
    const { close, open, high, low } = context.data;
    const { plotchar, plot, color, ta, math } = context.pine;

    // Indicator logic (identical)
    let ema = ta.ema(close, 9);
    let diff = math.abs(ema - close);
    plotchar(diff, 'diff', { color: color.red });
};
```

## Timeline [#timeline]

* **Current**: Both syntaxes supported, warnings for old syntax
* **Future**: Old syntax may be removed in major version update (with ample notice)
* **Recommendation**: Migrate to new syntax at your earliest convenience

## Getting Help [#getting-help]

If you encounter issues migrating to the new syntax:

1. Check the transpiled output for any errors
2. Review this documentation
3. Open an issue on GitHub with your code example


# Array (array) (/developers/pinets/architecture/namespaces/array)



## PineTS Array (`array`) Namespace [#pinets-array-array-namespace]

This directory contains the implementation of Pine Script's `array.*` namespace functions. These functions manage dynamically sized arrays, distinct from time-series variables.

## Architecture [#architecture]

Functions are factory functions accessing the `context`.

## The `param()` Method [#the-param-method]

**Crucial Difference**: unlike `ta` or `math`, the `array.param()` method returns the **raw value**, not a `Series` object.

```typescript
export function param(context: any) {
    return (source: any, index: number = 0) => {
        return Series.from(source).get(index);
    };
}
```

### Why? [#why]

`array.*` functions (like `array.new`, `array.push`) operate on specific array instances or scalar values at the current bar. They do not operate on the "history" of an array variable in the same way `ta.sma` operates on the history of `close`.

## Pine Array Object [#pine-array-object]

PineTS implements a `PineArrayObject` (or uses native JS arrays with specific handling) to mimic Pine Script array behavior (e.g., distinct from time-series arrays).

## Implementation Specifics [#implementation-specifics]

### 1. Mutability [#1-mutability]

Pine Script arrays are mutable references. `array.push(id, value)` modifies the array in place.

### 2. Type Safety [#2-type-safety]

While JavaScript is loosely typed, Pine Script arrays are typed (`float[]`, `int[]`, etc.). The implementation may enforce or ignore strict typing depending on the specific method's requirements.

## Generating the Barrel File [#generating-the-barrel-file]

To regenerate the `array.index.ts` file:

```bash
npm run generate:array-index
```


# Namespaces (/developers/pinets/architecture/namespaces)



## Namespace Architecture [#namespace-architecture]

PineTS mimics Pine Script's namespacing (`ta.`, `math.`, `request.`) through modular namespace classes attached to the `Context`.

## Common Pattern: The Factory [#common-pattern-the-factory]

Every function in a namespace is implemented using a factory pattern:

```typescript
// Factory receives the Context
export function myFunc(context: any) {
    // Returns the actual function to be called by user code
    return (arg1, arg2, _callId) => {
        // Logic accessing context...
    };
}
```

## The `param()` Function [#the-param-function]

Each namespace implements its own `param()` function. This is the interface between the transpiler and the runtime.

* **Input**: Raw value, Array, or Series.
* **Output**: `Series` object (usually), or raw value (for `array` namespace).

### Why different `param()` implementations? [#why-different-param-implementations]

1. **`ta` / `math`**: Need to operate on **Series**. They wrap scalars in Series so `.get(0)` works universally.
2. **`array`**: Operates on **Pine Array Objects**. `param()` returns the raw object/value, not a Series wrapper.
3. **`request`**: Handles **Tuples** and needs the **Parameter ID** for caching secondary contexts.

## Property-Like Access (Universal Method Pattern) [#property-like-access-universal-method-pattern]

In Pine Script, namespace members can be accessed without parentheses:

```javascript
// Pine Script - property-like access
tr1 = ta.tr        // No parentheses
tr2 = ta.tr(true)  // With parameter
pi = math.pi       // Constant access
```

### PineTS Unified Approach [#pinets-unified-approach]

PineTS simplifies this by implementing **everything as methods**. The transpiler automatically converts property access to method calls. This eliminates the complexity of maintaining separate getters and methods.

#### How It Works [#how-it-works]

1. **Implementation**: All namespace members are implemented as methods (even constants):

```typescript
// Indicator with optional parameter
export function tr(context: any) {
    return (handle_na?: any) => {
        const handleNa = handle_na !== undefined ? Series.from(handle_na).get(0) : true;
        // ... calculation logic
    };
}

// Constant (zero-parameter method)
export function pi(context: any) {
    return () => Math.PI;
}
```

2. **Transpiler Transformation**: When the transpiler encounters namespace member access without parentheses, it automatically adds them:

```javascript
// User writes:
const tr = ta.tr;
const pi = math.pi;

// Transpiler converts to:
const tr = ta.tr();
const pi = math.pi();
```

3. **Explicit Calls**: Methods called with parentheses work normally:

```javascript
// User writes:
const tr = ta.tr(true);

// Transpiler passes through (with parameter wrapping):
const tr = ta.tr(ta.param(true, undefined, 'p0'), '_ta0');
```

#### Transpiler Logic [#transpiler-logic]

The transformation applies to **direct namespace access** for known Pine Script namespaces (`ta`, `math`, `request`, `array`, `input`):

```typescript
// ✅ Transformed: Direct namespace access (all members)
ta.tr          → ta.tr()
ta.ema         → ta.ema()
math.pi        → math.pi()
math.abs       → math.abs()

// ✅ Not transformed: Already a call
ta.tr()        → ta.tr()
ta.tr(true)    → ta.tr(true)

// ✅ Not transformed: Variable assignment
const myTa = context.ta;
myTa.tr        → myTa.tr  (no transformation)
```

#### Benefits of This Approach [#benefits-of-this-approach]

1. **Simplicity**: Single implementation pattern for all namespace members
2. **No Special Cases**: No need to distinguish between "getters" and "methods"
3. **Consistency**: All namespace functions follow the same factory pattern
4. **Flexibility**: Easy to add optional parameters to any function
5. **Maintainability**: Less code, fewer edge cases to handle
6. **Performance**: No overhead from property getters or descriptor lookups

#### Examples in PineTS [#examples-in-pinets]

**All namespace members work the same way:**

```javascript
// Indicators with optional parameters
const tr1 = ta.tr; // Auto-converted to ta.tr()
const tr2 = ta.tr(true); // With parameter

// Indicators with required parameters
const ema = ta.ema; // Auto-converted to ta.ema() (transpiler adds params)
const ema2 = ta.ema(close, 14); // With explicit parameters

// Constants (zero-parameter methods)
const pi = math.pi; // Auto-converted to math.pi()
const e = math.e; // Auto-converted to math.e()
```

### Why Not JavaScript Getters? [#why-not-javascript-getters]

Prior to this approach, PineTS used JavaScript getters for some namespace members. This created unnecessary complexity:

* ❌ Two different patterns to maintain (getters vs methods)
* ❌ Getters couldn't accept optional parameters
* ❌ Required special handling in the namespace class
* ❌ More complex barrel file generation

**The unified method approach solves all of these issues** by treating everything the same way.

## Specific Namespace Details [#specific-namespace-details]

For implementation details of specific namespaces, see their source READMEs:

* [Technical Analysis (`ta`)](/developers/pinets/architecture/namespaces/ta)
* [Math (`math`)](/developers/pinets/architecture/namespaces/math)
* [Array (`array`)](/developers/pinets/architecture/namespaces/array)
* [Request (`request`)](/developers/pinets/architecture/namespaces/request)
* [Input (`input`)](/developers/pinets/architecture/namespaces/input)


# Input (input) (/developers/pinets/architecture/namespaces/input)



## PineTS Input (`input`) Namespace [#pinets-input-input-namespace]

This directory contains the implementation of Pine Script's `input.*` namespace functions. These functions handle user inputs/parameters for indicators.

## Architecture [#architecture]

Functions are factory functions accessing the `context`.

### Runtime Input Resolution [#runtime-input-resolution]

Starting with v0.8.0, `input` functions automatically resolve values from `context.inputs` if available. This allows passing custom values at runtime using the `Indicator` class.

1. **Check `context.inputs`**: The function looks for a key matching the input's `title`.
2. **Fallback**: If no runtime input is found for that title, the `defval` (default value) specified in the script is used.

## The `param()` Method [#the-param-method]

The `input.param()` method returns the value using Series.from(source).get(index)

### Purpose [#purpose]

This wrapping might be a historical artifact or used to distinguish input values from standard scalar values in the `Context.init()` logic, similar to how tuple returns are wrapped in double brackets.

## Implementation Specifics [#implementation-specifics]

### 1. Constant Nature [#1-constant-nature]

In Pine Script, `input` values are constant throughout the script execution (except for `input.source` or `input.price` which might vary if mapped to changing data).

### 2. Types [#2-types]

Functions like `input.int`, `input.float`, `input.bool` perform validation or casting to ensure the provided value matches the expected type.

## Generating the Barrel File [#generating-the-barrel-file]

To regenerate the `input.index.ts` file:

```bash
npm run generate:input-index
```


# Math (math) (/developers/pinets/architecture/namespaces/math)



## PineTS Math (`math`) Namespace [#pinets-math-math-namespace]

This directory contains the implementation of Pine Script's `math.*` namespace functions. These functions provide standard mathematical operations adapted for time-series data.

## Architecture [#architecture]

Like other namespaces, `math` functions are factory functions that receive the `context` and return the implementation.

## The `param()` Method [#the-param-method]

The `math.param()` method is identical to `ta.param()`. It ensures that all mathematical operations can handle both scalar values and `Series` objects seamlessly.

### Behavior [#behavior]

* **Series Input**: Returns the Series object (with offset if specified).
* **Scalar Input**: Wraps the scalar in a new `Series` object.
* **Array Input**: Wraps the array in a `Series` object.

This uniformity allows math functions to work directly on `Series` objects, enabling operations like `math.abs(close)`.

## Implementation Specifics [#implementation-specifics]

### 1. Series Support [#1-series-support]

Most math functions are designed to work on the *current value* of a Series. They typically extract the value using `Series.from(source).get(0)` and perform the operation.

```typescript
// Example: math.abs
const val = Series.from(source).get(0);
return Math.abs(val);
```

### 2. NaN Handling [#2-nan-handling]

Math functions should propagate `NaN` correctly. `Math.abs(NaN)` returns `NaN` in JavaScript, which aligns with Pine Script behavior.

### 3. Equality Check (`__eq`) [#3-equality-check-__eq]

A special `__eq` function handles equality checks, specifically for `NaN`. In Pine Script, `NaN == NaN` is true (unlike JavaScript). The transpiler converts `==` to `$.math.__eq()` to ensure correct behavior.

## Generating the Barrel File [#generating-the-barrel-file]

To regenerate the `math.index.ts` file after adding new methods:

```bash
npm run generate:math-index
```


# Property-Like Access (/developers/pinets/architecture/namespaces/property-access)



## Property-Like Access in PineTS [#property-like-access-in-pinets]

This document explains how PineTS handles Pine Script's property-like syntax where namespace members can be accessed without parentheses.

## The Problem [#the-problem]

In Pine Script, namespace members can be accessed without parentheses:

```javascript
// Pine Script - property-like access
tr1 = ta.tr           // No parentheses
tr2 = ta.tr(true)     // With parameter
pi = math.pi          // Constant access
```

This creates a challenge in JavaScript/TypeScript where methods require parentheses to be called.

## PineTS Unified Solution [#pinets-unified-solution]

**PineTS takes a simplified approach**: Everything in namespaces is implemented as a **method**, and the transpiler automatically converts property access to method calls. This eliminates the complexity of maintaining separate getters and methods.

### Why This Approach? [#why-this-approach]

1. **Simplicity**: Single implementation pattern for all namespace members
2. **No special cases**: No need to distinguish between "getters" and "methods"
3. **Transpiler handles it**: The conversion happens at transpile time, not runtime
4. **Flexibility**: Easy to add optional parameters to any function later
5. **Maintainability**: Less code, fewer edge cases to handle

### Architecture [#architecture]

```
┌─────────────────────────────────────────────────┐
│  User Code: ta.tr                            │
└────────────────┬────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────┐
│  Transpiler: Detects namespace method access │
│  without parentheses                         │
└────────────────┬────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────┐
│  Transformed Code: ta.tr()                   │
└─────────────────────────────────────────────────┘
```

### How It Works [#how-it-works]

#### 1. Implementation (All Methods) [#1-implementation-all-methods]

All getter-like functions are implemented as **regular methods** with optional parameters:

```typescript
// src/namespaces/ta/methods/tr.ts
export function tr(context: any) {
    return (handle_na?: any) => {
        // Default parameter value
        const handleNa = handle_na !== undefined ? Series.from(handle_na).get(0) : true;

        const high0 = context.get(context.data.high, 0);
        const low0 = context.get(context.data.low, 0);
        const close1 = context.get(context.data.close, 1);

        if (isNaN(close1)) {
            return handleNa ? high0 - low0 : NaN;
        }

        return Math.max(high0 - low0, Math.abs(high0 - close1), Math.abs(low0 - close1));
    };
}
```

#### 2. Transpiler Detection [#2-transpiler-detection]

The transpiler's `transformMemberExpression` function detects namespace method access:

```typescript
// Checks for known namespaces
const KNOWN_NAMESPACES = ['ta', 'math', 'request', 'array', 'input'];

const isDirectNamespaceMemberAccess =
    memberNode.object && memberNode.object.type === 'Identifier' && KNOWN_NAMESPACES.includes(memberNode.object.name) && !memberNode.computed;

// If not already being called, add parentheses
if (!isAlreadyBeingCalled) {
    // Convert to: namespace.method()
}
```

#### 3. Transformation Examples [#3-transformation-examples]

```javascript
// ✅ Property access → Method call (for all namespace members)
ta.tr          →  ta.tr()
math.pi        →  math.pi()
ta.ema         →  ta.ema()

// ✅ Already a method call → Pass through
ta.tr()        →  ta.tr()
ta.tr(true)    →  ta.tr(true)
math.pi()      →  math.pi()

// ✅ Variable assignment → No transformation
const myTa = context.ta;
myTa.tr        →  myTa.tr  (unchanged)
```

### Smart Detection [#smart-detection]

The transpiler only transforms **direct namespace access** to avoid false positives:

```javascript
// ✅ Transformed: Direct namespace
const tr = ta.tr; // → ta.tr()

// ❌ Not transformed: Variable holds namespace
const myTa = context.ta;
const tr = myTa.tr; // No change (myTa is not a known namespace)

// ✅ Not transformed: Already in a call
ta.tr(); // Already a call, no change needed

// ✅ Not transformed: In destructuring
const { tr } = ta; // Part of destructuring, no change
```

## Usage Examples [#usage-examples]

### Basic Usage [#basic-usage]

```javascript
const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', '1h', 100);

const sourceCode = (context) => {
    const { ta } = context.pine;

    // All these work correctly:
    const tr1 = ta.tr; // Auto-converted to ta.tr()
    const tr2 = ta.tr(); // Explicit call, default parameter
    const tr3 = ta.tr(true); // With parameter: handle_na = true
    const tr4 = ta.tr(false); // With parameter: handle_na = false

    return { tr1, tr2, tr3, tr4 };
};

const { result } = await pineTS.run(sourceCode);
```

### With Optional Parameters [#with-optional-parameters]

```javascript
// Without parameter - uses default
const tr = ta.tr; // Auto-converted to ta.tr(), handle_na defaults to true

// With explicit parameter
const trWithNA = ta.tr(true); // Returns high-low when close[1] is NaN
const trStrict = ta.tr(false); // Returns NaN when close[1] is NaN
```

### Constants [#constants]

Even constants are implemented as methods for consistency:

```javascript
// All converted to method calls
const pi = math.pi; // → math.pi() returns 3.14159...
const e = math.e; // → math.e() returns 2.71828...

// Implementation is simple:
export function pi(context: any) {
    return () => Math.PI;
}
```

## Implementation Guide [#implementation-guide]

### For New Getter-Like Methods [#for-new-getter-like-methods]

When implementing a new function that should work as both a property and method:

#### Step 1: Create Method File [#step-1-create-method-file]

Place in `src/namespaces/{namespace}/methods/yourfunction.ts`:

```typescript
import { Series } from '../../../Series';

export function yourFunction(context: any) {
    return (optionalParam?: any) => {
        // Extract parameter with default
        const param = optionalParam !== undefined
            ? Series.from(optionalParam).get(0)
            : defaultValue;

        // Implementation
        const result = /* ... calculation ... */;

        return context.precision(result);
    };
}
```

#### Step 2: Regenerate Barrel File [#step-2-regenerate-barrel-file]

```bash
npm run generate:ta-index
# or
npm run generate:math-index
# etc.
```

#### Step 3: Add Tests [#step-3-add-tests]

Test both syntaxes in your test file:

```typescript
it('should work without parentheses', async () => {
    const sourceCode = (context) => {
        const { ta } = context.pine;
        const result = ta.yourFunction; // No parentheses
        return { result };
    };
    // ... assertions
});

it('should work with parameter', async () => {
    const sourceCode = (context) => {
        const { ta } = context.pine;
        const result = ta.yourFunction(true); // With parameter
        return { result };
    };
    // ... assertions
});
```

### Converting Existing Getters [#converting-existing-getters]

If you have an existing getter in `getters/` directory, convert it to a method:

1. **Move** the file to `methods/` directory
2. **Keep the same signature** (or add optional parameters if needed)
3. **Regenerate** the barrel file
4. **Delete** the old getter file

Example migration:

```typescript
// OLD: getters/obv.ts (JavaScript getter)
export function obv(context: any) {
    return () => {
        // ... implementation
        return obvValue;
    };
}

// NEW: methods/obv.ts (method - same signature!)
export function obv(context: any) {
    return () => {
        // ... same implementation
        return obvValue;
    };
}
```

The transpiler handles the conversion from `ta.obv` to `ta.obv()`, so the implementation stays the same. You're just moving it from `getters/` to `methods/` for consistency.

## Benefits [#benefits]

### 1. Simplicity [#1-simplicity]

* Single implementation pattern for all functions
* No special getter/method dual implementations
* Easier to maintain and understand

### 2. Flexibility [#2-flexibility]

* Easy to add optional parameters to existing functions
* Can extend functionality without breaking changes
* Backward compatible with property access syntax

### 3. Type Safety [#3-type-safety]

* TypeScript can properly type-check method signatures
* IDE autocomplete works correctly
* Better developer experience

### 4. Performance [#4-performance]

* No overhead from property getters being called repeatedly
* Transpilation happens once, not at runtime
* Same performance as regular method calls

### 5. Pine Script Compatibility [#5-pine-script-compatibility]

* Matches Pine Script's property-like access syntax
* Users can write code that looks like Pine Script
* Smooth migration path from Pine Script

### 6. No Runtime Overhead [#6-no-runtime-overhead]

* No JavaScript getters being invoked
* No property descriptor lookups
* Direct method calls after transpilation

## Limitations [#limitations]

### Variable Assignment Edge Case [#variable-assignment-edge-case]

When a namespace is assigned to a variable, the transformation doesn't apply:

```javascript
// This works
const tr = ta.tr; // → ta.tr()

// This doesn't transform
const myTa = context.ta;
const tr = myTa.tr; // Not transformed (myTa is not a known namespace)

// Workaround: Use explicit call
const tr = myTa.tr(); // ✅ Works
```

**Reason**: The transpiler only recognizes direct access to known namespace identifiers (`ta`, `math`, etc.) to avoid false positives.

### Destructuring [#destructuring]

Destructured methods need explicit calls:

```javascript
// Doesn't auto-transform
const { tr } = ta;
const value = tr; // Need: tr()

// Workaround: Call explicitly
const value = tr(); // ✅ Works
```

## Universal Application [#universal-application]

### Everything is a Method [#everything-is-a-method]

This transpiler-based approach applies to **all namespace members** - no exceptions:

```javascript
// ✅ Indicators with optional parameters
ta.tr; // → ta.tr()
ta.tr(true); // → ta.tr(true)

// ✅ Indicators with required parameters
ta.ema; // → ta.ema() (will need params from transpiler)
ta.ema(close, 14); // → ta.ema(close, 14)

// ✅ Constants (implemented as zero-parameter methods)
math.pi; // → math.pi()
math.e; // → math.e()

// ✅ Any namespace member
array.size; // → array.size()
request.security; // → request.security()
```

### Implementation Consistency [#implementation-consistency]

**All namespace members follow the same pattern:**

```typescript
// Constant (zero parameters)
export function pi(context: any) {
    return () => Math.PI;
}

// Indicator with optional parameter
export function tr(context: any) {
    return (handle_na?: any) => {
        const handleNa = handle_na !== undefined ? Series.from(handle_na).get(0) : true;
        // ... calculation
    };
}

// Indicator with required parameters
export function ema(context: any) {
    return (source: any, length: any, _callId?: string) => {
        // ... calculation
    };
}
```

All are methods, all work with the same transpiler transformation logic.

## Troubleshooting [#troubleshooting]

### Method Not Being Called [#method-not-being-called]

**Problem**: `ta.tr` returns a function instead of a value.

**Solution**: Check that:

1. The namespace is in the `KNOWN_NAMESPACES` list in the transpiler
2. You're using direct namespace access (not through a variable)
3. The barrel file was regenerated after adding the method

### Parameter Not Working [#parameter-not-working]

**Problem**: Parameter is ignored or causes an error.

**Solution**: Ensure:

1. The method signature includes the optional parameter
2. You're using `Series.from()` to extract the parameter value
3. You're providing a default value for backward compatibility

### Tests Failing [#tests-failing]

**Problem**: Tests fail after converting from getter to method.

**Solution**: Update tests to:

1. Test both syntaxes (with and without parentheses)
2. Test with different parameter values
3. Verify default behavior matches old getter behavior

## See Also [#see-also]

* [Namespace Architecture](/developers/pinets/architecture/namespaces)
* [Technical Analysis (ta) Namespace](/developers/pinets/architecture/namespaces/ta)
* [Best Practices](/developers/pinets/architecture/best-practices)
* [Transpiler Architecture](/developers/pinets/architecture/transpiler)


# Request (request) (/developers/pinets/architecture/namespaces/request)



## PineTS Request (`request`) Namespace [#pinets-request-request-namespace]

This directory contains the implementation of Pine Script's `request.*` functions, primarily `request.security` for multi-timeframe analysis.

## Architecture [#architecture]

Functions are factory functions accessing the `context`.

## The `param()` Method [#the-param-method]

The `request.param()` method is highly specialized. It performs two critical tasks:

1. **Tuple Detection**: It distinguishes between a **tuple of expressions** (e.g., `[open, close]`) and a **time-series array**.
   * If inputs are `Series` objects, it extracts their current values.
   * If inputs are scalars, it preserves them.
   * Heuristic: `hasOnlySeries` or `hasOnlyScalars` check.

2. **ID Return*&#x2A;: Unlike other param methods, it returns a tuple &#x2A;*`[value, name]`**.
   * `value`: The extracted value(s).
   * `name`: The unique ID (`p0`, `p1`...) assigned by the transpiler.

```typescript
return [val, name];
```

### Why Return the Name? [#why-return-the-name]

The `request.security` function relies on caching secondary contexts (HTF contexts). To do this efficiently, it constructs a cache key using the parameter ID (`name`). Without this ID, it wouldn't know which expression corresponds to which cached context.

## Implementation Specifics [#implementation-specifics]

### 1. Secondary Contexts [#1-secondary-contexts]

`request.security` creates a **new PineTS instance** (a secondary context) to evaluate the expression in the requested timeframe.

* It prevents recursion: Secondary contexts have a flag `isSecondaryContext = true`.
* If `request.security` is called within a secondary context, it returns the expression directly (no new context).

### 2. Tuple Handling [#2-tuple-handling]

When `request.security` returns a tuple (e.g., from `[open, close]`), it wraps the result in a **2D array** `[[val1, val2]]`. This signals to `Context.init()` that the result is a tuple to be destructured, not a history array.

## Generating the Barrel File [#generating-the-barrel-file]

To regenerate the `request.index.ts` file:

```bash
npm run generate:request-index
```


# Technical Analysis (ta) (/developers/pinets/architecture/namespaces/ta)



## PineTS Technical Analysis (`ta`) Namespace [#pinets-technical-analysis-ta-namespace]

This directory contains the implementation of Pine Script's `ta.*` namespace functions. These functions are designed to replicate Pine Script's behavior in a JavaScript environment, with a strong focus on **incremental calculation** and **Series compatibility**.

## Architecture Overview [#architecture-overview]

Each TA function is implemented as a factory function that takes the execution `context` and returns the actual calculation function. This closure pattern allows functions to access the global state and other context methods efficiently.

```typescript
export function ema(context: any) {
    return (source: any, period: any, callId?: string) => {
        // Implementation...
    };
}
```

## The `param()` Method [#the-param-method]

The `param()` method is a critical component of the PineTS transpiler system. It is automatically injected by the transpiler to wrap arguments passed to namespace functions.

### Purpose [#purpose]

1. **Normalization**: Converts various input types (raw values, arrays, existing Series) into a unified `Series` object.
2. **Lookback Handling**: Manages the offset logic when accessing historical data (e.g., `close[1]`).
3. **Caching**: Generates unique IDs (`p0`, `p1`, etc.) to cache Series objects and avoid redundant object creation.

### How it Works [#how-it-works]

When you write `ta.ema(close, 14)` in PineTS code, the transpiler converts it to:

```javascript
ta.ema(ta.param(close, undefined, 'p0'), ta.param(14, undefined, 'p1'), '_ta0');
```

Inside a TA function implementation, inputs are always unwrapped using `Series.from()`:

```typescript
const value = Series.from(source).get(0); // Get current value
const length = Series.from(period).get(0); // Get period
```

## Incremental Calculation [#incremental-calculation]

PineTS uses incremental calculation to process time-series data efficiently. Instead of recalculating indicators over the entire history for every new bar, functions maintain **state** and update it with the new bar's data.

### State Management [#state-management]

All TA functions store their state in `context.taState`. A unique `callId` (generated by the transpiler) is used as the key to isolate state for different calls to the same function.

Example (EMA):

```typescript
// Unique key for this specific function call
const stateKey = _callId || `ema_${period}`;

// Initialize state if not present
if (!context.taState[stateKey]) {
    context.taState[stateKey] = { prevEma: null, initSum: 0, initCount: 0 };
}

const state = context.taState[stateKey];

// Update state with new value
const ema = calculateEma(currentValue, state.prevEma);
state.prevEma = ema;
```

### Benefits [#benefits]

* **Performance**: O(1) calculation per bar for most indicators.
* **Memory Efficiency**: Only necessary state (e.g., previous value, running sum) is stored, not full history arrays for intermediate steps.

## Implementation Specifics [#implementation-specifics]

### 1. Tuple Returns (Double Bracket Convention) [#1-tuple-returns-double-bracket-convention]

When a Pine Script function returns a tuple (multiple values), PineTS requires a specific return format to ensure it's treated as a single "element" of a Series, rather than an array of values spread across time.

**Rule**: Return tuples wrapped in **double brackets**.

```typescript
// Pine Script: [macd, signal, hist] = ta.macd(...)
// PineTS Implementation:
return [[macdLine, signalLine, histLine]];
```

* **Inner Array**: The actual tuple values `[a, b, c]`.
* **Outer Array**: Wraps the tuple so the Series initialization logic (`$.init()`) treats the inner array as a single value for the current bar.

### 2. Precision [#2-precision]

All numeric returns should be formatted using `context.precision()`. This ensures consistent decimal handling matching Pine Script defaults (usually 10 decimals).

```typescript
return context.precision(result);
```

### 3. NaN Handling [#3-nan-handling]

Functions must gracefully handle `NaN` inputs and initialization phases.

* **Input**: Check for `NaN` before updating state to avoid corruption (e.g., `initSum += NaN` results in permanent `NaN`).
* **Output**: Return `NaN` during the initialization phase (warm-up period) before enough data is available.

### 4. Unique Call IDs [#4-unique-call-ids]

The optional `_callId` parameter is crucial. It allows multiple calls to the same function (e.g., `ta.ema(close, 14)` called in two different places) to maintain separate states. The transpiler automatically injects these IDs.

## Example Implementation Structure [#example-implementation-structure]

```typescript
import { Series } from '../../../Series';

export function myIndicator(context: any) {
    return (source: any, length: any, _callId?: string) => {
        // 1. Unwrap inputs
        const period = Series.from(length).get(0);

        // 2. Initialize State
        if (!context.taState) context.taState = {};
        const stateKey = _callId || `myInd_${period}`;

        if (!context.taState[stateKey]) {
            context.taState[stateKey] = {
                /* initial state */
            };
        }

        // 3. Calculate
        const current = Series.from(source).get(0);
        // ... calculation logic ...

        // 4. Return result
        return context.precision(result);
    };
}
```

## Getter-Like Methods in TA Namespace [#getter-like-methods-in-ta-namespace]

Some Pine Script TA functions can be accessed both as properties and as methods. PineTS handles this through transpiler transformation.

### Example: `ta.tr` (True Range) [#example-tatr-true-range]

**Pine Script behavior:**

```javascript
// As property (getter)
tr1 = ta.tr  // Uses default behavior

// As method with parameter
tr2 = ta.tr(true)   // handle_na = true
tr3 = ta.tr(false)  // handle_na = false
```

**PineTS implementation:**

```typescript
export function tr(context: any) {
    return (handle_na?: any) => {
        // Default to true for backward compatibility
        const handleNa = handle_na !== undefined ? Series.from(handle_na).get(0) : true;

        const high0 = context.get(context.data.high, 0);
        const low0 = context.get(context.data.low, 0);
        const close1 = context.get(context.data.close, 1);

        if (isNaN(close1)) {
            return handleNa ? high0 - low0 : NaN;
        }

        return Math.max(high0 - low0, Math.abs(high0 - close1), Math.abs(low0 - close1));
    };
}
```

**Usage in PineTS:**

```javascript
// All these work correctly:
const tr1 = ta.tr; // Auto-converted to ta.tr()
const tr2 = ta.tr(); // Explicit call with default
const tr3 = ta.tr(true); // Explicit parameter
const tr4 = ta.tr(false); // Different behavior
```

### Implementation Guidelines [#implementation-guidelines]

When implementing getter-like methods:

1. **Always implement as a method** in `methods/` directory
2. **Use optional parameters** with sensible defaults
3. **Document the default behavior** in comments

The transpiler automatically handles the conversion from property access to method calls for all known namespaces (`ta`, `math`, `request`, `array`, `input`).

## Generating the Barrel File [#generating-the-barrel-file]

When you add a new method (e.g., `methods/newIndicator.ts`), you must regenerate the namespace index file (`ta.index.ts`) to export it.

**Command:**

```bash
npm run generate:ta-index
```

This script automatically:

1. Scans the `methods/` directory.
2. Generates imports for all detected files.
3. Updates `ta.index.ts` to register the new functions in the `TechnicalAnalysis` class.

**Note**: getter-like functions should be implemented as methods with no parameter.


# Context Class (/developers/pinets/architecture/runtime/context)



## The Context Class (`Context.class.ts`) [#the-context-class-contextclassts]

The `Context` is the central object (usually named `$`) passed to the transpiled function. It acts as the container for:

1. **Market Data**: `$.data.open`, `$.data.close`, etc.
2. **User Variables**: `$.let`, `$.const`, `$.var`.
3. **Function State**: `$.taState`, `$.params`.
4. **Namespaces**: `$.ta`, `$.math`, `$.request`, `$.pine`.

## Initialization (`init`) [#initialization-init]

The `init` method is used for every variable assignment.

```typescript
init(target, source, index = 0)
```

* If `target` exists, it updates the current value.
* If `target` is null, it creates a new `Series`.
* It handles converting raw values or arrays into `Series`.

## Accessors (`get` / `set`) [#accessors-get--set]

PineTS uses specific accessors to handle Pine Script indexing semantics.

* `get(series, index)`: Returns `series[length - 1 - index]`. Index 0 is the most recent item.
* `set(series, value)`: Updates `series[length - 1]`.

## Precision [#precision]

All numeric outputs are routed through `context.precision()` to enforce consistent decimal rounding (default 10 places), matching Pine Script behavior.


# Execution Flow (/developers/pinets/architecture/runtime/execution-flow)



## Execution Flow and Pagination [#execution-flow-and-pagination]

## Complete Execution Cycle [#complete-execution-cycle]

```
1. User calls: pineTS.run(code, periods)
   ↓
2. PineTS.ready() - Ensure market data loaded
   ↓
3. Initialize Context
   ↓
4. Transpile user code
   ↓
5. For each bar (iteration i):
   ┌─────────────────────────────────┐
   │ a. Set context.idx = i          │
   │ b. Push new OHLCV data          │
   │    (data.close.push(value))     │
   │ c. Execute transpiled function  │
   │ d. Collect result               │
   │ e. Grow all user variables      │
   │    ($.let.var.push(current))    │
   └─────────────────────────────────┘
   ↓
6. Return final Context with results
```

**Key Points:**

* Data arrays grow with `.push()` (forward append)
* Variables accessed via `$.get(var, index)` use Pine Script semantics
* Each iteration processes one bar, growing all series by one element

## The Run Loop (`PineTS.class.ts`) [#the-run-loop-pinetsclassts]

The `run()` method orchestrates the execution.

```typescript
async run(code) {
    // 1. Transpile Code
    const script = this.transpile(code);

    // 2. Loop through data
    for (let i = 0; i < data.length; i++) {
        this.context.idx = i;

        // Update context.data with current bar
        this.pushData(data[i]);

        // Execute script
        script(this.context);

        // Grow user variables (history)
        this.context.growVariables();
    }
}
```

## Data Flow During Iteration [#data-flow-during-iteration]

```
Iteration N (Processing Bar N):

┌──────────────────────┐
│  Market Data Arrays  │
│  [0, 1, 2, ..., N]   │  (Forward order)
└─────────┬────────────┘
          │ push(value[N])
          ▼
┌──────────────────────┐
│ Context.data.close   │
│  [0, 1, ..., N]      │  (Forward order)
└─────────┬────────────┘
          │ Access via $.get(close, index)
          ▼
┌──────────────────────┐
│  Transpiled Code     │
│  $.get(close, 0)     │  → close[N] (current)
│  $.get(close, 1)     │  → close[N-1] (previous)
└─────────┬────────────┘
          │ Results written to $.let.var
          ▼
┌──────────────────────┐
│  Variable Updates    │
│  $.set($.let.var, x) │  → var[var.length-1] = x
└─────────┬────────────┘
          │ push(current_value)
          ▼
┌──────────────────────┐
│  Series History      │
│  [0, 1, ..., N]      │  (Forward order)
└──────────────────────┘
```

## Pagination [#pagination]

For large datasets or streaming, PineTS supports pagination.

```javascript
const generator = pineTS.run(code, periods, pageSize);

for await (const pageContext of generator) {
    // pageContext contains only NEW results for this page
    console.log(pageContext.result);
}
```

**Key Features:**

* Processes data in chunks (pages)
* Maintains state across pages
* Supports live data streaming
* Automatically recalculates last candle on updates (for live data)
* Each page yields only new results, not cumulative

**Live Streaming Behavior:**

When live streaming is enabled (`eDate` undefined + provider source):

1. Fetches new candles from provider starting at last candle's openTime
2. Updates last candle if still open (same openTime)
3. Recalculates last bar's results to reflect updated data
4. Appends new complete candles
5. Yields `null` when no new data is available


# Runtime (/developers/pinets/architecture/runtime)



## PineTS Runtime Engine [#pinets-runtime-engine]

The runtime environment executes the transpiled code. It simulates the Pine Script execution model where the script runs once for every bar in the dataset.

## Key Components [#key-components]

* **PineTS Class**: The entry point. Manages the data provider, pagination, and execution loop.
* **Context Class**: The "Global State" passed to the script. Holds all variables, series data, and namespace instances.
* **Series Class**: The fundamental data structure wrapping arrays.

## Execution Model [#execution-model]

PineTS follows a "Push-Based" execution model:

1. **Load Data**: Fetch OHLCV data from a Provider.
2. **Initialize Context**: Create the `$` object.
3. **Run Loop**:
   * For each historical bar:
     * Update `context.idx`.
     * Push new OHLCV data into `context.data` arrays.
     * Execute the user script.
     * Grow all user variables (push current value to history).
   * Collect results/plots.

This ensures that `close[1]` always refers to the data from the previous iteration.


# Series Class (/developers/pinets/architecture/runtime/series)



## Series Class (`Series.ts`) [#series-class-seriests]

The `Series` class is the bridge between JavaScript arrays and Pine Script semantics.

## Storage Strategy: Forward vs. Reverse [#storage-strategy-forward-vs-reverse]

### Visual Representation [#visual-representation]

```
Pine Script Perspective (User View):
[Bar 0, Bar 1, Bar 2, Bar 3, ..., Bar N]
   ↑
  Current bar (index 0)

PineTS Internal Storage (Forward Order):
[Bar 0, Bar 1, Bar 2, ..., Bar N-1, Bar N]
   ↑                                   ↑
  Oldest bar                     Newest bar (current)

Series Access Layer:
Series.get(0) → Bar N (most recent)
Series.get(1) → Bar N-1 (previous)
Series.get(n) → Bar N-n (n bars ago)
```

### Why Forward Storage with Series Wrapper? [#why-forward-storage-with-series-wrapper]

1. **Pine Script Compatibility**: The Series class provides `series[0]` = current bar semantics
2. **Efficient Appending**: New bars are added with `.push()` at the end
3. **Natural Storage**: Matches chronological order of market data
4. **Reverse Index Calculation**: `Series.get(index)` translates to `data[length - 1 - index]`

**Internal Storage**: Standard JavaScript array, **forward chronological order** (oldest at index 0, newest at end).

**External Access**: Pine Script **reverse indexing** (0 = current, 1 = previous).

## The `Series` Wrapper [#the-series-wrapper]

```typescript
class Series {
    constructor(public data: any[], public offset: number = 0) {}

    // Get value at Pine Script index (0 = current, 1 = previous, etc.)
    get(index: number): any {
        const realIndex = this.data.length - 1 - (this.offset + index);
        if (realIndex < 0 || realIndex >= this.data.length) {
            return NaN;
        }
        return this.data[realIndex];
    }

    // Set value at Pine Script index
    set(index: number, value: any): void {
        const realIndex = this.data.length - 1 - (this.offset + index);
        if (realIndex >= 0 && realIndex < this.data.length) {
            this.data[realIndex] = value;
        }
    }
}
```

**Key Features:**

* **Offset Support**: Enables lookback operations like `close[1]` by creating a new Series with `offset = 1`
* **Automatic NaN**: Returns NaN for out-of-bounds access (Pine Script behavior)
* **Forward Array**: Wraps a standard forward-ordered array

### Offset Handling [#offset-handling]

When you do `close[1]`, PineTS doesn't copy the array. It creates a lightweight `Series` wrapper with `offset = 1`.

```
close (offset = 0):
Series.get(0) → data[length - 1 - (0 + 0)] = data[length - 1] = last element

close[1] (offset = 1):
Series.get(0) → data[length - 1 - (1 + 0)] = data[length - 2] = second-to-last
```

* `close`: `offset = 0`. `get(0)` -> last element.
* `close[1]`: `offset = 1`. `get(0)` -> second to last element.

This allows efficient passing of "historical views" to functions without data duplication.

## Series Growth Pattern [#series-growth-pattern]

At the end of each iteration, all series grow by pushing the current value:

```
Before iteration N+1: [val0, val1, ..., valN]
                                          ↑ current
                                          index N-1

After processing N+1:  [val0, val1, ..., valN, valN+1]
                                          ↑       ↑
                                          [1]     [0] current
                                          index   index N
                                          N-1

Access via $.get():
  $.get(arr, 0) → arr[N]     (current)
  $.get(arr, 1) → arr[N-1]   (previous)
```

## Series.from() Helper [#seriesfrom-helper]

Many TA functions use `Series.from()` to normalize inputs:

```typescript
static from(source: any): Series {
    if (source instanceof Series) return source;
    if (Array.isArray(source)) return new Series(source);
    return new Series([source]); // Wrap scalar in array
}
```

This allows functions to accept:

* Series objects (pass through)
* Arrays (wrap in Series)
* Scalar values (wrap in single-element array, then Series)


# Specifics (/developers/pinets/architecture/specifics)



## Critical Implementation Details [#critical-implementation-details]

This section covers specific architectural patterns used to solve complex problems in Pine Script emulation.

* [Tuple Handling](/developers/pinets/architecture/specifics/tuples): How `[a, b]` is distinguished from `[history]`.
* [Request Security](/developers/pinets/architecture/specifics/request-security): Multi-timeframe context management and recursion prevention.


# Request Security (/developers/pinets/architecture/specifics/request-security)



## Request Security Architecture [#request-security-architecture]

`request.security` is the most complex function in PineTS as it involves creating and managing secondary execution contexts.

## Context Hierarchy [#context-hierarchy]

PineTS creates a strictly **Two-Level Hierarchy** to prevent infinite recursion.

1. **Primary Context**: The main script execution.
2. **Secondary Context**: Created by `request.security` to run the same script on a different timeframe/symbol.

**Guard**: Secondary contexts have `isSecondaryContext = true`. If `request.security` is called inside a secondary context, it returns the expression directly instead of creating a tertiary context.

## Caching Strategy [#caching-strategy]

Since creating a `PineTS` instance and running a script is expensive, secondary contexts are aggressively cached.

### Cache Key [#cache-key]

The cache key is composite:

```typescript
key = `${symbol}_${timeframe}_${expressionID}`;
```

* `expressionID`: The unique ID (`p0`, `p1`...) of the expression parameter passed to `security`. This is why `request.param()` returns `[val, name]`.

## Data Flow [#data-flow]

1. **User calls `request.security(..., expression)`**.
2. **Primary Context**:
   * Checks cache.
   * If miss: Creates `new PineTS(...)` for target timeframe.
   * Sets `isSecondaryContext = true`.
   * Runs the script.
   * Stores result in cache.
   * Extracts result aligned to primary timeframe.
3. **Secondary Context**:
   * Runs the same script.
   * When it encounters `request.security`:
   * Checks `isSecondaryContext`.
   * Returns `expression` directly.
   * This effectively "unwraps" the security call inside the secondary context, allowing the script to calculate the expression natively in that timeframe.


# Tuple Handling (/developers/pinets/architecture/specifics/tuples)



## Tuple Handling Architecture [#tuple-handling-architecture]

Pine Script supports tuples (functions returning `[a, b]`). PineTS has a specific architecture to handle this while distinguishing tuples from time-series arrays.

## The Problem [#the-problem]

Distinguishing between:

1. **Tuple**: `[val1, val2]` (Result of `ta.macd(...)`)
2. **Time-Series Array**: `[bar0, bar1, bar2]` (History of a variable)

## The Solution [#the-solution]

### 1. Double Bracket Convention `[[...]]` [#1-double-bracket-convention-]

Functions that return tuples must wrap them in an outer array:

```typescript
// Implementation of a tuple-returning function
return [[macd, signal, hist]];
```

### 2. `Context.init` Detection [#2-contextinit-detection]

When assigning a variable, `Context.init` checks the structure:

```typescript
if (Array.isArray(src)) {
    if (Array.isArray(src[0])) {
        // It's a tuple wrapped in 2D array [[a, b]]
        // Extract inner array
        value = src[0]; 
    } else {
        // It's a 1D history array [old, ..., new]
        // Extract last element
        value = src[src.length - 1];
    }
}
```

### 3. `request.security` Tuple Handling [#3-requestsecurity-tuple-handling]

`request.security` also follows this convention. If the requested expression returns a tuple, `security` wraps the result in `[[...]]` before returning it to the primary context.

### 4. Transpiler Destructuring [#4-transpiler-destructuring]

The transpiler handles the destructuring assignment:

```javascript
// User: const [a, b] = func();
// Transpiled: 
// const temp = func();
// const a = $.init(..., $.get(temp, 0)[0]);
// const b = $.init(..., $.get(temp, 0)[1]);
```


# Transpilation Examples (/developers/pinets/architecture/transpiler/examples)



## Real Transpilation Examples [#real-transpilation-examples]

This page shows actual transpiler output to help understand the transformation process.

## Example 1: Basic Variable Assignment [#example-1-basic-variable-assignment]

**Input:**

```javascript
let sma = ta.sma(close, 20);
```

**Transpiled Output:**

```javascript
const { close } = $.data;
const ta = $.ta;
const p0 = ta.param(close, undefined, 'p0');
const p1 = ta.param(20, undefined, 'p1');
const temp_1 = ta.sma(p0, p1, '_ta0');
$.let.glb1_sma = $.init($.let.glb1_sma, temp_1);
```

**Key Transformations:**

* Implicit imports injected (`close`, `ta`)
* Arguments wrapped in `ta.param()` with unique IDs (`p0`, `p1`)
* Function call receives unique call ID (`_ta0`)
* Nested call hoisted to temporary variable (`temp_1`)
* Variable renamed with scope prefix (`glb1_sma`)
* Assignment wrapped in `$.init()`

## Example 2: Array Access and Assignment [#example-2-array-access-and-assignment]

**Input:**

```javascript
let prev_close = close[1];
cc = close[2];
```

**Transpiled Output:**

```javascript
const { close } = $.data;
$.let.glb1_prev_close = $.init($.let.glb1_prev_close, $.get(close, 1));
$.set($.let.glb1_cc, $.get(close, 2));
```

**Key Transformations:**

* `close[1]` → `$.get(close, 1)` (Pine Script semantics)
* `cc = close[2]` → `$.set($.let.glb1_cc, $.get(close, 2))`
* Series access translated to context methods

## Example 3: Binary Operations [#example-3-binary-operations]

**Input:**

```javascript
const green_candle = close > open;
const bull_bias = ema9 > ema18;
```

**Transpiled Output:**

```javascript
const { close, open } = $.data;
$.const.glb1_green_candle = $.init($.const.glb1_green_candle, $.get(close, 0) > $.get(open, 0));
$.const.glb1_bull_bias = $.init($.const.glb1_bull_bias, $.get($.const.glb1_ema9, 0) > $.get($.const.glb1_ema18, 0));
```

**Key Transformations:**

* Binary operations preserve structure
* Series values extracted with `$.get(series, 0)`
* User variables accessed through context (`$.const.glb1_ema9`)

## Example 4: Nested Function Calls (Expression Hoisting) [#example-4-nested-function-calls-expression-hoisting]

**Input:**

```javascript
let d = ta.ema(math.abs(ap - 99), 10);
```

**Transpiled Output:**

```javascript
const ta = $.ta;
const math = $.math;
const p0 = math.param($.get($.let.glb1_ap, 0) - 99, undefined, 'p0');
const temp_1 = math.abs(p0);
const p1 = ta.param(temp_1, undefined, 'p1');
const p2 = ta.param(10, undefined, 'p2');
const temp_2 = ta.ema(p1, p2, '_ta0');

$.let.glb1_d = $.init($.let.glb1_d, temp_2);
```

**Key Transformations:**

* Nested `math.abs(ap - 99)` hoisted to `temp_1`
* Each function call isolated with unique IDs
* Inner function executed first, result passed to outer function
* Ensures proper execution order for stateful functions

## Example 5: Scoped Variables (If Statement) [#example-5-scoped-variables-if-statement]

**Input:**

```javascript
let aa = 0;
if (_cc > 1) {
    let bb = 1;
    aa = 1;
}
```

**Transpiled Output:**

```javascript
$.let.glb1_aa = $.init($.let.glb1_aa, 0);
if ($.get($.const.glb1__cc, 0) > 1) {
    $.let.if2_bb = $.init($.let.if2_bb, 1); // Scoped to 'if2'
    $.set($.let.glb1_aa, 1); // Updates global scope
}
```

**Key Transformations:**

* Global variable: `glb1_aa`
* If-scoped variable: `if2_bb` (different prefix)
* Assignment to existing variable uses `$.set()`
* New variable declaration uses `$.init()`

## Example 6: Equality Checks (NaN Handling) [#example-6-equality-checks-nan-handling]

**Input:**

```javascript
if (avg_len === 0) {
    ret_val = cc[1];
}
```

**Transpiled Output:**

```javascript
if ($.math.__eq($.get(avg_len, 0), 0)) {
    $.set($.let.fn2_ret_val, $.get($.let.fn2_cc, 1));
}
```

**Key Transformations:**

* `===` → `$.math.__eq()` (handles `NaN == NaN` correctly)
* Variables accessed with `$.get()`
* Assignment uses `$.set()`

## Example 7: Array Pattern Destructuring [#example-7-array-pattern-destructuring]

**Input:**

```javascript
let [a, b] = ta.supertrend(close, 10, 3);
```

**Transpiled Output:**

```javascript
const p0 = ta.param(close, undefined, 'p0');
const p1 = ta.param(10, undefined, 'p1');
const p2 = ta.param(3, undefined, 'p2');
const temp_1 = ta.supertrend(p0, p1, p2, '_ta0');

let a = $.init($.let.glb1_a, $.get($.const.glb1_temp_1, 0)[0]);
let b = $.init($.let.glb1_b, $.get($.const.glb1_temp_1, 0)[1]);
```

**Key Transformations:**

* Function call hoisted to `temp_1`
* Destructuring split into individual assignments
* Each element accessed via `$.get(temp, 0)[index]`
* Tuple convention: `[[a, b]]` returned by function, destructured here

## Example 8: Array Expression in request.security [#example-8-array-expression-in-requestsecurity]

**User Variables:**

```javascript
const o = open;
const c = close;
const [res, data] = await request.security('BTCUSDC', '240', [o, c], false, false);
```

**Transpiled Output:**

```javascript
$.const.glb1_o = $.init($.const.glb1_o, open);
$.const.glb1_c = $.init($.const.glb1_c, close);
const p2 = request.param([$.get($.const.glb1_o, 0), $.get($.const.glb1_c, 0)], undefined, 'p2');
const temp_1 = await request.security('BTCUSDC', '240', p2, false, false);
// ... destructuring follows
```

**Direct Series:**

```javascript
const [res, data] = await request.security('BTCUSDC', '240', [open, close], false, false);
```

**Transpiled Output:**

```javascript
const p2 = request.param([open, close], undefined, 'p2');
const temp_1 = await request.security('BTCUSDC', '240', p2, false, false);
// ... destructuring follows
```

**Key Transformations:**

* User variables in arrays: `[o, c]` → `[$.get(o, 0), $.get(c, 0)]`
* Direct Series: `[open, close]` → passed through unchanged
* `request.param()` handles tuple detection internally

## Example 9: For Loop with Scope [#example-9-for-loop-with-scope]

**Input:**

```javascript
let sum = 0;
for (let i = 0; i < 10; i++) {
    sum += values[i];
}
```

**Transpiled Output:**

```javascript
$.let.glb1_sum = $.init($.let.glb1_sum, 0);
for (let i = 0; i < 10; i++) {
    // Loop variable NOT transformed
    $.set($.let.glb1_sum, $.get($.let.glb1_sum, 0) + $.get($.let.glb1_values, i));
}
```

**Key Transformations:**

* Loop variable `i` remains unchanged (not a time-series)
* `sum` transformed to `$.let.glb1_sum`
* Compound assignment `+=` becomes explicit get/set
* `values[i]` uses standard array access (not Pine Script semantics)

## Data Flow Visualization [#data-flow-visualization]

### Variable Assignment Flow [#variable-assignment-flow]

```
User Code: let ema9 = ta.ema(close, 9);
                ↓
        Transpiler Analysis
                ↓
    ┌───────────────────────┐
    │ 1. Inject imports     │
    │    const { close } =  │
    │    $.data             │
    └───────────┬───────────┘
                ↓
    ┌───────────────────────┐
    │ 2. Wrap arguments     │
    │    p0 = ta.param(...) │
    │    p1 = ta.param(...) │
    └───────────┬───────────┘
                ↓
    ┌───────────────────────┐
    │ 3. Add call ID        │
    │    ta.ema(p0, p1,     │
    │    '_ta0')            │
    └───────────┬───────────┘
                ↓
    ┌───────────────────────┐
    │ 4. Hoist to temp      │
    │    temp_1 = ...       │
    └───────────┬───────────┘
                ↓
    ┌───────────────────────┐
    │ 5. Rename variable    │
    │    glb1_ema9          │
    └───────────┬───────────┘
                ↓
    ┌───────────────────────┐
    │ 6. Wrap in $.init()   │
    │    $.let.glb1_ema9 =  │
    │    $.init(...)        │
    └───────────────────────┘
```

## Understanding the Transformation Rules [#understanding-the-transformation-rules]

| Original Pattern     | Transformed Pattern                           | Reason                            |
| -------------------- | --------------------------------------------- | --------------------------------- |
| `let x = value`      | `$.let.scope_x = $.init(...)`                 | State persistence                 |
| `x = value`          | `$.set($.let.scope_x, value)`                 | Update current value              |
| `x[1]`               | `$.get(x, 1)`                                 | Pine Script indexing              |
| `func(arg)`          | `func(ns.param(arg, undefined, 'p0'), '_id')` | Series wrapping + state isolation |
| `a == b`             | `$.math.__eq(a, b)`                           | NaN comparison                    |
| `const [a, b] = f()` | Split into individual inits                   | Tuple destructuring               |


# Transpiler (/developers/pinets/architecture/transpiler)



## PineTS Transpiler Architecture [#pinets-transpiler-architecture]

The transpiler is responsible for converting user-written "PineTS" code (which mimics Pine Script syntax) into executable JavaScript that runs within the PineTS runtime environment.

## The Pipeline [#the-pipeline]

The transpilation process follows a multi-pass pipeline:

```
Input Code String/Function
        ↓
┌─────────────────────────┐
│  0. Wrapper Check     │
│  • Detect if code is  │
│    already wrapped in │
│    a function         │
│  • Auto-wrap unwrapped│
│    code in (context)  │
│    => { ... }         │
└───────┬─────────────────┘
        ↓
┌─────────────────────────┐
│  1. Parse to AST      │ (using acorn)
│     (ECMAScript AST)  │
└───────┬─────────────────┘
        ↓
┌─────────────────────────┐
│  2. Pre-Processing    │
│  • Transform nested   │
│    arrow functions    │
│  • Normalize native   │
│    imports (aliasing) │
│  • Inject implicit    │
│    imports (context.*)│
│  • Identify context-  │
│    bound variables    │
└───────┬─────────────────┘
        ↓
┌─────────────────────────┐
│  3. Analysis Pass     │
│  • Register functions │
│  • Track parameters   │
│  • Build scope tree   │
│  • Destructure arrays │
└───────┬─────────────────┘
        ↓
┌─────────────────────────┐
│  4. Transformation    │
│  • Variable scoping   │
│  • Series wrapping    │
│  • Expression Unwrapping│ (Hoisting)
│  • param() injection  │
│  • ID generation      │
└───────┬─────────────────┘
        ↓
┌─────────────────────────┐
│  5. Post-Processing   │
│  • Transform == to    │
│    $.math.__eq()      │
└───────┬─────────────────┘
        ↓
┌─────────────────────────┐
│  6. Code Generation   │ (using astring)
│     (Final JS code)   │
└─────────────────────────┘
```

### Pipeline Details [#pipeline-details]

1. **Pre-Processing**:

   * **Wrapper Check**: Ensures code is wrapped in a function: `(context) => { ... }`.
   * **Implicit Imports**: Injects `const { close } = context.data` etc. if missing.
   * **Normalization**: Prevents renaming of native symbols (e.g. `const { close: c } = ...` -> `const { close } = ...`).

2. **Parsing**:

   * Uses `acorn` to parse code into an ESTree-compatible AST.

3. **Analysis Pass (`AnalysisPass.ts`)**:

   * Scans the AST to build a **Scope Tree**.
   * Registers function declarations and parameters.
   * Identifies "Context-Bound" variables (variables that come from `context.data` or `context.pine`).

4. **Transformation Pass (`MainTransformer.ts`)**:

   * Traverses the AST and modifies nodes based on rules.
   * **Variable Renaming**: Transforms `let x = 10` into `$.let.glb1_x = ...` using the `ScopeManager`.
   * **Series Wrapping**: Wraps inputs to functions with `param()`.
   * **Expression Unwrapping**: Hoists complex expressions into temporary variables to ensure correct execution order.

5. **Post-Processing**:

   * Transforms equality checks (`==`, `===`) into `$.math.__eq()` calls to handle `NaN` correctly.

6. **Code Generation**:
   * Uses `astring` to generate the final JavaScript string from the modified AST.

## Key Components [#key-components]

* **ScopeManager**: Tracks variable scopes, renames variables to avoid collisions (`glb1_`, `fn2_`), and manages unique IDs.
* **Transformers**: Specialized classes (`ExpressionTransformer`, `StatementTransformer`) that handle specific AST node types.


# Scope Manager (/developers/pinets/architecture/transpiler/scope-manager)



## Scope Manager and Variable Transformation [#scope-manager-and-variable-transformation]

One of the transpiler's most critical jobs is managing variable scope and state persistence across bar iterations.

## The Problem [#the-problem]

In standard JavaScript:

```javascript
let x = 10; // Re-initialized every time the function runs?
```

If the entire indicator function runs for *every bar*, `let x = 10` would reset `x` every time. Pine Script variables need to maintain history (`x[1]`).

## The Solution: Context Object [#the-solution-context-object]

The transpiler moves all variables into a persistent `context` object (`$`).

**User Code:**

```javascript
let ema9 = ta.ema(close, 9);
```

**Transformed Code:**

```javascript
$.let.glb1_ema9 = $.init($.let.glb1_ema9, temp_result);
```

### Variable Renaming [#variable-renaming]

To prevent naming collisions between scopes (e.g., a variable `x` in the global scope vs `x` in a function), the `ScopeManager` assigns unique prefixes:

* `glb1_`: Global scope variables.
* `fn{id}_`: Function scope variables.
* `if{id}_`: If-block scope variables.
* `for{id}_`: For-loop scope variables.

This ensures that even if the user reuses variable names, they map to distinct properties on the context object.

## ID Generation [#id-generation]

The transpiler generates unique IDs for:

1. **Parameters (`p0`, `p1`...)**: Passed to `param()` to cache Series objects.
2. **Function Calls (`_ta0`, `_ta1`...)**: Passed to namespace functions to maintain internal state (like `prevEma`).
3. **Cache Keys**: For caching intermediate results.

This allows functions like `ta.ema` to identify *which* call they are handling, even if called multiple times with the same parameters.


# Transformers (/developers/pinets/architecture/transpiler/transformers)



## Code Transformers [#code-transformers]

The transformation logic is split into specialized transformers found in `src/transpiler/transformers/`.

## WrapperTransformer [#wrappertransformer]

Ensures the user code is wrapped in a standard context function:

```javascript
(context) => {
    // user code
}
```

## NormalizationTransformer [#normalizationtransformer]

Prevents users from aliasing standard Pine Script symbols in import destructuring. This ensures the transpiler can reliably identify standard variables like `close` or `ta`.

## InjectionTransformer [#injectiontransformer]

Scans for usage of standard variables (like `close`, `open`) and injects the necessary destructuring statements if they are missing:

```javascript
const { close, open } = context.data;
```

## MainTransformer & StatementTransformer [#maintransformer--statementtransformer]

These handle the bulk of the logic:

* **Variable Declaration**: Transforms `let x = ...` into `$.let.scope_x = $.init(...)`.
* **Assignment**: Transforms `x = ...` into `$.set($.let.scope_x, ...)`.
* **Array Access**: Transforms `x[1]` into `$.get($.let.scope_x, 1)`.
* **Loops/Conditionals**: Ensures scoping rules are respected within blocks.

## ExpressionTransformer [#expressiontransformer]

Handles expressions, primarily function calls and binary operations.

* **Function Calls**: Injects `param()` wrappers around arguments.
* **Hoisting**: "Unwraps" nested calls. `ta.ema(ta.sma(close, 10), 10)` is transformed into:
  1. Calculate SMA -> temp var.
  2. Calculate EMA using temp var.
     This simplifies the generated code and ensures proper order of operations for stateful functions.