openbroker 1.0.54 → 1.0.56
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/CHANGELOG.md +19 -0
- package/SKILL.md +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/core/client.ts +155 -33
- package/scripts/info/account.ts +9 -0
- package/scripts/operations/bracket.ts +4 -2
- package/scripts/operations/chase.ts +3 -1
- package/scripts/operations/limit-order.ts +4 -1
- package/scripts/operations/market-order.ts +4 -1
- package/scripts/operations/scale.ts +4 -1
- package/scripts/operations/trigger-order.ts +4 -1
- package/scripts/operations/twap.ts +3 -1
- package/scripts/plugin/tools.ts +17 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Open Broker will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.0.56] - 2026-03-10
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Unified Account Support**: Detects and handles Hyperliquid's account abstraction modes (standard, unified, portfolio margin, dex abstraction)
|
|
9
|
+
- `getAccountMode()` / `isUnifiedAccount()` — queries `userAbstraction` API to detect mode
|
|
10
|
+
- **Unified accounts**: equity comes from `spotClearinghouseState` (single USDC balance shared across all dexes); skips `sendAsset` transfers for HIP-3 trading
|
|
11
|
+
- **Standard accounts**: keeps existing behavior (per-dex margin aggregation + USDC transfers)
|
|
12
|
+
- `account` command now displays the account abstraction mode
|
|
13
|
+
- Prepares for deprecation of DEX abstraction mode per Hyperliquid docs
|
|
14
|
+
|
|
15
|
+
## [1.0.55] - 2026-03-09
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- **Leverage Control**: All trading commands now support `--leverage` flag to set leverage when opening positions
|
|
19
|
+
- Main perps: sets cross margin leverage before ordering
|
|
20
|
+
- HIP-3 perps: sets isolated margin at specified leverage (capped at asset's max)
|
|
21
|
+
- CLI: `--leverage 10` on `market`, `limit`, `trigger`, `bracket`, `chase`, `twap`, `scale`
|
|
22
|
+
- Plugin: `leverage` parameter on `ob_buy`, `ob_sell`, `ob_limit`, `ob_twap`, `ob_bracket`, `ob_chase`
|
|
23
|
+
|
|
5
24
|
## [1.0.54] - 2026-03-09
|
|
6
25
|
|
|
7
26
|
### Fixed
|
package/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Hyperliquid trading plugin with background position monitoring. Exe
|
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires Node.js 22+, network access to api.hyperliquid.xyz
|
|
6
6
|
homepage: https://www.npmjs.com/package/openbroker
|
|
7
|
-
metadata: {"author": "monemetrics", "version": "1.0.
|
|
7
|
+
metadata: {"author": "monemetrics", "version": "1.0.56", "openclaw": {"requires": {"bins": ["openbroker"], "env": ["HYPERLIQUID_PRIVATE_KEY"]}, "primaryEnv": "HYPERLIQUID_PRIVATE_KEY", "install": [{"id": "node", "kind": "node", "package": "openbroker", "bins": ["openbroker"], "label": "Install openbroker (npm)"}]}}
|
|
8
8
|
allowed-tools: ob_account ob_positions ob_funding ob_markets ob_search ob_spot ob_fills ob_orders ob_order_status ob_fees ob_candles ob_funding_history ob_trades ob_rate_limit ob_funding_scan ob_buy ob_sell ob_limit ob_trigger ob_tpsl ob_cancel ob_twap ob_bracket ob_chase ob_watcher_status Bash(openbroker:*)
|
|
9
9
|
---
|
|
10
10
|
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/scripts/core/client.ts
CHANGED
|
@@ -35,6 +35,8 @@ export class HyperliquidClient {
|
|
|
35
35
|
private hip3IsolatedSet: Set<string> = new Set();
|
|
36
36
|
/** Cached maxLeverage for HIP-3 assets */
|
|
37
37
|
private hip3MaxLeverageMap: Map<string, number> = new Map();
|
|
38
|
+
/** Cached account abstraction mode: 'standard' | 'unified' | 'portfolio' | 'dexAbstraction' */
|
|
39
|
+
private accountMode: string | null = null;
|
|
38
40
|
public verbose: boolean = false;
|
|
39
41
|
|
|
40
42
|
constructor(config?: OpenBrokerConfig) {
|
|
@@ -631,6 +633,57 @@ export class HyperliquidClient {
|
|
|
631
633
|
|
|
632
634
|
// ============ Account Info ============
|
|
633
635
|
|
|
636
|
+
/**
|
|
637
|
+
* Get the account's abstraction mode.
|
|
638
|
+
* Returns: 'standard' | 'unified' | 'portfolio' | 'dexAbstraction'
|
|
639
|
+
* Unified accounts have a single USDC balance shared across all dexes.
|
|
640
|
+
* Standard accounts have separate balances per dex (need sendAsset transfers).
|
|
641
|
+
*/
|
|
642
|
+
async getAccountMode(user?: string): Promise<string> {
|
|
643
|
+
if (this.accountMode) return this.accountMode;
|
|
644
|
+
|
|
645
|
+
const baseUrl = isMainnet()
|
|
646
|
+
? 'https://api.hyperliquid.xyz'
|
|
647
|
+
: 'https://api.hyperliquid-testnet.xyz';
|
|
648
|
+
|
|
649
|
+
try {
|
|
650
|
+
const response = await fetch(baseUrl + '/info', {
|
|
651
|
+
method: 'POST',
|
|
652
|
+
headers: { 'Content-Type': 'application/json' },
|
|
653
|
+
body: JSON.stringify({
|
|
654
|
+
type: 'userAbstraction',
|
|
655
|
+
user: user ?? this.address,
|
|
656
|
+
}),
|
|
657
|
+
});
|
|
658
|
+
const data = await response.json();
|
|
659
|
+
this.log('userAbstraction response:', data);
|
|
660
|
+
|
|
661
|
+
// API returns: "default" | "disabled" | "dexAbstraction" | "unifiedAccount" | "portfolioMargin"
|
|
662
|
+
if (data === 'unifiedAccount') {
|
|
663
|
+
this.accountMode = 'unified';
|
|
664
|
+
} else if (data === 'portfolioMargin') {
|
|
665
|
+
this.accountMode = 'portfolio';
|
|
666
|
+
} else if (data === 'dexAbstraction') {
|
|
667
|
+
this.accountMode = 'dexAbstraction';
|
|
668
|
+
} else {
|
|
669
|
+
// "default" or "disabled" both mean standard mode
|
|
670
|
+
this.accountMode = 'standard';
|
|
671
|
+
}
|
|
672
|
+
} catch (err) {
|
|
673
|
+
this.log('Failed to fetch account abstraction mode:', err instanceof Error ? err.message : String(err));
|
|
674
|
+
this.accountMode = 'standard'; // Safe fallback
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
this.log('Account mode:', this.accountMode);
|
|
678
|
+
return this.accountMode;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/** Whether the account uses unified balances (unified or portfolio margin) */
|
|
682
|
+
async isUnifiedAccount(user?: string): Promise<boolean> {
|
|
683
|
+
const mode = await this.getAccountMode(user);
|
|
684
|
+
return mode === 'unified' || mode === 'portfolio';
|
|
685
|
+
}
|
|
686
|
+
|
|
634
687
|
/**
|
|
635
688
|
* Check if an address has sub-accounts (is a master account)
|
|
636
689
|
* Sub-accounts cannot approve builder fees - only master accounts can
|
|
@@ -1075,14 +1128,17 @@ export class HyperliquidClient {
|
|
|
1075
1128
|
|
|
1076
1129
|
/**
|
|
1077
1130
|
* Get user state across all dexes (main + HIP-3).
|
|
1078
|
-
*
|
|
1131
|
+
* For unified accounts: equity comes from spotClearinghouseState (single USDC balance).
|
|
1132
|
+
* For standard accounts: aggregates margin summaries from each dex.
|
|
1079
1133
|
*/
|
|
1080
1134
|
async getUserStateAll(user?: string): Promise<ClearinghouseState> {
|
|
1081
1135
|
await this.getMetaAndAssetCtxs(); // Ensure HIP-3 dex list is loaded
|
|
1082
1136
|
|
|
1137
|
+
const unified = await this.isUnifiedAccount(user);
|
|
1083
1138
|
const mainState = await this.getUserState(user);
|
|
1084
1139
|
const dexs = await this.getPerpDexs();
|
|
1085
1140
|
|
|
1141
|
+
// Collect positions from all HIP-3 dexes
|
|
1086
1142
|
for (let i = 1; i < dexs.length; i++) {
|
|
1087
1143
|
const dex = dexs[i];
|
|
1088
1144
|
if (!dex) continue;
|
|
@@ -1093,24 +1149,63 @@ export class HyperliquidClient {
|
|
|
1093
1149
|
mainState.assetPositions.push(...dexState.assetPositions);
|
|
1094
1150
|
}
|
|
1095
1151
|
|
|
1096
|
-
//
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1152
|
+
// For standard accounts, aggregate margin from each dex
|
|
1153
|
+
if (!unified) {
|
|
1154
|
+
const dexMargin = dexState.marginSummary;
|
|
1155
|
+
if (dexMargin) {
|
|
1156
|
+
const addToSummary = (summary: { accountValue: string; totalNtlPos: string; totalRawUsd: string; totalMarginUsed: string; withdrawable: string }) => {
|
|
1157
|
+
summary.accountValue = String(parseFloat(summary.accountValue) + parseFloat(dexMargin.accountValue));
|
|
1158
|
+
summary.totalNtlPos = String(parseFloat(summary.totalNtlPos) + parseFloat(dexMargin.totalNtlPos));
|
|
1159
|
+
summary.totalRawUsd = String(parseFloat(summary.totalRawUsd) + parseFloat(dexMargin.totalRawUsd));
|
|
1160
|
+
summary.totalMarginUsed = String(parseFloat(summary.totalMarginUsed) + parseFloat(dexMargin.totalMarginUsed));
|
|
1161
|
+
summary.withdrawable = String(parseFloat(summary.withdrawable) + parseFloat(dexMargin.withdrawable));
|
|
1162
|
+
};
|
|
1163
|
+
addToSummary(mainState.marginSummary);
|
|
1164
|
+
addToSummary(mainState.crossMarginSummary);
|
|
1165
|
+
}
|
|
1108
1166
|
}
|
|
1109
1167
|
} catch (err) {
|
|
1110
1168
|
this.log(`Failed to fetch state for dex ${dex.name}:`, err instanceof Error ? err.message : String(err));
|
|
1111
1169
|
}
|
|
1112
1170
|
}
|
|
1113
1171
|
|
|
1172
|
+
// For unified accounts: equity is the USDC balance from spot clearinghouse
|
|
1173
|
+
if (unified) {
|
|
1174
|
+
try {
|
|
1175
|
+
const spotState = await this.getSpotBalances(user);
|
|
1176
|
+
const usdcBalance = spotState.balances.find(b => b.coin === 'USDC');
|
|
1177
|
+
if (usdcBalance) {
|
|
1178
|
+
const totalUsdc = usdcBalance.total;
|
|
1179
|
+
const holdUsdc = usdcBalance.hold;
|
|
1180
|
+
const withdrawable = String(parseFloat(totalUsdc) - parseFloat(holdUsdc));
|
|
1181
|
+
|
|
1182
|
+
// Compute total margin used and notional from all positions
|
|
1183
|
+
let totalMarginUsed = 0;
|
|
1184
|
+
let totalNtlPos = 0;
|
|
1185
|
+
for (const ap of mainState.assetPositions) {
|
|
1186
|
+
const pos = ap.position;
|
|
1187
|
+
if (parseFloat(pos.szi) === 0) continue;
|
|
1188
|
+
totalMarginUsed += parseFloat(pos.marginUsed);
|
|
1189
|
+
totalNtlPos += Math.abs(parseFloat(pos.positionValue));
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
const summary = {
|
|
1193
|
+
accountValue: totalUsdc,
|
|
1194
|
+
totalNtlPos: String(totalNtlPos),
|
|
1195
|
+
totalRawUsd: totalUsdc,
|
|
1196
|
+
totalMarginUsed: String(totalMarginUsed),
|
|
1197
|
+
withdrawable,
|
|
1198
|
+
};
|
|
1199
|
+
mainState.marginSummary = summary;
|
|
1200
|
+
mainState.crossMarginSummary = { ...summary };
|
|
1201
|
+
|
|
1202
|
+
this.log(`Unified account: USDC balance $${parseFloat(totalUsdc).toFixed(2)}, margin used $${totalMarginUsed.toFixed(2)}`);
|
|
1203
|
+
}
|
|
1204
|
+
} catch (err) {
|
|
1205
|
+
this.log('Failed to fetch spot balances for unified account:', err instanceof Error ? err.message : String(err));
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1114
1209
|
return mainState;
|
|
1115
1210
|
}
|
|
1116
1211
|
|
|
@@ -1123,22 +1218,25 @@ export class HyperliquidClient {
|
|
|
1123
1218
|
// ============ Trading ============
|
|
1124
1219
|
|
|
1125
1220
|
/**
|
|
1126
|
-
* HIP-3 perps
|
|
1221
|
+
* HIP-3 perps: prepare for trading.
|
|
1127
1222
|
* 1. Set isolated margin mode (required for HIP-3)
|
|
1128
|
-
* 2.
|
|
1223
|
+
* 2. For standard accounts only: transfer USDC from main perp to HIP-3 dex
|
|
1224
|
+
* (unified accounts share USDC across all dexes automatically)
|
|
1129
1225
|
*/
|
|
1130
|
-
private async ensureHip3Ready(coin: string, notional: number): Promise<void> {
|
|
1226
|
+
private async ensureHip3Ready(coin: string, notional: number, leverage?: number): Promise<void> {
|
|
1131
1227
|
if (!this.isHip3(coin)) return;
|
|
1132
1228
|
|
|
1133
1229
|
const dexInfo = this.coinDexMap.get(coin);
|
|
1134
1230
|
if (!dexInfo?.dexName) return;
|
|
1135
1231
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1232
|
+
const maxLev = this.hip3MaxLeverageMap.get(coin) ?? 10;
|
|
1233
|
+
const effectiveLev = Math.min(leverage ?? maxLev, maxLev);
|
|
1234
|
+
|
|
1235
|
+
// Set isolated margin on first order per asset, or when leverage changes
|
|
1236
|
+
if (!this.hip3IsolatedSet.has(coin) || leverage) {
|
|
1237
|
+
this.log(`HIP-3 asset ${coin} (dex: ${dexInfo.dexName}) — setting isolated margin at ${effectiveLev}x`);
|
|
1140
1238
|
try {
|
|
1141
|
-
await this.updateLeverage(coin,
|
|
1239
|
+
await this.updateLeverage(coin, effectiveLev, false); // false = isolated
|
|
1142
1240
|
this.hip3IsolatedSet.add(coin);
|
|
1143
1241
|
} catch (err) {
|
|
1144
1242
|
this.log(`Failed to set isolated margin for ${coin}:`, err instanceof Error ? err.message : String(err));
|
|
@@ -1146,13 +1244,19 @@ export class HyperliquidClient {
|
|
|
1146
1244
|
}
|
|
1147
1245
|
}
|
|
1148
1246
|
|
|
1149
|
-
//
|
|
1150
|
-
const
|
|
1151
|
-
|
|
1247
|
+
// Unified accounts share USDC across all dexes — no transfer needed
|
|
1248
|
+
const unified = await this.isUnifiedAccount();
|
|
1249
|
+
if (unified) {
|
|
1250
|
+
this.log(`Unified account — skipping USDC transfer for ${coin} (shared balance)`);
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
// Standard accounts: transfer USDC to the HIP-3 dex to cover margin
|
|
1255
|
+
const requiredMargin = notional / effectiveLev;
|
|
1152
1256
|
// Add 20% buffer for fees and slippage
|
|
1153
1257
|
const transferAmount = Math.ceil(requiredMargin * 1.2 * 100) / 100;
|
|
1154
1258
|
|
|
1155
|
-
this.log(`HIP-3 margin transfer: ${transferAmount} USDC from main → ${dexInfo.dexName} (notional: ${notional}, leverage: ${
|
|
1259
|
+
this.log(`HIP-3 margin transfer: ${transferAmount} USDC from main → ${dexInfo.dexName} (notional: ${notional}, leverage: ${effectiveLev}x)`);
|
|
1156
1260
|
try {
|
|
1157
1261
|
await this.exchange.sendAsset({
|
|
1158
1262
|
destination: this.address as `0x${string}`,
|
|
@@ -1175,13 +1279,20 @@ export class HyperliquidClient {
|
|
|
1175
1279
|
price: number,
|
|
1176
1280
|
orderType: { limit: { tif: 'Gtc' | 'Ioc' | 'Alo' } },
|
|
1177
1281
|
reduceOnly: boolean = false,
|
|
1178
|
-
includeBuilder: boolean = true
|
|
1282
|
+
includeBuilder: boolean = true,
|
|
1283
|
+
leverage?: number
|
|
1179
1284
|
): Promise<OrderResponse> {
|
|
1180
1285
|
this.requireTrading();
|
|
1181
1286
|
await this.getMetaAndAssetCtxs();
|
|
1182
1287
|
|
|
1288
|
+
// Set leverage if specified (for main perps, cross margin; for HIP-3, handled in ensureHip3Ready)
|
|
1289
|
+
if (leverage && !this.isHip3(coin)) {
|
|
1290
|
+
this.log(`Setting leverage for ${coin} to ${leverage}x cross`);
|
|
1291
|
+
await this.updateLeverage(coin, leverage, true);
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1183
1294
|
// HIP-3 perps: set isolated margin + transfer USDC to dex
|
|
1184
|
-
await this.ensureHip3Ready(coin, size * price);
|
|
1295
|
+
await this.ensureHip3Ready(coin, size * price, leverage);
|
|
1185
1296
|
|
|
1186
1297
|
const assetIndex = this.getAssetIndex(coin);
|
|
1187
1298
|
const szDecimals = this.getSzDecimals(coin);
|
|
@@ -1230,7 +1341,8 @@ export class HyperliquidClient {
|
|
|
1230
1341
|
coin: string,
|
|
1231
1342
|
isBuy: boolean,
|
|
1232
1343
|
size: number,
|
|
1233
|
-
slippageBps?: number
|
|
1344
|
+
slippageBps?: number,
|
|
1345
|
+
leverage?: number
|
|
1234
1346
|
): Promise<OrderResponse> {
|
|
1235
1347
|
await this.getMetaAndAssetCtxs();
|
|
1236
1348
|
|
|
@@ -1256,7 +1368,8 @@ export class HyperliquidClient {
|
|
|
1256
1368
|
limitPrice,
|
|
1257
1369
|
{ limit: { tif: 'Ioc' } },
|
|
1258
1370
|
false,
|
|
1259
|
-
true
|
|
1371
|
+
true,
|
|
1372
|
+
leverage
|
|
1260
1373
|
);
|
|
1261
1374
|
}
|
|
1262
1375
|
|
|
@@ -1266,7 +1379,8 @@ export class HyperliquidClient {
|
|
|
1266
1379
|
size: number,
|
|
1267
1380
|
price: number,
|
|
1268
1381
|
tif: 'Gtc' | 'Ioc' | 'Alo' = 'Gtc',
|
|
1269
|
-
reduceOnly: boolean = false
|
|
1382
|
+
reduceOnly: boolean = false,
|
|
1383
|
+
leverage?: number
|
|
1270
1384
|
): Promise<OrderResponse> {
|
|
1271
1385
|
return this.order(
|
|
1272
1386
|
coin,
|
|
@@ -1275,7 +1389,8 @@ export class HyperliquidClient {
|
|
|
1275
1389
|
price,
|
|
1276
1390
|
{ limit: { tif } },
|
|
1277
1391
|
reduceOnly,
|
|
1278
|
-
true
|
|
1392
|
+
true,
|
|
1393
|
+
leverage
|
|
1279
1394
|
);
|
|
1280
1395
|
}
|
|
1281
1396
|
|
|
@@ -1296,13 +1411,20 @@ export class HyperliquidClient {
|
|
|
1296
1411
|
triggerPrice: number,
|
|
1297
1412
|
limitPrice: number,
|
|
1298
1413
|
tpsl: 'tp' | 'sl',
|
|
1299
|
-
reduceOnly: boolean = true
|
|
1414
|
+
reduceOnly: boolean = true,
|
|
1415
|
+
leverage?: number
|
|
1300
1416
|
): Promise<OrderResponse> {
|
|
1301
1417
|
this.requireTrading();
|
|
1302
1418
|
await this.getMetaAndAssetCtxs();
|
|
1303
1419
|
|
|
1420
|
+
// Set leverage if specified (for main perps)
|
|
1421
|
+
if (leverage && !this.isHip3(coin)) {
|
|
1422
|
+
this.log(`Setting leverage for ${coin} to ${leverage}x cross`);
|
|
1423
|
+
await this.updateLeverage(coin, leverage, true);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1304
1426
|
// HIP-3 perps: set isolated margin + transfer USDC to dex
|
|
1305
|
-
await this.ensureHip3Ready(coin, size * limitPrice);
|
|
1427
|
+
await this.ensureHip3Ready(coin, size * limitPrice, leverage);
|
|
1306
1428
|
|
|
1307
1429
|
const assetIndex = this.getAssetIndex(coin);
|
|
1308
1430
|
const szDecimals = this.getSzDecimals(coin);
|
package/scripts/info/account.ts
CHANGED
|
@@ -17,6 +17,15 @@ async function main() {
|
|
|
17
17
|
console.log(`Signing Wallet: ${client.walletAddress}`);
|
|
18
18
|
console.log(`Wallet Type: ${client.isApiWallet ? 'API Wallet' : 'Main Wallet'}`);
|
|
19
19
|
|
|
20
|
+
const accountMode = await client.getAccountMode();
|
|
21
|
+
const modeLabel: Record<string, string> = {
|
|
22
|
+
standard: 'Standard (separate balances per dex)',
|
|
23
|
+
unified: 'Unified Account (shared USDC across all dexes)',
|
|
24
|
+
portfolio: 'Portfolio Margin',
|
|
25
|
+
dexAbstraction: 'DEX Abstraction (deprecated)',
|
|
26
|
+
};
|
|
27
|
+
console.log(`Account Mode: ${modeLabel[accountMode] ?? accountMode}`);
|
|
28
|
+
|
|
20
29
|
// Check builder fee approval
|
|
21
30
|
const builderApproval = await client.getMaxBuilderFee();
|
|
22
31
|
console.log(`Builder Address: ${client.builderAddress}`);
|
|
@@ -24,6 +24,7 @@ Options:
|
|
|
24
24
|
--tp Take profit distance in % from entry
|
|
25
25
|
--sl Stop loss distance in % from entry
|
|
26
26
|
--slippage Slippage for market entry in bps (default: 50)
|
|
27
|
+
--leverage Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3
|
|
27
28
|
--dry Dry run - show bracket plan without executing
|
|
28
29
|
|
|
29
30
|
Take Profit / Stop Loss:
|
|
@@ -66,6 +67,7 @@ async function main() {
|
|
|
66
67
|
const tpPct = parseFloat(args.tp as string);
|
|
67
68
|
const slPct = parseFloat(args.sl as string);
|
|
68
69
|
const slippage = args.slippage ? parseInt(args.slippage as string) : undefined;
|
|
70
|
+
const leverage = args.leverage ? parseInt(args.leverage as string) : undefined;
|
|
69
71
|
const dryRun = args.dry as boolean;
|
|
70
72
|
|
|
71
73
|
if (!coin || !side || isNaN(size) || isNaN(tpPct) || isNaN(slPct)) {
|
|
@@ -171,7 +173,7 @@ async function main() {
|
|
|
171
173
|
let actualEntry = entry;
|
|
172
174
|
|
|
173
175
|
if (entryType === 'market') {
|
|
174
|
-
const entryResponse = await client.marketOrder(coin, isLong, size, slippage);
|
|
176
|
+
const entryResponse = await client.marketOrder(coin, isLong, size, slippage, leverage);
|
|
175
177
|
|
|
176
178
|
if (entryResponse.status === 'ok' && entryResponse.response && typeof entryResponse.response === 'object') {
|
|
177
179
|
const status = entryResponse.response.data.statuses[0];
|
|
@@ -189,7 +191,7 @@ async function main() {
|
|
|
189
191
|
process.exit(1);
|
|
190
192
|
}
|
|
191
193
|
} else {
|
|
192
|
-
const entryResponse = await client.limitOrder(coin, isLong, size, entry, 'Gtc', false);
|
|
194
|
+
const entryResponse = await client.limitOrder(coin, isLong, size, entry, 'Gtc', false, leverage);
|
|
193
195
|
|
|
194
196
|
if (entryResponse.status === 'ok' && entryResponse.response && typeof entryResponse.response === 'object') {
|
|
195
197
|
const status = entryResponse.response.data.statuses[0];
|
|
@@ -23,6 +23,7 @@ Options:
|
|
|
23
23
|
--timeout Max time to chase in seconds (default: 300 = 5 min)
|
|
24
24
|
--interval Update interval in ms (default: 2000)
|
|
25
25
|
--max-chase Max price to chase to in bps from start (default: 100 = 1%)
|
|
26
|
+
--leverage Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3
|
|
26
27
|
--reduce Reduce-only order
|
|
27
28
|
--dry Dry run - show chase parameters without executing
|
|
28
29
|
|
|
@@ -54,6 +55,7 @@ async function main() {
|
|
|
54
55
|
const timeoutSec = args.timeout ? parseInt(args.timeout as string) : 300;
|
|
55
56
|
const intervalMs = args.interval ? parseInt(args.interval as string) : 2000;
|
|
56
57
|
const maxChaseBps = args['max-chase'] ? parseInt(args['max-chase'] as string) : 100;
|
|
58
|
+
const leverage = args.leverage ? parseInt(args.leverage as string) : undefined;
|
|
57
59
|
const reduceOnly = args.reduce as boolean;
|
|
58
60
|
const dryRun = args.dry as boolean;
|
|
59
61
|
|
|
@@ -168,7 +170,7 @@ async function main() {
|
|
|
168
170
|
// Place new order
|
|
169
171
|
process.stdout.write(`[${iteration}] Mid: ${formatUsd(currentMid)} → Order: ${formatUsd(orderPrice)}... `);
|
|
170
172
|
|
|
171
|
-
const response = await client.limitOrder(coin, isBuy, size, orderPrice, 'Alo', reduceOnly);
|
|
173
|
+
const response = await client.limitOrder(coin, isBuy, size, orderPrice, 'Alo', reduceOnly, leverage);
|
|
172
174
|
|
|
173
175
|
if (response.status === 'ok' && response.response && typeof response.response === 'object') {
|
|
174
176
|
const status = response.response.data.statuses[0];
|
|
@@ -23,6 +23,7 @@ Options:
|
|
|
23
23
|
GTC = Good Till Cancel (rests on book)
|
|
24
24
|
IOC = Immediate Or Cancel (fills or cancels)
|
|
25
25
|
ALO = Add Liquidity Only (post-only, maker only)
|
|
26
|
+
--leverage Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3
|
|
26
27
|
--reduce Reduce-only order (default: false)
|
|
27
28
|
--dry Dry run - show order details without executing
|
|
28
29
|
|
|
@@ -42,6 +43,7 @@ async function main() {
|
|
|
42
43
|
const size = parseFloat(args.size as string);
|
|
43
44
|
const price = parseFloat(args.price as string);
|
|
44
45
|
const tifArg = (args.tif as string)?.toUpperCase() || 'GTC';
|
|
46
|
+
const leverage = args.leverage ? parseInt(args.leverage as string) : undefined;
|
|
45
47
|
const reduceOnly = args.reduce as boolean;
|
|
46
48
|
const dryRun = args.dry as boolean;
|
|
47
49
|
|
|
@@ -107,6 +109,7 @@ async function main() {
|
|
|
107
109
|
console.log(`Notional: ${formatUsd(notional)}`);
|
|
108
110
|
console.log(`Time in Force: ${tif}`);
|
|
109
111
|
console.log(`Reduce Only: ${reduceOnly ? 'Yes' : 'No'}`);
|
|
112
|
+
if (leverage) console.log(`Leverage: ${leverage}x`);
|
|
110
113
|
console.log(`Builder Fee: ${client.builderInfo.f / 10} bps`);
|
|
111
114
|
|
|
112
115
|
// Warning if order would be aggressively priced
|
|
@@ -121,7 +124,7 @@ async function main() {
|
|
|
121
124
|
|
|
122
125
|
console.log('\nSubmitting...');
|
|
123
126
|
|
|
124
|
-
const response = await client.limitOrder(coin, isBuy, size, price, tif, reduceOnly);
|
|
127
|
+
const response = await client.limitOrder(coin, isBuy, size, price, tif, reduceOnly, leverage);
|
|
125
128
|
|
|
126
129
|
console.log('\nResult');
|
|
127
130
|
console.log('------');
|
|
@@ -19,6 +19,7 @@ Options:
|
|
|
19
19
|
--side Order side: buy or sell
|
|
20
20
|
--size Order size in base asset
|
|
21
21
|
--slippage Slippage tolerance in bps (default: from config, usually 50 = 0.5%)
|
|
22
|
+
--leverage Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3
|
|
22
23
|
--reduce Reduce-only order (default: false)
|
|
23
24
|
--dry Dry run - show order details without executing
|
|
24
25
|
--verbose Show full API request/response for debugging
|
|
@@ -44,6 +45,7 @@ async function main() {
|
|
|
44
45
|
const side = args.side as string;
|
|
45
46
|
const size = parseFloat(args.size as string);
|
|
46
47
|
const slippage = args.slippage ? parseInt(args.slippage as string) : undefined;
|
|
48
|
+
const leverage = args.leverage ? parseInt(args.leverage as string) : undefined;
|
|
47
49
|
const reduceOnly = args.reduce as boolean;
|
|
48
50
|
const dryRun = args.dry as boolean;
|
|
49
51
|
|
|
@@ -103,6 +105,7 @@ async function main() {
|
|
|
103
105
|
console.log(`Limit Price: ${formatUsd(limitPrice)} (${slippageBps} bps slippage)`);
|
|
104
106
|
console.log(`Notional: ~${formatUsd(notional)}`);
|
|
105
107
|
console.log(`Reduce Only: ${reduceOnly ? 'Yes' : 'No'}`);
|
|
108
|
+
if (leverage) console.log(`Leverage: ${leverage}x`);
|
|
106
109
|
console.log(`Builder Fee: ${client.builderInfo.f / 10} bps`);
|
|
107
110
|
|
|
108
111
|
if (dryRun) {
|
|
@@ -112,7 +115,7 @@ async function main() {
|
|
|
112
115
|
|
|
113
116
|
console.log('\nExecuting...');
|
|
114
117
|
|
|
115
|
-
const response = await client.marketOrder(coin, isBuy, size, slippage);
|
|
118
|
+
const response = await client.marketOrder(coin, isBuy, size, slippage, leverage);
|
|
116
119
|
|
|
117
120
|
console.log('\nResult');
|
|
118
121
|
console.log('------');
|
|
@@ -25,6 +25,7 @@ Options:
|
|
|
25
25
|
- linear: more size at better prices
|
|
26
26
|
- exponential: much more size at better prices
|
|
27
27
|
- flat: equal size at all levels
|
|
28
|
+
--leverage Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3
|
|
28
29
|
--reduce Reduce-only orders (for scaling out of position)
|
|
29
30
|
--tif Time in force: GTC, ALO (default: GTC)
|
|
30
31
|
--dry Dry run - show order plan without executing
|
|
@@ -108,6 +109,7 @@ async function main() {
|
|
|
108
109
|
const numLevels = parseInt(args.levels as string);
|
|
109
110
|
const rangePct = parseFloat(args.range as string);
|
|
110
111
|
const distribution = (args.distribution as string || 'linear') as 'linear' | 'exponential' | 'flat';
|
|
112
|
+
const leverage = args.leverage ? parseInt(args.leverage as string) : undefined;
|
|
111
113
|
const reduceOnly = args.reduce as boolean;
|
|
112
114
|
const tifArg = ((args.tif as string)?.toUpperCase() || 'GTC');
|
|
113
115
|
const dryRun = args.dry as boolean;
|
|
@@ -208,7 +210,8 @@ async function main() {
|
|
|
208
210
|
level.size,
|
|
209
211
|
level.price,
|
|
210
212
|
tif,
|
|
211
|
-
reduceOnly
|
|
213
|
+
reduceOnly,
|
|
214
|
+
leverage
|
|
212
215
|
);
|
|
213
216
|
|
|
214
217
|
if (response.status === 'ok' && response.response && typeof response.response === 'object') {
|
|
@@ -23,6 +23,7 @@ Options:
|
|
|
23
23
|
--type Order type: tp (take profit) or sl (stop loss)
|
|
24
24
|
--limit Limit price when triggered (default: trigger price for TP, with slippage for SL)
|
|
25
25
|
--slippage Slippage for SL in bps (default: 100 = 1%)
|
|
26
|
+
--leverage Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3
|
|
26
27
|
--reduce Reduce-only order (default: true for TP/SL)
|
|
27
28
|
--dry Dry run - show order without placing
|
|
28
29
|
|
|
@@ -62,6 +63,7 @@ async function main() {
|
|
|
62
63
|
const orderType = args.type as string;
|
|
63
64
|
const limitPriceOverride = args.limit ? parseFloat(args.limit as string) : undefined;
|
|
64
65
|
const slippageBps = args.slippage ? parseInt(args.slippage as string) : 100;
|
|
66
|
+
const leverage = args.leverage ? parseInt(args.leverage as string) : undefined;
|
|
65
67
|
const reduceOnly = args.reduce !== 'false'; // Default true
|
|
66
68
|
const dryRun = args.dry as boolean;
|
|
67
69
|
|
|
@@ -167,7 +169,8 @@ async function main() {
|
|
|
167
169
|
triggerPrice,
|
|
168
170
|
limitPrice,
|
|
169
171
|
tpsl,
|
|
170
|
-
reduceOnly
|
|
172
|
+
reduceOnly,
|
|
173
|
+
leverage
|
|
171
174
|
);
|
|
172
175
|
|
|
173
176
|
console.log('\nResult');
|
|
@@ -23,6 +23,7 @@ Options:
|
|
|
23
23
|
--intervals Number of slices (default: calculates based on duration)
|
|
24
24
|
--randomize Randomize timing by ±X percent (default: 0)
|
|
25
25
|
--slippage Slippage tolerance in bps per slice (default: 50)
|
|
26
|
+
--leverage Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3
|
|
26
27
|
--dry Dry run - show execution plan without trading
|
|
27
28
|
|
|
28
29
|
Examples:
|
|
@@ -57,6 +58,7 @@ async function main() {
|
|
|
57
58
|
const intervals = args.intervals ? parseInt(args.intervals as string) : Math.max(6, Math.floor(duration / 300)); // default: 1 slice per 5 min
|
|
58
59
|
const randomize = args.randomize ? parseInt(args.randomize as string) : 0;
|
|
59
60
|
const slippage = args.slippage ? parseInt(args.slippage as string) : undefined;
|
|
61
|
+
const leverage = args.leverage ? parseInt(args.leverage as string) : undefined;
|
|
60
62
|
const dryRun = args.dry as boolean;
|
|
61
63
|
|
|
62
64
|
if (!coin || !side || isNaN(totalSize) || isNaN(duration)) {
|
|
@@ -146,7 +148,7 @@ async function main() {
|
|
|
146
148
|
};
|
|
147
149
|
|
|
148
150
|
try {
|
|
149
|
-
const response = await client.marketOrder(coin, isBuy, sliceSize, slippage);
|
|
151
|
+
const response = await client.marketOrder(coin, isBuy, sliceSize, slippage, leverage);
|
|
150
152
|
|
|
151
153
|
if (response.status === 'ok' && response.response && typeof response.response === 'object') {
|
|
152
154
|
const statuses = response.response.data.statuses;
|
package/scripts/plugin/tools.ts
CHANGED
|
@@ -743,6 +743,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
743
743
|
coin: { type: 'string', description: 'Asset symbol (ETH, BTC, SOL, etc.)' },
|
|
744
744
|
size: { type: 'number', description: 'Order size in base asset' },
|
|
745
745
|
slippage: { type: 'number', description: 'Slippage tolerance in bps (default: 50)' },
|
|
746
|
+
leverage: { type: 'number', description: 'Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3' },
|
|
746
747
|
dry: { type: 'boolean', description: 'Preview without executing' },
|
|
747
748
|
},
|
|
748
749
|
required: ['coin', 'size'],
|
|
@@ -757,6 +758,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
757
758
|
const coin = normalizeCoin(params.coin as string);
|
|
758
759
|
const size = params.size as number;
|
|
759
760
|
const slippageBps = (params.slippage as number) ?? client.builderInfo.f;
|
|
761
|
+
const leverage = params.leverage as number | undefined;
|
|
760
762
|
|
|
761
763
|
const mids = await client.getAllMids();
|
|
762
764
|
const midPrice = parseFloat(mids[coin]);
|
|
@@ -775,11 +777,12 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
775
777
|
midPrice,
|
|
776
778
|
slippagePrice,
|
|
777
779
|
slippageBps,
|
|
780
|
+
leverage,
|
|
778
781
|
});
|
|
779
782
|
}
|
|
780
783
|
|
|
781
|
-
const result = await client.marketOrder(coin, true, parseFloat(roundedSize), slippageBps);
|
|
782
|
-
return json({ action: 'buy', coin, size: roundedSize, result });
|
|
784
|
+
const result = await client.marketOrder(coin, true, parseFloat(roundedSize), slippageBps, leverage);
|
|
785
|
+
return json({ action: 'buy', coin, size: roundedSize, leverage, result });
|
|
783
786
|
},
|
|
784
787
|
},
|
|
785
788
|
|
|
@@ -792,6 +795,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
792
795
|
coin: { type: 'string', description: 'Asset symbol (ETH, BTC, SOL, etc.)' },
|
|
793
796
|
size: { type: 'number', description: 'Order size in base asset' },
|
|
794
797
|
slippage: { type: 'number', description: 'Slippage tolerance in bps (default: 50)' },
|
|
798
|
+
leverage: { type: 'number', description: 'Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3' },
|
|
795
799
|
dry: { type: 'boolean', description: 'Preview without executing' },
|
|
796
800
|
},
|
|
797
801
|
required: ['coin', 'size'],
|
|
@@ -806,6 +810,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
806
810
|
const coin = normalizeCoin(params.coin as string);
|
|
807
811
|
const size = params.size as number;
|
|
808
812
|
const slippageBps = (params.slippage as number) ?? client.builderInfo.f;
|
|
813
|
+
const leverage = params.leverage as number | undefined;
|
|
809
814
|
|
|
810
815
|
const mids = await client.getAllMids();
|
|
811
816
|
const midPrice = parseFloat(mids[coin]);
|
|
@@ -824,11 +829,12 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
824
829
|
midPrice,
|
|
825
830
|
slippagePrice,
|
|
826
831
|
slippageBps,
|
|
832
|
+
leverage,
|
|
827
833
|
});
|
|
828
834
|
}
|
|
829
835
|
|
|
830
|
-
const result = await client.marketOrder(coin, false, parseFloat(roundedSize), slippageBps);
|
|
831
|
-
return json({ action: 'sell', coin, size: roundedSize, result });
|
|
836
|
+
const result = await client.marketOrder(coin, false, parseFloat(roundedSize), slippageBps, leverage);
|
|
837
|
+
return json({ action: 'sell', coin, size: roundedSize, leverage, result });
|
|
832
838
|
},
|
|
833
839
|
},
|
|
834
840
|
|
|
@@ -843,6 +849,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
843
849
|
size: { type: 'number', description: 'Order size in base asset' },
|
|
844
850
|
price: { type: 'number', description: 'Limit price' },
|
|
845
851
|
tif: { type: 'string', enum: ['GTC', 'IOC', 'ALO'], description: 'Time in force (default: GTC)' },
|
|
852
|
+
leverage: { type: 'number', description: 'Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3' },
|
|
846
853
|
reduce: { type: 'boolean', description: 'Reduce-only order' },
|
|
847
854
|
dry: { type: 'boolean', description: 'Preview without executing' },
|
|
848
855
|
},
|
|
@@ -860,6 +867,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
860
867
|
const size = params.size as number;
|
|
861
868
|
const price = params.price as number;
|
|
862
869
|
const tif = ((params.tif as string) || 'GTC').toLowerCase();
|
|
870
|
+
const leverage = params.leverage as number | undefined;
|
|
863
871
|
const reduceOnly = (params.reduce as boolean) || false;
|
|
864
872
|
|
|
865
873
|
const szDecimals = await client.getSzDecimals(coin);
|
|
@@ -884,9 +892,9 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
884
892
|
}
|
|
885
893
|
|
|
886
894
|
const result = await client.limitOrder(
|
|
887
|
-
coin, isBuy, parseFloat(roundedSize), parseFloat(roundedPrice), sdkTif, reduceOnly,
|
|
895
|
+
coin, isBuy, parseFloat(roundedSize), parseFloat(roundedPrice), sdkTif, reduceOnly, leverage,
|
|
888
896
|
);
|
|
889
|
-
return json({ action: 'limit', coin, side: params.side, size: roundedSize, price: roundedPrice, result });
|
|
897
|
+
return json({ action: 'limit', coin, side: params.side, size: roundedSize, price: roundedPrice, leverage, result });
|
|
890
898
|
},
|
|
891
899
|
},
|
|
892
900
|
|
|
@@ -1068,6 +1076,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
1068
1076
|
duration: { type: 'number', description: 'Duration in seconds' },
|
|
1069
1077
|
intervals: { type: 'number', description: 'Number of slices' },
|
|
1070
1078
|
randomize: { type: 'number', description: 'Randomize timing by this % (0-50)' },
|
|
1079
|
+
leverage: { type: 'number', description: 'Set leverage (e.g., 10 for 10x)' },
|
|
1071
1080
|
dry: { type: 'boolean', description: 'Preview without executing' },
|
|
1072
1081
|
},
|
|
1073
1082
|
required: ['coin', 'side', 'size', 'duration'],
|
|
@@ -1102,6 +1111,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
1102
1111
|
sl: { type: 'number', description: 'Stop loss percentage from entry' },
|
|
1103
1112
|
entry: { type: 'string', enum: ['market', 'limit'], description: 'Entry type (default: market)' },
|
|
1104
1113
|
price: { type: 'number', description: 'Entry price (required if entry=limit)' },
|
|
1114
|
+
leverage: { type: 'number', description: 'Set leverage (e.g., 10 for 10x)' },
|
|
1105
1115
|
dry: { type: 'boolean', description: 'Preview without executing' },
|
|
1106
1116
|
},
|
|
1107
1117
|
required: ['coin', 'side', 'size', 'tp', 'sl'],
|
|
@@ -1134,6 +1144,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
1134
1144
|
size: { type: 'number', description: 'Order size' },
|
|
1135
1145
|
offset: { type: 'number', description: 'Tick offset from best price (default: 1)' },
|
|
1136
1146
|
timeout: { type: 'number', description: 'Timeout in seconds' },
|
|
1147
|
+
leverage: { type: 'number', description: 'Set leverage (e.g., 10 for 10x)' },
|
|
1137
1148
|
dry: { type: 'boolean', description: 'Preview without executing' },
|
|
1138
1149
|
},
|
|
1139
1150
|
required: ['coin', 'side', 'size'],
|