openbroker 1.0.65 → 1.0.67
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/README.md +4 -1
- package/SKILL.md +30 -11
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/core/client.ts +22 -2
- package/scripts/info/orders.ts +70 -0
- package/scripts/plugin/tools.ts +31 -2
package/README.md
CHANGED
|
@@ -172,10 +172,12 @@ openbroker fills --coin BTC --side buy --top 50
|
|
|
172
172
|
|
|
173
173
|
#### `orders` — Order History
|
|
174
174
|
|
|
175
|
-
View all historical orders including filled, canceled, and rejected.
|
|
175
|
+
View all historical orders including filled, canceled, and rejected. Use `--open` to show only currently open orders.
|
|
176
176
|
|
|
177
177
|
```bash
|
|
178
178
|
openbroker orders # Recent orders
|
|
179
|
+
openbroker orders --open # Currently open orders only
|
|
180
|
+
openbroker orders --open --coin ETH # Open orders for a specific coin
|
|
179
181
|
openbroker orders --coin ETH --status filled
|
|
180
182
|
openbroker orders --top 50
|
|
181
183
|
```
|
|
@@ -184,6 +186,7 @@ openbroker orders --top 50
|
|
|
184
186
|
|------|-------------|---------|
|
|
185
187
|
| `--coin` | Filter by coin symbol | — |
|
|
186
188
|
| `--status` | Filter by status (filled, canceled, open, triggered, etc.) | — |
|
|
189
|
+
| `--open` | Show only currently open orders | — |
|
|
187
190
|
| `--top` | Number of recent orders to show | `20` |
|
|
188
191
|
|
|
189
192
|
#### `order-status` — Check Order Status
|
package/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Hyperliquid trading plugin with background position monitoring and
|
|
|
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.67", "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 ob_auto_run ob_auto_stop ob_auto_list Bash(openbroker:*)
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -100,7 +100,7 @@ The simplest setup for agents. A fresh wallet is generated, the builder fee is a
|
|
|
100
100
|
**Flow:**
|
|
101
101
|
1. Run `openbroker setup` and choose option 1 ("Generate a fresh wallet")
|
|
102
102
|
2. The CLI generates a wallet, saves the config, and approves the builder fee automatically
|
|
103
|
-
3. Fund the wallet
|
|
103
|
+
3. Fund the wallet by sending USDC from your Hyperliquid account to the agent's wallet address using the **Send** feature on https://app.hyperliquid.xyz/. **Funding should be done on Hyperliquid L1 only.**
|
|
104
104
|
4. Start trading
|
|
105
105
|
|
|
106
106
|
### API Wallet Setup (Alternative)
|
|
@@ -177,6 +177,8 @@ openbroker fills --coin BTC --side buy --top 50
|
|
|
177
177
|
### Order History
|
|
178
178
|
```bash
|
|
179
179
|
openbroker orders # Recent orders (all statuses)
|
|
180
|
+
openbroker orders --open # Currently open orders only
|
|
181
|
+
openbroker orders --open --coin ETH # Open orders for a specific coin
|
|
180
182
|
openbroker orders --coin ETH --status filled
|
|
181
183
|
openbroker orders --top 50
|
|
182
184
|
```
|
|
@@ -912,15 +914,32 @@ openbroker auto status # Show running automations
|
|
|
912
914
|
| `--id <name>` | Custom automation ID | filename |
|
|
913
915
|
| `--poll <ms>` | Poll interval in milliseconds | 10000 |
|
|
914
916
|
|
|
915
|
-
**
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
-
|
|
919
|
-
- Use `api.publish()` to
|
|
920
|
-
-
|
|
921
|
-
-
|
|
922
|
-
-
|
|
923
|
-
-
|
|
917
|
+
**Guidelines for agents writing automations:**
|
|
918
|
+
|
|
919
|
+
**Risk & Safety (mandatory):**
|
|
920
|
+
- Always attach a liquidation monitoring automation to every open position. Subscribe to `margin_warning` and `pnl_threshold` events so the user is never blindsided by liquidation risk. If no margin/liquidation automation is already running, create one before placing trades.
|
|
921
|
+
- Use `api.publish()` to notify the user of important events — position opens/closes, TP/SL triggers, large PnL swings, margin warnings, errors, and any situation that requires human attention. Do NOT silently handle critical events.
|
|
922
|
+
- Always register an `api.onStop()` handler to clean up — cancel open orders and close positions (or at minimum alert the user) on shutdown. Never leave orphaned orders or unmanaged positions.
|
|
923
|
+
- Do NOT use `--dry` unless the user explicitly asks for it. Automations should run live by default.
|
|
924
|
+
- Never place trades without validating that sufficient margin is available. Check account state before sizing orders.
|
|
925
|
+
- Cap position sizes relative to account equity. Do not risk more than a reasonable percentage of equity on a single trade unless the user explicitly specifies the size.
|
|
926
|
+
- Always set TP/SL on new positions — either within the automation or by confirming the user has them set. Unprotected positions are a liability.
|
|
927
|
+
|
|
928
|
+
**State & Reliability:**
|
|
929
|
+
- Use `api.state` to track position state, entry prices, and flags across restarts. Never rely on in-memory variables alone — automations persist across gateway restarts and are automatically restarted.
|
|
930
|
+
- Use idempotency guards (`api.state.get`/`set`) to prevent duplicate orders. Events can fire multiple times for the same condition across polls — always check state before placing orders.
|
|
931
|
+
- The runtime catches errors per handler — one failing handler won't crash others, but always handle expected errors (e.g. order rejection, insufficient margin) gracefully within handlers.
|
|
932
|
+
|
|
933
|
+
**Communication:**
|
|
934
|
+
- Use `api.publish()` to send alerts/events back to the OpenClaw agent — do NOT manually construct webhook requests.
|
|
935
|
+
- Publish on: position opened/closed, TP/SL triggered, PnL threshold exceeded, margin warning, automation errors, and any automated trade execution. The user should always know what the automation did and why.
|
|
936
|
+
- Include actionable context in publish messages — coin, price, size, PnL, and what happened — so the user can make informed decisions without checking the terminal.
|
|
937
|
+
|
|
938
|
+
**General:**
|
|
939
|
+
- Scripts are loaded from `~/.openbroker/automations/` by name, or from any absolute path.
|
|
940
|
+
- All trading commands support HIP-3 assets (`api.client.marketOrder('xyz:CL', true, 1)`).
|
|
941
|
+
- Automations persist across gateway restarts — they are automatically restarted when the gateway comes back up.
|
|
942
|
+
- Prefer `api.every(ms, fn)` over `tick` for periodic tasks with intervals longer than the poll cycle.
|
|
924
943
|
|
|
925
944
|
## Risk Warning
|
|
926
945
|
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/scripts/core/client.ts
CHANGED
|
@@ -1221,8 +1221,28 @@ export class HyperliquidClient {
|
|
|
1221
1221
|
|
|
1222
1222
|
async getOpenOrders(user?: string): Promise<OpenOrder[]> {
|
|
1223
1223
|
this.log('Fetching openOrders for:', user ?? this.address);
|
|
1224
|
-
|
|
1225
|
-
|
|
1224
|
+
await this.getMetaAndAssetCtxs(); // Ensure HIP-3 dex list is loaded
|
|
1225
|
+
|
|
1226
|
+
// Fetch main dex orders
|
|
1227
|
+
const orders = await this.info.openOrders({ user: user ?? this.address }) as OpenOrder[];
|
|
1228
|
+
|
|
1229
|
+
// Fetch HIP-3 dex orders
|
|
1230
|
+
const dexs = await this.getPerpDexs();
|
|
1231
|
+
for (let i = 1; i < dexs.length; i++) {
|
|
1232
|
+
const dex = dexs[i];
|
|
1233
|
+
if (!dex) continue;
|
|
1234
|
+
try {
|
|
1235
|
+
const dexOrders = await this.info.openOrders({ user: user ?? this.address, dex: dex.name }) as OpenOrder[];
|
|
1236
|
+
if (dexOrders.length > 0) {
|
|
1237
|
+
this.log(`Found ${dexOrders.length} open orders on HIP-3 dex ${dex.name}`);
|
|
1238
|
+
orders.push(...dexOrders);
|
|
1239
|
+
}
|
|
1240
|
+
} catch (err) {
|
|
1241
|
+
this.log(`Failed to fetch open orders for dex ${dex.name}:`, err instanceof Error ? err.message : String(err));
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
return orders;
|
|
1226
1246
|
}
|
|
1227
1247
|
|
|
1228
1248
|
// ============ Trading ============
|
package/scripts/info/orders.ts
CHANGED
|
@@ -11,11 +11,14 @@ Usage: openbroker orders [options]
|
|
|
11
11
|
Options:
|
|
12
12
|
--coin <symbol> Filter by coin (e.g. ETH, BTC)
|
|
13
13
|
--status <status> Filter by status (filled, canceled, open, triggered, rejected, etc.)
|
|
14
|
+
--open Show only currently open orders
|
|
14
15
|
--top <n> Show last N orders (default: 20)
|
|
15
16
|
--help, -h Show this help
|
|
16
17
|
|
|
17
18
|
Examples:
|
|
18
19
|
openbroker orders
|
|
20
|
+
openbroker orders --open
|
|
21
|
+
openbroker orders --open --coin ETH
|
|
19
22
|
openbroker orders --coin ETH --status filled
|
|
20
23
|
openbroker orders --top 50
|
|
21
24
|
`);
|
|
@@ -31,11 +34,78 @@ async function main() {
|
|
|
31
34
|
|
|
32
35
|
const filterCoin = args.coin as string | undefined;
|
|
33
36
|
const filterStatus = args.status as string | undefined;
|
|
37
|
+
const openOnly = args.open as boolean;
|
|
34
38
|
const top = parseInt(args.top as string) || 20;
|
|
35
39
|
const jsonOutput = args.json as boolean;
|
|
36
40
|
const client = getClient();
|
|
37
41
|
|
|
38
42
|
try {
|
|
43
|
+
if (openOnly) {
|
|
44
|
+
// Use the dedicated open orders endpoint
|
|
45
|
+
let openOrders = await client.getOpenOrders();
|
|
46
|
+
|
|
47
|
+
if (filterCoin) {
|
|
48
|
+
openOrders = openOrders.filter(o => o.coin === normalizeCoin(filterCoin));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
openOrders.sort((a, b) => b.timestamp - a.timestamp);
|
|
52
|
+
openOrders = openOrders.slice(0, top);
|
|
53
|
+
|
|
54
|
+
if (jsonOutput) {
|
|
55
|
+
console.log(JSON.stringify(openOrders.map(o => ({
|
|
56
|
+
time: new Date(o.timestamp).toISOString(),
|
|
57
|
+
coin: o.coin,
|
|
58
|
+
side: o.side === 'B' ? 'buy' : 'sell',
|
|
59
|
+
orderType: o.orderType,
|
|
60
|
+
size: o.sz,
|
|
61
|
+
origSize: o.origSz,
|
|
62
|
+
price: o.limitPx,
|
|
63
|
+
status: 'open',
|
|
64
|
+
oid: o.oid,
|
|
65
|
+
})), null, 2));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log('Open Broker - Open Orders');
|
|
70
|
+
console.log('=========================\n');
|
|
71
|
+
|
|
72
|
+
if (openOrders.length === 0) {
|
|
73
|
+
console.log('No open orders found');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Table header
|
|
78
|
+
console.log(
|
|
79
|
+
'Time'.padEnd(20) +
|
|
80
|
+
'Coin'.padEnd(10) +
|
|
81
|
+
'Side'.padEnd(6) +
|
|
82
|
+
'Type'.padEnd(14) +
|
|
83
|
+
'Size'.padEnd(12) +
|
|
84
|
+
'Price'.padEnd(14) +
|
|
85
|
+
'OID'
|
|
86
|
+
);
|
|
87
|
+
console.log('─'.repeat(90));
|
|
88
|
+
|
|
89
|
+
for (const o of openOrders) {
|
|
90
|
+
const time = new Date(o.timestamp).toLocaleString();
|
|
91
|
+
const side = o.side === 'B' ? 'BUY' : 'SELL';
|
|
92
|
+
|
|
93
|
+
console.log(
|
|
94
|
+
time.padEnd(20) +
|
|
95
|
+
o.coin.padEnd(10) +
|
|
96
|
+
side.padEnd(6) +
|
|
97
|
+
o.orderType.padEnd(14) +
|
|
98
|
+
o.sz.padEnd(12) +
|
|
99
|
+
formatUsd(parseFloat(o.limitPx)).padEnd(14) +
|
|
100
|
+
String(o.oid)
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log('─'.repeat(90));
|
|
105
|
+
console.log(`Showing ${openOrders.length} open orders`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
39
109
|
let orders = await client.getHistoricalOrders();
|
|
40
110
|
|
|
41
111
|
if (filterCoin) {
|
package/scripts/plugin/tools.ts
CHANGED
|
@@ -491,18 +491,48 @@ export function createTools(watcherOrCtx: PositionWatcher | null | ToolsContext)
|
|
|
491
491
|
|
|
492
492
|
{
|
|
493
493
|
name: 'ob_orders',
|
|
494
|
-
description: 'View order history with status (filled, canceled, open, etc.)',
|
|
494
|
+
description: 'View order history with status (filled, canceled, open, etc.). Use open_only to get currently open orders.',
|
|
495
495
|
parameters: {
|
|
496
496
|
type: 'object',
|
|
497
497
|
properties: {
|
|
498
498
|
coin: { type: 'string', description: 'Filter by coin symbol' },
|
|
499
499
|
status: { type: 'string', description: 'Filter by status (filled, canceled, open, etc.)' },
|
|
500
|
+
open_only: { type: 'boolean', description: 'If true, return only currently open orders (uses dedicated endpoint)' },
|
|
500
501
|
top: { type: 'number', description: 'Number of recent orders (default: 20)' },
|
|
501
502
|
},
|
|
502
503
|
},
|
|
503
504
|
async execute(_id, params) {
|
|
504
505
|
const { getClient } = await import('../core/client.js');
|
|
505
506
|
const client = getClient();
|
|
507
|
+
const top = (params.top as number) || 20;
|
|
508
|
+
|
|
509
|
+
if (params.open_only) {
|
|
510
|
+
let openOrders = await client.getOpenOrders();
|
|
511
|
+
|
|
512
|
+
if (params.coin) {
|
|
513
|
+
const coin = normalizeCoin(params.coin as string);
|
|
514
|
+
openOrders = openOrders.filter(o => o.coin === coin);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
openOrders.sort((a, b) => b.timestamp - a.timestamp);
|
|
518
|
+
openOrders = openOrders.slice(0, top);
|
|
519
|
+
|
|
520
|
+
return json({
|
|
521
|
+
address: client.address,
|
|
522
|
+
orders: openOrders.map(o => ({
|
|
523
|
+
coin: o.coin,
|
|
524
|
+
side: o.side === 'B' ? 'buy' : 'sell',
|
|
525
|
+
size: o.sz,
|
|
526
|
+
origSize: o.origSz,
|
|
527
|
+
price: o.limitPx,
|
|
528
|
+
orderType: o.orderType,
|
|
529
|
+
oid: o.oid,
|
|
530
|
+
status: 'open',
|
|
531
|
+
timestamp: o.timestamp,
|
|
532
|
+
})),
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
|
|
506
536
|
let orders = await client.getHistoricalOrders();
|
|
507
537
|
|
|
508
538
|
if (params.coin) {
|
|
@@ -515,7 +545,6 @@ export function createTools(watcherOrCtx: PositionWatcher | null | ToolsContext)
|
|
|
515
545
|
}
|
|
516
546
|
|
|
517
547
|
orders.sort((a, b) => b.order.timestamp - a.order.timestamp);
|
|
518
|
-
const top = (params.top as number) || 20;
|
|
519
548
|
orders = orders.slice(0, top);
|
|
520
549
|
|
|
521
550
|
return json({
|