openbroker 1.3.1 → 1.4.0
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 +12 -0
- package/SKILL.md +7 -4
- package/dist/auto/audit.d.ts +57 -0
- package/dist/auto/audit.d.ts.map +1 -0
- package/dist/auto/audit.js +407 -0
- package/dist/auto/cli.d.ts +2 -0
- package/dist/auto/cli.d.ts.map +1 -0
- package/dist/auto/cli.js +423 -0
- package/dist/auto/events.d.ts +11 -0
- package/dist/auto/events.d.ts.map +1 -0
- package/dist/auto/events.js +36 -0
- package/dist/auto/examples/dca.d.ts +4 -0
- package/dist/auto/examples/dca.d.ts.map +1 -0
- package/dist/auto/examples/dca.js +60 -0
- package/dist/auto/examples/funding-arb.d.ts +4 -0
- package/dist/auto/examples/funding-arb.d.ts.map +1 -0
- package/dist/auto/examples/funding-arb.js +81 -0
- package/dist/auto/examples/grid.d.ts +4 -0
- package/dist/auto/examples/grid.d.ts.map +1 -0
- package/dist/auto/examples/grid.js +114 -0
- package/dist/auto/examples/mm-maker.d.ts +4 -0
- package/dist/auto/examples/mm-maker.d.ts.map +1 -0
- package/dist/auto/examples/mm-maker.js +131 -0
- package/dist/auto/examples/mm-spread.d.ts +4 -0
- package/dist/auto/examples/mm-spread.d.ts.map +1 -0
- package/dist/auto/examples/mm-spread.js +119 -0
- package/dist/auto/examples/price-alert.d.ts +4 -0
- package/dist/auto/examples/price-alert.d.ts.map +1 -0
- package/dist/auto/examples/price-alert.js +85 -0
- package/dist/auto/keep-awake.d.ts +11 -0
- package/dist/auto/keep-awake.d.ts.map +1 -0
- package/dist/auto/keep-awake.js +70 -0
- package/dist/auto/loader.d.ts +22 -0
- package/dist/auto/loader.d.ts.map +1 -0
- package/dist/auto/loader.js +127 -0
- package/dist/auto/prune.d.ts +40 -0
- package/dist/auto/prune.d.ts.map +1 -0
- package/dist/auto/prune.js +204 -0
- package/dist/auto/registry.d.ts +24 -0
- package/dist/auto/registry.d.ts.map +1 -0
- package/dist/auto/registry.js +93 -0
- package/dist/auto/report.d.ts +3 -0
- package/dist/auto/report.d.ts.map +1 -0
- package/dist/auto/report.js +385 -0
- package/dist/auto/runtime.d.ts +33 -0
- package/dist/auto/runtime.d.ts.map +1 -0
- package/dist/auto/runtime.js +844 -0
- package/dist/auto/types.d.ts +236 -0
- package/dist/auto/types.d.ts.map +1 -0
- package/dist/auto/types.js +3 -0
- package/dist/core/client.d.ts +684 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +2040 -0
- package/dist/core/config.d.ts +22 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +143 -0
- package/dist/core/types.d.ts +221 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/utils.d.ts +61 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +142 -0
- package/dist/core/ws.d.ts +121 -0
- package/dist/core/ws.d.ts.map +1 -0
- package/dist/core/ws.js +222 -0
- package/dist/info/account.d.ts +3 -0
- package/dist/info/account.d.ts.map +1 -0
- package/dist/info/account.js +198 -0
- package/dist/info/all-markets.d.ts +3 -0
- package/dist/info/all-markets.d.ts.map +1 -0
- package/dist/info/all-markets.js +272 -0
- package/dist/info/candles.d.ts +3 -0
- package/dist/info/candles.d.ts.map +1 -0
- package/dist/info/candles.js +120 -0
- package/dist/info/fees.d.ts +3 -0
- package/dist/info/fees.d.ts.map +1 -0
- package/dist/info/fees.js +87 -0
- package/dist/info/fills.d.ts +3 -0
- package/dist/info/fills.d.ts.map +1 -0
- package/dist/info/fills.js +105 -0
- package/dist/info/funding-history.d.ts +3 -0
- package/dist/info/funding-history.d.ts.map +1 -0
- package/dist/info/funding-history.js +98 -0
- package/dist/info/funding-scan.d.ts +3 -0
- package/dist/info/funding-scan.d.ts.map +1 -0
- package/dist/info/funding-scan.js +178 -0
- package/dist/info/funding.d.ts +3 -0
- package/dist/info/funding.d.ts.map +1 -0
- package/dist/info/funding.js +158 -0
- package/dist/info/markets.d.ts +3 -0
- package/dist/info/markets.d.ts.map +1 -0
- package/dist/info/markets.js +178 -0
- package/dist/info/order-status.d.ts +3 -0
- package/dist/info/order-status.d.ts.map +1 -0
- package/dist/info/order-status.js +85 -0
- package/dist/info/orders.d.ts +3 -0
- package/dist/info/orders.d.ts.map +1 -0
- package/dist/info/orders.js +162 -0
- package/dist/info/outcomes.d.ts +3 -0
- package/dist/info/outcomes.d.ts.map +1 -0
- package/dist/info/outcomes.js +175 -0
- package/dist/info/positions.d.ts +3 -0
- package/dist/info/positions.d.ts.map +1 -0
- package/dist/info/positions.js +127 -0
- package/dist/info/rate-limit.d.ts +3 -0
- package/dist/info/rate-limit.d.ts.map +1 -0
- package/dist/info/rate-limit.js +58 -0
- package/dist/info/search-markets.d.ts +3 -0
- package/dist/info/search-markets.d.ts.map +1 -0
- package/dist/info/search-markets.js +296 -0
- package/dist/info/spot.d.ts +3 -0
- package/dist/info/spot.d.ts.map +1 -0
- package/dist/info/spot.js +192 -0
- package/dist/info/trades.d.ts +3 -0
- package/dist/info/trades.d.ts.map +1 -0
- package/dist/info/trades.js +97 -0
- package/dist/lib.d.ts +14 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +17 -0
- package/dist/operations/bracket.d.ts +28 -0
- package/dist/operations/bracket.d.ts.map +1 -0
- package/dist/operations/bracket.js +266 -0
- package/dist/operations/cancel.d.ts +3 -0
- package/dist/operations/cancel.d.ts.map +1 -0
- package/dist/operations/cancel.js +107 -0
- package/dist/operations/chase.d.ts +25 -0
- package/dist/operations/chase.d.ts.map +1 -0
- package/dist/operations/chase.js +215 -0
- package/dist/operations/limit-order.d.ts +3 -0
- package/dist/operations/limit-order.d.ts.map +1 -0
- package/dist/operations/limit-order.js +144 -0
- package/dist/operations/market-order.d.ts +3 -0
- package/dist/operations/market-order.d.ts.map +1 -0
- package/dist/operations/market-order.js +153 -0
- package/dist/operations/outcome-order.d.ts +3 -0
- package/dist/operations/outcome-order.d.ts.map +1 -0
- package/dist/operations/outcome-order.js +171 -0
- package/dist/operations/scale.d.ts +3 -0
- package/dist/operations/scale.d.ts.map +1 -0
- package/dist/operations/scale.js +212 -0
- package/dist/operations/set-tpsl.d.ts +3 -0
- package/dist/operations/set-tpsl.d.ts.map +1 -0
- package/dist/operations/set-tpsl.js +277 -0
- package/dist/operations/spot-order.d.ts +3 -0
- package/dist/operations/spot-order.d.ts.map +1 -0
- package/dist/operations/spot-order.js +173 -0
- package/dist/operations/trigger-order.d.ts +3 -0
- package/dist/operations/trigger-order.d.ts.map +1 -0
- package/dist/operations/trigger-order.js +177 -0
- package/dist/operations/twap-cancel.d.ts +3 -0
- package/dist/operations/twap-cancel.d.ts.map +1 -0
- package/dist/operations/twap-cancel.js +57 -0
- package/dist/operations/twap-status.d.ts +3 -0
- package/dist/operations/twap-status.d.ts.map +1 -0
- package/dist/operations/twap-status.js +81 -0
- package/dist/operations/twap.d.ts +3 -0
- package/dist/operations/twap.d.ts.map +1 -0
- package/dist/operations/twap.js +124 -0
- package/dist/setup/approve-builder.d.ts +3 -0
- package/dist/setup/approve-builder.d.ts.map +1 -0
- package/dist/setup/approve-builder.js +155 -0
- package/dist/setup/env.d.ts +4 -0
- package/dist/setup/env.d.ts.map +1 -0
- package/dist/setup/env.js +8 -0
- package/dist/setup/onboard.d.ts +10 -0
- package/dist/setup/onboard.d.ts.map +1 -0
- package/dist/setup/onboard.js +462 -0
- package/package.json +10 -4
- package/scripts/core/client.ts +13 -3
- package/scripts/info/all-markets.ts +18 -2
- package/scripts/info/search-markets.ts +18 -2
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
// Execute a limit order on Hyperliquid
|
|
3
|
+
import { getClient } from '../core/client.js';
|
|
4
|
+
import { formatUsd, parseArgs } from '../core/utils.js';
|
|
5
|
+
function printUsage() {
|
|
6
|
+
console.log(`
|
|
7
|
+
Open Broker - Limit Order
|
|
8
|
+
=========================
|
|
9
|
+
|
|
10
|
+
Place a limit order with specified price.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
npx tsx scripts/operations/limit-order.ts --coin <COIN> --side <buy|sell> --size <SIZE> --price <PRICE>
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--coin Asset to trade (e.g., ETH, BTC)
|
|
17
|
+
--side Order side: buy or sell
|
|
18
|
+
--size Order size in base asset
|
|
19
|
+
--price Limit price
|
|
20
|
+
--tif Time in force: GTC, IOC, or ALO (default: GTC)
|
|
21
|
+
GTC = Good Till Cancel (rests on book)
|
|
22
|
+
IOC = Immediate Or Cancel (fills or cancels)
|
|
23
|
+
ALO = Add Liquidity Only (post-only, maker only)
|
|
24
|
+
--leverage Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3
|
|
25
|
+
--reduce Reduce-only order (default: false)
|
|
26
|
+
--dry Dry run - show order details without executing
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
npx tsx scripts/operations/limit-order.ts --coin ETH --side buy --size 1 --price 3000
|
|
30
|
+
npx tsx scripts/operations/limit-order.ts --coin BTC --side sell --size 0.1 --price 100000 --tif ALO
|
|
31
|
+
npx tsx scripts/operations/limit-order.ts --coin SOL --side buy --size 10 --price 150 --reduce
|
|
32
|
+
`);
|
|
33
|
+
}
|
|
34
|
+
async function main() {
|
|
35
|
+
const args = parseArgs(process.argv.slice(2));
|
|
36
|
+
// Parse and validate arguments
|
|
37
|
+
const coin = args.coin;
|
|
38
|
+
const side = args.side;
|
|
39
|
+
const size = parseFloat(args.size);
|
|
40
|
+
const price = parseFloat(args.price);
|
|
41
|
+
const tifArg = args.tif?.toUpperCase() || 'GTC';
|
|
42
|
+
const leverage = args.leverage ? parseInt(args.leverage) : undefined;
|
|
43
|
+
const reduceOnly = args.reduce;
|
|
44
|
+
const dryRun = args.dry;
|
|
45
|
+
if (!coin || !side || isNaN(size) || isNaN(price)) {
|
|
46
|
+
printUsage();
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
if (side !== 'buy' && side !== 'sell') {
|
|
50
|
+
console.error('Error: --side must be "buy" or "sell"');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
if (size <= 0) {
|
|
54
|
+
console.error('Error: --size must be positive');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
if (price <= 0) {
|
|
58
|
+
console.error('Error: --price must be positive');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
// Map uppercase CLI input to Pascal case for SDK
|
|
62
|
+
const tifMap = {
|
|
63
|
+
'GTC': 'Gtc',
|
|
64
|
+
'IOC': 'Ioc',
|
|
65
|
+
'ALO': 'Alo'
|
|
66
|
+
};
|
|
67
|
+
const tif = tifMap[tifArg];
|
|
68
|
+
if (!tif) {
|
|
69
|
+
console.error(`Error: --tif must be one of: GTC, IOC, ALO`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
const isBuy = side === 'buy';
|
|
73
|
+
const client = getClient();
|
|
74
|
+
console.log('Open Broker - Limit Order');
|
|
75
|
+
console.log('=========================\n');
|
|
76
|
+
try {
|
|
77
|
+
// Get current price for reference
|
|
78
|
+
const mids = await client.getAllMids();
|
|
79
|
+
const midPrice = parseFloat(mids[coin]);
|
|
80
|
+
if (!midPrice) {
|
|
81
|
+
console.error(`Error: No market data for ${coin}`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
const notional = price * size;
|
|
85
|
+
const distanceFromMid = ((price - midPrice) / midPrice) * 100;
|
|
86
|
+
console.log('Order Details');
|
|
87
|
+
console.log('-------------');
|
|
88
|
+
console.log(`Coin: ${coin}`);
|
|
89
|
+
console.log(`Side: ${isBuy ? 'BUY' : 'SELL'}`);
|
|
90
|
+
console.log(`Size: ${size}`);
|
|
91
|
+
console.log(`Limit Price: ${formatUsd(price)}`);
|
|
92
|
+
console.log(`Current Mid: ${formatUsd(midPrice)}`);
|
|
93
|
+
console.log(`Distance: ${distanceFromMid >= 0 ? '+' : ''}${distanceFromMid.toFixed(2)}% from mid`);
|
|
94
|
+
console.log(`Notional: ${formatUsd(notional)}`);
|
|
95
|
+
console.log(`Time in Force: ${tif}`);
|
|
96
|
+
console.log(`Reduce Only: ${reduceOnly ? 'Yes' : 'No'}`);
|
|
97
|
+
if (leverage)
|
|
98
|
+
console.log(`Leverage: ${leverage}x`);
|
|
99
|
+
console.log(`Builder Fee: ${client.builderInfo.f / 10} bps`);
|
|
100
|
+
// Warning if order would be aggressively priced
|
|
101
|
+
if ((isBuy && price > midPrice) || (!isBuy && price < midPrice)) {
|
|
102
|
+
console.log(`\n⚠️ Order is priced aggressively - may fill immediately as taker`);
|
|
103
|
+
}
|
|
104
|
+
if (dryRun) {
|
|
105
|
+
console.log('\n🔍 Dry run - order not submitted');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
console.log('\nSubmitting...');
|
|
109
|
+
const response = await client.limitOrder(coin, isBuy, size, price, tif, reduceOnly, leverage);
|
|
110
|
+
console.log('\nResult');
|
|
111
|
+
console.log('------');
|
|
112
|
+
if (response.status === 'ok' && response.response && typeof response.response !== 'string') {
|
|
113
|
+
const statuses = response.response.data.statuses;
|
|
114
|
+
for (const status of statuses) {
|
|
115
|
+
if (status.filled) {
|
|
116
|
+
const fillSz = parseFloat(status.filled.totalSz);
|
|
117
|
+
const avgPx = parseFloat(status.filled.avgPx);
|
|
118
|
+
const fillNotional = fillSz * avgPx;
|
|
119
|
+
console.log(`✅ Filled`);
|
|
120
|
+
console.log(` Order ID: ${status.filled.oid}`);
|
|
121
|
+
console.log(` Size: ${fillSz}`);
|
|
122
|
+
console.log(` Avg Price: ${formatUsd(avgPx)}`);
|
|
123
|
+
console.log(` Notional: ${formatUsd(fillNotional)}`);
|
|
124
|
+
}
|
|
125
|
+
else if (status.resting) {
|
|
126
|
+
console.log(`✅ Order placed`);
|
|
127
|
+
console.log(` Order ID: ${status.resting.oid}`);
|
|
128
|
+
console.log(` Status: Resting on book`);
|
|
129
|
+
}
|
|
130
|
+
else if (status.error) {
|
|
131
|
+
console.log(`❌ Error: ${status.error}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
console.log(`❌ Error: ${response.error || 'Unknown error'}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.error('Error submitting order:', error);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"market-order.d.ts","sourceRoot":"","sources":["../../scripts/operations/market-order.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
// Execute a market order on Hyperliquid
|
|
3
|
+
import { getClient } from '../core/client.js';
|
|
4
|
+
import { formatUsd, parseArgs, checkBuilderFeeApproval } from '../core/utils.js';
|
|
5
|
+
function printUsage() {
|
|
6
|
+
console.log(`
|
|
7
|
+
Open Broker - Market Order
|
|
8
|
+
==========================
|
|
9
|
+
|
|
10
|
+
Execute a market order with slippage protection.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
npx tsx scripts/operations/market-order.ts --coin <COIN> --side <buy|sell> --size <SIZE>
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--coin Asset to trade (e.g., ETH, BTC)
|
|
17
|
+
--side Order side: buy or sell
|
|
18
|
+
--size Order size in base asset
|
|
19
|
+
--slippage Slippage tolerance in bps (default: from config, usually 50 = 0.5%)
|
|
20
|
+
--leverage Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3
|
|
21
|
+
--reduce Reduce-only order (default: false)
|
|
22
|
+
--dry Dry run - show order details without executing
|
|
23
|
+
--verbose Show full API request/response for debugging
|
|
24
|
+
|
|
25
|
+
Environment:
|
|
26
|
+
HYPERLIQUID_PRIVATE_KEY Your wallet private key (0x...)
|
|
27
|
+
HYPERLIQUID_NETWORK "mainnet" or "testnet" (default: mainnet)
|
|
28
|
+
VERBOSE=1 Enable verbose logging
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
npx tsx scripts/operations/market-order.ts --coin ETH --side buy --size 0.1
|
|
32
|
+
npx tsx scripts/operations/market-order.ts --coin BTC --side sell --size 0.01 --slippage 100
|
|
33
|
+
npx tsx scripts/operations/market-order.ts --coin SOL --side buy --size 10 --dry
|
|
34
|
+
npx tsx scripts/operations/market-order.ts --coin ETH --side buy --size 0.1 --verbose
|
|
35
|
+
`);
|
|
36
|
+
}
|
|
37
|
+
async function main() {
|
|
38
|
+
const args = parseArgs(process.argv.slice(2));
|
|
39
|
+
// Parse and validate arguments
|
|
40
|
+
const coin = args.coin;
|
|
41
|
+
const side = args.side;
|
|
42
|
+
const size = parseFloat(args.size);
|
|
43
|
+
const slippage = args.slippage ? parseInt(args.slippage) : undefined;
|
|
44
|
+
const leverage = args.leverage ? parseInt(args.leverage) : undefined;
|
|
45
|
+
const reduceOnly = args.reduce;
|
|
46
|
+
const dryRun = args.dry;
|
|
47
|
+
if (!coin || !side || isNaN(size)) {
|
|
48
|
+
printUsage();
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
if (side !== 'buy' && side !== 'sell') {
|
|
52
|
+
console.error('Error: --side must be "buy" or "sell"');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
if (size <= 0) {
|
|
56
|
+
console.error('Error: --size must be positive');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const isBuy = side === 'buy';
|
|
60
|
+
const client = getClient();
|
|
61
|
+
// Enable verbose mode if requested
|
|
62
|
+
if (args.verbose) {
|
|
63
|
+
client.verbose = true;
|
|
64
|
+
}
|
|
65
|
+
console.log('Open Broker - Market Order');
|
|
66
|
+
console.log('==========================\n');
|
|
67
|
+
// Check builder fee approval (warning only, don't block)
|
|
68
|
+
await checkBuilderFeeApproval(client);
|
|
69
|
+
try {
|
|
70
|
+
// Get current price
|
|
71
|
+
const mids = await client.getAllMids();
|
|
72
|
+
const midPrice = parseFloat(mids[coin]);
|
|
73
|
+
if (!midPrice) {
|
|
74
|
+
console.error(`Error: No market data for ${coin}`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
// Calculate order details
|
|
78
|
+
const slippageBps = slippage ?? 50;
|
|
79
|
+
const slippageMultiplier = slippageBps / 10000;
|
|
80
|
+
const limitPrice = isBuy
|
|
81
|
+
? midPrice * (1 + slippageMultiplier)
|
|
82
|
+
: midPrice * (1 - slippageMultiplier);
|
|
83
|
+
const notional = midPrice * size;
|
|
84
|
+
console.log('Order Details');
|
|
85
|
+
console.log('-------------');
|
|
86
|
+
console.log(`Coin: ${coin}`);
|
|
87
|
+
console.log(`Side: ${isBuy ? 'BUY' : 'SELL'}`);
|
|
88
|
+
console.log(`Size: ${size}`);
|
|
89
|
+
console.log(`Mid Price: ${formatUsd(midPrice)}`);
|
|
90
|
+
console.log(`Limit Price: ${formatUsd(limitPrice)} (${slippageBps} bps slippage)`);
|
|
91
|
+
console.log(`Notional: ~${formatUsd(notional)}`);
|
|
92
|
+
console.log(`Reduce Only: ${reduceOnly ? 'Yes' : 'No'}`);
|
|
93
|
+
if (leverage)
|
|
94
|
+
console.log(`Leverage: ${leverage}x`);
|
|
95
|
+
console.log(`Builder Fee: ${client.builderInfo.f / 10} bps`);
|
|
96
|
+
if (dryRun) {
|
|
97
|
+
console.log('\n🔍 Dry run - order not submitted');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
console.log('\nExecuting...');
|
|
101
|
+
const response = await client.marketOrder(coin, isBuy, size, slippage, leverage);
|
|
102
|
+
console.log('\nResult');
|
|
103
|
+
console.log('------');
|
|
104
|
+
// Log full response for debugging
|
|
105
|
+
if (args.verbose || process.env.VERBOSE) {
|
|
106
|
+
console.log('\nFull Response:');
|
|
107
|
+
console.log(JSON.stringify(response, null, 2));
|
|
108
|
+
}
|
|
109
|
+
if (response.status === 'ok' && response.response && typeof response.response === 'object') {
|
|
110
|
+
const statuses = response.response.data.statuses;
|
|
111
|
+
for (const status of statuses) {
|
|
112
|
+
if (status.filled) {
|
|
113
|
+
const fillSz = parseFloat(status.filled.totalSz);
|
|
114
|
+
const avgPx = parseFloat(status.filled.avgPx);
|
|
115
|
+
const fillNotional = fillSz * avgPx;
|
|
116
|
+
const slippageActual = isBuy
|
|
117
|
+
? (avgPx - midPrice) / midPrice
|
|
118
|
+
: (midPrice - avgPx) / midPrice;
|
|
119
|
+
console.log(`✅ Filled`);
|
|
120
|
+
console.log(` Order ID: ${status.filled.oid}`);
|
|
121
|
+
console.log(` Size: ${fillSz}`);
|
|
122
|
+
console.log(` Avg Price: ${formatUsd(avgPx)}`);
|
|
123
|
+
console.log(` Notional: ${formatUsd(fillNotional)}`);
|
|
124
|
+
console.log(` Slippage: ${(slippageActual * 10000).toFixed(1)} bps`);
|
|
125
|
+
}
|
|
126
|
+
else if (status.resting) {
|
|
127
|
+
console.log(`⏳ Resting (partial fill)`);
|
|
128
|
+
console.log(` Order ID: ${status.resting.oid}`);
|
|
129
|
+
}
|
|
130
|
+
else if (status.error) {
|
|
131
|
+
console.log(`❌ Error: ${status.error}`);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// Unknown status - show the whole thing
|
|
135
|
+
console.log(`⚠️ Unknown status:`);
|
|
136
|
+
console.log(JSON.stringify(status, null, 2));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (response.status === 'err') {
|
|
141
|
+
console.log(`❌ API Error: ${response.response || JSON.stringify(response)}`);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
console.log(`❌ Unexpected response:`);
|
|
145
|
+
console.log(JSON.stringify(response, null, 2));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error('Error executing order:', error);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcome-order.d.ts","sourceRoot":"","sources":["../../scripts/operations/outcome-order.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
// Execute a HIP-4 outcome order on Hyperliquid
|
|
3
|
+
import { getClient } from '../core/client.js';
|
|
4
|
+
import { checkBuilderFeeApproval, formatUsd, parseArgs } from '../core/utils.js';
|
|
5
|
+
function printUsage() {
|
|
6
|
+
console.log(`
|
|
7
|
+
Open Broker - HIP-4 Outcome Order
|
|
8
|
+
=================================
|
|
9
|
+
|
|
10
|
+
Buy or sell a YES/NO outcome token.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
openbroker outcome-order --outcome <id|#encoding|+encoding> --outcome-side <yes|no> --side <buy|sell> --size <SIZE>
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--outcome Outcome id, outcome spot coin (#1230), or token name (+1230)
|
|
17
|
+
--outcome-side Outcome side when --outcome is a plain id: yes/no or 0/1 (default: yes)
|
|
18
|
+
--side Trade side: buy or sell
|
|
19
|
+
--size Order size in outcome token units
|
|
20
|
+
--price Limit price between 0.001 and 0.999 (omit for market IOC)
|
|
21
|
+
--tif Time-in-force for limit orders: Gtc, Ioc, Alo (default: Gtc)
|
|
22
|
+
--slippage Slippage tolerance in bps for market orders (default: config, usually 50)
|
|
23
|
+
--sz-decimals Override size decimals if outcome metadata omits token decimals
|
|
24
|
+
--dry Dry run - show order details without executing
|
|
25
|
+
--verbose Show full API request/response for debugging
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
openbroker outcomes --query BTC
|
|
29
|
+
openbroker outcome-order --outcome 123 --outcome-side yes --side buy --size 10 --dry
|
|
30
|
+
openbroker outcome-buy --outcome 123 --outcome-side no --size 5 --price 0.42
|
|
31
|
+
openbroker outcome-sell --outcome #1230 --size 10
|
|
32
|
+
`);
|
|
33
|
+
}
|
|
34
|
+
function formatOutcomePrice(price) {
|
|
35
|
+
return price.toFixed(4);
|
|
36
|
+
}
|
|
37
|
+
async function main() {
|
|
38
|
+
const args = parseArgs(process.argv.slice(2));
|
|
39
|
+
if (args.help || args.h) {
|
|
40
|
+
printUsage();
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
const outcomeRef = args.outcome;
|
|
44
|
+
const outcomeSide = args['outcome-side'];
|
|
45
|
+
const side = args.side;
|
|
46
|
+
const size = parseFloat(args.size);
|
|
47
|
+
const price = args.price ? parseFloat(args.price) : undefined;
|
|
48
|
+
const tif = args.tif ?? 'Gtc';
|
|
49
|
+
const slippage = args.slippage ? parseInt(args.slippage) : undefined;
|
|
50
|
+
const szDecimals = args['sz-decimals'] ? parseInt(args['sz-decimals'], 10) : undefined;
|
|
51
|
+
const dryRun = args.dry;
|
|
52
|
+
if (!outcomeRef || !side || isNaN(size)) {
|
|
53
|
+
printUsage();
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
if (side !== 'buy' && side !== 'sell') {
|
|
57
|
+
console.error('Error: --side must be "buy" or "sell"');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
if (size <= 0) {
|
|
61
|
+
console.error('Error: --size must be positive');
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
if (price !== undefined && (price <= 0 || price >= 1)) {
|
|
65
|
+
console.error('Error: --price must be between 0 and 1 for outcome tokens');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
if (szDecimals !== undefined && (szDecimals < 0 || szDecimals > 8)) {
|
|
69
|
+
console.error('Error: --sz-decimals must be between 0 and 8');
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
const client = getClient();
|
|
73
|
+
if (args.verbose)
|
|
74
|
+
client.verbose = true;
|
|
75
|
+
const isBuy = side === 'buy';
|
|
76
|
+
const isMarket = price === undefined;
|
|
77
|
+
console.log('Open Broker - HIP-4 Outcome Order');
|
|
78
|
+
console.log('=================================\n');
|
|
79
|
+
await checkBuilderFeeApproval(client);
|
|
80
|
+
try {
|
|
81
|
+
const resolved = client.resolveOutcomeRef(outcomeRef, outcomeSide);
|
|
82
|
+
const market = await client.getOutcomeMarket(resolved.outcome);
|
|
83
|
+
const marketSide = market?.sides.find((s) => s.side === resolved.side);
|
|
84
|
+
const sideName = marketSide?.name ?? (resolved.side === 0 ? 'Yes' : 'No');
|
|
85
|
+
const midPrice = await client.getOutcomeMidPrice(resolved.outcome, resolved.side);
|
|
86
|
+
const slippageBps = slippage ?? 50;
|
|
87
|
+
const limitPrice = isMarket
|
|
88
|
+
? (isBuy ? midPrice * (1 + slippageBps / 10000) : midPrice * (1 - slippageBps / 10000))
|
|
89
|
+
: price;
|
|
90
|
+
const notional = midPrice * size;
|
|
91
|
+
console.log('Order Details');
|
|
92
|
+
console.log('-------------');
|
|
93
|
+
console.log(`Outcome: ${resolved.outcome}`);
|
|
94
|
+
console.log(`Market: ${market?.name ?? 'Unknown'}${market?.parsedDescription.underlying ? ` (${market.parsedDescription.underlying})` : ''}`);
|
|
95
|
+
if (market?.parsedDescription.expiry)
|
|
96
|
+
console.log(`Expiry: ${market.parsedDescription.expiry}`);
|
|
97
|
+
if (market?.parsedDescription.targetPrice)
|
|
98
|
+
console.log(`Target: ${market.parsedDescription.targetPrice}`);
|
|
99
|
+
console.log(`Outcome Side: ${sideName.toUpperCase()} (${resolved.side})`);
|
|
100
|
+
console.log(`Coin: ${resolved.coin}`);
|
|
101
|
+
console.log(`Asset ID: ${resolved.assetId}`);
|
|
102
|
+
console.log(`Trade Side: ${isBuy ? 'BUY' : 'SELL'}`);
|
|
103
|
+
console.log(`Size: ${size}`);
|
|
104
|
+
console.log(`Mid Price: ${formatOutcomePrice(midPrice)}`);
|
|
105
|
+
if (isMarket) {
|
|
106
|
+
console.log(`Type: Market (IOC)`);
|
|
107
|
+
console.log(`Limit Price: ${formatOutcomePrice(limitPrice)} (${slippageBps} bps slippage)`);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.log(`Type: Limit (${tif})`);
|
|
111
|
+
console.log(`Limit Price: ${formatOutcomePrice(price)}`);
|
|
112
|
+
}
|
|
113
|
+
console.log(`Notional: ~${formatUsd(notional)}`);
|
|
114
|
+
if (marketSide?.szDecimals !== undefined || szDecimals !== undefined) {
|
|
115
|
+
console.log(`Sz Decimals: ${szDecimals ?? marketSide?.szDecimals}`);
|
|
116
|
+
}
|
|
117
|
+
console.log(`Builder Fee: ${client.builderInfo.f / 10} bps`);
|
|
118
|
+
if (dryRun) {
|
|
119
|
+
console.log('\nDry run - order not submitted');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
console.log('\nExecuting...');
|
|
123
|
+
const response = isMarket
|
|
124
|
+
? await client.outcomeMarketOrder(outcomeRef, outcomeSide, isBuy, size, slippage, szDecimals)
|
|
125
|
+
: await client.outcomeLimitOrder(outcomeRef, outcomeSide, isBuy, size, price, tif, szDecimals);
|
|
126
|
+
console.log('\nResult');
|
|
127
|
+
console.log('------');
|
|
128
|
+
if (args.verbose || process.env.VERBOSE) {
|
|
129
|
+
console.log('\nFull Response:');
|
|
130
|
+
console.log(JSON.stringify(response, null, 2));
|
|
131
|
+
}
|
|
132
|
+
if (response.status === 'ok' && response.response && typeof response.response === 'object') {
|
|
133
|
+
const statuses = response.response.data.statuses;
|
|
134
|
+
for (const status of statuses) {
|
|
135
|
+
if (status.filled) {
|
|
136
|
+
const fillSz = parseFloat(status.filled.totalSz);
|
|
137
|
+
const avgPx = parseFloat(status.filled.avgPx);
|
|
138
|
+
console.log('Filled');
|
|
139
|
+
console.log(` Order ID: ${status.filled.oid}`);
|
|
140
|
+
console.log(` Size: ${fillSz}`);
|
|
141
|
+
console.log(` Avg Price: ${formatOutcomePrice(avgPx)}`);
|
|
142
|
+
console.log(` Notional: ${formatUsd(fillSz * avgPx)}`);
|
|
143
|
+
}
|
|
144
|
+
else if (status.resting) {
|
|
145
|
+
console.log('Resting');
|
|
146
|
+
console.log(` Order ID: ${status.resting.oid}`);
|
|
147
|
+
}
|
|
148
|
+
else if (status.error) {
|
|
149
|
+
console.log(`Error: ${status.error}`);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
console.log('Unknown status:');
|
|
153
|
+
console.log(JSON.stringify(status, null, 2));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (response.status === 'err') {
|
|
158
|
+
console.log(`API Error: ${response.response || JSON.stringify(response)}`);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
console.log('Unexpected response:');
|
|
162
|
+
console.log(JSON.stringify(response, null, 2));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
console.error('Error executing outcome order:', error);
|
|
167
|
+
console.error('Note: Hyperliquid currently documents outcomeMeta as testnet-only.');
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scale.d.ts","sourceRoot":"","sources":["../../scripts/operations/scale.ts"],"names":[],"mappings":""}
|