HeyTraders Documentation

Signal Template

Signal templates create a single-instrument strategy from price, indicator, and position-state conditions. They are the default choice when your idea is "enter when this condition happens, exit when that condition happens."

Available Signal Templates

TemplateBehavior
BreakoutEnters when price breaks a recent high or low.
Mean ReversionEnters when price stretches away from a reference band and momentum confirms exhaustion.
MA TrendFollows moving-average trend changes.
Range BounceLooks for reversals near recent support and resistance.
MACD MomentumEnters and exits on MACD momentum flips.

Choosing a Starting Template

Choose the template whose market assumption matches the idea you want to test. Breakout and MA Trend follow directional movement, while Mean Reversion and Range Bounce expect price to return toward a reference. MACD Momentum reacts to changes in momentum rather than a fixed price level. A template is a starting point, not evidence that its assumption holds for a particular symbol or period.

Before running a backtest, replace the example ticker and notional, review every lookback window, and confirm that long and short behavior is intended. Test entry and exit conditions separately, then inspect trade count, drawdown, fees, and out-of-sample behavior. Add position sizing and risk controls only after the basic signals behave as expected.

Product and Inventory Requirements

The examples below are two-sided strategies: they attempt to open a short from a flat strategy position. Use a supported perpetual/futures ticker for that behavior. Do not copy the short-entry branch into a cash-only spot Paper strategy and expect it to borrow coins.

Spot orders can sell only owned inventory after existing sell reservations. SHORT and SELL are aliases for a sell-direction intent; neither enables borrowing. NoPosition() refers to the strategy position, not the account's available inventory. Broker validates actual sellable inventory when the order is submitted.

For a user-specified spot strategy, agree on long-only behavior if that is what the user wants; do not silently switch to futures or remove the short leg. Setting a leverage number does not change the product. DSL validation success is not proof that a future order has sufficient inventory.

Candle and Tick Cadence

The indicator templates below are candle strategies. Run them with a candle timeframe such as 1m or 1h; their bare open, high, low, close, and volume variables are completed candle series.

Do not copy a bare close condition into a tick strategy. Tick strategies use the scalar price for the current observation. When a tick strategy also needs candle history, request it explicitly:

ticker = context['ticker']
candles = get_data(ticker, '1m')
entry_signal = (price >= 0.55) & (candles.close[-1] > candles.open[-1])
emit(entry_signal, entry(ticker, 'LONG', Notional(5), when=NoPosition()))

Breakout DSL

ticker = context['ticker']
breakout_high = highest(high, 20).shift(1)
breakout_low = lowest(low, 20).shift(1)
long_entry = close > breakout_high
short_entry = close < breakout_low
long_exit = close < sma(close, 20)
short_exit = close > sma(close, 20)

emit(long_entry, entry(ticker, 'LONG', Notional(500), when=NoPosition()))
emit(short_entry, entry(ticker, 'SHORT', Notional(500), when=NoPosition()))
emit(long_exit, exit_position(ticker, when=HasPosition(side='LONG')))
emit(short_exit, exit_position(ticker, when=HasPosition(side='SHORT')))

Mean Reversion DSL

ticker = context['ticker']
upper, middle, lower = bollinger_bands(close, 20, 2.0)
rsi_val = rsi(close, 14)
long_entry = (close < lower) & (rsi_val < 30)
short_entry = (close > upper) & (rsi_val > 70)
long_exit = close >= middle
short_exit = close <= middle

emit(long_entry, entry(ticker, 'LONG', Notional(500), when=NoPosition()))
emit(short_entry, entry(ticker, 'SHORT', Notional(500), when=NoPosition()))
emit(long_exit, exit_position(ticker, when=HasPosition(side='LONG')))
emit(short_exit, exit_position(ticker, when=HasPosition(side='SHORT')))

MA Trend DSL

ticker = context['ticker']
fast = ema(close, 20)
slow = ema(close, 50)
long_entry = crossover(fast, slow)
short_entry = crossunder(fast, slow)
long_exit = crossunder(fast, slow)
short_exit = crossover(fast, slow)

emit(long_entry, entry(ticker, 'LONG', Notional(500), when=NoPosition()))
emit(short_entry, entry(ticker, 'SHORT', Notional(500), when=NoPosition()))
emit(long_exit, exit_position(ticker, when=HasPosition(side='LONG')))
emit(short_exit, exit_position(ticker, when=HasPosition(side='SHORT')))

Range Bounce DSL

ticker = context['ticker']
support = lowest(low, 20).shift(1)
resistance = highest(high, 20).shift(1)
mid = (support + resistance) / 2
long_entry = close <= support * 1.01
short_entry = close >= resistance * 0.99
long_exit = close >= mid
short_exit = close <= mid

emit(long_entry, entry(ticker, 'LONG', Notional(500), when=NoPosition()))
emit(short_entry, entry(ticker, 'SHORT', Notional(500), when=NoPosition()))
emit(long_exit, exit_position(ticker, when=HasPosition(side='LONG')))
emit(short_exit, exit_position(ticker, when=HasPosition(side='SHORT')))

MACD Momentum DSL

ticker = context['ticker']
macd_line, signal_line, _ = macd(close, 12, 26, 9)
long_entry = crossover(macd_line, signal_line)
short_entry = crossunder(macd_line, signal_line)
long_exit = crossunder(macd_line, signal_line)
short_exit = crossover(macd_line, signal_line)

emit(long_entry, entry(ticker, 'LONG', Notional(500), when=NoPosition()))
emit(short_entry, entry(ticker, 'SHORT', Notional(500), when=NoPosition()))
emit(long_exit, exit_position(ticker, when=HasPosition(side='LONG')))
emit(short_exit, exit_position(ticker, when=HasPosition(side='SHORT')))