Skip to content
Back to App

Custom Strategies

Templates are a great starting point, but the real power of the Algo Playground is writing your own strategies. With custom Python code, you can implement any trading logic — combine multiple indicators, add risk management rules, filter by time of day, or build entirely novel approaches.

What you can build

The Algo Playground gives you full access to:

  • All OHLCV bar data — Open, high, low, close, and volume for every bar in your date range
  • Order placement — Market, limit, and stop orders with any size
  • Position management — Query open positions, close individual positions, or flatten everything
  • Final results — The run’s closing balance and net P&L from Replay/end, printed by the cleanup section
  • Raw API access — Call any Simulator API endpoint directly for advanced use cases
  • Custom indicators — Compute any indicator from raw price data using Python standard library math

Since strategies run as standalone Python scripts with only standard library imports, there are no external dependencies to manage. You compute everything from raw data — which also means your code has no dependency on TestMax-specific libraries.

The Custom template

When you select the Custom template in the Playground, you get a blank strategy with all the infrastructure already set up:

# Available functions:
# place_order(ACCOUNT_ID, CONTRACT_ID, side, size) — side: 0=Buy, 1=Sell
# get_positions(ACCOUNT_ID) — returns open positions
# close_all_positions(ACCOUNT_ID, CONTRACT_ID) — flatten all positions
# get_account(ACCOUNT_ID) — returns account info
# api("endpoint", body) — call any Simulator API endpoint
#
# Each bar is a dict: { "t": timestamp, "o": open, "h": high, "l": low, "c": close, "v": volume }
closes = []
position = None
trade_count = 0
wins = 0
entry_price = 0
print("[INFO] Strategy: Custom")
try:
for i in range(MAX_BARS):
bar = get_next_bar()
if bar is None:
break
closes.append(bar["c"])
read_speed()
if STEP_DELAY > 0:
time.sleep(STEP_DELAY)
price = bar["c"]
# ──── Write your trading logic below ────
pass # Replace with your logic

This is an excerpt. The editor holds the complete script: the header (imports, config, API helpers) and session setup sit above it, and the cleanup (the finally: block and results) sits below it. Whatever is in the editor runs as the whole file, so write your logic inside the main loop and leave the rest of the scaffold in place.

Development workflow

Here is a recommended workflow for building custom strategies:

  1. Start from a template. Run an existing template to understand the structure and output format.
  2. Switch to Custom. Select the Custom template and start modifying the main loop.
  3. Test incrementally. Change the MAX_BARS line in the code to a small number (e.g., MAX_BARS = 100) while developing. This lets you iterate quickly without waiting for a full backtest.
  4. Use print() for debugging. Add print(f"[INFO] ...") statements to inspect variable values and logic flow. The output appears in real time in the Output tab.
  5. Run a full backtest. Once your logic works on a small sample, restore the original MAX_BARS line and run against the full date range.
  6. Save your strategy. When you have something worth keeping, save it with a descriptive name so you can reload it later.

Example: SMA + RSI combo strategy

Here is a quick example of a strategy that combines two indicators — only taking SMA crossover entries that are confirmed by RSI. It replaces the strategy section of the Custom Strategy script (from the state variables to the end of the loop); keep the header, setup and cleanup around it:

# Parameters
FAST = 10
SLOW = 30
RSI_PERIOD = 14
closes = []
position = None
trade_count = 0
wins = 0
entry_price = 0
def sma(data, period):
if len(data) < period:
return None
return sum(data[-period:]) / period
def calc_rsi(data, period):
if len(data) < period + 1:
return 50
gains, losses = [], []
for j in range(len(data) - period, len(data)):
change = data[j] - data[j - 1]
gains.append(max(0, change))
losses.append(max(0, -change))
avg_gain = sum(gains) / period
avg_loss = sum(losses) / period
if avg_loss == 0:
return 100
return 100 - (100 / (1 + avg_gain / avg_loss))
print("[INFO] Strategy: SMA + RSI Combo")
try:
for i in range(MAX_BARS):
bar = get_next_bar()
if bar is None:
break
closes.append(bar["c"])
read_speed()
if STEP_DELAY > 0:
time.sleep(STEP_DELAY)
if len(closes) < SLOW:
continue
fast_ma = sma(closes, FAST)
slow_ma = sma(closes, SLOW)
rsi = calc_rsi(closes, RSI_PERIOD)
price = bar["c"]
# Buy: fast SMA above slow SMA AND RSI above 50
if fast_ma > slow_ma and rsi > 50 and position != "LONG":
if position == "SHORT":
place_order(ACCOUNT_ID, CONTRACT_ID, 0, 2)
if entry_price - price > 0: wins += 1
trade_count += 1
else:
place_order(ACCOUNT_ID, CONTRACT_ID, 0, 1)
position = "LONG"
entry_price = price
trade_count += 1
print(f"[TRADE] BUY @ {price:.2f} | RSI: {rsi:.1f}")
# Sell: fast SMA below slow SMA AND RSI below 50
elif fast_ma < slow_ma and rsi < 50 and position != "SHORT":
if position == "LONG":
place_order(ACCOUNT_ID, CONTRACT_ID, 1, 2)
if price - entry_price > 0: wins += 1
trade_count += 1
else:
place_order(ACCOUNT_ID, CONTRACT_ID, 1, 1)
position = "SHORT"
entry_price = price
trade_count += 1
print(f"[TRADE] SELL @ {price:.2f} | RSI: {rsi:.1f}")

What you need to know

Before writing custom strategies, make sure you understand: