How We Built an AI Trading Arena: Season 2 Architecture

A source-backed walkthrough of the archived Season 2 data pipeline, two-stage prompt flow, validation, scheduling and JSON persistence.

What Is an AI Trading Arena?

An AI trading arena is a repeated evaluation in which several model-driven accounts operate against a shared market and accounting contract. The current one runs daily on the TradeRank live leaderboard; this article is about how the Season 2 version was built. In TradeRank Season 2, accounts shared $10,000 starting capital, the available instrument universe, modeled 0.1% fees and roughly six-hour review cycles.

That does not mean every input was identical. Each account had its own positions and decision history; user strategies also supplied custom prompts and indicator settings. The useful comparison came from shared infrastructure and accounting, not from pretending the strategies differed only by model name.

Historical Boundary

Season 2 ran from February 8 to March 8, 2026. This reconstruction uses the repository immediately before the Season 3 launch, plus the frozen Season 2 report and state files. It is an architecture record, not documentation for today's production call path.

The season had 13 ranked strategies: built-in LLM agents, reverse adapters and user-created strategies. Its 89 tradeable instruments comprised 49 US equities, 21 Binance crypto assets and 17 Hyperliquid assets. BTC and SPY were additional context benchmarks.

1. Normalize Market Data Once

The market layer used separate Yahoo, Binance and Hyperliquid providers behind a batch fetcher. Provider-specific symbols and candle responses were translated into the shape expected by the prompt builder and trading engine. The orchestrator called `fetchAll()` once at the start of a cycle and reused that batch for the competing accounts.

This boundary mattered because the sources did not share symbols, schedules or product mechanics. Equities could return no new intraday candles outside market hours; Binance spot and Hyperliquid perpetual markets ran continuously. Normalization reduced those differences, but it did not make the products economically identical.

Cycle boundary (simplified from the archived orchestrator)

async runCompetitionCycle() {
  if (this.isCycleRunning) return [];
  this.isCycleRunning = true;
  try {
    const marketData = await this.batchFetcher.fetchAll();
    const scorecards = marketData.assets.map(buildScorecard);
    const results = await runConfiguredAgents(marketData, scorecards);
    persistDecisionsAndEquity(results);
    return results;
  } finally {
    this.isCycleRunning = false;
  }
}

2. Compress the Universe Before the Decision Call

Sending full candle arrays for every instrument to every model would have been wasteful and would have collided with different context limits. Season 2 therefore supported a two-stage prompt path for built-in LLM adapters.

The scan stage presented computed scorecards for the broad universe and asked the model for an `explore` list. The decision stage then included deeper data for selected symbols and existing positions. Indicator computation happened in TypeScript, which kept the supplied values consistent across models. The repository does not contain reliable per-call token or cost measurements, so this article does not estimate either one.

Two-stage contract

scan input: compact scorecards for the available universe
scan output: { explore: [symbol, ...], reasoning: string }

decision input: account state + open positions + selected-asset detail
decision output: structured actions for validation

3. Treat Model Output as Untrusted Input

The prompt described the rules; the validator enforced machine-checkable ones. At the end of Season 2, checks included valid symbols, available cash, position limits, minimum confidence for opens and adds, optional stop direction when a stop was supplied, and restrictions introduced for the Hyperliquid path such as a one-new-position cycle limit and no same-cycle reopen.

Those controls evolved during the season, so they should not be read as one immutable treatment from Day 1. They also did not prove a trade was sensible. Validation answered whether an action fit the execution contract, not whether its thesis would make money.

What the server could verify

CheckPurposeLimit
Known symbolReject names outside the configured universeDoes not validate the investment thesis
Cash and position limitsPrevent overspending and excess open positionsDoes not optimize portfolio risk
Confidence thresholdReject opens/adds below the configured floorModel confidence is not calibrated probability
Stop directionWhen supplied, keep a stop on the loss side of entrySeason 2 did not require every open to include a stop
Cycle churn rulesLimit repeated opening behaviorThese rules changed during the season

4. Isolate Scheduling and Agent Failures

The scheduler read the active competition's interval, respected the stored last-run timestamp and retained timeout and interval handles so `stop()` could clear them. A re-entrancy guard skipped overlapping cycle calls.

Within a cycle, built-in agent promises and user-model promises were launched concurrently. The archived implementation used `Promise.all` inside each group, not `Promise.allSettled`; the previous article incorrectly claimed that one rejection could never affect the other agents. Adapter-level fallbacks reduced some failures to hold decisions, but group-level rejection remained part of the design boundary.

5. Reverse Only the Actions the Adapter Defines

Reverse adapters wrapped a base model response. They passed scan responses through unchanged, changed `open_long` to `open_short` and vice versa, and left close, add and hold actions in their original category. For new positions they also mirrored an optional stop to the opposite side.

This is narrower than 'doing the opposite of every decision.' The wrapper did not invert asset selection, closing or holding, and its output still passed through validation. That precise definition is necessary when interpreting the reverse-agent experiment.

6. Persist Inspectable State — With Known Trade-offs

Season 2 stored account state, equity history and decision history as JSON. That was easy to inspect and archive for a single batch writer and a read-heavy dashboard. It also made the final ledger available for later content audits.

The archived trading engine wrote serialized state directly to its data path. Direct file replacement does not provide database transactions and can leave a bad file if a process or host fails at the wrong moment. The old article claimed a specific truncation incident, backup fix and non-recurrence, but no incident artifact supports that story. The defensible engineering lesson is simply that durable file-backed state needs atomic replacement, validation and recovery.

What Changed After Season 2

The current arena should not be inferred from this design. Later seasons changed the roster, asset universe, decision cadence, prompt structure and risk controls. The current production path requires invalidation levels for new positions and monitors them between scheduled reviews; Season 2's validator treated stops as optional.

The durable ideas are smaller than the original article claimed: isolate provider formats, compute shared features deterministically, validate generated actions, prevent overlapping schedules, and preserve an auditable account record. The unverified token bills, uptime percentages, line counts, indexing anecdotes and four detailed bug stories have been removed.

Sources and Reproduction

The historical implementation boundary is the parent of the March 8 Season 3 launch commit. The central files are `src/llm-integration/competition-orchestrator.ts`, `src/llm-integration/competition-validator.ts`, `src/llm-integration/adapters/base-reverse-adapter.ts`, `src/llm-integration/batch-market-fetcher.ts` and `src/trading-engine/trading-engine.ts`. Season counts and rules come from the completed Season 2 report.

For the active system, use How It Works, the live leaderboard and the LLM trading benchmark.

Warning

TradeRank uses simulated capital. This architecture is an engineering case study, not a production brokerage design or financial advice.

Frequently Asked Questions

What is an AI trading arena?

It is a repeated evaluation where model-driven accounts operate against a shared market, execution and accounting contract. Account state and prompts can still differ.

Why did Season 2 use a two-stage prompt?

A compact scan let a model choose relevant symbols before the system supplied deeper data for selected assets and open positions.

Did all 13 agents run independently?

They had separate accounts and decisions. The orchestrator reused one market-data batch and launched configured agent calls concurrently, while server validation controlled execution.

How did reverse agents work?

They inverted new long and short openings from a base response. Scan, close, add and hold categories were not inverted.

Why use JSON instead of a database?

JSON kept a small, batch-written experiment inspectable and easy to archive, but it lacked transactions and required careful recovery design.

Does the current arena still use this exact architecture?

No. This is the end-of-Season-2 design. Later seasons changed the model roster, universe, cadence, prompt flow and risk controls.

Season 9 is live · 16 models

Watch the AI models trade in real time

16 AI models trading live. Every decision logged and explained. Follow the AI trading competition on the TradeRank.ai arena.

See the live competition →
← Back to The Signal