openbroker 1.9.2 → 1.9.4
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 +21 -0
- package/README.md +16 -0
- package/SKILL.md +69 -3
- package/dist/auto/cli.js +4 -1
- package/dist/auto/examples/dca.d.ts +2 -1
- package/dist/auto/examples/dca.d.ts.map +1 -1
- package/dist/auto/examples/dca.js +19 -1
- package/dist/auto/examples/funding-arb.d.ts +2 -1
- package/dist/auto/examples/funding-arb.d.ts.map +1 -1
- package/dist/auto/examples/funding-arb.js +19 -2
- package/dist/auto/examples/grid.d.ts +2 -1
- package/dist/auto/examples/grid.d.ts.map +1 -1
- package/dist/auto/examples/grid.js +18 -2
- package/dist/auto/examples/mm-maker.d.ts +2 -1
- package/dist/auto/examples/mm-maker.d.ts.map +1 -1
- package/dist/auto/examples/mm-maker.js +18 -2
- package/dist/auto/examples/mm-spread.d.ts +2 -1
- package/dist/auto/examples/mm-spread.d.ts.map +1 -1
- package/dist/auto/examples/mm-spread.js +18 -2
- package/dist/auto/examples/price-alert.d.ts +2 -1
- package/dist/auto/examples/price-alert.d.ts.map +1 -1
- package/dist/auto/examples/price-alert.js +2 -1
- package/dist/auto/guardrails.d.ts +19 -0
- package/dist/auto/guardrails.d.ts.map +1 -0
- package/dist/auto/guardrails.js +575 -0
- package/dist/auto/guardrails.test.d.ts +2 -0
- package/dist/auto/guardrails.test.d.ts.map +1 -0
- package/dist/auto/guardrails.test.js +173 -0
- package/dist/auto/loader.d.ts +3 -3
- package/dist/auto/loader.d.ts.map +1 -1
- package/dist/auto/loader.js +25 -3
- package/dist/auto/realtime.d.ts +45 -0
- package/dist/auto/realtime.d.ts.map +1 -0
- package/dist/auto/realtime.js +177 -0
- package/dist/auto/realtime.test.d.ts +2 -0
- package/dist/auto/realtime.test.d.ts.map +1 -0
- package/dist/auto/realtime.test.js +73 -0
- package/dist/auto/runtime.d.ts +3 -3
- package/dist/auto/runtime.d.ts.map +1 -1
- package/dist/auto/runtime.js +155 -65
- package/dist/auto/types.d.ts +45 -1
- package/dist/auto/types.d.ts.map +1 -1
- package/dist/core/client.d.ts +66 -1
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +141 -4
- package/dist/core/ws.d.ts +14 -2
- package/dist/core/ws.d.ts.map +1 -1
- package/dist/core/ws.js +53 -7
- package/dist/lib.d.ts +2 -0
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +1 -0
- package/package.json +5 -3
- package/scripts/auto/cli.ts +4 -1
- package/scripts/auto/examples/dca.ts +21 -2
- package/scripts/auto/examples/funding-arb.ts +21 -3
- package/scripts/auto/examples/grid.ts +20 -3
- package/scripts/auto/examples/mm-maker.ts +20 -3
- package/scripts/auto/examples/mm-spread.ts +20 -3
- package/scripts/auto/examples/price-alert.ts +4 -2
- package/scripts/auto/guardrails.test.ts +227 -0
- package/scripts/auto/guardrails.ts +700 -0
- package/scripts/auto/loader.ts +41 -4
- package/scripts/auto/realtime.test.ts +84 -0
- package/scripts/auto/realtime.ts +194 -0
- package/scripts/auto/runtime.ts +163 -69
- package/scripts/auto/types.ts +56 -1
- package/scripts/core/client.ts +175 -4
- package/scripts/core/ws.ts +57 -8
- package/scripts/lib.ts +10 -0
package/dist/auto/runtime.js
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import os from 'os';
|
|
6
|
-
import {
|
|
6
|
+
import { HyperliquidClient } from '../core/client.js';
|
|
7
7
|
import { roundPrice, roundSize, sleep, normalizeCoin, formatUsd, formatPercent, annualizeFundingRate, } from '../core/utils.js';
|
|
8
8
|
import { WebSocketManager } from '../core/ws.js';
|
|
9
|
+
import { AutomationRealtimeData } from './realtime.js';
|
|
9
10
|
import { AutomationEventBus } from './events.js';
|
|
10
11
|
import { loadAutomation } from './loader.js';
|
|
12
|
+
import { CLIENT_WRITE_METHODS, createGuardrailedClient } from './guardrails.js';
|
|
11
13
|
import { registerAutomation, unregisterAutomation, getRegisteredAutomations as getRegisteredFromFile } from './registry.js';
|
|
12
14
|
import { createAutomationAudit, toSerializable } from './audit.js';
|
|
13
15
|
import { startKeepAwake } from './keep-awake.js';
|
|
@@ -74,12 +76,6 @@ function fanOutAgentAction(observers, action, status, details, txHash) {
|
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
78
|
const STATE_DIR = path.join(os.homedir(), '.openbroker', 'state');
|
|
77
|
-
const AUDITED_WRITE_METHODS = new Set([
|
|
78
|
-
'order', 'marketOrder', 'limitOrder', 'triggerOrder',
|
|
79
|
-
'takeProfit', 'stopLoss', 'cancel', 'cancelAll',
|
|
80
|
-
'spotOrder', 'spotMarketOrder', 'spotLimitOrder', 'spotCancel',
|
|
81
|
-
'updateLeverage', 'approveBuilderFee', 'twapOrder', 'twapCancel',
|
|
82
|
-
]);
|
|
83
79
|
function createState(id) {
|
|
84
80
|
mkdirSync(STATE_DIR, { recursive: true });
|
|
85
81
|
const stateFile = path.join(STATE_DIR, `${id}.json`);
|
|
@@ -157,18 +153,11 @@ function createLogger(id, verbose, audit) {
|
|
|
157
153
|
};
|
|
158
154
|
}
|
|
159
155
|
// ── Dry-run client proxy ────────────────────────────────────────────
|
|
160
|
-
const WRITE_METHODS = new Set([
|
|
161
|
-
'order', 'marketOrder', 'limitOrder', 'triggerOrder',
|
|
162
|
-
'takeProfit', 'stopLoss', 'cancel', 'cancelAll',
|
|
163
|
-
'updateLeverage', 'approveBuilderFee',
|
|
164
|
-
'spotOrder', 'spotMarketOrder', 'spotLimitOrder', 'spotCancel',
|
|
165
|
-
'twapOrder', 'twapCancel',
|
|
166
|
-
]);
|
|
167
156
|
function createDryClient(client, log) {
|
|
168
157
|
return new Proxy(client, {
|
|
169
158
|
get(target, prop, receiver) {
|
|
170
159
|
const value = Reflect.get(target, prop, receiver);
|
|
171
|
-
if (typeof prop === 'string' &&
|
|
160
|
+
if (typeof prop === 'string' && CLIENT_WRITE_METHODS.has(prop) && typeof value === 'function') {
|
|
172
161
|
return (...args) => {
|
|
173
162
|
log.info(`[DRY] ${prop}(${args.map(a => JSON.stringify(a)).join(', ')})`);
|
|
174
163
|
return Promise.resolve({ status: 'ok', response: { type: 'dry_run' } });
|
|
@@ -182,7 +171,7 @@ function createAuditedClient(client, audit, dryRun, observers) {
|
|
|
182
171
|
return new Proxy(client, {
|
|
183
172
|
get(target, prop, receiver) {
|
|
184
173
|
const value = Reflect.get(target, prop, receiver);
|
|
185
|
-
if (typeof prop === 'string' &&
|
|
174
|
+
if (typeof prop === 'string' && CLIENT_WRITE_METHODS.has(prop) && typeof value === 'function') {
|
|
186
175
|
return async (...args) => {
|
|
187
176
|
const actionId = `${prop}:${Date.now()}:${Math.random().toString(16).slice(2)}`;
|
|
188
177
|
audit.recordAction({
|
|
@@ -356,8 +345,8 @@ export function getAutomation(id) {
|
|
|
356
345
|
export { getRegisteredFromFile as getRegisteredAutomations };
|
|
357
346
|
export async function startAutomation(options) {
|
|
358
347
|
const { scriptPath, dryRun = false, verbose = false, gatewayPort, hooksToken, initialState, useWebSocket = true, } = options;
|
|
359
|
-
//
|
|
360
|
-
//
|
|
348
|
+
// This is the disconnected REST fallback cadence. While WebSocket is live,
|
|
349
|
+
// reconciliation is clamped to at least 60 seconds below.
|
|
361
350
|
const pollIntervalMs = options.pollIntervalMs ?? (useWebSocket ? 30_000 : 10_000);
|
|
362
351
|
const id = options.id || path.basename(scriptPath, '.ts');
|
|
363
352
|
if (registry.has(id)) {
|
|
@@ -371,7 +360,10 @@ export async function startAutomation(options) {
|
|
|
371
360
|
}
|
|
372
361
|
}
|
|
373
362
|
const eventBus = new AutomationEventBus();
|
|
374
|
-
|
|
363
|
+
// Each automation owns its client + realtime cache. This prevents one run
|
|
364
|
+
// from replacing or detaching another run's WebSocket provider when several
|
|
365
|
+
// automations share a host process.
|
|
366
|
+
const rawClient = new HyperliquidClient();
|
|
375
367
|
const audit = createAutomationAudit({
|
|
376
368
|
automationId: id,
|
|
377
369
|
scriptPath,
|
|
@@ -396,8 +388,39 @@ export async function startAutomation(options) {
|
|
|
396
388
|
}
|
|
397
389
|
}
|
|
398
390
|
const observers = await loadConventionObservers(log);
|
|
391
|
+
let loaded;
|
|
392
|
+
try {
|
|
393
|
+
log.info(`Loading automation: ${scriptPath}`);
|
|
394
|
+
loaded = await loadAutomation(scriptPath, { config: stateController.snapshot() });
|
|
395
|
+
}
|
|
396
|
+
catch (err) {
|
|
397
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
398
|
+
audit.recordError('guardrail_validation', error);
|
|
399
|
+
await audit.stop({
|
|
400
|
+
status: 'error',
|
|
401
|
+
stopReason: 'guardrail_validation_error',
|
|
402
|
+
pollCount: 0,
|
|
403
|
+
eventsEmitted: 0,
|
|
404
|
+
});
|
|
405
|
+
keepAwake?.stop();
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
408
|
+
audit.recordNote('guardrails', loaded.guardrails);
|
|
399
409
|
const baseClient = dryRun ? createDryClient(rawClient, log) : rawClient;
|
|
400
|
-
const
|
|
410
|
+
const guardedClient = createGuardrailedClient(baseClient, {
|
|
411
|
+
policy: loaded.guardrails,
|
|
412
|
+
rawClient,
|
|
413
|
+
log,
|
|
414
|
+
onViolation: (error, method, args) => {
|
|
415
|
+
audit.recordNote('guardrail_block', {
|
|
416
|
+
code: error.code,
|
|
417
|
+
message: error.message,
|
|
418
|
+
method,
|
|
419
|
+
args: toSerializable(args),
|
|
420
|
+
});
|
|
421
|
+
},
|
|
422
|
+
});
|
|
423
|
+
const client = createAuditedClient(guardedClient, audit, dryRun, observers);
|
|
401
424
|
const startHooks = [];
|
|
402
425
|
const stopHooks = [];
|
|
403
426
|
const errorHooks = [];
|
|
@@ -418,7 +441,7 @@ export async function startAutomation(options) {
|
|
|
418
441
|
client,
|
|
419
442
|
utils: { roundPrice, roundSize, sleep, normalizeCoin, formatUsd, formatPercent, annualizeFundingRate },
|
|
420
443
|
on: (event, handler) => eventBus.on(event, handler),
|
|
421
|
-
every: (intervalMs, handler) => scheduledTasks.push({ intervalMs, handler, lastRun:
|
|
444
|
+
every: (intervalMs, handler) => scheduledTasks.push({ intervalMs, handler, lastRun: Date.now() }),
|
|
422
445
|
onStart: (handler) => startHooks.push(handler),
|
|
423
446
|
onStop: (handler) => stopHooks.push(handler),
|
|
424
447
|
onError: (handler) => errorHooks.push(handler),
|
|
@@ -428,23 +451,11 @@ export async function startAutomation(options) {
|
|
|
428
451
|
audit: auditApi,
|
|
429
452
|
id,
|
|
430
453
|
dryRun,
|
|
454
|
+
guardrails: loaded.guardrails,
|
|
431
455
|
};
|
|
432
456
|
try {
|
|
433
|
-
//
|
|
434
|
-
|
|
435
|
-
const factory = await loadAutomation(scriptPath);
|
|
436
|
-
await factory(api);
|
|
437
|
-
// Call onStart hooks
|
|
438
|
-
for (const hook of startHooks) {
|
|
439
|
-
try {
|
|
440
|
-
await hook();
|
|
441
|
-
}
|
|
442
|
-
catch (err) {
|
|
443
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
444
|
-
audit.recordError('onStart', error);
|
|
445
|
-
log.error(`onStart hook error: ${error.message}`);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
457
|
+
// Execute the already validated factory function (registers handlers).
|
|
458
|
+
await loaded.factory(api);
|
|
448
459
|
}
|
|
449
460
|
catch (err) {
|
|
450
461
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -464,6 +475,7 @@ export async function startAutomation(options) {
|
|
|
464
475
|
let eventsEmitted = 0;
|
|
465
476
|
let isPolling = false;
|
|
466
477
|
let stopped = false;
|
|
478
|
+
let automationReady = false;
|
|
467
479
|
async function handleErrors(errors) {
|
|
468
480
|
for (const err of errors) {
|
|
469
481
|
audit.recordError('handler', err);
|
|
@@ -491,11 +503,19 @@ export async function startAutomation(options) {
|
|
|
491
503
|
// ── WebSocket setup ─────────────────────────────────────────────
|
|
492
504
|
let ws = null;
|
|
493
505
|
let wsConnected = false;
|
|
506
|
+
let realtimeData = null;
|
|
494
507
|
// Track latest prices from WebSocket for real-time price_change events
|
|
495
508
|
let wsPrices = new Map();
|
|
509
|
+
let wsFundingRates = new Map();
|
|
496
510
|
if (useWebSocket) {
|
|
497
511
|
try {
|
|
498
512
|
ws = new WebSocketManager(verbose);
|
|
513
|
+
const unified = await rawClient.isUnifiedAccount().catch(() => null);
|
|
514
|
+
const orderDexes = await rawClient.getPerpDexs()
|
|
515
|
+
.then((dexes) => dexes.slice(1).flatMap((dex) => dex?.name ? [dex.name] : []))
|
|
516
|
+
.catch(() => []);
|
|
517
|
+
realtimeData = new AutomationRealtimeData(ws, rawClient, rawClient.address, unified, orderDexes);
|
|
518
|
+
rawClient.setRealtimeDataProvider(realtimeData);
|
|
499
519
|
// Wire WebSocket events to the automation event bus
|
|
500
520
|
ws.on('allMids', ({ mids }) => {
|
|
501
521
|
const now = Date.now();
|
|
@@ -505,7 +525,7 @@ export async function startAutomation(options) {
|
|
|
505
525
|
continue;
|
|
506
526
|
const oldPrice = wsPrices.get(coin);
|
|
507
527
|
wsPrices.set(coin, newPrice);
|
|
508
|
-
if (oldPrice !== undefined && oldPrice !== 0 && eventBus.has('price_change')) {
|
|
528
|
+
if (automationReady && oldPrice !== undefined && oldPrice !== 0 && eventBus.has('price_change')) {
|
|
509
529
|
const changePct = ((newPrice - oldPrice) / oldPrice) * 100;
|
|
510
530
|
if (Math.abs(changePct) >= 0.01) {
|
|
511
531
|
void emitAutomationEvent('price_change', { coin, oldPrice, newPrice, changePct }, 'ws');
|
|
@@ -513,6 +533,23 @@ export async function startAutomation(options) {
|
|
|
513
533
|
}
|
|
514
534
|
}
|
|
515
535
|
});
|
|
536
|
+
ws.on('allDexsAssetCtxs', ({ ctxs }) => {
|
|
537
|
+
if (!automationReady || !eventBus.has('funding_update'))
|
|
538
|
+
return;
|
|
539
|
+
const next = rawClient.fundingRatesFromWs(ctxs);
|
|
540
|
+
for (const [coin, data] of next) {
|
|
541
|
+
const previous = wsFundingRates.get(coin);
|
|
542
|
+
wsFundingRates.set(coin, data.rate);
|
|
543
|
+
if (previous === undefined || previous === data.rate)
|
|
544
|
+
continue;
|
|
545
|
+
void emitAutomationEvent('funding_update', {
|
|
546
|
+
coin,
|
|
547
|
+
fundingRate: data.rate,
|
|
548
|
+
annualized: annualizeFundingRate(data.rate),
|
|
549
|
+
premium: data.premium,
|
|
550
|
+
}, 'ws');
|
|
551
|
+
}
|
|
552
|
+
});
|
|
516
553
|
ws.on('orderUpdate', (update) => {
|
|
517
554
|
audit.recordOrderUpdate({
|
|
518
555
|
coin: update.order.coin,
|
|
@@ -525,7 +562,7 @@ export async function startAutomation(options) {
|
|
|
525
562
|
statusTimestamp: update.statusTimestamp,
|
|
526
563
|
raw: update,
|
|
527
564
|
});
|
|
528
|
-
if (eventBus.has('order_update')) {
|
|
565
|
+
if (automationReady && eventBus.has('order_update')) {
|
|
529
566
|
void emitAutomationEvent('order_update', {
|
|
530
567
|
coin: update.order.coin,
|
|
531
568
|
oid: update.order.oid,
|
|
@@ -566,7 +603,7 @@ export async function startAutomation(options) {
|
|
|
566
603
|
// Fee is converted to USD using feeToken: for non-USDC fees (spot
|
|
567
604
|
// buys pay in the received asset), fee × price yields USD since the
|
|
568
605
|
// fee token is the base of the traded pair and `price` is quote/base.
|
|
569
|
-
if (eventBus.has('order_filled')) {
|
|
606
|
+
if (automationReady && eventBus.has('order_filled')) {
|
|
570
607
|
const size = parseFloat(fill.sz);
|
|
571
608
|
const price = parseFloat(fill.px);
|
|
572
609
|
const rawFee = parseFloat(fill.fee);
|
|
@@ -600,7 +637,7 @@ export async function startAutomation(options) {
|
|
|
600
637
|
ws.on('userEvent', (event) => {
|
|
601
638
|
audit.recordUserEvent(event);
|
|
602
639
|
// Handle liquidation events — only available through WebSocket
|
|
603
|
-
if ('liquidation' in event && eventBus.has('liquidation')) {
|
|
640
|
+
if (automationReady && 'liquidation' in event && eventBus.has('liquidation')) {
|
|
604
641
|
const liq = event.liquidation;
|
|
605
642
|
void emitAutomationEvent('liquidation', {
|
|
606
643
|
lid: liq.lid,
|
|
@@ -623,10 +660,18 @@ export async function startAutomation(options) {
|
|
|
623
660
|
wsConnected = true;
|
|
624
661
|
log.info('WebSocket connected — real-time events active');
|
|
625
662
|
});
|
|
626
|
-
// Connect and
|
|
663
|
+
// Connect subscriptions and warm only the main static universe in
|
|
664
|
+
// parallel. HIP-3 metadata is loaded lazily when referenced.
|
|
627
665
|
const userAddress = rawClient.address;
|
|
628
|
-
await
|
|
629
|
-
|
|
666
|
+
await Promise.all([
|
|
667
|
+
ws.subscribeAll(userAddress, orderDexes),
|
|
668
|
+
rawClient.initializeRealtimeMetadata().catch((error) => {
|
|
669
|
+
log.warn(`Realtime metadata warmup failed: ${error instanceof Error ? error.message : String(error)}; REST fallback remains available`);
|
|
670
|
+
}),
|
|
671
|
+
]);
|
|
672
|
+
const seeded = await realtimeData.waitUntilReady();
|
|
673
|
+
const readiness = realtimeData.readinessSummary();
|
|
674
|
+
log.info(`WebSocket subscriptions active (mids, asset contexts, account state, spot state, orders, fills, user events)${seeded ? '' : ` · initial snapshots incomplete; REST fallback armed · openOrders ${readiness.seededOrderDexes}/${readiness.expectedOrderDexes}`}`);
|
|
630
675
|
}
|
|
631
676
|
catch (err) {
|
|
632
677
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -635,6 +680,8 @@ export async function startAutomation(options) {
|
|
|
635
680
|
log.debug(`WebSocket setup stack: ${error.stack}`);
|
|
636
681
|
}
|
|
637
682
|
log.warn(`WebSocket setup failed: ${error.message} — using REST polling only`);
|
|
683
|
+
rawClient.setRealtimeDataProvider(null);
|
|
684
|
+
realtimeData = null;
|
|
638
685
|
ws = null;
|
|
639
686
|
wsConnected = false;
|
|
640
687
|
}
|
|
@@ -671,7 +718,7 @@ export async function startAutomation(options) {
|
|
|
671
718
|
}
|
|
672
719
|
}
|
|
673
720
|
// Funding updates
|
|
674
|
-
if (eventBus.has('funding_update')) {
|
|
721
|
+
if (eventBus.has('funding_update') && !wsConnected) {
|
|
675
722
|
for (const [coin, data] of snapshot.fundingRates) {
|
|
676
723
|
await emitAutomationEvent('funding_update', {
|
|
677
724
|
coin,
|
|
@@ -752,21 +799,6 @@ export async function startAutomation(options) {
|
|
|
752
799
|
// Order filled — compare open order IDs
|
|
753
800
|
// (Skipped for MVP — requires tracking open orders per poll, will add when needed)
|
|
754
801
|
}
|
|
755
|
-
// Run scheduled tasks
|
|
756
|
-
for (const task of scheduledTasks) {
|
|
757
|
-
if (now - task.lastRun >= task.intervalMs) {
|
|
758
|
-
try {
|
|
759
|
-
await task.handler();
|
|
760
|
-
}
|
|
761
|
-
catch (err) {
|
|
762
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
763
|
-
audit.recordError('scheduled_task', error);
|
|
764
|
-
log.error(`Scheduled task error: ${error.message}`);
|
|
765
|
-
await handleErrors([error]);
|
|
766
|
-
}
|
|
767
|
-
task.lastRun = now;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
802
|
previousSnapshot = snapshot;
|
|
771
803
|
}
|
|
772
804
|
catch (err) {
|
|
@@ -778,24 +810,82 @@ export async function startAutomation(options) {
|
|
|
778
810
|
isPolling = false;
|
|
779
811
|
}
|
|
780
812
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
813
|
+
async function runScheduledTasks() {
|
|
814
|
+
const now = Date.now();
|
|
815
|
+
for (const task of scheduledTasks) {
|
|
816
|
+
if (task.running || now - task.lastRun < task.intervalMs)
|
|
817
|
+
continue;
|
|
818
|
+
task.running = true;
|
|
819
|
+
task.lastRun = now;
|
|
820
|
+
try {
|
|
821
|
+
await task.handler();
|
|
822
|
+
}
|
|
823
|
+
catch (err) {
|
|
824
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
825
|
+
audit.recordError('scheduled_task', error);
|
|
826
|
+
log.error(`Scheduled task error: ${error.message}`);
|
|
827
|
+
await handleErrors([error]);
|
|
828
|
+
}
|
|
829
|
+
finally {
|
|
830
|
+
task.running = false;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
// Seed an audit snapshot. With a healthy socket this is assembled from the
|
|
835
|
+
// live cache; REST is used only for any feed that has not produced its first
|
|
836
|
+
// snapshot yet.
|
|
786
837
|
await poll();
|
|
838
|
+
// Start hooks run after WebSocket caches are seeded, so strategy reads are
|
|
839
|
+
// WebSocket-first from their very first decision.
|
|
840
|
+
for (const hook of startHooks) {
|
|
841
|
+
try {
|
|
842
|
+
await hook();
|
|
843
|
+
}
|
|
844
|
+
catch (err) {
|
|
845
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
846
|
+
audit.recordError('onStart', error);
|
|
847
|
+
log.error(`onStart hook error: ${error.message}`);
|
|
848
|
+
for (const errorHook of errorHooks) {
|
|
849
|
+
try {
|
|
850
|
+
await errorHook(error);
|
|
851
|
+
}
|
|
852
|
+
catch { /* swallow */ }
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
automationReady = true;
|
|
857
|
+
// api.every() is a real scheduler now; it no longer depends on how often a
|
|
858
|
+
// heavyweight REST reconciliation snapshot runs.
|
|
859
|
+
const scheduleTimer = setInterval(() => { void runScheduledTasks(); }, 500);
|
|
860
|
+
// --poll controls the REST fallback cadence while the socket is down. While
|
|
861
|
+
// connected, reconcile at most once per minute regardless of a shorter
|
|
862
|
+
// fallback interval supplied by older launch commands.
|
|
863
|
+
const restReconcileMs = Math.max(60_000, pollIntervalMs);
|
|
864
|
+
let lastPollAt = Date.now();
|
|
865
|
+
const pollTimer = setInterval(() => {
|
|
866
|
+
const interval = wsConnected ? restReconcileMs : pollIntervalMs;
|
|
867
|
+
if (Date.now() - lastPollAt < interval)
|
|
868
|
+
return;
|
|
869
|
+
lastPollAt = Date.now();
|
|
870
|
+
void poll();
|
|
871
|
+
}, Math.min(1_000, pollIntervalMs));
|
|
872
|
+
const wsLabel = wsConnected ? ', ws=on' : (useWebSocket ? ', ws=failed' : '');
|
|
873
|
+
log.info(`Started (REST fallback ${pollIntervalMs / 1000}s, connected reconcile ${restReconcileMs / 1000}s, dry=${dryRun}${wsLabel})`);
|
|
787
874
|
// Stop function
|
|
788
875
|
async function stop(opts) {
|
|
789
876
|
if (stopped)
|
|
790
877
|
return;
|
|
791
878
|
stopped = true;
|
|
792
|
-
clearInterval(
|
|
879
|
+
clearInterval(scheduleTimer);
|
|
880
|
+
clearInterval(pollTimer);
|
|
793
881
|
// Close WebSocket
|
|
794
882
|
if (ws) {
|
|
795
883
|
ws.removeAllListeners();
|
|
796
884
|
await ws.close();
|
|
797
885
|
ws = null;
|
|
798
886
|
}
|
|
887
|
+
rawClient.setRealtimeDataProvider(null);
|
|
888
|
+
realtimeData = null;
|
|
799
889
|
for (const hook of stopHooks) {
|
|
800
890
|
try {
|
|
801
891
|
await hook();
|
package/dist/auto/types.d.ts
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
import type { HyperliquidClient } from '../core/client.js';
|
|
2
2
|
/** What an automation .ts file exports */
|
|
3
3
|
export type AutomationFactory = (api: AutomationAPI) => void | Promise<void>;
|
|
4
|
+
/** Runtime-enforced policy exported by every automation module. */
|
|
5
|
+
export type AutomationGuardrails = ReadOnlyAutomationGuardrails | TradingAutomationGuardrails;
|
|
6
|
+
export interface ReadOnlyAutomationGuardrails {
|
|
7
|
+
/** Read-only automations cannot call any client write method. */
|
|
8
|
+
mode: 'read-only';
|
|
9
|
+
}
|
|
10
|
+
export interface TradingAutomationGuardrails {
|
|
11
|
+
mode: 'trading';
|
|
12
|
+
/** Canonical markets this automation may trade: `ETH`, `xyz:CL`, `spot:HYPE`, `#1230`. */
|
|
13
|
+
allowedMarkets: string[];
|
|
14
|
+
/** Maximum USD notional for one submitted order. */
|
|
15
|
+
maxOrderUsd: number;
|
|
16
|
+
/** Maximum absolute USD exposure in any one market after an order. */
|
|
17
|
+
maxPositionUsd: number;
|
|
18
|
+
/** Maximum absolute USD exposure across the account after an order. */
|
|
19
|
+
maxTotalExposureUsd: number;
|
|
20
|
+
/** Maximum leverage the automation may request or use when increasing perp exposure. */
|
|
21
|
+
maxLeverage: number;
|
|
22
|
+
/** Maximum account margin utilization allowed after a risk-increasing perp order. */
|
|
23
|
+
maxMarginUsedPct: number;
|
|
24
|
+
/** Maximum account-wide open orders after this automation submits an order. */
|
|
25
|
+
maxOpenOrders: number;
|
|
26
|
+
/** Maximum risk-increasing order submissions in a rolling 60-second window. */
|
|
27
|
+
maxOrdersPerMinute: number;
|
|
28
|
+
/** Maximum slippage passed to market-order helpers. */
|
|
29
|
+
maxSlippageBps: number;
|
|
30
|
+
/** Whether market-order helpers may be used. */
|
|
31
|
+
allowMarketOrders: boolean;
|
|
32
|
+
/** Whether `cancelAll()` without a market or an armed `scheduleCancel()` is allowed. */
|
|
33
|
+
allowAccountWideCancel: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface AutomationGuardrailContext {
|
|
36
|
+
/** Persisted automation state with current `--set` overrides applied. */
|
|
37
|
+
config: Readonly<Record<string, unknown>>;
|
|
38
|
+
}
|
|
39
|
+
/** Guardrails may be static or derived from startup config, but the result is always validated. */
|
|
40
|
+
export type AutomationGuardrailsExport = AutomationGuardrails | ((context: AutomationGuardrailContext) => AutomationGuardrails);
|
|
41
|
+
export interface LoadedAutomation {
|
|
42
|
+
factory: AutomationFactory;
|
|
43
|
+
guardrails: AutomationGuardrails;
|
|
44
|
+
}
|
|
4
45
|
/** Config field descriptor for example automations */
|
|
5
46
|
export interface AutomationConfigField {
|
|
6
47
|
type: 'string' | 'number' | 'boolean';
|
|
@@ -160,7 +201,7 @@ export interface AutomationAPI {
|
|
|
160
201
|
};
|
|
161
202
|
/** Subscribe to a market/account event */
|
|
162
203
|
on<E extends AutomationEventType>(event: E, handler: AutomationEventHandler<E>): void;
|
|
163
|
-
/** Run a handler on
|
|
204
|
+
/** Run a handler on its own recurring scheduler, independent of REST reconciliation. */
|
|
164
205
|
every(intervalMs: number, handler: () => void | Promise<void>): void;
|
|
165
206
|
/** Called after all handlers are registered and polling begins */
|
|
166
207
|
onStart(handler: () => void | Promise<void>): void;
|
|
@@ -188,6 +229,8 @@ export interface AutomationAPI {
|
|
|
188
229
|
id: string;
|
|
189
230
|
/** True if running in --dry mode (write methods are intercepted) */
|
|
190
231
|
dryRun: boolean;
|
|
232
|
+
/** Validated policy currently enforced by the runtime client proxy. */
|
|
233
|
+
guardrails: Readonly<AutomationGuardrails>;
|
|
191
234
|
}
|
|
192
235
|
export interface AutomationSnapshot {
|
|
193
236
|
prices: Map<string, number>;
|
|
@@ -216,6 +259,7 @@ export interface ScheduledTask {
|
|
|
216
259
|
intervalMs: number;
|
|
217
260
|
handler: () => void | Promise<void>;
|
|
218
261
|
lastRun: number;
|
|
262
|
+
running?: boolean;
|
|
219
263
|
}
|
|
220
264
|
export interface RunningAutomation {
|
|
221
265
|
id: string;
|
package/dist/auto/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../scripts/auto/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../scripts/auto/types.ts"],"names":[],"mappings":"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"}
|
package/dist/core/client.d.ts
CHANGED
|
@@ -1,4 +1,38 @@
|
|
|
1
|
-
import type { OpenBrokerConfig, BuilderInfo, OrderResponse, CancelResponse, MetaAndAssetCtxs, ClearinghouseState, OpenOrder, OutcomeMetaResponse, OutcomeMarket } from './types.js';
|
|
1
|
+
import type { OpenBrokerConfig, BuilderInfo, OrderResponse, CancelResponse, MetaAndAssetCtxs, AssetCtx, ClearinghouseState, OpenOrder, OutcomeMetaResponse, OutcomeMarket } from './types.js';
|
|
2
|
+
export interface RealtimeBookSnapshot {
|
|
3
|
+
coin: string;
|
|
4
|
+
time: number;
|
|
5
|
+
levels: [
|
|
6
|
+
Array<{
|
|
7
|
+
px: string;
|
|
8
|
+
sz: string;
|
|
9
|
+
n: number;
|
|
10
|
+
}>,
|
|
11
|
+
Array<{
|
|
12
|
+
px: string;
|
|
13
|
+
sz: string;
|
|
14
|
+
n: number;
|
|
15
|
+
}>
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
export interface RealtimeDataProvider {
|
|
19
|
+
readonly connected: boolean;
|
|
20
|
+
getAllMids(): Record<string, string> | null;
|
|
21
|
+
getMainAssetCtxs(): AssetCtx[] | null;
|
|
22
|
+
getUserState(user: string, dex?: string): ClearinghouseState | null;
|
|
23
|
+
getUserStateAll(user: string): ClearinghouseState | null;
|
|
24
|
+
getSpotBalances(user: string): {
|
|
25
|
+
balances: Array<{
|
|
26
|
+
coin: string;
|
|
27
|
+
token: number;
|
|
28
|
+
hold: string;
|
|
29
|
+
total: string;
|
|
30
|
+
entryNtl: string;
|
|
31
|
+
}>;
|
|
32
|
+
} | null;
|
|
33
|
+
getOpenOrders(user: string): OpenOrder[] | null;
|
|
34
|
+
getL2Book(coin: string): Promise<RealtimeBookSnapshot | null>;
|
|
35
|
+
}
|
|
2
36
|
export declare class HyperliquidClient {
|
|
3
37
|
private config;
|
|
4
38
|
private account;
|
|
@@ -10,6 +44,8 @@ export declare class HyperliquidClient {
|
|
|
10
44
|
private szDecimalsMap;
|
|
11
45
|
/** Maps coin name → dex info for HIP-3 assets. Main dex assets have dexName=null */
|
|
12
46
|
private coinDexMap;
|
|
47
|
+
/** Static positional universe per dex, used to join allDexsAssetCtxs WS pushes. */
|
|
48
|
+
private dexUniverseMap;
|
|
13
49
|
/** Cache of perpDexs list */
|
|
14
50
|
private perpDexsCache;
|
|
15
51
|
/** Whether HIP-3 assets have been loaded into maps */
|
|
@@ -40,6 +76,13 @@ export declare class HyperliquidClient {
|
|
|
40
76
|
private spotMetaLoaded;
|
|
41
77
|
/** HIP-4 outcome metadata cache */
|
|
42
78
|
private outcomeMeta;
|
|
79
|
+
/** Static main-dex universe retained while live asset contexts arrive over WebSocket. */
|
|
80
|
+
private staticMeta;
|
|
81
|
+
/** Runtime-owned live data cache. CLI calls leave this detached and remain REST-only. */
|
|
82
|
+
private realtime;
|
|
83
|
+
private predictedFundingsCache;
|
|
84
|
+
private predictedFundingsInFlight;
|
|
85
|
+
private spotMetaCache;
|
|
43
86
|
verbose: boolean;
|
|
44
87
|
constructor(config?: OpenBrokerConfig);
|
|
45
88
|
private log;
|
|
@@ -61,6 +104,8 @@ export declare class HyperliquidClient {
|
|
|
61
104
|
get isReadOnly(): boolean;
|
|
62
105
|
/** Whether connected to testnet (HIP-3 dexes not auto-loaded) */
|
|
63
106
|
get isTestnet(): boolean;
|
|
107
|
+
/** Attach/detach the automation runtime's WebSocket-first data cache. */
|
|
108
|
+
setRealtimeDataProvider(provider: RealtimeDataProvider | null): void;
|
|
64
109
|
/**
|
|
65
110
|
* Returns vaultAddress param for SDK exchange calls.
|
|
66
111
|
* Only used for vault trading (HYPERLIQUID_VAULT_ADDRESS set explicitly).
|
|
@@ -70,6 +115,13 @@ export declare class HyperliquidClient {
|
|
|
70
115
|
/** Throw error if trying to trade in read-only mode. Validates API wallet on first call. */
|
|
71
116
|
private requireTrading;
|
|
72
117
|
getMetaAndAssetCtxs(): Promise<MetaAndAssetCtxs>;
|
|
118
|
+
/**
|
|
119
|
+
* Warm only the native/main static universe for an automation runtime.
|
|
120
|
+
* Dynamic contexts arrive via allDexsAssetCtxs, and HIP-3 metadata is loaded
|
|
121
|
+
* on demand when a strategy references a prefixed market. This avoids the
|
|
122
|
+
* old startup burst that fetched every HIP-3 dex before the socket opened.
|
|
123
|
+
*/
|
|
124
|
+
initializeRealtimeMetadata(): Promise<void>;
|
|
73
125
|
/**
|
|
74
126
|
* Load HIP-3 perp dex assets into the asset/szDecimals maps.
|
|
75
127
|
* Asset index formula: 100000 + dexIdx * 10000 + assetIdx
|
|
@@ -253,6 +305,7 @@ export declare class HyperliquidClient {
|
|
|
253
305
|
nextFundingTime: number;
|
|
254
306
|
}]>
|
|
255
307
|
]>>;
|
|
308
|
+
private fetchPredictedFundings;
|
|
256
309
|
/**
|
|
257
310
|
* Get L2 order book for an asset
|
|
258
311
|
* Returns best bid/ask and depth
|
|
@@ -274,6 +327,7 @@ export declare class HyperliquidClient {
|
|
|
274
327
|
spread: number;
|
|
275
328
|
spreadBps: number;
|
|
276
329
|
}>;
|
|
330
|
+
private normalizeL2Book;
|
|
277
331
|
getAssetIndexAsync(coin: string): Promise<number>;
|
|
278
332
|
getAssetIndex(coin: string): number;
|
|
279
333
|
getSzDecimalsAsync(coin: string): Promise<number>;
|
|
@@ -300,6 +354,17 @@ export declare class HyperliquidClient {
|
|
|
300
354
|
* Useful for long-running strategies that need updated funding rates.
|
|
301
355
|
*/
|
|
302
356
|
invalidateMetaCache(): void;
|
|
357
|
+
/** Join an allDexsAssetCtxs WebSocket payload with the cached static universes. */
|
|
358
|
+
fundingRatesFromWs(ctxs: ReadonlyArray<[
|
|
359
|
+
string,
|
|
360
|
+
ReadonlyArray<{
|
|
361
|
+
funding?: string | number | null;
|
|
362
|
+
premium?: string | number | null;
|
|
363
|
+
}>
|
|
364
|
+
]>): Map<string, {
|
|
365
|
+
rate: number;
|
|
366
|
+
premium: number;
|
|
367
|
+
}>;
|
|
303
368
|
/**
|
|
304
369
|
* Get all loaded asset names (main + HIP-3)
|
|
305
370
|
*/
|