openbroker 1.9.2 → 1.9.3

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/SKILL.md +49 -0
  3. package/dist/auto/cli.js +3 -0
  4. package/dist/auto/examples/dca.d.ts +2 -1
  5. package/dist/auto/examples/dca.d.ts.map +1 -1
  6. package/dist/auto/examples/dca.js +19 -1
  7. package/dist/auto/examples/funding-arb.d.ts +2 -1
  8. package/dist/auto/examples/funding-arb.d.ts.map +1 -1
  9. package/dist/auto/examples/funding-arb.js +19 -2
  10. package/dist/auto/examples/grid.d.ts +2 -1
  11. package/dist/auto/examples/grid.d.ts.map +1 -1
  12. package/dist/auto/examples/grid.js +18 -2
  13. package/dist/auto/examples/mm-maker.d.ts +2 -1
  14. package/dist/auto/examples/mm-maker.d.ts.map +1 -1
  15. package/dist/auto/examples/mm-maker.js +18 -2
  16. package/dist/auto/examples/mm-spread.d.ts +2 -1
  17. package/dist/auto/examples/mm-spread.d.ts.map +1 -1
  18. package/dist/auto/examples/mm-spread.js +18 -2
  19. package/dist/auto/examples/price-alert.d.ts +2 -1
  20. package/dist/auto/examples/price-alert.d.ts.map +1 -1
  21. package/dist/auto/examples/price-alert.js +1 -0
  22. package/dist/auto/guardrails.d.ts +19 -0
  23. package/dist/auto/guardrails.d.ts.map +1 -0
  24. package/dist/auto/guardrails.js +575 -0
  25. package/dist/auto/guardrails.test.d.ts +2 -0
  26. package/dist/auto/guardrails.test.d.ts.map +1 -0
  27. package/dist/auto/guardrails.test.js +173 -0
  28. package/dist/auto/loader.d.ts +3 -3
  29. package/dist/auto/loader.d.ts.map +1 -1
  30. package/dist/auto/loader.js +25 -3
  31. package/dist/auto/runtime.d.ts.map +1 -1
  32. package/dist/auto/runtime.js +38 -20
  33. package/dist/auto/types.d.ts +43 -0
  34. package/dist/auto/types.d.ts.map +1 -1
  35. package/dist/lib.d.ts +2 -0
  36. package/dist/lib.d.ts.map +1 -1
  37. package/dist/lib.js +1 -0
  38. package/package.json +4 -3
  39. package/scripts/auto/cli.ts +3 -0
  40. package/scripts/auto/examples/dca.ts +21 -2
  41. package/scripts/auto/examples/funding-arb.ts +21 -3
  42. package/scripts/auto/examples/grid.ts +20 -3
  43. package/scripts/auto/examples/mm-maker.ts +20 -3
  44. package/scripts/auto/examples/mm-spread.ts +20 -3
  45. package/scripts/auto/examples/price-alert.ts +3 -1
  46. package/scripts/auto/guardrails.test.ts +227 -0
  47. package/scripts/auto/guardrails.ts +700 -0
  48. package/scripts/auto/loader.ts +41 -4
  49. package/scripts/auto/runtime.ts +38 -22
  50. package/scripts/auto/types.ts +54 -0
  51. package/scripts/lib.ts +10 -0
@@ -0,0 +1,173 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { CLIENT_WRITE_METHODS, GuardrailViolation, createGuardrailedClient, validateAutomationGuardrails, } from './guardrails.js';
4
+ import { listExamples, loadAutomation } from './loader.js';
5
+ const logger = {
6
+ info() { },
7
+ warn() { },
8
+ error() { },
9
+ debug() { },
10
+ };
11
+ function tradingPolicy(overrides = {}) {
12
+ return {
13
+ mode: 'trading',
14
+ allowedMarkets: ['ETH'],
15
+ maxOrderUsd: 1_000,
16
+ maxPositionUsd: 2_000,
17
+ maxTotalExposureUsd: 5_000,
18
+ maxLeverage: 2,
19
+ maxMarginUsedPct: 50,
20
+ maxOpenOrders: 10,
21
+ maxOrdersPerMinute: 10,
22
+ maxSlippageBps: 40,
23
+ allowMarketOrders: true,
24
+ allowAccountWideCancel: false,
25
+ ...overrides,
26
+ };
27
+ }
28
+ function mockClient(options = {}) {
29
+ const calls = [];
30
+ const client = {
31
+ address: '0x0000000000000000000000000000000000000001',
32
+ walletAddress: '0x0000000000000000000000000000000000000002',
33
+ isApiWallet: true,
34
+ async getUserStateAll() {
35
+ return {
36
+ assetPositions: (options.positions ?? []).map((position) => ({
37
+ position: {
38
+ coin: position.coin,
39
+ szi: String(position.size),
40
+ positionValue: String(Math.abs(position.size * position.price)),
41
+ leverage: { type: 'cross', value: position.leverage ?? 1 },
42
+ },
43
+ })),
44
+ marginSummary: { accountValue: '10000', totalMarginUsed: '0' },
45
+ };
46
+ },
47
+ async getAllMids() { return { ETH: '2000', BTC: '50000' }; },
48
+ async getOpenOrders() { return []; },
49
+ async getSpotBalances() { return { balances: options.spotBalances ?? [] }; },
50
+ async getSpotMetaAndAssetCtxs() {
51
+ return options.spotData ?? { meta: { tokens: [], universe: [] }, assetCtxs: [] };
52
+ },
53
+ resolveOutcomeRef() {
54
+ return { outcome: 1, side: 0, encoding: 10, coin: '#10', tokenName: '+10', assetId: 100000010 };
55
+ },
56
+ async marketOrder(...args) {
57
+ calls.push({ method: 'marketOrder', args });
58
+ return { status: 'ok' };
59
+ },
60
+ async limitOrder(...args) {
61
+ calls.push({ method: 'limitOrder', args });
62
+ return { status: 'ok' };
63
+ },
64
+ async bulkOrder(...args) {
65
+ calls.push({ method: 'bulkOrder', args });
66
+ return { status: 'ok' };
67
+ },
68
+ async spotLimitOrder(...args) {
69
+ calls.push({ method: 'spotLimitOrder', args });
70
+ return { status: 'ok' };
71
+ },
72
+ };
73
+ return { client, calls };
74
+ }
75
+ test('guardrail schema rejects missing and internally inconsistent limits', () => {
76
+ assert.throws(() => validateAutomationGuardrails({ mode: 'trading' }), /allowedMarkets/);
77
+ assert.throws(() => validateAutomationGuardrails(tradingPolicy({ maxOrderUsd: 3_000 })), /maxOrderUsd.*maxPositionUsd/);
78
+ assert.deepEqual(validateAutomationGuardrails({ mode: 'read-only' }), { mode: 'read-only' });
79
+ });
80
+ test('all client write families are included in the enforcement boundary', () => {
81
+ for (const method of [
82
+ 'bulkOrder', 'bulkCancel', 'scheduleCancel',
83
+ 'outcomeOrder', 'outcomeMarketOrder', 'outcomeLimitOrder',
84
+ ]) {
85
+ assert.equal(CLIENT_WRITE_METHODS.has(method), true, `${method} must be guarded`);
86
+ }
87
+ });
88
+ test('every bundled example exports a valid guardrail policy', async () => {
89
+ for (const example of listExamples()) {
90
+ const loaded = await loadAutomation(example.path, { config: {} });
91
+ assert.ok(loaded.guardrails.mode === 'read-only' || loaded.guardrails.mode === 'trading');
92
+ }
93
+ });
94
+ test('read-only policy blocks every write before it reaches the client', async () => {
95
+ const { client, calls } = mockClient();
96
+ const guarded = createGuardrailedClient(client, {
97
+ policy: { mode: 'read-only' },
98
+ rawClient: client,
99
+ log: logger,
100
+ });
101
+ await assert.rejects(guarded.bulkOrder([{ coin: 'ETH', isBuy: true, size: 0.1, price: 2000 }]), (error) => error instanceof GuardrailViolation && error.code === 'read-only');
102
+ assert.equal(calls.length, 0);
103
+ });
104
+ test('trading policy blocks disallowed markets, missing leverage, and oversized orders', async () => {
105
+ const { client, calls } = mockClient();
106
+ const guarded = createGuardrailedClient(client, {
107
+ policy: tradingPolicy(),
108
+ rawClient: client,
109
+ log: logger,
110
+ });
111
+ await assert.rejects(guarded.limitOrder('BTC', true, 0.01, 50_000, 'Gtc', false, 1), (error) => error instanceof GuardrailViolation && error.code === 'market-not-allowed');
112
+ await assert.rejects(guarded.limitOrder('ETH', true, 0.1, 2_000), (error) => error instanceof GuardrailViolation && error.code === 'leverage-required');
113
+ await assert.rejects(guarded.limitOrder('ETH', true, 0.6, 2_000, 'Gtc', false, 1), (error) => error instanceof GuardrailViolation && error.code === 'order-notional');
114
+ assert.equal(calls.length, 0);
115
+ });
116
+ test('valid market orders execute with runtime-capped slippage', async () => {
117
+ const { client, calls } = mockClient();
118
+ const guarded = createGuardrailedClient(client, {
119
+ policy: tradingPolicy(),
120
+ rawClient: client,
121
+ log: logger,
122
+ });
123
+ await guarded.marketOrder('ETH', true, 0.1, undefined, 1);
124
+ assert.equal(calls.length, 1);
125
+ assert.deepEqual(calls[0], {
126
+ method: 'marketOrder',
127
+ args: ['ETH', true, 0.1, 40, 1],
128
+ });
129
+ });
130
+ test('account-wide exposure is checked while genuine reductions remain available above caps', async () => {
131
+ const exposed = mockClient({ positions: [{ coin: 'BTC', size: 0.1, price: 50_000 }] });
132
+ const exposureGuarded = createGuardrailedClient(exposed.client, {
133
+ policy: tradingPolicy({ maxTotalExposureUsd: 5_100 }),
134
+ rawClient: exposed.client,
135
+ log: logger,
136
+ });
137
+ await assert.rejects(exposureGuarded.limitOrder('ETH', true, 0.1, 2_000, 'Gtc', false, 1), (error) => error instanceof GuardrailViolation && error.code === 'total-exposure');
138
+ const reducing = mockClient({ positions: [{ coin: 'ETH', size: 1, price: 2_000 }] });
139
+ const reductionGuarded = createGuardrailedClient(reducing.client, {
140
+ policy: tradingPolicy({ maxPositionUsd: 1_000, maxTotalExposureUsd: 1_000 }),
141
+ rawClient: reducing.client,
142
+ log: logger,
143
+ });
144
+ await reductionGuarded.marketOrder('ETH', false, 0.1);
145
+ assert.equal(reducing.calls.length, 1);
146
+ });
147
+ test('spot exposure joins market contexts by pair identifier instead of array position', async () => {
148
+ const { client, calls } = mockClient({
149
+ spotBalances: [{ coin: 'HYPE', total: '3' }],
150
+ spotData: {
151
+ meta: {
152
+ tokens: [{ index: 0, name: 'USDC' }, { index: 1, name: 'HYPE' }],
153
+ universe: [{ name: '@107', tokens: [1, 0] }],
154
+ },
155
+ assetCtxs: [
156
+ { coin: '@999', midPx: '99999', markPx: '99999' },
157
+ { coin: '@107', midPx: '62.5', markPx: '62.5' },
158
+ ],
159
+ },
160
+ });
161
+ const guarded = createGuardrailedClient(client, {
162
+ policy: tradingPolicy({
163
+ allowedMarkets: ['spot:HYPE'],
164
+ maxOrderUsd: 100,
165
+ maxPositionUsd: 500,
166
+ maxTotalExposureUsd: 500,
167
+ }),
168
+ rawClient: client,
169
+ log: logger,
170
+ });
171
+ await guarded.spotLimitOrder('HYPE', true, 0.1, 62.5, 'Gtc');
172
+ assert.equal(calls.length, 1);
173
+ });
@@ -1,4 +1,4 @@
1
- import type { AutomationFactory, AutomationConfig } from './types.js';
1
+ import type { AutomationConfig, AutomationGuardrailContext, LoadedAutomation } from './types.js';
2
2
  /** Resolve a script path from a name or path */
3
3
  export declare function resolveScriptPath(nameOrPath: string): string;
4
4
  /** Resolve a bundled example by name */
@@ -10,8 +10,8 @@ export declare function listExamples(): Array<{
10
10
  }>;
11
11
  /** Load config metadata from all bundled examples */
12
12
  export declare function loadExampleConfigs(): Promise<Record<string, AutomationConfig>>;
13
- /** Load an automation module and validate the default export */
14
- export declare function loadAutomation(scriptPath: string): Promise<AutomationFactory>;
13
+ /** Load an automation module and validate its factory plus required guardrails export. */
14
+ export declare function loadAutomation(scriptPath: string, context?: AutomationGuardrailContext): Promise<LoadedAutomation>;
15
15
  /** List available automation scripts in ~/.openbroker/automations/ */
16
16
  export declare function listAutomations(): Array<{
17
17
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../scripts/auto/loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAQtE,gDAAgD;AAChD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA2B5D;AAED,wCAAwC;AACxC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED,uCAAuC;AACvC,wBAAgB,YAAY,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CASpE;AAED,qDAAqD;AACrD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAiBpF;AAmCD,gEAAgE;AAChE,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAenF;AAED,sEAAsE;AACtE,wBAAgB,eAAe,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CASvE;AAED,8CAA8C;AAC9C,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../scripts/auto/loader.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAEV,gBAAgB,EAChB,0BAA0B,EAE1B,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAQpB,gDAAgD;AAChD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA2B5D;AAED,wCAAwC;AACxC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED,uCAAuC;AACvC,wBAAgB,YAAY,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CASpE;AAED,qDAAqD;AACrD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAiBpF;AAmDD,0FAA0F;AAC1F,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,0BAA2C,GACnD,OAAO,CAAC,gBAAgB,CAAC,CA0B3B;AAED,sEAAsE;AACtE,wBAAgB,eAAe,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CASvE;AAED,8CAA8C;AAC9C,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C"}
@@ -3,6 +3,7 @@ import { existsSync, readdirSync, mkdirSync } from 'fs';
3
3
  import path from 'path';
4
4
  import os from 'os';
5
5
  import { fileURLToPath } from 'url';
6
+ import { resolveAutomationGuardrails } from './guardrails.js';
6
7
  const __filename = fileURLToPath(import.meta.url);
7
8
  const __dirname = path.dirname(__filename);
8
9
  const AUTOMATIONS_DIR = path.join(os.homedir(), '.openbroker', 'automations');
@@ -98,8 +99,21 @@ function resolveAutomationConfig(mod) {
98
99
  }
99
100
  return null;
100
101
  }
101
- /** Load an automation module and validate the default export */
102
- export async function loadAutomation(scriptPath) {
102
+ function resolveGuardrailsExport(mod) {
103
+ const candidates = [
104
+ mod.guardrails,
105
+ mod.default?.guardrails,
106
+ mod["module.exports"]?.guardrails,
107
+ ];
108
+ for (const candidate of candidates) {
109
+ if (candidate && (typeof candidate === 'object' || typeof candidate === 'function')) {
110
+ return candidate;
111
+ }
112
+ }
113
+ return null;
114
+ }
115
+ /** Load an automation module and validate its factory plus required guardrails export. */
116
+ export async function loadAutomation(scriptPath, context = { config: {} }) {
103
117
  const absolutePath = path.resolve(scriptPath);
104
118
  // Dynamic import — tsx handles TypeScript transpilation
105
119
  const mod = await import(absolutePath);
@@ -108,7 +122,15 @@ export async function loadAutomation(scriptPath) {
108
122
  throw new Error(`Automation script must export a default function.\n` +
109
123
  `Got: ${typeof factory} from ${scriptPath}`);
110
124
  }
111
- return factory;
125
+ const guardrailsExport = resolveGuardrailsExport(mod);
126
+ if (!guardrailsExport) {
127
+ throw new Error(`Automation script must export "guardrails".\n` +
128
+ `Use { mode: "read-only" } for monitoring-only scripts or a validated trading policy.`);
129
+ }
130
+ return {
131
+ factory: factory,
132
+ guardrails: resolveAutomationGuardrails(guardrailsExport, context),
133
+ };
112
134
  }
113
135
  /** List available automation scripts in ~/.openbroker/automations/ */
114
136
  export function listAutomations() {
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../scripts/auto/runtime.ts"],"names":[],"mappings":"AAeA,OAAO,EAA4C,wBAAwB,IAAI,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAG5H,OAAO,KAAK,EAYV,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAoZpB,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sFAAsF;IACtF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAKD,wBAAgB,qBAAqB,IAAI,iBAAiB,EAAE,CAE3D;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAEvE;AAED,8EAA8E;AAC9E,OAAO,EAAE,qBAAqB,IAAI,wBAAwB,EAAE,CAAC;AAE7D,wBAAsB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAohBzF"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../scripts/auto/runtime.ts"],"names":[],"mappings":"AAgBA,OAAO,EAA4C,wBAAwB,IAAI,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAG5H,OAAO,KAAK,EAYV,iBAAiB,EAElB,MAAM,YAAY,CAAC;AAqYpB,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sFAAsF;IACtF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAKD,wBAAgB,qBAAqB,IAAI,iBAAiB,EAAE,CAE3D;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAEvE;AAED,8EAA8E;AAC9E,OAAO,EAAE,qBAAqB,IAAI,wBAAwB,EAAE,CAAC;AAE7D,wBAAsB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAijBzF"}
@@ -8,6 +8,7 @@ import { roundPrice, roundSize, sleep, normalizeCoin, formatUsd, formatPercent,
8
8
  import { WebSocketManager } from '../core/ws.js';
9
9
  import { AutomationEventBus } from './events.js';
10
10
  import { loadAutomation } from './loader.js';
11
+ import { CLIENT_WRITE_METHODS, createGuardrailedClient } from './guardrails.js';
11
12
  import { registerAutomation, unregisterAutomation, getRegisteredAutomations as getRegisteredFromFile } from './registry.js';
12
13
  import { createAutomationAudit, toSerializable } from './audit.js';
13
14
  import { startKeepAwake } from './keep-awake.js';
@@ -74,12 +75,6 @@ function fanOutAgentAction(observers, action, status, details, txHash) {
74
75
  }
75
76
  }
76
77
  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
78
  function createState(id) {
84
79
  mkdirSync(STATE_DIR, { recursive: true });
85
80
  const stateFile = path.join(STATE_DIR, `${id}.json`);
@@ -157,18 +152,11 @@ function createLogger(id, verbose, audit) {
157
152
  };
158
153
  }
159
154
  // ── 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
155
  function createDryClient(client, log) {
168
156
  return new Proxy(client, {
169
157
  get(target, prop, receiver) {
170
158
  const value = Reflect.get(target, prop, receiver);
171
- if (typeof prop === 'string' && WRITE_METHODS.has(prop) && typeof value === 'function') {
159
+ if (typeof prop === 'string' && CLIENT_WRITE_METHODS.has(prop) && typeof value === 'function') {
172
160
  return (...args) => {
173
161
  log.info(`[DRY] ${prop}(${args.map(a => JSON.stringify(a)).join(', ')})`);
174
162
  return Promise.resolve({ status: 'ok', response: { type: 'dry_run' } });
@@ -182,7 +170,7 @@ function createAuditedClient(client, audit, dryRun, observers) {
182
170
  return new Proxy(client, {
183
171
  get(target, prop, receiver) {
184
172
  const value = Reflect.get(target, prop, receiver);
185
- if (typeof prop === 'string' && AUDITED_WRITE_METHODS.has(prop) && typeof value === 'function') {
173
+ if (typeof prop === 'string' && CLIENT_WRITE_METHODS.has(prop) && typeof value === 'function') {
186
174
  return async (...args) => {
187
175
  const actionId = `${prop}:${Date.now()}:${Math.random().toString(16).slice(2)}`;
188
176
  audit.recordAction({
@@ -396,8 +384,39 @@ export async function startAutomation(options) {
396
384
  }
397
385
  }
398
386
  const observers = await loadConventionObservers(log);
387
+ let loaded;
388
+ try {
389
+ log.info(`Loading automation: ${scriptPath}`);
390
+ loaded = await loadAutomation(scriptPath, { config: stateController.snapshot() });
391
+ }
392
+ catch (err) {
393
+ const error = err instanceof Error ? err : new Error(String(err));
394
+ audit.recordError('guardrail_validation', error);
395
+ await audit.stop({
396
+ status: 'error',
397
+ stopReason: 'guardrail_validation_error',
398
+ pollCount: 0,
399
+ eventsEmitted: 0,
400
+ });
401
+ keepAwake?.stop();
402
+ throw error;
403
+ }
404
+ audit.recordNote('guardrails', loaded.guardrails);
399
405
  const baseClient = dryRun ? createDryClient(rawClient, log) : rawClient;
400
- const client = createAuditedClient(baseClient, audit, dryRun, observers);
406
+ const guardedClient = createGuardrailedClient(baseClient, {
407
+ policy: loaded.guardrails,
408
+ rawClient,
409
+ log,
410
+ onViolation: (error, method, args) => {
411
+ audit.recordNote('guardrail_block', {
412
+ code: error.code,
413
+ message: error.message,
414
+ method,
415
+ args: toSerializable(args),
416
+ });
417
+ },
418
+ });
419
+ const client = createAuditedClient(guardedClient, audit, dryRun, observers);
401
420
  const startHooks = [];
402
421
  const stopHooks = [];
403
422
  const errorHooks = [];
@@ -428,12 +447,11 @@ export async function startAutomation(options) {
428
447
  audit: auditApi,
429
448
  id,
430
449
  dryRun,
450
+ guardrails: loaded.guardrails,
431
451
  };
432
452
  try {
433
- // Load and execute the factory function (registers handlers)
434
- log.info(`Loading automation: ${scriptPath}`);
435
- const factory = await loadAutomation(scriptPath);
436
- await factory(api);
453
+ // Execute the already validated factory function (registers handlers).
454
+ await loaded.factory(api);
437
455
  // Call onStart hooks
438
456
  for (const hook of startHooks) {
439
457
  try {
@@ -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';
@@ -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>;
@@ -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/lib.d.ts CHANGED
@@ -11,6 +11,8 @@ export type { ChaseOptions, ChaseResult } from './operations/chase.js';
11
11
  export { startAutomation, getRunningAutomations, getAutomation, getRegisteredAutomations, } from './auto/runtime.js';
12
12
  export type { RuntimeOptions } from './auto/runtime.js';
13
13
  export { resolveScriptPath, resolveExamplePath, listAutomations, listExamples, loadExampleConfigs, ensureAutomationsDir, loadAutomation, } from './auto/loader.js';
14
+ export { GuardrailViolation, CLIENT_WRITE_METHODS, canonicalMarket, validateAutomationGuardrails, resolveAutomationGuardrails, createGuardrailedClient, } from './auto/guardrails.js';
15
+ export type { GuardrailedClientOptions } from './auto/guardrails.js';
14
16
  export { registerAutomation, unregisterAutomation, cleanRegistry, getAutomationsToRestart, markAutomationError, } from './auto/registry.js';
15
17
  export type * from './auto/types.js';
16
18
  //# sourceMappingURL=lib.d.ts.map
package/dist/lib.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../scripts/lib.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,iBAAiB,EACjB,SAAS,GACV,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE5E,OAAO,EACL,UAAU,EACV,YAAY,EACZ,UAAU,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,2BAA2B,GAC5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,UAAU,EACV,SAAS,EACT,KAAK,EACL,aAAa,EACb,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,WAAW,EACX,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,mBAAmB,iBAAiB,CAAC;AAIrC,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7E,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIvE,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,aAAa,EACb,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,mBAAmB,iBAAiB,CAAC"}
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../scripts/lib.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,iBAAiB,EACjB,SAAS,GACV,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE5E,OAAO,EACL,UAAU,EACV,YAAY,EACZ,UAAU,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,2BAA2B,GAC5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,UAAU,EACV,SAAS,EACT,KAAK,EACL,aAAa,EACb,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,WAAW,EACX,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,mBAAmB,iBAAiB,CAAC;AAIrC,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7E,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIvE,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,aAAa,EACb,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,4BAA4B,EAC5B,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAErE,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,mBAAmB,iBAAiB,CAAC"}
package/dist/lib.js CHANGED
@@ -18,4 +18,5 @@ export { runChase } from './operations/chase.js';
18
18
  // ── Automation runtime ──────────────────────────────────────────────
19
19
  export { startAutomation, getRunningAutomations, getAutomation, getRegisteredAutomations, } from './auto/runtime.js';
20
20
  export { resolveScriptPath, resolveExamplePath, listAutomations, listExamples, loadExampleConfigs, ensureAutomationsDir, loadAutomation, } from './auto/loader.js';
21
+ export { GuardrailViolation, CLIENT_WRITE_METHODS, canonicalMarket, validateAutomationGuardrails, resolveAutomationGuardrails, createGuardrailedClient, } from './auto/guardrails.js';
21
22
  export { registerAutomation, unregisterAutomation, cleanRegistry, getAutomationsToRestart, markAutomationError, } from './auto/registry.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openbroker",
3
- "version": "1.9.2",
3
+ "version": "1.9.3",
4
4
  "description": "Hyperliquid trading CLI - execute orders, manage positions, and run trading strategies",
5
5
  "type": "module",
6
6
  "bin": {
@@ -51,8 +51,9 @@
51
51
  "funding-scan": "tsx scripts/info/funding-scan.ts",
52
52
  "outcomes": "tsx scripts/info/outcomes.ts",
53
53
  "build": "tsc",
54
- "prepublishOnly": "npm run build && npm run test:cli",
55
- "test:cli": "node --import tsx bin/cli.ts --help"
54
+ "prepublishOnly": "npm run build && npm run test:guardrails && npm run test:cli",
55
+ "test:cli": "node --import tsx bin/cli.ts --help",
56
+ "test:guardrails": "node --import tsx --test scripts/auto/guardrails.test.ts"
56
57
  },
57
58
  "dependencies": {
58
59
  "@nktkas/hyperliquid": "^0.30.3",
@@ -50,6 +50,8 @@ Scripts are loaded from:
50
50
  3. Bundled examples (via --example)
51
51
 
52
52
  Writing an automation:
53
+ export const guardrails = { mode: 'read-only' };
54
+
53
55
  export default function(api) {
54
56
  api.on('price_change', async ({ coin, changePct }) => {
55
57
  api.log.info(\`\${coin} moved \${changePct.toFixed(2)}%\`);
@@ -207,6 +209,7 @@ function listCommand() {
207
209
  if (automations.length === 0 && examples.length === 0) {
208
210
  console.log('No automations found in ~/.openbroker/automations/');
209
211
  console.log('\nCreate a .ts file there with:');
212
+ console.log(" export const guardrails = { mode: 'read-only' };");
210
213
  console.log(' export default function(api) { ... }');
211
214
  console.log('\nOr run a bundled example: openbroker auto examples');
212
215
  return;
@@ -1,6 +1,6 @@
1
1
  // DCA (Dollar Cost Averaging) — Buy fixed USD amounts at regular intervals
2
2
 
3
- import type { AutomationAPI, AutomationConfig } from '../types.js';
3
+ import type { AutomationAPI, AutomationConfig, AutomationGuardrailContext, AutomationGuardrails } from '../types.js';
4
4
 
5
5
  export const config: AutomationConfig = {
6
6
  description: 'Dollar cost averaging — buy fixed USD amounts at regular intervals',
@@ -12,6 +12,25 @@ export const config: AutomationConfig = {
12
12
  },
13
13
  };
14
14
 
15
+ export function guardrails({ config: values }: AutomationGuardrailContext): AutomationGuardrails {
16
+ const amount = Number(values.amount ?? 100);
17
+ const count = Number(values.count ?? 24);
18
+ return {
19
+ mode: 'trading',
20
+ allowedMarkets: [String(values.coin ?? 'HYPE')],
21
+ maxOrderUsd: amount * 1.1,
22
+ maxPositionUsd: amount * count * 1.1,
23
+ maxTotalExposureUsd: amount * count * 1.1,
24
+ maxLeverage: 1,
25
+ maxMarginUsedPct: 50,
26
+ maxOpenOrders: 5,
27
+ maxOrdersPerMinute: 5,
28
+ maxSlippageBps: 50,
29
+ allowMarketOrders: true,
30
+ allowAccountWideCancel: false,
31
+ };
32
+ }
33
+
15
34
  export default function dca(api: AutomationAPI) {
16
35
  const COIN = api.state.get<string>('coin', 'HYPE')!;
17
36
  const AMOUNT_USD = api.state.get<number>('amount', 100)!;
@@ -43,7 +62,7 @@ export default function dca(api: AutomationAPI) {
43
62
  const size = AMOUNT_USD / price;
44
63
  api.log.info(`[${purchased + 1}/${MAX_PURCHASES}] Buying ~$${AMOUNT_USD} of ${COIN} @ $${price.toFixed(2)}`);
45
64
 
46
- const response = await api.client.marketOrder(COIN, true, size);
65
+ const response = await api.client.marketOrder(COIN, true, size, undefined, 1);
47
66
  if (response.status === 'ok' && response.response && typeof response.response === 'object') {
48
67
  const status = response.response.data.statuses[0];
49
68
  if (status?.filled) {
@@ -1,6 +1,6 @@
1
1
  // Funding Arbitrage — Collect funding by positioning opposite to the crowd
2
2
 
3
- import type { AutomationAPI, AutomationConfig } from '../types.js';
3
+ import type { AutomationAPI, AutomationConfig, AutomationGuardrailContext, AutomationGuardrails } from '../types.js';
4
4
 
5
5
  export const config: AutomationConfig = {
6
6
  description: 'Funding arbitrage — collect funding by positioning opposite to the crowd',
@@ -13,6 +13,24 @@ export const config: AutomationConfig = {
13
13
  },
14
14
  };
15
15
 
16
+ export function guardrails({ config: values }: AutomationGuardrailContext): AutomationGuardrails {
17
+ const sizeUsd = Number(values.sizeUsd ?? 5000);
18
+ return {
19
+ mode: 'trading',
20
+ allowedMarkets: [String(values.coin ?? 'HYPE')],
21
+ maxOrderUsd: sizeUsd * 1.1,
22
+ maxPositionUsd: sizeUsd * 1.1,
23
+ maxTotalExposureUsd: sizeUsd * 1.1,
24
+ maxLeverage: 1,
25
+ maxMarginUsedPct: 50,
26
+ maxOpenOrders: 5,
27
+ maxOrdersPerMinute: 4,
28
+ maxSlippageBps: 50,
29
+ allowMarketOrders: true,
30
+ allowAccountWideCancel: false,
31
+ };
32
+ }
33
+
16
34
  export default function fundingArb(api: AutomationAPI) {
17
35
  const COIN = api.state.get<string>('coin', 'HYPE')!;
18
36
  const SIZE_USD = api.state.get<number>('sizeUsd', 5000)!;
@@ -48,7 +66,7 @@ export default function fundingArb(api: AutomationAPI) {
48
66
  if (shouldClose) {
49
67
  api.log.info(`Funding dropped to ${annualizedPct.toFixed(2)}% (below ${CLOSE_AT}%), closing ${positionSide}`);
50
68
  const closeIsBuy = positionSide === 'short';
51
- await api.client.marketOrder(coin, closeIsBuy, positionSize);
69
+ await api.client.marketOrder(coin, closeIsBuy, positionSize, undefined, 1);
52
70
 
53
71
  inPosition = false;
54
72
  api.state.set('inPosition', false);
@@ -69,7 +87,7 @@ export default function fundingArb(api: AutomationAPI) {
69
87
  const size = SIZE_USD / price;
70
88
 
71
89
  api.log.info(`Funding at ${annualizedPct.toFixed(2)}% — opening ${side} ${size.toFixed(6)} ${coin}`);
72
- const response = await api.client.marketOrder(coin, !shouldShort, size);
90
+ const response = await api.client.marketOrder(coin, !shouldShort, size, undefined, 1);
73
91
 
74
92
  if (response.status === 'ok' && response.response && typeof response.response === 'object') {
75
93
  const status = response.response.data.statuses[0];