dexbot 1.4.25 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +6 -2
- package/analysis/README.md +14 -7
- package/analysis/ama_fitting/package.json +1 -1
- package/analysis/analyze_derivatives.ts +1 -1
- package/analysis/bot_key_utils.ts +272 -3
- package/analysis/bot_usage/discover_bot_accounts.ts +64 -9
- package/analysis/bot_usage/kibana_bot_queries.ts +40 -1
- package/analysis/chart_utils.ts +6 -3
- package/analysis/grid_correction_check.ts +56 -20
- package/analysis/resolve_bot_accounts.ts +199 -0
- package/analysis/trade_profitability.ts +36 -8
- package/analysis/tradingview/README.md +25 -0
- package/analysis/tradingview/analyze_tradingview.ts +2 -2
- package/analysis/trend_detection/package.json +1 -1
- package/claw/modules/claw_launcher.ts +5 -4
- package/claw/modules/credit_runtime_adapter.ts +2 -1
- package/claw/modules/dexbot_profiles.ts +4 -3
- package/claw/package.json +1 -1
- package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
- package/claw/runtimes/openclaw-plugin/package.json +1 -1
- package/claw/tests/test_claw_mcp_transport.ts +2 -2
- package/dist/analysis/analyze_derivatives.js +1 -1
- package/dist/analysis/bot_key_utils.d.ts +28 -1
- package/dist/analysis/bot_key_utils.d.ts.map +1 -1
- package/dist/analysis/bot_key_utils.js +385 -3
- package/dist/analysis/bot_key_utils.js.map +1 -1
- package/dist/analysis/bot_usage/discover_bot_accounts.js +60 -10
- package/dist/analysis/bot_usage/discover_bot_accounts.js.map +1 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.d.ts +45 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.d.ts.map +1 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.js +39 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.js.map +1 -1
- package/dist/analysis/chart_utils.d.ts +2 -1
- package/dist/analysis/chart_utils.d.ts.map +1 -1
- package/dist/analysis/chart_utils.js +4 -1
- package/dist/analysis/chart_utils.js.map +1 -1
- package/dist/analysis/grid_correction_check.d.ts.map +1 -1
- package/dist/analysis/grid_correction_check.js +60 -20
- package/dist/analysis/grid_correction_check.js.map +1 -1
- package/dist/analysis/resolve_bot_accounts.d.ts +3 -0
- package/dist/analysis/resolve_bot_accounts.d.ts.map +1 -0
- package/dist/analysis/resolve_bot_accounts.js +216 -0
- package/dist/analysis/resolve_bot_accounts.js.map +1 -0
- package/dist/analysis/trade_profitability.d.ts.map +1 -1
- package/dist/analysis/trade_profitability.js +40 -8
- package/dist/analysis/trade_profitability.js.map +1 -1
- package/dist/analysis/tradingview/analyze_tradingview.js +2 -2
- package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -1
- package/dist/bot.d.ts.map +1 -1
- package/dist/bot.js +2 -1
- package/dist/bot.js.map +1 -1
- package/dist/dexbot.d.ts.map +1 -1
- package/dist/dexbot.js +73 -17
- package/dist/dexbot.js.map +1 -1
- package/dist/market_adapter/ama_signal_runner.js +2 -2
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js +2 -1
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js.map +1 -1
- package/dist/market_adapter/inputs/fetch_lp_data.d.ts +4 -1
- package/dist/market_adapter/inputs/fetch_lp_data.d.ts.map +1 -1
- package/dist/market_adapter/inputs/fetch_lp_data.js +3 -2
- package/dist/market_adapter/inputs/fetch_lp_data.js.map +1 -1
- package/dist/modules/account_bots.d.ts +42 -1
- package/dist/modules/account_bots.d.ts.map +1 -1
- package/dist/modules/account_bots.js +168 -17
- package/dist/modules/account_bots.js.map +1 -1
- package/dist/modules/bitshares_client.d.ts +4 -1
- package/dist/modules/bitshares_client.d.ts.map +1 -1
- package/dist/modules/bitshares_client.js +12 -4
- package/dist/modules/bitshares_client.js.map +1 -1
- package/dist/modules/bot_settings.d.ts.map +1 -1
- package/dist/modules/bot_settings.js +2 -1
- package/dist/modules/bot_settings.js.map +1 -1
- package/dist/modules/cli_colors.d.ts +39 -0
- package/dist/modules/cli_colors.d.ts.map +1 -0
- package/dist/modules/cli_colors.js +42 -0
- package/dist/modules/cli_colors.js.map +1 -0
- package/dist/modules/constants.d.ts.map +1 -1
- package/dist/modules/constants.js +15 -11
- package/dist/modules/constants.js.map +1 -1
- package/dist/modules/credit_pricing.d.ts +61 -0
- package/dist/modules/credit_pricing.d.ts.map +1 -0
- package/dist/modules/credit_pricing.js +237 -0
- package/dist/modules/credit_pricing.js.map +1 -0
- package/dist/modules/credit_runtime.d.ts +1 -0
- package/dist/modules/credit_runtime.d.ts.map +1 -1
- package/dist/modules/credit_runtime.js +49 -107
- package/dist/modules/credit_runtime.js.map +1 -1
- package/dist/modules/dexbot_class.d.ts +2 -1
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_class.js +22 -6
- package/dist/modules/dexbot_class.js.map +1 -1
- package/dist/modules/dexbot_cow_runtime.d.ts +19 -2
- package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.js +249 -26
- package/dist/modules/dexbot_cow_runtime.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts +15 -16
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +69 -42
- package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
- package/dist/modules/fund_registry.d.ts +1 -1
- package/dist/modules/fund_registry.js +1 -1
- package/dist/modules/launcher/adapter_requirement.d.ts +38 -0
- package/dist/modules/launcher/adapter_requirement.d.ts.map +1 -0
- package/dist/modules/launcher/adapter_requirement.js +141 -0
- package/dist/modules/launcher/adapter_requirement.js.map +1 -0
- package/dist/modules/launcher/bot_supervisor.d.ts +2 -2
- package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -1
- package/dist/modules/launcher/bot_supervisor.js +32 -3
- package/dist/modules/launcher/bot_supervisor.js.map +1 -1
- package/dist/modules/launcher/launch_modes.d.ts.map +1 -1
- package/dist/modules/launcher/launch_modes.js +5 -4
- package/dist/modules/launcher/launch_modes.js.map +1 -1
- package/dist/modules/launcher/monolithic_runtime.d.ts +1 -1
- package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -1
- package/dist/modules/launcher/monolithic_runtime.js +12 -8
- package/dist/modules/launcher/monolithic_runtime.js.map +1 -1
- package/dist/modules/launcher/status_reporting.d.ts +6 -6
- package/dist/modules/launcher/status_reporting.d.ts.map +1 -1
- package/dist/modules/launcher/status_reporting.js +9 -6
- package/dist/modules/launcher/status_reporting.js.map +1 -1
- package/dist/modules/order/export.d.ts +1 -1
- package/dist/modules/order/export.js +1 -1
- package/dist/modules/order/grid.d.ts.map +1 -1
- package/dist/modules/order/grid.js +47 -12
- package/dist/modules/order/grid.js.map +1 -1
- package/dist/modules/order/logger.d.ts +23 -1
- package/dist/modules/order/logger.d.ts.map +1 -1
- package/dist/modules/order/logger.js +60 -17
- package/dist/modules/order/logger.js.map +1 -1
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +43 -3
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/sync_engine.js +1 -1
- package/dist/modules/order/utils/system.d.ts +52 -4
- package/dist/modules/order/utils/system.d.ts.map +1 -1
- package/dist/modules/order/utils/system.js +106 -14
- package/dist/modules/order/utils/system.js.map +1 -1
- package/dist/modules/order/utils/validate.d.ts +1 -1
- package/dist/modules/order/utils/validate.d.ts.map +1 -1
- package/dist/modules/order/utils/validate.js +43 -3
- package/dist/modules/order/utils/validate.js.map +1 -1
- package/dist/modules/utils/chain_logs.d.ts +11 -0
- package/dist/modules/utils/chain_logs.d.ts.map +1 -0
- package/dist/modules/utils/chain_logs.js +23 -0
- package/dist/modules/utils/chain_logs.js.map +1 -0
- package/dist/modules/utils/sanitize_key.d.ts +7 -0
- package/dist/modules/utils/sanitize_key.d.ts.map +1 -1
- package/dist/modules/utils/sanitize_key.js +15 -0
- package/dist/modules/utils/sanitize_key.js.map +1 -1
- package/dist/pm2.d.ts +11 -1
- package/dist/pm2.d.ts.map +1 -1
- package/dist/pm2.js +66 -15
- package/dist/pm2.js.map +1 -1
- package/dist/scripts/analyze-credit.d.ts +4 -0
- package/dist/scripts/analyze-credit.d.ts.map +1 -0
- package/dist/scripts/analyze-credit.js +760 -0
- package/dist/scripts/analyze-credit.js.map +1 -0
- package/dist/scripts/analyze-orders.d.ts +1 -10
- package/dist/scripts/analyze-orders.d.ts.map +1 -1
- package/dist/scripts/analyze-orders.js +11 -34
- package/dist/scripts/analyze-orders.js.map +1 -1
- package/dist/scripts/test-credit-renewal.d.ts.map +1 -1
- package/dist/scripts/test-credit-renewal.js +37 -5
- package/dist/scripts/test-credit-renewal.js.map +1 -1
- package/dist/scripts/tv.d.ts +11 -0
- package/dist/scripts/tv.d.ts.map +1 -0
- package/dist/scripts/tv.js +357 -0
- package/dist/scripts/tv.js.map +1 -0
- package/dist/scripts/update.js +77 -10
- package/dist/scripts/update.js.map +1 -1
- package/dist/unlock.d.ts.map +1 -1
- package/dist/unlock.js +20 -8
- package/dist/unlock.js.map +1 -1
- package/docs/DEXBOT_COMPARISON.md +3 -3
- package/docs/EVOLUTION.md +7 -6
- package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
- package/docs/MPA_CREDIT_USAGE.md +3 -1
- package/docs/README.md +2 -2
- package/docs/WORKFLOW.md +3 -1
- package/package.json +2 -1
- package/scripts/README.md +12 -0
|
@@ -32,6 +32,10 @@
|
|
|
32
32
|
* limit_order_cancel:
|
|
33
33
|
* op_object.fee_paying_account — account that cancelled
|
|
34
34
|
* op_object.order — the order ID cancelled
|
|
35
|
+
*
|
|
36
|
+
* limit_order_update (op 77, DEXBot2-only — native in-place re-price):
|
|
37
|
+
* op_object.seller — account that updated the order
|
|
38
|
+
* op_object.order — the limit order ID updated
|
|
35
39
|
*/
|
|
36
40
|
|
|
37
41
|
|
|
@@ -42,6 +46,11 @@ import { kibanaSearch, DEFAULT_CONFIG as BASE_CONFIG } from '../../market_adapte
|
|
|
42
46
|
const OP_LIMIT_ORDER_CREATE = 1;
|
|
43
47
|
const OP_LIMIT_ORDER_CANCEL = 2;
|
|
44
48
|
const OP_FILL_ORDER = 4;
|
|
49
|
+
// Native in-place order re-price. Only DEXBot2 broadcasts this (COW UPDATE
|
|
50
|
+
// actions via buildUpdateOrderOp); DEXBot1 (Python staggered_orders) predates
|
|
51
|
+
// it and can only cancel + recreate. Presence of op 77 on an account is the
|
|
52
|
+
// DEXBot2 fingerprint; absence means cancel-only (DEXBot1-style).
|
|
53
|
+
const OP_LIMIT_ORDER_UPDATE = 77;
|
|
45
54
|
|
|
46
55
|
const DEFAULT_CONFIG = {
|
|
47
56
|
...BASE_CONFIG,
|
|
@@ -166,7 +175,37 @@ function buildTopFilledAccountsQuery(lookbackHours: number, topN: number = 100,
|
|
|
166
175
|
};
|
|
167
176
|
}
|
|
168
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Top N accounts by limit_order_update count (seller field, op 77).
|
|
180
|
+
* DEXBot2 fingerprint: only DEXBot2 broadcasts native in-place re-prices
|
|
181
|
+
* (COW UPDATE actions). DEXBot1-style bots never emit op 77 — they cancel
|
|
182
|
+
* (op 2) and recreate (op 1) instead.
|
|
183
|
+
*/
|
|
184
|
+
function buildTopUpdaterAccountsQuery(lookbackHours: number, topN: number = 200, minUpdates: number = 1) {
|
|
185
|
+
return {
|
|
186
|
+
size: 0,
|
|
187
|
+
query: {
|
|
188
|
+
bool: {
|
|
189
|
+
filter: [
|
|
190
|
+
{ term: { operation_type: OP_LIMIT_ORDER_UPDATE } },
|
|
191
|
+
{ range: { 'block_data.block_time': { gte: `now-${lookbackHours}h`, lte: 'now' } } },
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
aggs: {
|
|
196
|
+
by_account: {
|
|
197
|
+
terms: {
|
|
198
|
+
field: 'operation_history.op_object.seller.keyword',
|
|
199
|
+
size: topN,
|
|
200
|
+
min_doc_count: minUpdates,
|
|
201
|
+
order: { _count: 'desc' },
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
169
208
|
// ─── Exports ──────────────────────────────────────────────────────────────────
|
|
170
209
|
|
|
171
|
-
export { DEFAULT_CONFIG, kibanaSearch, buildOrderPriceQuery, buildTopSellerAccountsQuery, buildTopCancellerAccountsQuery, buildTopFilledAccountsQuery }
|
|
210
|
+
export { DEFAULT_CONFIG, kibanaSearch, buildOrderPriceQuery, buildTopSellerAccountsQuery, buildTopCancellerAccountsQuery, buildTopFilledAccountsQuery, buildTopUpdaterAccountsQuery, OP_LIMIT_ORDER_UPDATE }
|
|
172
211
|
|
package/analysis/chart_utils.ts
CHANGED
|
@@ -54,8 +54,11 @@ function toEpochSeconds(ts: any, fallbackIdx: any) {
|
|
|
54
54
|
return fallbackIdx * 3600;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function
|
|
58
|
-
|
|
57
|
+
function toFileUrl(filePath: any): string {
|
|
58
|
+
return `file://${path.resolve(String(filePath))}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function writeChartFile(filePath: any, html: any) { const chartDir = path.dirname(filePath);
|
|
59
62
|
if (!fs.existsSync(chartDir)) ensureDir(chartDir);
|
|
60
63
|
ensureSiblingUplotAssets(chartDir);
|
|
61
64
|
// Atomic write (tmp + rename, matching the production storage adapter
|
|
@@ -190,5 +193,5 @@ function bindPan(chart) {
|
|
|
190
193
|
}
|
|
191
194
|
`;
|
|
192
195
|
|
|
193
|
-
export { escapeHtml, serializeJsonForScript, toEpochSeconds, writeChartFile, embedFunctionSources, UPLOT_SHARED_SCRIPT }
|
|
196
|
+
export { escapeHtml, serializeJsonForScript, toEpochSeconds, writeChartFile, toFileUrl, embedFunctionSources, UPLOT_SHARED_SCRIPT }
|
|
194
197
|
|
|
@@ -37,7 +37,7 @@ import fs from 'node:fs';
|
|
|
37
37
|
import { pathToFileURL } from 'node:url';
|
|
38
38
|
import * as KC from '../market_adapter/core/kibana_client.js';
|
|
39
39
|
import * as C from '../modules/constants.js';
|
|
40
|
-
import { loadBotMeta, loadBotSettings, computeBotKey } from './bot_key_utils.js';
|
|
40
|
+
import { loadBotMeta, loadBotSettings, computeBotKey, persistBotAccountId } from './bot_key_utils.js';
|
|
41
41
|
|
|
42
42
|
const { kibanaSearch, DEFAULT_CONFIG: BASE_CONFIG } = KC;
|
|
43
43
|
|
|
@@ -221,8 +221,11 @@ Time range (one of):
|
|
|
221
221
|
|
|
222
222
|
Options:
|
|
223
223
|
--account <id> Override account ID (default: from bot preferredAccount)
|
|
224
|
-
--lookup
|
|
225
|
-
--
|
|
224
|
+
--lookup Legacy: account names always resolve via BitShares node
|
|
225
|
+
--refresh-account Force re-resolution of preferredAccount and update the
|
|
226
|
+
stored accountId when it changed (default: reuse the
|
|
227
|
+
stored accountId with no chain lookup)
|
|
228
|
+
--node <url> BitShares node URL for account / precision resolution
|
|
226
229
|
--increment <pct> Grid increment percent (default: from bot config or ${Number((C as any)?.DEFAULT_CONFIG?.incrementPercent ?? 0.5)})
|
|
227
230
|
--tolerance <pct> Deprecated alias for --increment (kept for compat, prefer --increment)
|
|
228
231
|
--per-fill Check at fill granularity (default: per-order aggregated)
|
|
@@ -262,6 +265,7 @@ function parseArgs() {
|
|
|
262
265
|
end: null,
|
|
263
266
|
account: null,
|
|
264
267
|
lookup: false,
|
|
268
|
+
refreshAccount: false,
|
|
265
269
|
node: C.NODE_MANAGEMENT.DEFAULT_NODES[0],
|
|
266
270
|
perFill: false,
|
|
267
271
|
includeCrossPair: false,
|
|
@@ -279,6 +283,7 @@ function parseArgs() {
|
|
|
279
283
|
case '--end': opts.end = args[++i]; break;
|
|
280
284
|
case '--account': opts.account = args[++i]; break;
|
|
281
285
|
case '--lookup': opts.lookup = true; break;
|
|
286
|
+
case '--refresh-account': opts.refreshAccount = true; break;
|
|
282
287
|
case '--node': opts.node = args[++i]; break;
|
|
283
288
|
case '--per-fill': opts.perFill = true; break;
|
|
284
289
|
case '--include-cross-pair': opts.includeCrossPair = true; break;
|
|
@@ -356,32 +361,63 @@ async function resolveAccountId(name: string, nodeUrl: string): Promise<string |
|
|
|
356
361
|
}
|
|
357
362
|
}
|
|
358
363
|
|
|
359
|
-
async function resolveAccountForBot(botKey: string, overrideAccount: string | null, nodeUrl: string,
|
|
364
|
+
async function resolveAccountForBot(botKey: string, overrideAccount: string | null, nodeUrl: string, doRefresh: boolean): Promise<{ accountId: string; botMeta: any }> {
|
|
360
365
|
const botMeta = loadBotMeta(botKey);
|
|
361
366
|
if (!botMeta && !overrideAccount) {
|
|
362
367
|
console.error(`Error: bot key '${botKey}' not found in profiles/bots.json and no --account provided.`);
|
|
363
368
|
console.error(`Use --list-bots to see available keys, or pass --account <1.2.x> explicitly.`);
|
|
364
369
|
process.exit(1);
|
|
365
370
|
}
|
|
366
|
-
|
|
367
|
-
if (
|
|
371
|
+
// An explicit --account always wins and is never persisted.
|
|
372
|
+
if (overrideAccount) {
|
|
373
|
+
if (/^1\.2\.\d+$/.test(String(overrideAccount))) {
|
|
374
|
+
return { accountId: String(overrideAccount), botMeta };
|
|
375
|
+
}
|
|
376
|
+
console.log(` Resolving account name '${overrideAccount}'...`);
|
|
377
|
+
const resolvedOverride = await resolveAccountId(String(overrideAccount), nodeUrl);
|
|
378
|
+
if (!resolvedOverride) {
|
|
379
|
+
console.error(`Error: failed to resolve --account '${overrideAccount}' to 1.2.x`);
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
console.log(` → ${resolvedOverride}`);
|
|
383
|
+
return { accountId: String(resolvedOverride), botMeta };
|
|
384
|
+
}
|
|
385
|
+
const prefRaw = botMeta?.preferredAccount != null ? String(botMeta.preferredAccount) : null;
|
|
386
|
+
const prefIsId = !!prefRaw && /^1\.2\.\d+$/.test(prefRaw);
|
|
387
|
+
const storedId = /^1\.2\.\d+$/.test(String(botMeta?.accountId ?? '')) ? String(botMeta.accountId) : null;
|
|
388
|
+
// An explicit 1.2.x preferredAccount is authoritative: the stored accountId
|
|
389
|
+
// is a derived cache and must never override it. Self-heal the cache.
|
|
390
|
+
if (prefIsId) {
|
|
391
|
+
if (storedId && storedId !== prefRaw && persistBotAccountId(botKey, prefRaw as string)) {
|
|
392
|
+
console.log(` Updated stale stored accountId ${storedId} → ${prefRaw} from preferredAccount`);
|
|
393
|
+
}
|
|
394
|
+
return { accountId: prefRaw as string, botMeta };
|
|
395
|
+
}
|
|
396
|
+
if (!prefRaw) {
|
|
368
397
|
console.error(`Error: bot '${botKey}' has no preferredAccount and no --account provided.`);
|
|
369
398
|
process.exit(1);
|
|
370
399
|
}
|
|
371
|
-
//
|
|
372
|
-
if (
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
400
|
+
// Name + fresh-enough cache + no refresh requested: offline-friendly fast path.
|
|
401
|
+
if (storedId && !doRefresh) {
|
|
402
|
+
console.log(` Using stored accountId ${storedId} from profiles/bots.json (no lookup needed; pass --refresh-account to re-verify)`);
|
|
403
|
+
return { accountId: storedId, botMeta };
|
|
404
|
+
}
|
|
405
|
+
// No cache, or --refresh-account: resolve the name on-chain.
|
|
406
|
+
console.log(` Resolving account name '${prefRaw}'...`);
|
|
407
|
+
const resolved = await resolveAccountId(prefRaw, nodeUrl);
|
|
408
|
+
if (!resolved) {
|
|
409
|
+
console.error(`Error: failed to resolve account name '${prefRaw}' to 1.2.x`);
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
|
412
|
+
console.log(` → ${resolved}`);
|
|
413
|
+
if (!storedId && persistBotAccountId(botKey, resolved)) {
|
|
414
|
+
console.log(` Stored accountId ${resolved} in profiles/bots.json for '${botKey}'`);
|
|
415
|
+
} else if (storedId && storedId !== resolved && persistBotAccountId(botKey, resolved)) {
|
|
416
|
+
console.log(` Updated stored accountId ${storedId} → ${resolved} in profiles/bots.json for '${botKey}'`);
|
|
417
|
+
} else if (storedId === resolved) {
|
|
418
|
+
console.log(` Stored accountId ${resolved} confirmed up to date`);
|
|
383
419
|
}
|
|
384
|
-
return { accountId: String(
|
|
420
|
+
return { accountId: String(resolved), botMeta };
|
|
385
421
|
}
|
|
386
422
|
|
|
387
423
|
// ─── Asset precision resolution ───────────────────────────────────────────────
|
|
@@ -829,7 +865,7 @@ async function main() {
|
|
|
829
865
|
console.log(`\nGrid correction check — bot-key: ${opts.botKey}`);
|
|
830
866
|
console.log(`Range: ${label}`);
|
|
831
867
|
|
|
832
|
-
const { accountId, botMeta } = await resolveAccountForBot(opts.botKey, opts.account, opts.node, opts.
|
|
868
|
+
const { accountId, botMeta } = await resolveAccountForBot(opts.botKey, opts.account, opts.node, opts.refreshAccount);
|
|
833
869
|
console.log(`Account: ${accountId}${botMeta ? ` (${botMeta.assetA}/${botMeta.assetB})` : ''}`);
|
|
834
870
|
const incrementPercent = resolveIncrementPercent(botMeta, opts.incrementPercent);
|
|
835
871
|
console.log(`Increment: ${incrementPercent}% (halfInc ${incrementPercent/2}%)${opts.incrementPercent == null && botMeta?.incrementPercent != null ? ' — from bot config' : opts.incrementPercent != null ? ' — from --increment' : ' — default'}`);
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* BOT ACCOUNT-ID BACKFILL
|
|
6
|
+
*
|
|
7
|
+
* Resolves every bot's preferredAccount name to its chain account ID and
|
|
8
|
+
* stamps `accountId` next to `preferredAccount` in profiles/bots.json, so
|
|
9
|
+
* analysis tools and scripts can skip the chain lookup on later runs.
|
|
10
|
+
* Nothing is ever written by hand — all IDs in the file come from this
|
|
11
|
+
* script, the bot editor, or the analysis tools themselves.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* node dist/analysis/resolve_bot_accounts.js [--bot-key <key>] [--refresh]
|
|
15
|
+
* [--timeout-ms <n>] [--dry-run] [--json]
|
|
16
|
+
*
|
|
17
|
+
* Exit code 0 = all bot accounts resolved (or nothing to do),
|
|
18
|
+
* 1 = one or more resolutions failed.
|
|
19
|
+
*
|
|
20
|
+
* Short-lived batch tool: suppresses chain INFO logging and disconnects the
|
|
21
|
+
* shared client on exit. Run standalone only — never import/call from a
|
|
22
|
+
* long-running process holding a live connection.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { loadBotSettings, computeBotKey, persistBotAccountId } from './bot_key_utils.js';
|
|
26
|
+
import { setGlobalConsoleLevel, getGlobalConsoleLevel } from '../modules/order/logger.js';
|
|
27
|
+
import { sleep } from '../modules/order/utils/system.js';
|
|
28
|
+
import { getErrorMessage } from '../modules/utils/errors.js';
|
|
29
|
+
|
|
30
|
+
const DEFAULT_TIMEOUT_MS = 20000;
|
|
31
|
+
|
|
32
|
+
function printHelp() {
|
|
33
|
+
console.log(`\
|
|
34
|
+
Usage: node dist/analysis/resolve_bot_accounts.js [options]
|
|
35
|
+
|
|
36
|
+
Resolve bot preferredAccount names to chain IDs and store them as accountId
|
|
37
|
+
in profiles/bots.json (byte-preserving; comments and formatting survive).
|
|
38
|
+
|
|
39
|
+
Options:
|
|
40
|
+
--bot-key <key> Only process this bot key or bot name (default: all bots)
|
|
41
|
+
--refresh Re-resolve even when a stored accountId exists
|
|
42
|
+
--timeout-ms <n> Per-account chain lookup timeout (default: ${DEFAULT_TIMEOUT_MS})
|
|
43
|
+
--dry-run Report what would change without writing anything
|
|
44
|
+
--json Append a machine-readable summary
|
|
45
|
+
--help, -h Show this help
|
|
46
|
+
`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function parseArgs() {
|
|
50
|
+
const args = process.argv.slice(2);
|
|
51
|
+
if (args.includes('--help') || args.includes('-h')) { printHelp(); process.exit(0); }
|
|
52
|
+
const opts: any = {
|
|
53
|
+
botKey: null,
|
|
54
|
+
refresh: false,
|
|
55
|
+
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
56
|
+
dryRun: false,
|
|
57
|
+
json: false,
|
|
58
|
+
};
|
|
59
|
+
for (let i = 0; i < args.length; i++) {
|
|
60
|
+
switch (args[i]) {
|
|
61
|
+
case '--bot-key': opts.botKey = args[++i]; break;
|
|
62
|
+
case '--refresh': opts.refresh = true; break;
|
|
63
|
+
case '--timeout-ms': opts.timeoutMs = parseInt(args[++i], 10); break;
|
|
64
|
+
case '--dry-run': opts.dryRun = true; break;
|
|
65
|
+
case '--json': opts.json = true; break;
|
|
66
|
+
default:
|
|
67
|
+
console.error(`Unknown option: ${args[i]}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!Number.isFinite(opts.timeoutMs) || opts.timeoutMs <= 0) {
|
|
72
|
+
console.error('Error: --timeout-ms must be a positive number.');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
return opts;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function resolveWithTimeout(chainOrders: any, ref: string, timeoutMs: number): Promise<{ id: string | null; reason: string }> {
|
|
79
|
+
const timeoutErr: any = new Error(`lookup timed out after ${timeoutMs}ms`);
|
|
80
|
+
timeoutErr.code = 'ACCOUNT_LOOKUP_TIMEOUT';
|
|
81
|
+
try {
|
|
82
|
+
const id = await Promise.race([
|
|
83
|
+
chainOrders.resolveAccountId(ref),
|
|
84
|
+
sleep(timeoutMs).then(() => { throw timeoutErr; }),
|
|
85
|
+
]);
|
|
86
|
+
if (id && /^1\.2\.\d+$/.test(String(id))) return { id: String(id), reason: 'resolved' };
|
|
87
|
+
return { id: null, reason: 'not-found' };
|
|
88
|
+
} catch (err: any) {
|
|
89
|
+
return { id: null, reason: err && err.code === 'ACCOUNT_LOOKUP_TIMEOUT' ? 'timeout' : 'error' };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function main() {
|
|
94
|
+
const opts = parseArgs();
|
|
95
|
+
const settings = loadBotSettings();
|
|
96
|
+
const entries = Array.isArray(settings?.bots) ? settings.bots : [];
|
|
97
|
+
if (!entries.length) {
|
|
98
|
+
console.log('No bots in profiles/bots.json — nothing to do.');
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let targets = entries.map((bot: any, index: number) => ({ bot, index, key: computeBotKey(bot, index) }));
|
|
103
|
+
if (opts.botKey) {
|
|
104
|
+
const want = String(opts.botKey).toLowerCase();
|
|
105
|
+
targets = targets.filter((t: any) => t.key === want || String(t.bot?.name ?? '').toLowerCase() === want);
|
|
106
|
+
if (!targets.length) {
|
|
107
|
+
console.error(`Error: bot '${opts.botKey}' not found in profiles/bots.json.`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Suppress chain INFO spam for the whole batch; restored in finally.
|
|
113
|
+
// (setSuppressConnectionLog is read lazily so this file never drags the
|
|
114
|
+
// chain stack in when it cannot be loaded.)
|
|
115
|
+
let chainClient: any = null;
|
|
116
|
+
let prevSuppress = false;
|
|
117
|
+
let prevGlobalLevel: string | null = null;
|
|
118
|
+
let suppressionArmed = false;
|
|
119
|
+
let chainOrders: any = null;
|
|
120
|
+
try {
|
|
121
|
+
chainClient = await import('../modules/bitshares_client.js');
|
|
122
|
+
prevSuppress = chainClient.isSuppressConnectionLog();
|
|
123
|
+
prevGlobalLevel = getGlobalConsoleLevel();
|
|
124
|
+
suppressionArmed = true;
|
|
125
|
+
chainClient.setSuppressConnectionLog(true);
|
|
126
|
+
setGlobalConsoleLevel('warn');
|
|
127
|
+
chainOrders = await import('../modules/chain_orders.js');
|
|
128
|
+
} catch (err: any) {
|
|
129
|
+
console.error(`Error: could not load the chain stack (${getErrorMessage(err)}).`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const results: any[] = [];
|
|
134
|
+
try {
|
|
135
|
+
for (const t of targets) {
|
|
136
|
+
const name = t.bot?.name ?? t.key;
|
|
137
|
+
const prefRaw = t.bot?.preferredAccount != null ? String(t.bot.preferredAccount) : '';
|
|
138
|
+
const stored = /^1\.2\.\d+$/.test(String(t.bot?.accountId ?? '')) ? String(t.bot.accountId) : null;
|
|
139
|
+
if (!prefRaw) {
|
|
140
|
+
results.push({ key: t.key, name, status: 'skipped', detail: 'no preferredAccount' });
|
|
141
|
+
console.log(`- ${t.key}: skipped (no preferredAccount)`);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (/^1\.2\.\d+$/.test(prefRaw)) {
|
|
145
|
+
if (stored !== prefRaw) {
|
|
146
|
+
const wrote = opts.dryRun ? true : persistBotAccountId(t.key, prefRaw);
|
|
147
|
+
const status = !wrote ? 'failed' : (opts.dryRun ? 'would-update' : 'updated');
|
|
148
|
+
results.push({ key: t.key, name, status, detail: wrote ? `preferredAccount is authoritative (${prefRaw})` : 'could not write profiles/bots.json' });
|
|
149
|
+
console.log(`- ${t.key}: ${!wrote ? 'FAILED to write' : (opts.dryRun ? 'would update' : 'updated')} stored accountId → ${prefRaw} (preferredAccount is authoritative)`);
|
|
150
|
+
} else {
|
|
151
|
+
results.push({ key: t.key, name, status: 'cached', detail: prefRaw });
|
|
152
|
+
console.log(`- ${t.key}: cached (${prefRaw})`);
|
|
153
|
+
}
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (stored && !opts.refresh) {
|
|
157
|
+
results.push({ key: t.key, name, status: 'cached', detail: stored });
|
|
158
|
+
console.log(`- ${t.key}: cached (${stored})`);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const r = await resolveWithTimeout(chainOrders, prefRaw, opts.timeoutMs);
|
|
162
|
+
if (r.id) {
|
|
163
|
+
const changed = stored !== r.id;
|
|
164
|
+
const wrote = !changed || opts.dryRun ? true : persistBotAccountId(t.key, r.id);
|
|
165
|
+
const status = !wrote ? 'failed' : (opts.dryRun ? (changed ? 'would-update' : 'unchanged') : (changed ? (stored ? 'updated' : 'stored') : 'unchanged'));
|
|
166
|
+
results.push({ key: t.key, name, status, detail: wrote ? r.id : 'could not write profiles/bots.json' });
|
|
167
|
+
console.log(`- ${t.key}: '${prefRaw}' → ${r.id}${!wrote ? ' (FAILED to store)' : (changed ? (opts.dryRun ? ' (would store)' : (stored ? ' (updated)' : ' (stored)')) : ' (unchanged)')}`);
|
|
168
|
+
} else {
|
|
169
|
+
const why = r.reason === 'timeout' ? `nodes unreachable (timed out after ${opts.timeoutMs}ms)` : 'not found on the blockchain';
|
|
170
|
+
results.push({ key: t.key, name, status: 'failed', detail: why });
|
|
171
|
+
console.log(`- ${t.key}: FAILED to resolve '${prefRaw}' (${why})`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
} finally {
|
|
175
|
+
if (chainClient) {
|
|
176
|
+
try { await chainClient.disconnectClient(); } catch (_) { /* best-effort cleanup */ }
|
|
177
|
+
if (suppressionArmed) {
|
|
178
|
+
try { chainClient.setSuppressConnectionLog(prevSuppress); } catch (_) { /* restore-only */ }
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (suppressionArmed) {
|
|
182
|
+
try { setGlobalConsoleLevel(prevGlobalLevel); } catch (_) { /* restore-only */ }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const failed = results.filter((r: any) => r.status === 'failed');
|
|
187
|
+
const skipped = results.filter((r: any) => r.status === 'skipped').length;
|
|
188
|
+
const ok = results.length - failed.length - skipped;
|
|
189
|
+
console.log(`\nDone: ${ok} resolved, ${skipped} skipped, ${failed.length} failed${opts.dryRun ? ' (dry run, nothing written)' : ''}.`);
|
|
190
|
+
if (opts.json) {
|
|
191
|
+
console.log(JSON.stringify({ dryRun: opts.dryRun, results }, null, 2));
|
|
192
|
+
}
|
|
193
|
+
process.exit(failed.length ? 1 : 0);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
main().catch((err: any) => {
|
|
197
|
+
console.error('Fatal:', getErrorMessage(err));
|
|
198
|
+
process.exit(1);
|
|
199
|
+
});
|
|
@@ -5,6 +5,7 @@ import fs from 'node:fs';
|
|
|
5
5
|
import { pathToFileURL } from 'node:url';
|
|
6
6
|
import * as KC from '../market_adapter/core/kibana_client.js';
|
|
7
7
|
import * as C from '../modules/constants.js';
|
|
8
|
+
import { findBotKeyByAccountRef, getStoredBotAccountId, persistBotAccountId } from './bot_key_utils.js';
|
|
8
9
|
|
|
9
10
|
const { kibanaSearch, DEFAULT_CONFIG: BASE_CONFIG } = KC;
|
|
10
11
|
|
|
@@ -194,14 +195,15 @@ Analyzes filled orders for a BitShares account, computing realized PnL
|
|
|
194
195
|
via FIFO or sequential (LIFO) inventory tracking.
|
|
195
196
|
|
|
196
197
|
Arguments:
|
|
197
|
-
accountId BitShares account ID (1.2.x) or name (
|
|
198
|
+
accountId BitShares account ID (1.2.x) or name (auto-resolved in the background)
|
|
198
199
|
|
|
199
200
|
Options:
|
|
200
201
|
--start <iso> Start time (ISO 8601, e.g. 2025-01-01 or 2025-01-01T00:00:00Z)
|
|
201
202
|
--end <iso> End time (ISO 8601)
|
|
202
203
|
--hours <n> Lookback hours from now (alternative to --start/--end)
|
|
203
204
|
--asset <assetId> Filter to one base asset (e.g. 1.3.113 for bitUSD)
|
|
204
|
-
--lookup
|
|
205
|
+
--lookup Legacy (no-op): account names always resolve automatically
|
|
206
|
+
--refresh-account Force re-resolution and update the stored accountId
|
|
205
207
|
--node <url> BitShares node URL (default: first healthy from built-in pool)
|
|
206
208
|
--csv <file> Export trade list as CSV
|
|
207
209
|
--json <file> Export full analysis as JSON
|
|
@@ -233,6 +235,7 @@ function parseArgs() {
|
|
|
233
235
|
end: null,
|
|
234
236
|
asset: null,
|
|
235
237
|
lookup: false,
|
|
238
|
+
refreshAccount: false,
|
|
236
239
|
node: C.NODE_MANAGEMENT.DEFAULT_NODES[0],
|
|
237
240
|
csv: null,
|
|
238
241
|
json: null,
|
|
@@ -249,6 +252,7 @@ function parseArgs() {
|
|
|
249
252
|
case '--end': opts.end = args[++i]; break;
|
|
250
253
|
case '--asset': opts.asset = args[++i]; break;
|
|
251
254
|
case '--lookup': opts.lookup = true; break;
|
|
255
|
+
case '--refresh-account': opts.refreshAccount = true; break;
|
|
252
256
|
case '--node': opts.node = args[++i]; break;
|
|
253
257
|
case '--csv': opts.csv = args[++i]; break;
|
|
254
258
|
case '--json': opts.json = args[++i]; break;
|
|
@@ -1329,13 +1333,37 @@ async function run() {
|
|
|
1329
1333
|
const opts = parseArgs();
|
|
1330
1334
|
let accountId = opts.accountId;
|
|
1331
1335
|
|
|
1332
|
-
if (
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1336
|
+
if (!/^1\.2\.\d+$/.test(String(accountId))) {
|
|
1337
|
+
// Background resolution: the Kibana query below filters on the 1.2.x
|
|
1338
|
+
// account_id field, so a name must always resolve first (a raw name
|
|
1339
|
+
// would silently return zero fills).
|
|
1340
|
+
// Stored ID first: when the name belongs to a bot in profiles/bots.json
|
|
1341
|
+
// and its stamped accountId still matches, no chain lookup is needed.
|
|
1342
|
+
let matchedKey: string | null = null;
|
|
1343
|
+
let stored: string | null = null;
|
|
1344
|
+
try {
|
|
1345
|
+
const match = findBotKeyByAccountRef(accountId);
|
|
1346
|
+
if (match) {
|
|
1347
|
+
matchedKey = match.botKey;
|
|
1348
|
+
stored = getStoredBotAccountId(match.botKey, accountId);
|
|
1349
|
+
}
|
|
1350
|
+
} catch (_) {
|
|
1351
|
+
// bots.json issues must never break resolution; fall through to chain.
|
|
1352
|
+
}
|
|
1353
|
+
if (stored && !opts.refreshAccount) {
|
|
1354
|
+
console.log(` Using stored accountId ${stored} from profiles/bots.json (no lookup needed; pass --refresh-account to re-verify)`);
|
|
1355
|
+
accountId = stored;
|
|
1356
|
+
} else {
|
|
1357
|
+
const resolved = await resolveAccountId(accountId, opts.node);
|
|
1358
|
+
if (!resolved) {
|
|
1359
|
+
console.error(` Could not resolve "${accountId}" to an account ID`);
|
|
1360
|
+
process.exit(1);
|
|
1361
|
+
}
|
|
1362
|
+
accountId = resolved;
|
|
1363
|
+
if (matchedKey && persistBotAccountId(matchedKey, resolved)) {
|
|
1364
|
+
console.log(` Stored accountId ${resolved} in profiles/bots.json`);
|
|
1365
|
+
}
|
|
1337
1366
|
}
|
|
1338
|
-
accountId = resolved;
|
|
1339
1367
|
}
|
|
1340
1368
|
|
|
1341
1369
|
// Build time range
|
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
This exporter generates a standalone HTML chart in the `analysis/charts/` folder using the local `uPlot`-based TradingView-style renderer.
|
|
4
4
|
|
|
5
|
+
## Recommended: `dexbot tv` (One Step)
|
|
6
|
+
|
|
7
|
+
`dexbot tv` fetches the candles itself (chunked Kibana scans for pools, order-book fallback for pairs) and then renders through this exporter — no manual fetch/export steps needed:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Bot chart with AMA overlay (bot key from profiles/bots.json, default: 3 months)
|
|
11
|
+
dexbot tv <bot>
|
|
12
|
+
|
|
13
|
+
# Any pool by bare or full ID
|
|
14
|
+
dexbot tv 133
|
|
15
|
+
dexbot tv 1.19.133
|
|
16
|
+
|
|
17
|
+
# Any pair (pool-first, order-book fallback)
|
|
18
|
+
dexbot tv TOKENA/TOKENB
|
|
19
|
+
|
|
20
|
+
# Options
|
|
21
|
+
dexbot tv <bot> --month 6 # months of 1h candles (default: 3)
|
|
22
|
+
dexbot tv <bot> --chart analysis/charts/custom.html
|
|
23
|
+
dexbot tv <bot> --scale linear # log (default) or linear
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Output: `analysis/charts/tv_<bot|pool_<id>|<a>_<b>>_1h_<N>m.html`, with a clickable `file://` link printed on completion. Unknown targets fail fast with the list of known bot keys.
|
|
27
|
+
|
|
28
|
+
The sections below cover manual usage (explicit candle files, direct runner flags).
|
|
29
|
+
|
|
5
30
|
## What It Produces
|
|
6
31
|
|
|
7
32
|
- Log-scale price chart
|
|
@@ -11,7 +11,7 @@ import { getErrorMessage } from '../../modules/utils/errors.js';
|
|
|
11
11
|
import { toIntervalLabel } from '../../market_adapter/interval_utils.js';
|
|
12
12
|
import { loadBotMeta } from '../bot_key_utils.js';
|
|
13
13
|
import { resolveSource, listAvailableBots } from '../resolve_source.js';
|
|
14
|
-
import { writeChartFile } from '../chart_utils.js';
|
|
14
|
+
import { writeChartFile, toFileUrl } from '../chart_utils.js';
|
|
15
15
|
import { PATHS } from '../../modules/paths.js';
|
|
16
16
|
|
|
17
17
|
|
|
@@ -148,7 +148,7 @@ async function main() {
|
|
|
148
148
|
|
|
149
149
|
writeChartFile(config.chartFile, html);
|
|
150
150
|
|
|
151
|
-
if (!config.quiet) console.log(
|
|
151
|
+
if (!config.quiet) console.log(`\n[TradingView] ✓ Chart saved. Open chart: (${toFileUrl(config.chartFile)})`);
|
|
152
152
|
} catch (err: any) {
|
|
153
153
|
console.error(`[TradingView] Error: ${getErrorMessage(err)}`);
|
|
154
154
|
process.exit(1);
|
|
@@ -9,6 +9,7 @@ import { loadSettingsFile, resolveRawBotEntries, saveSettingsFile, normalizeBotE
|
|
|
9
9
|
import { normalizeMode, detectMode, setPreferredMode, describeModeChoice } from './launcher_mode_detector.js';
|
|
10
10
|
import { normalizeRoot, resolveRuntimeScript, normalizeProfileDir } from './launcher_paths.js';
|
|
11
11
|
import { getErrorMessage } from '../../modules/utils/errors.js';
|
|
12
|
+
import { isSameBotName } from '../../modules/utils/sanitize_key.js';
|
|
12
13
|
|
|
13
14
|
const storage = getStorage();
|
|
14
15
|
|
|
@@ -174,7 +175,7 @@ async function launcherReset(botName: string | null, options: Record<string, any
|
|
|
174
175
|
const { config } = loadSettingsFile(PROFILES_BOTS_FILE);
|
|
175
176
|
const entries = normalizeBotEntries(resolveRawBotEntries(config));
|
|
176
177
|
|
|
177
|
-
const targets = botName ? entries.filter((b: any) => b.name
|
|
178
|
+
const targets = botName ? entries.filter((b: any) => isSameBotName(b.name, botName)) : entries.filter((b: any) => b.active !== false);
|
|
178
179
|
|
|
179
180
|
if (botName && targets.length === 0) {
|
|
180
181
|
throw new Error(`Bot '${botName}' not found in ${PROFILES_BOTS_FILE}`);
|
|
@@ -235,7 +236,7 @@ async function launcherDisable(botName: string | null, options: Record<string, a
|
|
|
235
236
|
};
|
|
236
237
|
}
|
|
237
238
|
|
|
238
|
-
const match = entries.find((b: any) => b.name
|
|
239
|
+
const match = entries.find((b: any) => isSameBotName(b.name, botName));
|
|
239
240
|
if (!match) {
|
|
240
241
|
throw new Error(`Bot '${botName}' not found in ${PROFILES_BOTS_FILE}`);
|
|
241
242
|
}
|
|
@@ -268,7 +269,7 @@ async function launcherPm2Start(botName: string | null, options: Record<string,
|
|
|
268
269
|
const { config } = loadSettingsFile(PROFILES_BOTS_FILE);
|
|
269
270
|
const entries = normalizeBotEntries(resolveRawBotEntries(config));
|
|
270
271
|
|
|
271
|
-
const targets = botName ? entries.filter((b: any) => b.name
|
|
272
|
+
const targets = botName ? entries.filter((b: any) => isSameBotName(b.name, botName)) : entries.filter((b: any) => b.active !== false);
|
|
272
273
|
|
|
273
274
|
if (botName && targets.length === 0) {
|
|
274
275
|
throw new Error(`Bot '${botName}' not found or not active in ${PROFILES_BOTS_FILE}`);
|
|
@@ -287,7 +288,7 @@ async function launcherPm2Start(botName: string | null, options: Record<string,
|
|
|
287
288
|
// Filter apps if specific bot requested
|
|
288
289
|
let appsToStart = apps;
|
|
289
290
|
if (botName) {
|
|
290
|
-
appsToStart = apps.filter((app: any) => app.name
|
|
291
|
+
appsToStart = apps.filter((app: any) => isSameBotName(app.name, botName));
|
|
291
292
|
if (appsToStart.length === 0) {
|
|
292
293
|
throw new Error(`No PM2 app found for bot '${botName}'`);
|
|
293
294
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { PATHS } from '../../modules/paths.js';
|
|
4
4
|
import CreditRuntime from '../../modules/credit_runtime.js';
|
|
5
5
|
import { getErrorMessage } from '../../modules/utils/errors.js';
|
|
6
|
+
import { isSameBotName } from '../../modules/utils/sanitize_key.js';
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
function createCreditRuntimeAdapter(infra: any, options: Record<string, any> = {}) {
|
|
@@ -55,7 +56,7 @@ function createCreditRuntimeAdapter(infra: any, options: Record<string, any> = {
|
|
|
55
56
|
try {
|
|
56
57
|
const bundle = await infra.profiles.loadBundle();
|
|
57
58
|
const match = bundle.bots.find(
|
|
58
|
-
(b: Record<string, any>) => b.botKey
|
|
59
|
+
(b: Record<string, any>) => isSameBotName(b.botKey, botRef) || isSameBotName(b.name, botRef),
|
|
59
60
|
);
|
|
60
61
|
return match || null;
|
|
61
62
|
} catch {
|
|
@@ -10,6 +10,7 @@ import { acquireFileLock } from '../../market_adapter/utils/file_lock.js';
|
|
|
10
10
|
import { assertNoDuplicateBotKeys } from '../../modules/bot_settings.js';
|
|
11
11
|
import { clone } from './utils.js';
|
|
12
12
|
import { createBotKey, sanitizeKey } from '../../modules/account_orders.js';
|
|
13
|
+
import { isSameBotName } from '../../modules/utils/sanitize_key.js';
|
|
13
14
|
const storage = getStorage();
|
|
14
15
|
|
|
15
16
|
import type { BotSettings, ProfileOptions, ClawProfileBundle } from './types.js';
|
|
@@ -815,10 +816,10 @@ function matchBotIdentifier(bot: any, identifier: any) {
|
|
|
815
816
|
}
|
|
816
817
|
|
|
817
818
|
if (typeof identifier === 'object') {
|
|
818
|
-
if (identifier.botKey && bot.botKey === identifier.botKey) {
|
|
819
|
+
if (identifier.botKey && (bot.botKey === identifier.botKey || isSameBotName(bot.botKey, identifier.botKey))) {
|
|
819
820
|
return true;
|
|
820
821
|
}
|
|
821
|
-
if (identifier.name && bot.name
|
|
822
|
+
if (identifier.name && isSameBotName(bot.name, identifier.name)) {
|
|
822
823
|
return true;
|
|
823
824
|
}
|
|
824
825
|
if (identifier.assetA && identifier.assetB && bot.assetA === identifier.assetA && bot.assetB === identifier.assetB) {
|
|
@@ -839,7 +840,7 @@ function matchBotIdentifier(bot: any, identifier: any) {
|
|
|
839
840
|
return false;
|
|
840
841
|
}
|
|
841
842
|
|
|
842
|
-
if (bot.botKey === value || bot.name
|
|
843
|
+
if (bot.botKey === value || isSameBotName(bot.botKey, value) || isSameBotName(bot.name, value)) {
|
|
843
844
|
return true;
|
|
844
845
|
}
|
|
845
846
|
|