amm-strategy-backtester
v1.1.2
Published
Write and backtest trading strategies against a multi-agent AMM token market with liquidity and vesting.
Maintainers
Readme
AMM STRATEGY BACKTESTER
A Node.js sandbox for writing and backtesting trading strategies against a simulated multi-agent token market.
Implement a strategy that returns buy/sell/hold decisions each tick, plug it into an agent population alongside whales, bots, and retail traders, and run it against a constant-product AMM with liquidity, vesting-driven circulating supply, and a seeded RNG for reproducible results.
Table of Contents
- Features
- Prerequisites
- Installation
- Strategy Interface
- Usage
- Configuration
- Built-in Strategies
- API Reference
- Project Structure
- Contributing
- License
Features
- Write custom trading strategies and backtest them over hundreds of ticks
- Mix your strategy into a population with built-in whale / bot / retail agents
- Extra factories: RSI, DCA, grid, MA crossover, market maker, volume-follow, mean reversion, breakout
- Constant-product AMM pool with swap fees and price impact
- Token vesting schedules (TGE, cliffs, linear unlocks) that drive circulating supply
- Seeded RNG for reproducible runs
- Per-agent
costBasisandrealizedPnlfor stop-loss / take-profit and performance checks
Prerequisites
- Node.js v14+
- npm or yarn
Installation
npm install amm-strategy-backtesterOr clone for local development:
git clone https://github.com/Blustdp/amm-strategy-backtester.git
cd amm-strategy-backtester
npm installStrategy Interface
Strategies are factories that return a decision function:
(agent, context) => ({ action: 'buy' | 'sell' | 'hold', amount: number })agent exposes balances and module-tracked position state:
baseBalance,tokenBalancecostBasis— running average buy price (use this for stop-loss / take-profit)realizedPnl
context each tick:
tick,currentPrice,launchPricepriceHistory— prices from prior ticksvolumeHistory— base-currency volume from prior ticksrng— seeded RNG from the simulator
Return hold with amount: 0 during indicator warm-up or when there is no trade.
Note:
Agenttracks a single runningcostBasis, not a list of partial positions. Multi-position strategies (e.g. grid bots) must keep their own per-agent state (typically aMapkeyed byagent.id). Each tick can emit only one{ action, amount }decision.
Usage
Importing the library
import {
TokenSimulator,
AMMPool,
VestingSchedule,
AllocationPlan,
Agent,
strategies,
generateAgentPopulation,
defaultArchetypes,
} from 'amm-strategy-backtester';Backtest a custom strategy in a population
Same pattern as plugging RSI / grid strategies into the market sandbox:
import { TokenSimulator, defaultArchetypes } from 'amm-strategy-backtester';
function myStrategy({ tradeFraction = 0.2 } = {}) {
return (agent, context) => {
const { currentPrice, priceHistory } = context;
if (priceHistory.length < 10) return { action: 'hold', amount: 0 };
if (agent.tokenBalance > 0 && agent.costBasis > 0) {
const changePct = (currentPrice - agent.costBasis) / agent.costBasis;
if (changePct <= -0.05 || changePct >= 0.1) {
return { action: 'sell', amount: agent.tokenBalance };
}
}
if (agent.tokenBalance <= 0 && agent.baseBalance > 0) {
return { action: 'buy', amount: agent.baseBalance * tradeFraction };
}
return { action: 'hold', amount: 0 };
};
}
const archetypes = [
{
type: 'myTrader',
weight: 50,
balanceRange: [500, 3000],
strategy: myStrategy(),
},
{
type: 'retail',
weight: 40,
balanceRange: [50, 2000],
strategy: defaultArchetypes()[2].strategy,
},
{
type: 'bot',
weight: 10,
balanceRange: [500, 5000],
strategy: defaultArchetypes()[1].strategy,
},
];
const simulator = new TokenSimulator({
seed: 99,
agentCount: 300,
totalTicks: 300,
archetypes,
});
const results = simulator.run();
console.log(results.agentSummary);
console.log(results.finalPrice, results.allTimeHigh, results.allTimeLow);Run the default market
import { TokenSimulator } from 'amm-strategy-backtester';
const simulator = new TokenSimulator({
agentCount: 500,
totalTicks: 52,
seed: 42,
});
console.log(simulator.run());Create a custom AMM pool
import { AMMPool } from 'amm-strategy-backtester';
const pool = new AMMPool({
tokenReserve: 100000,
baseReserve: 10000,
feeBps: 30,
});
const result = pool.buy(500);
console.log('tokens out:', result.tokensOut);
console.log('price after buy:', pool.getPrice());Create a custom vesting schedule
import { VestingSchedule } from 'amm-strategy-backtester';
const schedule = new VestingSchedule({
name: 'team',
totalAmount: 200000,
tgePercent: 0,
cliffMonths: 12,
vestingMonths: 24,
});
for (let month = 0; month <= 36; month += 6) {
console.log(`Month ${month}: unlocked ${schedule.unlockedAt(month)}`);
}Configuration
The simulator accepts a config object with these keys:
tokenName— token nametokenSymbol— token symboltotalSupply— total token supplyinitialBaseLiquidity— base asset liquidity for the AMMammFeeBps— fee in basis pointstotalTicks— number of ticks to simulateticksPerMonth— tick-to-month conversion for vestingagentCount— number of market agentsseed— RNG seed (reproducible backtests)archetypes— custom agent mix (your strategy + background traders)allocations— object of allocation buckets keyed by name
Each allocation bucket supports:
percentOfSupplytgePercentcliffMonthsvestingMonths
Each archetype entry supports:
type— label for results (agentSummary)weight— relative share of the populationbalanceRange—[min, max]starting base balancestrategy— decision function from a strategy factory
Built-in Strategies
All factories live on strategies and return (agent, context) => decision.
Background market (also used by defaultArchetypes()):
strategies.whale()— large early buys and profit-taking dumpsstrategies.bot()— sniping and quick flippingstrategies.retail()— momentum-driven retail trading
Technical / systematic (opt-in — mix them into archetypes):
strategies.rsi()— buy oversold, sell overboughtstrategies.dca()— buy a fixed slice of starting capital every N ticksstrategies.grid()— buy dips on a price grid, take profit one spacing upstrategies.movingAverageCrossover()— short/long MA cross with stop-loss / take-profitstrategies.marketMaker()— buy, sell after a small spread, repeatstrategies.volumeFollow()— trade in the direction of a volume spikestrategies.meanReversion()— buy below SMA − k·σ, sell above SMA + k·σstrategies.breakout()— buy a lookback high, sell a lookback low
defaultArchetypes() is still whale / bot / retail so existing seeded runs stay comparable. Plug a new factory in like this:
import { TokenSimulator, strategies, defaultArchetypes } from 'amm-strategy-backtester';
const archetypes = [
{
type: 'rsi',
weight: 25,
balanceRange: [500, 3000],
strategy: strategies.rsi({ period: 10, oversold: 35 }),
},
...defaultArchetypes(),
];
const results = new TokenSimulator({ seed: 42, agentCount: 200, archetypes }).run();
console.log(results.agentSummary);API Reference
TokenSimulator
new TokenSimulator(config)— create a market sandbox instancerunTick(tickIndex)— advance one tickrun(ticks)— run a multi-tick backtestgetResults()— price, volume, market cap, andagentSummaryby type
AMMPool
new AMMPool({ tokenReserve, baseReserve, feeBps })buy(baseAmountIn)— swap base currency for tokenssell(tokenAmountIn)— swap tokens for base currencygetPrice()— current spot pricegetMarketCap(circulatingSupply)— estimate market cap
VestingSchedule
new VestingSchedule({ name, totalAmount, tgePercent, cliffMonths, vestingMonths })unlockedAt(monthIndex)— unlocked amount at a monthunlockDelta(monthIndex)— newly unlocked amount in a month
Agent
new Agent({ id, type, baseBalance, tokenBalance, strategy })decide(context)— strategy decision for a tickrecordBuy(baseSpent, tokensReceived)— handle buy executionrecordSell(tokensSold, baseReceived)— handle sell executionnetWorth(currentPrice)— compute current agent net worth
Project Structure
amm-strategy-backtester/
├── Agent.js
├── AMMPool.js
├── MarketAgents.js # strategies object + population helpers
├── TokenSimulator.js # backtest orchestration
├── VestingSchedule.js
├── defaultConfig.js
├── index.js
├── package.json
├── README.md
├── rng.js
└── strategies/ # RSI, DCA, grid, MA, MM, volume-follow, mean reversion, breakout
├── index.js
├── indicators.js
├── Breakout.js
├── DCA.js
├── GridBot.js
├── MarketMaker.js
├── MeanReversion.js
├── MovingAverageCrossover.js
├── RSI.js
└── WhaleTrader.js # volumeFollow()Contributing
Contributions are welcome. Open issues, submit pull requests, add new trading strategies, or improve documentation.
License
ISC
