pump.fun Token Sniping Strategy
Extremely high risk, for learning and adaptation only: detects newly created tokens on pump.fun, filters them by set conditions, buys a fixed amount on the bonding curve, and sells the entire position when take-profit, stop-loss or the maximum holding time is reached.
How It Works
Uses the exchange.IO("pump.*", ...) commands:
- Every
POLL_SECseconds, callspump.newto find tokens created after the strategy started. - Filtering: buys only tokens quoted in SOL that are not of the mayhem or cashback type; the token must be no older than
MAX_AGE_SECseconds; the creator's first buy in the creation transaction must not exceedMAX_DEV_BUY_SOL; the curve progress returned bypump.infomust not exceedMAX_PROGRESS%, and the curve must not yet be complete. - Buying:
pump.buy(mint, BUY_SOL, {slippage, priorityFee}). Capital guardrails: at mostMAX_POSITIONSpositions held at the same time, and total purchases capped atMAX_TOTAL_SOL. - Holding: uses
pump.quoteto estimate from the curve how much SOL a full sell would return (graduated tokens use a Jupiter quote instead); when profit reachesTAKE_PROFIT_PCT%, loss reachesSTOP_LOSS_PCT%, or the position has been held longer thanMAX_HOLD_MINminutes, sells the entire position withpump.sell(mint, "all"). - Status bar: shows the number of tokens found and skipped, total purchases, the number of closed positions and realized PnL, plus the cost, current value, PnL and holding time of each open position.
Exchange Object
One Jupiter exchange object (using a dedicated wallet), with only a small amount of SOL in the wallet. Public nodes are rate-limited and have high latency, so configure your own paid node for sniping.
Parameters
| Parameter | Default | Description |
|---|---|---|
BUY_SOL | 0.01 | Buy amount per token (SOL, including protocol and creator fees) |
MAX_POSITIONS | 3 | Maximum number of tokens held at the same time |
MAX_TOTAL_SOL | 0.1 | Cap on total purchases (SOL) |
MAX_AGE_SEC | 30 | Only buy tokens created no more than this many seconds ago |
MAX_DEV_BUY_SOL | 1 | Skip when the creator's first buy exceeds this amount of SOL |
MAX_PROGRESS | 20 | Skip when curve progress exceeds this percentage |
TAKE_PROFIT_PCT | 50 | Take-profit (%) |
STOP_LOSS_PCT | 30 | Stop-loss (%) |
MAX_HOLD_MIN | 30 | Maximum holding time (minutes) |
SLIPPAGE | 0.2 | Slippage for buys and sells |
PRIORITY_FEE | 0.0005 | Priority fee per transaction (SOL) |
POLL_SEC | 5 | Polling interval (seconds) |
DRY_RUN | true | When true, only simulates and does not send transactions |
Risks
- The vast majority of new tokens quickly go to zero, and the creator or bundled wallets can dump at any time (rug pull); names and symbols can impersonate well-known projects, so always rely on the mint address.
- Competition from sniper bots and MEV is fierce; setting slippage too high makes you an easy target for sandwich attacks, filling at the worst price; when liquidity dries up, the stop-loss may not sell at the expected price.
- Every buy and sell incurs protocol fees, creator fees, network fees and priority fees.
- Only commit a small amount of capital you can afford to lose entirely. Please first read the "Pump.fun Sniping" section of the
Jupiterexchange page.
DRY_RUN vs. Live Trading
When DRY_RUN is true (the default), no transactions are sent: buys are simulated with the dryRun option of pump.buy (the simulation returns an error when the wallet holds no SOL, which is expected), and paper positions are recorded at the quoted amounts. Because the simulated buys never actually happened, the curve does not hold these tokens, so the current value of paper positions is estimated as the current price from pump.info × quantity, minus protocol and creator fees (graduated tokens still use pump.quote); take-profit and stop-loss are calculated as usual, and sells are only logged. Set DRY_RUN to false for live trading; it is recommended to start with the smallest BUY_SOL and MAX_TOTAL_SOL.
Examples
Complete strategy code (when DRY_RUN = true, it only runs in simulation and sends no transactions).
javascript
/*
* pump.fun sniping (Solana, example strategy) - extremely high risk, for study and adaptation only
*
* Uses the pump.* commands of the Jupiter exchange object: polls pump.new for coins just created on pump.fun,
* filters them, buys a fixed amount on the bonding curve (pump.buy), and while holding uses pump.quote to
* estimate the SOL that selling everything along the curve would return; on take-profit, stop-loss or the
* maximum holding time it sells everything (pump.sell(mint, "all")).
*
* Filters: only coins quoted in SOL that are neither mayhem nor cashback; created within MAX_AGE_SEC seconds;
* the creator's first buy in the creation transaction at most MAX_DEV_BUY_SOL; curve progress at most
* MAX_PROGRESS%. Money guards: at most MAX_POSITIONS positions at a time, total buys at most MAX_TOTAL_SOL.
*
* DRY_RUN = true (the default) sends no transactions: buys are simulated with the dryRun option of pump.buy
* (the simulation fails when the wallet has no SOL, which is expected), paper positions are booked at the
* quoted amounts, take-profit and stop-loss are evaluated as usual, and sells are only logged.
*
* Risks (read "Pump.fun sniping" on the exchange page first):
* - Most new coins go to zero quickly, and the creator or bundled wallets can dump at any time (rug pull);
* names and symbols can impersonate well-known projects, so go by the mint;
* - sniper bots and MEV compete hard, a wide slippage gets you sandwiched and filled at the worst price, and
* a stop-loss may not sell at the expected price once liquidity dries up;
* - protocol, creator, network and priority fees are paid on every buy and sell; public nodes are rate-limited
* and slow, so configure your own paid node for sniping.
* Only use small money you can afford to lose entirely.
*
* Usage: add a Jupiter exchange object (dedicated wallet) holding a little SOL.
*/
// ---------------- Parameters ----------------
var BUY_SOL = 0.01 // SOL spent per coin (including protocol and creator fees)
var MAX_POSITIONS = 3 // at most this many coins held at a time
var MAX_TOTAL_SOL = 0.1 // cap on total buys (SOL)
var MAX_AGE_SEC = 30 // only buy coins created at most this many seconds ago
var MAX_DEV_BUY_SOL = 1 // skip coins whose creator's first buy exceeds this many SOL
var MAX_PROGRESS = 20 // skip coins whose curve progress exceeds this %
var TAKE_PROFIT_PCT = 50 // take profit (%)
var STOP_LOSS_PCT = 30 // stop loss (%)
var MAX_HOLD_MIN = 30 // longest holding time (minutes)
var SLIPPAGE = 0.2 // slippage for buys and sells
var PRIORITY_FEE = 0.0005 // priority fee per transaction (SOL)
var POLL_SEC = 5 // polling interval (seconds)
var DRY_RUN = true // true: simulate only, send no transactions
var KEY = DRY_RUN ? "pump_sniper_paper" : "pump_sniper"
var S = null // {positions: {mint: {symbol, tokens, cost, time}}, spent, closed, realized}
var seen = {}
var skipped = 0
function fmt(x, d) {
return x === null || x === undefined || isNaN(x) ? "-" : Number(x).toFixed(d === undefined ? 2 : d)
}
// Filter: null when the coin passes, otherwise the reason
function reject(c) {
if (c.quote !== "SOL" || c.mayhem || c.cashback) return "not SOL-quoted / mayhem / cashback"
if (Date.now() - c.time > MAX_AGE_SEC * 1000) return "too old"
if (c.devBuy && (c.devBuy.quoteAmount === null || c.devBuy.quoteAmount > MAX_DEV_BUY_SOL)) return "creator's first buy too large"
var info = exchange.IO("pump.info", c.mint)
if (!info) return "pump.info failed: " + GetLastError()
if (info.complete || info.progress > MAX_PROGRESS) return "curve progress " + fmt(info.progress, 1) + "%"
return null
}
function buy(c) {
var opts = { slippage: SLIPPAGE, priorityFee: PRIORITY_FEE }
if (DRY_RUN) {
opts.dryRun = true
var sim = exchange.IO("pump.buy", c.mint, BUY_SOL, opts)
if (!sim) { Log("Simulated buy failed", c.symbol, GetLastError()); return }
Log("DRY_RUN buy", c.symbol, c.mint, BUY_SOL, "SOL → about", fmt(sim.expected.tokenAmount, 0), sim.ok ? "(simulation passed)" : "(simulation failed: " + sim.simulationError + ")")
S.positions[c.mint] = { symbol: c.symbol, tokens: sim.expected.tokenAmount, cost: BUY_SOL, time: Date.now() }
} else {
var r = exchange.IO("pump.buy", c.mint, BUY_SOL, opts)
if (!r) { Log("Buy failed", c.symbol, GetLastError(), "#FF0000"); return }
Log("Bought", c.symbol, c.mint, r.solAmount, "SOL →", r.tokenAmount, r.txHash, "#00AA00")
S.positions[c.mint] = { symbol: c.symbol, tokens: r.tokenAmount, cost: r.solAmount, time: Date.now() }
}
S.spent += BUY_SOL
}
// SOL that selling everything would return. Live: pump.quote quotes exactly along the curve. In DRY_RUN the buy
// never happened and the curve does not hold these coins, so a curve quote cannot sell them; estimate price ×
// amount − fees instead (graduated coins still use pump.quote, which then goes through Jupiter)
function sellValue(mint, p) {
if (DRY_RUN) {
var info = exchange.IO("pump.info", mint)
if (info && info.price !== null) return info.price * p.tokens * (1 - (info.fees.protocolBps + info.fees.creatorBps) / 10000)
}
var q = exchange.IO("pump.quote", mint, "sell", p.tokens)
return q ? q.amountOut : null
}
// Check one position: returns a row of the status table
function manage(mint) {
var p = S.positions[mint]
var value = sellValue(mint, p)
if (value === null) return [p.symbol, mint, fmt(p.cost, 4), "-", "-", fmt((Date.now() - p.time) / 60000, 1), "quote failed: " + GetLastError()]
var pct = (value / p.cost - 1) * 100
var age = (Date.now() - p.time) / 60000
var why = pct >= TAKE_PROFIT_PCT ? "take profit" : pct <= -STOP_LOSS_PCT ? "stop loss" : age >= MAX_HOLD_MIN ? "time out" : ""
if (why) {
Log(why, p.symbol, mint, "selling for about", fmt(value, 4), "SOL", fmt(pct, 1) + "%", "#FF9900")
var out = value
if (!DRY_RUN) {
var r = exchange.IO("pump.sell", mint, "all", { slippage: SLIPPAGE, priorityFee: PRIORITY_FEE })
if (!r) { Log("Sell failed", p.symbol, GetLastError(), "#FF0000"); return [p.symbol, mint, fmt(p.cost, 4), fmt(value, 4), fmt(pct, 1) + "%", fmt(age, 1), "sell failed, retrying next round"] }
out = r.solAmount
}
S.realized += out - p.cost
S.closed++
delete S.positions[mint]
}
return [p.symbol, mint, fmt(p.cost, 4), fmt(value, 4), fmt(pct, 1) + "%", fmt(age, 1), why || "holding"]
}
function main() {
if (exchange.GetName() !== "Jupiter") throw new Error("Add a Jupiter exchange object; this one is " + exchange.GetName())
S = _G(KEY) || { positions: {}, spent: 0, closed: 0, realized: 0 }
var start = Date.now()
while (true) {
try {
var coins = exchange.IO("pump.new", { limit: 10, since: start })
if (!coins) throw new Error("pump.new failed: " + GetLastError())
coins.forEach(function (c) {
if (seen[c.mint]) return
seen[c.mint] = true
if (Object.keys(S.positions).length >= MAX_POSITIONS || S.spent + BUY_SOL > MAX_TOTAL_SOL) { skipped++; return }
var why = reject(c)
if (why) { skipped++; return }
buy(c)
})
var rows = Object.keys(S.positions).map(manage)
_G(KEY, S)
LogStatus(_D(), "Found", Object.keys(seen).length, "new coins, skipped", skipped, ", bought", fmt(S.spent, 4), "/", MAX_TOTAL_SOL, "SOL in total, closed",
S.closed, ", realized P&L", fmt(S.realized, 4), "SOL", DRY_RUN ? "(DRY_RUN paper)" : "",
"\n`" + JSON.stringify({ type: "table", title: "Positions", cols: ["Coin", "mint", "Cost SOL", "Value SOL", "P&L", "Minutes held", "State"], rows: rows }) + "`")
} catch (e) {
Log("Error in this round:", e.message || e, "#FF0000")
}
Sleep(POLL_SEC * 1000)
}
}See Also