Skip to main content

Overview

Autonome’s autonomous trading system orchestrates AI agents that continuously analyze market data, manage positions, and execute trades with minimal human intervention. Each agent operates on a 5-minute polling cycle, using real-time indicators, portfolio state, and performance metrics to make informed decisions.

Trading Loop Architecture

The trading loop follows a structured workflow that ensures consistency and auditability:
1

Market Data Aggregation

Fetch real-time OHLCV data, technical indicators (RSI, MACD, EMA, Bollinger Bands), and funding rates from exchange APIs.
2

Portfolio State Snapshot

Retrieve current account balance, open positions with unrealized P&L, available cash, and exposure metrics.
3

AI Agent Invocation

Execute the trading agent with a structured prompt containing market intelligence, portfolio state, performance history, and strategy-specific instructions.
4

Tool Execution

Process agent tool calls (createPosition, closePosition, updateExitPlan, holding) and execute orders via the exchange simulator or live API.
5

State Update & Logging

Record invocation results, tool calls, and execution outcomes in the database. Update portfolio history and emit SSE events for real-time UI updates.

Decision Framework

Agents use a multi-step reasoning process powered by the AI SDK v6:
// src/server/features/trading/agent/tradeAgentFactory.ts (conceptual)
import { generateText } from "ai";
import { createTradingTools } from "./tools";

const result = await generateText({
  model: provider(modelName),
  system: systemPrompt,    // Strategy-specific rules
  prompt: userPrompt,       // Dynamic market data + portfolio state
  tools: createTradingTools(context),
  maxSteps: 5,              // Allow multi-step reasoning
});

Tool Context

Agents receive a ToolContext object that tracks session state and enforces constraints:
// src/server/features/trading/agent/tools/types.ts
export interface ToolContext {
  account: Account;                    // Account metadata (ID, balance, variant)
  invocationId: string;                // Current invocation DB record
  openPositions: EnrichedOpenPosition[]; // Open positions with P&L
  actedSymbols: Set<string>;           // Symbols acted on this session
  symbolActionCounts: Map<string, number>; // Per-symbol action tracking
  closedPositionCooldowns: Map<string, ClosedCooldown>; // Direction flip prevention
  capturedDecisions: NormalizedDecision[];  // Decisions for telemetry
  capturedExecutionResults: ExecutionResult[]; // Execution outcomes
}
The actedSymbols set prevents duplicate actions on the same symbol within a single invocation, while symbolActionCounts enforces session-wide limits (default: 3 actions per symbol per session).

Agent Tools

Agents interact with the trading system via four core tools:

createPosition

Opens one or more positions atomically. Validates cooldown periods, available cash, and symbol limits.
// src/server/features/trading/agent/tools/createPositionTool.ts
createPo sition({
  decisions: [
    {
      symbol: "BTC",
      side: "LONG",
      quantity: 0.1,
      leverage: 5,
      stop_loss: 95000,
      profit_target: 105000,
      invalidation_condition: "4h close below EMA50",
      invalidation_price: 94500,
      time_exit: "Close if held >24h",
      cooldown_minutes: 10,
      confidence: 75,
    },
  ],
});
Exit Plan Fields:
  • invalidation_condition: Qualitative thesis break (e.g., “Reversal candle”)
  • invalidation_price: Exact price where thesis is invalidated
  • time_exit: Time-based exit rule (e.g., “Close if held >2h”)
  • cooldown_minutes: Prevents direction flips (1-15 min)

closePosition

Closes one or more open positions at market price.
closePosition({ symbols: ["BTC", "ETH"] });

updateExitPlan

Modifies stop loss, take profit, or invalidation levels for open positions (e.g., trailing stops).
updateExitPlan({
  symbol: "BTC",
  stop_loss: 96000,  // Raise stop to breakeven
  profit_target: 110000,
  invalidation_condition: "EMA20 break",
});

holding

Explicit no-action decision with reasoning. Required when no trades are taken.
holding({ reasoning: "Waiting for ADX > 25. Current market is choppy (ADX 18)." });
Agents must end every invocation with a tool call. Failing to call holding() when no action is taken results in an incomplete workflow.

Cooldown System

To prevent impulsive direction flips, Autonome enforces cooldown periods:
  1. While Holding: Cannot open opposite direction until cooldown expires
  2. After Closing: Cooldown persists after exit (tracked in closedPositionCooldowns)
  3. Same Direction: Cooldown does not block adding to existing positions
// src/server/features/trading/agent/tools/createPositionTool.ts:27
function calculateCooldownUntil(minutes?: number | null): string {
  const cooldownMinutes = Math.min(15, Math.max(1, minutes ?? 15));
  return new Date(Date.now() + cooldownMinutes * 60 * 1000).toISOString();
}

Prompt Structure

Prompts follow a spoon-feeding philosophy: provide all data explicitly, never require the AI to infer or calculate.
// src/server/features/trading/promptBuilder.ts:121
const userPrompt = USER_PROMPT
  .replaceAll("{{INVOKATION_TIMES}}", account.invocationCount.toString())
  .replaceAll("{{CURRENT_TIME}}", currentTime)
  .replaceAll("{{AVAILABLE_CASH}}", formatUsd(portfolio.availableCash))
  .replaceAll("{{EXPOSURE_TO_EQUITY_PCT}}", exposurePercentLabel)
  .replaceAll("{{MARKET_INTELLIGENCE}}", marketIntelligence)
  .replaceAll("{{PORTFOLIO_SNAPSHOT}}", buildPortfolioSnapshotSection({...}))
  .replaceAll("{{OPEN_POSITIONS_TABLE}}", buildOpenPositionsSection(openPositions))
  .replaceAll("{{PERFORMANCE_OVERVIEW}}", buildPerformanceOverview({...}));
Key Principles:
  • Explicit labels: risk_usd $128.56 not risk $128.56
  • Show zeros: scaled_realized $0.00 is meaningful
  • Omit N/A: Don’t show data that doesn’t exist
  • No duplication: Each metric lives in exactly one section
See AI Strategy Prompts for variant-specific prompt examples.

Execution Pipeline

When createPosition is called, the system:
  1. Validates cooldowns, symbol limits, and available cash
  2. Normalizes decisions (uppercase symbols, clamp leverage)
  3. Executes orders via ExchangeSimulator or Lighter API
  4. Tracks fills using fillTracker.ts (polls for actual execution price)
  5. Records tool calls in ToolCalls table for audit trail
  6. Updates Orders table (single source of truth for positions)
  7. Emits SSE events to update UI in real-time
// src/server/features/trading/createPosition.ts (simplified)
export async function createPosition(
  account: Account,
  requests: PositionRequest[],
): Promise<PositionResult[]> {
  const simulator = await ExchangeSimulator.bootstrap();
  const results = [];

  for (const req of requests) {
    const orderResult = await simulator.placeOrder(
      { symbol: req.symbol, side: req.side, quantity: req.quantity, type: "market" },
      account.id,
    );

    if (orderResult.status === "filled") {
      // Record in Orders table
      await createOrder({
        modelId: account.id,
        symbol: req.symbol,
        side: req.side,
        quantity: req.quantity.toString(),
        entryPrice: orderResult.averagePrice.toString(),
        leverage: req.leverage?.toString(),
        exitPlan: { stop: req.stopLoss, target: req.profitTarget, ... },
      });
    }

    results.push({ success: true, ...orderResult });
  }

  return results;
}

Auto-Close Triggers

The simulator monitors open positions and automatically closes them when:
  • Stop Loss Hit: Mark price <= stop loss (longs) or >= stop loss (shorts)
  • Take Profit Hit: Mark price >= take profit (longs) or <= take profit (shorts)
// src/server/features/simulator/accountState.ts (conceptual)
collectExitPlanTriggers(): ExitTrigger[] {
  const triggers = [];
  for (const [symbol, position] of this.positions) {
    const exitPlan = this.exitPlans.get(symbol);
    if (!exitPlan) continue;

    if (exitPlan.stop && this.shouldTriggerStop(position, exitPlan.stop)) {
      triggers.push({ symbol, trigger: "STOP" });
    }
    if (exitPlan.target && this.shouldTriggerTarget(position, exitPlan.target)) {
      triggers.push({ symbol, trigger: "TARGET" });
    }
  }
  return triggers;
}
Auto-closes are recorded in the ToolCalls table with autoTrigger metadata for transparency.

State Synchronization

The system maintains consistency between simulator state and database:
  • Bootstrap: On startup, restorePositionsFromDb() rehydrates simulator from Orders table
  • Polling: Every refreshIntervalMs (default: 5s), fetch market prices and check triggers
  • SSE Events: Emit allDataChanged events to invalidate TanStack Query caches
The Orders table is the single source of truth. The simulator is stateless and can be rebuilt from the database at any time.

AI Strategy Prompts

Explore variant-specific prompts and reasoning styles

Trading Simulator

Learn how the simulator mirrors live trading

Portfolio Analytics

Deep dive into performance metrics and calculations

Configuration

Configure API keys, schedulers, and simulator options

Build docs developers (and LLMs) love