@clawnch/clawtomaton 0.8.1 → 0.8.2
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/README.md +5 -4
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +2 -0
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/prompt.d.ts.map +1 -1
- package/dist/agent/prompt.js +35 -0
- package/dist/agent/prompt.js.map +1 -1
- package/dist/heartbeat/index.d.ts +28 -0
- package/dist/heartbeat/index.d.ts.map +1 -1
- package/dist/heartbeat/index.js +188 -2
- package/dist/heartbeat/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/market/index.d.ts +39 -0
- package/dist/market/index.d.ts.map +1 -1
- package/dist/market/index.js +95 -1
- package/dist/market/index.js.map +1 -1
- package/dist/skills/herd.d.ts +23 -0
- package/dist/skills/herd.d.ts.map +1 -0
- package/dist/skills/herd.js +687 -0
- package/dist/skills/herd.js.map +1 -0
- package/dist/skills/hummingbot.d.ts +7 -17
- package/dist/skills/hummingbot.d.ts.map +1 -1
- package/dist/skills/hummingbot.js +984 -362
- package/dist/skills/hummingbot.js.map +1 -1
- package/dist/skills/index.d.ts +2 -1
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +5 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/state/index.d.ts +16 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +67 -0
- package/dist/state/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,456 +1,1078 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Skill: hummingbot —
|
|
2
|
+
* Skill: hummingbot — Professional market making via Hummingbot integration.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Full-featured integration with the Hummingbot API server covering all 14
|
|
5
|
+
* upstream MCP tool capabilities: connector setup, multi-server config,
|
|
6
|
+
* portfolio, order placement, leverage/position mode, executors (with
|
|
7
|
+
* preferences), market data (all order book query types), controllers,
|
|
8
|
+
* bots, gateway (container/config/swaps/CLMM), and history search.
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
* The agent controls strategy parameters; Hummingbot executes the trades.
|
|
10
|
-
*
|
|
11
|
-
* Supported strategies:
|
|
12
|
-
* - pure_market_making: Provide liquidity with configurable spread/order size
|
|
13
|
-
* - avellaneda_market_making: Optimal market making (Avellaneda-Stoikov model)
|
|
14
|
-
* - grid: Grid trading with defined price levels
|
|
15
|
-
* - twap: Time-weighted average price execution
|
|
16
|
-
*
|
|
17
|
-
* Prerequisites:
|
|
18
|
-
* - Docker installed and running
|
|
19
|
-
* - Hummingbot image: docker pull hummingbot/hummingbot:latest
|
|
20
|
-
* - Or Hummingbot MCP server running locally
|
|
10
|
+
* Also includes Clawnch-specific strategy templates.
|
|
21
11
|
*
|
|
22
12
|
* @see https://github.com/hummingbot/hummingbot
|
|
23
13
|
* @see https://github.com/hummingbot/mcp
|
|
24
14
|
*/
|
|
15
|
+
import { HummingbotClient } from '@clawnch/clawncher-sdk';
|
|
25
16
|
// ============================================================================
|
|
26
|
-
//
|
|
17
|
+
// Lazy client singleton
|
|
27
18
|
// ============================================================================
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (!SAFE_NAME_RE.test(name) || name.length > 128) {
|
|
41
|
-
throw new Error(`Invalid name "${name}": must be 1-128 chars, alphanumeric/dash/underscore/dot, start with alphanumeric`);
|
|
19
|
+
let _client = null;
|
|
20
|
+
function getClient(ctx) {
|
|
21
|
+
if (!_client) {
|
|
22
|
+
const config = {
|
|
23
|
+
apiUrl: process.env.HUMMINGBOT_API_URL ?? 'http://localhost:8000',
|
|
24
|
+
username: process.env.HUMMINGBOT_USERNAME ?? 'admin',
|
|
25
|
+
password: process.env.HUMMINGBOT_PASSWORD ?? 'admin',
|
|
26
|
+
timeout: parseInt(process.env.HUMMINGBOT_TIMEOUT ?? '30000', 10),
|
|
27
|
+
maxRetries: parseInt(process.env.HUMMINGBOT_MAX_RETRIES ?? '3', 10),
|
|
28
|
+
retryDelay: parseInt(process.env.HUMMINGBOT_RETRY_DELAY ?? '2000', 10),
|
|
29
|
+
};
|
|
30
|
+
_client = new HummingbotClient(config);
|
|
42
31
|
}
|
|
43
|
-
return
|
|
32
|
+
return _client;
|
|
44
33
|
}
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const { execSync } = await import('child_process');
|
|
51
|
-
try {
|
|
52
|
-
return execSync(command, { encoding: 'utf8', timeout: 30000 }).trim();
|
|
53
|
-
}
|
|
54
|
-
catch (err) {
|
|
55
|
-
throw new Error(`Command failed: ${command}\n${err.stderr ?? err.message}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Check if Docker is available and Hummingbot image exists.
|
|
60
|
-
*/
|
|
61
|
-
async function checkPrerequisites() {
|
|
62
|
-
let docker = false;
|
|
63
|
-
let image = false;
|
|
64
|
-
let mcp = false;
|
|
65
|
-
try {
|
|
66
|
-
await exec('docker info --format "{{.ID}}"');
|
|
67
|
-
docker = true;
|
|
68
|
-
}
|
|
69
|
-
catch { /* Docker not available */ }
|
|
70
|
-
if (docker) {
|
|
71
|
-
try {
|
|
72
|
-
const images = await exec('docker images hummingbot/hummingbot --format "{{.Tag}}"');
|
|
73
|
-
image = images.length > 0;
|
|
74
|
-
}
|
|
75
|
-
catch { /* Image not found */ }
|
|
76
|
-
}
|
|
77
|
-
// Check for MCP server
|
|
78
|
-
try {
|
|
79
|
-
const response = await fetch('http://localhost:8000/health', { signal: AbortSignal.timeout(2000) });
|
|
80
|
-
mcp = response.ok;
|
|
81
|
-
}
|
|
82
|
-
catch { /* MCP not running */ }
|
|
83
|
-
return { docker, image, mcp };
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Generate a Hummingbot strategy config file content.
|
|
87
|
-
*/
|
|
88
|
-
function generateStrategyConfig(strategy, params, tokenAddress, walletAddress) {
|
|
89
|
-
const base = {
|
|
90
|
-
strategy,
|
|
91
|
-
exchange: 'uniswap_base_base',
|
|
92
|
-
market: `${tokenAddress}-WETH`,
|
|
93
|
-
};
|
|
94
|
-
switch (strategy) {
|
|
95
|
-
case 'pure_market_making':
|
|
96
|
-
return yamlify({
|
|
97
|
-
...base,
|
|
98
|
-
bid_spread: params.bid_spread ?? 0.01,
|
|
99
|
-
ask_spread: params.ask_spread ?? 0.01,
|
|
100
|
-
order_amount: params.order_amount ?? 0.001,
|
|
101
|
-
order_refresh_time: params.order_refresh_time ?? 30,
|
|
102
|
-
inventory_skew_enabled: true,
|
|
103
|
-
inventory_target_base_pct: params.inventory_target ?? 0.5,
|
|
104
|
-
filled_order_delay: 10,
|
|
105
|
-
});
|
|
106
|
-
case 'avellaneda_market_making':
|
|
107
|
-
return yamlify({
|
|
108
|
-
...base,
|
|
109
|
-
risk_factor: params.risk_factor ?? 0.5,
|
|
110
|
-
order_amount: params.order_amount ?? 0.001,
|
|
111
|
-
order_refresh_time: params.order_refresh_time ?? 10,
|
|
112
|
-
inventory_target_base_pct: params.inventory_target ?? 0.5,
|
|
113
|
-
min_spread: params.min_spread ?? 0.002,
|
|
114
|
-
max_spread: params.max_spread ?? 0.05,
|
|
115
|
-
vol_to_spread_multiplier: params.vol_multiplier ?? 1.0,
|
|
116
|
-
execution_timeframe: 'infinite',
|
|
117
|
-
});
|
|
118
|
-
case 'grid':
|
|
119
|
-
return yamlify({
|
|
120
|
-
...base,
|
|
121
|
-
n_levels: params.levels ?? 5,
|
|
122
|
-
grid_price_ceiling: params.price_ceiling ?? 0,
|
|
123
|
-
grid_price_floor: params.price_floor ?? 0,
|
|
124
|
-
order_amount: params.order_amount ?? 0.001,
|
|
125
|
-
order_refresh_time: params.order_refresh_time ?? 300,
|
|
126
|
-
start_order_spread: params.start_spread ?? 0.01,
|
|
127
|
-
});
|
|
128
|
-
case 'twap':
|
|
129
|
-
return yamlify({
|
|
130
|
-
...base,
|
|
131
|
-
trade_side: params.side ?? 'buy',
|
|
132
|
-
target_asset_amount: params.total_amount ?? 0.01,
|
|
133
|
-
order_step_size: params.step_size ?? 0.001,
|
|
134
|
-
order_delay_time: params.delay_seconds ?? 60,
|
|
135
|
-
is_time_span_execution: true,
|
|
136
|
-
cancel_order_wait_time: params.cancel_wait ?? 30,
|
|
137
|
-
});
|
|
138
|
-
default:
|
|
139
|
-
return yamlify(base);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
/** Simple YAML serializer for config objects */
|
|
143
|
-
function yamlify(obj) {
|
|
144
|
-
const lines = [];
|
|
145
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
146
|
-
if (value === undefined || value === null)
|
|
147
|
-
continue;
|
|
148
|
-
if (typeof value === 'boolean') {
|
|
149
|
-
lines.push(`${key}: ${value ? 'true' : 'false'}`);
|
|
150
|
-
}
|
|
151
|
-
else if (typeof value === 'number') {
|
|
152
|
-
lines.push(`${key}: ${value}`);
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
lines.push(`${key}: "${value}"`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return lines.join('\n');
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* List running Hummingbot instances managed by Clawtomaton.
|
|
162
|
-
*/
|
|
163
|
-
async function listInstances() {
|
|
164
|
-
try {
|
|
165
|
-
const output = await exec(`docker ps -a --filter "name=${HB_CONTAINER_PREFIX}" --format "{{.Names}}|{{.Status}}|{{.CreatedAt}}"`);
|
|
166
|
-
if (!output)
|
|
167
|
-
return [];
|
|
168
|
-
return output.split('\n').filter(Boolean).map(line => {
|
|
169
|
-
const [name, status, created] = line.split('|');
|
|
170
|
-
return { name, status, created };
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
return [];
|
|
175
|
-
}
|
|
34
|
+
/** Helper to split comma-separated strings into arrays */
|
|
35
|
+
function splitCsv(val) {
|
|
36
|
+
if (!val)
|
|
37
|
+
return undefined;
|
|
38
|
+
return String(val).split(',').map(s => s.trim()).filter(Boolean);
|
|
176
39
|
}
|
|
177
40
|
// ============================================================================
|
|
178
41
|
// Skill definition
|
|
179
42
|
// ============================================================================
|
|
180
43
|
export const hummingbotSkill = {
|
|
181
44
|
name: 'hummingbot',
|
|
182
|
-
description: '
|
|
183
|
-
'
|
|
184
|
-
'
|
|
45
|
+
description: 'Professional market making and trading via Hummingbot. ' +
|
|
46
|
+
'Actions: "status", "portfolio", "order", "leverage", "executor", "market_data", ' +
|
|
47
|
+
'"controller", "bot", "gateway", "history", "templates", "connector", "servers", ' +
|
|
48
|
+
'"backtest", "discovery", "archived", "scripts", "analytics", "accounts". ' +
|
|
49
|
+
'Requires Hummingbot API server (default: localhost:8000).',
|
|
185
50
|
parameters: [
|
|
186
51
|
{
|
|
187
52
|
name: 'action',
|
|
188
53
|
type: 'string',
|
|
189
|
-
description: 'Action: "status"
|
|
190
|
-
'"
|
|
54
|
+
description: 'Action: "status", "portfolio", "order", "leverage", "executor", "market_data", ' +
|
|
55
|
+
'"controller", "bot", "gateway", "history", "templates", "connector", "servers", ' +
|
|
56
|
+
'"backtest", "discovery", "archived", "scripts", "analytics", "accounts".',
|
|
191
57
|
required: true,
|
|
192
58
|
},
|
|
193
59
|
{
|
|
194
|
-
name: '
|
|
195
|
-
type: 'string',
|
|
196
|
-
description: 'Strategy name for start/configure: "pure_market_making" (simple spread), ' +
|
|
197
|
-
'"avellaneda_market_making" (optimal, Avellaneda-Stoikov model), "grid" (grid trading), "twap" (time-weighted execution).',
|
|
198
|
-
required: false,
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
name: 'token_address',
|
|
202
|
-
type: 'string',
|
|
203
|
-
description: 'Token address to market-make. Uses agent\'s deployed token by default.',
|
|
204
|
-
required: false,
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
name: 'instance_name',
|
|
208
|
-
type: 'string',
|
|
209
|
-
description: 'Instance name for stop/logs. Auto-generated if not provided for start.',
|
|
210
|
-
required: false,
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
name: 'bid_spread',
|
|
214
|
-
type: 'number',
|
|
215
|
-
description: 'Bid spread as decimal (0.01 = 1%). Default: 0.01.',
|
|
216
|
-
required: false,
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
name: 'ask_spread',
|
|
220
|
-
type: 'number',
|
|
221
|
-
description: 'Ask spread as decimal (0.01 = 1%). Default: 0.01.',
|
|
222
|
-
required: false,
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
name: 'order_amount',
|
|
60
|
+
name: 'sub_action',
|
|
226
61
|
type: 'string',
|
|
227
|
-
description: '
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
62
|
+
description: 'Sub-action for compound actions. ' +
|
|
63
|
+
'executor: create|search|stop|logs|positions|get_preferences|save_preferences|reset_preferences|clear_position|types|type_config|summary|get_by_id. ' +
|
|
64
|
+
'bot: deploy|status|logs|stop|stop_controllers|start_controllers|history|runs|archive. ' +
|
|
65
|
+
'gateway: container_status|container_start|container_stop|container_restart|container_logs|' +
|
|
66
|
+
'config_list|config_get|config_update|config_add|config_delete|' +
|
|
67
|
+
'swap_quote|swap_execute|swap_search|swap_status|' +
|
|
68
|
+
'clmm_list|clmm_info|clmm_open|clmm_close|clmm_fees|clmm_positions. ' +
|
|
69
|
+
'market_data: prices|candles|historical_candles|funding|orderbook|vwap|add_pair|remove_pair. ' +
|
|
70
|
+
'history: orders|perp_positions|clmm_positions. ' +
|
|
71
|
+
'templates: list|describe|build. ' +
|
|
72
|
+
'connector: list|setup|delete. ' +
|
|
73
|
+
'controller: list|describe|upsert|delete. ' +
|
|
74
|
+
'servers: list|add|modify|set_default|remove. ' +
|
|
75
|
+
'discovery: connectors|config_map|trading_rules|order_types. ' +
|
|
76
|
+
'archived: list|summary|performance|trades|orders. ' +
|
|
77
|
+
'scripts: list|get|upsert|delete|configs|get_config|upsert_config|delete_config|template. ' +
|
|
78
|
+
'analytics: history|distribution|accounts_distribution|funding_payments|rates|rate. ' +
|
|
79
|
+
'accounts: list|credentials|create|delete.',
|
|
240
80
|
required: false,
|
|
241
81
|
},
|
|
82
|
+
// --- Shared ---
|
|
83
|
+
{ name: 'connector_name', type: 'string', description: 'Exchange connector name.', required: false },
|
|
84
|
+
{ name: 'trading_pair', type: 'string', description: 'Trading pair (e.g. "ETH-USDT").', required: false },
|
|
85
|
+
{ name: 'trading_pairs', type: 'string', description: 'Comma-separated trading pairs.', required: false },
|
|
86
|
+
{ name: 'account_name', type: 'string', description: 'Account name.', required: false },
|
|
87
|
+
{ name: 'account_names', type: 'string', description: 'Comma-separated account names.', required: false },
|
|
88
|
+
{ name: 'connector_names', type: 'string', description: 'Comma-separated connector names for filtering.', required: false },
|
|
89
|
+
// --- Order ---
|
|
90
|
+
{ name: 'side', type: 'string', description: 'Trade side: "BUY" or "SELL".', required: false },
|
|
91
|
+
{ name: 'amount', type: 'string', description: 'Order amount (base currency, or "$100" for USD value).', required: false },
|
|
92
|
+
{ name: 'order_type', type: 'string', description: 'Order type: "MARKET", "LIMIT", "LIMIT_MAKER".', required: false },
|
|
93
|
+
{ name: 'price', type: 'string', description: 'Price for limit orders.', required: false },
|
|
94
|
+
{ name: 'position_action', type: 'string', description: 'Position action for perps: "OPEN" or "CLOSE".', required: false },
|
|
95
|
+
// --- Leverage ---
|
|
96
|
+
{ name: 'leverage', type: 'number', description: 'Leverage multiplier for perpetuals.', required: false },
|
|
97
|
+
{ name: 'position_mode', type: 'string', description: 'Position mode: "HEDGE" or "ONE-WAY".', required: false },
|
|
98
|
+
// --- Executor ---
|
|
99
|
+
{ name: 'executor_type', type: 'string', description: 'Executor type: grid_executor, dca_executor, position_executor, order_executor.', required: false },
|
|
100
|
+
{ name: 'executor_id', type: 'string', description: 'Executor ID for stop/logs/search.', required: false },
|
|
101
|
+
{ name: 'executor_config', type: 'string', description: 'JSON string of executor/controller configuration.', required: false },
|
|
102
|
+
{ name: 'keep_position', type: 'boolean', description: 'Keep position when stopping executor.', required: false },
|
|
103
|
+
{ name: 'preferences_content', type: 'string', description: 'Markdown content for save_preferences.', required: false },
|
|
104
|
+
// --- Bot ---
|
|
105
|
+
{ name: 'bot_name', type: 'string', description: 'Bot name.', required: false },
|
|
106
|
+
{ name: 'controllers_config', type: 'string', description: 'Comma-separated controller config/names.', required: false },
|
|
107
|
+
// --- Market data ---
|
|
108
|
+
{ name: 'interval', type: 'string', description: 'Candle interval: "1m", "5m", "15m", "30m", "1h", "4h", "1d".', required: false },
|
|
109
|
+
{ name: 'days', type: 'number', description: 'Days of historical data for candles.', required: false },
|
|
110
|
+
{ name: 'query_type', type: 'string', description: 'Order book query type: snapshot, volume_for_price, price_for_volume, quote_volume_for_price, price_for_quote_volume.', required: false },
|
|
111
|
+
{ name: 'query_value', type: 'number', description: 'Query value for order book queries (price or volume).', required: false },
|
|
112
|
+
{ name: 'is_buy', type: 'boolean', description: 'Side for order book queries (default: true).', required: false },
|
|
113
|
+
// --- Controller ---
|
|
114
|
+
{ name: 'target', type: 'string', description: 'Controller target: "controller" or "config".', required: false },
|
|
115
|
+
{ name: 'controller_type', type: 'string', description: 'Controller type: directional_trading, market_making, generic.', required: false },
|
|
116
|
+
{ name: 'controller_name', type: 'string', description: 'Controller name.', required: false },
|
|
117
|
+
{ name: 'controller_code', type: 'string', description: 'Python code for controller upsert.', required: false },
|
|
118
|
+
{ name: 'config_name', type: 'string', description: 'Config name for controller operations.', required: false },
|
|
119
|
+
{ name: 'config_data', type: 'string', description: 'JSON config data for controller upsert.', required: false },
|
|
120
|
+
{ name: 'confirm_override', type: 'boolean', description: 'Confirm overwrite for controller/connector.', required: false },
|
|
121
|
+
// --- Templates ---
|
|
122
|
+
{ name: 'template', type: 'string', description: 'Strategy template name.', required: false },
|
|
123
|
+
// --- History ---
|
|
124
|
+
{ name: 'limit', type: 'number', description: 'Max results.', required: false },
|
|
125
|
+
{ name: 'offset', type: 'number', description: 'Pagination offset.', required: false },
|
|
126
|
+
{ name: 'status', type: 'string', description: 'Status filter (OPEN, CLOSED, FILLED, CANCELED, RUNNING, TERMINATED, etc).', required: false },
|
|
127
|
+
{ name: 'start_time', type: 'number', description: 'Start time (unix seconds) for history.', required: false },
|
|
128
|
+
{ name: 'end_time', type: 'number', description: 'End time (unix seconds) for history.', required: false },
|
|
129
|
+
// --- Gateway ---
|
|
130
|
+
{ name: 'network', type: 'string', description: 'Network for gateway (e.g. "solana-mainnet-beta").', required: false },
|
|
131
|
+
{ name: 'pool_address', type: 'string', description: 'Pool address for CLMM.', required: false },
|
|
132
|
+
{ name: 'position_address', type: 'string', description: 'Position NFT address for CLMM.', required: false },
|
|
133
|
+
{ name: 'wallet_address', type: 'string', description: 'Wallet address for gateway operations.', required: false },
|
|
134
|
+
{ name: 'slippage_pct', type: 'string', description: 'Slippage tolerance percentage (e.g. "1.0").', required: false },
|
|
135
|
+
{ name: 'lower_price', type: 'string', description: 'Lower price bound for CLMM open_position.', required: false },
|
|
136
|
+
{ name: 'upper_price', type: 'string', description: 'Upper price bound for CLMM open_position.', required: false },
|
|
137
|
+
{ name: 'base_token_amount', type: 'string', description: 'Base token amount for CLMM.', required: false },
|
|
138
|
+
{ name: 'quote_token_amount', type: 'string', description: 'Quote token amount for CLMM.', required: false },
|
|
139
|
+
{ name: 'transaction_hash', type: 'string', description: 'Transaction hash for swap status lookup.', required: false },
|
|
140
|
+
// --- Gateway config ---
|
|
141
|
+
{ name: 'resource_type', type: 'string', description: 'Gateway config resource: chains, networks, tokens, connectors, pools, wallets.', required: false },
|
|
142
|
+
{ name: 'chain', type: 'string', description: 'Blockchain chain name.', required: false },
|
|
143
|
+
{ name: 'private_key', type: 'string', description: 'Private key for wallet operations.', required: false },
|
|
144
|
+
{ name: 'token_address', type: 'string', description: 'Token contract address.', required: false },
|
|
145
|
+
{ name: 'token_symbol', type: 'string', description: 'Token symbol.', required: false },
|
|
146
|
+
{ name: 'token_decimals', type: 'number', description: 'Token decimals.', required: false },
|
|
147
|
+
{ name: 'pool_type', type: 'string', description: 'Pool type: CLMM or AMM.', required: false },
|
|
148
|
+
{ name: 'search', type: 'string', description: 'Search/filter term.', required: false },
|
|
149
|
+
// --- Connector setup ---
|
|
150
|
+
{ name: 'credentials', type: 'string', description: 'JSON credentials for connector setup.', required: false },
|
|
151
|
+
// --- Server config ---
|
|
152
|
+
{ name: 'host', type: 'string', description: 'API server host for servers action.', required: false },
|
|
153
|
+
{ name: 'port', type: 'number', description: 'API server port.', required: false },
|
|
154
|
+
{ name: 'server_name', type: 'string', description: 'Server name for servers action.', required: false },
|
|
155
|
+
// --- Backtesting ---
|
|
156
|
+
{ name: 'config', type: 'string', description: 'Config name or JSON for backtesting.', required: false },
|
|
157
|
+
{ name: 'resolution', type: 'string', description: 'Backtesting resolution (1m, 5m, 1h).', required: false },
|
|
158
|
+
{ name: 'trade_cost', type: 'number', description: 'Trade cost as decimal (0.0006 = 0.06%).', required: false },
|
|
159
|
+
// --- Scripts ---
|
|
160
|
+
{ name: 'script_name', type: 'string', description: 'Script name.', required: false },
|
|
161
|
+
{ name: 'script_content', type: 'string', description: 'Python source code for script upsert.', required: false },
|
|
162
|
+
// --- Rate Oracle ---
|
|
163
|
+
{ name: 'db_path', type: 'string', description: 'Archived bot database path.', required: false },
|
|
164
|
+
// --- Active Orders / Trades ---
|
|
165
|
+
{ name: 'client_order_id', type: 'string', description: 'Client order ID for cancellation.', required: false },
|
|
166
|
+
{ name: 'trade_types', type: 'string', description: 'Comma-separated trade types filter.', required: false },
|
|
167
|
+
{ name: 'verbose', type: 'boolean', description: 'Verbose output for bot history.', required: false },
|
|
168
|
+
{ name: 'run_status', type: 'string', description: 'Bot run status filter.', required: false },
|
|
169
|
+
{ name: 'volume', type: 'number', description: 'Volume for VWAP calculation.', required: false },
|
|
170
|
+
// --- Portfolio ---
|
|
171
|
+
{ name: 'refresh', type: 'boolean', description: 'Force refresh portfolio from exchanges.', required: false },
|
|
172
|
+
{ name: 'as_distribution', type: 'boolean', description: 'Show portfolio as distribution percentages.', required: false },
|
|
173
|
+
{ name: 'include_balances', type: 'boolean', description: 'Include token balances in portfolio.', required: false },
|
|
174
|
+
{ name: 'include_perp_positions', type: 'boolean', description: 'Include perp positions in portfolio.', required: false },
|
|
175
|
+
{ name: 'include_lp_positions', type: 'boolean', description: 'Include LP positions in portfolio.', required: false },
|
|
176
|
+
{ name: 'include_active_orders', type: 'boolean', description: 'Include active orders in portfolio.', required: false },
|
|
177
|
+
// --- Gateway container ---
|
|
178
|
+
{ name: 'passphrase', type: 'string', description: 'Gateway container passphrase.', required: false },
|
|
179
|
+
{ name: 'image', type: 'string', description: 'Docker image for gateway/bot.', required: false },
|
|
180
|
+
{ name: 'tail', type: 'number', description: 'Number of log lines.', required: false },
|
|
242
181
|
],
|
|
243
182
|
execute: async (params, ctx) => {
|
|
244
183
|
try {
|
|
184
|
+
const client = getClient(ctx);
|
|
245
185
|
const action = params.action;
|
|
246
186
|
switch (action) {
|
|
247
187
|
// ================================================================
|
|
248
|
-
// Status
|
|
188
|
+
// Status
|
|
249
189
|
// ================================================================
|
|
250
190
|
case 'status': {
|
|
251
|
-
const prereqs = await checkPrerequisites();
|
|
252
|
-
const
|
|
191
|
+
const prereqs = await client.checkPrerequisites();
|
|
192
|
+
const dockerInstances = prereqs.docker ? await client.listDockerInstances() : [];
|
|
253
193
|
const lines = [
|
|
254
194
|
'Hummingbot Status:',
|
|
255
|
-
`
|
|
256
|
-
`
|
|
257
|
-
`
|
|
258
|
-
`
|
|
195
|
+
` API Server: ${prereqs.api ? `connected at ${process.env.HUMMINGBOT_API_URL ?? 'localhost:8000'}` : 'NOT CONNECTED'}`,
|
|
196
|
+
` Docker: ${prereqs.docker ? 'available' : 'NOT FOUND'}`,
|
|
197
|
+
` HB Image: ${prereqs.image ? 'installed' : 'not found'}`,
|
|
198
|
+
` MCP Server: ${prereqs.mcp ? 'running' : 'not running'}`,
|
|
259
199
|
];
|
|
260
|
-
if (
|
|
261
|
-
lines.push('');
|
|
262
|
-
|
|
263
|
-
|
|
200
|
+
if (prereqs.api) {
|
|
201
|
+
lines.push('', 'Full capabilities available: portfolio, order, leverage, executor, market_data, controller, bot, gateway, history, templates, connector, servers');
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
lines.push('', 'Set HUMMINGBOT_API_URL, HUMMINGBOT_USERNAME, HUMMINGBOT_PASSWORD to connect.');
|
|
205
|
+
}
|
|
206
|
+
if (dockerInstances.length > 0) {
|
|
207
|
+
lines.push('', `Docker instances (${dockerInstances.length}):`);
|
|
208
|
+
for (const inst of dockerInstances) {
|
|
264
209
|
lines.push(` ${inst.name}: ${inst.status} (created ${inst.created})`);
|
|
265
210
|
}
|
|
266
211
|
}
|
|
267
|
-
if (!prereqs.docker && !prereqs.mcp) {
|
|
268
|
-
lines.push('');
|
|
269
|
-
lines.push('To get started:');
|
|
270
|
-
lines.push(' 1. Install Docker: https://docs.docker.com/get-docker/');
|
|
271
|
-
lines.push(' 2. Pull image: docker pull hummingbot/hummingbot:latest');
|
|
272
|
-
lines.push(' 3. Or install Hummingbot MCP: https://github.com/hummingbot/mcp');
|
|
273
|
-
}
|
|
274
212
|
return { callId: '', success: true, result: lines.join('\n') };
|
|
275
213
|
}
|
|
276
214
|
// ================================================================
|
|
277
|
-
//
|
|
215
|
+
// Portfolio
|
|
216
|
+
// ================================================================
|
|
217
|
+
case 'portfolio': {
|
|
218
|
+
const result = await client.getPortfolioOverview({
|
|
219
|
+
accountNames: splitCsv(params.account_names),
|
|
220
|
+
connectorNames: splitCsv(params.connector_names) ?? (params.connector_name ? [params.connector_name] : undefined),
|
|
221
|
+
includeBalances: params.include_balances ?? true,
|
|
222
|
+
includePerpPositions: params.include_perp_positions ?? true,
|
|
223
|
+
includeLPPositions: params.include_lp_positions ?? true,
|
|
224
|
+
includeActiveOrders: params.include_active_orders ?? true,
|
|
225
|
+
asDistribution: params.as_distribution ?? false,
|
|
226
|
+
refresh: params.refresh ?? true,
|
|
227
|
+
});
|
|
228
|
+
return { callId: '', success: true, result };
|
|
229
|
+
}
|
|
230
|
+
// ================================================================
|
|
231
|
+
// Order
|
|
232
|
+
// ================================================================
|
|
233
|
+
case 'order': {
|
|
234
|
+
const connectorName = params.connector_name;
|
|
235
|
+
const tradingPair = params.trading_pair;
|
|
236
|
+
const side = params.side;
|
|
237
|
+
const amount = params.amount;
|
|
238
|
+
if (!connectorName || !tradingPair || !side || !amount) {
|
|
239
|
+
return {
|
|
240
|
+
callId: '', success: false, result: null,
|
|
241
|
+
error: 'Required: connector_name, trading_pair, side (BUY/SELL), amount.',
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const result = await client.placeOrder({
|
|
245
|
+
connectorName,
|
|
246
|
+
tradingPair,
|
|
247
|
+
tradeType: side.toUpperCase(),
|
|
248
|
+
amount,
|
|
249
|
+
orderType: params.order_type?.toUpperCase() ?? 'MARKET',
|
|
250
|
+
price: params.price,
|
|
251
|
+
positionAction: params.position_action?.toUpperCase() ?? 'OPEN',
|
|
252
|
+
accountName: params.account_name,
|
|
253
|
+
});
|
|
254
|
+
return {
|
|
255
|
+
callId: '', success: result.success, result: result.result,
|
|
256
|
+
error: result.success ? undefined : result.result,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
// ================================================================
|
|
260
|
+
// Leverage / Position Mode
|
|
261
|
+
// ================================================================
|
|
262
|
+
case 'leverage': {
|
|
263
|
+
const accountName = params.account_name ?? 'master_account';
|
|
264
|
+
const connectorName = params.connector_name;
|
|
265
|
+
if (!connectorName) {
|
|
266
|
+
return { callId: '', success: false, result: null, error: 'connector_name required for leverage.' };
|
|
267
|
+
}
|
|
268
|
+
const result = await client.setPositionModeAndLeverage({
|
|
269
|
+
accountName,
|
|
270
|
+
connectorName,
|
|
271
|
+
tradingPair: params.trading_pair,
|
|
272
|
+
positionMode: params.position_mode?.toUpperCase(),
|
|
273
|
+
leverage: params.leverage,
|
|
274
|
+
});
|
|
275
|
+
return { callId: '', success: true, result };
|
|
276
|
+
}
|
|
277
|
+
// ================================================================
|
|
278
|
+
// Executor
|
|
279
|
+
// ================================================================
|
|
280
|
+
case 'executor': {
|
|
281
|
+
const subAction = params.sub_action ?? 'search';
|
|
282
|
+
switch (subAction) {
|
|
283
|
+
case 'create': {
|
|
284
|
+
const configStr = params.executor_config;
|
|
285
|
+
if (!configStr) {
|
|
286
|
+
const executorType = params.executor_type;
|
|
287
|
+
if (executorType) {
|
|
288
|
+
const result = await client.getExecutorTypeConfig(executorType);
|
|
289
|
+
return { callId: '', success: true, result };
|
|
290
|
+
}
|
|
291
|
+
const result = await client.getExecutorTypes();
|
|
292
|
+
return { callId: '', success: true, result };
|
|
293
|
+
}
|
|
294
|
+
let config;
|
|
295
|
+
try {
|
|
296
|
+
config = JSON.parse(configStr);
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
return { callId: '', success: false, result: null, error: 'executor_config must be valid JSON.' };
|
|
300
|
+
}
|
|
301
|
+
const result = await client.createExecutor(config);
|
|
302
|
+
return { callId: '', success: true, result };
|
|
303
|
+
}
|
|
304
|
+
case 'search': {
|
|
305
|
+
const result = await client.searchExecutors({
|
|
306
|
+
executorId: params.executor_id,
|
|
307
|
+
executorTypes: params.executor_type ? [params.executor_type] : undefined,
|
|
308
|
+
connectorNames: splitCsv(params.connector_names) ?? (params.connector_name ? [params.connector_name] : undefined),
|
|
309
|
+
tradingPairs: splitCsv(params.trading_pairs) ?? (params.trading_pair ? [params.trading_pair] : undefined),
|
|
310
|
+
status: params.status,
|
|
311
|
+
limit: params.limit ?? 50,
|
|
312
|
+
});
|
|
313
|
+
return { callId: '', success: true, result };
|
|
314
|
+
}
|
|
315
|
+
case 'stop': {
|
|
316
|
+
if (!params.executor_id) {
|
|
317
|
+
return { callId: '', success: false, result: null, error: 'executor_id required to stop.' };
|
|
318
|
+
}
|
|
319
|
+
const result = await client.stopExecutor(params.executor_id, params.keep_position ?? false);
|
|
320
|
+
return { callId: '', success: true, result };
|
|
321
|
+
}
|
|
322
|
+
case 'logs': {
|
|
323
|
+
if (!params.executor_id) {
|
|
324
|
+
return { callId: '', success: false, result: null, error: 'executor_id required for logs.' };
|
|
325
|
+
}
|
|
326
|
+
const result = await client.getExecutorLogs(params.executor_id);
|
|
327
|
+
return { callId: '', success: true, result };
|
|
328
|
+
}
|
|
329
|
+
case 'positions': {
|
|
330
|
+
const result = await client.getPositionsSummary(params.connector_name, params.trading_pair);
|
|
331
|
+
return { callId: '', success: true, result };
|
|
332
|
+
}
|
|
333
|
+
case 'clear_position': {
|
|
334
|
+
if (!params.connector_name || !params.trading_pair) {
|
|
335
|
+
return { callId: '', success: false, result: null, error: 'connector_name and trading_pair required.' };
|
|
336
|
+
}
|
|
337
|
+
const result = await client.clearPosition(params.connector_name, params.trading_pair);
|
|
338
|
+
return { callId: '', success: true, result };
|
|
339
|
+
}
|
|
340
|
+
case 'types': {
|
|
341
|
+
const result = await client.getExecutorTypes();
|
|
342
|
+
return { callId: '', success: true, result };
|
|
343
|
+
}
|
|
344
|
+
case 'type_config': {
|
|
345
|
+
if (!params.executor_type)
|
|
346
|
+
return { callId: '', success: false, result: null, error: 'executor_type required.' };
|
|
347
|
+
const result = await client.getExecutorTypeConfig(params.executor_type);
|
|
348
|
+
return { callId: '', success: true, result };
|
|
349
|
+
}
|
|
350
|
+
case 'summary': {
|
|
351
|
+
const result = await client.getExecutorsSummary();
|
|
352
|
+
return { callId: '', success: true, result };
|
|
353
|
+
}
|
|
354
|
+
case 'get_by_id': {
|
|
355
|
+
if (!params.executor_id)
|
|
356
|
+
return { callId: '', success: false, result: null, error: 'executor_id required.' };
|
|
357
|
+
const result = await client.getExecutorById(params.executor_id);
|
|
358
|
+
return { callId: '', success: true, result };
|
|
359
|
+
}
|
|
360
|
+
default:
|
|
361
|
+
return {
|
|
362
|
+
callId: '', success: false, result: null,
|
|
363
|
+
error: `Unknown executor sub_action: "${subAction}". Use: create, search, stop, logs, positions, clear_position, types, type_config, summary, get_by_id.`,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// ================================================================
|
|
368
|
+
// Market Data
|
|
369
|
+
// ================================================================
|
|
370
|
+
case 'market_data': {
|
|
371
|
+
const subAction = params.sub_action ?? 'prices';
|
|
372
|
+
const connectorName = params.connector_name;
|
|
373
|
+
if (!connectorName) {
|
|
374
|
+
return { callId: '', success: false, result: null, error: 'connector_name required for market_data.' };
|
|
375
|
+
}
|
|
376
|
+
switch (subAction) {
|
|
377
|
+
case 'prices': {
|
|
378
|
+
const pairs = splitCsv(params.trading_pairs) ?? splitCsv(params.trading_pair) ?? [];
|
|
379
|
+
if (pairs.length === 0) {
|
|
380
|
+
return { callId: '', success: false, result: null, error: 'trading_pairs or trading_pair required.' };
|
|
381
|
+
}
|
|
382
|
+
const result = await client.getPrices(connectorName, pairs);
|
|
383
|
+
return { callId: '', success: true, result };
|
|
384
|
+
}
|
|
385
|
+
case 'candles': {
|
|
386
|
+
if (!params.trading_pair) {
|
|
387
|
+
return { callId: '', success: false, result: null, error: 'trading_pair required for candles.' };
|
|
388
|
+
}
|
|
389
|
+
const result = await client.getCandles(connectorName, params.trading_pair, params.interval ?? '1h', params.days ?? 30);
|
|
390
|
+
return { callId: '', success: true, result };
|
|
391
|
+
}
|
|
392
|
+
case 'funding': {
|
|
393
|
+
if (!params.trading_pair) {
|
|
394
|
+
return { callId: '', success: false, result: null, error: 'trading_pair required for funding.' };
|
|
395
|
+
}
|
|
396
|
+
const result = await client.getFundingRate(connectorName, params.trading_pair);
|
|
397
|
+
return { callId: '', success: true, result };
|
|
398
|
+
}
|
|
399
|
+
case 'orderbook': {
|
|
400
|
+
if (!params.trading_pair) {
|
|
401
|
+
return { callId: '', success: false, result: null, error: 'trading_pair required for orderbook.' };
|
|
402
|
+
}
|
|
403
|
+
const result = await client.getOrderBook(connectorName, params.trading_pair, params.query_type ?? 'snapshot', params.query_value, params.is_buy ?? true);
|
|
404
|
+
return { callId: '', success: true, result };
|
|
405
|
+
}
|
|
406
|
+
case 'historical_candles': {
|
|
407
|
+
if (!params.trading_pair)
|
|
408
|
+
return { callId: '', success: false, result: null, error: 'trading_pair required.' };
|
|
409
|
+
if (!params.start_time || !params.end_time)
|
|
410
|
+
return { callId: '', success: false, result: null, error: 'start_time and end_time required.' };
|
|
411
|
+
const result = await client.getHistoricalCandles({
|
|
412
|
+
connectorName,
|
|
413
|
+
tradingPair: params.trading_pair,
|
|
414
|
+
interval: params.interval ?? '1h',
|
|
415
|
+
startTime: params.start_time,
|
|
416
|
+
endTime: params.end_time,
|
|
417
|
+
});
|
|
418
|
+
return { callId: '', success: true, result };
|
|
419
|
+
}
|
|
420
|
+
case 'vwap': {
|
|
421
|
+
if (!params.trading_pair)
|
|
422
|
+
return { callId: '', success: false, result: null, error: 'trading_pair required.' };
|
|
423
|
+
if (!params.volume)
|
|
424
|
+
return { callId: '', success: false, result: null, error: 'volume required for VWAP.' };
|
|
425
|
+
const result = await client.getVWAP(connectorName, params.trading_pair, params.is_buy ?? true, params.volume);
|
|
426
|
+
return { callId: '', success: true, result };
|
|
427
|
+
}
|
|
428
|
+
case 'add_pair': {
|
|
429
|
+
if (!params.trading_pair)
|
|
430
|
+
return { callId: '', success: false, result: null, error: 'trading_pair required.' };
|
|
431
|
+
const result = await client.addTradingPair(connectorName, params.trading_pair, params.account_name);
|
|
432
|
+
return { callId: '', success: true, result };
|
|
433
|
+
}
|
|
434
|
+
case 'remove_pair': {
|
|
435
|
+
if (!params.trading_pair)
|
|
436
|
+
return { callId: '', success: false, result: null, error: 'trading_pair required.' };
|
|
437
|
+
const result = await client.removeTradingPair(connectorName, params.trading_pair, params.account_name);
|
|
438
|
+
return { callId: '', success: true, result };
|
|
439
|
+
}
|
|
440
|
+
default:
|
|
441
|
+
return {
|
|
442
|
+
callId: '', success: false, result: null,
|
|
443
|
+
error: `Unknown market_data sub_action: "${subAction}". Use: prices, candles, historical_candles, funding, orderbook, vwap, add_pair, remove_pair.`,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
// ================================================================
|
|
448
|
+
// Controller management
|
|
278
449
|
// ================================================================
|
|
279
|
-
case '
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
450
|
+
case 'controller': {
|
|
451
|
+
const subAction = params.sub_action ?? 'list';
|
|
452
|
+
const target = params.target ?? 'controller';
|
|
453
|
+
switch (subAction) {
|
|
454
|
+
case 'list': {
|
|
455
|
+
if (target === 'config') {
|
|
456
|
+
const result = await client.listControllerConfigs();
|
|
457
|
+
return { callId: '', success: true, result };
|
|
458
|
+
}
|
|
459
|
+
const result = await client.listControllers(params.controller_type);
|
|
460
|
+
return { callId: '', success: true, result };
|
|
461
|
+
}
|
|
462
|
+
case 'describe': {
|
|
463
|
+
if (target === 'config' && params.config_name) {
|
|
464
|
+
const result = await client.getControllerConfig(params.config_name);
|
|
465
|
+
return { callId: '', success: true, result };
|
|
466
|
+
}
|
|
467
|
+
if (!params.controller_name) {
|
|
468
|
+
return { callId: '', success: false, result: null, error: 'controller_name required.' };
|
|
469
|
+
}
|
|
470
|
+
const result = await client.getController(params.controller_name);
|
|
471
|
+
return { callId: '', success: true, result };
|
|
472
|
+
}
|
|
473
|
+
case 'upsert': {
|
|
474
|
+
if (target === 'config') {
|
|
475
|
+
if (!params.config_name || !params.config_data) {
|
|
476
|
+
return { callId: '', success: false, result: null, error: 'config_name and config_data required.' };
|
|
477
|
+
}
|
|
478
|
+
let configData;
|
|
479
|
+
try {
|
|
480
|
+
configData = JSON.parse(params.config_data);
|
|
481
|
+
}
|
|
482
|
+
catch {
|
|
483
|
+
return { callId: '', success: false, result: null, error: 'config_data must be valid JSON.' };
|
|
484
|
+
}
|
|
485
|
+
const result = await client.upsertControllerConfig(params.config_name, configData);
|
|
486
|
+
return { callId: '', success: true, result };
|
|
487
|
+
}
|
|
488
|
+
if (!params.controller_name) {
|
|
489
|
+
return { callId: '', success: false, result: null, error: 'controller_name required.' };
|
|
490
|
+
}
|
|
491
|
+
const result = await client.upsertController(params.controller_name, {
|
|
492
|
+
controllerType: params.controller_type,
|
|
493
|
+
controllerCode: params.controller_code,
|
|
494
|
+
});
|
|
495
|
+
return { callId: '', success: true, result };
|
|
496
|
+
}
|
|
497
|
+
case 'delete': {
|
|
498
|
+
if (target === 'config' && params.config_name) {
|
|
499
|
+
const result = await client.deleteControllerConfig(params.config_name);
|
|
500
|
+
return { callId: '', success: true, result };
|
|
501
|
+
}
|
|
502
|
+
if (!params.controller_name) {
|
|
503
|
+
return { callId: '', success: false, result: null, error: 'controller_name required.' };
|
|
504
|
+
}
|
|
505
|
+
const result = await client.deleteController(params.controller_name);
|
|
506
|
+
return { callId: '', success: true, result };
|
|
507
|
+
}
|
|
508
|
+
default:
|
|
509
|
+
return { callId: '', success: false, result: null, error: `Unknown controller sub_action: "${subAction}". Use: list, describe, upsert, delete.` };
|
|
283
510
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
511
|
+
}
|
|
512
|
+
// ================================================================
|
|
513
|
+
// Bot management
|
|
514
|
+
// ================================================================
|
|
515
|
+
case 'bot': {
|
|
516
|
+
const subAction = params.sub_action ?? 'status';
|
|
517
|
+
switch (subAction) {
|
|
518
|
+
case 'deploy': {
|
|
519
|
+
const botName = params.bot_name;
|
|
520
|
+
const configsStr = params.controllers_config;
|
|
521
|
+
if (!botName || !configsStr) {
|
|
522
|
+
return { callId: '', success: false, result: null, error: 'bot_name and controllers_config required.' };
|
|
523
|
+
}
|
|
524
|
+
const result = await client.deployBot({
|
|
525
|
+
botName,
|
|
526
|
+
controllersConfig: configsStr.split(',').map(s => s.trim()),
|
|
527
|
+
accountName: params.account_name,
|
|
528
|
+
image: params.image,
|
|
529
|
+
});
|
|
530
|
+
return { callId: '', success: true, result };
|
|
531
|
+
}
|
|
532
|
+
case 'status': {
|
|
533
|
+
const result = await client.getBotsStatus();
|
|
534
|
+
return { callId: '', success: true, result };
|
|
535
|
+
}
|
|
536
|
+
case 'logs': {
|
|
537
|
+
if (!params.bot_name) {
|
|
538
|
+
return { callId: '', success: false, result: null, error: 'bot_name required.' };
|
|
539
|
+
}
|
|
540
|
+
const result = await client.getBotLogs({
|
|
541
|
+
botName: params.bot_name,
|
|
542
|
+
limit: params.limit ?? 50,
|
|
543
|
+
searchTerm: params.search,
|
|
544
|
+
});
|
|
545
|
+
return { callId: '', success: true, result };
|
|
546
|
+
}
|
|
547
|
+
case 'stop': {
|
|
548
|
+
if (!params.bot_name) {
|
|
549
|
+
return { callId: '', success: false, result: null, error: 'bot_name required.' };
|
|
550
|
+
}
|
|
551
|
+
const result = await client.stopBot(params.bot_name);
|
|
552
|
+
return { callId: '', success: true, result };
|
|
553
|
+
}
|
|
554
|
+
case 'stop_controllers':
|
|
555
|
+
case 'start_controllers': {
|
|
556
|
+
if (!params.bot_name || !params.controllers_config) {
|
|
557
|
+
return { callId: '', success: false, result: null, error: 'bot_name and controllers_config required.' };
|
|
558
|
+
}
|
|
559
|
+
const names = params.controllers_config.split(',').map(s => s.trim());
|
|
560
|
+
const result = subAction === 'stop_controllers'
|
|
561
|
+
? await client.stopControllers(params.bot_name, names)
|
|
562
|
+
: await client.startControllers(params.bot_name, names);
|
|
563
|
+
return { callId: '', success: true, result };
|
|
564
|
+
}
|
|
565
|
+
case 'history': {
|
|
566
|
+
const name = params.bot_name;
|
|
567
|
+
if (!name)
|
|
568
|
+
return { callId: '', success: false, result: null, error: 'bot_name required.' };
|
|
569
|
+
const result = await client.getBotHistory(name, params.days ?? 0, params.verbose ?? false);
|
|
570
|
+
return { callId: '', success: true, result };
|
|
571
|
+
}
|
|
572
|
+
case 'runs': {
|
|
573
|
+
const result = await client.getBotRuns({
|
|
574
|
+
botName: params.bot_name,
|
|
575
|
+
accountName: params.account_name,
|
|
576
|
+
runStatus: params.run_status,
|
|
577
|
+
limit: params.limit,
|
|
578
|
+
offset: params.offset,
|
|
579
|
+
});
|
|
580
|
+
return { callId: '', success: true, result };
|
|
581
|
+
}
|
|
582
|
+
case 'archive': {
|
|
583
|
+
const name = params.bot_name;
|
|
584
|
+
if (!name)
|
|
585
|
+
return { callId: '', success: false, result: null, error: 'bot_name required.' };
|
|
586
|
+
const result = await client.archiveBot(name);
|
|
587
|
+
return { callId: '', success: true, result };
|
|
588
|
+
}
|
|
589
|
+
default:
|
|
590
|
+
return {
|
|
591
|
+
callId: '', success: false, result: null,
|
|
592
|
+
error: `Unknown bot sub_action: "${subAction}". Use: deploy, status, logs, stop, stop_controllers, start_controllers, history, runs, archive.`,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
// ================================================================
|
|
597
|
+
// Gateway: container, config, swaps, CLMM
|
|
598
|
+
// ================================================================
|
|
599
|
+
case 'gateway': {
|
|
600
|
+
const subAction = params.sub_action ?? 'container_status';
|
|
601
|
+
// --- Container lifecycle ---
|
|
602
|
+
if (subAction === 'container_status') {
|
|
603
|
+
const result = await client.getGatewayStatus();
|
|
604
|
+
return { callId: '', success: true, result };
|
|
287
605
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
606
|
+
if (subAction === 'container_start') {
|
|
607
|
+
const result = await client.startGateway({
|
|
608
|
+
passphrase: params.passphrase ?? 'clawnch',
|
|
609
|
+
image: params.image ?? 'hummingbot/gateway:latest',
|
|
610
|
+
port: params.port,
|
|
611
|
+
environment: undefined,
|
|
612
|
+
});
|
|
613
|
+
return { callId: '', success: true, result };
|
|
291
614
|
}
|
|
292
|
-
if (
|
|
293
|
-
|
|
615
|
+
if (subAction === 'container_stop') {
|
|
616
|
+
const result = await client.stopGateway();
|
|
617
|
+
return { callId: '', success: true, result };
|
|
294
618
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
return { callId: '', success:
|
|
619
|
+
if (subAction === 'container_logs') {
|
|
620
|
+
const result = await client.getGatewayLogs(params.tail);
|
|
621
|
+
return { callId: '', success: true, result };
|
|
298
622
|
}
|
|
299
|
-
|
|
300
|
-
|
|
623
|
+
// --- Gateway config ---
|
|
624
|
+
if (subAction === 'config_chains') {
|
|
625
|
+
const result = await client.listGatewayChains();
|
|
626
|
+
return { callId: '', success: true, result };
|
|
301
627
|
}
|
|
302
|
-
if (
|
|
303
|
-
|
|
628
|
+
if (subAction === 'config_networks') {
|
|
629
|
+
const result = await client.listGatewayNetworks(params.chain);
|
|
630
|
+
return { callId: '', success: true, result };
|
|
304
631
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
632
|
+
if (subAction === 'config_connectors') {
|
|
633
|
+
const result = await client.listGatewayConnectors();
|
|
634
|
+
return { callId: '', success: true, result };
|
|
635
|
+
}
|
|
636
|
+
if (subAction === 'config_tokens') {
|
|
637
|
+
const result = await client.listGatewayTokens(params.chain, params.network, params.search);
|
|
638
|
+
return { callId: '', success: true, result };
|
|
639
|
+
}
|
|
640
|
+
if (subAction === 'config_wallets') {
|
|
641
|
+
const result = await client.listGatewayWallets(params.chain);
|
|
642
|
+
return { callId: '', success: true, result };
|
|
643
|
+
}
|
|
644
|
+
if (subAction === 'config_add_token') {
|
|
645
|
+
const result = await client.addGatewayToken({
|
|
646
|
+
chain: params.chain,
|
|
647
|
+
network: params.network,
|
|
648
|
+
tokenAddress: params.token_address,
|
|
649
|
+
tokenSymbol: params.token_symbol,
|
|
650
|
+
tokenDecimals: params.token_decimals,
|
|
651
|
+
tokenName: params.token_name,
|
|
652
|
+
});
|
|
653
|
+
return { callId: '', success: true, result };
|
|
654
|
+
}
|
|
655
|
+
if (subAction === 'config_add_wallet') {
|
|
656
|
+
const result = await client.addGatewayWallet(params.chain, params.private_key);
|
|
657
|
+
return { callId: '', success: true, result };
|
|
658
|
+
}
|
|
659
|
+
// --- Swaps ---
|
|
660
|
+
if (subAction === 'swap_quote') {
|
|
661
|
+
const result = await client.getGatewayAmmPrice({
|
|
662
|
+
connector: params.connector_name,
|
|
663
|
+
chain: params.chain,
|
|
664
|
+
network: params.network,
|
|
665
|
+
tradingPair: params.trading_pair,
|
|
666
|
+
side: params.side?.toUpperCase(),
|
|
667
|
+
amount: params.amount,
|
|
668
|
+
});
|
|
669
|
+
return { callId: '', success: true, result };
|
|
670
|
+
}
|
|
671
|
+
if (subAction === 'swap_execute') {
|
|
672
|
+
const result = await client.executeGatewayAmmTrade({
|
|
673
|
+
connector: params.connector_name,
|
|
674
|
+
chain: params.chain,
|
|
675
|
+
network: params.network,
|
|
676
|
+
tradingPair: params.trading_pair,
|
|
677
|
+
side: params.side?.toUpperCase(),
|
|
678
|
+
amount: params.amount,
|
|
679
|
+
slippagePct: params.slippage_pct,
|
|
680
|
+
walletAddress: params.wallet_address ?? ctx.identity.address,
|
|
681
|
+
});
|
|
682
|
+
return { callId: '', success: true, result };
|
|
683
|
+
}
|
|
684
|
+
// --- CLMM ---
|
|
685
|
+
if (subAction === 'clmm_list') {
|
|
686
|
+
const result = await client.listCLMMPools({
|
|
687
|
+
connector: params.connector_name,
|
|
688
|
+
chain: params.chain,
|
|
689
|
+
network: params.network,
|
|
690
|
+
limit: params.limit ?? 50,
|
|
691
|
+
searchTerm: params.search,
|
|
692
|
+
});
|
|
693
|
+
return { callId: '', success: true, result };
|
|
694
|
+
}
|
|
695
|
+
if (subAction === 'clmm_info') {
|
|
696
|
+
const result = await client.getCLMMPoolInfo({
|
|
697
|
+
connector: params.connector_name,
|
|
698
|
+
chain: params.chain,
|
|
699
|
+
network: params.network,
|
|
700
|
+
poolAddress: params.pool_address,
|
|
701
|
+
});
|
|
702
|
+
return { callId: '', success: true, result };
|
|
703
|
+
}
|
|
704
|
+
if (subAction === 'clmm_positions') {
|
|
705
|
+
const result = await client.getCLMMPositions({
|
|
706
|
+
connector: params.connector_name,
|
|
707
|
+
chain: params.chain,
|
|
708
|
+
network: params.network,
|
|
709
|
+
walletAddress: params.wallet_address ?? ctx.identity.address,
|
|
710
|
+
poolAddress: params.pool_address,
|
|
711
|
+
});
|
|
712
|
+
return { callId: '', success: true, result };
|
|
713
|
+
}
|
|
714
|
+
if (subAction === 'clmm_open') {
|
|
715
|
+
const result = await client.addCLMMLiquidity({
|
|
716
|
+
connector: params.connector_name,
|
|
717
|
+
chain: params.chain,
|
|
718
|
+
network: params.network,
|
|
719
|
+
poolAddress: params.pool_address,
|
|
720
|
+
walletAddress: params.wallet_address ?? ctx.identity.address,
|
|
721
|
+
lowerPrice: params.lower_price,
|
|
722
|
+
upperPrice: params.upper_price,
|
|
723
|
+
baseTokenAmount: params.base_token_amount,
|
|
724
|
+
quoteTokenAmount: params.quote_token_amount,
|
|
725
|
+
slippagePct: params.slippage_pct,
|
|
726
|
+
});
|
|
727
|
+
return { callId: '', success: true, result };
|
|
728
|
+
}
|
|
729
|
+
if (subAction === 'clmm_close') {
|
|
730
|
+
const result = await client.removeCLMMLiquidity({
|
|
731
|
+
connector: params.connector_name,
|
|
732
|
+
chain: params.chain,
|
|
733
|
+
network: params.network,
|
|
734
|
+
positionAddress: params.position_address,
|
|
735
|
+
walletAddress: params.wallet_address ?? ctx.identity.address,
|
|
736
|
+
slippagePct: params.slippage_pct,
|
|
737
|
+
});
|
|
738
|
+
return { callId: '', success: true, result };
|
|
739
|
+
}
|
|
740
|
+
return {
|
|
741
|
+
callId: '', success: false, result: null,
|
|
742
|
+
error: `Unknown gateway sub_action: "${subAction}". Prefixes: container_, config_, swap_, clmm_.`,
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
// ================================================================
|
|
746
|
+
// History
|
|
747
|
+
// ================================================================
|
|
748
|
+
case 'history': {
|
|
749
|
+
const dataType = params.sub_action ?? 'orders';
|
|
750
|
+
const validTypes = ['orders', 'perp_positions', 'clmm_positions'];
|
|
751
|
+
if (!validTypes.includes(dataType)) {
|
|
332
752
|
return {
|
|
333
|
-
callId: '',
|
|
334
|
-
|
|
335
|
-
result: [
|
|
336
|
-
`Hummingbot instance started.`,
|
|
337
|
-
` Instance: ${instanceName}`,
|
|
338
|
-
` Container: ${containerId.substring(0, 12)}`,
|
|
339
|
-
` Strategy: ${strategy}`,
|
|
340
|
-
` Token: ${tokenAddr}`,
|
|
341
|
-
` Config: ${configDir}/${strategy}.yml`,
|
|
342
|
-
'',
|
|
343
|
-
`Monitor: use action "logs" with instance_name "${instanceName}"`,
|
|
344
|
-
`Stop: use action "stop" with instance_name "${instanceName}"`,
|
|
345
|
-
].join('\n'),
|
|
346
|
-
metadata: { instance_name: instanceName, container_id: containerId.substring(0, 12) },
|
|
753
|
+
callId: '', success: false, result: null,
|
|
754
|
+
error: `Invalid history sub_action: "${dataType}". Use: orders, perp_positions, clmm_positions.`,
|
|
347
755
|
};
|
|
348
756
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
757
|
+
const result = await client.searchHistory({
|
|
758
|
+
dataType: dataType,
|
|
759
|
+
accountNames: splitCsv(params.account_names),
|
|
760
|
+
connectorNames: splitCsv(params.connector_names) ?? (params.connector_name ? [params.connector_name] : undefined),
|
|
761
|
+
tradingPairs: splitCsv(params.trading_pairs) ?? (params.trading_pair ? [params.trading_pair] : undefined),
|
|
762
|
+
status: params.status,
|
|
763
|
+
startTime: params.start_time,
|
|
764
|
+
endTime: params.end_time,
|
|
765
|
+
limit: params.limit ?? 50,
|
|
766
|
+
offset: params.offset ?? 0,
|
|
767
|
+
network: params.network,
|
|
768
|
+
walletAddress: params.wallet_address,
|
|
769
|
+
});
|
|
770
|
+
return { callId: '', success: true, result };
|
|
352
771
|
}
|
|
353
772
|
// ================================================================
|
|
354
|
-
//
|
|
773
|
+
// Templates
|
|
355
774
|
// ================================================================
|
|
356
|
-
case '
|
|
357
|
-
const
|
|
358
|
-
if (
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
775
|
+
case 'templates': {
|
|
776
|
+
const subAction = params.sub_action ?? 'list';
|
|
777
|
+
if (subAction === 'list') {
|
|
778
|
+
const templates = client.getStrategyTemplates();
|
|
779
|
+
const clawnchTemplates = templates.filter((t) => t.template.startsWith('clawnch_'));
|
|
780
|
+
const genericTemplates = templates.filter((t) => !t.template.startsWith('clawnch_'));
|
|
781
|
+
const lines = [
|
|
782
|
+
'Clawnch Token Strategy Templates:',
|
|
783
|
+
...clawnchTemplates.map((t) => ` ${t.template}: ${t.description} (executor: ${t.executorType})`),
|
|
784
|
+
'',
|
|
785
|
+
'Generic Strategy Templates:',
|
|
786
|
+
...genericTemplates.map((t) => ` ${t.template}: ${t.description} (executor: ${t.executorType})`),
|
|
787
|
+
'', 'Use sub_action="describe" with template to see details, or sub_action="build" to create.',
|
|
788
|
+
];
|
|
789
|
+
return { callId: '', success: true, result: lines.join('\n') };
|
|
790
|
+
}
|
|
791
|
+
if (subAction === 'describe') {
|
|
792
|
+
if (!params.template) {
|
|
793
|
+
return { callId: '', success: false, result: null, error: 'template parameter required.' };
|
|
794
|
+
}
|
|
795
|
+
const tmpl = client.getStrategyTemplate(params.template);
|
|
796
|
+
if (!tmpl) {
|
|
797
|
+
return { callId: '', success: false, result: null, error: `Unknown template: ${params.template}` };
|
|
362
798
|
}
|
|
363
799
|
return {
|
|
364
|
-
callId: '',
|
|
365
|
-
|
|
366
|
-
result: null,
|
|
367
|
-
error: `instance_name is required. Running instances:\n${instances.map(i => ` ${i.name}: ${i.status}`).join('\n')}`,
|
|
800
|
+
callId: '', success: true,
|
|
801
|
+
result: `Template: ${tmpl.template}\nDescription: ${tmpl.description}\nExecutor Type: ${tmpl.executorType}\nDefaults: ${JSON.stringify(tmpl.defaultConfig, null, 2)}\nRequired: ${tmpl.requiredOverrides.join(', ')}`,
|
|
368
802
|
};
|
|
369
803
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
804
|
+
if (subAction === 'build') {
|
|
805
|
+
if (!params.template)
|
|
806
|
+
return { callId: '', success: false, result: null, error: 'template required.' };
|
|
807
|
+
if (!params.executor_config)
|
|
808
|
+
return { callId: '', success: false, result: null, error: 'executor_config (JSON) required.' };
|
|
809
|
+
let overrides;
|
|
810
|
+
try {
|
|
811
|
+
overrides = JSON.parse(params.executor_config);
|
|
812
|
+
}
|
|
813
|
+
catch {
|
|
814
|
+
return { callId: '', success: false, result: null, error: 'executor_config must be valid JSON.' };
|
|
815
|
+
}
|
|
816
|
+
const config = client.buildFromTemplate(params.template, overrides);
|
|
817
|
+
const result = await client.createExecutor(config);
|
|
818
|
+
return { callId: '', success: true, result };
|
|
373
819
|
}
|
|
374
|
-
|
|
375
|
-
|
|
820
|
+
return { callId: '', success: false, result: null, error: `Unknown templates sub_action: "${subAction}". Use: list, describe, build.` };
|
|
821
|
+
}
|
|
822
|
+
// ================================================================
|
|
823
|
+
// Connector setup
|
|
824
|
+
// ================================================================
|
|
825
|
+
case 'connector': {
|
|
826
|
+
const subAction = params.sub_action ?? 'list';
|
|
827
|
+
if (subAction === 'list') {
|
|
828
|
+
const result = await client.listConnectors();
|
|
829
|
+
return { callId: '', success: true, result };
|
|
376
830
|
}
|
|
831
|
+
if (subAction === 'config_map') {
|
|
832
|
+
if (!params.connector_name) {
|
|
833
|
+
return { callId: '', success: false, result: null, error: 'connector_name required.' };
|
|
834
|
+
}
|
|
835
|
+
const result = await client.getConnectorConfigMap(params.connector_name);
|
|
836
|
+
return { callId: '', success: true, result };
|
|
837
|
+
}
|
|
838
|
+
if (subAction === 'setup') {
|
|
839
|
+
if (!params.connector_name || !params.credentials) {
|
|
840
|
+
return { callId: '', success: false, result: null, error: 'connector_name and credentials (JSON) required.' };
|
|
841
|
+
}
|
|
842
|
+
let creds;
|
|
843
|
+
try {
|
|
844
|
+
creds = JSON.parse(params.credentials);
|
|
845
|
+
}
|
|
846
|
+
catch {
|
|
847
|
+
return { callId: '', success: false, result: null, error: 'credentials must be valid JSON.' };
|
|
848
|
+
}
|
|
849
|
+
const accountName = params.account_name ?? 'master_account';
|
|
850
|
+
const result = await client.addConnector(accountName, params.connector_name, creds);
|
|
851
|
+
return { callId: '', success: true, result };
|
|
852
|
+
}
|
|
853
|
+
if (subAction === 'trading_rules') {
|
|
854
|
+
if (!params.connector_name) {
|
|
855
|
+
return { callId: '', success: false, result: null, error: 'connector_name required.' };
|
|
856
|
+
}
|
|
857
|
+
const tradingPairs = params.trading_pair ? [params.trading_pair] : undefined;
|
|
858
|
+
const result = await client.getTradingRules(params.connector_name, tradingPairs);
|
|
859
|
+
return { callId: '', success: true, result };
|
|
860
|
+
}
|
|
861
|
+
if (subAction === 'order_types') {
|
|
862
|
+
if (!params.connector_name) {
|
|
863
|
+
return { callId: '', success: false, result: null, error: 'connector_name required.' };
|
|
864
|
+
}
|
|
865
|
+
const result = await client.getOrderTypes(params.connector_name);
|
|
866
|
+
return { callId: '', success: true, result };
|
|
867
|
+
}
|
|
868
|
+
return { callId: '', success: false, result: null, error: `Unknown connector sub_action: "${subAction}". Use: list, config_map, setup, trading_rules, order_types.` };
|
|
869
|
+
}
|
|
870
|
+
// ================================================================
|
|
871
|
+
// Backtesting
|
|
872
|
+
// ================================================================
|
|
873
|
+
case 'backtest': {
|
|
874
|
+
let config = params.config;
|
|
377
875
|
try {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
876
|
+
config = JSON.parse(config);
|
|
877
|
+
}
|
|
878
|
+
catch { /* keep as string (config name) */ }
|
|
879
|
+
const result = await client.runBacktest({
|
|
880
|
+
config,
|
|
881
|
+
startTime: params.start_time,
|
|
882
|
+
endTime: params.end_time,
|
|
883
|
+
resolution: params.resolution,
|
|
884
|
+
tradeCost: params.trade_cost,
|
|
885
|
+
});
|
|
886
|
+
return { callId: '', success: true, result };
|
|
887
|
+
}
|
|
888
|
+
// ================================================================
|
|
889
|
+
// Connector / Market Discovery
|
|
890
|
+
// ================================================================
|
|
891
|
+
case 'discovery': {
|
|
892
|
+
const subAction = params.sub_action;
|
|
893
|
+
if (subAction === 'connectors' || !subAction) {
|
|
894
|
+
const result = await client.listConnectors();
|
|
895
|
+
return { callId: '', success: true, result };
|
|
896
|
+
}
|
|
897
|
+
if (subAction === 'config_map') {
|
|
898
|
+
const result = await client.getConnectorConfigMap(params.connector_name);
|
|
899
|
+
return { callId: '', success: true, result };
|
|
381
900
|
}
|
|
382
|
-
|
|
383
|
-
|
|
901
|
+
if (subAction === 'trading_rules') {
|
|
902
|
+
const result = await client.getTradingRules(params.connector_name, splitCsv(params.trading_pairs));
|
|
903
|
+
return { callId: '', success: true, result };
|
|
384
904
|
}
|
|
905
|
+
if (subAction === 'order_types') {
|
|
906
|
+
const result = await client.getOrderTypes(params.connector_name);
|
|
907
|
+
return { callId: '', success: true, result };
|
|
908
|
+
}
|
|
909
|
+
return { callId: '', success: false, result: null, error: `Unknown discovery sub_action: "${subAction}". Use: connectors, config_map, trading_rules, order_types.` };
|
|
385
910
|
}
|
|
386
911
|
// ================================================================
|
|
387
|
-
//
|
|
912
|
+
// Archived Bot Analytics
|
|
388
913
|
// ================================================================
|
|
389
|
-
case '
|
|
390
|
-
const
|
|
391
|
-
if (
|
|
392
|
-
|
|
914
|
+
case 'archived': {
|
|
915
|
+
const subAction = params.sub_action;
|
|
916
|
+
if (subAction === 'list' || !subAction) {
|
|
917
|
+
const result = await client.listArchivedBots();
|
|
918
|
+
return { callId: '', success: true, result };
|
|
393
919
|
}
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
callId: '',
|
|
397
|
-
|
|
398
|
-
result
|
|
399
|
-
|
|
920
|
+
const dbPath = params.db_path;
|
|
921
|
+
if (!dbPath)
|
|
922
|
+
return { callId: '', success: false, result: null, error: 'db_path required for archived bot queries.' };
|
|
923
|
+
if (subAction === 'summary') {
|
|
924
|
+
const result = await client.getArchivedBotSummary(dbPath);
|
|
925
|
+
return { callId: '', success: true, result };
|
|
926
|
+
}
|
|
927
|
+
if (subAction === 'performance') {
|
|
928
|
+
const result = await client.getArchivedBotPerformance(dbPath);
|
|
929
|
+
return { callId: '', success: true, result };
|
|
930
|
+
}
|
|
931
|
+
if (subAction === 'trades') {
|
|
932
|
+
const result = await client.getArchivedBotTrades(dbPath, params.limit ?? 100, params.offset ?? 0);
|
|
933
|
+
return { callId: '', success: true, result };
|
|
934
|
+
}
|
|
935
|
+
if (subAction === 'orders') {
|
|
936
|
+
const result = await client.getArchivedBotOrders(dbPath, params.limit ?? 100, params.offset ?? 0, params.status);
|
|
937
|
+
return { callId: '', success: true, result };
|
|
938
|
+
}
|
|
939
|
+
return { callId: '', success: false, result: null, error: `Unknown archived sub_action: "${subAction}". Use: list, summary, performance, trades, orders.` };
|
|
400
940
|
}
|
|
401
941
|
// ================================================================
|
|
402
|
-
//
|
|
942
|
+
// Script Management
|
|
403
943
|
// ================================================================
|
|
404
|
-
case '
|
|
405
|
-
const
|
|
406
|
-
if (!
|
|
407
|
-
|
|
944
|
+
case 'scripts': {
|
|
945
|
+
const subAction = params.sub_action;
|
|
946
|
+
if (subAction === 'list' || !subAction) {
|
|
947
|
+
const result = await client.listScripts();
|
|
948
|
+
return { callId: '', success: true, result };
|
|
408
949
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
950
|
+
if (subAction === 'get') {
|
|
951
|
+
const result = await client.getScript(params.script_name);
|
|
952
|
+
return { callId: '', success: true, result };
|
|
412
953
|
}
|
|
413
|
-
|
|
414
|
-
|
|
954
|
+
if (subAction === 'upsert') {
|
|
955
|
+
const result = await client.upsertScript(params.script_name, params.script_content);
|
|
956
|
+
return { callId: '', success: true, result };
|
|
415
957
|
}
|
|
416
|
-
|
|
417
|
-
const
|
|
418
|
-
return { callId: '', success: true, result
|
|
958
|
+
if (subAction === 'delete') {
|
|
959
|
+
const result = await client.deleteScript(params.script_name);
|
|
960
|
+
return { callId: '', success: true, result };
|
|
961
|
+
}
|
|
962
|
+
if (subAction === 'configs') {
|
|
963
|
+
const result = await client.listScriptConfigs();
|
|
964
|
+
return { callId: '', success: true, result };
|
|
965
|
+
}
|
|
966
|
+
if (subAction === 'get_config') {
|
|
967
|
+
const result = await client.getScriptConfig(params.config_name);
|
|
968
|
+
return { callId: '', success: true, result };
|
|
969
|
+
}
|
|
970
|
+
if (subAction === 'upsert_config') {
|
|
971
|
+
let data;
|
|
972
|
+
try {
|
|
973
|
+
data = JSON.parse(params.config_data);
|
|
974
|
+
}
|
|
975
|
+
catch {
|
|
976
|
+
return { callId: '', success: false, result: null, error: 'config_data must be valid JSON.' };
|
|
977
|
+
}
|
|
978
|
+
const result = await client.upsertScriptConfig(params.config_name, data);
|
|
979
|
+
return { callId: '', success: true, result };
|
|
980
|
+
}
|
|
981
|
+
if (subAction === 'delete_config') {
|
|
982
|
+
const result = await client.deleteScriptConfig(params.config_name);
|
|
983
|
+
return { callId: '', success: true, result };
|
|
419
984
|
}
|
|
420
|
-
|
|
421
|
-
|
|
985
|
+
if (subAction === 'template') {
|
|
986
|
+
const result = await client.getScriptConfigTemplate(params.script_name);
|
|
987
|
+
return { callId: '', success: true, result };
|
|
422
988
|
}
|
|
989
|
+
return { callId: '', success: false, result: null, error: `Unknown scripts sub_action: "${subAction}". Use: list, get, upsert, delete, configs, get_config, upsert_config, delete_config, template.` };
|
|
423
990
|
}
|
|
424
991
|
// ================================================================
|
|
425
|
-
//
|
|
992
|
+
// Portfolio Analytics & Rate Oracle
|
|
426
993
|
// ================================================================
|
|
427
|
-
case '
|
|
428
|
-
const
|
|
429
|
-
if (
|
|
430
|
-
|
|
994
|
+
case 'analytics': {
|
|
995
|
+
const subAction = params.sub_action;
|
|
996
|
+
if (subAction === 'history') {
|
|
997
|
+
const result = await client.getPortfolioHistory({
|
|
998
|
+
accountNames: splitCsv(params.account_names),
|
|
999
|
+
connectorNames: splitCsv(params.connector_names),
|
|
1000
|
+
startTime: params.start_time,
|
|
1001
|
+
endTime: params.end_time,
|
|
1002
|
+
interval: params.interval,
|
|
1003
|
+
limit: params.limit,
|
|
1004
|
+
});
|
|
1005
|
+
return { callId: '', success: true, result };
|
|
431
1006
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
1007
|
+
if (subAction === 'distribution') {
|
|
1008
|
+
const result = await client.getPortfolioDistribution({
|
|
1009
|
+
accountNames: splitCsv(params.account_names),
|
|
1010
|
+
connectorNames: splitCsv(params.connector_names),
|
|
1011
|
+
});
|
|
1012
|
+
return { callId: '', success: true, result };
|
|
435
1013
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
callId: '',
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
1014
|
+
if (subAction === 'accounts_distribution') {
|
|
1015
|
+
const result = await client.getAccountsDistribution();
|
|
1016
|
+
return { callId: '', success: true, result };
|
|
1017
|
+
}
|
|
1018
|
+
if (subAction === 'funding_payments') {
|
|
1019
|
+
const result = await client.getFundingPayments({
|
|
1020
|
+
accountNames: splitCsv(params.account_names),
|
|
1021
|
+
connectorNames: splitCsv(params.connector_names),
|
|
1022
|
+
tradingPair: params.trading_pair,
|
|
1023
|
+
limit: params.limit,
|
|
1024
|
+
});
|
|
1025
|
+
return { callId: '', success: true, result };
|
|
1026
|
+
}
|
|
1027
|
+
if (subAction === 'rates') {
|
|
1028
|
+
const pairs = splitCsv(params.trading_pairs);
|
|
1029
|
+
if (!pairs?.length)
|
|
1030
|
+
return { callId: '', success: false, result: null, error: 'trading_pairs required.' };
|
|
1031
|
+
const result = await client.getRates(pairs);
|
|
1032
|
+
return { callId: '', success: true, result };
|
|
1033
|
+
}
|
|
1034
|
+
if (subAction === 'rate') {
|
|
1035
|
+
const result = await client.getRate(params.trading_pair);
|
|
1036
|
+
return { callId: '', success: true, result };
|
|
1037
|
+
}
|
|
1038
|
+
return { callId: '', success: false, result: null, error: `Unknown analytics sub_action: "${subAction}". Use: history, distribution, accounts_distribution, funding_payments, rates, rate.` };
|
|
1039
|
+
}
|
|
1040
|
+
// ================================================================
|
|
1041
|
+
// Account Management
|
|
1042
|
+
// ================================================================
|
|
1043
|
+
case 'accounts': {
|
|
1044
|
+
const subAction = params.sub_action;
|
|
1045
|
+
if (subAction === 'list' || !subAction) {
|
|
1046
|
+
const result = await client.listAccounts();
|
|
1047
|
+
return { callId: '', success: true, result };
|
|
1048
|
+
}
|
|
1049
|
+
if (subAction === 'credentials') {
|
|
1050
|
+
const result = await client.getAccountCredentials(params.account_name);
|
|
1051
|
+
return { callId: '', success: true, result };
|
|
1052
|
+
}
|
|
1053
|
+
if (subAction === 'create') {
|
|
1054
|
+
const result = await client.createAccount(params.account_name);
|
|
1055
|
+
return { callId: '', success: true, result };
|
|
1056
|
+
}
|
|
1057
|
+
if (subAction === 'delete') {
|
|
1058
|
+
const result = await client.deleteAccount(params.account_name);
|
|
1059
|
+
return { callId: '', success: true, result };
|
|
1060
|
+
}
|
|
1061
|
+
return { callId: '', success: false, result: null, error: `Unknown accounts sub_action: "${subAction}". Use: list, credentials, create, delete.` };
|
|
442
1062
|
}
|
|
1063
|
+
// ================================================================
|
|
1064
|
+
// Unknown
|
|
1065
|
+
// ================================================================
|
|
443
1066
|
default:
|
|
444
1067
|
return {
|
|
445
|
-
callId: '',
|
|
446
|
-
|
|
447
|
-
result: null,
|
|
448
|
-
error: `Unknown hummingbot action: "${action}". Valid: status, start, stop, list, logs, configure.`,
|
|
1068
|
+
callId: '', success: false, result: null,
|
|
1069
|
+
error: `Unknown hummingbot action: "${action}". Valid: status, portfolio, order, leverage, executor, market_data, controller, bot, gateway, history, templates, connector, servers, backtest, discovery, archived, scripts, analytics, accounts.`,
|
|
449
1070
|
};
|
|
450
1071
|
}
|
|
451
1072
|
}
|
|
452
1073
|
catch (err) {
|
|
453
|
-
|
|
1074
|
+
const message = err?.code ? `[${err.code}] ${err.message}` : err?.message ?? String(err);
|
|
1075
|
+
return { callId: '', success: false, result: null, error: message };
|
|
454
1076
|
}
|
|
455
1077
|
},
|
|
456
1078
|
};
|