Live Deployment
Live deployment runs a compiled .rqc strategy in a dedicated trading session connected to Tradovate. Demo and Live use CME data for the same real-time strategy market-data path; the selected Tradovate account determines only where orders are routed and where account, order, position, and fill state comes from. Tradovate is not a Roboquant market-data source.
This is the native Roboquant Engine workflow. The production TradingView webhook connector is a separate product and deployment path; these native plan limits do not change its behavior.
Current live scope
| Capability | Status |
|---|---|
Compiled single-symbol .rqc strategy | Supported |
| Tradovate Demo account | Supported and recommended first |
| Tradovate Live account | Supported with explicit confirmation |
| Bar-close strategy | Supported |
Tick strategy with wants_ticks() -> true | Supported |
| Strategy parameters at deployment | Supported |
Parameter changes on a running .rqc | Redeploy required |
Multi-symbol MultiStrategy deployment | Not yet supported |
Live L2 reads in compiled strategy code (ctx.book()) | Supported |
Architecture
Deployments UI
↓
Backend creates a deployment job
↓
Worker starts an isolated trading pod with the .rqc artifact
↓
CME market data → compiled strategy → order-management engine → Tradovate execution
↓
Orders, positions, account P&L, logs, and status → Redis
↓
Backend WebSocket → Deployment detail
Each session gets an isolated pod and session ID. A stable deployment identity (strategy, account, symbol, timeframe, and broker) prevents two bots of the same deployment from running at once. Trades, position, and P&L on a deployment page cover only that deployment's own run: a redeploy starts with an empty history and its own figures.
Before deploying
Confirm that:
- The
.rqsource compiles and a current.rqcartifact exists. - The strategy has completed a realistic backtest with the intended parameters and costs.
- A Tradovate account of the correct type—Demo or Live—is connected.
- The deployment symbol and timeframe match what the strategy expects.
- Contract sizing and risk limits fit the connected account.
- The strategy is not a multi-symbol strategy: live sessions trade one symbol, and Deploy refuses a multi-symbol strategy with: "This strategy trades several symbols, which live trading does not support yet. Deploy a single-symbol strategy instead."
Deploy from the dashboard
From Strategies → select a strategy → Deploy:
- Click New deployment.
- Select a Tradovate Demo or Live mode and a matching connected account.
- Choose a CME symbol and timeframe.
- Set the order size and strategy parameters.
- Set maximum order size, maximum position size, the order-rate limit, and a daily-loss limit.
- For Live, type the displayed confirmation phrase.
- Start the deployment and wait for status to become Running.
The deployment form reads the real Tradovate account balance before launch. This balance anchors live sizing and the equity baseline; Roboquant does not silently fall back to the backtest's starting capital when the broker balance cannot be read.
Plan allowances
| Allowance | Free | Charts | Starter | Pro | Elite | Ultra |
|---|---|---|---|---|---|---|
| Enabled Tradovate accounts | 0 | 5 Demo only | 5 Demo only | 10 | 10 | 20 |
| Native deployment slots | 0 | 0 | 0 | 1 | 3 | 7 |
Extra deployment slots. On any plan, including Free and Charts, you can add deployment slots on top of the plan's allowance for $15 a month each, from Billing. Each slot adds one native deployment, and having at least one slot also lets you enable a Tradovate account, Live included. Cancel a slot in the Whop hub. When a slot ends, running bots are never stopped; only new deployments above your allowance are refused.
Demo and Live accounts share the connection allowance. Each enabled sub-account counts separately, even if several share one Tradovate login. Roboquant lists the accounts exposed by that login; when they exceed your allowance, choose which to enable in Accounts.
Demo and Live native deployments share the deployment allowance. Each native deployment runs one strategy on one symbol, on one Tradovate account or on several accounts of the same login. A saved strategy does not consume a slot, and saved strategies have no plan-based count limit. The standalone TradingView webhook connector has its own subscription and execution behavior; Roboquant 2.0 connection and native deployment allowances do not govern that product.
Starting, running, paused, degraded, halted and stopping pods occupy a slot until they terminate. Connection and deployment limits apply to new admissions. Existing enabled accounts and running bots remain usable after a downgrade or rollout, even above the current allowance. Adding or enabling an account requires room in the connection allowance; starting another deployment requires room in the deployment allowance. Having excess existing connections does not by itself block a new deployment when a deployment slot is available. Closing positions, cancelling orders and stopping bots remain available regardless of plan allowances.
Running the same strategy on multiple accounts
One Tradovate deployment can run on several accounts of the same Tradovate login, for example all your funded accounts. In the deploy form, pick the main account and tick the others under Also run on. The deployment uses one plan slot however many accounts it runs on; the accounts still count against the connection allowance.
Every account runs its own bot: it is sized on its own balance (an account reporting $0, such as a prop-firm funded account, uses the account size you enter), and the risk limits (max order, max position, orders per minute, daily loss) apply to each account separately. One account's rejected order, disconnect or daily-loss halt does not affect the others, and identical fills across accounts are not guaranteed. Demo and Live accounts cannot be mixed in one deployment, and the accounts must belong to the same login. The deployment starts on all of its accounts or on none: if one account cannot start, the others are stopped again and the error names that account.
The deployments list shows each account's bot with its own P&L, together with the other accounts of its deployment. Stop in the list stops the deployment on all of its accounts; the bot's detail view stops only that account.
The standalone production webhook connector is separate from this native deployment workflow.
Duplicate deployment protection
Roboquant prevents two live pods from running the same strategy/account/symbol/timeframe/broker identity at once. This protects against duplicate orders and mixed trade history. The same strategy can run on a different account as a separate deployment, within the plan's slot allowance.
Stopping cancels working orders and flattens the bot's position (closes it) before the pod terminates. Wait for that shutdown to finish before redeploying the same identity. This duplicate guard is separate from the plan's slot limit; the old blanket five-session cap no longer applies.
Demo vs Live
| Mode | Order destination | Recommended use |
|---|---|---|
| Tradovate Demo | Tradovate simulated account | First deployment, operational validation, reconnect testing |
| Tradovate Live | Real-money Tradovate account | Only after backtest and Demo validation |
The old generic Paper mode is no longer used. Tradovate Demo is the paper-trading path because it exercises the same account, contract, and order semantics as Live. Market data remains CME in both modes.
Order book in live strategies
Compiled strategies can read the live L2 order book with ctx.book(). It returns the latest MBP-10 snapshot from Roboquant's own CME feed, not from the broker, so a strategy sees the same book on Tradovate Demo and Live. The BookView API is the same as in backtests.
- The book is subscribed only when the compiled strategy's code reads
ctx.book(). - Snapshots are published at most every 250 ms (up to 4 per second).
- There is no book callback. Read the book from
on_bar,on_tick, oron_timer. ctx.book()returnsNoneuntil the first snapshot arrives after the deployment starts, and while the feed is stale for more than 10 seconds (the book is cleared rather than served frozen). Always handleNone.
Live bars are never held behind trade processing: if on_tick falls more than 2 seconds behind the feed, the oldest pending trades are skipped rather than delivered late, so a slow on_tick can miss trades live that a backtest would see. Keep on_tick fast.
Tradovate prop-firm accounts use the same execution connection as personal accounts when the authorized login exposes them and the account permits API execution. Choose the actual Demo or Live environment for the account; a prop firm's “funded” label alone does not determine which API environment it uses. Confirm the firm's current automation rules and account agreement before deploying.
Safety controls
Compiled deployments include controls outside strategy code:
- Broker reconciliation at startup — the engine reads cash, net liquidation value, and the open position from Tradovate before it starts processing new signals. A clean stop flattens the position and cancels its stop-loss and take-profit orders. After an unplanned restart, the engine picks up the open position and its working stop-loss and take-profit orders — each is picked up only when exactly one working order of that type is on the exit side — resizes them to the position, and later stop/target changes modify those orders instead of placing duplicates. Other working orders are left untouched and listed in the Deployment Logs. Trailing-stop state is not restored; call
ctx.set_trailing_stopagain after a restart if the strategy uses it. A partially filled entry order stays working and can still be cancelled. - Maximum order size — rejects a single order larger than the configured contract count when it could add exposure.
- Maximum position size — blocks an order that could take the net position beyond the allowed exposure.
- Order-rate limit — supported by the deployment API and enforced before orders reach the broker.
- Daily-loss halt — when the configured dollar loss is reached, the engine pauses the strategy, cancels working orders, flattens, and halts the session. No new entries are sent until the bot is redeployed.
- Exchange-resident protection — stop loss, take profit, and trailing protection are maintained at the broker where supported.
- Rejected stop-loss — Tradovate rejects a stop that is already on the wrong side of the market. When the open position's stop-loss is rejected and the market is at or through the stop, the bot closes the position at market, as a backtest would; otherwise it places the stop again, once per position, sized to the smaller of the bot's position and the account's position on the same side (it is not placed again when the account is flat or on the other side). A stop that a strategy sets on the wrong side of the market is refused before it is sent: the working stop is kept, at its own price, so a trailing stop keeps tightening from it, or, when no stop is working, the position is closed at market. A market entry whose bracket stop is already through the market is refused. These checks use the last price only while it is younger than the stale-price limit (
RQ_STALE_TICK_SEC, 30 seconds by default; a bar close is as old as the bar's close time), and only a price of the bot's own contract: in roll week, ticks of another month on the market-data feed never count. A bot fed only bars does not yet learn which contract its feed carries, so it bases these checks only on its own fills. With no such price the stop is sent and Tradovate judges it. When the bot cannot close after refusing a strategy's stop, it does not try again for that position until the position changes. The close cancels only that position's stop and target; the stop and target of an entry order that has not filled stay with that entry. The close only exits the bot's own position: when Tradovate reports a different position on the contract (another bot or a manual trade on the same account), the bot sends a market order for at most its own size. It cancels the strategy's resting reduce-only exit orders first, waits up to 2 seconds until Tradovate reports each of them cancelled or filled, and reads the account position again. When one of them cannot be cancelled or is not yet reported cancelled or filled (an order waiting to be cancelled can still fill), it sends no market order, since both could fill and reverse the position, and reports that this order is still working at Tradovate, naming it; the bot staysdegradedwith that message until the order reaches a final status at Tradovate (filled, cancelled, rejected or expired); then, if the position is still open with no stop-loss working, it staysdegradedand says so. When the account position moved toward flat during the cancel, one of those exits may have filled: the bot sends a market order that much smaller, or none, and reports that the position may be open with no stop-loss. The bot sends nothing when the account is flat, on the other side, or cannot be read, and reports that no stop-loss is working; it does the same when its market order cannot be sent (for example over the order-rate limit). When Tradovate answers the liquidation with an error, the bot sends no market order (Tradovate may still have run the liquidation, so an order sized from a new read could reverse the position). It pauses new entries and retries the liquidation for about 15 seconds, only while Tradovate's position on the contract still equals the bot's own (otherwise another holding shares the contract and nothing is sent); the retry never cancels the strategy's resting entry orders. If every retry fails, the bot staysdegraded(a halted or paused bot keeps its status) with entries paused and an error in the Deployment Logs so you can check the account; it does not return torunningon its own. When a retry succeeds, entries resume, unless the bot is halted or paused. While such a close at market is in flight (for up to 30 seconds, or until its order ends), the bot sends no other order for that position and logs any stop or target the strategy sets meanwhile; an order it skips is logged once per close. The bot does not close at market when the strategy's own market exits, sent in the last 30 seconds, already cover the position. When they cover only part of it, the bot sends a market order for the rest and never liquidates the account position. When the close order ends with the order status Rejected, Canceled or Expired, or fills but leaves part of the position open, the bot sends the latest stop and target that the strategy set during the close; that order can no longer fill, so the position is never closed twice. After a fill, they are sized to the smaller of the bot's position and the account's position on the same side, and not sent at all when the account is flat, on the other side, or cannot be read: a stop on a flat account could open a new position. If no stop is then working, it reports that the position is open without protection, with Tradovate's rejection text. When the 30 seconds pass and the same position is still open with no stop working, the bot reports that the position is open without protection and sends nothing: the close may still fill. When the bot cannot cancel its own stop or target before closing at market, it still closes and retries the cancel once afterwards. It reports any stop or target still working at Tradovate by its order number. A refused stop is logged once per price, not on every tick. When a take profit or stop fills, the bot cancels or resizes any exit order that Tradovate does not cancel with it, such as a stop placed again. Tradovate's rejection text is shown in the Deployment Logs. A stop that is cancelled or expires at the exchange is not placed again; the bot reports that no stop-loss is working. - Stale-price guard — a tick that arrives more than 30 seconds after it traded (
RQ_STALE_TICK_SEC) never reaches the strategy, compiled or Python, so no order is placed on an old price. The bot logs that it skipped stale ticks and trades again once its feed has caught up. Bars are never skipped. Each bot logs its price lag once a minute. - Trap/stop flattening — a fatal strategy failure, a daily-loss halt or an explicit stop cancels working orders and flattens the position through the broker's liquidation of its confirmed position. It never sends a fallback close sized from the bot's own records, which could open a position in the opposite direction. When the position cannot be read or the liquidation fails, nothing is sent and the bot retries: after a failure or halt it pauses new entries and retries for about 15 seconds; on stop it retries for at most about 30 seconds, inside the pod's shutdown time. The broker's liquidation acts on the contract's whole net position on the account. If every retry fails, the bot reports
degradedwith an error saying the position may still be open, and keeps new entries paused until you stop it; a bot that already failed (error), is halted or paused, or is stopping (stopped) keeps that status and still logs the error. On Python bots, when the broker's liquidation fails or reports the account flat, nothing more is sent. When the broker has no liquidation, a Python bot closes only its own size, capped at the broker's confirmed position on the same side, so another bot's or a manual position on the same contract is left untouched; when that read fails it sends nothing. - Broker-truth equity — the live equity stream is periodically corrected from Tradovate's account values rather than relying only on the latest market-data mark.
- Position mismatch guard — transient broker reads do not create phantom closes; reconciliation waits for repeated mismatches. When a position is closed outside the bot (a manual exit or a prop-firm liquidation) and that also cancelled the bot's stop and target, it logs that the position was closed outside the bot instead of warning that no stop-loss is working. When a reconcile adopts an open position the bot did not open itself, its trade counter counts that round trip once the position closes. The closed trade records the broker's entry price for the adopted position; its open time shows as "--" unless the bot opened the position itself.
- Tradovate request limit — when Tradovate answers a request with HTTP 429 (too many requests), it pauses that login for an hour, and every further request restarts the hour. Roboquant then stops calling Tradovate for that login for one hour, for every bot and account on it. A deploy onto a paused login is refused with the time the pause ends. A bot that starts on a paused login stops before it calls Tradovate, and a bot whose start-up contract lookup gets the 429 stops right after it; both stop with the reason "Tradovate paused this login until HH:MM UTC after too many requests — deploy again after that." A running bot stays
degradedwith the pause and its end time, places no new order and keeps no account connection open until the pause ends. Its own exit, liquidation, cancels and a protective stop or target on an open position are still let through once per pause, together with the position and order reads they need; if Tradovate refuses any of those calls with another 429, nothing more is sent for that pause — close the position in Tradovate's own app or by phone.
Exits are exempt from both size caps, so a position the caps allowed is always closable. An exit is a close_position() / close_partial() / reduce-only order, or an order on the opposite side of the open position that has no stop-loss, take-profit or trailing stop attached and is no larger than the part of the position that other working exits (other plain exits and closes in flight) do not already cover. An order that would reverse the position is checked against both caps as before. The order-rate limit applies to every order.
Parameters and code updates
Deployment parameters are applied when the .rqc session is created. A running compiled session does not hot-swap parameters.
To change code or parameters:
- Stop the current deployment.
- Edit the
.rqsource if needed. - Save and compile a fresh
.rqcartifact. - Start a new deployment with the desired parameter values.
This makes the deployed artifact and its configuration explicit. Editing a source file does not mutate a pod that is already trading.
Contract rolls
A live deployment resolves the front-month contract when it starts and trades that contract for the life of the session. When that contract reaches its roll date (before it expires), the bot halts: new entries stop and the status shows a message naming the contract.
- If the bot is flat, its pending orders are cancelled.
- If a position is open, it stays open. The strategy's own exits are paused, and only stop and target orders already resting at the exchange remain active. Manage that position yourself.
This is expected. Stop the bot and redeploy it to trade the next contract.
Live monitoring
Click a deployment row to open its detail view. The WebSocket stream carries:
| Event | UI effect |
|---|---|
| Status | Starting, running, paused, degraded, halted, stopping, stopped, or error |
| Bar | Extends the live chart |
| Order | Shows accepted, filled, and cancelled orders |
| Position | Updates side, size, entry, and unrealized P&L |
| Trade | Adds a completed round trip |
| Equity | Updates net liquidation, realized P&L, and unrealized P&L |
| Log | Shows strategy messages and engine/broker warnings |
The detail view provides Overview, Chart, Trades, and Logs views. Strategy ctx.log(...) messages are sent to both the live stream and pod logs. Before a line reaches the Logs view, internal network addresses (connection URLs, IP addresses, localhost:<port> and host.domain:<port>) are replaced with [internal endpoint], and error tracebacks are kept to the pod logs; your own file and line references such as main.rq:42 stay readable.
Bot performance vs. account equity
The deployment page shows only the bot's own performance for the current run. Trades, position, realized and unrealized P&L, win rate, and the P&L curve come from Tradovate fills of orders this run's pod placed. Manual orders, other deployments, and earlier runs of the same deployment are never included.
The only shared figure is Account equity (shared): the Tradovate account's net liquidation value. It includes manual trading and every other deployment on the account.
If a position the bot opened is closed outside the bot (for example by a manual order), the bot detects that it is flat. The same happens when the run stops or errors while still holding a remainder. The remainder then appears in the trades list as Closed outside the bot: no exit price or time, P&L shown as "—", and excluded from bot performance. The page never shows a position the bot no longer holds.
Status meanings
| Status | Meaning |
|---|---|
starting | Pod or broker session is being created |
running | Strategy and broker connection are active |
paused | The pod and broker connection remain active, but strategy callbacks are paused. A degraded bot can be paused too; it stays paused when broker reads recover |
degraded | Repeated broker account reads are failing. With no open position, strategy callbacks pause so no new entries are sent; with a position open, the strategy keeps running so its exits are still attempted. The session returns to running on its own once broker reads recover. The message names the cause: Tradovate not responding (wait; it resumes by itself), login expired (reconnect the account), or account not found (stop the bot). When a compiled (.rqc) Tradovate bot can no longer get a working login on its own, the message is one of two: "Tradovate connection expired — open Accounts, reconnect this Tradovate account and make sure it is enabled. New entries are paused; the bot can't manage an open position until then." (reconnect the account under Accounts, and click Enable when the plan's account limit requires it), or "This bot's Roboquant session expired — stop and redeploy it. The bot can't manage an open position until then." (stop and redeploy the bot). In that state the bot backs off to reading its Tradovate account once every 5 minutes (its first retries come after 60 s and 120 s), never flattens or cancels anything on its own, and returns to running within about 3 minutes of a reconnect. A compiled (.rqc) Tradovate bot that stays held with no open position and the "account not found" text for 12 hours stops itself, but only when its own records show no position and, while it still has a working login, Tradovate's position list for the account is empty (once Roboquant has switched the connection off as "account no longer exists", that confirmation replaces the position-list read, which needs a login). It ends as error (below) and its owner gets one email. A transient failure text in between (Tradovate not responding, login expired) keeps the 12-hour count running; a successful account read, a position opening, a user pause or stop, or a halt resets it. A failed check is retried after 5 minutes. Bots deployed before this release do not stop on their own |
halted | A safety rule flattened and stopped new trading |
stopping | Kubernetes accepted the stop and the engine is flattening or terminating |
stopped | The session was intentionally stopped. A plain stop carries no reason |
error | The strategy or session failed; safety cleanup is attempted. A bot that could not look up its contract at Tradovate (the lookup is retried three times, including when Tradovate rate-limits it), or that was shut down before it received any market data for its symbol, ends here with that reason. When Tradovate refuses the lookup, the reason says so: login expired (reconnect the account) or market access refused for the account. A Tradovate account that Tradovate no longer lists for its login (closed or reset by a prop firm), or an expired Tradovate login, is refused at deploy before any pod starts; a compiled (.rqc) bot that meets either when it connects ends here with "Tradovate account … no longer exists (closed or reset by your prop firm?) — reconnect and pick the current account", "Tradovate refused access to account … — reconnect your Tradovate account, then pick the current account" (Tradovate answered the account lookup with a permission error) or "Tradovate login expired — reconnect your Tradovate account". Interpreted Python-lane bots keep their earlier connect error wording until a follow-up. A bot stopped by the account-not-found auto-stop (see degraded) ends here with "Stopped: Tradovate has reported for 12 hours that this account no longer exists (closed or reset by your prop firm?). The bot had no open position. Deploy it again on your current account." Its stop flatten cannot reach an account Tradovate no longer has; the log then says the bot was verified flat just before stopping instead of warning that a position may be open |
The pod exposes /live for liveness and /ready for readiness. A halted or degraded engine remains live so Kubernetes does not restart it into trading, while readiness fails until the engine is running or paused.
Troubleshooting
| Symptom | Check |
|---|---|
| Deploy is disabled | A valid .rqc exists and the selected account type matches Demo/Live |
| Invalid artifact error | Save and compile the .rq source again |
| No orders | Strategy logs, wants_ticks for tick strategies, session filters, and parameter values |
| No live chart updates | Deployment status, market session hours, and selected symbol/timeframe |
| Session is degraded | Logs for repeated Tradovate account-read or transport errors. If the account shows Expired under Accounts, reconnect it; the session resumes by itself. "Tradovate connection expired": a deactivated account appears under Accounts → "Choose accounts to enable" with a Reconnect button; reconnect it (and click Enable if the plan's account limit requires it) and the bot resumes within about 3 minutes, its open position untouched. "Roboquant session expired": stop and redeploy the bot |
| Bot stopped with "Tradovate has reported for 12 hours that this account no longer exists" | The prop firm closed or reset the account. Connect the current account under Accounts and deploy the bot again on it. If the account should still exist, check it with Tradovate or the prop firm first |
| Session halted | Daily-loss or other risk-limit messages in Logs |
| Redeploy is rejected | Wait for the previous pod for the same deployment identity to terminate |
| Deploy says live bots are not available for a symbol | The live feed does not stream that contract yet; pick one of the listed symbols |
| Parameter change is rejected | Stop and redeploy; compiled sessions do not hot-swap parameters |
Related docs
- Strategies — compile the artifact that gets deployed
- Backtesting — validate the same artifact first
- Runtime reference — live-supported hooks and methods