pump dataresearch · guides · the daily wire · stats · docs

How to backtest a pump.fun strategy

· · Prices and limits as of 2026-10-20.

How do I backtest a pump.fun trading strategy?

Turn the rule into a list of picks: which coin, when the rule decided, and how much SOL. Send the list to pump data's POST /replay. It fills each pick at the coin's pool as it stood after your latency, charges the fees pump.fun charged then, counts every pick including the coins that died, and returns what the rule made, pick by pick and week by week. A run of up to 500 picks costs $2 in USDC over x402, with no account.

What a replay charges for

A backtest is only as honest as its fills. These are the costs every /replay result includes, and the result's execution_model lists them:

CostHow /replay charges it
LatencyThe fill is at the pool as it stood latency_s seconds after the decision (3 by default), against its depth.
FeesWhat pump.fun charged at that pool, market-cap tier and date: 1.25% falling to 0.30% as market cap rises.
Fixed costs145,000 lamports a side, entry and exit, for the tip and the network.
Failed triesA fill rate by the minute from the pool's own history; the share that does not land pays the tip and earns nothing.
Bankrollbankroll_sol caps the SOL at risk at once. A pick with no room is skipped and earns nothing.
Dead coinsEvery pick stays in the sum. A coin that never trades again is valued at its last state.
OverfittingThe latest eight weeks come back apart from the rest, once per rule. Read them once, after the rule is fixed.

1. Write the picks

Each pick is a mint, the time your rule decided (at, ISO 8601) and a size in SOL. List every coin the rule would have bought, decided only from what was known at that time. Add the latency, the bankroll and an exit: take (a multiple of the entry), trail (a share off the high), hold_s (seconds), in any combination.

{
  "picks": [
    { "mint": "<a graduate's mint>", "at": "2026-10-14T16:02:11Z", "size_sol": 0.5 },
    { "mint": "<another mint>",      "at": "2026-10-15T03:40:57Z", "size_sol": 0.5 }
  ],
  "latency_s": 3,
  "bankroll_sol": 5,
  "exit": { "take": 2, "trail": 0.3, "hold_s": 86400 }
}

Today /replay fills coins that reached their PumpSwap pool (graduates). Picks on the bonding curve, or before the pool's first trade, come back in skipped_not_covered with their reason, until curve fills ship. Up to 500 picks a run: split a longer list.

2. Pay and send it

Without payment the call answers 402 with its price. Pay it over x402 (a USDC transfer on Solana that the server's facilitator lands; your wallet needs USDC, not SOL) and send the same request again. The answer is 202 with a job id, which is your receipt.

curl -si -X POST https://pumpdata.fun/replay \
  -H 'content-type: application/json' -d @picks.json | head -20
# HTTP/2 402, the price in the PAYMENT-REQUIRED header ($2 in USDC on Solana)

In TypeScript, with the x402 Foundation's client, capped at a run's price:

// npm i @x402/core @x402/svm @solana/kit
import { createKeyPairSignerFromBytes } from "@solana/kit";
import { x402Client, x402HTTPClient } from "@x402/core/client";
import { decodePaymentRequiredHeader } from "@x402/core/http";
import { ExactSvmScheme } from "@x402/svm/exact/client";
import { readFileSync } from "node:fs";

const SOLANA = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
const signer = await createKeyPairSignerFromBytes(Uint8Array.from(JSON.parse(readFileSync(process.env.KEYPAIR!, "utf8"))));
const x402 = new x402Client();
x402.register(SOLANA, new ExactSvmScheme(signer));
x402.setSpendControls({ maxAmountPerPayment: "$2" }); // refuse any offer above a run's price
const http = new x402HTTPClient(x402);

const body = readFileSync("picks.json", "utf8");
const post = (headers = {}) => fetch("https://pumpdata.fun/replay", {
  method: "POST", headers: { "content-type": "application/json", ...headers }, body });

let res = await post();
if (res.status === 402) {
  const offer = decodePaymentRequiredHeader(res.headers.get("payment-required")!);
  res = await post(http.encodePaymentSignatureHeader(await http.createPaymentPayload(offer)));
}
const { id } = await res.json(); // 202: the job id is the receipt; keep it

for (;;) { // GET /replay/{id} is free
  const job = await (await fetch(`https://pumpdata.fun/replay/${id}`)).json();
  if (job.state === "done") { console.log(JSON.stringify(job.result, null, 2)); break; }
  if (job.state === "failed") throw new Error(job.error);
  await new Promise((r) => setTimeout(r, 15_000));
}

In Python, the payment is built by hand in the Chainstack pump.fun bot's pumpdata recipe; elizaOS agents have elizaos-plugin-pumpdata. MCP clients can call the replay tool at https://pumpdata.fun/mcp.

3. Read the result

GET /replay/{id} is free. Poll it until state is done; a run that fails is rerun free on the same id. The result holds:

FieldWhat it says
sum_solWhat the picks made in SOL, net of every fee and fixed cost, outside the held-out weeks.
picks, filledPicks sent, and picks filled.
skipped_no_room, skipped_not_coveredPicks the bankroll had no room for, and picks the replay cannot fill yet; by_pick[].skipped gives each one's reason.
by_pickEach pick's fill price, fill rate, the fee on entry and exit, its exit time and its return.
by_weekSOL made in each ISO week, so you can see how many weeks were up.
held_out, held_out_readsThe latest eight weeks on their own, on your first read of this rule; after that they are withheld, and the count of reads says so.
execution_modelThe latency, fees, fixed costs, fill rates, bankroll and exits it charged.

A prompt for agents

Give this to a coding or trading agent with a funded wallet. It keeps the agent to the two rules that make a backtest worth reading: no information from after the decision, and one look at the held-out weeks.

You are backtesting a pump.fun trading rule with pumpdata.fun's POST /replay.
The docs are at https://pumpdata.fun/llms.txt and https://pumpdata.fun/openapi.json; read them first.

The rule: <describe it exactly: which coins, the moment it decides, the size, the exit>.

1. Fix the rule before you look at any result. Write it down with every threshold. Do not change it after
   seeing a replay, except as a new, named version.
2. Build the picks: every coin the rule would have bought in the window, not a chosen few. Each pick is
   {mint, at, size_sol}, where "at" is when the rule could have decided, using only what was known then.
   No information from after "at" may decide a pick (no peaks, no closes, no later holders).
3. /replay fills only coins that reached their PumpSwap pool (graduates) today. Keep curve picks in the list
   anyway: they come back in skipped_not_covered, and the share skipped is part of the answer.
4. Set latency_s (3 unless the rule is faster), bankroll_sol (what is at risk at once) and exit
   ({take, trail, hold_s}, any combination), as the rule says.
5. Send at most 500 picks a run. A run costs $2 in USDC over x402; refuse any offer above $2, and stop at
   <budget> in total. Keep each run's id: GET /replay/{id} is free; poll it every 15 to 30 seconds.
6. Report, for each run: sum_sol; picks, filled, skipped_no_room and skipped_not_covered (with the reasons
   from by_pick[].skipped); by_week (how many weeks were up); the held-out weeks' result on its own line;
   and the execution_model it charged. Say plainly when filled picks are few.
7. The held-out weeks (the latest eight) come back once per rule. Treat that first read as the
   out-of-sample test. If held_out_reads is more than 1, say the held-out result is no longer clean.
8. Do not call a rule profitable unless sum_sol is positive over the rest and over the held-out weeks.
   Never quote a return before fees; /replay's numbers are already net of every fee and fixed cost.

Before you trust a result

Look at how many picks filled, not only sum_sol: a sum over a dozen fills is a story about a dozen coins. Look at by_week: a rule that made its whole sum in two weeks is a rule that caught two weeks. And read the held-out weeks once, after the rule is written down. If they lose while the rest wins, the rule fits the past.

Questions

How much does a pump.fun backtest cost on pump data?

$2 a run of up to 500 picks, paid in USDC on Solana over x402, with no account. Fetching the result (GET /replay/{id}) is free, and a run that fails is rerun free on the same id. Split a longer list into runs of 500.

Are dead coins counted?

Yes. Every pick stays in the sum. A coin that never trades again is valued at its last state, so a rule that buys coins which then die shows the loss. A backtest over coins that survived would flatter every rule.

Which picks can /replay fill today?

Picks on coins that reached their PumpSwap pool (graduates), filled at the pool after your latency. Picks on the bonding curve, before the pool's first trade, or outside the archive are counted in skipped_not_covered, each with its reason, until curve fills ship.

What are the held-out weeks?

The most recent eight weeks come back apart from the rest, the first time you send a rule. Send the same picks and settings again and those weeks are withheld, and held_out_reads says how many times you have read them. Read them once, after the rule is fixed: more than one read is tuning on them.

Which fees does a replay charge?

The fee pump.fun charged at that pool, market-cap tier and date (1.25% falling to 0.30% as market cap rises), 145,000 lamports a side for the tip and the network, and the tip lost on a try that did not land, at a fill rate taken from the pool's own history by the minute. The result's execution_model lists what it charged.

The calls and their prices: llms.txt and openapi.json. Questions a pick list can start from (a coin's launch, its dev, its holders, the base rate from its market cap): the pump.fun API.