BSC On-Chain 1inch Limit Orders + Binance Perpetual Hedge Arbitrage
BSC 链上 1inch 挂单 + 币安永续对冲套利策略
风险提示:本策略面向研究和小额验证,不承诺收益。链上挂单存在逆向选择风险:买单有效期内币安价格急跌时,挂单可能被知情者按旧价格成交。链上成交到币安对冲之间有短暂的单边敞口。价差可能长时间不回归,最终按不利价差强制平仓。策略涉及钱包私钥、代币授权和第三方合约(1inch、PancakeSwap),存在合约与操作风险。运行期间钱包里代币的任何变动都会被当作挂单成交去对冲,请勿手动操作该钱包。正式运行前应先使用模拟模式(DRY_RUN)积累记录。
策略思想
这是一套运行在发明者量化平台上的链上与交易所价差套利策略,链上部分在 BSC(PancakeSwap 池子 + 1inch 限价单协议),交易所部分在币安 USDT 本位永续。
BSC 上有一类卖家会反复按固定金额出货,每次把池子价格砸下去几十个 bp,随后下一个区块开头就会有回补机器人低价买走、再到币安卖出对冲。这块利润被几十个机器人用区块位置竞价瓜分,普通策略拼速度抢不到。
本策略换了一个位置:不去抢价差,而是提前在链上挂好比币安便宜的买单。1inch 限价单是“链下签名、链上成交”,挂单不上链、不花 Gas。有人砸盘把池子价格压到挂单价以下时,回补机器人可以在同一笔交易里从池子低价买币、再卖给我们的挂单,赚中间差价;策略只需要等着被成交,接到币后立刻在币安开空锁住价差。之后等链上价格相对币安回归,在 PancakeSwap 卖出、同时在币安平空。
text
扫描全部池子链上成交 → 统计“定额砸盘”的币,选前 N 个
↓
拉币安全市场盘口
↓
核对账本(链上真实余额 / 币安真实空单)
↓
检测买单成交 → 币安市价开空
↓
风控:单腿 / 单币亏损 / 总亏损
↓
链上询价:价差回归或持有超时 → PancakeSwap 卖出 + 币安平空
↓
维护买单:过期或币安价格漂移超过阈值时重挂
选币
策略从 PancakeSwap 代币列表中找出在币安也有 USDT 永续的币(排除稳定币和 WBNB),用 Multicall 一次查出它们在 PancakeSwap V2/V3 上对 USDT、WBNB 的全部池子,约 200 个币、1400 个池子。
启动时用 eth_getLogs 回溯最近 WINDOW_H 小时的 Swap 事件,之后每 SCAN_SEC 秒增量拉取新区块。逐笔计算成交前后的池子价格:单笔把价格推动 MIN_BP 以上、金额 MIN_USD 以上,记为一次冲击,向下为砸盘、向上为拉盘。
同一个币的砸盘再按金额分档,相差约 10% 以内算同一档。档内次数越多,越像是有人在按固定金额出货:
javascript
s.dumps.forEach(function (u) { var b = Math.round(Math.log(u) / Math.log(1.1)); (bucket[b] = bucket[b] || []).push(u) })
砸盘总次数不少于 MIN_DUMPS 的币,按“定额砸盘次数”、再按“砸盘总次数”排序,取前 TOP_N 个挂单。掉出名单的币不再挂新买单,已有持仓照常平仓。也可以关闭自动选币,只做 SYMBOLS 指定的币。
开仓
挂单价格 = 币安买一 × (1 − BUY_BPS),有效期 EXPIRE_SEC 秒,每张金额 USDT_AMT。订单按 1inch Limit Order Protocol v4 的 EIP-712 结构签名,允许部分成交,过期时间编码在 makerTraits 里,签名后提交到 1inch 挂单平台。
上一张买单过期,或币安买一相对挂单时变动超过 DRIFT_BPS,就按新价再挂一张。旧单不撤(链上撤单要花 Gas),到期自动作废,因此同一时间可能有多张买单有效。挂单总量受以下限制:
- 单币:持仓 + 所有未过期买单 ≤
MAX_INV; - 全部币:持仓 + 所有未过期买单 ≤
MAX_TOTAL; - 同时持有的币数 ≤
MAX_COINS。
挂单前做一次流动性试算:假设这一单成交,把持币在链上立刻卖出,价格比币安卖一低超过 EXIT_BPS 就说明池子太浅,这个币暂不挂单。币安最近 60 秒波动超过 VOL_BPS、处于冷却期、手动暂停或总亏损停机时,也不挂买单。
启动时策略会签一张测试单,用 1inch 合约的 hashOrder 核对订单指纹,再用 ecrecover 预编译合约核对签名人,两项都与钱包一致才继续运行。
成交与对冲
实盘每轮读取钱包中该币余额,余额增加即视为买单成交,立即在币安按成交数量市价开空。不足币安最小下单量的零头先累积,凑够后一起对冲。对冲下单失败时进入单腿处理。
平仓
策略不挂固定价格的卖单。每轮用 PancakeSwap Quoter(V3)或 Router(V2)在该币所有 USDT 池中询价,取“现在把持仓全部卖掉”报价最高的池子,与币安卖一比较:
javascript
var entryBp = (inv.shortCost / inv.short / (inv.cost / inv.qty) - 1) * 1e4 // 开仓时锁定的价差
var exitBp = (best.px / t.ask - 1) * 1e4 // 现在链上卖价相对币安卖一
var netBp = entryBp + exitBp - 2 * TAKER_FEE * 1e4
扣费后价差 ≥ TP_BPS 时,通过 PancakeSwap SmartRouter 卖出(允许 0.3% 滑点),并按链上实际收到的 USDT 计算成交价,同时在币安平空。持有超过 MAX_HOLD_H 小时价差仍未回归,则按当时价差强制平仓。
风控
| 风控项 | 处理方式 |
|---|---|
| 总仓位 | 全部币持仓 + 未过期买单 ≤ MAX_TOTAL,持币数 ≤ MAX_COINS |
| 行情波动 | 币安 60 秒波动超过 VOL_BPS,暂停该币挂买单 |
| 池子太浅 | 试卖价格比币安卖一低超过 EXIT_BPS,不挂买单 |
| 单腿 | 链上与币安数量对不上时,每轮补对冲;超过 LEG_TIMEOUT_SEC 仍补不上,平掉多出来的那条腿并冷却该币 |
| 账本核对 | 实盘每 60 秒用链上真实余额和币安真实空单校正账本,以实际为准 |
| 被扫旧单 | 买单成交时锁定价差比 −BAD_FILL_BPS 还差,冷却该币 |
| 单币亏损 | 单币已实现 + 浮动亏损达到 COIN_MAX_LOSS,强制平仓并冷却 |
| 总亏损 | 策略总亏损达到 MAX_LOSS_U,停止所有新开仓,只平不开;点“恢复挂单”解除 |
冷却期 COOLDOWN_MIN 分钟内不挂买单,平仓照常。链上持仓按“链上现在真实能卖出的价格”估值,不用币安价格代替,因此两边价差拉大时会直接体现为浮亏。
状态栏显示账户总览、逐腿持仓明细、挂单与平仓条件、单币风控、运行状态、砸盘排行和最近成交记录,并提供“暂停挂单 / 恢复挂单”按钮。
主要参数
| 参数 | 默认值 | 说明 |
|---|---|---|
| ONEINCH_KEY | — | 1inch Developer Portal API Key(加密字符串),实盘必填 |
| DRY_RUN | true | 模拟模式:流程与实盘一致,不授权、不提交挂单、不在币安下单 |
| AUTO | true | 自动选币;false 时只做 SYMBOLS |
| SYMBOLS | SOON,XPL,ZEC | 手动模式的币种,逗号分隔 |
| TOP_N | 5 | 自动选币同时做几个币 |
| WINDOW_H | 3 | 统计最近几小时的砸盘 |
| MIN_BP / MIN_USD | 40 / 100 | 单笔至少推动多少 bp、至少多少 U 才计为一次冲击 |
| MIN_DUMPS | 5 | 窗口内至少砸盘几次才入选 |
| SCAN_SEC | 20 | 拉取新链上记录的间隔(秒) |
| USDT_AMT | 20 | 每张买单金额(U) |
| BUY_BPS | 40 | 买单比币安买一低多少 bp |
| EXPIRE_SEC | 15 | 买单有效期(秒) |
| DRIFT_BPS | 10 | 币安价格变动超过多少 bp 重挂 |
| MAX_INV | 60 | 单币持仓 + 未过期买单上限(U) |
| TP_BPS | 5 | 平仓目标:扣费后价差 bp |
| MAX_HOLD_H | 72 | 最长持有小时数 |
| LEVERAGE | 3 | 币安合约杠杆 |
| MAX_TOTAL | 200 | 全部币持仓 + 未过期买单上限(U) |
| MAX_COINS | 3 | 最多同时持有几个币 |
| VOL_BPS | 50 | 币安 60 秒波动阈值(bp) |
| EXIT_BPS | 30 | 开仓前链上试卖滑点阈值(bp) |
| LEG_TIMEOUT_SEC | 30 | 单腿超时(秒) |
| COIN_MAX_LOSS | 2 | 单币亏损上限(U) |
| BAD_FILL_BPS | 30 | 被扫旧单判定阈值(bp) |
| COOLDOWN_MIN | 60 | 冷却分钟数 |
| MAX_LOSS_U | 10 | 策略总亏损上限(U) |
交易所对象需要按顺序添加:第一个是币安 USDT 本位永续 Futures_Binance,第二个是 Web3,链选 BSC。节点建议使用支持 5000 块区间 eth_getLogs 的节点,例如 https://rpc.48.club。钱包需要放入 USDT 和少量 BNB 作 Gas,签名使用 FMZ 自带的 IO("sign")。实盘首次运行会把 USDT 授权给 1inch Router,入选币授权给 1inch Router 和 PancakeSwap SmartRouter。
模拟与实盘使用不同的状态键,互不干扰。模拟成交的判定为:挂单有效期内,链上某笔成交价格穿过挂单价,且差额足以覆盖池子手续费,就视为整张成交,按成交时刻的币安买一/卖一记对冲。模拟不考虑 1inch 机器人是否真的会来成交,也不考虑与其他挂单的竞争,因此结果会比实盘乐观。
验证情况
选币扫描已用真实链上数据跑通:全市场约 200 个币、1400 个池子的 Swap 事件拉取、价格计算和定额砸盘排序可以正常工作。启动自检(订单指纹与签名核对)已通过。
截至本文撰写时,策略处于模拟模式运行阶段,尚未提交真实挂单,也没有在实盘中验证 1inch 平台接受挂单、机器人成交、币安对冲和链上平仓的完整流程。
上线前建议依次完成:
- 在模拟模式下运行,检查选币结果、挂单价格,以及模拟成交与平仓价差的分布;
- 挂一张远离市价的买单,确认 1inch 平台接受并能在订单簿中查到;
- 以 20U 一张的小额实盘,核对实际成交率、对冲延迟、被扫旧单的比例和平仓滑点;
- 验证单腿处理、账本核对和重启恢复;
- 根据实盘结果调整
BUY_BPS、EXPIRE_SEC、TP_BPS等参数后,再逐步增加资金。
||
BSC On-Chain 1inch Limit Orders + Binance Perpetual Hedge Arbitrage
Risk warning: This strategy is intended for research and small-scale validation and does not guarantee profits. On-chain resting orders carry adverse-selection risk: if the Binance price drops sharply while a buy order is live, an informed trader may fill it at the stale price. There is a brief unhedged window between the on-chain fill and the Binance hedge. The spread may not revert for a long time, in which case the position is force-closed at an unfavourable spread. The strategy relies on a wallet private key, token approvals, and third-party contracts (1inch, PancakeSwap), which carry contract and operational risk. Any change in the wallet's token balance is treated as an order fill and hedged, so do not touch the wallet manually while the strategy is running. Run it in simulation mode (DRY_RUN) first to build a track record.
Strategy Concept
This is an arbitrage strategy between an on-chain market and an exchange on the FMZ platform. The on-chain side runs on BSC (PancakeSwap pools + the 1inch Limit Order Protocol), and the exchange side runs on Binance USDT-margined perpetuals.
On BSC, some sellers repeatedly unload fixed amounts, each time pushing the pool price down by tens of bp. At the start of the next block, backrun bots buy the cheap tokens and hedge by selling on Binance. Dozens of bots compete for this profit by bidding for block position, and ordinary strategies cannot win on speed.
This strategy takes the other side. Instead of chasing the spread, it rests buy orders on-chain below the Binance price in advance. 1inch limit orders are signed off-chain and filled on-chain, so posting an order costs no gas. When someone dumps and pushes the pool price below the order price, a backrun bot can, in one transaction, buy cheaply from the pool and sell to our order, keeping the difference. The strategy simply waits to be filled, then immediately opens a Binance short to lock in the spread. When the on-chain price reverts relative to Binance, it sells on PancakeSwap and closes the Binance short at the same time.
text
Scan on-chain trades across all pools → rank tokens by "fixed-size dumps", pick top N
↓
Fetch Binance order books for all markets
↓
Reconcile the ledger (real on-chain balances / real Binance shorts)
↓
Detect buy-order fills → market-sell short on Binance
↓
Risk checks: legging / per-coin loss / total loss
↓
On-chain quote: spread reverted or holding timed out → sell on PancakeSwap + close Binance short
↓
Maintain buy orders: re-post when expired or when Binance drifts past the threshold
Token Selection
From the PancakeSwap token list, the strategy picks tokens that also have a Binance USDT perpetual (excluding stablecoins and WBNB). A Multicall query finds all their PancakeSwap V2/V3 pools against USDT and WBNB, roughly 200 tokens and 1,400 pools.
At startup it backfills Swap events for the last WINDOW_H hours with eth_getLogs, then pulls new blocks every SCAN_SEC seconds. For each trade it computes the pool price before and after. A single trade that moves the price by at least MIN_BP and is worth at least MIN_USD counts as a shock: down is a dump, up is a pump.
Each token's dumps are bucketed by size, with amounts within about 10% in the same bucket. The more dumps in one bucket, the more it looks like someone is selling a fixed amount:
javascript
s.dumps.forEach(function (u) { var b = Math.round(Math.log(u) / Math.log(1.1)); (bucket[b] = bucket[b] || []).push(u) })
Tokens with at least MIN_DUMPS dumps are ranked by fixed-size dump count, then by total dump count, and the top TOP_N get buy orders. A token that drops off the list gets no new orders, but existing positions are still closed normally. Automatic selection can be turned off to trade only the tokens in SYMBOLS.
Entry
Order price = Binance best bid × (1 − BUY_BPS), valid for EXPIRE_SEC seconds, size USDT_AMT per order. Orders are signed in the 1inch Limit Order Protocol v4 EIP-712 format with partial fills allowed and the expiry encoded in makerTraits, then submitted to the 1inch order book.
When the previous order expires, or the Binance best bid moves by more than DRIFT_BPS from the price at posting, a new order is posted at the new price. Old orders are not cancelled (on-chain cancellation costs gas) and simply expire, so several buy orders may be live at once. Total exposure is capped by:
- Per token: position + all unexpired buy orders ≤
MAX_INV; - All tokens: positions + all unexpired buy orders ≤
MAX_TOTAL; - Number of tokens held at once ≤
MAX_COINS.
Before posting, the strategy runs a liquidity check. Assuming the order fills, it quotes selling the whole position on-chain immediately. If that price is more than EXIT_BPS below the Binance best ask, the pool is too shallow and the token is skipped. No buy orders are posted while Binance's 60-second range exceeds VOL_BPS, during a cooldown, when manually paused, or after the total-loss halt.
At startup the strategy signs a test order, checks the order hash against the 1inch contract's hashOrder, and recovers the signer through the ecrecover precompile. It continues only if both match the wallet.
Fills and Hedging
In live mode, each round reads the wallet's token balance. An increase is treated as a buy fill, and the strategy immediately opens a Binance market short of the same size. Amounts below Binance's minimum order size accumulate until they can be hedged together. If the hedge order fails, legging handling takes over.
Exit
The strategy does not rest fixed-price sell orders. Each round it quotes all of the token's USDT pools through the PancakeSwap Quoter (V3) or Router (V2), takes the best price for selling the whole position now, and compares it with the Binance best ask:
javascript
var entryBp = (inv.shortCost / inv.short / (inv.cost / inv.qty) - 1) * 1e4 // spread locked at entry
var exitBp = (best.px / t.ask - 1) * 1e4 // current on-chain bid vs Binance ask
var netBp = entryBp + exitBp - 2 * TAKER_FEE * 1e4
When the net spread after fees is at least TP_BPS, the strategy sells through the PancakeSwap SmartRouter (0.3% slippage allowed), computes the fill price from the USDT actually received, and closes the Binance short at the same time. If the spread has not reverted after MAX_HOLD_H hours, the position is force-closed at the prevailing spread.
Risk Controls
| Control | Handling |
|---|---|
| Total exposure | Positions + unexpired buy orders across all tokens ≤ MAX_TOTAL; tokens held ≤ MAX_COINS |
| Volatility | Binance 60-second range above VOL_BPS pauses buy orders for that token |
| Shallow pool | No buy order if the test sale is more than EXIT_BPS below the Binance ask |
| Legging | When on-chain and Binance quantities differ, re-hedge every round; after LEG_TIMEOUT_SEC, close the excess leg and cool down the token |
| Reconciliation | In live mode, correct the ledger every 60 seconds against real on-chain balances and real Binance shorts |
| Stale-order sniping | If a buy fill locks in a spread worse than −BAD_FILL_BPS, cool down the token |
| Per-token loss | Realized + floating loss on a token reaching COIN_MAX_LOSS forces a close and a cooldown |
| Total loss | Total loss reaching MAX_LOSS_U stops all new entries (exits continue); "Resume" clears it |
During a COOLDOWN_MIN-minute cooldown, no buy orders are posted but exits continue. The on-chain position is valued at the price it could actually be sold for on-chain, not at the Binance price, so a widening spread between the two venues shows up directly as a floating loss.
The status panel shows the account summary, per-leg positions, order and exit conditions, per-token risk, run state, the dump ranking, and recent fills, with Pause / Resume buttons.
Main Parameters
| Parameter | Default | Description |
|---|---|---|
| ONEINCH_KEY | — | 1inch Developer Portal API key (encrypted string), required for live mode |
| DRY_RUN | true | Simulation: same workflow as live, but no approvals, no order submission, no Binance orders |
| AUTO | true | Automatic token selection; when false, only SYMBOLS is traded |
| SYMBOLS | SOON,XPL,ZEC | Tokens for manual mode, comma-separated |
| TOP_N | 5 | Number of tokens traded with automatic selection |
| WINDOW_H | 3 | Look-back window for dumps (hours) |
| MIN_BP / MIN_USD | 40 / 100 | Minimum price move (bp) and size (U) for a shock |
| MIN_DUMPS | 5 | Minimum dumps in the window to qualify |
| SCAN_SEC | 20 | Interval between on-chain scans (seconds) |
| USDT_AMT | 20 | Size per buy order (U) |
| BUY_BPS | 40 | Buy order offset below Binance best bid (bp) |
| EXPIRE_SEC | 15 | Buy order lifetime (seconds) |
| DRIFT_BPS | 10 | Binance move that triggers a re-post (bp) |
| MAX_INV | 60 | Per-token position + unexpired orders cap (U) |
| TP_BPS | 5 | Exit target: net spread after fees (bp) |
| MAX_HOLD_H | 72 | Maximum holding time (hours) |
| LEVERAGE | 3 | Binance futures leverage |
| MAX_TOTAL | 200 | Positions + unexpired orders cap across all tokens (U) |
| MAX_COINS | 3 | Maximum number of tokens held at once |
| VOL_BPS | 50 | Binance 60-second range threshold (bp) |
| EXIT_BPS | 30 | Pre-entry on-chain test-sale slippage threshold (bp) |
| LEG_TIMEOUT_SEC | 30 | Legging timeout (seconds) |
| COIN_MAX_LOSS | 2 | Per-token loss cap (U) |
| BAD_FILL_BPS | 30 | Stale-order sniping threshold (bp) |
| COOLDOWN_MIN | 60 | Cooldown length (minutes) |
| MAX_LOSS_U | 10 | Total strategy loss cap (U) |
Exchange objects must be added in order: first Binance USDT-margined perpetuals Futures_Binance, then Web3 with the BSC chain. Use a node that supports eth_getLogs over 5,000-block ranges, such as https://rpc.48.club. Fund the wallet with USDT and a little BNB for gas; signing uses FMZ's built-in IO("sign"). On the first live run, the strategy approves USDT to the 1inch Router, and each selected token to both the 1inch Router and the PancakeSwap SmartRouter.
Simulation and live mode use separate state keys and do not interfere with each other. In simulation, an order counts as fully filled if, while it is live, an on-chain trade price crosses the order price by enough to cover the pool fee. The hedge is recorded at the Binance best bid/ask at that moment. Simulation does not model whether a 1inch bot would actually fill the order or competition from other resting orders, so its results are more optimistic than live trading.
Validation Status
Token selection has been run on real on-chain data: Swap-event retrieval, price computation, and fixed-size dump ranking work across roughly 200 tokens and 1,400 pools. The startup self-check (order hash and signature verification) passes.
At the time of writing, the strategy is running in simulation mode. No real orders have been submitted, and the full live flow (1inch accepting orders, bot fills, Binance hedging, and on-chain exits) has not yet been verified.
Before live deployment, the recommended validation sequence is:
- Run in simulation mode and check token selection, order prices, and the distribution of simulated fill and exit spreads;
- Post one buy order far from the market and confirm the 1inch order book accepts and lists it;
- Trade small live size (20U per order) and measure fill rate, hedge latency, the share of stale-order sniping, and exit slippage;
- Verify legging handling, ledger reconciliation, and restart recovery;
- Tune
BUY_BPS,EXPIRE_SEC,TP_BPS, and related parameters from live results before increasing capital.
/*
1inch 链上挂单套利(BSC)· FMZ 发明者 JavaScript
大白话:
· 自动选币:盯 PancakeSwap 上"币安也有永续"的全部币(约 200 个币、1400 个池子),找出最近几小时里
反复被大单砸盘、尤其是"每次金额差不多"(有人在稳定出货)的币,只在这些币上挂买单
· 开仓:在链上挂"比币安买一便宜 BUY_BPS"的买单(1inch 限价单,挂单不上链、不花 Gas),
有效期 EXPIRE_SEC 秒;币安价格变动超过 DRIFT_BPS 就按新价再挂一张,可同时挂多张,持仓 + 挂单总额受 MAX_INV 限制。有人砸盘时机器人把便宜的币卖给我们 → 立刻在币安开空,锁住价差
· 平仓:不挂固定价格的卖单,而是等价差回归——每轮用链上真实报价算"现在卖掉能拿多少",和币安卖一比:
开仓价差 + 平仓价差 − 币安两次手续费 ≥ TP_BPS,就链上卖出、同时币安平空。两边同时成交,币价涨跌不影响,只赚价差
· 持有超过 MAX_HOLD_H 小时价差仍未回归,按当时价差强制平仓
- 1