Skip to main content

Strategy Limitations

Strategies is coming soon and starts as a beta. This page lists everything it does not do yet, and every place a strategy running in NinjaTrader can behave differently from the same script on TradingView. Read it once before you run a strategy on a live account, and run every new strategy on a Sim account first.

What it runs​

Strategies and indicators. A script that starts with strategy( runs as a strategy and places its own orders. One that starts with indicator( or study( runs as an indicator: it places no orders itself, and its alerts do what you set (see Indicators below). A library pasted on its own runs its example code, as TradingView does when a library is added to a chart.

NinjaTrader 8 only. Strategies run in the CrossTrade add-on, v1.14.0 or later. They are not available for Tradovate accounts connected directly to CrossTrade.

NinjaTrader has to stay open. The strategy evaluates on your computer. If NinjaTrader closes, the computer sleeps, or the add-on disconnects, the strategy stops evaluating until it is back. Stops and targets already sent to a live broker stay at the broker. On a Sim account they live inside NinjaTrader, so they do nothing while NinjaTrader is closed. Run NinjaTrader on a machine that stays on.

One account per strategy. A strategy trades the one account you pick. To run the same script on another account, use Make a copy under Settings and pick the other account for the copy.

One strategy per account and contract. A strategy will not start on an account and contract that another strategy or automation is already trading, or where the account already holds a position the strategy did not open.

A paid plan to run. Pasting, converting and editing are free. Starting a strategy needs a paid plan or the free trial. You can save up to 50 strategies and run up to 10 at the same time.

Instruments and contracts​

Fifteen markets. ES, NQ, YM, RTY, CL, GC, SI, ZB, MES, MNQ, MYM, MCL, MGC, M2K and 6E. Your NinjaTrader data feed has to provide the contract.

One contract for the whole run. When a strategy starts, the market you picked resolves to the front-month contract (for example MES 12-26), and the strategy trades that contract until it stops. It does not roll over by itself. Before the roll, close the position and stop the strategy, then start it again after the roll to move it to the new contract. Strategies with Auto start move to the new front month the next time NinjaTrader connects.

Other symbols need NinjaTrader data. A request.security call on another symbol works when NinjaTrader has data for that symbol (for example NQ1! maps to the NQ front month). A symbol NinjaTrader does not have stops the start with a message.

History and warm-up​

About 400 bars of history. When a strategy starts, it warms up on the most recent closed bars NinjaTrader has: 400 by default, more when the script reads further back (a close[3000], or a 3,000-bar average, warms up on 3,000 bars), up to 5,000. A max_bars_back in the strategy() line on its own raises it to at most 2,000. TradingView may run the same script over tens of thousands of bars. An indicator that depends on a very long look-back, or a var that accumulates from the first bar of the chart, can start from a different value than it has on TradingView.

open, high, low, close and time more than 5,000 bars back are empty at first. TradingView lets a script read these up to 10,000 bars back, and so does CrossTrade, but the warm-up stops at 5,000 bars. A read further back than that is na until enough live bars have closed to reach it.

A computed look-back has to fit that history. A length written in the script or set by an input is sized when the script converts. A length the script works out while it runs (one that changes from bar to bar) can reach only the history the strategy keeps; if it asks for more, the strategy stops with "A length of 500 bars reaches further back than the 400 bars of history this script keeps". Add max_bars_back with that length to the strategy() line (up to 2,000) and start it again.

Lists filled over time can still be short. A script that fills an array as months or years pass (a monthly results table, for example) may find it empty on NinjaTrader's shorter history and stop with "Array index out of bounds". TradingView stops such a script too when its chart is short.

Long intervals have little history. Bar intervals go up to 1 week; monthly intervals are not offered. On weekly bars NinjaTrader often cannot load 400 bars, so the strategy warms up on fewer bars or on live bars as they close. Intraday and daily intervals work best.

bar_index counts from the start of the warm-up. On TradingView it counts from the first bar of your chart; here it counts from the first of the warm-up bars. A script that uses bar_index as a clock (bar_index % 20 == 0, bar_index > 5000) or counts back from last_bar_index can act on different bars.

Every run starts flat. A position the strategy holds at the end of its warm-up history is closed on paper, so a new run starts flat and waits for its next signal. TradingView would show that position as still open. The orders waiting on that position go with it, including a pending entry that would have reversed it, so a reversal TradingView shows at that point does not happen here. When the warm-up ends flat, an entry the last closed bar signalled is not lost: it goes in on the first live price, so a strategy can enter right after you press Start.

The chart starts when the run starts. Plots, shapes and trades are drawn for the time the strategy has been running. The warm-up history is not drawn.

Orders and fills​

Fills come from your broker. Live entries and exits are real orders, filled at real prices with real slippage. TradingView's backtest fills at its own modeled prices, so the trades and results will not be identical.

A stop the market has already passed goes in at market. NinjaTrader refuses a stop order on the wrong side of the market, which TradingView would fill at once. CrossTrade sends it at market instead, so the fill can be worse than the stop price. The same goes for a stop entry or stop loss the script moves past the market, and for a stop NinjaTrader refuses because the market reached it while the order was on its way. The run shows a notice when it happens.

Stop entries on both sides (a straddle). When a strategy places a long and a short stop entry while it is flat and one of them fills, TradingView turns the other into a reversal: if it fills too, the position flips to the other side. Here the other entry keeps its own size, so if it fills it only closes the position and the strategy is flat. To get the same result on both, put the two entries in one OCA group (oca_name with oca_type=strategy.oca.cancel) so the second is cancelled when the first fills, or cancel it yourself with strategy.cancel.

A close leaves the other stop and limit entries working. As on TradingView, strategy.close and strategy.close_all close the position but leave the script's pending stop and limit entries in place. An entry on the opposite side is taken off while the close fills and then placed again, so NinjaTrader may show it cancelled and sent again. An entry that was sized to reverse the position keeps that size, as on TradingView: from flat it opens the larger position.

An order priced at zero or below. This usually means a stop or target distance written for a market priced far higher, for example a 90-point stop on silver near 36. CrossTrade follows TradingView: a sell stop or a buy limit at or below zero (a long's stop, a short's target, a short entry's stop, a long entry's limit) never fills, so it is left out and the strategy shows a notice once. That position then has no stop working, and the strategy page warns about it. A buy stop or a sell limit at or below zero would fill at once on TradingView, like a market order; instead of surprising you with that fill, the strategy stops and names the price. Set the script's stop and target inputs for the instrument you run it on. A level that is still 0 while the strategy is flat (var float stop = 0.0, set when it enters) is fine.

Whole contracts only. Orders are in whole contracts. A size worked out by strategy.percent_of_equity or strategy.cash comes from the strategy's own simulated equity (initial_capital plus its results), not from your account balance. A percent-of-equity size leaves room for the commission in your strategy() line, as TradingView does. When that money buys less than one contract (2% of $500,000 does not buy one NQ contract), no order is placed and the strategy shows a notice saying so.

A Pine v6 script that sets no order size trades 1 contract. On TradingView, a v6 strategy() line without default_qty_type trades 100% of a $100,000 starting equity: no ES or NQ contract at all, and several contracts of a micro. CrossTrade trades 1 contract instead, and Settings > Order size shows it, so set the size you mean there. A v6 script that sizes by strategy.percent_of_equity or strategy.cash without an initial_capital starts from $100,000, as on TradingView (v5 and older scripts start from $1,000,000).

Margin calls happen in the warm-up only. TradingView closes part of a position when the strategy's simulated equity no longer covers its margin_long or margin_short. CrossTrade does that during the warm-up history, so a run starts in the state TradingView shows, but a live strategy never sends a margin-call order: your broker enforces real margin. The strategy shows a notice once if TradingView would have margin-called it.

Performance uses your script's commission. The Performance tab applies the commission_type and commission_value in your strategy() line, not your broker's actual fees.

Account rules apply. Entries go through the same checks as your CrossTrade webhook orders. Opposing Position Protection can refuse an entry that would open the opposite side of a position on another account in the same market (ES and MES count as one market). The strategy acts again at its next signal.

TradingView settings that do not come across​

CrossTrade reads the script, nothing else. Settings you changed in TradingView's strategy Properties dialog instead of in the code are not part of the script: initial capital, base currency, order size, pyramiding, commission, slippage, margin, "Recalculate after order is filled", "On every tick", "Fill orders on bar close" and "Using bar magnifier". The values written in your strategy() line are used instead. use_bar_magnifier=true in the code is accepted, but the warm-up history fills on the chart's own bars rather than lower-timeframe ones; live fills are real broker fills either way. You can change the order size under Settings; for the rest, put the value in the strategy() line and convert again.

Your TradingView chart does not come across either. CrossTrade runs on regular candles of the bar interval you pick, during the trading hours NinjaTrader uses for the contract. A strategy you tested on Heikin Ashi, Renko or another chart type, or with extended hours turned off, will see different bars here.

Indicators​

Plots draw on the CrossTrade chart. An indicator's plots, shapes and drawings show on the chart on its Overview tab. They are not added to a NinjaTrader chart window.

Alert conditions trigger once per bar close. An alertcondition() alert triggers when its condition is true at a bar close, like TradingView's Once Per Bar Close. TradingView's Once Per Bar and Only Once choices are not offered for alert conditions. An alert() call triggers as often as its freq argument says.

Up to five alerts per indicator. Each alert listens to one condition, or to any alert, and does one thing. To send one condition to two places, use two of the five.

Alert titles and messages are fixed text. An alertcondition() title and message must be written in the script (text, or text joined with +), as TradingView requires. Placeholders such as {{close}} in the message are filled in when it triggers; a placeholder TradingView does not document is sent as written.

An alert that places an order uses your webhook rules. Its command goes through the same checks as a CrossTrade webhook order: account rules, Opposing Position Protection, rate limits and the kill switch. The indicator does not track the position its alerts open, so a stop or target has to be part of the command (for example a bracket) or managed in NinjaTrader.

TradingView's alert dialog settings do not come across. An alert's expiration, its sound, and the options in TradingView's Create Alert dialog are not part of the script. CrossTrade alerts stay on until you change or remove them.

Alerts​

alert() calls and alert_message arguments convert, and nothing is sent until you choose where they go under Settings > Alerts: a notification, or a webhook of your own (see Alerts). Alerts never place orders. A CrossTrade webhook URL is refused as a destination, because the strategy already places its own orders and sending one would trade twice. Settings in TradingView's Create Alert dialog (its message, its frequency, its webhook URL) are not part of the script and do not come across.

An alert_message built from values computed in the same block as the order call is worked out when the order is placed, so any prices in it are from that bar. TradingView works it out when the order fills; for a market order filled at the next bar's open, the two are the same.

Changing a running strategy​

Edits apply at the next start. A running strategy keeps the script, inputs, order size, account, instrument and bar interval it started with. Stop it and start it again to pick up a change.

Stopping and restarting​

Stop leaves the position open. Stop cancels the strategy's working entry orders, but its position and its stop and target orders stay. Close the position before you stop if you want to be flat.

A stopped strategy still owns its position. While its position or its stop and target orders are open, CrossTrade's close buttons, webhook orders and other CrossTrade orders on that contract and account are refused. Close them in NinjaTrader itself.

After NinjaTrader restarts, a running strategy starts again on fresh history. When that history ends in exactly the position your account holds, it takes the position over, priced from its own history rather than your actual fill, and that trade is not counted in Performance. When the account holds anything else, it stays stopped until you clear the position in NinjaTrader. See Running strategies.

After a CrossTrade update, a strategy converts again. When CrossTrade updates its converter, each strategy converts again from its saved Pine the next time it starts or resumes after a restart, so it never runs an out-of-date conversion. A fix can make a script behave slightly differently from before (closer to TradingView). If the new version cannot convert a script, the strategy does not start or resume, and its Pine Script tab says why.

Scripts that will not convert​

CrossTrade never converts part of a script. If anything below is in it, the conversion stops and names the line.

What the script usesWhy
request.financial, request.earnings, request.dividends, request.splits, and fundamental syminfo fields such as shares outstandingNinjaTrader has no company fundamentals
request.economic, request.quandl, request.seedNinjaTrader has no economic, Nasdaq Data Link or TradingView community data
request.currency_rate between two different currenciesNinjaTrader gives scripts no exchange rates
A later contract month, such as ES2!NinjaTrader follows only the front month (ES1!) or a contract you name
A seconds timeframe in a request, such as "1S"Not supported yet
request.footprintVolume footprint data is not available to converted scripts yet
A timeframe= argument in the declarationNot supported yet; call request.security in the script instead
syminfo.expiration_dateNinjaTrader does not pass the contract's last trading day to scripts yet
A list of tables (array.new_table, table.all)Not supported yet; single tables work
An imported library that is not built in and whose source you have not pastedCrossTrade bundles TradingView's own libraries and about 380 published open-source ones (Libraries). A private or invite-only library has to be pasted
Part of a script, copied from a TradingView script page without pressing ExpandThe rest of the code is missing; press Expand or open it in the Pine Editor, then copy all of it
An alert_message built from an indicator call, a request.security call or an arrayWorking it out only when the order is placed would change the indicator's state; compute it into a variable first
Code TradingView itself rejectsThe converter checks the same rules

Very large libraries convert slowly. A script that imports libraries adding up to about 200,000 characters or more (3D drawing, neural network or planetary ephemeris libraries, for example) can take a few minutes to convert, and some are still too large to run. A conversion that runs past about three minutes stops and says so. Most libraries are far smaller and convert in a second or two.

A script that requests many other symbols or timeframes, or reads a very long history, can convert but be too large to start in NinjaTrader. The start says so; request fewer symbols or timeframes, or read less history.

A script that does a lot of work on every price change (many requests, long loops, large arrays) can fall behind when the market moves fast. One that recalculates on every price (calc_on_every_tick = true) catches up by evaluating the newest price, as TradingView's own realtime updates are batched, and always evaluates the bar close. If a run still falls thousands of prices behind, for example in the burst of a major news release, it stops rather than build its bars from prices it missed, and says that NinjaTrader sent prices faster than it could process them. Start it again; if it keeps happening, lighten the script or run it on a quieter instrument.

If a script runs on TradingView and does not convert here, send it to support with the line the conversion named.