perp-cli 0.6.3 → 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.
Files changed (177) hide show
  1. package/README.md +7 -2
  2. package/dist/api/public/hyperliquid.d.ts +2 -2
  3. package/dist/api/public/hyperliquid.js +0 -10
  4. package/dist/api/public/lighter.d.ts +2 -1
  5. package/dist/api/public/pacifica.d.ts +2 -1
  6. package/dist/arb/index.d.ts +7 -4
  7. package/dist/arb/index.js +5 -4
  8. package/dist/arb/sizing.d.ts +1 -1
  9. package/dist/arb/state.d.ts +1 -1
  10. package/dist/arb/state.js +4 -3
  11. package/dist/arb/utils.d.ts +0 -10
  12. package/dist/arb/utils.js +0 -42
  13. package/dist/bot/apex/guard.d.ts +26 -0
  14. package/dist/bot/apex/guard.js +86 -0
  15. package/dist/bot/apex/index.d.ts +6 -0
  16. package/dist/bot/apex/index.js +4 -0
  17. package/dist/bot/apex/pulse.d.ts +22 -0
  18. package/dist/bot/apex/pulse.js +107 -0
  19. package/dist/bot/apex/radar.d.ts +17 -0
  20. package/dist/bot/apex/radar.js +150 -0
  21. package/dist/bot/conditions.d.ts +1 -1
  22. package/dist/bot/config.d.ts +5 -1
  23. package/dist/bot/config.js +1 -1
  24. package/dist/bot/engine.d.ts +23 -2
  25. package/dist/bot/engine.js +296 -351
  26. package/dist/bot/index.d.ts +16 -0
  27. package/dist/bot/index.js +10 -0
  28. package/dist/bot/indicators.d.ts +29 -0
  29. package/dist/bot/indicators.js +144 -0
  30. package/dist/bot/reflect.d.ts +34 -0
  31. package/dist/bot/reflect.js +155 -0
  32. package/dist/bot/strategies/aggressive-taker.d.ts +36 -0
  33. package/dist/bot/strategies/aggressive-taker.js +125 -0
  34. package/dist/bot/strategies/apex-strategy.d.ts +36 -0
  35. package/dist/bot/strategies/apex-strategy.js +185 -0
  36. package/dist/bot/strategies/avellaneda-mm.d.ts +27 -0
  37. package/dist/bot/strategies/avellaneda-mm.js +111 -0
  38. package/dist/bot/strategies/basis-arb.d.ts +27 -0
  39. package/dist/bot/strategies/basis-arb.js +97 -0
  40. package/dist/bot/strategies/claude-agent.d.ts +31 -0
  41. package/dist/bot/strategies/claude-agent.js +155 -0
  42. package/dist/bot/strategies/dca-strategy.d.ts +25 -0
  43. package/dist/bot/strategies/dca-strategy.js +59 -0
  44. package/dist/bot/strategies/engine-mm.d.ts +29 -0
  45. package/dist/bot/strategies/engine-mm.js +141 -0
  46. package/dist/bot/strategies/funding-arb-strategy.d.ts +32 -0
  47. package/dist/bot/strategies/funding-arb-strategy.js +168 -0
  48. package/dist/bot/strategies/funding-arb-v2.d.ts +32 -0
  49. package/dist/bot/strategies/funding-arb-v2.js +222 -0
  50. package/dist/bot/strategies/funding-auto.d.ts +1 -0
  51. package/dist/bot/strategies/funding-auto.js +572 -0
  52. package/dist/bot/strategies/grid-mm.d.ts +25 -0
  53. package/dist/bot/strategies/grid-mm.js +99 -0
  54. package/dist/bot/strategies/grid-strategy.d.ts +39 -0
  55. package/dist/bot/strategies/grid-strategy.js +176 -0
  56. package/dist/bot/strategies/hedge-agent.d.ts +25 -0
  57. package/dist/bot/strategies/hedge-agent.js +79 -0
  58. package/dist/bot/strategies/liquidation-mm.d.ts +25 -0
  59. package/dist/bot/strategies/liquidation-mm.js +108 -0
  60. package/dist/bot/strategies/mean-reversion.d.ts +35 -0
  61. package/dist/bot/strategies/mean-reversion.js +118 -0
  62. package/dist/bot/strategies/momentum-breakout.d.ts +36 -0
  63. package/dist/bot/strategies/momentum-breakout.js +159 -0
  64. package/dist/bot/strategies/regime-mm.d.ts +25 -0
  65. package/dist/bot/strategies/regime-mm.js +111 -0
  66. package/dist/bot/strategies/rfq-agent.d.ts +25 -0
  67. package/dist/bot/strategies/rfq-agent.js +71 -0
  68. package/dist/bot/strategies/simple-mm.d.ts +25 -0
  69. package/dist/bot/strategies/simple-mm.js +76 -0
  70. package/dist/bot/strategy-registry.d.ts +9 -0
  71. package/dist/bot/strategy-registry.js +14 -0
  72. package/dist/bot/strategy-types.d.ts +64 -0
  73. package/dist/bot/strategy-types.js +5 -0
  74. package/dist/bot/trade-journal.d.ts +27 -0
  75. package/dist/bot/trade-journal.js +53 -0
  76. package/dist/bot/tui/BotDashboard.d.ts +49 -0
  77. package/dist/bot/tui/BotDashboard.js +93 -0
  78. package/dist/bot/tui/index.d.ts +6 -0
  79. package/dist/bot/tui/index.js +8 -0
  80. package/dist/bridge-engine.d.ts +0 -1
  81. package/dist/bridge-engine.js +0 -1
  82. package/dist/cache.d.ts +0 -1
  83. package/dist/cache.js +0 -1
  84. package/dist/commands/account.d.ts +1 -1
  85. package/dist/commands/account.js +106 -75
  86. package/dist/commands/agent.d.ts +1 -1
  87. package/dist/commands/arb/index.d.ts +1 -1
  88. package/dist/commands/arb/index.js +1 -1
  89. package/dist/commands/arb-auto.d.ts +1 -1
  90. package/dist/commands/arb-auto.js +1 -1
  91. package/dist/commands/arb.d.ts +1 -1
  92. package/dist/commands/bot.d.ts +1 -1
  93. package/dist/commands/bot.js +198 -14
  94. package/dist/commands/dashboard.d.ts +1 -1
  95. package/dist/commands/dashboard.js +1 -1
  96. package/dist/commands/funds.d.ts +1 -1
  97. package/dist/commands/funds.js +33 -20
  98. package/dist/commands/history.d.ts +1 -1
  99. package/dist/commands/history.js +5 -4
  100. package/dist/commands/manage.d.ts +2 -3
  101. package/dist/commands/manage.js +11 -18
  102. package/dist/commands/market.d.ts +1 -1
  103. package/dist/commands/market.js +7 -7
  104. package/dist/commands/plan.d.ts +1 -1
  105. package/dist/commands/rebalance.d.ts +1 -1
  106. package/dist/commands/rebalance.js +8 -9
  107. package/dist/commands/risk.d.ts +1 -1
  108. package/dist/commands/settings.d.ts +1 -1
  109. package/dist/commands/settings.js +3 -1
  110. package/dist/commands/trade.d.ts +1 -1
  111. package/dist/commands/trade.js +39 -52
  112. package/dist/commands/wallet.d.ts +0 -2
  113. package/dist/commands/wallet.js +0 -5
  114. package/dist/constants.d.ts +0 -3
  115. package/dist/constants.js +0 -4
  116. package/dist/cross-chain-margin.d.ts +1 -1
  117. package/dist/dashboard/index.d.ts +4 -0
  118. package/dist/dashboard/index.js +3 -0
  119. package/dist/dashboard/server.d.ts +1 -1
  120. package/dist/dashboard/server.js +1 -1
  121. package/dist/dashboard/ws-feeds.d.ts +1 -1
  122. package/dist/event-stream.d.ts +1 -1
  123. package/dist/exchanges/aster.d.ts +58 -0
  124. package/dist/exchanges/aster.js +409 -0
  125. package/dist/exchanges/capabilities.d.ts +74 -0
  126. package/dist/exchanges/capabilities.js +44 -0
  127. package/dist/exchanges/hyperliquid-spot.d.ts +1 -1
  128. package/dist/exchanges/hyperliquid-spot.js +20 -12
  129. package/dist/exchanges/hyperliquid.d.ts +8 -2
  130. package/dist/exchanges/hyperliquid.js +21 -6
  131. package/dist/exchanges/index.d.ts +11 -0
  132. package/dist/exchanges/index.js +10 -0
  133. package/dist/exchanges/interface.d.ts +3 -0
  134. package/dist/exchanges/lighter-spot.d.ts +13 -0
  135. package/dist/exchanges/lighter-spot.js +98 -26
  136. package/dist/exchanges/lighter.d.ts +9 -3
  137. package/dist/exchanges/lighter.js +18 -8
  138. package/dist/exchanges/nado.d.ts +52 -0
  139. package/dist/exchanges/nado.js +223 -0
  140. package/dist/exchanges/pacifica.d.ts +7 -1
  141. package/dist/exchanges/pacifica.js +17 -2
  142. package/dist/exchanges/registry.d.ts +13 -0
  143. package/dist/exchanges/registry.js +37 -0
  144. package/dist/exchanges/spot-interface.d.ts +9 -0
  145. package/dist/funding/rates.js +1 -1
  146. package/dist/index.js +2 -6
  147. package/dist/jobs.js +1 -1
  148. package/dist/liquidity.d.ts +1 -2
  149. package/dist/liquidity.js +12 -5
  150. package/dist/mcp-server.js +4 -2
  151. package/dist/pacifica/constants.d.ts +0 -3
  152. package/dist/pacifica/constants.js +0 -4
  153. package/dist/pacifica/types/account.d.ts +0 -8
  154. package/dist/pacifica/types/lake.d.ts +0 -6
  155. package/dist/pacifica/types/position.d.ts +0 -12
  156. package/dist/pacifica/types/ws.d.ts +0 -24
  157. package/dist/pacifica/types/ws.js +1 -41
  158. package/dist/plan-executor.d.ts +1 -1
  159. package/dist/rebalance.d.ts +1 -7
  160. package/dist/rebalance.js +0 -6
  161. package/dist/risk.d.ts +1 -1
  162. package/dist/risk.js +3 -1
  163. package/dist/smart-order.d.ts +1 -1
  164. package/dist/strategies/dca.d.ts +1 -1
  165. package/dist/strategies/funding-arb.d.ts +1 -1
  166. package/dist/strategies/funding-arb.js +193 -181
  167. package/dist/strategies/grid.d.ts +1 -1
  168. package/dist/strategies/grid.js +1 -1
  169. package/dist/strategies/index.d.ts +12 -0
  170. package/dist/strategies/index.js +7 -0
  171. package/dist/strategies/split-order.d.ts +1 -1
  172. package/dist/strategies/trailing-stop.d.ts +1 -1
  173. package/dist/strategies/twap.d.ts +1 -1
  174. package/dist/trade-validator.d.ts +1 -1
  175. package/package.json +7 -1
  176. package/skills/perp-cli/SKILL.md +20 -0
  177. package/skills/perp-cli/references/commands.md +22 -8
@@ -0,0 +1,222 @@
1
+ import { registerStrategy } from "../strategy-registry.js";
2
+ export class FundingArbV2Strategy {
3
+ name = "funding-arb-v2";
4
+ _config = {};
5
+ describe() {
6
+ return {
7
+ description: "Enhanced multi-exchange funding rate arbitrage with auto-close",
8
+ params: [
9
+ { name: "minSpread", type: "number", required: false, default: 20, description: "Min annualized spread % to open" },
10
+ { name: "closeSpread", type: "number", required: false, default: 5, description: "Spread % to close position" },
11
+ { name: "maxPositions", type: "number", required: false, default: 3, description: "Max concurrent arb positions" },
12
+ { name: "sizeUsd", type: "number", required: true, description: "Position size in USD per leg" },
13
+ { name: "scanIntervalSec", type: "number", required: false, default: 60, description: "Seconds between full exchange scans" },
14
+ ],
15
+ };
16
+ }
17
+ get minSpread() { return Number(this._config.minSpread ?? 20); }
18
+ get closeSpread() { return Number(this._config.closeSpread ?? 5); }
19
+ get maxPositions() { return Number(this._config.maxPositions ?? 3); }
20
+ get sizeUsd() { return Number(this._config.sizeUsd ?? 50); }
21
+ get scanIntervalSec() { return Number(this._config.scanIntervalSec ?? 60); }
22
+ async init(ctx, _snapshot) {
23
+ this._config = ctx.config;
24
+ ctx.state.set("positions", []);
25
+ ctx.state.set("lastScan", 0);
26
+ ctx.log(` [ARB-V2] Ready | minSpread=${this.minSpread}% closeSpread=${this.closeSpread}% maxPos=${this.maxPositions} size=$${this.sizeUsd}`);
27
+ }
28
+ async onTick(ctx, _snapshot) {
29
+ const positions = ctx.state.get("positions");
30
+ const lastScan = ctx.state.get("lastScan");
31
+ const now = Date.now();
32
+ // Throttle scanning
33
+ if (now - lastScan < this.scanIntervalSec * 1000) {
34
+ return [];
35
+ }
36
+ ctx.state.set("lastScan", now);
37
+ // Get all adapters
38
+ const adapters = this.getAdapters(ctx);
39
+ if (adapters.size < 2) {
40
+ ctx.log(` [ARB-V2] Need 2+ exchanges, have ${adapters.size}. Skipping.`);
41
+ return [];
42
+ }
43
+ // Fetch funding rates from all exchanges
44
+ const ratesByExchange = new Map();
45
+ for (const [name, adapter] of adapters) {
46
+ try {
47
+ const markets = await adapter.getMarkets();
48
+ const map = new Map();
49
+ for (const m of markets) {
50
+ map.set(m.symbol.toUpperCase(), {
51
+ rate: parseFloat(m.fundingRate),
52
+ price: parseFloat(m.markPrice),
53
+ });
54
+ }
55
+ ratesByExchange.set(name, map);
56
+ }
57
+ catch {
58
+ // Skip unavailable exchange
59
+ }
60
+ }
61
+ // Phase 1: Check existing positions for auto-close
62
+ const toClose = [];
63
+ for (let i = 0; i < positions.length; i++) {
64
+ const pos = positions[i];
65
+ const longRates = ratesByExchange.get(pos.longExchange);
66
+ const shortRates = ratesByExchange.get(pos.shortExchange);
67
+ if (!longRates || !shortRates)
68
+ continue;
69
+ const longInfo = longRates.get(pos.symbol);
70
+ const shortInfo = shortRates.get(pos.symbol);
71
+ if (!longInfo || !shortInfo)
72
+ continue;
73
+ const currentSpread = this.annualizeSpread(shortInfo.rate, longInfo.rate);
74
+ if (currentSpread <= this.closeSpread) {
75
+ ctx.log(` [ARB-V2] Auto-close ${pos.symbol}: spread ${currentSpread.toFixed(1)}% <= ${this.closeSpread}% (entry was ${pos.entrySpread.toFixed(1)}%)`);
76
+ // Close both legs
77
+ const longAdapter = adapters.get(pos.longExchange);
78
+ const shortAdapter = adapters.get(pos.shortExchange);
79
+ if (longAdapter && shortAdapter) {
80
+ const price = longInfo.price;
81
+ const size = price > 0 ? (pos.sizeUsd / price).toFixed(6) : "0";
82
+ try {
83
+ await Promise.all([
84
+ longAdapter.marketOrder(pos.symbol, "sell", size),
85
+ shortAdapter.marketOrder(pos.symbol, "buy", size),
86
+ ]);
87
+ ctx.log(` [ARB-V2] Closed ${pos.symbol} (long ${pos.longExchange}, short ${pos.shortExchange})`);
88
+ }
89
+ catch (err) {
90
+ const msg = err instanceof Error ? err.message : String(err);
91
+ ctx.log(` [ARB-V2] Close failed for ${pos.symbol}: ${msg}`);
92
+ continue; // Don't mark as closed
93
+ }
94
+ }
95
+ toClose.push(i);
96
+ }
97
+ }
98
+ // Remove closed positions (reverse order to preserve indices)
99
+ for (let i = toClose.length - 1; i >= 0; i--) {
100
+ positions.splice(toClose[i], 1);
101
+ }
102
+ ctx.state.set("positions", positions);
103
+ // Phase 2: Scan for new opportunities
104
+ if (positions.length >= this.maxPositions)
105
+ return [];
106
+ const exchangeNames = [...ratesByExchange.keys()];
107
+ const allSymbols = new Set();
108
+ for (const [, map] of ratesByExchange) {
109
+ for (const sym of map.keys())
110
+ allSymbols.add(sym);
111
+ }
112
+ // Skip symbols we already have positions for
113
+ const activeSymbols = new Set(positions.map(p => p.symbol));
114
+ const opportunities = [];
115
+ for (const sym of allSymbols) {
116
+ if (activeSymbols.has(sym))
117
+ continue;
118
+ let minRate = Infinity, maxRate = -Infinity;
119
+ let minExchange = "", maxExchange = "";
120
+ let bestPrice = 0;
121
+ for (const exName of exchangeNames) {
122
+ const info = ratesByExchange.get(exName)?.get(sym);
123
+ if (!info)
124
+ continue;
125
+ if (info.rate < minRate) {
126
+ minRate = info.rate;
127
+ minExchange = exName;
128
+ }
129
+ if (info.rate > maxRate) {
130
+ maxRate = info.rate;
131
+ maxExchange = exName;
132
+ bestPrice = info.price;
133
+ }
134
+ }
135
+ if (minExchange && maxExchange && minExchange !== maxExchange) {
136
+ const spread = this.annualizeSpread(maxRate, minRate);
137
+ if (spread >= this.minSpread) {
138
+ opportunities.push({
139
+ symbol: sym,
140
+ longExchange: minExchange, // long where rate is lowest
141
+ shortExchange: maxExchange, // short where rate is highest
142
+ spread,
143
+ price: bestPrice,
144
+ });
145
+ }
146
+ }
147
+ }
148
+ opportunities.sort((a, b) => b.spread - a.spread);
149
+ // Open new positions
150
+ const slotsAvailable = this.maxPositions - positions.length;
151
+ const toOpen = opportunities.slice(0, slotsAvailable);
152
+ for (const opp of toOpen) {
153
+ const longAdapter = adapters.get(opp.longExchange);
154
+ const shortAdapter = adapters.get(opp.shortExchange);
155
+ if (!longAdapter || !shortAdapter || opp.price <= 0)
156
+ continue;
157
+ const size = (this.sizeUsd / opp.price).toFixed(6);
158
+ try {
159
+ ctx.log(` [ARB-V2] Opening ${opp.symbol}: spread ${opp.spread.toFixed(1)}% (long ${opp.longExchange}, short ${opp.shortExchange})`);
160
+ await Promise.all([
161
+ longAdapter.marketOrder(opp.symbol, "buy", size),
162
+ shortAdapter.marketOrder(opp.symbol, "sell", size),
163
+ ]);
164
+ positions.push({
165
+ symbol: opp.symbol,
166
+ longExchange: opp.longExchange,
167
+ shortExchange: opp.shortExchange,
168
+ entrySpread: opp.spread,
169
+ entryTime: now,
170
+ sizeUsd: this.sizeUsd,
171
+ });
172
+ ctx.log(` [ARB-V2] Position opened! (${positions.length}/${this.maxPositions})`);
173
+ }
174
+ catch (err) {
175
+ const msg = err instanceof Error ? err.message : String(err);
176
+ ctx.log(` [ARB-V2] Execution failed: ${msg}`);
177
+ }
178
+ }
179
+ ctx.state.set("positions", positions);
180
+ // Return noop — arb executes directly via multi-adapter coordination
181
+ return positions.length > 0 || toOpen.length > 0 ? [{ type: "noop" }] : [];
182
+ }
183
+ async onStop(ctx) {
184
+ const positions = ctx.state.get("positions");
185
+ if (positions.length === 0)
186
+ return [];
187
+ const adapters = this.getAdapters(ctx);
188
+ for (const pos of positions) {
189
+ const longAdapter = adapters.get(pos.longExchange);
190
+ const shortAdapter = adapters.get(pos.shortExchange);
191
+ if (longAdapter && shortAdapter) {
192
+ try {
193
+ // Best-effort close all legs
194
+ await Promise.all([
195
+ longAdapter.cancelAllOrders(pos.symbol),
196
+ shortAdapter.cancelAllOrders(pos.symbol),
197
+ ]);
198
+ }
199
+ catch { /* best effort */ }
200
+ }
201
+ }
202
+ ctx.state.set("positions", []);
203
+ return [{ type: "cancel_all" }];
204
+ }
205
+ getAdapters(ctx) {
206
+ const adapters = new Map();
207
+ adapters.set(ctx.adapter.name.toLowerCase(), ctx.adapter);
208
+ const extra = ctx.state.get("extraAdapters");
209
+ if (extra) {
210
+ for (const [name, a] of extra)
211
+ adapters.set(name, a);
212
+ }
213
+ return adapters;
214
+ }
215
+ /** Convert 8h funding rate difference to annualized spread % */
216
+ annualizeSpread(highRate, lowRate) {
217
+ // Funding is typically per 8h period = 3x daily = 1095x annual
218
+ const spreadPer8h = highRate - lowRate;
219
+ return spreadPer8h * 3 * 365 * 100; // annualized %
220
+ }
221
+ }
222
+ registerStrategy("funding-arb-v2", (_config) => new FundingArbV2Strategy());
@@ -0,0 +1 @@
1
+ export {};