perp-cli 0.3.8 → 0.3.9
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/exchanges/hyperliquid.d.ts +5 -0
- package/dist/exchanges/hyperliquid.js +26 -16
- package/package.json +1 -1
- package/skills/perp-cli/SKILL.md +55 -3
- package/skills/perp-cli/references/agent-operations.md +27 -2
- package/skills/perp-cli/references/commands.md +2 -2
- package/skills/perp-cli/references/strategies.md +109 -41
- package/skills/perp-cli/templates/arb-scan.sh +1 -5
|
@@ -20,6 +20,11 @@ export declare class HyperliquidAdapter implements ExchangeAdapter {
|
|
|
20
20
|
init(): Promise<void>;
|
|
21
21
|
/** Load asset index map — supports native and HIP-3 dex. */
|
|
22
22
|
private _loadAssetMap;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a symbol to the canonical name in the asset map.
|
|
25
|
+
* Handles: "ICP" → "ICP-PERP", "BTC-PERP" → "BTC-PERP", "km:GOOGL" → "km:GOOGL"
|
|
26
|
+
*/
|
|
27
|
+
resolveSymbol(symbol: string): string;
|
|
23
28
|
getAssetIndex(symbol: string): number;
|
|
24
29
|
getMarkets(): Promise<ExchangeMarketInfo[]>;
|
|
25
30
|
getOrderbook(symbol: string): Promise<{
|
|
@@ -93,24 +93,34 @@ export class HyperliquidAdapter {
|
|
|
93
93
|
// non-critical
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Resolve a symbol to the canonical name in the asset map.
|
|
98
|
+
* Handles: "ICP" → "ICP-PERP", "BTC-PERP" → "BTC-PERP", "km:GOOGL" → "km:GOOGL"
|
|
99
|
+
*/
|
|
100
|
+
resolveSymbol(symbol) {
|
|
97
101
|
const sym = symbol.toUpperCase();
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (
|
|
101
|
-
return
|
|
102
|
-
|
|
102
|
+
if (this._assetMap.has(sym))
|
|
103
|
+
return sym;
|
|
104
|
+
if (this._assetMap.has(`${sym}-PERP`))
|
|
105
|
+
return `${sym}-PERP`;
|
|
106
|
+
if (sym.endsWith("-PERP") && this._assetMap.has(sym.replace(/-PERP$/, "")))
|
|
107
|
+
return sym.replace(/-PERP$/, "");
|
|
103
108
|
if (sym.includes(":")) {
|
|
104
109
|
const [prefix, base] = sym.split(":");
|
|
105
|
-
|
|
106
|
-
if (
|
|
107
|
-
return
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (idx !== undefined)
|
|
111
|
-
return idx;
|
|
110
|
+
const lower = `${prefix.toLowerCase()}:${base}`;
|
|
111
|
+
if (this._assetMap.has(lower))
|
|
112
|
+
return lower;
|
|
113
|
+
if (this._assetMap.has(base))
|
|
114
|
+
return base;
|
|
112
115
|
}
|
|
113
|
-
|
|
116
|
+
return sym; // return as-is, let downstream error
|
|
117
|
+
}
|
|
118
|
+
getAssetIndex(symbol) {
|
|
119
|
+
const resolved = this.resolveSymbol(symbol);
|
|
120
|
+
const idx = this._assetMap.get(resolved);
|
|
121
|
+
if (idx !== undefined)
|
|
122
|
+
return idx;
|
|
123
|
+
throw new Error(`Unknown asset: ${symbol}`);
|
|
114
124
|
}
|
|
115
125
|
async getMarkets() {
|
|
116
126
|
let universe;
|
|
@@ -589,14 +599,14 @@ export class HyperliquidAdapter {
|
|
|
589
599
|
* Uses SDK's built-in updateLeverage method.
|
|
590
600
|
*/
|
|
591
601
|
async updateLeverage(symbol, leverage, isCross = true) {
|
|
592
|
-
return this.sdk.exchange.updateLeverage(symbol, isCross ? "cross" : "isolated", leverage);
|
|
602
|
+
return this.sdk.exchange.updateLeverage(this.resolveSymbol(symbol), isCross ? "cross" : "isolated", leverage);
|
|
593
603
|
}
|
|
594
604
|
/**
|
|
595
605
|
* Update isolated margin for a position.
|
|
596
606
|
* amount > 0 to add margin, amount < 0 to remove
|
|
597
607
|
*/
|
|
598
608
|
async updateIsolatedMargin(symbol, amount) {
|
|
599
|
-
return this.sdk.exchange.updateIsolatedMargin(symbol, amount > 0, Math.round(Math.abs(amount) * 1e6));
|
|
609
|
+
return this.sdk.exchange.updateIsolatedMargin(this.resolveSymbol(symbol), amount > 0, Math.round(Math.abs(amount) * 1e6));
|
|
600
610
|
}
|
|
601
611
|
/**
|
|
602
612
|
* Withdraw from Hyperliquid L1 bridge.
|
package/package.json
CHANGED
package/skills/perp-cli/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ allowed-tools: "Bash(perp:*), Bash(npx perp-cli:*), Bash(npx -y perp-cli:*)"
|
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
7
|
author: hypurrquant
|
|
8
|
-
version: "0.3.
|
|
8
|
+
version: "0.3.9"
|
|
9
9
|
mcp-server: perp-cli
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -66,6 +66,12 @@ perp --json -e lighter ... # Lighter (Ethereum)
|
|
|
66
66
|
```
|
|
67
67
|
If a default exchange is set, `-e` can be omitted.
|
|
68
68
|
|
|
69
|
+
### Symbol naming
|
|
70
|
+
Symbols are auto-resolved across exchanges. Use bare symbols (e.g., `BTC`, `SOL`, `ICP`) everywhere — the CLI handles exchange-specific naming:
|
|
71
|
+
- **Hyperliquid**: `ICP` → `ICP-PERP` (auto-resolved, `-PERP` suffix added)
|
|
72
|
+
- **Pacifica / Lighter**: bare symbols as-is
|
|
73
|
+
- `arb scan` returns bare symbols — pass them directly to any exchange command.
|
|
74
|
+
|
|
69
75
|
### Common operations
|
|
70
76
|
```bash
|
|
71
77
|
perp --json wallet show # check configured wallets
|
|
@@ -73,10 +79,19 @@ perp --json -e hl account info # balance & margin
|
|
|
73
79
|
perp --json -e hl account positions # open positions
|
|
74
80
|
perp --json -e hl market list # available markets
|
|
75
81
|
perp --json -e hl market mid BTC # BTC mid price
|
|
76
|
-
perp --json arb
|
|
82
|
+
perp --json arb scan --min 5 # find funding arb opportunities (>5bps spread)
|
|
77
83
|
perp --json portfolio # unified multi-exchange view
|
|
78
84
|
```
|
|
79
85
|
|
|
86
|
+
### Funding arb direction (CRITICAL — do NOT reverse)
|
|
87
|
+
```
|
|
88
|
+
arb scan returns: longExch, shortExch, netSpread
|
|
89
|
+
→ ALWAYS follow longExch/shortExch exactly. NEVER reverse the direction.
|
|
90
|
+
→ NEVER enter if netSpread ≤ 0 (= loss after fees)
|
|
91
|
+
→ Positive funding = longs pay shorts → be SHORT to receive
|
|
92
|
+
→ Negative funding = shorts pay longs → be LONG to receive
|
|
93
|
+
```
|
|
94
|
+
|
|
80
95
|
### Trade execution (MANDATORY checklist)
|
|
81
96
|
```
|
|
82
97
|
BEFORE ANY TRADE:
|
|
@@ -98,6 +113,43 @@ BEFORE ANY TRADE:
|
|
|
98
113
|
9. perp --json -e <EX> account positions → verify result + check liquidation price
|
|
99
114
|
```
|
|
100
115
|
|
|
116
|
+
### Exchange-specific constraints
|
|
117
|
+
```
|
|
118
|
+
Minimum order values (notional, enforced by exchange):
|
|
119
|
+
- Hyperliquid: $10 minimum per order
|
|
120
|
+
- Pacifica: varies by symbol (usually ~$1)
|
|
121
|
+
- Lighter: varies by symbol
|
|
122
|
+
|
|
123
|
+
If your calculated size falls below the minimum, increase to meet it or skip the opportunity.
|
|
124
|
+
trade check returns valid: true/false but is ADVISORY — it does NOT block execution.
|
|
125
|
+
The exchange itself will reject orders below its minimum.
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Arb order sizing (CRITICAL — both legs MUST match)
|
|
129
|
+
```
|
|
130
|
+
For funding arb, BOTH legs must have the EXACT SAME SIZE. Size mismatch = directional exposure.
|
|
131
|
+
|
|
132
|
+
1. Check orderbook depth on BOTH exchanges:
|
|
133
|
+
perp --json -e <LONG_EX> market book <SYM> → asks (you're buying)
|
|
134
|
+
perp --json -e <SHORT_EX> market book <SYM> → bids (you're selling)
|
|
135
|
+
|
|
136
|
+
2. Compute ORDER_SIZE:
|
|
137
|
+
- fillable_long = sum of ask sizes at best 2-3 levels
|
|
138
|
+
- fillable_short = sum of bid sizes at best 2-3 levels
|
|
139
|
+
- ORDER_SIZE = min(fillable_long, fillable_short, desired_size)
|
|
140
|
+
- Must be ≥ BOTH exchanges' minimum order value (e.g. HL requires ≥$10 notional)
|
|
141
|
+
|
|
142
|
+
3. Execute BOTH legs with the SAME ORDER_SIZE:
|
|
143
|
+
perp --json -e <LONG_EX> trade market <SYM> buy <ORDER_SIZE>
|
|
144
|
+
→ verify fill via account positions
|
|
145
|
+
perp --json -e <SHORT_EX> trade market <SYM> sell <ORDER_SIZE>
|
|
146
|
+
→ verify fill via account positions
|
|
147
|
+
|
|
148
|
+
4. Confirm matched: both positions must show identical size.
|
|
149
|
+
If mismatch (partial fill), adjust the larger to match the smaller.
|
|
150
|
+
```
|
|
151
|
+
See `references/strategies.md` for detailed execution strategy (chunked orders, limit orders, failure handling).
|
|
152
|
+
|
|
101
153
|
### Post-entry monitoring (MANDATORY while positions are open)
|
|
102
154
|
```
|
|
103
155
|
Every 15 minutes:
|
|
@@ -106,7 +158,7 @@ Every 15 minutes:
|
|
|
106
158
|
perp --json -e <EX> account positions → check each position P&L
|
|
107
159
|
|
|
108
160
|
Every 1 hour (at funding settlement):
|
|
109
|
-
perp --json arb
|
|
161
|
+
perp --json arb scan --min 5 → is spread still profitable?
|
|
110
162
|
perp --json portfolio → total equity across exchanges
|
|
111
163
|
Compare both legs' unrealized P&L — they should roughly offset
|
|
112
164
|
|
|
@@ -78,7 +78,7 @@ perp --json bridge send --from solana --to arbitrum --amount 500
|
|
|
78
78
|
perp --json bridge status <orderId> # wait for completion
|
|
79
79
|
|
|
80
80
|
# 5. Verify both sides have balance, then start arb
|
|
81
|
-
perp --json arb
|
|
81
|
+
perp --json arb scan --min 5
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
### Lighter API Key Setup
|
|
@@ -229,7 +229,7 @@ Error case:
|
|
|
229
229
|
- `wallet show`, `wallet balance` — read-only
|
|
230
230
|
- `account info`, `account positions`, `account orders` — read-only
|
|
231
231
|
- `market list`, `market mid`, `market book` — read-only
|
|
232
|
-
- `arb
|
|
232
|
+
- `arb scan` — read-only (`arb rates` is deprecated)
|
|
233
233
|
- `portfolio`, `risk overview` — read-only
|
|
234
234
|
- `bridge quote` — read-only
|
|
235
235
|
- `bridge status` — read-only
|
|
@@ -242,6 +242,29 @@ Error case:
|
|
|
242
242
|
|
|
243
243
|
**For non-idempotent commands:** always verify the result before retrying. Check positions or balances to confirm whether the first attempt succeeded.
|
|
244
244
|
|
|
245
|
+
## Symbol Naming Across Exchanges
|
|
246
|
+
|
|
247
|
+
Symbols are auto-resolved by the CLI. **Always use bare symbols** (e.g., `BTC`, `SOL`, `ICP`) — the CLI handles exchange-specific naming automatically:
|
|
248
|
+
|
|
249
|
+
| Input | Hyperliquid | Pacifica | Lighter |
|
|
250
|
+
|-------|-------------|----------|---------|
|
|
251
|
+
| `ICP` | → `ICP-PERP` | → `ICP` | → `ICP` |
|
|
252
|
+
| `BTC` | → `BTC` | → `BTC` | → `BTC` |
|
|
253
|
+
| `SOL` | → `SOL` | → `SOL` | → `SOL` |
|
|
254
|
+
|
|
255
|
+
- `arb scan` returns bare symbols — pass them directly to trade/leverage commands on any exchange.
|
|
256
|
+
- Do NOT manually add `-PERP` suffix — the CLI resolves this automatically.
|
|
257
|
+
|
|
258
|
+
## Exchange-Specific Constraints
|
|
259
|
+
|
|
260
|
+
| Exchange | Min Order (notional) | Notes |
|
|
261
|
+
|----------|---------------------|-------|
|
|
262
|
+
| Hyperliquid | **$10** | Rejects orders below $10 notional |
|
|
263
|
+
| Pacifica | ~$1 (varies by symbol) | Lower minimums |
|
|
264
|
+
| Lighter | Varies by symbol | Check market info |
|
|
265
|
+
|
|
266
|
+
**`trade check` is advisory only** — it returns `valid: true/false` but does NOT block execution. The exchange itself enforces minimums and will reject with an error if the order is too small.
|
|
267
|
+
|
|
245
268
|
## Common Agent Mistakes
|
|
246
269
|
|
|
247
270
|
1. **Using `perp init`** — interactive, will hang forever. Use `wallet set` instead.
|
|
@@ -250,3 +273,5 @@ Error case:
|
|
|
250
273
|
4. **Retrying a trade without checking** — leads to double positions. Always check `account positions` after a trade, even if it seemed to fail.
|
|
251
274
|
5. **Bridging without quoting** — always run `bridge quote` first to show the user fees and estimated time.
|
|
252
275
|
6. **Assuming deposit is instant** — after `bridge send`, wait for `bridge status` to confirm completion before depositing to the destination exchange.
|
|
276
|
+
7. **Manually adding `-PERP` suffix** — the CLI auto-resolves symbols. Just use bare names like `ICP`, `SOL`, `BTC`.
|
|
277
|
+
8. **Order below exchange minimum** — Hyperliquid requires $10+ notional. Compute `size × price` before submitting.
|
|
@@ -89,11 +89,11 @@ perp --json bridge status <ORDER_ID>
|
|
|
89
89
|
|
|
90
90
|
## Arbitrage
|
|
91
91
|
```bash
|
|
92
|
-
perp --json arb
|
|
93
|
-
perp --json arb scan --min <BPS> # find opportunities (>N bps spread)
|
|
92
|
+
perp --json arb scan --min <BPS> # find opportunities (>N bps spread) — PRIMARY command
|
|
94
93
|
perp --json arb funding # detailed funding analysis
|
|
95
94
|
perp --json arb dex # HIP-3 cross-dex arb (Hyperliquid)
|
|
96
95
|
perp --json gap show # cross-exchange price gaps
|
|
96
|
+
# NOTE: 'arb rates' is deprecated — use 'arb scan' instead
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
## Wallet Management
|
|
@@ -9,7 +9,7 @@ You are not expected to follow rigid rules — use this as a decision framework
|
|
|
9
9
|
- Funding rates settle **every 1 hour** on all supported exchanges
|
|
10
10
|
- Positive rate = longs pay shorts, negative rate = shorts pay longs
|
|
11
11
|
- Rates are annualized in display but applied hourly: `hourly = annual / 8760`
|
|
12
|
-
- Scan
|
|
12
|
+
- Scan opportunities: `perp --json arb scan --min 5` (shows spread, longExch, shortExch, netSpread)
|
|
13
13
|
|
|
14
14
|
### Opportunity Cost Awareness
|
|
15
15
|
|
|
@@ -56,10 +56,29 @@ During transition, you are **unhedged**. Price can move against you. Factor this
|
|
|
56
56
|
|
|
57
57
|
### Discovery Loop
|
|
58
58
|
```bash
|
|
59
|
-
perp --json arb
|
|
60
|
-
|
|
59
|
+
perp --json arb scan --min 5 # find spreads > 5 bps (shows longExch/shortExch/netSpread)
|
|
60
|
+
# NOTE: 'arb rates' is deprecated — use 'arb scan' instead
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
### CRITICAL: Reading arb scan Results
|
|
64
|
+
|
|
65
|
+
The `arb scan` output tells you EXACTLY what to do:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
longExch: "hyperliquid" → open LONG on this exchange
|
|
69
|
+
shortExch: "pacifica" → open SHORT on this exchange
|
|
70
|
+
netSpread: 12.87 → profit after fees (bps/hour)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Rules:**
|
|
74
|
+
1. **ALWAYS follow longExch/shortExch directions exactly.** DO NOT reverse them.
|
|
75
|
+
2. **NEVER enter if netSpread ≤ 0.** Negative netSpread = loss after fees.
|
|
76
|
+
3. The direction logic: go LONG where funding is negative (longs receive), go SHORT where funding is positive (shorts receive).
|
|
77
|
+
|
|
78
|
+
**Why these directions?**
|
|
79
|
+
- Positive funding (+) = longs pay shorts → you want to be SHORT to receive
|
|
80
|
+
- Negative funding (-) = shorts pay longs → you want to be LONG to receive
|
|
81
|
+
|
|
63
82
|
### Decision Framework
|
|
64
83
|
When evaluating an arb opportunity:
|
|
65
84
|
|
|
@@ -118,53 +137,102 @@ Actual hold: 6h | Actual net: ~130 bps
|
|
|
118
137
|
perp --json portfolio # unified multi-exchange view
|
|
119
138
|
perp --json risk overview # cross-exchange risk assessment
|
|
120
139
|
perp --json -e <EX> account positions # per-exchange positions
|
|
121
|
-
perp --json arb
|
|
140
|
+
perp --json arb scan --min 5 # are current rates still favorable?
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Order Execution: Matched Size, Sequential Legs
|
|
144
|
+
|
|
145
|
+
**The #1 rule of arb execution: BOTH LEGS MUST HAVE THE EXACT SAME SIZE.** A size mismatch means you have net directional exposure — the whole point of arb is to be delta-neutral.
|
|
146
|
+
|
|
147
|
+
#### Step 1: Determine Matched Order Size
|
|
148
|
+
|
|
149
|
+
Before placing ANY order, compute a single `ORDER_SIZE` that BOTH exchanges can fill:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# 1. Check orderbook depth on BOTH sides
|
|
153
|
+
perp --json -e <LONG_EX> market book <SYM> # check asks (you're buying)
|
|
154
|
+
perp --json -e <SHORT_EX> market book <SYM> # check bids (you're selling)
|
|
155
|
+
|
|
156
|
+
# 2. Find immediately fillable size at best 2-3 ticks
|
|
157
|
+
# LONG side: sum ask sizes at best 2-3 ask levels → fillable_long
|
|
158
|
+
# SHORT side: sum bid sizes at best 2-3 bid levels → fillable_short
|
|
159
|
+
|
|
160
|
+
# 3. ORDER_SIZE = min(fillable_long, fillable_short, desired_size)
|
|
161
|
+
# The SMALLER side limits your matched size.
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Example:**
|
|
165
|
+
```
|
|
166
|
+
LONG exchange asks: $85.00 × 0.5, $85.01 × 0.3 → fillable = 0.8
|
|
167
|
+
SHORT exchange bids: $85.10 × 0.4, $85.09 × 0.2 → fillable = 0.6
|
|
168
|
+
Desired size: 1.0
|
|
169
|
+
|
|
170
|
+
→ ORDER_SIZE = min(0.8, 0.6, 1.0) = 0.6
|
|
171
|
+
→ Both legs get exactly 0.6
|
|
122
172
|
```
|
|
123
173
|
|
|
124
|
-
|
|
174
|
+
**CRITICAL: Each exchange has its own minimum order size and step size.**
|
|
175
|
+
```bash
|
|
176
|
+
perp --json -e <EX> market info <SYM> # check minOrderSize, stepSize
|
|
177
|
+
```
|
|
178
|
+
Round `ORDER_SIZE` to the coarser step size of the two exchanges. If the matched size falls below either exchange's minimum, the arb is NOT executable at this size — reduce target or skip.
|
|
179
|
+
|
|
180
|
+
#### Step 2: Execute Legs Sequentially with Same Size
|
|
181
|
+
|
|
182
|
+
Once you have a single `ORDER_SIZE`, execute both legs using THAT EXACT SIZE:
|
|
125
183
|
|
|
126
|
-
|
|
184
|
+
```bash
|
|
185
|
+
# Leg 1: Open on exchange A
|
|
186
|
+
perp --json -e <LONG_EX> trade market <SYM> buy <ORDER_SIZE>
|
|
187
|
+
|
|
188
|
+
# Verify leg 1 filled
|
|
189
|
+
perp --json -e <LONG_EX> account positions
|
|
127
190
|
|
|
128
|
-
|
|
129
|
-
|
|
191
|
+
# Leg 2: Open on exchange B with SAME size
|
|
192
|
+
perp --json -e <SHORT_EX> trade market <SYM> sell <ORDER_SIZE>
|
|
130
193
|
|
|
131
|
-
|
|
132
|
-
|
|
194
|
+
# Verify leg 2 filled
|
|
195
|
+
perp --json -e <SHORT_EX> account positions
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**After both legs, verify sizes match:**
|
|
133
199
|
```bash
|
|
134
|
-
perp --json -e <
|
|
135
|
-
perp --json -e <
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
4.
|
|
154
|
-
|
|
155
|
-
#### Paired Execution Rule
|
|
156
|
-
Each chunk must be a **matched pair**: close on one side, open on the other. Never execute multiple closes without the corresponding opens. If one leg fails:
|
|
157
|
-
- STOP immediately
|
|
158
|
-
- Assess your current exposure
|
|
159
|
-
- Decide whether to retry the failed leg or unwind the completed leg
|
|
160
|
-
- Do NOT continue with remaining chunks
|
|
200
|
+
perp --json -e <LONG_EX> account positions # size = X
|
|
201
|
+
perp --json -e <SHORT_EX> account positions # size must = X
|
|
202
|
+
```
|
|
203
|
+
If sizes differ (partial fill, rounding), immediately adjust the larger position to match the smaller one.
|
|
204
|
+
|
|
205
|
+
#### Step 3: Split Large Orders into Matched Chunks
|
|
206
|
+
|
|
207
|
+
If your desired size exceeds what the orderbooks can absorb at best ticks, split into chunks — but **every chunk must be a matched pair with identical size on both sides**:
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
Chunk 1: buy 0.3 on Exchange A → sell 0.3 on Exchange B → verify both
|
|
211
|
+
Chunk 2: buy 0.3 on Exchange A → sell 0.3 on Exchange B → verify both
|
|
212
|
+
... repeat until full size is executed
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Rules:**
|
|
216
|
+
1. Re-check orderbook depth between chunks — liquidity changes
|
|
217
|
+
2. Each chunk: same size on both sides, no exceptions
|
|
218
|
+
3. If one leg fails → STOP immediately, do NOT continue
|
|
219
|
+
4. Assess exposure: retry the failed leg, or unwind the completed leg
|
|
220
|
+
5. NEVER execute multiple orders on one side without matching the other
|
|
161
221
|
|
|
162
222
|
#### Using Limit Orders for Better Execution
|
|
163
|
-
For non-urgent
|
|
223
|
+
For non-urgent entries, use limit orders at best bid/ask instead of market:
|
|
224
|
+
```bash
|
|
225
|
+
perp --json -e <EX> trade buy <SYM> <ORDER_SIZE> -p <PRICE> # limit order
|
|
226
|
+
```
|
|
227
|
+
This avoids crossing the spread but risks not getting filled. Set a timeout and fall back to market if unfilled. **Both legs must still use the same `ORDER_SIZE`.**
|
|
228
|
+
|
|
229
|
+
#### Closing Arb Positions: Same Rules Apply
|
|
230
|
+
When exiting, close BOTH legs with the SAME size:
|
|
164
231
|
```bash
|
|
165
|
-
perp --json -e <
|
|
232
|
+
perp --json -e <LONG_EX> trade close <SYM> # closes full position
|
|
233
|
+
perp --json -e <SHORT_EX> trade close <SYM> # closes full position
|
|
166
234
|
```
|
|
167
|
-
|
|
235
|
+
If positions already have mismatched sizes, close to the smaller size first, then close the remaining delta on the larger side.
|
|
168
236
|
|
|
169
237
|
### When to Exit
|
|
170
238
|
- Spread compressed below your breakeven (including fees)
|
|
@@ -287,7 +355,7 @@ Check the output:
|
|
|
287
355
|
#### Every hour (at funding settlement):
|
|
288
356
|
```bash
|
|
289
357
|
perp --json portfolio # total equity across exchanges
|
|
290
|
-
perp --json arb
|
|
358
|
+
perp --json arb scan --min 5 # are rates still favorable?
|
|
291
359
|
perp --json -e <EX_A> account positions # P&L on leg A
|
|
292
360
|
perp --json -e <EX_B> account positions # P&L on leg B
|
|
293
361
|
```
|
|
@@ -11,9 +11,5 @@ echo "=== Funding Rate Arbitrage Scanner ==="
|
|
|
11
11
|
echo "Minimum spread: ${MIN_SPREAD} bps"
|
|
12
12
|
echo ""
|
|
13
13
|
|
|
14
|
-
echo "1.
|
|
15
|
-
perp --json arb rates
|
|
16
|
-
|
|
17
|
-
echo ""
|
|
18
|
-
echo "2. Opportunities (>= ${MIN_SPREAD} bps):"
|
|
14
|
+
echo "1. Opportunities (>= ${MIN_SPREAD} bps):"
|
|
19
15
|
perp --json arb scan --min "$MIN_SPREAD"
|