openbroker 1.0.49 → 1.0.51
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 +11 -0
- package/SKILL.md +16 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/plugin/tools.ts +28 -19
- package/scripts/plugin/watcher.ts +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Open Broker will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.0.51] - 2026-03-09
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Watcher Poll Logging**: Position watcher now logs each poll cycle at debug level — shows position count, equity, and margin usage so you can confirm the watcher is running.
|
|
9
|
+
|
|
10
|
+
## [1.0.50] - 2026-03-09
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Plugin `ob_search` HIP-3 Results**: Fixed empty results for HIP-3 assets when using the `ob_search` plugin tool. Added type filter normalization (handles `HIP3`, `HIP-3`, `hip3`, `all`), added `enum` constraint to type parameter schema, surfaced errors instead of silently swallowing them, and aligned HIP-3 iteration with CLI search (index-based with null guards).
|
|
14
|
+
- **SKILL.md**: Added "Finding Assets Before Trading" section instructing agents to always search for unfamiliar assets before trading, with examples of `ob_search` and `openbroker search`.
|
|
15
|
+
|
|
5
16
|
## [1.0.49] - 2026-03-09
|
|
6
17
|
|
|
7
18
|
### 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.51", "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
|
|
|
@@ -31,6 +31,21 @@ openbroker account
|
|
|
31
31
|
openbroker buy --coin ETH --size 0.1
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
## Important: Finding Assets Before Trading
|
|
35
|
+
|
|
36
|
+
**Always search before trading an unfamiliar asset.** Hyperliquid has main perps (ETH, BTC, SOL...), HIP-3 perps (xyz:CL, xyz:GOLD, km:USOIL...), and spot markets. Use search to discover the correct ticker:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
openbroker search --query GOLD # Find all GOLD markets across all providers
|
|
40
|
+
openbroker search --query oil # Find oil-related assets (CL, BRENTOIL, USOIL...)
|
|
41
|
+
openbroker search --query BTC --type perp # BTC perps only
|
|
42
|
+
openbroker search --query NATGAS --type hip3 # HIP-3 only
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or with the `ob_search` plugin tool: `{ "query": "gold" }` or `{ "query": "oil", "type": "hip3" }`
|
|
46
|
+
|
|
47
|
+
**HIP-3 assets use `dex:COIN` format** — e.g., `xyz:CL` not just `CL`. If you get an error like "No market data found", search for the asset to find the correct prefixed ticker. Common HIP-3 dexes: `xyz`, `flx`, `km`, `hyna`, `vntl`, `cash`.
|
|
48
|
+
|
|
34
49
|
## Command Reference
|
|
35
50
|
|
|
36
51
|
### Setup
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/scripts/plugin/tools.ts
CHANGED
|
@@ -200,7 +200,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
200
200
|
type: 'object',
|
|
201
201
|
properties: {
|
|
202
202
|
query: { type: 'string', description: 'Search query (e.g. GOLD, BTC, ETH)' },
|
|
203
|
-
type: { type: 'string', description: 'Filter by market type: perp, hip3, spot' },
|
|
203
|
+
type: { type: 'string', enum: ['perp', 'hip3', 'spot', 'all'], description: 'Filter by market type: perp, hip3, spot, or all (default: all)' },
|
|
204
204
|
},
|
|
205
205
|
required: ['query'],
|
|
206
206
|
},
|
|
@@ -208,35 +208,42 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
208
208
|
const { getClient } = await import('../core/client.js');
|
|
209
209
|
const client = getClient();
|
|
210
210
|
const query = (params.query as string).toUpperCase();
|
|
211
|
-
|
|
211
|
+
// Normalize type filter: lowercase, strip hyphens, treat "all" as no filter
|
|
212
|
+
const rawType = params.type ? String(params.type).toLowerCase().replace(/-/g, '') : undefined;
|
|
213
|
+
const typeFilter = rawType === 'all' ? undefined : rawType;
|
|
212
214
|
|
|
213
215
|
const results: Array<Record<string, unknown>> = [];
|
|
216
|
+
const errors: string[] = [];
|
|
214
217
|
|
|
215
218
|
// Search main perps
|
|
216
219
|
if (!typeFilter || typeFilter === 'perp') {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
220
|
+
try {
|
|
221
|
+
const { meta, assetCtxs } = await client.getMetaAndAssetCtxs();
|
|
222
|
+
for (let i = 0; i < meta.universe.length; i++) {
|
|
223
|
+
const asset = meta.universe[i];
|
|
224
|
+
if (asset.name.toUpperCase().includes(query)) {
|
|
225
|
+
results.push({
|
|
226
|
+
coin: asset.name,
|
|
227
|
+
type: 'perp',
|
|
228
|
+
markPx: assetCtxs[i]?.markPx,
|
|
229
|
+
dayVolume: assetCtxs[i]?.dayNtlVlm,
|
|
230
|
+
maxLeverage: asset.maxLeverage,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
228
233
|
}
|
|
229
|
-
}
|
|
234
|
+
} catch (e) { errors.push(`perp: ${e instanceof Error ? e.message : String(e)}`); }
|
|
230
235
|
}
|
|
231
236
|
|
|
232
237
|
// Search HIP-3 perps
|
|
233
238
|
if (!typeFilter || typeFilter === 'hip3' || typeFilter === 'perp') {
|
|
234
239
|
try {
|
|
235
240
|
const allPerps = await client.getAllPerpMetas();
|
|
236
|
-
for (
|
|
237
|
-
|
|
241
|
+
for (let dexIdx = 1; dexIdx < allPerps.length; dexIdx++) {
|
|
242
|
+
const dexData = allPerps[dexIdx];
|
|
243
|
+
if (!dexData || !dexData.meta?.universe) continue;
|
|
238
244
|
for (let i = 0; i < dexData.meta.universe.length; i++) {
|
|
239
245
|
const asset = dexData.meta.universe[i];
|
|
246
|
+
if (!asset) continue;
|
|
240
247
|
if (asset.name.toUpperCase().includes(query)) {
|
|
241
248
|
results.push({
|
|
242
249
|
// API returns names already prefixed (e.g., "xyz:CL")
|
|
@@ -250,7 +257,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
250
257
|
}
|
|
251
258
|
}
|
|
252
259
|
}
|
|
253
|
-
} catch {
|
|
260
|
+
} catch (e) { errors.push(`hip3: ${e instanceof Error ? e.message : String(e)}`); }
|
|
254
261
|
}
|
|
255
262
|
|
|
256
263
|
// Search spot
|
|
@@ -268,10 +275,12 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
|
|
|
268
275
|
});
|
|
269
276
|
}
|
|
270
277
|
}
|
|
271
|
-
} catch {
|
|
278
|
+
} catch (e) { errors.push(`spot: ${e instanceof Error ? e.message : String(e)}`); }
|
|
272
279
|
}
|
|
273
280
|
|
|
274
|
-
|
|
281
|
+
const response: Record<string, unknown> = { query, results };
|
|
282
|
+
if (errors.length > 0) response.errors = errors;
|
|
283
|
+
return json(response);
|
|
275
284
|
},
|
|
276
285
|
},
|
|
277
286
|
|
|
@@ -120,6 +120,12 @@ export class PositionWatcher implements PluginService {
|
|
|
120
120
|
const state = await client.getUserState(this.accountAddress);
|
|
121
121
|
|
|
122
122
|
const snapshot = this.buildSnapshot(state);
|
|
123
|
+
|
|
124
|
+
const posCount = snapshot.positions.size;
|
|
125
|
+
const equity = parseFloat(snapshot.equity).toFixed(2);
|
|
126
|
+
const marginPct = snapshot.marginUsedPct.toFixed(1);
|
|
127
|
+
this.logger.debug(`Poll: ${posCount} position(s), equity $${equity}, margin ${marginPct}%`);
|
|
128
|
+
|
|
123
129
|
const events = this.seeded ? this.detectEvents(snapshot) : [];
|
|
124
130
|
|
|
125
131
|
if (events.length > 0) {
|
|
@@ -128,6 +134,8 @@ export class PositionWatcher implements PluginService {
|
|
|
128
134
|
this.logger.info(`[${event.type}] ${event.message}`);
|
|
129
135
|
await this.sendHook(event);
|
|
130
136
|
}
|
|
137
|
+
} else if (this.seeded) {
|
|
138
|
+
this.logger.debug('No position changes detected');
|
|
131
139
|
}
|
|
132
140
|
|
|
133
141
|
this.previousSnapshot = snapshot;
|