openbroker 1.0.80 → 1.0.82
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 +1 -1
- package/bin/cli.ts +22 -1
- package/bin/openbroker.js +4 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/auto/cli.ts +9 -1
- package/scripts/auto/loader.ts +37 -3
- package/scripts/auto/runtime.ts +5 -4
- package/scripts/core/client.ts +603 -214
- package/scripts/core/config.ts +19 -0
- package/scripts/core/types.ts +1 -0
- package/scripts/core/utils.ts +4 -1
- package/scripts/info/account.ts +12 -8
- package/scripts/info/all-markets.ts +3 -0
- package/scripts/info/candles.ts +3 -0
- package/scripts/info/funding-history.ts +3 -0
- package/scripts/info/funding-scan.ts +4 -0
- package/scripts/info/funding.ts +7 -0
- package/scripts/info/markets.ts +7 -0
- package/scripts/info/orders.ts +5 -0
- package/scripts/info/search-markets.ts +8 -0
- package/scripts/info/trades.ts +3 -0
- package/scripts/setup/onboard.ts +95 -44
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.82", "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_spot_buy ob_spot_sell ob_twap ob_twap_cancel ob_twap_status ob_bracket ob_chase ob_watcher_status ob_auto_run ob_auto_stop ob_auto_list Bash(openbroker:*)
|
|
9
9
|
---
|
|
10
10
|
|
package/bin/cli.ts
CHANGED
|
@@ -112,6 +112,8 @@ Automations:
|
|
|
112
112
|
auto status Show running automations
|
|
113
113
|
|
|
114
114
|
Options:
|
|
115
|
+
-c, --config <path> Use a specific .env config file
|
|
116
|
+
--testnet Use testnet
|
|
115
117
|
--help, -h Show help for a command
|
|
116
118
|
--dry Preview without executing
|
|
117
119
|
--verbose Show debug output
|
|
@@ -134,11 +136,30 @@ Documentation: https://github.com/aurracloud/open-broker
|
|
|
134
136
|
function runScript(scriptPath: string, args: string[]) {
|
|
135
137
|
const fullPath = path.join(scriptsDir, scriptPath);
|
|
136
138
|
|
|
139
|
+
// Handle global flags: set env vars and strip from args
|
|
140
|
+
const env = { ...process.env };
|
|
141
|
+
const testnetIdx = args.indexOf('--testnet');
|
|
142
|
+
if (testnetIdx !== -1) {
|
|
143
|
+
env.HYPERLIQUID_NETWORK = 'testnet';
|
|
144
|
+
args = [...args.slice(0, testnetIdx), ...args.slice(testnetIdx + 1)];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Handle -c / --config flag: load the specified .env file
|
|
148
|
+
// Resolve relative to the user's original working directory (not the package root)
|
|
149
|
+
let configIdx = args.indexOf('-c');
|
|
150
|
+
if (configIdx === -1) configIdx = args.indexOf('--config');
|
|
151
|
+
if (configIdx !== -1 && args[configIdx + 1]) {
|
|
152
|
+
const originalCwd = process.env.OPENBROKER_CWD || process.cwd();
|
|
153
|
+
const configPath = path.resolve(originalCwd, args[configIdx + 1]);
|
|
154
|
+
env.OPENBROKER_CONFIG = configPath;
|
|
155
|
+
args = [...args.slice(0, configIdx), ...args.slice(configIdx + 2)];
|
|
156
|
+
}
|
|
157
|
+
|
|
137
158
|
// Use tsx to run TypeScript directly
|
|
138
159
|
const child = spawn('npx', ['tsx', fullPath, ...args], {
|
|
139
160
|
stdio: 'inherit',
|
|
140
161
|
cwd: path.resolve(__dirname, '..'),
|
|
141
|
-
env
|
|
162
|
+
env,
|
|
142
163
|
});
|
|
143
164
|
|
|
144
165
|
child.on('error', (err) => {
|
package/bin/openbroker.js
CHANGED
|
@@ -56,6 +56,10 @@ child.on('error', (err) => {
|
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
+
// Let the child handle SIGINT (Ctrl+C) — don't exit the wrapper early
|
|
60
|
+
process.on('SIGINT', () => {});
|
|
61
|
+
process.on('SIGTERM', () => { child.kill('SIGTERM'); });
|
|
62
|
+
|
|
59
63
|
child.on('exit', (code) => {
|
|
60
64
|
process.exit(code ?? 0);
|
|
61
65
|
});
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/scripts/auto/cli.ts
CHANGED
|
@@ -71,11 +71,13 @@ function parseSetFlags(rawArgs: string[]): Record<string, unknown> {
|
|
|
71
71
|
}
|
|
72
72
|
const key = pair.slice(0, eqIdx);
|
|
73
73
|
const raw = pair.slice(eqIdx + 1);
|
|
74
|
+
const isHexLike = /^0x[0-9a-fA-F]+$/.test(raw);
|
|
75
|
+
const isDecimalLike = /^-?(?:\d+|\d+\.\d+|\.\d+)$/.test(raw);
|
|
74
76
|
|
|
75
77
|
// Auto-parse numbers and booleans
|
|
76
78
|
if (raw === 'true') config[key] = true;
|
|
77
79
|
else if (raw === 'false') config[key] = false;
|
|
78
|
-
else if (
|
|
80
|
+
else if (!isHexLike && isDecimalLike) config[key] = Number(raw);
|
|
79
81
|
else config[key] = raw;
|
|
80
82
|
|
|
81
83
|
i++; // skip the value
|
|
@@ -116,6 +118,12 @@ async function runCommand(args: Record<string, string | boolean>, positional: st
|
|
|
116
118
|
const pollIntervalMs = args.poll ? parseInt(String(args.poll), 10) : undefined;
|
|
117
119
|
const id = args.id ? String(args.id) : undefined;
|
|
118
120
|
|
|
121
|
+
if (args.testnet === true) {
|
|
122
|
+
process.env.HYPERLIQUID_NETWORK = 'testnet';
|
|
123
|
+
} else if (args.mainnet === true) {
|
|
124
|
+
process.env.HYPERLIQUID_NETWORK = 'mainnet';
|
|
125
|
+
}
|
|
126
|
+
|
|
119
127
|
if (pollIntervalMs !== undefined && (isNaN(pollIntervalMs) || pollIntervalMs < 1000)) {
|
|
120
128
|
console.error('Error: --poll must be at least 1000ms');
|
|
121
129
|
process.exit(1);
|
package/scripts/auto/loader.ts
CHANGED
|
@@ -70,8 +70,9 @@ export async function loadExampleConfigs(): Promise<Record<string, AutomationCon
|
|
|
70
70
|
for (const example of examples) {
|
|
71
71
|
try {
|
|
72
72
|
const mod = await import(example.path);
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
const config = resolveAutomationConfig(mod);
|
|
74
|
+
if (config && typeof config === 'object' && config.description) {
|
|
75
|
+
configs[example.name] = config;
|
|
75
76
|
}
|
|
76
77
|
} catch {
|
|
77
78
|
// Skip examples that fail to load
|
|
@@ -81,6 +82,39 @@ export async function loadExampleConfigs(): Promise<Record<string, AutomationCon
|
|
|
81
82
|
return configs;
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
function resolveAutomationFactory(mod: Record<string, unknown>): AutomationFactory | null {
|
|
86
|
+
const candidates = [
|
|
87
|
+
mod.default,
|
|
88
|
+
(mod.default as Record<string, unknown> | undefined)?.default,
|
|
89
|
+
mod["module.exports"],
|
|
90
|
+
((mod["module.exports"] as Record<string, unknown> | undefined)?.default)
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
for (const candidate of candidates) {
|
|
94
|
+
if (typeof candidate === "function") {
|
|
95
|
+
return candidate as AutomationFactory;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function resolveAutomationConfig(mod: Record<string, unknown>): AutomationConfig | null {
|
|
103
|
+
const candidates = [
|
|
104
|
+
mod.config,
|
|
105
|
+
(mod.default as Record<string, unknown> | undefined)?.config,
|
|
106
|
+
(mod["module.exports"] as Record<string, unknown> | undefined)?.config
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
for (const candidate of candidates) {
|
|
110
|
+
if (candidate && typeof candidate === "object" && "description" in candidate) {
|
|
111
|
+
return candidate as AutomationConfig;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
84
118
|
/** Load an automation module and validate the default export */
|
|
85
119
|
export async function loadAutomation(scriptPath: string): Promise<AutomationFactory> {
|
|
86
120
|
const absolutePath = path.resolve(scriptPath);
|
|
@@ -88,7 +122,7 @@ export async function loadAutomation(scriptPath: string): Promise<AutomationFact
|
|
|
88
122
|
// Dynamic import — tsx handles TypeScript transpilation
|
|
89
123
|
const mod = await import(absolutePath);
|
|
90
124
|
|
|
91
|
-
const factory = mod
|
|
125
|
+
const factory = resolveAutomationFactory(mod as Record<string, unknown>);
|
|
92
126
|
if (typeof factory !== 'function') {
|
|
93
127
|
throw new Error(
|
|
94
128
|
`Automation script must export a default function.\n` +
|
package/scripts/auto/runtime.ts
CHANGED
|
@@ -388,12 +388,10 @@ export async function startAutomation(options: RuntimeOptions): Promise<RunningA
|
|
|
388
388
|
|
|
389
389
|
const stateController = createState(id);
|
|
390
390
|
|
|
391
|
-
//
|
|
391
|
+
// Apply --set flags before the factory function runs so CLI overrides win over persisted state.
|
|
392
392
|
if (initialState) {
|
|
393
393
|
for (const [key, value] of Object.entries(initialState)) {
|
|
394
|
-
|
|
395
|
-
stateController.state.set(key, value);
|
|
396
|
-
}
|
|
394
|
+
stateController.state.set(key, value);
|
|
397
395
|
}
|
|
398
396
|
}
|
|
399
397
|
|
|
@@ -626,6 +624,9 @@ export async function startAutomation(options: RuntimeOptions): Promise<RunningA
|
|
|
626
624
|
} catch (err) {
|
|
627
625
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
628
626
|
audit.recordError('websocket_setup', error);
|
|
627
|
+
if (verbose && error.stack) {
|
|
628
|
+
log.debug(`WebSocket setup stack: ${error.stack}`);
|
|
629
|
+
}
|
|
629
630
|
log.warn(`WebSocket setup failed: ${error.message} — using REST polling only`);
|
|
630
631
|
ws = null;
|
|
631
632
|
wsConnected = false;
|