# Automating TradingView with CrossTrade — Complete AI Assistant Guide > **If you are a human reading this:** copy this entire file and paste it into your AI assistant > (Claude, ChatGPT, Gemini, or any other), then paste your Pine Script source code (or a link to > the script) and say what you want to automate. The AI will identify what kind of script you > have, tell you whether it can be automated, and build your exact alert settings and payload. > If you can't share the script's source, paste this file anyway: the AI will ask you a few > questions about what you see in the alert dialog and work from that. > > **If you are an AI assistant reading this:** this file is your complete, authoritative > reference for wiring TradingView alerts to CrossTrade. Follow the workflow in Section 1. > Everything you need is in this document. **If a command, field, or value is not in this > document, it does not exist. Never invent fields.** Canonical docs live at https://crosstrade.io/docs — this file is a condensed, self-contained version for AI consumption. CrossTrade routes TradingView webhook alerts to **NinjaTrader 8** (via the CrossTrade NT8 Add-On running on the user's machine) or directly to **Tradovate** (cloud, no desktop software required). --- ## 1. Instructions for the AI assistant Your job is to get this specific user from "I have a TradingView script" to "my alerts place orders correctly," or to tell them clearly and early that their script cannot be automated as-is and what their options are. ### Workflow 1. **Gather facts.** Before generating anything, you need: - Which platform executes the trades: NinjaTrader 8 or Tradovate (Section 3). - The exact broker account name (e.g. `Sim101` for the NT8 sim account). Never guess it. - The Pine Script source, or if unavailable, the user's answers to the classification questions in Section 4. - What they want automated: entries only, entries and exits, brackets (TP/SL), one account or several. 2. **Classify the script** using the decision tree in Section 4. This determines everything else. 3. **Generate the deliverable.** Always output all four parts: - Which case their script is (in one plain sentence). - The exact TradingView alert dialog settings: Condition, trigger option, Message content, Notifications tab (Section 5). - The complete alert Message payload, ready to paste (Sections 6 and 7). One payload per alert; some cases need two or more alerts. - The test protocol from Section 9, adapted to their setup. 4. **If they're authoring or editing Pine Script**, use the templates in Section 8. ### Hard rules - **Never invent payload fields, commands, or placeholder variables.** Only what is documented here is valid. Unknown text inside the alert message causes the alert to fail. - **Always test on a simulation account first.** NT8: `Sim101`. Tradovate: a Demo account. Say this every time you hand over a payload. - **The secret key is sensitive.** It goes in the payload (`key=...;`) or in a Pine input, but warn the user never to publish a script or screenshot containing their real key. - **Every payload line ends with a semicolon.** The single most common failure. - **The alert Message must contain only the CrossTrade payload** (plus optional `//` comment lines). Any other text, including the default text TradingView pre-fills, breaks the alert. - **Be honest about dead ends.** A closed-source script with no usable alert conditions cannot be automated (Section 10). Say so directly and give the user their real options. Do not string them along. - **Do not fabricate performance claims** about the user's strategy, and do not present any automation as risk-free. You are wiring plumbing, not giving financial advice. --- ## 2. How CrossTrade works, in 30 seconds ``` TradingView alert fires │ TradingView POSTs the alert's Message text to the user's CrossTrade webhook URL ▼ CrossTrade (cloud) authenticates the key, validates the payload, routes it │ ├── default ──────────► NinjaTrader 8 desktop (CrossTrade NT8 Add-On executes the order) │ └── destination=tradovate; ──► Tradovate cloud API (no desktop software involved) ``` Two credentials, both found on the user's **My Account** page (https://crosstrade.io/user/my-account) and on the Webhooks dashboard: - **Webhook URL**: pasted into the alert's Notifications tab. TradingView requires 2FA to be enabled on the TradingView account before webhooks can be used; it prompts for this the first time. - **Secret Key**: the `key=...;` line inside every payload. It goes between the `=` and the `;`. The alert **Message** is the entire instruction set. TradingView substitutes any `{{...}}` placeholders at fire time, then delivers the final text. CrossTrade parses it and executes. --- ## 3. Step 1: Which destination? | | NinjaTrader 8 (default) | Tradovate (`destination=tradovate;`) | |---|---|---| | How orders execute | CrossTrade NT8 Add-On inside the user's running NinjaTrader 8 | CrossTrade sends orders to Tradovate's cloud API | | Requires | NT8 open with a connected data feed, Add-On installed, logged in, green indicator | A Tradovate account linked under My Account → Brokers tab | | Computer can be off? | No | Yes, for market and absolute-price orders | | Account name | The NT8 account Name property (e.g. `Sim101`, `APEX1234`) | The Tradovate account name shown on the Brokers tab | - Omitting `destination` routes to NinjaTrader 8. `destination=nt8;` is the explicit form. Only `nt8` and `tradovate` are valid values (case-insensitive); anything else is rejected. - **Prop firm / funded accounts on Tradovate** (Apex, Topstep, etc.): link with **Link Demo** and the firm-issued login. These are simulation-environment accounts even when funded. **Link Live** is only for a personal real-money Tradovate login. - Tradovate has real capability differences from NT8 (extra order types, some rejected fields). See Section 6.8 before reusing an NT8 payload on Tradovate. --- ## 4. Step 2: Classify the script (the decision tree) This is the most important step. TradingView scripts expose alerts in different ways, and the mechanism determines where the CrossTrade payload can go. Get this wrong and the user sees confusing dialog behavior (a Message box that "only wants a name," conditions that don't appear, alerts that never fire). **First, which kind of script?** If the Pine source starts with `strategy(...)`, it's a strategy (the chart shows a Strategy Tester tab when it's applied). If it starts with `indicator(...)`, it's an indicator. **If you cannot see the source** (invite-only / protected script), ask the user: 1. When you add an alert and open the **Condition** dropdown and select the script, what appears in the box next to or below it? Named entries like "Buy Signal" / "Bearish Cross"? Options mentioning "order fills"? Only something like "Any alert() function call"? 2. Does the script add a **Strategy Tester** tab at the bottom of the chart? 3. Can you edit the source (does the script show an "unlock" / source view in the Pine editor)? Then match against the cases below. ### Case A — Strategy, user can edit the source (best case) **How to tell:** `strategy(...)` declaration, source editable. **What to do:** the `alert_message` pattern. Build the full CrossTrade payload as a string inside the Pine code and pass it to `strategy.entry()` / `strategy.exit()` / `strategy.close()` via the `alert_message=` parameter. Template in Section 8.1. **Alert dialog:** Condition = the strategy; choose the trigger option **"Order fills only"**. Message field contains exactly one line: ``` {{strategy.order.alert_message}} ``` This is the only approach that supports per-signal computed values (calculated stops from tick math, different payloads for long vs. short, quantity from ATR). It is the canonical pattern when the user controls the code. ### Case B — Strategy, user cannot edit the source **How to tell:** `strategy(...)` / Strategy Tester tab present, but the script is third-party, built-in, or protected. **What to do:** paste the payload into the alert dialog's Message field and let TradingView's strategy placeholders supply the direction and size: ``` key=your-secret-key; command=place; account=Sim101; instrument=ES1!; action={{strategy.order.action}}; qty={{strategy.order.contracts}}; order_type=market; tif=day; flatten_first=true; ``` **Alert dialog:** Condition = the strategy, firing on the strategy's order fills (not "Once"). One alert covers both directions because `{{strategy.order.action}}` resolves to `buy` or `sell` at fire time. **Critical caveat:** every TradingView strategy must have offsetting actions (an entry eventually has an exit; this is a Pine requirement). `{{strategy.order.action}}` only says `buy` or `sell`; it cannot distinguish an entry from an exit. Handle this with either `flatten_first=true;` (simple stop-and-reverse behavior) or Strategy Sync (Section 6.6) for position tracking. Do not hand over a Case B payload without one of the two. ### Case C — Indicator with `alertcondition()` calls **How to tell:** when the indicator is selected in the alert dialog's Condition dropdown, a second dropdown lists named conditions ("Buy Signal", "Regular Bearish Divergence", etc.). In source, `alertcondition(...)` calls. **What to do:** one alert **per condition**, each with a complete static payload in the Message field. Typically two alerts: the buy condition with `action=buy;`, the sell condition with `action=sell;`. The two payloads are identical except for `action`. Add `flatten_first=true;` to both for clean stop-and-reverse. Full recipe in Section 7.2. Indicators do not have strategy placeholders. `action` and `qty` must be hardcoded. Available indicator placeholders: `{{ticker}}`, `{{close}}`, `{{open}}`, `{{high}}`, `{{low}}`. **Watch for:** if the condition dropdown for the indicator shows no named signals, this is not Case C. Check Cases D and E. ### Case D — Script that fires `alert()` function calls **How to tell:** the Condition dropdown offers **"Any alert() function call"** (indicator) or **"alert() function calls only"** (strategy), and when selected, **the dialog does not offer a usable Message box — it effectively only asks for an alert name.** This confuses everyone. **Why:** with `alert()`-based conditions, the message text is defined **inside the Pine code**, in the `alert("...", freq)` call itself. The dialog's message is not used. There is nowhere in the dialog to put a payload, by design. **What to do:** - **Source editable:** edit the `alert()` calls so the string they emit IS the CrossTrade payload. Template in Section 8.3. Alert dialog: Condition = the script, "Any alert() function call" / "alert() function calls only", name it, add the webhook URL. Done. - **Source not editable:** the payload cannot be injected from the dialog, so this mechanism is unusable for CrossTrade. Check whether the script *also* exposes `alertcondition()` entries (then use Case C). If it doesn't, this is Case F. - **Strategies:** if the script is a strategy, prefer the order-fills route (Case A or B) over `alert()` where possible; it's better supported and carries the strategy placeholders. ### Case E — Script with no alert plumbing at all, source editable **How to tell:** no named conditions in the dropdown, no alert() option, but the user has the source (often their own or copy-pasted code). **What to do:** add the plumbing, then proceed as Case C (add `alertcondition()`, indicator only) or Case A / D (strategies / `alert()`). Templates in Section 8. Adding two `alertcondition()` lines is usually a four-line change. ### Case F — Closed-source, no usable conditions (the honest dead end) **How to tell:** protected/invite-only script, nothing usable in the Condition dropdown, only after-the-fact drawn arrows or labels on the chart. **What to do:** tell the user plainly: **this script cannot be automated as-is.** Chart drawings are not alertable, and there is no way to attach a payload. Their real options: 1. Ask the script's author to add `alertcondition()` calls for the buy/sell signals (a small change on the author's side, and a common request). 2. Find or build an open alternative with the same logic (Section 8 helps them build one). 3. If signals repaint (appear after the fact), automation would misfire even with plumbing; see Section 10. Do not attempt workarounds like price-level alerts approximating the signals unless the user explicitly understands they are no longer automating the script, just a guess at it. --- ## 5. Step 3: The TradingView alert dialog, field by field 1. **Open the alert window**: the clock/alarm icon on the chart's right sidebar or top toolbar. 2. **Condition**: select the strategy or indicator (not a price level), then the specific condition/trigger per the case in Section 4. 3. **Trigger frequency**: - Strategies: fire on the strategy's order fills every time (never "Once"). - Indicator conditions: "Every time the condition is met" is the norm. If a condition stays true across many bars and floods orders, switch to "Once per bar close" and/or add `require_market_position=flat;` to the payload. 4. **Expiration**: user's choice; expired alerts silently stop firing. Flag it if they set one. 5. **Message**: **delete all default text first.** TradingView pre-fills this box with the script's default message and that text will break the alert. Paste only the CrossTrade payload (Case B/C) or only `{{strategy.order.alert_message}}` (Case A). Not applicable in Case D (the box isn't used). 6. **Notifications tab**: check the **Webhook URL** box, paste the CrossTrade webhook URL. First-time webhook users are prompted by TradingView to enable 2FA; enable it, come back, paste, save. ### Dialog gotchas that burn beginners - **Alerts snapshot the script's settings at creation time.** Change any input afterward (lengths, quantity, risk settings, the key input from Section 8.1) and the existing alert keeps using the OLD values with no warning. Fix: delete the alert, create a new one. This is the #1 "why is CrossTrade receiving the wrong quantity" cause. - **Payload assigned to the wrong condition** (SELL payload on the buy condition) = inverted trades. Double-check condition ↔ payload pairing on multi-alert setups. - **Renko / Range / Line Break / Kagi / P&F charts**: bars only print after the move completes, so alerts fire late and `{{close}}` is the synthetic bar close, not live price. Warn the user. CrossTrade's free TRUE OHLC indicator (TradingView Tools section of their account) overlays real-time OHLC on such charts. - TradingView itself can deliver alerts a few seconds late around the top of the hour and major news. Out of everyone's control; design accordingly. --- ## 6. Payload reference ### 6.1 Format rules - One `field=value;` per line (newlines optional but recommended). **Every field ends with `;`.** - Field names use underscores where a space would be: `order_type`, `stop_price`, `oco_id`. - Values are case-insensitive (`command=place;` ≡ `command=PLACE;`, `action=buy;` ≡ `action=BUY;`). - Comment lines start with `//` and are ignored. `notes=any text;` attaches a note that shows in Alert History / Activity logs (and on Tradovate, on the order itself). - Multiple accounts: comma-separated INSIDE one `account` field: `account=APEX1234,APEX1235;` (comma between accounts, semicolon at the end; a semicolon between accounts is a classic error). - `key=...;` is required on every payload. ### 6.2 Commands | Command | What it does | Key fields | |---|---|---| | `place` | Submit a new order. The workhorse. | See 6.3 | | `flatplace` | Flatten same account+instrument and cancel its working orders, confirm both, then place the entry. Equivalent to `place` + `flatten_first=true;`. | Same as `place` | | `closeposition` | Close a position. Partial close: `quantity=N;` (up to N contracts) or `percent=0.5;` (fraction 0–1, rounds up; `quantity` wins if both). | `account`, `instrument` | | `flatten` | Close positions matching optional filters: `account`, `instrument`, `market_position` (`long`/`short`). **With no filters it closes every open position on every linked account.** Always confirm scope with the user before handing this over unfiltered. | filters optional | | `flatteneverything` | Flatten every position AND cancel every working order across all accounts. (Tradovate: optional `account` limits the sweep.) | none required | | `reverse` | Requires an open position: flattens it, clears its working orders, then enters the same quantity in the opposite direction at market. | `account`, `instrument` | | `reverseposition` | Flatten first, then enter with the explicit direction/qty/type/prices you supply. | like `place` | | `cancel` | Cancel one order by `order_id` (the id you set on the original place; tracked 7 days. Tradovate also accepts the raw numeric broker id). | `order_id` | | `cancelorders` | Cancel working orders on one account, optionally narrowed to one `instrument`. | `account` | | `cancelallorders` | Cancel every working order across all linked accounts. | none required | | `change` | Modify a working order by `order_id`: quantity and applicable limit/stop prices (Tradovate also: type, TIF, expiry, trail offset, iceberg size). | `order_id` + changed fields | | `cancelreplace` | Guarded cancel-then-place (not broker-atomic). Advanced. | `order_id` + new order fields | | `cancelandbracket` | Cancel the instrument's working orders and wrap the LIVE position in a TP/SL OCO. `action` = the side of the position being protected (buy protects a long), not the exit side. Requires an open matching position. | `account`, `instrument`, `action`, `take_profit`, `stop_loss` | | `closestrategy` | Close a running NinjaScript strategy by `strategy_id`. **NT8 only** (rejected on Tradovate). | `strategy_id` | ### 6.3 `place` core fields | Field | Values | Required? | |---|---|---| | `key` | The user's secret key | Always | | `command` | `place` | Always | | `account` | Exact account name (NT8 Name property / Tradovate account name). Comma list for multi-account. | Always | | `instrument` | `ES1!` (TradingView continuous, auto-resolves front month; recommended), `ES 09-26` (NT8 style), `ESU6` (Tradovate style). Indicator alerts can use `instrument={{ticker}};`. | Always | | `action` | `buy` / `sell` (or `{{strategy.order.action}}` in Case B) | Always | | `qty` | Whole integer (or `{{strategy.order.contracts}}` in Case B). Fractional quantities are rejected. | Always | | `order_type` | `market`, `limit`, `stopmarket`, `stoplimit`. Tradovate adds `mit`, `trailingstop`, `trailingstoplimit`. | Always | | `tif` | `day` or `gtc`. Tradovate adds `ioc`, `fok`, `gtd` (+ required `expire_time=` ISO-8601). | NT8: required. Tradovate: optional | | `limit_price` | Decimal or relative (6.5). Required for `limit` / `stoplimit` (and Tradovate `mit` touch price). | Conditional | | `stop_price` | Decimal or relative. Required for `stopmarket` / `stoplimit` / trailing types. The trigger price ALWAYS goes in `stop_price`, never `limit_price`. | Conditional | Sending a buy and then a sell of the same qty offsets to flat; it does not create a short. For directional flips use `flatten_first=true;`, `reverse`, or `reverseposition`. ### 6.4 Brackets: `take_profit` / `stop_loss` Add to a `place` to create protective orders around the entry (OCO-linked; on Tradovate they become a server-side bracket that survives disconnects): ``` take_profit=50 ticks; stop_loss=30 ticks; ``` - Positive magnitudes; CrossTrade orients them from `action` (buy: TP above, SL below; sell: reversed). - On limit/stop entries, brackets wait for the entry fill before being submitted, and relative values are computed from the actual fill price. - NT8-only stop-limit stop-loss leg: `stop_loss_stop=` + `stop_loss_limit=` (rejected on Tradovate). - Alternative on NT8: `atm_strategy=TemplateName;` applies a saved NinjaTrader ATM template instead (see 6.7). Use payload brackets when levels change per trade; ATMs are fixed. ### 6.5 Relative price syntax The six price fields (`take_profit`, `stop_loss`, `stop_loss_stop`, `stop_loss_limit`, `limit_price`, `stop_price`) accept, besides absolute decimals: | Form | Examples | |---|---| | Percent | `5%`, `2.5 %` | | Ticks | `12 ticks`, `1tick`, `7.5 ticks` | | Points | `15 points`, `12pts`, `-12pts` | | Dollars | `$500`, `-$500`, `$1,000.50` (dollar move in the underlying's price, not position P&L) | - TP/SL fields: positive magnitude, direction derived from `action`. - Entry fields (`limit_price`, `stop_price`): explicitly signed. Positive = above the current quote, negative = below, regardless of action. Buy limit 10 ticks below market: `limit_price=-10 ticks;`. - CrossTrade resolves relative values to an absolute tick-rounded price using a fresh quote (NT8: the Add-On's feed; Tradovate: CrossTrade's own server-side pricing, which **fails closed**: no fresh quote, no order). - **Misspelled units (`10 tic`) are NOT caught** and pass through as garbage NT8 won't error on. Spell `ticks`, `points`, `%`, `$` exactly. ### 6.6 Safety, sync, and flow-control fields (all optional, all on `place` unless noted) | Field | What it does | |---|---| | `flatten_first=true;` | Close any existing position and cancel pending orders on this instrument before the entry. The default recommendation for simple stop-and-reverse automations and for anything using ATMs. | | `require_market_position=flat;` | A gate, not an action: blocks the alert unless the account's position in this instrument matches. Values: `flat`, `long`, `short`, or comma combos (`flat,long` for long-only setups). Evaluated per-instrument. Blocks, never closes. | | `max_positions=1;` | Blocks an opening order that would add a new instrument position when the account already holds that many. Scaling an existing instrument doesn't consume a slot. | | `sync_strategy=true;` + `market_position={{strategy.market_position}};` + `prev_market_position={{strategy.prev_market_position}};` + `out_of_sync=...;` | **Strategy Sync** (strategies only). Before executing, compares TradingView's expected position transition against the real broker position. `out_of_sync` values: `wait` (block, do nothing), `flatten` (close to reset; the right default), `resync` (auto-correct the broker position to match), `ignore` (proceed anyway; advanced). | | `strategy_exit_block=true;` | With Strategy Sync: drop signals where the strategy is exiting a position (prev position not flat), letting only fresh entries through. Required when an ATM or payload brackets manage exits, so TradingView's mandatory exit signals don't fight them. | | `trading_window=09:30-16:00 ET;` | Only accept the alert inside the window; outside it the alert is silently dropped. Also: `start_time`, `end_time`, `closing_only_after`, and `bypass_trade_windows=true;` to override account/global windows. | | `delay=30;` | Delay execution up to 60 seconds. | | `rate_limit=60; id=my-group;` | Rate-limit alerts sharing the same id. | | `cancel_after=15;` | Cancel an unfilled limit order after N minutes (1–180). On Tradovate this is scheduled broker-side and fires even if everything else is offline. | | `order_id=my-unique-id;` | Your handle on the placed order for later `cancel`/`change`/`cancelreplace` (tracked 7 days). Advanced; one-way webhook flows rarely need it. | | `cycle_accounts=true;` | With a comma account list: each signal goes to ONE account in rotation instead of all. Skips accounts locked by an Account Manager monitor. | | `oco_id=any-string;` | NT8 only: group orders into an OCO manually. On Tradovate, use `take_profit`+`stop_loss` instead (auto-OCO). | | `destination=tradovate;` | Route to the linked Tradovate account (6.8). Omit for NT8. | How the overlapping protections differ: `flatten_first` is an action (clears the deck, then enters), `require_market_position` is a gate (rejects the signal, touches nothing), `out_of_sync` is a recovery policy (what to do when TV and broker disagree), `max_positions` is an account-wide guard. They can be combined; `require_market_position` is checked first. ### 6.7 NinjaTrader ATM strategies `atm_strategy=TemplateName;` (alias `strategy=`) on a `place` makes the entry fill trigger a saved NT8 ATM template (its stops/targets are managed by NinjaTrader on the desktop). - The name must match the saved template exactly: case-sensitive, spaces included. - **The alert `qty` must match the ATM template's order quantity.** Mismatches cause silent failures or a position opened with only the innermost bracket. No error is raised. Check this every time. - Always pair with `flatten_first=true;`: a plain sell after an ATM buy offsets the position but **orphans the ATM's stop and target orders**. `closeposition` / `reverseposition` / `flatten_first` clean them up. - When the ATM closes the trade (TP/SL hit), TradingView doesn't know. With Strategy Sync you'll see expected out-of-sync events; `out_of_sync=flatten;` + `strategy_exit_block=true;` is the standard pairing (recipe 7.4). - Dynamic per-trade levels can't live in an ATM template; use payload brackets (6.4) instead. - `strategy_id=` attaches an additional order to an already-active ATM (scaling in). Advanced, NT8 only. ### 6.8 Tradovate destination differences Add `destination=tradovate;` and note: - **Extra capabilities:** server-side OSO brackets from `take_profit`/`stop_loss`; native trailing stops (`order_type=trailingstop;` + `stop_price` + `trail_offset=`; `trailingstoplimit` additionally needs `limit_price`); `mit` order type (touch price in `limit_price`); iceberg via `max_show=`; TIFs `ioc`, `fok`, `gtd` (+`expire_time=`); broker-side `cancel_after`; execution with the user's computer off (except relative prices, which need CrossTrade's live pricing and fail closed without it). - **`tif` is optional** on Tradovate entries (required on NT8). - **Accepted but ignored:** `strategy_tag`. - **Rejected with a clear error:** `strategy_id`, `append_atm`, `oco_id`, `stop_loss_stop`, `stop_loss_limit`, `playbook`, `custom_tag`, and the `closestrategy` command. - `atm_strategy=`/`strategy=` on Tradovate resolves a **CrossTrade-stored** Tradovate ATM template (managed in the CrossTrade dashboard), never an NT8 desktop template. Inline fields also exist: `atm_targets`, `atm_stops`, `atm_qtys`, `atm_trail`, `atm_trail_trigger`, `atm_trail_offset`, `atm_breakeven`, `atm_breakeven_offset`. - **"Accepted" is not "resting":** Tradovate can accept an order and reject it ~1 second later (e.g. price outside exchange limits). CrossTrade re-checks and flags the Alert History row with a yellow warning carrying Tradovate's reason. Tell users with prices near limits to check back. ### 6.9 TradingView placeholder variables (the complete usable set) Strategy alerts (Case A/B): | Placeholder | Resolves to | |---|---| | `{{strategy.order.action}}` | `buy` / `sell` | | `{{strategy.order.contracts}}` | Order quantity (use this one for futures) | | `{{strategy.order.alert_message}}` | The exact string set via `alert_message=` in Pine (Case A). Empty if the script never sets it, which makes the alert fail. | | `{{strategy.market_position}}` | Position after this order: `long` / `short` / `flat` (for Strategy Sync) | | `{{strategy.prev_market_position}}` | Position before this order (for Strategy Sync) | Any alert, including indicators (Case C): | Placeholder | Resolves to | |---|---| | `{{ticker}}` | Chart symbol (use as `instrument={{ticker}};`) | | `{{close}}` `{{open}}` `{{high}}` `{{low}}` | Current bar prices (usable in price fields; on Renko-style charts these are synthetic values, see Section 5) | Do not use placeholders not listed here in CrossTrade payloads. --- ## 7. Ready-made recipes Replace `your-secret-key`, account names, and instruments with the user's real values. Everything below defaults to NT8; append `destination=tradovate;` (and check 6.8) for Tradovate. ### 7.1 Third-party strategy, simple stop-and-reverse (Case B minimal) One alert, Condition = the strategy, firing on order fills: ``` key=your-secret-key; command=place; account=Sim101; instrument=ES1!; action={{strategy.order.action}}; qty={{strategy.order.contracts}}; order_type=market; tif=day; flatten_first=true; ``` ### 7.2 Indicator with buy/sell conditions (Case C): two alerts BUY alert (Condition = the buy signal): ``` key=your-secret-key; command=place; account=Sim101; instrument=ES1!; action=buy; qty=1; order_type=market; tif=day; flatten_first=true; ``` SELL alert (Condition = the sell signal): identical except `action=sell;`. Optional third/fourth alert for a distinct exit signal: ``` key=your-secret-key; command=closeposition; account=Sim101; instrument=ES1!; ``` ### 7.3 Strategy with full Strategy Sync (Case B, position tracking) For strategies with clean entries and exits (long → flat → short, not constant flipping): ``` key=your-secret-key; command=place; account=Sim101; instrument=ES1!; action={{strategy.order.action}}; qty={{strategy.order.contracts}}; order_type=market; tif=day; sync_strategy=true; market_position={{strategy.market_position}}; prev_market_position={{strategy.prev_market_position}}; out_of_sync=flatten; ``` Stop-and-reverse strategies never go flat, so sync can't work for them: use 7.1 instead. ### 7.4 Strategy + NT8 ATM template (NT8 exits managed by NinjaTrader) ``` key=your-secret-key; command=place; account=Sim101; instrument=ES1!; action={{strategy.order.action}}; qty={{strategy.order.contracts}}; order_type=market; tif=day; flatten_first=true; atm_strategy=YourATMTemplateName; sync_strategy=true; market_position={{strategy.market_position}}; prev_market_position={{strategy.prev_market_position}}; out_of_sync=flatten; strategy_exit_block=true; ``` Remember: template name exact-match, alert qty = ATM template qty, and out-of-sync warnings after the ATM closes a trade are expected behavior. ### 7.5 Entry with payload brackets (works both destinations) ``` key=your-secret-key; command=place; account=Sim101; instrument=ES1!; action=buy; qty=1; order_type=market; tif=day; flatten_first=true; take_profit=50 ticks; stop_loss=30 ticks; ``` ### 7.6 Prop-firm multi-account (Tradovate example) ``` key=your-secret-key; command=place; account=APEX1234,APEX1235; instrument=ES 09-26; action=buy; qty=1; order_type=market; take_profit=5300.00; stop_loss=5285.00; destination=tradovate; ``` Each account executes independently and gets its own Alert History row. Add `cycle_accounts=true;` to rotate one account per signal instead. ### 7.7 Long-only entry gate Add to any entry payload to skip signals while a position is open (no closing, just skipping), and to only ever be flat or long: ``` require_market_position=flat,long; ``` --- ## 8. Writing Pine Script for CrossTrade (the authoring guide) Use this section when the user owns the code: Case A (strategy), Case D with editable source, Case E, or "build me a strategy from scratch." **If the user asks you to write a strategy from scratch**, first collect: entry rules, exit rules (opposite signal? fixed ticks? trailing?), instrument and timeframe, contracts per trade, and any session restriction. Then adapt template 8.1. Never silently invent trading logic; confirm the rules back to them in plain language before or alongside the code. Remind them a backtest in the Strategy Tester is not a guarantee of live behavior. ### 8.1 Strategy template with `alert_message` (Case A, the canonical pattern) Complete, compilable Pine v6. Entries carry their full CrossTrade payload; the alert dialog needs only `{{strategy.order.alert_message}}` with Condition = "Order fills only". ```pinescript //@version=6 strategy("XT SMA Cross", overlay = true, initial_capital = 100000) // ── Inputs ───────────────────────────────────────────────────────────── // The key lives in an input, not in the code, so the script can be shared // without leaking it. Remember: alerts snapshot inputs at creation time — // after changing ANY input, delete and recreate the alert. xtKey = input.string("", "CrossTrade secret key") xtAccount = input.string("Sim101", "Account name") contracts = input.int(1, "Contracts", minval = 1) fastLen = input.int(9, "Fast SMA") slowLen = input.int(21, "Slow SMA") targetTicks = input.int(50, "Take profit (ticks)", minval = 0) stopTicks = input.int(30, "Stop loss (ticks)", minval = 0) // ── Signal logic (replace with the user's rules) ─────────────────────── fast = ta.sma(close, fastLen) slow = ta.sma(close, slowLen) goLong = ta.crossover(fast, slow) goShort = ta.crossunder(fast, slow) // ── CrossTrade payload builder ───────────────────────────────────────── // {{ticker}} is substituted by TradingView at alert time. // take_profit / stop_loss use CrossTrade's relative tick syntax, so no // price math is needed and levels are computed from the actual fill. xtPayload(string act) => string p = "key=" + xtKey + ";" p += "command=place;" p += "account=" + xtAccount + ";" p += "instrument={{ticker}};" p += "action=" + act + ";" p += "qty=" + str.tostring(contracts) + ";" p += "order_type=market;" p += "tif=day;" p += "flatten_first=true;" p += targetTicks > 0 ? "take_profit=" + str.tostring(targetTicks) + " ticks;" : "" p += stopTicks > 0 ? "stop_loss=" + str.tostring(stopTicks) + " ticks;" : "" p // ── Orders ───────────────────────────────────────────────────────────── if goLong strategy.entry("Long", strategy.long, qty = contracts, alert_message = xtPayload("buy")) if goShort strategy.entry("Short", strategy.short, qty = contracts, alert_message = xtPayload("sell")) ``` Alert dialog for this script: Condition = "XT SMA Cross" → **Order fills only**; Message = `{{strategy.order.alert_message}}` (nothing else); Notifications → Webhook URL. Because the payload includes `flatten_first=true;` and brackets, exits are handled by CrossTrade, and long/short payloads can diverge freely (edit `xtPayload` per side if asked). Pine gotcha worth repeating to users: a variable assigned inside an `if` block can be `na` or stale when the alert fires. Build payload values from inputs or globally-scoped series (as above), not from locals computed in another branch. ### 8.2 Adding `alertcondition()` to an indicator (Case E → Case C) `alertcondition()` works in indicators only. It doesn't fire anything by itself; it adds named entries to the alert dialog's Condition dropdown. The CrossTrade payload then goes in the **dialog's Message box**, one alert per condition (recipe 7.2). ```pinescript //@version=6 indicator("My Indicator", overlay = true) fast = ta.sma(close, 9) slow = ta.sma(close, 21) buySignal = ta.crossover(fast, slow) sellSignal = ta.crossunder(fast, slow) plotshape(buySignal, style = shape.triangleup, location = location.belowbar, color = color.green) plotshape(sellSignal, style = shape.triangledown, location = location.abovebar, color = color.red) // These two lines are the entire CrossTrade integration: alertcondition(buySignal, title = "Buy signal", message = "Buy fired") alertcondition(sellSignal, title = "Sell signal", message = "Sell fired") ``` The `message` argument here is only the dialog's default text; the user replaces it with the CrossTrade payload when creating each alert. ### 8.3 Using `alert()` with the payload in code (Case D, source editable) With `alert()`, the emitted string IS the delivered message, so it must be the complete CrossTrade payload. The alert dialog's message box is not used (this is why it "only asks for a name"). Works in indicators and strategies. ```pinescript //@version=6 indicator("My Alert() Indicator", overlay = true) xtKey = input.string("", "CrossTrade secret key") xtAccount = input.string("Sim101", "Account name") fast = ta.sma(close, 9) slow = ta.sma(close, 21) buySignal = ta.crossover(fast, slow) sellSignal = ta.crossunder(fast, slow) xtPayload(string act) => "key=" + xtKey + ";command=place;account=" + xtAccount + ";instrument={{ticker}};action=" + act + ";qty=1;order_type=market;tif=day;flatten_first=true;" if buySignal alert(xtPayload("buy"), alert.freq_once_per_bar_close) if sellSignal alert(xtPayload("sell"), alert.freq_once_per_bar_close) ``` Alert dialog: Condition = the script → **"Any alert() function call"** (indicators) or **"alert() function calls only"** (strategies), give it a name, add the webhook URL. One alert covers all `alert()` calls in the script. Use `alert.freq_once_per_bar_close` to avoid intrabar repeat-firing on unconfirmed bars. ### 8.4 Beginner guardrails for AI-written Pine - Target `//@version=6`. Declare exactly one of `strategy()` / `indicator()`. - Fire on confirmed bars (`alert.freq_once_per_bar_close`, or strategies evaluated on bar close) unless the user explicitly wants intrabar signals; intrabar conditions can trigger and then un-trigger ("repainting"), which places real orders on signals that vanish. - Avoid `request.security()` calls with lookahead, and any logic that repaints, in anything meant to trade real money. If the user's reference indicator repaints, say so. - Whole-number contracts only; CrossTrade/NT8 reject fractional qty. - Keep the secret key in an `input.string` (never hardcode it in shareable code), and remind the user: changing an input requires recreating the alert. --- ## 9. Testing protocol (walk the user through this every time) 1. **Sim first.** NT8: `account=Sim101;` (markets closed? connect NT8's "Simulated Data Feed" for dummy data). Tradovate: a Demo account. Only switch the account name to a live/funded account after a verified sim round-trip. 2. **Need a fast trigger?** CrossTrade's free **XT Troubleshooting Indicator** on TradingView fires a signal every bar (use "Once per bar close" + `flatten_first=true;`). The **XT Strategy Help** script does the same for strategy-variable testing. Both are linked from the docs and the TradingView Tools section of the account. 3. **Trace the pipeline in order:** - TradingView's alert log (clock icon panel) shows the alert fired and the final message text after placeholder substitution. Not firing? The problem is TradingView-side: condition, expiration, or repainting signals. - CrossTrade **Alert History** (https://crosstrade.io/user/alert-history) shows the received payload, its parse/validation result, destination, and any warnings (including sync mismatches and late Tradovate rejections). Fired in TV but nothing here? Wrong webhook URL. - NT8: the Add-On logs each request; the platform's Orders tab shows the order. Tradovate: the order appears in Tradovate's own UI. 4. **Common failures, fastest checks:** | Symptom / error | Cause | |---|---| | Formatting error | Missing `;` at a line end, `;` instead of `,` between accounts, or a required field missing for the command | | Nothing executes, alert shows in TV | Webhook URL missing/wrong in Notifications tab, or non-payload text left in the Message box | | Wrong qty / old settings | Alert snapshot is stale: delete and recreate the alert after any input change | | "No market data available to drive the simulation" | Expired contract month in `instrument`: use `ES1!` or update to front month | | "Something went wrong, check message format" | Fractional `qty`: whole contracts only | | ATM order doesn't open / opens without brackets | ATM template name mismatch (case-sensitive) or alert qty ≠ ATM template qty; also check Chart Trader "Select active ATM strategy on order submission" | | "Unable to submit to infrastructure" / "Contact Administrator" | Prop firm has disabled the account; contact the firm | | Account not connected warning | NT8 not connected to that account/feed (Tradovate-in-NT8 users: sign in via NT8's own connection dialog, not just the browser) | | Inverted trades | Buy/sell payloads swapped between the two alert conditions | | Tradovate row turns yellow after success | Late broker rejection (e.g. price outside exchange limits); the warning carries Tradovate's reason | --- ## 10. What cannot be automated (tell users the truth) - **Closed-source scripts with no `alertcondition()` entries and no usable signals** (Case F). There is nothing to attach a payload to. The fix lives with the script's author. - **Drawn arrows/labels that appear after the fact.** Repainting signals look perfect on the chart and either can't trigger real-time alerts at all or fire on signals that later vanish. Automating them faithfully reproduces the repaint, not the chart's hindsight. - **A one-click universal "automate my script" button.** The payload must reflect the script's design: how it signals, whether it tracks a position, who manages exits. That mapping is exactly what this document lets an AI do for each specific script, but it is a mapping, not a bypass. - **Perfect timing.** TradingView delivers alerts with occasional seconds-level delay (top of hour, news), and Renko-style charts add structural lag. Strategies that depend on sub-second precision from a webhook chain will disappoint. - **Unattended NT8 trading with the computer off.** The NT8 path requires NinjaTrader running and connected (VPS hosting is the standard answer). Only the Tradovate destination executes with everything local switched off. When one of these applies, say it in the first paragraph of your answer, then offer the nearest viable alternative from this document. --- *This file is maintained by CrossTrade (crosstrade.io). It is safe to share in full with any AI assistant. For the human-oriented documentation, see https://crosstrade.io/docs — for the REST/MCP developer surface, see https://crosstrade.io/docs/api/ai-assisted-development.*