exchange.IO("lp.add", ...)
按建仓计划建立集中流动性仓位:缺哪种代币先用Jupiter换,每个仓位一笔链上交易。
exchange.IO(k, plan)示例
-
按SOL/USDC现价上下5%建仓。
javascriptfunction main() { var r = exchange.IO("lp.add", {symbol: "SOL_USDC", budget: 100, range: {pct: [-5, 5]}}) if (!r) { Log("建仓失败:", GetLastError()) return } if (r.swapTxHash) { Log("先换币:", r.swapTxHash) } r.positions.forEach(function(p) { Log(p.id, p.priceLower.toFixed(2), "-", p.priceUpper.toFixed(2), "存入", p.amountBase, "SOL", p.amountQuote, "USDC", "租金", p.rentSol, "SOL") }) } -
完整流程:选池 → 规划 → 模拟 → 建仓 → 监控与领取 → 撤出。
javascriptvar BUDGET = 100 // 预算(USDC) var MIN_FEES = 1 // 未领手续费超过1 USDC就领取 function main() { // 1. 列出池子并自动选一个 var pools = exchange.IO("lp.pools", "SOL", "USDC") if (!pools || pools.length == 0) { Log("没有池子:", GetLastError()) return } var pool = exchange.IO("lp.select", "SOL", BUDGET, {quote: "USDC"}) if (!pool) { Log("选池失败:", GetLastError()) return } Log("候选", pools.length, "个,选中", pool.dex, pool.id, pool.reason) // 2. 规划:现价上下5% var plan = exchange.IO("lp.plan", {symbol: "SOL_USDC", budget: BUDGET, pool: pool.id, range: {pct: [-5, 5]}, shape: "spot"}) if (!plan || plan.shortfall) { Log("规划失败或余额不足:", plan ? plan.shortfall : GetLastError()) return } Log("仓位数", plan.positions.length, "先换币", JSON.stringify(plan.swap), "租金", plan.rentSol) // 3. 模拟 var sim = exchange.IO("lp.simulate", plan) if (!sim || !sim.ok) { Log("模拟未通过:", sim ? JSON.stringify(sim.positions) : GetLastError()) return } // 4. 建仓 var r = exchange.IO("lp.add", plan) if (!r) { Log("建仓失败:", GetLastError()) return } var ids = r.positions.map(function(p) { return p.id }) Log("已建仓", ids.join(", "), "交易", r.txHashes.join(", ")) // 5. 监控:价格出区间就退出循环,手续费够多就领取 while (true) { Sleep(60 * 1000) var list = exchange.IO("lp.positions", "SOL_USDC") if (!list) { continue } var mine = list.filter(function(p) { return ids.indexOf(p.id) >= 0 }) var out = mine.some(function(p) { return !p.inRange }) var fees = mine.reduce(function(s, p) { return s + p.feesValue }, 0) LogStatus(_D(), "仓位", mine.length, "出区间", out, "未领手续费", fees.toFixed(4)) if (out) { break } if (fees > MIN_FEES) { var c = exchange.IO("lp.collect", ids) Log("领取手续费:", c ? c.txHashes.join(", ") : GetLastError()) } } // 6. 全部撤出、关仓退租金,并把拿回的SOL卖成USDC var rm = exchange.IO("lp.remove", ids, 1, true) if (!rm) { Log("撤出失败:", GetLastError()) return } rm.positions.forEach(function(p) { Log(p.id, "拿回", p.receivedBase, "SOL", p.receivedQuote, "USDC", "手续费", p.feesBase, p.feesQuote, "退租金", p.rentRefundSol) }) }
返回值
| 类型 | 描述 |
object / 空值 |
|
参数
| 名称 | 类型 | 必填 | 描述 |
k | string | 是 | 固定为 |
plan | object | 是 |
|
参考
Jupiter exchange.IO exchange.IO("lp.plan") exchange.IO("lp.simulate") exchange.IO("lp.positions") exchange.IO("lp.collect") exchange.IO("lp.remove") exchange.IO("lp.increase")
备注
执行步骤
- 按计划的
params在最新池价与钱包余额下重新规划;有shortfall时报错,不发任何交易。 - 计划要先换币时,用本交易所对象的Jupiter市价单换(滑点见
exchange.IO("slippage")),等确认。 - 换币后再重新规划一次;余额因滑点、手续费略少于计划量时,各仓位按持有量等比缩小。
- 每个仓位一笔交易:DLMM的仓位账户、Raydium与Orca的仓位NFT mint用新生成的密钥创建,并由它一起签名。
- 交易确认后,按交易前后的余额给出实际存入的
amountBase、amountQuote;rentSol为这笔交易押的租金(含新建BinArray、TickArray与仓位账户的),feeSol为网络费。
交易的执行
每笔交易发送前都用节点模拟一遍,模拟失败不发送并报出原因(DLMM、Raydium CLMM、Orca Whirlpool的错误码附名字);计算单元上限按模拟用量加余量,优先费按最近写这些账户的交易的费率取第75百分位,不超过exchange.IO("priorityFee")的上限。交易确认后才返回,实际数量按交易前后的余额计算。120秒内既没确认也证明不了过期时报outcome is unknown并附交易签名:交易可能已经上链,先用exchange.IO("receipt")核对再决定是否重试。
仓位ID与失败处理
- 仓位
id写成meteora:仓位账户地址、raydium:仓位NFT的mint或orca:仓位NFT的mint,传给lp.increase、lp.decrease、lp.collect、lp.remove;本进程建的仓位会自动记住,lp.positions一定查得到。 - 换币之后的步骤失败时,错误信息带换币交易签名(换到的币留在钱包);多个仓位中途失败时,错误信息列出已经建好的仓位。
风险
- 无常损失:价格偏离建仓价越远,仓位价值相对单纯持有越低。
- 价格出区间后仓位只剩一种代币,不再赚手续费。
- 新建BinArray、TickArray的租金不退;拆成多个仓位时每个都要押一份仓位租金。
- 每次建仓、调仓、领取、撤出都是链上交易,频繁调仓的成本可能超过手续费收入。