openbroker 1.0.75 → 1.0.80
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/SKILL.md +6 -2
- package/bin/cli.ts +16 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/auto/audit-daemon.js +567 -0
- package/scripts/auto/audit.ts +552 -0
- package/scripts/auto/cli.ts +32 -0
- package/scripts/auto/report.ts +459 -0
- package/scripts/auto/runtime.ts +264 -79
- package/scripts/auto/types.ts +10 -0
- package/scripts/core/client.ts +245 -0
- package/scripts/core/ws.ts +25 -0
- package/scripts/info/funding-history.ts +5 -5
- package/scripts/info/search-markets.ts +30 -6
- package/scripts/info/spot.ts +23 -8
- package/scripts/operations/spot-order.ts +189 -0
- package/scripts/plugin/tools.ts +126 -6
package/scripts/plugin/tools.ts
CHANGED
|
@@ -398,14 +398,27 @@ export function createTools(watcherOrCtx: PositionWatcher | null | ToolsContext)
|
|
|
398
398
|
if (!typeFilter || typeFilter === 'spot') {
|
|
399
399
|
try {
|
|
400
400
|
const spotData = await client.getSpotMetaAndAssetCtxs();
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
401
|
+
// Build ctx map by coin name — contexts are NOT aligned with universe by index
|
|
402
|
+
const ctxMap = new Map<string, Record<string, string>>();
|
|
403
|
+
for (const ctx of spotData.assetCtxs as Array<Record<string, string>>) {
|
|
404
|
+
if (ctx.coin) ctxMap.set(ctx.coin, ctx);
|
|
405
|
+
}
|
|
406
|
+
// Build token name map
|
|
407
|
+
const tMap = new Map<number, string>();
|
|
408
|
+
for (const t of spotData.meta.tokens) tMap.set(t.index, t.name);
|
|
409
|
+
|
|
410
|
+
for (const pair of spotData.meta.universe) {
|
|
411
|
+
const baseName = tMap.get(pair.tokens[0]) ?? '';
|
|
412
|
+
const quoteName = tMap.get(pair.tokens[1]) ?? '';
|
|
413
|
+
const searchable = `${pair.name} ${baseName} ${quoteName}`.toUpperCase();
|
|
414
|
+
if (searchable.includes(query)) {
|
|
415
|
+
const ctx = ctxMap.get(pair.name);
|
|
416
|
+
const displayName = baseName && quoteName ? `${baseName}/${quoteName}` : pair.name;
|
|
404
417
|
results.push({
|
|
405
|
-
coin:
|
|
418
|
+
coin: displayName,
|
|
406
419
|
type: 'spot',
|
|
407
|
-
markPx:
|
|
408
|
-
dayVolume:
|
|
420
|
+
markPx: ctx?.markPx,
|
|
421
|
+
dayVolume: ctx?.dayNtlVlm,
|
|
409
422
|
});
|
|
410
423
|
}
|
|
411
424
|
}
|
|
@@ -1193,6 +1206,113 @@ export function createTools(watcherOrCtx: PositionWatcher | null | ToolsContext)
|
|
|
1193
1206
|
},
|
|
1194
1207
|
},
|
|
1195
1208
|
|
|
1209
|
+
{
|
|
1210
|
+
name: 'ob_spot_buy',
|
|
1211
|
+
description: 'Buy spot tokens on Hyperliquid. Always use dry=true first to preview.',
|
|
1212
|
+
parameters: {
|
|
1213
|
+
type: 'object',
|
|
1214
|
+
properties: {
|
|
1215
|
+
coin: { type: 'string', description: 'Base token symbol (PURR, HYPE, etc.)' },
|
|
1216
|
+
size: { type: 'number', description: 'Order size in base token units' },
|
|
1217
|
+
price: { type: 'number', description: 'Limit price (omit for market order)' },
|
|
1218
|
+
tif: { type: 'string', description: 'Time-in-force for limit: Gtc, Ioc, Alo (default: Gtc)' },
|
|
1219
|
+
slippage: { type: 'number', description: 'Slippage tolerance in bps for market orders (default: 50)' },
|
|
1220
|
+
dry: { type: 'boolean', description: 'Preview without executing' },
|
|
1221
|
+
},
|
|
1222
|
+
required: ['coin', 'size'],
|
|
1223
|
+
},
|
|
1224
|
+
async execute(_id, params) {
|
|
1225
|
+
const { getClient } = await import('../core/client.js');
|
|
1226
|
+
const { formatUsd } = await import('../core/utils.js');
|
|
1227
|
+
const client = getClient();
|
|
1228
|
+
|
|
1229
|
+
if (client.isReadOnly) return error('Wallet not configured. Run "openbroker setup" first.');
|
|
1230
|
+
|
|
1231
|
+
const coin = (params.coin as string).toUpperCase();
|
|
1232
|
+
const size = params.size as number;
|
|
1233
|
+
const price = params.price as number | undefined;
|
|
1234
|
+
const isMarket = price === undefined;
|
|
1235
|
+
|
|
1236
|
+
if (params.dry) {
|
|
1237
|
+
// Use allMids for accurate spot price preview
|
|
1238
|
+
await client.getMetaAndAssetCtxs(); // ensure spot meta loaded
|
|
1239
|
+
const spotIdx = client.getSpotAssetIndex(coin);
|
|
1240
|
+
const mids = await client.getAllMids();
|
|
1241
|
+
const spotKey = spotIdx !== undefined ? (spotIdx === 10000 ? 'PURR/USDC' : `@${spotIdx - 10000}`) : '';
|
|
1242
|
+
const midPrice = parseFloat(mids[spotKey] || '0');
|
|
1243
|
+
return json({
|
|
1244
|
+
dryRun: true,
|
|
1245
|
+
action: 'spot_buy',
|
|
1246
|
+
coin,
|
|
1247
|
+
size,
|
|
1248
|
+
type: isMarket ? 'market' : 'limit',
|
|
1249
|
+
midPrice,
|
|
1250
|
+
price: price ?? midPrice,
|
|
1251
|
+
notional: formatUsd(midPrice * size),
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
const result = isMarket
|
|
1256
|
+
? await client.spotMarketOrder(coin, true, size, params.slippage as number | undefined)
|
|
1257
|
+
: await client.spotLimitOrder(coin, true, size, price!, (params.tif as 'Gtc' | 'Ioc' | 'Alo') ?? 'Gtc');
|
|
1258
|
+
|
|
1259
|
+
return json({ action: 'spot_buy', coin, size, type: isMarket ? 'market' : 'limit', result });
|
|
1260
|
+
},
|
|
1261
|
+
},
|
|
1262
|
+
|
|
1263
|
+
{
|
|
1264
|
+
name: 'ob_spot_sell',
|
|
1265
|
+
description: 'Sell spot tokens on Hyperliquid. Always use dry=true first to preview.',
|
|
1266
|
+
parameters: {
|
|
1267
|
+
type: 'object',
|
|
1268
|
+
properties: {
|
|
1269
|
+
coin: { type: 'string', description: 'Base token symbol (PURR, HYPE, etc.)' },
|
|
1270
|
+
size: { type: 'number', description: 'Order size in base token units' },
|
|
1271
|
+
price: { type: 'number', description: 'Limit price (omit for market order)' },
|
|
1272
|
+
tif: { type: 'string', description: 'Time-in-force for limit: Gtc, Ioc, Alo (default: Gtc)' },
|
|
1273
|
+
slippage: { type: 'number', description: 'Slippage tolerance in bps for market orders (default: 50)' },
|
|
1274
|
+
dry: { type: 'boolean', description: 'Preview without executing' },
|
|
1275
|
+
},
|
|
1276
|
+
required: ['coin', 'size'],
|
|
1277
|
+
},
|
|
1278
|
+
async execute(_id, params) {
|
|
1279
|
+
const { getClient } = await import('../core/client.js');
|
|
1280
|
+
const { formatUsd } = await import('../core/utils.js');
|
|
1281
|
+
const client = getClient();
|
|
1282
|
+
|
|
1283
|
+
if (client.isReadOnly) return error('Wallet not configured. Run "openbroker setup" first.');
|
|
1284
|
+
|
|
1285
|
+
const coin = (params.coin as string).toUpperCase();
|
|
1286
|
+
const size = params.size as number;
|
|
1287
|
+
const price = params.price as number | undefined;
|
|
1288
|
+
const isMarket = price === undefined;
|
|
1289
|
+
|
|
1290
|
+
if (params.dry) {
|
|
1291
|
+
await client.getMetaAndAssetCtxs();
|
|
1292
|
+
const spotIdx = client.getSpotAssetIndex(coin);
|
|
1293
|
+
const mids = await client.getAllMids();
|
|
1294
|
+
const spotKey = spotIdx !== undefined ? (spotIdx === 10000 ? 'PURR/USDC' : `@${spotIdx - 10000}`) : '';
|
|
1295
|
+
const midPrice = parseFloat(mids[spotKey] || '0');
|
|
1296
|
+
return json({
|
|
1297
|
+
dryRun: true,
|
|
1298
|
+
action: 'spot_sell',
|
|
1299
|
+
coin,
|
|
1300
|
+
size,
|
|
1301
|
+
type: isMarket ? 'market' : 'limit',
|
|
1302
|
+
midPrice,
|
|
1303
|
+
price: price ?? midPrice,
|
|
1304
|
+
notional: formatUsd(midPrice * size),
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
const result = isMarket
|
|
1309
|
+
? await client.spotMarketOrder(coin, false, size, params.slippage as number | undefined)
|
|
1310
|
+
: await client.spotLimitOrder(coin, false, size, price!, (params.tif as 'Gtc' | 'Ioc' | 'Alo') ?? 'Gtc');
|
|
1311
|
+
|
|
1312
|
+
return json({ action: 'spot_sell', coin, size, type: isMarket ? 'market' : 'limit', result });
|
|
1313
|
+
},
|
|
1314
|
+
},
|
|
1315
|
+
|
|
1196
1316
|
{
|
|
1197
1317
|
name: 'ob_cancel',
|
|
1198
1318
|
description: 'Cancel open orders on Hyperliquid',
|