perp-cli 0.7.48 → 0.9.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/dist/bot/engine.d.ts +1 -0
- package/dist/bot/engine.js +1 -0
- package/dist/bot/strategies/funding-arb-strategy.js +30 -23
- package/dist/bot/strategies/funding-arb-utils.d.ts +49 -0
- package/dist/bot/strategies/funding-arb-utils.js +239 -0
- package/dist/bot/strategies/funding-arb-v2-strategy.d.ts +42 -0
- package/dist/bot/strategies/funding-arb-v2-strategy.js +873 -0
- package/dist/bot/strategies/spot-perp-arb-strategy.d.ts +43 -0
- package/dist/bot/strategies/spot-perp-arb-strategy.js +547 -0
- package/dist/bot/strategies/spot-perp-executor.d.ts +81 -0
- package/dist/bot/strategies/spot-perp-executor.js +271 -0
- package/dist/commands/bot.js +80 -1
- package/dist/commands/trade.js +131 -0
- package/dist/exchanges/aster.d.ts +1 -1
- package/dist/exchanges/aster.js +5 -5
- package/dist/exchanges/hyperliquid.d.ts +7 -0
- package/dist/exchanges/hyperliquid.js +23 -0
- package/dist/exchanges/lighter-spot.js +20 -0
- package/dist/exchanges/lighter.js +45 -1
- package/dist/exchanges/pacifica.js +42 -1
- package/dist/exchanges/spot-interface.js +4 -0
- package/package.json +1 -1
|
@@ -478,7 +478,51 @@ export class LighterAdapter {
|
|
|
478
478
|
orderExpiry: 0, // DEFAULT_IOC_EXPIRY
|
|
479
479
|
nonce,
|
|
480
480
|
});
|
|
481
|
-
|
|
481
|
+
const result = await this.sendTx(signed);
|
|
482
|
+
// Verify fill via tx endpoint
|
|
483
|
+
const txResult = result;
|
|
484
|
+
if (txResult.tx_hash) {
|
|
485
|
+
// Poll tx status (may take a moment to process)
|
|
486
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
487
|
+
await new Promise(r => setTimeout(r, 500));
|
|
488
|
+
try {
|
|
489
|
+
const tx = await this.restGet("/tx", { by: "hash", value: txResult.tx_hash });
|
|
490
|
+
const status = Number(tx.status ?? 0);
|
|
491
|
+
if (status === 3) {
|
|
492
|
+
// status 3 = executed. Parse event_info for fill details
|
|
493
|
+
try {
|
|
494
|
+
const eventInfo = JSON.parse(String(tx.event_info ?? "{}"));
|
|
495
|
+
const takerOrder = eventInfo.to;
|
|
496
|
+
if (takerOrder && Number(takerOrder.rs ?? -1) === 0) {
|
|
497
|
+
return result; // fully filled
|
|
498
|
+
}
|
|
499
|
+
// Partially filled or unfilled
|
|
500
|
+
if (takerOrder && Number(takerOrder.rs) > 0 && Number(takerOrder.is) > 0) {
|
|
501
|
+
const filled = Number(takerOrder.is) - Number(takerOrder.rs);
|
|
502
|
+
if (filled > 0)
|
|
503
|
+
return result; // partially filled is OK for market orders
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
catch { /* parse error, fall through to status=3 return */ }
|
|
507
|
+
// status=3 means tx was executed (even if zero-fill IOC — strategy verifies position)
|
|
508
|
+
return result;
|
|
509
|
+
}
|
|
510
|
+
if (status > 3) {
|
|
511
|
+
// status > 3 likely means rejected/cancelled
|
|
512
|
+
throw new Error(`Market ${side} ${symbol}: tx rejected (status=${status}, hash=${txResult.tx_hash})`);
|
|
513
|
+
}
|
|
514
|
+
// status < 3: still processing, retry
|
|
515
|
+
}
|
|
516
|
+
catch (e) {
|
|
517
|
+
if (e instanceof Error && e.message.startsWith("Market "))
|
|
518
|
+
throw e;
|
|
519
|
+
// tx query failed, retry
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
// After 5 attempts, tx still not processed
|
|
523
|
+
throw new Error(`Market ${side} ${symbol}: tx not confirmed after 2.5s (hash=${txResult.tx_hash})`);
|
|
524
|
+
}
|
|
525
|
+
return result;
|
|
482
526
|
}
|
|
483
527
|
async limitOrder(symbol, side, price, size, opts) {
|
|
484
528
|
this.ensureSigner();
|
|
@@ -182,7 +182,48 @@ export class PacificaAdapter {
|
|
|
182
182
|
}
|
|
183
183
|
async marketOrder(symbol, side, size, opts) {
|
|
184
184
|
this.ensureSigner();
|
|
185
|
-
|
|
185
|
+
const result = await this.client.createMarketOrder({ symbol, amount: size, side: side === "buy" ? "bid" : "ask", reduce_only: opts?.reduceOnly ?? false, slippage_percent: "1" }, this.account, this.signMessage);
|
|
186
|
+
// Validate response
|
|
187
|
+
const r = result;
|
|
188
|
+
const data = (r.data ?? r);
|
|
189
|
+
if (r.success === false || r.error) {
|
|
190
|
+
throw new Error(`Market ${side} ${symbol}: ${r.error ?? 'order rejected'}`);
|
|
191
|
+
}
|
|
192
|
+
// Get order_id from response
|
|
193
|
+
const orderId = data.order_id ? String(data.order_id) : null;
|
|
194
|
+
if (orderId && !(opts?.reduceOnly ?? false)) {
|
|
195
|
+
// Poll order status via history_by_id
|
|
196
|
+
let lastPollError = null;
|
|
197
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
198
|
+
await new Promise(r => setTimeout(r, 500));
|
|
199
|
+
try {
|
|
200
|
+
const history = await this.client.getOrderHistoryById(Number(orderId));
|
|
201
|
+
const histData = (history.data ?? history);
|
|
202
|
+
if (Array.isArray(histData) && histData.length > 0) {
|
|
203
|
+
const latest = histData[histData.length - 1];
|
|
204
|
+
const status = String(latest.order_status ?? "");
|
|
205
|
+
const filled = parseFloat(String(latest.filled_amount ?? "0"));
|
|
206
|
+
if (status === "filled" || filled > 0)
|
|
207
|
+
return result; // confirmed fill
|
|
208
|
+
if (status === "cancelled" || status === "rejected") {
|
|
209
|
+
throw new Error(`Market ${side} ${symbol}: order ${orderId} ${status}`);
|
|
210
|
+
}
|
|
211
|
+
// Still processing, retry
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
if (e instanceof Error && e.message.startsWith("Market "))
|
|
216
|
+
throw e;
|
|
217
|
+
lastPollError = e;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const errDetail = lastPollError instanceof Error ? ` (poll error: ${lastPollError.message})` : "";
|
|
221
|
+
throw new Error(`Market ${side} ${symbol}: order ${orderId} not confirmed after 2.5s${errDetail}`);
|
|
222
|
+
}
|
|
223
|
+
if (!(opts?.reduceOnly ?? false)) {
|
|
224
|
+
console.error(`[pacifica] Warning: market ${side} ${symbol} accepted but no order_id in response — skipping fill verification`);
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
186
227
|
}
|
|
187
228
|
async limitOrder(symbol, side, price, size, opts) {
|
|
188
229
|
this.ensureSigner();
|
|
@@ -15,6 +15,10 @@ export const SPOT_PERP_TOKEN_MAP = {
|
|
|
15
15
|
UETH: "ETH",
|
|
16
16
|
USOL: "SOL",
|
|
17
17
|
UFART: "FARTCOIN",
|
|
18
|
+
UPUMP: "PUMP",
|
|
19
|
+
LINK0: "LINK",
|
|
20
|
+
AVAX0: "AVAX",
|
|
21
|
+
AAVE0: "AAVE",
|
|
18
22
|
};
|
|
19
23
|
/** Reverse: perp symbol → U-token spot name on HL */
|
|
20
24
|
export const PERP_TO_SPOT_MAP = Object.fromEntries(Object.entries(SPOT_PERP_TOKEN_MAP).map(([k, v]) => [v, k]));
|