Trader — FX Backtesting and Live Trading Engine

A Go engine for backtesting and live paper-trading Forex strategies against OANDA, with a REST/WebSocket API, embedded UI, and Claude MCP tool integration for querying accounts and running backtests from an AI assistant.

Trader is a Forex backtesting and live paper-trading engine written in Go. It downloads historical candle data, runs configurable strategies against that data, and — using the same strategy interface — executes those same strategies live against an OANDA practice or live account. A REST and WebSocket API, an embedded web UI, and a Claude MCP server round out the system.

The Problem

Building a trading strategy is straightforward. Knowing whether it works is not. The gap between “this looks good on a chart” and “this survives statistical scrutiny across years of data” is where most strategy ideas die.

Trader addresses this by making the path from idea to verified result as short as possible: write a strategy, point it at historical data, get a report. The same strategy then runs live without modification.

Architecture

The core backtest pipeline is a linear data flow:

Config (YAML) → DataManager → Backtest → Strategy → Broker → Account → Journal

Config — a YAML file specifies the instrument, date range, starting balance, risk percentage per trade, stop and take-profit sizing, and which strategy to run.

DataManager — loads historical candle data from the local store, built from Dukascopy tick data or OANDA candle downloads.

Backtest — feeds candles one at a time to the strategy, routes the resulting orders through the broker, and accumulates the trade record.

Strategy — a single Go interface:

type Strategy interface {
    Name() string
    Reset()
    Ready() bool
    Update(ctx context.Context, candle *CandleTime, bt *Backtest) *StrategyPlan
}

Update receives the current candle and returns a plan — open, close, or hold. It knows nothing about whether it is running in a backtest or live. That is the key design property: the strategy is pure logic, isolated from execution.

Broker — executes the plan. In backtesting, the broker simulates fills with configurable slippage and spread filters. In live trading, the broker calls the OANDA API.

Account — tracks balance, open positions, margin, and unrealized P/L. Accounting invariants are enforced: Equity = Balance + UnrealizedPL, FreeMargin = Equity − MarginUsed.

Journal — records every closed trade for reporting and analysis.

Key Design Decisions

Fixed-point arithmetic for all financial values. Price is int32 scaled at 100,000 (five decimal places — standard forex pip precision). Money is int64 scaled at 1,000,000. Pips is int32 scaled at 10. Floats appear only at I/O boundaries — parsing config and formatting output. See Numeric Types in Financial Software for the reasoning.

One strategy interface, two execution environments. The Strategy interface is the only contract between business logic and the execution engine. Swapping the broker from a simulation to a live OANDA connection requires no changes to the strategy. See The Strategy Pattern in a Backtesting Engine for the architecture.

Regression testing for strategies. The trader backtest regress command runs every YAML config in a directory and writes JSON and org-mode reports. A strategy that passes unit tests but degrades on out-of-sample data is caught before it touches a live account.

Strategies

StrategyDescription
emacrossEMA crossover — fast/slow moving average
emacrossadxEMA crossover filtered by ADX trend strength
donchianDonchian channel breakout
pulseMechanical open/close on a fixed tick schedule
noopBaseline — does nothing, establishes spread/slippage cost

New strategies implement the four-method Strategy interface. The tmpl strategy is a documented starting point.

MCP Integration

trader mcp starts an MCP server over stdio, exposing Trader as typed tools for Claude Code and Claude Desktop:

ToolDescription
get_account_summaryBalance, NAV, margin, unrealized P/L
list_open_tradesAll open OANDA positions
run_backtestRun a YAML config, return summary
place_orderRisk-sized market order (write-gated)
close_tradeFull or partial close by trade ID (write-gated)
update_stopMove stop-loss or take-profit (write-gated)

Read tools are always available. Write tools require --enable-write at startup. This means you can query your account, review open trades, and run backtests from an AI assistant without ever accidentally placing an order.

See Exposing a Go App as Claude MCP Tools for the implementation approach.

Data Sources

  • Dukascopy — free tick data, aggregated into OHLC candles locally: trader data sync --instruments EUR_USD --from 2022-01 --to 2024-12
  • OANDA — candles via API (requires token): trader data oanda --instrument EUR_USD --granularity H1 --from 2024-01-01

Candle data is stored under $DATA_DIR as /<source>/<INSTRUMENT>/<YYYY>/<MM>/, keeping sources and instruments cleanly separated.

GitHub

rustyeddy/trader