lpsignal 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/README.zh.md +91 -0
- package/dist/types.d.ts +18 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
Official Node.js SDK for [LPSignal](https://lpsignal.app): net-of-IL APR signals for concentrated-liquidity pools.
|
|
4
4
|
ESM, typed, Node ≥ 20. One runtime dependency (`ws`).
|
|
5
5
|
|
|
6
|
+
[中文说明](README.zh.md) · Python SDK: [LPSignals/lpsignal-python](https://github.com/LPSignals/lpsignal-python) · [API docs](https://lpsignal.app/docs)
|
|
7
|
+
|
|
6
8
|
```bash
|
|
7
9
|
npm install lpsignal
|
|
8
10
|
```
|
|
@@ -28,7 +30,7 @@ try {
|
|
|
28
30
|
| Method | Endpoint | Key |
|
|
29
31
|
|---|---|---|
|
|
30
32
|
| `health()` · `chains()` | `/v1/health` · `/v1/chains` | – |
|
|
31
|
-
| `pools({ chain, class, window, minTvlUsd, limit, offset, sort, order })` | `GET /v1/pools` — `sort`: `netApr` (default) · `feeApr` · `ilApr` · `inRange` · `emissionApr` · `tvl` · `fee`; `order`: `desc` (default) · `asc`; the page carries `total` | – |
|
|
33
|
+
| `pools({ chain, class, window, minTvlUsd, limit, offset, sort, order })` | `GET /v1/pools` — `sort`: `netApr` (default) · `feeApr` · `ilApr` · `inRange` · `emissionApr` · `tvl` · `fee` · `volume24h` · `fees24h`; `order`: `desc` (default) · `asc`; the page carries `total`; each pool carries `volume24hUsd`, `fees24hUsd` (an estimate: volume × the current fee rate) and `best.net24h` | – |
|
|
32
34
|
| `iteratePools({ …, sort, order })` | every page, in that order (best effort: none twice; one whose place changes meanwhile may be missed) | – |
|
|
33
35
|
| `pool(chain, address)` · `poolHours(chain, address, { hours })` | `GET /v1/pools/:chain/:address[/hours]` | – |
|
|
34
36
|
| `backtest(chain, address, { rangePct, days })` | `GET …/backtest` | – |
|
|
@@ -36,7 +38,7 @@ try {
|
|
|
36
38
|
| `signalStats({ days })` | `GET /v1/signals/stats` — the public track record | – |
|
|
37
39
|
| `iterateSignals({ kind })` | every page, newest first | optional |
|
|
38
40
|
| `signalsAfter(id)` | everything newer than `id`, oldest first | optional |
|
|
39
|
-
| `smartLps({ windowDays, chain, limit, offset, sort, order })` | `GET /v1/smart-lps` — `sort`: `rank` (default, = pnl rank) · `pnl` · `return` · `capital` · `closes` · `wins`; `rank` stays the pnl rank whatever the sort; `total` | optional |
|
|
41
|
+
| `smartLps({ windowDays, chain, limit, offset, sort, order })` | `GET /v1/smart-lps` — `sort`: `rank` (default, = pnl rank) · `pnl` · `return` · `capital` · `closes` · `wins` · `apr` · `winRate`; `rank` stays the pnl rank whatever the sort; `total`; each wallet carries `aprVsHold` (annualised vs holding), `winRate`, `avgHoldH` | optional |
|
|
40
42
|
| `iterateSmartLps({ …, sort, order })` | every wallet on the board, in that order | optional |
|
|
41
43
|
| `walletPositions(owner, { limit, openOffset, openSort, openOrder, closedOffset, closedSort, closedOrder })` | `GET /v1/smart-lps/:owner/positions` — each list pages and sorts on its own (`openSort`: `lastEvent` · `openedAt` · `entryUsd`; `closedSort`: `closedAt` · `openedAt` · `capitalUsd` · `pnlUsd`); `openTotal` / `closedTotal` | Pro |
|
|
42
44
|
| `follows()` · `follow(owner)` · `unfollow(owner)` | `/v1/me/follows` | Pro |
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# lpsignal(Node.js)
|
|
2
|
+
|
|
3
|
+
[LPSignal](https://lpsignal.app) 的官方 Node.js SDK。LPSignal 为蓝筹集中流动性池(Uniswap v3/v4、PancakeSwap v3、
|
|
4
|
+
Aerodrome / Velodrome Slipstream)提供扣除无常损失后的净 APR 信号,覆盖 Ethereum、BNB Chain、Base、Arbitrum、
|
|
5
|
+
Optimism 和 Polygon。
|
|
6
|
+
|
|
7
|
+
[English](README.md) · Python 版:[LPSignals/lpsignal-python](https://github.com/LPSignals/lpsignal-python) · [API 文档](https://lpsignal.app/docs)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install lpsignal
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
它提供:
|
|
14
|
+
|
|
15
|
+
- **REST API**:按净 APR 排序的池子、各区间指标、小时级历史、区间回测、信号及其 7 天后的实际结果、Smart LP
|
|
16
|
+
排行榜、账户信息、Webhook 与 Telegram 设置、付费链接。
|
|
17
|
+
- **不漏信号的实时流**。服务端 WebSocket 重连时只补最近 24 小时;SDK 在每次连接前还会用 REST 拉取你最后一条信号之后
|
|
18
|
+
的全部信号,所以停机多久都能补齐。信号按 id 顺序到达,进程运行期间每条只给一次。把最后一条 id 存下来(自带文件
|
|
19
|
+
存储),重启后从断点继续;如果恰好在 handler 处理完、还没存盘时崩溃,这一条会再给一次,所以 handler 要按
|
|
20
|
+
`signal.id` 做幂等。
|
|
21
|
+
- **Webhook 验签**:校验 `x-lpsignal-signature` 的 HMAC 和时间戳,返回解析后的内容。
|
|
22
|
+
|
|
23
|
+
## 快速开始
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { LPSignal, SignalStream, FileLastIdStore } from 'lpsignal';
|
|
27
|
+
|
|
28
|
+
const lps = new LPSignal({ apiKey: process.env.LPSIGNAL_API_KEY });
|
|
29
|
+
|
|
30
|
+
const { pools } = await lps.pools({ chain: 'base', class: 'volatile', limit: 10 });
|
|
31
|
+
for (const p of pools) console.log(p.pair, p.dex, `${(p.best.netApr * 100).toFixed(1)}%`, `±${p.best.rangeBp / 100}%`);
|
|
32
|
+
|
|
33
|
+
const stream = new SignalStream({
|
|
34
|
+
client: lps,
|
|
35
|
+
store: new FileLastIdStore('./lpsignal-state.json'),
|
|
36
|
+
onSignal: async (signal) => {
|
|
37
|
+
if (signal.kind === 'net_apr') console.log(`开仓 ${signal.pair} ticks ${signal.tickLower}..${signal.tickUpper}`);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
await stream.start();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 单位约定
|
|
44
|
+
|
|
45
|
+
- APR 和比例都是小数:`0.345` 表示 34.5%。
|
|
46
|
+
- `ilApr` / `il7d` 是相对"直接持有两个币"的损失,所以**为 0 或负数**,`netApr = feeApr + ilApr`。
|
|
47
|
+
- `fee` 的单位是百分之一个基点:`500` 即 0.05% 费率档。
|
|
48
|
+
- `rangeBp` 是区间半宽,单位为价格的基点:`500` 即 ±5%,`0` 即全区间。实际要开的仓位是 `tickLower..tickUpper`,
|
|
49
|
+
已按池子的 tick spacing 对齐。
|
|
50
|
+
- id 一律是字符串(可能超过 2^53),时间是 UTC 的 ISO 8601 字符串。
|
|
51
|
+
|
|
52
|
+
## 套餐
|
|
53
|
+
|
|
54
|
+
公开接口不需要 key,但机会信号要满 24 小时后才能看到。实时流、Webhook 和实时机会信号需要 Basic 或 Pro;Smart LP
|
|
55
|
+
信号和钱包持仓需要 Pro。套餐和完整 API 文档见 [lpsignal.app](https://lpsignal.app)。
|
|
56
|
+
|
|
57
|
+
## 推送哪些信号
|
|
58
|
+
|
|
59
|
+
信号流、webhook 和 Telegram 推送你订阅的类型:核心事件 `net_apr`、`tvl_outflow`、`depeg`、`smart_lp` 默认开启。
|
|
60
|
+
短时机会(`burst`:最近 1 小时净 APR 很高且有真实成交)需要添加后才推送——`setSubscriptions([...])`,或只对某个信号流
|
|
61
|
+
`new SignalStream({ ..., kinds: ['burst'] })`。
|
|
62
|
+
|
|
63
|
+
## 自定义规则
|
|
64
|
+
|
|
65
|
+
Basic(3 条规则)和 Pro(20 条规则)可以设置自己的阈值。命中只推送给你(信号流、webhook、Telegram),并带有
|
|
66
|
+
`signal.rule = { id, name }`。阈值和其他字段一样用小数表示。
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
await lps.createRule({ kind: 'net_apr', name: 'Base, 15%+', minNet7d: 0.15, chains: ['base'] });
|
|
70
|
+
await lps.createRule({ kind: 'depeg', name: 'early depeg', minDeviation: 0.003 });
|
|
71
|
+
await lps.setSubscriptions(['net_apr', 'burst', 'depeg']); // 推送哪些类型(规则命中总会推送)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 开发
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm install && npm test # 单元测试
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
用 [`testdata/webhook-vectors.json`](testdata/webhook-vectors.json) 校验 webhook 签名,这份向量是用
|
|
81
|
+
LPSignal 服务端自己的签名函数生成的(Node 与 Python 两个仓库共用同一份)。
|
|
82
|
+
|
|
83
|
+
对运行中的 API 做端到端测试(默认只读;`E2E_WRITE=1` 会调用写接口,只能用测试账号):
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm run build && LPSIGNAL_BASE_URL=https://api.lpsignal.app LPSIGNAL_API_KEY=lps_... npm run e2e
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 许可证
|
|
90
|
+
|
|
91
|
+
[MIT](LICENSE)
|
package/dist/types.d.ts
CHANGED
|
@@ -42,6 +42,8 @@ export interface BestRange {
|
|
|
42
42
|
/** Slipstream: gauge emissions if staked instead (fees forgone, not backtested); 0 = not applicable */
|
|
43
43
|
stakedEmissionApr: number;
|
|
44
44
|
asOf: string;
|
|
45
|
+
/** pools list only: the same range's net APR over the last 24h (null = not computed) */
|
|
46
|
+
net24h?: number | null;
|
|
45
47
|
}
|
|
46
48
|
export interface RankedPool {
|
|
47
49
|
chain: Chain;
|
|
@@ -55,11 +57,17 @@ export interface RankedPool {
|
|
|
55
57
|
/** v4 pools: liquidity within ±2% of price, since v4 has no per-pool balances */
|
|
56
58
|
tvlUsd: number;
|
|
57
59
|
tvlAt: string | null;
|
|
60
|
+
/**
|
|
61
|
+
* the last 24h in USD at the latest prices; null = no price yet. fees = an ESTIMATE of the swap fees paid: volume ×
|
|
62
|
+
* the pool's current fee rate, before protocol cuts (dynamic-fee pools: approximate) — not what LPs were paid
|
|
63
|
+
*/
|
|
64
|
+
volume24hUsd: number | null;
|
|
65
|
+
fees24hUsd: number | null;
|
|
58
66
|
best: BestRange;
|
|
59
67
|
}
|
|
60
68
|
export type Order = 'asc' | 'desc';
|
|
61
69
|
/** what `pools` can sort by: the best range's figures, the pool's TVL or fee tier */
|
|
62
|
-
export type PoolSort = 'netApr' | 'feeApr' | 'ilApr' | 'inRange' | 'emissionApr' | 'tvl' | 'fee';
|
|
70
|
+
export type PoolSort = 'netApr' | 'feeApr' | 'ilApr' | 'inRange' | 'emissionApr' | 'tvl' | 'fee' | 'volume24h' | 'fees24h';
|
|
63
71
|
/** what `signals` can sort by: time (newest first, cursor), return (APR at firing), outcome (realised result) */
|
|
64
72
|
export type SignalSort = 'time' | 'return' | 'outcome';
|
|
65
73
|
export interface PoolsPage {
|
|
@@ -268,6 +276,12 @@ export interface LeaderboardWallet {
|
|
|
268
276
|
pnlUsd: number;
|
|
269
277
|
returnPct: number;
|
|
270
278
|
chains: Chain[];
|
|
279
|
+
/** annualised return vs holding over the capital × time deployed; null = no close with a known opening */
|
|
280
|
+
aprVsHold: number | null;
|
|
281
|
+
/** closes with a positive pnl / closes */
|
|
282
|
+
winRate: number;
|
|
283
|
+
/** capital-weighted hours a position was held */
|
|
284
|
+
avgHoldH: number | null;
|
|
271
285
|
}
|
|
272
286
|
export interface Leaderboard {
|
|
273
287
|
windowDays: 30 | 90;
|
|
@@ -282,7 +296,7 @@ export interface Leaderboard {
|
|
|
282
296
|
order: Order;
|
|
283
297
|
}
|
|
284
298
|
/** what `smartLps` can sort by; `rank` (the pnl rank) stays the wallet's rank whatever the sort */
|
|
285
|
-
export type BoardSort = 'rank' | 'pnl' | 'return' | 'capital' | 'closes' | 'wins';
|
|
299
|
+
export type BoardSort = 'rank' | 'pnl' | 'return' | 'capital' | 'closes' | 'wins' | 'apr' | 'winRate';
|
|
286
300
|
interface PositionPool {
|
|
287
301
|
chain: Chain;
|
|
288
302
|
pool: string;
|
|
@@ -297,6 +311,8 @@ export interface OpenPosition extends PositionPool {
|
|
|
297
311
|
openedAt: string | null;
|
|
298
312
|
staked: boolean;
|
|
299
313
|
entryUsd: number | null;
|
|
314
|
+
/** the pool's tick now (its latest hourly close): in range when tickLower <= poolTick < tickUpper; null = unknown */
|
|
315
|
+
poolTick: number | null;
|
|
300
316
|
}
|
|
301
317
|
export interface ClosedPosition extends PositionPool {
|
|
302
318
|
tokenId: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lpsignal",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Official LPSignal SDK: net-of-IL APR signals for concentrated-liquidity pools, a WebSocket stream that never misses a signal, webhook verification and the REST API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"pancakeswap",
|
|
38
38
|
"aerodrome"
|
|
39
39
|
],
|
|
40
|
-
"homepage": "https://github.com/
|
|
40
|
+
"homepage": "https://github.com/LPSignals/lpsignal-node#readme",
|
|
41
41
|
"repository": {
|
|
42
42
|
"type": "git",
|
|
43
|
-
"url": "git+https://github.com/
|
|
43
|
+
"url": "git+https://github.com/LPSignals/lpsignal-node.git",
|
|
44
44
|
"directory": "node"
|
|
45
45
|
},
|
|
46
46
|
"license": "MIT",
|