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 = Nonetrade_count = 0wins = 0entry_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 logicThe 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:
- Start from a template. Run an existing template to understand the structure and output format.
- Switch to Custom. Select the Custom template and start modifying the main loop.
- Test incrementally. Set
MAX_BARSto a small number (e.g., 100) while developing. This lets you iterate quickly without waiting for a full backtest. - 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. - Run a full backtest. Once your logic works on a small sample, set
MAX_BARSback to0(unlimited) and run against the full date range. - 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:
# ParametersFAST = 10SLOW = 30RSI_PERIOD = 14
closes = []position = Nonetrade_count = 0wins = 0entry_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: