Skip to main content
The Autopilot SDK (@shoot/autopilot) lets traders configure autonomous trading playbooks that execute around the clock within Adrena prop challenges. Traders pick a playbook, tune its parameters, and the autopilot handles execution — generating continuous volume without requiring the trader to be online.

Why Autopilot?

Adrena’s competitions generated massive volume — but only during human trading hours. The autopilot flips this: traders configure a playbook (strategy + risk guardrails), register it on-chain as an agent, and the FlightController executes ticks 24/7. This keeps volume flowing around the clock while keeping humans in the loop for strategy design.

Architecture

┌──────────────────────────────────────────────────┐
│  FlightController (cockpit/)                     │
│  Tick loop: fetch → assess → validate → execute  │
├──────────────────────────┬───────────────────────┤
│  Playbooks (playbooks/)  │  RiskHarness          │
│  TrendSurfer             │  Ceiling leverage      │
│  FadeTrader              │  Max exposure fraction  │
│  RangeSniper             │  Cut-loss / lock-gain   │
│  FundingArb              │  Cooldown enforcement   │
│  GridRunner              │                        │
├──────────────────────────┼───────────────────────┤
│  Indicators (indicators/)  │  Feed (feed/)          │
│  VWAP · ATR · MACD       │  OracleTap (Pyth)     │
│  Stochastic · Keltner    │  ReplayTap (test)      │
├──────────────────────────┴───────────────────────┤
│  On-chain (onchain/)                             │
│  ShootProgram · deriveAgentPda · PerpBuilder      │
└──────────────────────────────────────────────────┘

Playbooks

Each playbook implements the Playbook interface with an assess() method that returns a Verdict:
interface Playbook {
  readonly label: string;
  readonly summary: string;
  assess(bars: Bar[], exposure: Exposure | null): Verdict;
}

type Verdict =
  | { kind: "buy"; conviction: number; allocation: number }
  | { kind: "sell"; conviction: number; allocation: number }
  | { kind: "exit"; memo: string }
  | { kind: "pass" };

TrendSurfer

MACD histogram zero-line crossover. Enters when the MACD histogram crosses from negative to positive (buy) or positive to negative (sell). Exits when histogram reverses against the position direction. Uses histogram momentum — a derivative signal — rather than raw line crossover.
ParameterDefaultDescription
fastLen12Fast MACD period
slowLen26Slow MACD period
signalLen9Signal line smoothing

FadeTrader

Keltner Channel + Stochastic confirmation. Fades price when it breaches the outer Keltner envelope AND the Stochastic %K confirms oversold/overbought. Requires dual confirmation from two independent indicators. Exits when price returns to the Keltner basis (center EMA).
ParameterDefaultDescription
keltnerPeriod20Keltner Channel EMA period
atrMultiplier2.0ATR band width
stochK14Stochastic %K period
stochD3Stochastic %D smoothing
stochOB80Overbought threshold
stochOS20Oversold threshold
Keltner Channels use EMA + ATR for the envelope. This is mathematically different from Bollinger Bands (SMA + standard deviation). The ATR-based envelope adapts to true price range rather than just close-price variance.

RangeSniper

ATR contraction/expansion detector with VWAP directional bias. Detects volatility squeezes (ATR declining for N consecutive bars), then enters when ATR suddenly expands and price moves away from VWAP. This captures volatility regime changes rather than static price level breaks.
ParameterDefaultDescription
atrPeriod14ATR calculation period
contractionBars5Consecutive bars of declining ATR
expansionMult1.5Required ATR expansion ratio

FundingArb

Perpetual funding rate exploitation via price deviation proxy. Unique to perps — estimates “implied funding” from the spread between current price and recent average price. When shorts are overcrowded (negative implied funding), goes long expecting mean reversion. Conservative allocation (counter-trend).
ParameterDefaultDescription
fundingThreshold0.01Entry threshold (1% deviation)
exitThreshold0.003Exit threshold (0.3% deviation)
lookbackBars20Recent price window
FundingArb is the only playbook specifically designed for perpetual futures mechanics. It exploits the tendency of overcrowded positions to generate funding rate pressure that eventually mean-reverts.

GridRunner

Dynamic ATR-based grid trading around a VWAP anchor. Places virtual buy/sell levels above and below VWAP, spaced by ATR fractions. When price crosses a grid level, it triggers a trade. Stateful: tracks which grid level was most recently crossed.
ParameterDefaultDescription
gridLevels5Number of levels above/below center
atrPeriod14ATR period for spacing
gridSpacingAtrFrac0.5Grid spacing as fraction of ATR

Indicators

All indicator functions are pure (no side effects, no IO). Each takes numeric arrays or Bar[] and returns computed values.
FunctionInputOutputDescription
computeVWAPBar[]number[]Running volume-weighted average price
computeATRBar[], periodnumber[]Wilder-smoothed average true range
computeMACDcloses[], fast, slow, signalMacdResultMACD line, signal, histogram
computeStochasticBar[], kPeriod, dPeriodStochResult%K and %D oscillator lines
computeKeltnerBar[], period, atrMultKeltnerEnvelopeEMA basis + ATR-based channels

Risk Management

The RiskHarness validates every verdict before execution:
const guardrails: Guardrails = {
  ceilingLeverage: 5, // max leverage
  maxExposureFrac: 0.25, // max bankroll fraction per trade
  cutLossPct: 0.03, // 3% stop-loss
  lockGainPct: 0.06, // 6% take-profit
  pauseMs: 30_000, // 30s cooldown between trades
};
Checks applied to every verdict:
  1. Cooldown: rejects if less than pauseMs since last trade
  2. Duplicate exposure: rejects buy when already long (or sell when already short)
  3. Exposure fraction: rejects if allocation exceeds maxExposureFrac
  4. Leverage: rejects if implied leverage exceeds ceilingLeverage
  5. Stop-loss / take-profit: force-exits when thresholds are breached

FlightController

The FlightController runs the autonomous trading loop:
Each tick:
1. Fetch bars from feed (OracleTap or ReplayTap)
2. Check guardrails (stop-loss / take-profit)
3. If guardrail exit → execute and return
4. Get playbook verdict via assess()
5. Validate through RiskHarness
6. If buy/sell/exit → execute trade
7. Wait cadenceMs, repeat

On-Chain Integration

Autopilot agents are registered on-chain via the Shoot Anchor program:
import { ShootProgram, deriveAgentPda } from "@shoot/autopilot";

// Derive agent PDA
const [agentPda] = deriveAgentPda(ownerPublicKey);

// Build register instruction
const ix = await program.buildRegisterAgentIx(
  "MyTrendSurfer",
  strategyHash // SHA-256 of strategy config
);
Each agent PDA stores:
  • Owner public key
  • Strategy hash (SHA-256 of config, updated on strategy change)
  • ELO rating (starts at 1000)
  • Win/loss record and total P&L
  • Competition count and last trade timestamp

Testing

cd sdk
npm install
npm test    # 144 tests across 14 suites
SuiteTestsCoverage
Indicators (VWAP, ATR, MACD, Stochastic, Keltner)42Pure math, edge cases, known values
Playbooks (TrendSurfer, FadeTrader, RangeSniper, FundingArb, GridRunner)66Signals, exits, conviction, exposure
Cockpit (FlightController, RiskHarness)23Risk validation, stop-loss, tick lifecycle
Feed + On-chain (ReplayTap, ShootProgram, PerpBuilder)13Cursor replay, PDA derivation, IX builders