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
  • Account info — Check balance, equity, and account status at any time
  • Raw API access — Call any TopStep 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 is fully portable to live trading environments.

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 TopStep 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

The header (imports, config, API helpers) and footer (cleanup, results) are auto-generated. You only need to write the strategy logic inside the main loop.

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. Set MAX_BARS to a small number (e.g., 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, set MAX_BARS back to 0 (unlimited) 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 signals that are confirmed by RSI:

# 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: