# Troubleshooting backtests

> Symptom by symptom: a run that stays queued, a run limit, a refused estimate, a program that does not build, a workflow that is not a strategy, odd results and a stopped forward test.

Source: https://orbitalaip.com/docs/backtesting/troubleshooting

> [!IMPORTANT] Disclaimer
> Backtest results are hypothetical and do not predict future performance. Nothing here is investment advice: Orbital computes the rules you supplied and recommends nothing. Orbital is not a registered investment adviser or broker-dealer. Costs are as modelled by the chosen cost profile and may differ from live trading.

## Where to look first

In the Run dialog, a setting it refuses shows **This run can’t start yet.** above the reason, and **Run backtest** stays disabled. On the strategy page, a run that ends without a result reads **This run failed** with the engine's reason and its code, or **Cancelled before completion**. A **backtest failed** notification carries the same reason.

Chat, agents and the Orbital AI connector get the same refusals; [Troubleshooting](https://orbitalaip.com/docs/claude-and-chatgpt/troubleshooting) for Claude and ChatGPT covers the connection's own errors.

## The run stays queued

A submitted run reads **Queued — waiting for the engine** until the engine begins it, and holds one of the workspace's four run slots. The engine replays a few runs at a time across every workspace, so a run waits while others finish.

A submit the engine never received fails after about ten minutes with **The engine never received this run — the submit was lost in transport. Run it again.** A run the engine finished but Orbital could not import for six hours fails too, with a message beginning **The engine finished this run, but Orbital could not import its result**. To stop waiting, click **Cancel run…** on the strategy page; see [Cancel a run](https://orbitalaip.com/docs/backtesting/run-a-backtest#cancel-a-run).

## The run quota is reached

Every workspace has two ceilings, on every plan: 4 runs queued or running at once, and 200 runs started in any rolling 24 hours. A launch over either is refused with the toast **Run limit reached**. The message reads **this workspace already has N runs in flight — the ceiling is N at a time** or **N runs in the last 24h — the ceiling is N**. A forward test's updates count toward both.

- **The first ceiling:** wait for a run to finish, or click **Cancel run…** on a queued or running run to free its slot.

- **The second ceiling:** wait. It is rolling: each run stops counting 24 hours after it started, and nothing resets it early.

In Chat and through the connector, the estimate warns first: **N runs are already in flight for this workspace (the ceiling is N); Run will be refused until one finishes.**

In a project where backtest pricing is on, a run the workspace cannot cover is refused with **Not enough credits for this run**; see [Credits and billing](https://orbitalaip.com/docs/workspace-and-account/credits-and-billing#backtests).

## The estimate was refused

The estimate on the right of the Run dialog refuses a run before anything is spent.

### The data does not cover the period

The data check reads **The data does not cover the period**, then the engine's sentence and the code **DATA_UNAVAILABLE**. A run that failed this way lists each gap, such as **Daily bars missing for SPY:** and the dates.

1. **Keep the period on the tape**
   
   The line under the period presets says where the tape begins, such as **Daily bars are mirrored from 2016-01-04**. A preset outside it is greyed and marked **before the tape**.

2. **Drop the symbols the gaps name, or move the period off their dates**
   
   A ticker that had not listed yet is not a gap; see the next section.

A period that runs past the last mirrored session is not a gap: the check reads **The data covers the period through** that date, and the run goes to it.

### A symbol listed too recently

This is a warning, not a refusal. A symbol that listed after the first session the run reads cannot supply the [warm-up](https://orbitalaip.com/docs/glossary#warm-up) its indicators need. The dialog shows an amber line such as **Universe: 1 of 13 symbols listed too recently to warm a 200-bar indicator by 2023-01-03**. It says the symbol joins the run at its own first warm session, rather than trading from the start. It ends **Start on** a date **or later for a window it covers end to end.** The top of the run's **Overview** repeats it. If no symbol becomes warm inside the period, the run fails with **DATA_UNAVAILABLE**; start the period later.

### Too many symbols, or too long a window

A static list may name at most 500 symbols; more is refused with **N symbols exceeds the 500-symbol ceiling.** Narrow the list in the dialog's **Symbols** field, or on the strategy's **Input** node.

The period times the universe is bounded on each bar size. The refusal reads **At daily bars, N symbols allow a window of up to N months — this run asks for** the two dates. Shorten the period, test fewer symbols, or run on daily bars. A preset past this bound is greyed and marked **too large to run**.

### A setting the strategy cannot take

A Rules strategy that sells short reads **This strategy sells short — run it on a margin account (toggle Margin in the launcher).** Turn on **Margin account**; see [Margin defaults](https://orbitalaip.com/docs/backtesting/backtest-vs-forward-test-vs-paper#margin-defaults).

## A program does not build

The code editor's **Problems** tab holds the compiler's answer, and **Test** holds the check.

| What you see | Cause | What to do |
| --- | --- | --- |
| Not built — the node is missing a setting | History the program reads is blank. | Set it to the deepest bar offset the program reads, then build. |
| Not built — the save was refused | A build saves the workflow first, and the save failed. | Fix what the save names, then build. |
| Compiler messages with a line and column | The source did not compile. | Click a message to jump to its line. Import every function from orbital_strategy and export exactly on_bar. |
| The source compiled, but the module failed its run-once check: and a reason | It trapped or broke a rule on a check scenario: a read deeper than its history, a stop set while flat, or a stop at or through the market. | Fix the source as the reason says and build again. |
| A sentence about a 60-second budget | The engine is still compiling. | Build the same source again in a minute; it answers at once. |

A run refuses a program changed since its last build: for a Program named Breakout it reads **Program 'Breakout' changed since its last build — build the program before running it.** A run stops if the program runs out of fuel on one bar; a program that exhausts the default budget needs rewriting to do less per bar. See [Build and test](https://orbitalaip.com/docs/backtesting/program-strategies#build-and-test) and [Fuel and limits](https://orbitalaip.com/docs/backtesting/program-strategies#fuel-and-limits).

A build through an agent or [build_program](https://orbitalaip.com/docs/claude-and-chatgpt/tools/build_program) can also return warnings; see [A Program from Claude or an agent](https://orbitalaip.com/docs/backtesting/program-strategies#a-program-from-claude-or-an-agent). It is refused while a deployment runs the strategy; see [What a running deployment locks](https://orbitalaip.com/docs/backtesting/manage-and-delete-strategies#what-a-running-deployment-locks).

## The strategy does not qualify

If a workflow is missing from the **Strategies** list, or Chat says it is not a strategy, its saved graph does not qualify; see [Backtesting](https://orbitalaip.com/docs/backtesting/overview#saving-a-strategy-enrols-it). Chat, agents and the connector give the reason after **Not a strategy yet:** or **It does not qualify:**.

- **A strategy needs at least one Entry Rule node, or a Program node.**

- **A strategy needs an Output node declaring the signal columns (symbol, action, asOf).** A missing column is named instead.

- An **Input** node in universe mode that names a symbol, which is ignored, so the universe would be the default preset. Set **Universe preset** to **Custom list (use field below)** and list the tickers in **Custom tickers (preset = "Custom")**, or for one ticker switch **Source** to the single-ticker asset option.

Fix the graph in the workflow editor and save it; the strategy is listed at once. A workflow never saved with an admitted graph is refused with a sentence ending **save it first**.

A listed strategy can still be refused at the estimate with **This strategy graph is not backtestable yet.** and the named diagnostics under it.

| Diagnostic | What to do |
| --- | --- |
| Not backtestable: the graph has no working entry rule. | Every Entry Rule failed to compile. Read each rule's own line. |
| input A is not wired or input A must come from an Indicator node | Wire an Indicator node into the rule's Indicator A port. |
| names indicator 'rsi14', which this graph does not define | Give an Indicator node that Reference name, and use each name once. |
| Sizing: risk-percent sizing needs a stop loss to size against — add one in Exit Rules. | Add a stop loss, or pick another sizing method. A trailing stop does not count. |
| Program 'Breakout' decides its own entries and exits, so it cannot share a graph with the rule cards | Delete the rule cards, or the program. |
| Program: the graph carries 2 Program nodes | Delete all but one. |
| An agent-decided strategy is not backtestable — its decisions come from a model call each tick. | A graph decided by an Agent node is for Trade, not Backtesting. |

## Results look wrong

Backtests are deterministic, so a surprising result is a question about the settings, the costs or the data.

- **The costs.** **Costs modelled** under **Run properties** on the **Report** tab states what was charged. The bid-ask spread and market impact are [not modelled](https://orbitalaip.com/docs/backtesting/backtest-vs-forward-test-vs-paper#what-is-not-modelled) under any profile.

- **Open positions.** Positions still held at the last close stay open, read **Open** under **Closed by**, and count in **Net profit** but not in closed-trade statistics; see [Open positions at the end of a run](https://orbitalaip.com/docs/backtesting/backtest-vs-forward-test-vs-paper#open-positions-at-the-end-of-a-run).

- **The benchmark.** It is SPY unless you change it, as total return with dividends reinvested. **Compare** rebases its chart lines to 100 on a shared first day, but its table keeps each run's own figures.

- **The data moved.** The footer of the strategy's **Overview** says when bars were restated or corporate actions changed after the run.

- **The result is too good.** The [deflated Sharpe ratio](https://orbitalaip.com/docs/backtesting/avoiding-overfitting#the-deflated-sharpe) discounts the Sharpe for the [trial count](https://orbitalaip.com/docs/glossary#trial-count).

## A forward test stopped

The strategy page's header line says what happened, and the **Forward test** card adds the date. Click the header line to open the last update, if there is one.

| Header line | Card | Cause |
| --- | --- | --- |
| Forward test stopped | stopped on the date | Someone clicked Stop forward test…. |
| Forward test stopped: its window grew past what one run can hold | stopped: the window grew past what one run can hold | The window grows by a session each day, and one run's window is bounded by the bar size and the symbol count. A minute-bar strategy with many symbols reaches it first. |
| Forward test stopped: its updates were refused | stopped after two updates in a row failed | Two updates failed on two different sessions, or one was refused before it could start, such as by a gap in the data or a version or workflow that no longer exists. |

A single failed update does not stop the test; the next half-hourly check tries again, and two failures on one session wait for the next session. Each failed update sends a **backtest failed** notification, but the stop itself sends none.

1. **Read the reason**
   
   The header line gives a refusal's reason; after two failed updates, the **Runs** view gives each one's. A window that grew too large needs a run over fewer symbols or on daily bars. For refused updates, fix what the reason names: move the period onto the data, or run the version again so a completed run exists.

2. **Start a new test**
   
   Open the completed run and click **Forward test…**. The new test has its own start date.

A test that is not stopped but not updating is usually waiting: the first update lands around noon ET on the day after the start date. See [How updates work](https://orbitalaip.com/docs/backtesting/forward-testing#how-updates-work).
