Skip to content
Back to App

Debugging & Errors

When your strategy does not behave as expected, the Playground provides several tools to help you find and fix the problem. This page covers how to use print statements for debugging, how to read error messages, and solutions for the most common issues.

Debugging with print()

The simplest and most effective debugging tool is print(). Everything you print is streamed in real time to the Output tab in the Playground. Use the standard log prefix conventions so your messages are easy to scan:

# Info messages — general status
print(f"[INFO] Bar {i}: price={price:.2f}, sma={fast_ma:.2f}")
# Trade messages — when orders are placed
print(f"[TRADE] BUY @ {price:.2f} | Reason: RSI={rsi:.1f}")
# Error messages — when something goes wrong
print(f"[ERROR] Unexpected position state: {position}")
# Debug messages — temporary, remove before final runs
print(f"[DEBUG] closes[-5:] = {closes[-5:]}")
print(f"[DEBUG] condition check: fast={fast_ma}, slow={slow_ma}, pos={position}")

Debugging tips

  • Print indicator values on every bar during development. This lets you verify that your calculations are correct before acting on entries.
  • Print the bar data to confirm you are reading the right fields: print(f"[DEBUG] bar = {bar}")
  • Print before and after orders to confirm they execute: print(f"[DEBUG] About to place order...") then result = place_order(...) then print(f"[DEBUG] Order result: {result}")
  • Set MAX_BARS = 50 in the code during debugging. Processing 50 bars takes seconds instead of minutes, letting you iterate quickly.
  • Remove or comment out debug prints before running full backtests. Printing on every bar slows down execution and floods the output.

Reading the output log

The Output tab shows everything your script prints to stdout and stderr. Messages appear in real time as the backtest runs.

Log prefixes

PrefixSourceMeaning
[INFO]Your strategy or setup codeStatus and configuration messages
[TRADE]Your strategyA trade was placed
[ERROR]Your strategy or API helperSomething went wrong
[DONE]Cleanup codeFinal results
[DEBUG]Your strategy (convention)Temporary debugging output
[STDERR]PlaygroundAdded to every line your script writes to stderr, such as API helper errors and Python tracebacks

Stderr output

The api() function prints HTTP and connection failures to stderr (file=sys.stderr). The Output tab shows every stderr line with a [STDERR] prefix, possibly interleaved with stdout messages, so look for lines starting with [STDERR] [ERROR]. Simulator errors such as a rejected order or an invalid token are different: they come back as a normal response with success: False and print nothing unless your code checks the result.

Common errors and solutions

”No more bars available” immediately

[INFO] Account: 12345, Contract: CON.F.US.ENQ.H25, Bars: 0
[INFO] No more bars available

Cause: The replay session returned 0 bars. This usually means:

  • The date range has no data for the selected instrument
  • The start date is a weekend or market holiday
  • The instrument was not traded on that date

Fix: Choose a date that falls on a weekday when the market was open. For US futures (NQ, ES), this means Monday through Friday, excluding holidays. Try a known good date like 2025-01-15 (a Wednesday).

Order rejected (success: False)

[ERROR] Order rejected: 7 Invalid order size

Cause: The simulator rejected the order. place_order() returns the rejection without printing anything, so check success and errorMessage on the result. Common reasons:

  • size is 0, negative, not a whole number or above 1,000 (errorCode 7, “Invalid order size”)
  • side is not 0 or 1, or order_type is not a supported type (errorCode 7, “Invalid order type or side”)
  • limit_price is missing for a limit order (type 1) (errorCode 2, “Invalid limit price”)
  • stop_price is missing for a stop order (type 4) (errorCode 2, “Invalid stop price”)

Fix: Check that all parameters have valid values. Print them before calling place_order(), and check the result:

print(f"[DEBUG] Placing order: side={side}, size={size}, type={order_type}")
result = place_order(ACCOUNT_ID, CONTRACT_ID, side, size, order_type)
if not result.get("success"):
print(f"[ERROR] Order rejected: {result.get('errorCode')} {result.get('errorMessage')}")

Invalid or expired token (errorCode 3)

[STDERR] [ERROR] Replay/step failed: Invalid or expired token

Cause: The authentication token is invalid or expired. The simulator answers with errorCode 3 and the message “Invalid or expired token” in a normal response, not an HTTP 401. get_next_bar() prints it and stops the script; other calls return it in their result.

Fix: This should not happen during normal Playground usage — the token is set automatically. If you see this, try refreshing the page and running the backtest again. If it persists, log out and log back in.

NameError: variable not defined

[ERROR] Cleanup failed: name 'trade_count' is not defined

Cause: Your strategy code has a bug that prevents it from reaching the variable definition. For example, if your code crashes before trade_count = 0 is executed, the cleanup section cannot reference it.

Fix: Always define tracking variables (trade_count, wins, entry_price, position) at the very top of your strategy section, before any logic that could fail:

# Define these FIRST, before any logic
closes = []
position = None
trade_count = 0
wins = 0
entry_price = 0
# Now your logic that might fail
try:
for i in range(MAX_BARS):
# ...

SyntaxError in strategy code

SyntaxError: invalid syntax (script.py, line 85)

Cause: Your Python code has a syntax error — a missing colon, unmatched parenthesis, incorrect indentation, etc.

Fix: Check the line number reported in the error. Common syntax issues:

  • Missing : after if, for, def, try, except, finally statements
  • Mismatched parentheses (), brackets [], or braces {}
  • Incorrect indentation (Python is whitespace-sensitive)
  • Using = instead of == in comparisons

TypeError: subscript or operation error

TypeError: 'NoneType' object is not subscriptable

Cause: You are trying to access fields on a None value. This usually happens when get_next_bar() returns None and you do not check for it before accessing bar["c"].

Fix: Always check for None immediately after calling get_next_bar():

bar = get_next_bar()
if bar is None:
break # Exit the loop
# Now safe to access bar fields
price = bar["c"]

Strategy runs but places no trades

Cause: Your entry conditions are never met. Common reasons:

  • Not enough bars to compute indicators (e.g., 30-period SMA needs 30 bars)
  • Conditions are too restrictive (e.g., RSI rarely drops below 20)
  • Logic error in the condition (e.g., using and when you meant or)
  • The continue statement skips the trading logic

Fix: Add print statements to check your indicator values and conditions:

if len(closes) >= SLOW_PERIOD:
fast_ma = sma(closes, FAST_PERIOD)
slow_ma = sma(closes, SLOW_PERIOD)
print(f"[DEBUG] Bar {i}: fast={fast_ma:.2f}, slow={slow_ma:.2f}, diff={fast_ma-slow_ma:.2f}")

If the indicator values look correct but no trades fire, check the position guard:

# This prevents re-entering the same direction
if fast_ma > slow_ma and position != "LONG":
# ...

If position is already "LONG" from a previous entry, the condition will not trigger. Make sure position tracking is correct.

Strategy is very slow

Cause: The strategy is doing too much work per bar, or the speed is set too low.

Fix:

  • Increase the speed slider to 50x
  • Reduce the amount of data processing per bar. For example, avoid recalculating indicators from scratch on every bar if you can maintain a running calculation.
  • Remove excessive print statements. Each print() call adds overhead because the output is streamed to the browser.
  • Use a longer timeframe (1m instead of 5s) to reduce the total number of bars.

WebSocket disconnection

If the chart stops updating but the Output tab still shows messages, the WebSocket connection may have dropped.

Fix: The Playground will attempt to reconnect automatically. If the chart remains frozen, stop the backtest and start a new one. The results up to the disconnection point are still valid.

Debugging workflow

Here is a recommended workflow for debugging a misbehaving strategy:

  1. Set MAX_BARS to 50-100 in the code. This limits execution to a small sample so you can iterate quickly.
  2. Add print statements before and after your key logic points.
  3. Run the backtest and check the Output tab.
  4. Verify indicator calculations. Compare your computed values against what you expect for the given price data.
  5. Check order placement. Print the result of place_order() to confirm orders are accepted.
  6. Check position tracking. Print the position variable after each trade to ensure it updates correctly.
  7. Run a full backtest. Once everything looks correct on a small sample, remove debug prints and restore the original MAX_BARS line.

Viewing the full script

With Custom Strategy selected, the Code tab in the bottom panel is the editor, and its contents are the exact Python script executed on the server, including:

  • The header with all imports and API helpers
  • Your strategy parameters
  • The backtest setup code
  • Your strategy logic
  • The cleanup code

When an error reports a line number, use the Code tab to find the corresponding line. The editor is locked while a run is in progress. The pre-built templates run from the server, so for them the Code tab shows only a short note.