pump.fun打新策略
极高风险,仅供学习与改写:发现pump.fun上刚创建的币,按条件过滤后以固定金额在联合曲线上买入,触发止盈、止损或达到最长持有时间时全部卖出。
做法
使用exchange.IO("pump.*", ...)指令:
- 每
POLL_SEC秒调用pump.new,发现策略启动后新创建的币。 - 过滤:只买以SOL计价、且不属于mayhem或cashback类型的币;创建时间不超过
MAX_AGE_SEC秒;创建者在创建交易中的首笔买入不超过MAX_DEV_BUY_SOL;pump.info返回的曲线进度不超过MAX_PROGRESS%,且曲线尚未完成。 - 买入:
pump.buy(mint, BUY_SOL, {slippage, priorityFee})。资金护栏:同时最多持有MAX_POSITIONS个仓位,累计买入不超过MAX_TOTAL_SOL。 - 持仓:用
pump.quote按曲线估算全部卖出可得的SOL(已毕业的币改用Jupiter报价);盈利达到TAKE_PROFIT_PCT%、亏损达到STOP_LOSS_PCT%或持有超过MAX_HOLD_MIN分钟时,用pump.sell(mint, "all")全部卖出。 - 状态栏:显示发现与跳过的数量、累计买入、已平仓数与已实现盈亏,以及各持仓的成本、现值、盈亏与持有时间。
交易所对象
一个Jupiter交易所对象(使用专用钱包),钱包中只放少量SOL。公共节点限流且延迟高,抢新时请配置自己的付费节点。
参数
| 参数 | 默认值 | 说明 |
|---|---|---|
BUY_SOL | 0.01 | 每个币的买入金额(SOL,含协议费与创作者费) |
MAX_POSITIONS | 3 | 同时持有的最大币数 |
MAX_TOTAL_SOL | 0.1 | 累计买入上限(SOL) |
MAX_AGE_SEC | 30 | 只买创建时间不超过该秒数的币 |
MAX_DEV_BUY_SOL | 1 | 创建者首买超过该数量SOL时跳过 |
MAX_PROGRESS | 20 | 曲线进度超过该百分比时跳过 |
TAKE_PROFIT_PCT | 50 | 止盈(%) |
STOP_LOSS_PCT | 30 | 止损(%) |
MAX_HOLD_MIN | 30 | 最长持有时间(分钟) |
SLIPPAGE | 0.2 | 买卖滑点 |
PRIORITY_FEE | 0.0005 | 每笔交易的优先费(SOL) |
POLL_SEC | 5 | 轮询间隔(秒) |
DRY_RUN | true | 为true时只模拟,不发送交易 |
风险
- 绝大多数新币会很快归零,创作者或捆绑钱包随时可能砸盘(rug pull);名称和符号可以冒充知名项目,请以mint地址为准。
- 抢新机器人与MEV竞争激烈,滑点设置过大容易遭到三明治攻击(被夹),以最差价格成交;流动性枯竭时,止损可能无法按预期价格卖出。
- 每次买卖都需支付协议费、创作者费、网络费与优先费。
- 只投入可以承受全部亏损的小额资金。请先阅读
Jupiter交易所页面的「Pump.fun打新」一节。
DRY_RUN与实盘
DRY_RUN为true(默认)时不发送交易:买入使用pump.buy的dryRun选项模拟(钱包中没有SOL时模拟会报错,属正常现象),并按报价记录纸面持仓。由于模拟买入并未真实发生,曲线中并没有这些币,因此纸面持仓的现值按pump.info的现价 × 数量估算,并扣除协议费与创作者费(已毕业的币仍用pump.quote);止盈止损照常计算,卖出只记录日志。将DRY_RUN改为false即为实盘,建议先使用最小的BUY_SOL和MAX_TOTAL_SOL。
示例
完整策略代码(DRY_RUN = true 时仅模拟运行,不发送交易)。
javascript
/*
* pump.fun 打新(Solana,范例策略)—— 极高风险,仅供学习改写
*
* 用 Jupiter 交易所对象的 pump.* 指令:轮询 pump.new 发现刚在 pump.fun 上创建的币,按条件过滤后用固定
* 金额在联合曲线上买入(pump.buy),持仓期间用 pump.quote 按曲线估算全部卖出可得的 SOL,达到止盈、止损或
* 最长持有时间就全部卖出(pump.sell(mint, "all"))。
*
* 过滤条件:只买以 SOL 计价、不是 mayhem / cashback 的币;创建时间在 MAX_AGE_SEC 秒以内;创建者在创建交易里
* 的首买不超过 MAX_DEV_BUY_SOL;曲线进度不超过 MAX_PROGRESS%。资金护栏:同时最多 MAX_POSITIONS 个持仓,
* 累计买入不超过 MAX_TOTAL_SOL。
*
* DRY_RUN = true(默认)不发交易:买入用 pump.buy 的 dryRun 选项模拟(钱包没有 SOL 时模拟报错,属正常),
* 按 pump.quote 的报价记纸面持仓,止盈止损照常计算,卖出只记日志。
*
* 风险(请先读交易所文档「Pump.fun打新」):
* - 绝大多数新币很快归零,创作者或捆绑钱包随时砸盘(rug pull);名称、符号可以冒充知名项目,以 mint 为准;
* - 抢新机器人与 MEV 竞争激烈,滑点设得大会被夹,按最差价成交;止损在流动性枯竭时可能卖不出预期价格;
* - 协议费、创作者费、网络费与优先费每次买卖都要付;公共节点限流、延迟高,抢新请配置自己的付费节点。
* 只用可以全部亏掉的小钱。
*
* 用法:添加 Jupiter 交易所对象(专用钱包),钱包里放少量 SOL。
*/
// ---------------- 参数 ----------------
var BUY_SOL = 0.01 // 每个币买入的 SOL(含协议费与创作者费)
var MAX_POSITIONS = 3 // 同时最多持有几个币
var MAX_TOTAL_SOL = 0.1 // 累计买入上限(SOL)
var MAX_AGE_SEC = 30 // 只买创建不超过这么多秒的币
var MAX_DEV_BUY_SOL = 1 // 创建者首买超过这么多 SOL 的跳过
var MAX_PROGRESS = 20 // 曲线进度超过这么多 % 的跳过
var TAKE_PROFIT_PCT = 50 // 止盈(%)
var STOP_LOSS_PCT = 30 // 止损(%)
var MAX_HOLD_MIN = 30 // 最长持有(分钟)
var SLIPPAGE = 0.2 // 买卖滑点
var PRIORITY_FEE = 0.0005 // 每笔交易的优先费(SOL)
var POLL_SEC = 5 // 轮询间隔(秒)
var DRY_RUN = true // true 只模拟,不发交易
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)
}
// 过滤;通过返回 null,否则返回原因
function reject(c) {
if (c.quote !== "SOL" || c.mayhem || c.cashback) return "非 SOL 计价 / mayhem / cashback"
if (Date.now() - c.time > MAX_AGE_SEC * 1000) return "太旧"
if (c.devBuy && (c.devBuy.quoteAmount === null || c.devBuy.quoteAmount > MAX_DEV_BUY_SOL)) return "创建者首买过大"
var info = exchange.IO("pump.info", c.mint)
if (!info) return "pump.info 失败:" + GetLastError()
if (info.complete || info.progress > MAX_PROGRESS) return "曲线进度 " + 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("模拟买入出错", c.symbol, GetLastError()); return }
Log("DRY_RUN 买入", c.symbol, c.mint, BUY_SOL, "SOL → 约", fmt(sim.expected.tokenAmount, 0), sim.ok ? "(模拟通过)" : "(模拟未通过:" + 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("买入失败", c.symbol, GetLastError(), "#FF0000"); return }
Log("买入", 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。实盘用 pump.quote 按曲线精确报价;DRY_RUN 的买入没有真的发生,曲线里没有这些币,
// 按曲线报价卖不出去,所以按现价 × 数量 − 费率估算(已毕业的币仍用 pump.quote,它改走 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
}
// 检查一个持仓:返回状态栏的一行
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), "报价失败:" + GetLastError()]
var pct = (value / p.cost - 1) * 100
var age = (Date.now() - p.time) / 60000
var why = pct >= TAKE_PROFIT_PCT ? "止盈" : pct <= -STOP_LOSS_PCT ? "止损" : age >= MAX_HOLD_MIN ? "到时" : ""
if (why) {
Log(why, p.symbol, mint, "卖出约", 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("卖出失败", p.symbol, GetLastError(), "#FF0000"); return [p.symbol, mint, fmt(p.cost, 4), fmt(value, 4), fmt(pct, 1) + "%", fmt(age, 1), "卖出失败,下轮重试"] }
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 || "持有"]
}
function main() {
if (exchange.GetName() !== "Jupiter") throw new Error("请添加 Jupiter 交易所对象,当前是 " + 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(), "已发现", Object.keys(seen).length, "个新币,跳过", skipped, ",累计买入", fmt(S.spent, 4), "/", MAX_TOTAL_SOL, "SOL,已平仓",
S.closed, "个,已实现盈亏", fmt(S.realized, 4), "SOL", DRY_RUN ? "(DRY_RUN 纸面)" : "",
"\n`" + JSON.stringify({ type: "table", title: "持仓", cols: ["币", "mint", "成本 SOL", "现值 SOL", "盈亏", "持有分钟", "状态"], rows: rows }) + "`")
} catch (e) {
Log("本轮出错:", e.message || e, "#FF0000")
}
Sleep(POLL_SEC * 1000)
}
}参考