dexbot 1.6.2 → 1.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/CHANGELOG.md +53 -3
  2. package/README.md +11 -7
  3. package/analysis/README.md +20 -14
  4. package/analysis/account_resolver.ts +171 -0
  5. package/analysis/ama_fitting/analyze_lambda_vs_slow.ts +2 -5
  6. package/analysis/ama_fitting/package.json +1 -1
  7. package/analysis/bot_usage/discover_bot_accounts.ts +20 -27
  8. package/analysis/chain_pool.ts +47 -0
  9. package/analysis/chart_utils.ts +33 -20
  10. package/analysis/derivative_chart_generator.ts +2 -3
  11. package/analysis/fills_source.ts +234 -0
  12. package/analysis/grid_correction_check.ts +104 -244
  13. package/analysis/results/ama_sweep_results_lp_pool_133_1h.json +2455 -0
  14. package/analysis/results/bot_fitting_results_lp_pool_133_1h.json +218 -0
  15. package/analysis/trade_profitability.ts +169 -304
  16. package/analysis/tradingview/README.md +2 -2
  17. package/analysis/tradingview/h-bts_tradingview.html +1570 -0
  18. package/analysis/tradingview/t-bts_tradingview.html +1570 -0
  19. package/analysis/tradingview/tradingview_uplot_chart_generator.ts +3 -4
  20. package/analysis/trend_detection/dynamic_weight_chart_generator.ts +2 -3
  21. package/analysis/trend_detection/kalman_chart_generator.ts +2 -3
  22. package/analysis/trend_detection/package.json +1 -1
  23. package/analysis/trend_detection/regime_chart_generator.ts +2 -3
  24. package/analysis/trend_detection/volatility_chart_generator.ts +2 -3
  25. package/claw/package.json +1 -1
  26. package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
  27. package/claw/runtimes/openclaw-plugin/package.json +1 -1
  28. package/claw/tests/test_claw_mcp_transport.ts +2 -2
  29. package/dist/analysis/account_resolver.d.ts +43 -0
  30. package/dist/analysis/account_resolver.d.ts.map +1 -0
  31. package/dist/analysis/account_resolver.js +137 -0
  32. package/dist/analysis/account_resolver.js.map +1 -0
  33. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.d.ts.map +1 -1
  34. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js +2 -4
  35. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js.map +1 -1
  36. package/dist/analysis/bot_usage/discover_bot_accounts.js +20 -28
  37. package/dist/analysis/bot_usage/discover_bot_accounts.js.map +1 -1
  38. package/dist/analysis/chain_pool.d.ts +10 -0
  39. package/dist/analysis/chain_pool.d.ts.map +1 -0
  40. package/dist/analysis/chain_pool.js +47 -0
  41. package/dist/analysis/chain_pool.js.map +1 -0
  42. package/dist/analysis/chart_utils.d.ts +14 -1
  43. package/dist/analysis/chart_utils.d.ts.map +1 -1
  44. package/dist/analysis/chart_utils.js +32 -22
  45. package/dist/analysis/chart_utils.js.map +1 -1
  46. package/dist/analysis/derivative_chart_generator.d.ts.map +1 -1
  47. package/dist/analysis/derivative_chart_generator.js +2 -3
  48. package/dist/analysis/derivative_chart_generator.js.map +1 -1
  49. package/dist/analysis/fills_source.d.ts +79 -0
  50. package/dist/analysis/fills_source.d.ts.map +1 -0
  51. package/dist/analysis/fills_source.js +186 -0
  52. package/dist/analysis/fills_source.js.map +1 -0
  53. package/dist/analysis/grid_correction_check.d.ts +4 -20
  54. package/dist/analysis/grid_correction_check.d.ts.map +1 -1
  55. package/dist/analysis/grid_correction_check.js +95 -248
  56. package/dist/analysis/grid_correction_check.js.map +1 -1
  57. package/dist/analysis/trade_profitability.d.ts +18 -19
  58. package/dist/analysis/trade_profitability.d.ts.map +1 -1
  59. package/dist/analysis/trade_profitability.js +136 -280
  60. package/dist/analysis/trade_profitability.js.map +1 -1
  61. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts.map +1 -1
  62. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js +3 -4
  63. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js.map +1 -1
  64. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.d.ts.map +1 -1
  65. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js +2 -3
  66. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js.map +1 -1
  67. package/dist/analysis/trend_detection/kalman_chart_generator.d.ts.map +1 -1
  68. package/dist/analysis/trend_detection/kalman_chart_generator.js +2 -3
  69. package/dist/analysis/trend_detection/kalman_chart_generator.js.map +1 -1
  70. package/dist/analysis/trend_detection/regime_chart_generator.d.ts.map +1 -1
  71. package/dist/analysis/trend_detection/regime_chart_generator.js +2 -3
  72. package/dist/analysis/trend_detection/regime_chart_generator.js.map +1 -1
  73. package/dist/analysis/trend_detection/volatility_chart_generator.d.ts.map +1 -1
  74. package/dist/analysis/trend_detection/volatility_chart_generator.js +2 -3
  75. package/dist/analysis/trend_detection/volatility_chart_generator.js.map +1 -1
  76. package/dist/market_adapter/lp_chart_core.d.ts.map +1 -1
  77. package/dist/market_adapter/lp_chart_core.js +2 -3
  78. package/dist/market_adapter/lp_chart_core.js.map +1 -1
  79. package/dist/modules/account_bots.d.ts +1 -18
  80. package/dist/modules/account_bots.d.ts.map +1 -1
  81. package/dist/modules/account_bots.js +1 -1
  82. package/dist/modules/account_bots.js.map +1 -1
  83. package/dist/modules/bitshares-native/serial/operations.d.ts +1 -84
  84. package/dist/modules/bitshares-native/serial/operations.d.ts.map +1 -1
  85. package/dist/modules/bitshares-native/serial/operations.js +1 -38
  86. package/dist/modules/bitshares-native/serial/operations.js.map +1 -1
  87. package/dist/modules/cli_colors.d.ts +0 -1
  88. package/dist/modules/cli_colors.d.ts.map +1 -1
  89. package/dist/modules/constants.js +5 -5
  90. package/dist/modules/constants.js.map +1 -1
  91. package/dist/modules/credential_policy.d.ts +1 -25
  92. package/dist/modules/credential_policy.d.ts.map +1 -1
  93. package/dist/modules/credential_policy.js +1 -1
  94. package/dist/modules/credential_policy.js.map +1 -1
  95. package/dist/modules/credential_runtime.d.ts +1 -6
  96. package/dist/modules/credential_runtime.d.ts.map +1 -1
  97. package/dist/modules/credential_runtime.js +1 -1
  98. package/dist/modules/credential_runtime.js.map +1 -1
  99. package/dist/modules/credential_session_cache.d.ts +1 -5
  100. package/dist/modules/credential_session_cache.d.ts.map +1 -1
  101. package/dist/modules/credential_session_cache.js +1 -1
  102. package/dist/modules/credential_session_cache.js.map +1 -1
  103. package/dist/modules/crypto/pure_secp256k1.d.ts +1 -2
  104. package/dist/modules/crypto/pure_secp256k1.d.ts.map +1 -1
  105. package/dist/modules/crypto/pure_secp256k1.js +1 -1
  106. package/dist/modules/crypto/pure_secp256k1.js.map +1 -1
  107. package/dist/modules/dexbot_class.d.ts +9 -8
  108. package/dist/modules/dexbot_class.d.ts.map +1 -1
  109. package/dist/modules/dexbot_class.js +9 -8
  110. package/dist/modules/dexbot_class.js.map +1 -1
  111. package/dist/modules/dexbot_cow_runtime.d.ts +87 -7
  112. package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
  113. package/dist/modules/dexbot_cow_runtime.js +489 -14
  114. package/dist/modules/dexbot_cow_runtime.js.map +1 -1
  115. package/dist/modules/dexbot_fill_runtime.d.ts +1 -17
  116. package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
  117. package/dist/modules/dexbot_fill_runtime.js +1 -1
  118. package/dist/modules/dexbot_fill_runtime.js.map +1 -1
  119. package/dist/modules/dexbot_maintenance_runtime.d.ts +1 -1
  120. package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
  121. package/dist/modules/dexbot_maintenance_runtime.js +21 -5
  122. package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
  123. package/dist/modules/dexbot_state_recovery.d.ts +1 -7
  124. package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
  125. package/dist/modules/dexbot_state_recovery.js +1 -1
  126. package/dist/modules/dexbot_state_recovery.js.map +1 -1
  127. package/dist/modules/launcher/adapter_requirement.d.ts +1 -5
  128. package/dist/modules/launcher/adapter_requirement.d.ts.map +1 -1
  129. package/dist/modules/launcher/adapter_requirement.js +1 -1
  130. package/dist/modules/launcher/adapter_requirement.js.map +1 -1
  131. package/dist/modules/launcher/bot_supervisor.d.ts +1 -6
  132. package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -1
  133. package/dist/modules/launcher/bot_supervisor.js +1 -1
  134. package/dist/modules/launcher/bot_supervisor.js.map +1 -1
  135. package/dist/modules/launcher/foreign_cred_daemon.d.ts +1 -3
  136. package/dist/modules/launcher/foreign_cred_daemon.d.ts.map +1 -1
  137. package/dist/modules/launcher/foreign_cred_daemon.js +1 -1
  138. package/dist/modules/launcher/foreign_cred_daemon.js.map +1 -1
  139. package/dist/modules/launcher/launch_modes.d.ts +1 -2
  140. package/dist/modules/launcher/launch_modes.d.ts.map +1 -1
  141. package/dist/modules/launcher/launch_modes.js +1 -1
  142. package/dist/modules/launcher/launch_modes.js.map +1 -1
  143. package/dist/modules/launcher/market_adapter_runtime.d.ts +1 -3
  144. package/dist/modules/launcher/market_adapter_runtime.d.ts.map +1 -1
  145. package/dist/modules/launcher/market_adapter_runtime.js +1 -1
  146. package/dist/modules/launcher/market_adapter_runtime.js.map +1 -1
  147. package/dist/modules/launcher/monolithic_runtime.d.ts +1 -5
  148. package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -1
  149. package/dist/modules/launcher/monolithic_runtime.js +1 -1
  150. package/dist/modules/launcher/monolithic_runtime.js.map +1 -1
  151. package/dist/modules/launcher/status_reporting.d.ts +1 -2
  152. package/dist/modules/launcher/status_reporting.d.ts.map +1 -1
  153. package/dist/modules/launcher/status_reporting.js +1 -1
  154. package/dist/modules/launcher/status_reporting.js.map +1 -1
  155. package/dist/modules/order/export.d.ts +1 -8
  156. package/dist/modules/order/export.d.ts.map +1 -1
  157. package/dist/modules/order/export.js +1 -1
  158. package/dist/modules/order/export.js.map +1 -1
  159. package/dist/modules/order/grid.d.ts +7 -0
  160. package/dist/modules/order/grid.d.ts.map +1 -1
  161. package/dist/modules/order/grid.js +52 -98
  162. package/dist/modules/order/grid.js.map +1 -1
  163. package/dist/modules/order/grid_reconcile_internal.d.ts +1 -10
  164. package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
  165. package/dist/modules/order/grid_reconcile_internal.js +2 -2
  166. package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
  167. package/dist/modules/order/manager.d.ts +10 -2
  168. package/dist/modules/order/manager.d.ts.map +1 -1
  169. package/dist/modules/order/manager.js +20 -7
  170. package/dist/modules/order/manager.js.map +1 -1
  171. package/dist/modules/order/sync_engine.d.ts.map +1 -1
  172. package/dist/modules/order/sync_engine.js +25 -20
  173. package/dist/modules/order/sync_engine.js.map +1 -1
  174. package/dist/modules/order/utils/math.d.ts +1 -10
  175. package/dist/modules/order/utils/math.d.ts.map +1 -1
  176. package/dist/modules/order/utils/math.js +1 -1
  177. package/dist/modules/order/utils/math.js.map +1 -1
  178. package/dist/modules/order/utils/order.d.ts +45 -7
  179. package/dist/modules/order/utils/order.d.ts.map +1 -1
  180. package/dist/modules/order/utils/order.js +160 -15
  181. package/dist/modules/order/utils/order.js.map +1 -1
  182. package/dist/modules/order/utils/system.d.ts +0 -15
  183. package/dist/modules/order/utils/system.d.ts.map +1 -1
  184. package/dist/modules/order/utils/system.js +2 -2
  185. package/dist/modules/order/utils/system.js.map +1 -1
  186. package/dist/modules/process_discovery.d.ts +0 -3
  187. package/dist/modules/process_discovery.d.ts.map +1 -1
  188. package/dist/modules/process_discovery.js +1 -1
  189. package/dist/modules/process_discovery.js.map +1 -1
  190. package/dist/modules/utils/chain_logs.d.ts +1 -2
  191. package/dist/modules/utils/chain_logs.d.ts.map +1 -1
  192. package/dist/modules/utils/chain_logs.js +1 -1
  193. package/dist/modules/utils/chain_logs.js.map +1 -1
  194. package/dist/scripts/analyze-git.js +2 -2
  195. package/dist/scripts/analyze-git.js.map +1 -1
  196. package/dist/scripts/update.js +46 -31
  197. package/dist/scripts/update.js.map +1 -1
  198. package/docs/BITSHARES_ONBOARDING.md +110 -40
  199. package/docs/COPY_ON_WRITE_MASTER_PLAN.md +10 -207
  200. package/docs/COW_INVARIANTS.md +16 -10
  201. package/docs/DEXBOT_COMPARISON.md +3 -3
  202. package/docs/EVOLUTION.md +75 -29
  203. package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +6 -6
  204. package/docs/GRID_RECALCULATION.md +14 -6
  205. package/docs/GRID_RECONCILE.md +28 -25
  206. package/docs/LIFECYCLE.md +12 -11
  207. package/docs/README.md +48 -42
  208. package/docs/architecture.md +19 -30
  209. package/docs/developer_guide.md +3 -3
  210. package/package.json +1 -1
  211. package/scripts/git-viewer.sh +1 -1
  212. package/tests/README.md +10 -1
@@ -37,81 +37,26 @@ 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, persistBotAccountId } from './bot_key_utils.js';
40
+ import { loadBotSettings, computeBotKey } from './bot_key_utils.js';
41
+ import { resolveBotAccount } from './account_resolver.js';
42
+ import {
43
+ BTS_ID,
44
+ FillRecord,
45
+ assetPrec,
46
+ assetSymbol,
47
+ fetchAllFills,
48
+ resolveAssetPrecisions,
49
+ toReal,
50
+ } from './fills_source.js';
41
51
 
42
52
  const { kibanaSearch, DEFAULT_CONFIG: BASE_CONFIG } = KC;
43
53
 
44
- const OP_FILL_ORDER = 4;
45
- const BTS_ID = '1.3.0';
46
-
47
- interface AssetInfo { symbol: string; precision: number; }
48
- const ASSETS: Record<string, AssetInfo> = {
49
- '1.3.0': { symbol: 'BTS', precision: 5 },
50
- '1.3.118': { symbol: 'GBP', precision: 4 },
51
- '1.3.119': { symbol: 'JPY', precision: 2 },
52
- '1.3.120': { symbol: 'EUR', precision: 4 },
53
- '1.3.1325': { symbol: 'RUBLE', precision: 5 },
54
- '1.3.2512': { symbol: 'EVRAZ', precision: 4 },
55
- '1.3.3291': { symbol: 'TWENTIX', precision: 5 },
56
- '1.3.4099': { symbol: 'XBTSX.STH', precision: 6 },
57
- '1.3.4156': { symbol: 'XBTSX.DOGE', precision: 5 },
58
- '1.3.4157': { symbol: 'XBTSX.BTC', precision: 8 },
59
- '1.3.4159': { symbol: 'XBTSX.LTC', precision: 8 },
60
- '1.3.4176': { symbol: 'XBTSX.DASH', precision: 8 },
61
- '1.3.4274': { symbol: 'XBTSX.BCH', precision: 8 },
62
- '1.3.4760': { symbol: 'XBTSX.ETH', precision: 7 },
63
- '1.3.5537': { symbol: 'IOB.XRP', precision: 4 },
64
- '1.3.5541': { symbol: 'XBTSX.BNB', precision: 7 },
65
- '1.3.5589': { symbol: 'XBTSX.USDT', precision: 6 },
66
- '1.3.5641': { symbol: 'HONEST.CNY', precision: 4 },
67
- '1.3.5649': { symbol: 'HONEST.USD', precision: 4 },
68
- '1.3.5650': { symbol: 'HONEST.BTC', precision: 8 },
69
- '1.3.5659': { symbol: 'HONEST.ETH', precision: 6 },
70
- '1.3.5870': { symbol: 'XBTSX.FIL', precision: 6 },
71
- '1.3.5887': { symbol: 'XBTSX.RUB', precision: 4 },
72
- '1.3.5902': { symbol: 'XBTSX.USDC', precision: 6 },
73
- '1.3.6013': { symbol: 'XBTSX.HIVE', precision: 6 },
74
- '1.3.6124': { symbol: 'XBTSX.AVAX', precision: 6 },
75
- '1.3.6139': { symbol: 'XBTSX.XAUT', precision: 6 },
76
- '1.3.6166': { symbol: 'XBTSX.MATIC', precision: 5 },
77
- '1.3.6241': { symbol: 'XBTSX.ETC', precision: 7 },
78
- '1.3.6268': { symbol: 'BTWTY.EOS', precision: 4 },
79
- '1.3.6301': { symbol: 'HONEST.MONEY', precision: 8 },
80
- '1.3.6304': { symbol: 'HONEST.ADA', precision: 8 },
81
- '1.3.6305': { symbol: 'HONEST.DOT', precision: 8 },
82
- '1.3.6309': { symbol: 'HONEST.ATOM', precision: 8 },
83
- '1.3.6311': { symbol: 'HONEST.ALGO', precision: 8 },
84
- '1.3.6312': { symbol: 'HONEST.FIL', precision: 8 },
85
- '1.3.6313': { symbol: 'HONEST.EOS', precision: 8 },
86
- '1.3.6315': { symbol: 'HONEST.EUR', precision: 4 },
87
- '1.3.6316': { symbol: 'HONEST.GBP', precision: 4 },
88
- '1.3.6317': { symbol: 'HONEST.JPY', precision: 4 },
89
- '1.3.6444': { symbol: 'IOB.XLM', precision: 4 },
90
- '1.3.6573': { symbol: 'XBTSX.DAI', precision: 6 },
91
- '1.3.6620': { symbol: 'XBTSX.A', precision: 6 },
92
- '1.3.6627': { symbol: 'XBTSX.LINK', precision: 6 },
93
- };
94
- const resolvedPrecisions: Record<string, number> = {};
95
- function assetSymbol(id: string): string { return ASSETS[id]?.symbol ?? id; }
96
- function assetPrec(id: string): number | undefined { return ASSETS[id]?.precision ?? resolvedPrecisions[id]; }
97
- function toReal(amount: number, assetId: string): number {
98
- const p = assetPrec(assetId);
99
- if (p === undefined) return NaN;
100
- return amount / Math.pow(10, p);
101
- }
54
+ const OP_LIMIT_ORDER_UPDATE = 77;
102
55
 
103
56
  // ─── Types ────────────────────────────────────────────────────────────────────
104
- interface FillRecord {
105
- time: string;
106
- blockNum: number;
107
- opNum: number;
57
+ interface OrderUpdate {
108
58
  orderId: string;
109
- accountId: string;
110
- pays: { amount: number; asset_id: string };
111
- receives: { amount: number; asset_id: string };
112
- fee: { amount: number; asset_id: string };
113
- isMaker: boolean;
114
- sort: any[];
59
+ sequence: number;
115
60
  }
116
61
  interface TradeFill {
117
62
  time: string;
@@ -225,7 +170,6 @@ Options:
225
170
  --refresh-account Force re-resolution of preferredAccount and update the
226
171
  stored accountId when it changed (default: reuse the
227
172
  stored accountId with no chain lookup)
228
- --node <url> BitShares node URL for account / precision resolution
229
173
  --increment <pct> Grid increment percent (default: from bot config or ${Number((C as any)?.DEFAULT_CONFIG?.incrementPercent ?? 0.5)})
230
174
  --tolerance <pct> Deprecated alias for --increment (kept for compat, prefer --increment)
231
175
  --per-fill Check at fill granularity (default: per-order aggregated)
@@ -266,7 +210,6 @@ function parseArgs() {
266
210
  account: null,
267
211
  lookup: false,
268
212
  refreshAccount: false,
269
- node: C.NODE_MANAGEMENT.DEFAULT_NODES[0],
270
213
  perFill: false,
271
214
  includeCrossPair: false,
272
215
  incrementPercent: null as number | null,
@@ -284,7 +227,6 @@ function parseArgs() {
284
227
  case '--account': opts.account = args[++i]; break;
285
228
  case '--lookup': opts.lookup = true; break;
286
229
  case '--refresh-account': opts.refreshAccount = true; break;
287
- case '--node': opts.node = args[++i]; break;
288
230
  case '--per-fill': opts.perFill = true; break;
289
231
  case '--include-cross-pair': opts.includeCrossPair = true; break;
290
232
  case '--increment': opts.incrementPercent = parseFloat(args[++i]); break;
@@ -341,183 +283,68 @@ function resolveTimeRange(opts: any): { gte: string; lte: string; label: string
341
283
  return { gte, lte, label };
342
284
  }
343
285
 
344
- // ─── Account resolution ───────────────────────────────────────────────────────
345
- async function resolveAccountId(name: string, nodeUrl: string): Promise<string | null> {
346
- const { createReadOnlyClient } = await import('../modules/bitshares-native/index.js');
347
- const client = createReadOnlyClient({ nodes: [nodeUrl] });
348
- const prevLevel = process.env.LOG_LEVEL;
349
- process.env.LOG_LEVEL = 'warn';
350
- try {
351
- await client.connect();
352
- const accounts = await client.db('lookup_account_names', [[name]]);
353
- if (Array.isArray(accounts) && accounts[0]?.id) return accounts[0].id;
354
- return null;
355
- } catch (e: any) {
356
- console.warn(` [warn] Account resolution failed: ${e.message}`);
357
- return null;
358
- } finally {
359
- try { client.disconnect(); } catch (_) {}
360
- process.env.LOG_LEVEL = prevLevel;
361
- }
362
- }
363
-
364
- async function resolveAccountForBot(botKey: string, overrideAccount: string | null, nodeUrl: string, doRefresh: boolean): Promise<{ accountId: string; botMeta: any }> {
365
- const botMeta = loadBotMeta(botKey);
366
- if (!botMeta && !overrideAccount) {
367
- console.error(`Error: bot key '${botKey}' not found in profiles/bots.json and no --account provided.`);
368
- console.error(`Use --list-bots to see available keys, or pass --account <1.2.x> explicitly.`);
369
- process.exit(1);
370
- }
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) {
397
- console.error(`Error: bot '${botKey}' has no preferredAccount and no --account provided.`);
398
- process.exit(1);
399
- }
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`);
419
- }
420
- return { accountId: String(resolved), botMeta };
421
- }
422
-
423
- // ─── Asset precision resolution ───────────────────────────────────────────────
424
- async function resolveAssetPrecisions(fills: FillRecord[], nodeUrl: string | null): Promise<void> {
425
- const unknownIds = new Set<string>();
426
- for (const f of fills) {
427
- for (const id of [f.pays.asset_id, f.receives.asset_id, f.fee.asset_id]) {
428
- if (id !== BTS_ID && !(id in ASSETS) && !(id in resolvedPrecisions)) unknownIds.add(id);
429
- }
430
- }
431
- if (unknownIds.size === 0 || !nodeUrl) return;
432
- const ids = [...unknownIds];
433
- console.log(` Resolving ${ids.length} unknown asset(s) from blockchain...`);
434
- const { createReadOnlyClient } = await import('../modules/bitshares-native/index.js');
435
- const client = createReadOnlyClient({ nodes: [nodeUrl] });
436
- try {
437
- await client.connect();
438
- const assets = await client.db('get_assets', [ids]);
439
- if (Array.isArray(assets)) {
440
- for (const asset of assets) {
441
- if (asset?.id && asset.precision != null) {
442
- resolvedPrecisions[asset.id] = asset.precision;
443
- console.log(` ${asset.id} → ${asset.symbol || '?'} (precision ${asset.precision})`);
444
- }
445
- }
446
- }
447
- const missing = ids.filter(id => !(id in resolvedPrecisions));
448
- if (missing.length > 0) console.warn(` [warn] ${missing.length} asset(s) not found on chain: ${missing.join(', ')}`);
449
- } catch (e: any) {
450
- console.warn(` [warn] Asset resolution failed: ${e.message}`);
451
- } finally {
452
- try { client.disconnect(); } catch (_) {}
286
+ // ─── Account resolution errors ────────────────────────────────────────────────
287
+ /** Map a shared account-resolver failure onto this tool's operator-facing hint. */
288
+ function reportAccountFailure(resolved: any, opts: any): void {
289
+ switch (resolved.reason) {
290
+ case 'bot-not-found':
291
+ console.error(`Error: bot key '${opts.botKey}' not found in profiles/bots.json and no --account provided.`);
292
+ console.error('Use --list-bots to see available keys, or pass --account <1.2.x> explicitly.');
293
+ break;
294
+ case 'override-unresolved':
295
+ console.error(`Error: failed to resolve --account '${opts.account}' to 1.2.x`);
296
+ break;
297
+ case 'no-preferred-account':
298
+ console.error(`Error: bot '${opts.botKey}' has no preferredAccount and no --account provided.`);
299
+ break;
300
+ default:
301
+ console.error(`Error: failed to resolve account name '${resolved?.botMeta?.preferredAccount ?? opts.botKey}' to 1.2.x`);
453
302
  }
454
303
  }
455
304
 
456
305
  // ─── Kibana fetch ─────────────────────────────────────────────────────────────
457
- function buildFillQuery(accountId: string, gte: string, lte: string, size: number) {
458
- return {
459
- size,
460
- track_total_hits: false,
461
- _source: [
462
- 'block_data.block_time',
463
- 'block_data.block_num',
464
- 'operation_id_num',
465
- 'operation_history.op_object.pays',
466
- 'operation_history.op_object.receives',
467
- 'operation_history.op_object.fee',
468
- 'operation_history.op_object.order_id',
469
- 'operation_history.op_object.account_id',
470
- 'operation_history.op_object.is_maker',
471
- ],
472
- query: {
473
- bool: {
474
- filter: [
475
- { term: { operation_type: OP_FILL_ORDER } },
476
- { term: { 'operation_history.op_object.account_id.keyword': accountId } },
477
- { range: { 'block_data.block_time': { gte, lte } } },
478
- ],
479
- },
480
- },
481
- sort: [
482
- { 'block_data.block_time': { order: 'asc' } },
483
- { operation_id_num: { order: 'asc' } },
484
- ],
485
- };
486
- }
487
-
488
- async function fetchAllFills(config: any, accountId: string, gte: string, lte: string): Promise<FillRecord[]> {
306
+ async function fetchAllOrderUpdates(config: any, accountId: string, gte: string, lte: string): Promise<OrderUpdate[]> {
489
307
  const pageSize = 10000;
490
- const fills: FillRecord[] = [];
308
+ const updates: OrderUpdate[] = [];
491
309
  let searchAfter: any[] | null = null;
492
310
  const cfg = { ...BASE_CONFIG, timeout: 60000, ...config };
493
311
  while (true) {
494
- const query = buildFillQuery(accountId, gte, lte, pageSize);
495
- if (searchAfter) (query as any).search_after = searchAfter;
312
+ const query: any = {
313
+ size: pageSize,
314
+ track_total_hits: false,
315
+ _source: [
316
+ 'block_data.block_num', 'operation_id_num',
317
+ 'operation_history.op_object.order',
318
+ 'operation_history.op_object.seller',
319
+ ],
320
+ query: { bool: { filter: [
321
+ { term: { operation_type: OP_LIMIT_ORDER_UPDATE } },
322
+ { term: { 'operation_history.op_object.seller.keyword': accountId } },
323
+ { range: { 'block_data.block_time': { gte, lte } } },
324
+ ] } },
325
+ sort: [
326
+ { 'block_data.block_num': { order: 'asc' } },
327
+ { operation_id_num: { order: 'asc' } },
328
+ ],
329
+ };
330
+ if (searchAfter) query.search_after = searchAfter;
496
331
  const result: any = await kibanaSearch(cfg, query);
497
332
  const hits = result?.hits?.hits ?? [];
498
333
  if (!hits.length) break;
499
334
  for (const hit of hits) {
500
- const src = hit?._source;
501
- const op = src?.operation_history?.op_object;
502
- if (!op || !op.pays || !op.receives) continue;
503
- fills.push({
504
- time: src.block_data?.block_time ?? '',
505
- blockNum: src.block_data?.block_num ?? 0,
506
- opNum: Number(src.operation_id_num ?? 0),
507
- orderId: op.order_id ?? '',
508
- accountId: op.account_id ?? '',
509
- pays: { amount: Number(op.pays.amount ?? 0), asset_id: op.pays.asset_id ?? '' },
510
- receives: { amount: Number(op.receives.amount ?? 0), asset_id: op.receives.asset_id ?? '' },
511
- fee: { amount: Number(op.fee?.amount ?? 0), asset_id: op.fee?.asset_id ?? '' },
512
- isMaker: op.is_maker ?? false,
513
- sort: hit.sort,
514
- });
335
+ const source = hit?._source || {};
336
+ const op = source.operation_history?.op_object || {};
337
+ const orderId = op.order;
338
+ if (!orderId) continue;
339
+ const blockNum = Number(source.block_data?.block_num ?? 0);
340
+ const opNum = Number(source.operation_id_num ?? 0);
341
+ updates.push({ orderId, sequence: blockNum * 1e6 + opNum });
515
342
  }
516
- if (hits.length < pageSize) break;
517
- searchAfter = hits[hits.length - 1].sort;
518
- if (!Array.isArray(searchAfter)) break;
343
+ const last = hits[hits.length - 1];
344
+ searchAfter = last?.sort;
345
+ if (!searchAfter || hits.length < pageSize) break;
519
346
  }
520
- return fills;
347
+ return updates;
521
348
  }
522
349
 
523
350
  // ─── Fill classification ─────────────────────────────────────────────────────
@@ -578,11 +405,31 @@ function classifyFills(fills: FillRecord[]): { trades: TradeFill[]; skipped: num
578
405
  return { trades, skipped };
579
406
  }
580
407
 
581
- // ─── Per-order aggregation (collapses multi-fill orders to weighted avg price) ──
582
- function aggregateByOrder(trades: TradeFill[]): AggregatedOrder[] {
408
+ // ─── Per-order/price-epoch aggregation ───────────────────────────────────────
409
+ // Collapses partial fills at one order lifetime, but never mixes fills from
410
+ // different native limit_order_update repricings of the same order ID.
411
+ function aggregateByOrder(trades: TradeFill[], updates: OrderUpdate[] = []): AggregatedOrder[] {
412
+ const updatesByOrder = new Map<string, number[]>();
413
+ for (const update of updates) {
414
+ const list = updatesByOrder.get(update.orderId) || [];
415
+ list.push(update.sequence);
416
+ updatesByOrder.set(update.orderId, list);
417
+ }
418
+ for (const list of updatesByOrder.values()) list.sort((a, b) => a - b);
419
+
583
420
  const map = new Map<string, { trades: TradeFill[] }>();
584
421
  for (const t of trades) {
585
- const k = t.orderId || `__fill_${t.sequence}`;
422
+ // An update is ordered before the fills it can affect. The epoch is
423
+ // therefore the latest update sequence at or before this fill.
424
+ const orderUpdates = updatesByOrder.get(t.orderId) || [];
425
+ let epoch = 0;
426
+ for (const sequence of orderUpdates) {
427
+ if (sequence <= t.sequence) epoch = sequence;
428
+ else break;
429
+ }
430
+ const k = t.orderId
431
+ ? `${t.orderId}:${t.direction}:${t.baseAsset}:${t.quoteAsset}:${epoch}`
432
+ : `__fill_${t.sequence}`;
586
433
  if (!map.has(k)) map.set(k, { trades: [] });
587
434
  map.get(k)!.trades.push(t);
588
435
  }
@@ -865,21 +712,34 @@ async function main() {
865
712
  console.log(`\nGrid correction check — bot-key: ${opts.botKey}`);
866
713
  console.log(`Range: ${label}`);
867
714
 
868
- const { accountId, botMeta } = await resolveAccountForBot(opts.botKey, opts.account, opts.node, opts.refreshAccount);
715
+ const resolvedAccount = await resolveBotAccount(opts.botKey, {
716
+ overrideAccount: opts.account,
717
+ refresh: opts.refreshAccount,
718
+ });
719
+ if (!resolvedAccount.accountId) {
720
+ reportAccountFailure(resolvedAccount, opts);
721
+ process.exit(1);
722
+ }
723
+ const accountId = resolvedAccount.accountId;
724
+ const botMeta = resolvedAccount.botMeta;
869
725
  console.log(`Account: ${accountId}${botMeta ? ` (${botMeta.assetA}/${botMeta.assetB})` : ''}`);
870
726
  const incrementPercent = resolveIncrementPercent(botMeta, opts.incrementPercent);
871
727
  console.log(`Increment: ${incrementPercent}% (halfInc ${incrementPercent/2}%)${opts.incrementPercent == null && botMeta?.incrementPercent != null ? ' — from bot config' : opts.incrementPercent != null ? ' — from --increment' : ' — default'}`);
872
728
 
873
- console.log(`\nFetching fills from Kibana...`);
874
- const fills = await fetchAllFills({}, accountId, gte, lte);
729
+ console.log(`\nFetching fills and order updates from Kibana...`);
730
+ const [fills, orderUpdates] = await Promise.all([
731
+ fetchAllFills({}, accountId, gte, lte),
732
+ fetchAllOrderUpdates({}, accountId, gte, lte),
733
+ ]);
875
734
  console.log(` Fetched ${fills.length} fill_order operation(s)`);
735
+ console.log(` Fetched ${orderUpdates.length} limit_order_update operation(s)`);
876
736
 
877
737
  if (fills.length === 0) {
878
738
  console.log('\nNo fills in range — nothing to check.');
879
739
  process.exit(0);
880
740
  }
881
741
 
882
- await resolveAssetPrecisions(fills, opts.node);
742
+ await resolveAssetPrecisions(fills);
883
743
 
884
744
  const { trades, skipped } = classifyFills(fills);
885
745
  console.log(` Classified ${trades.length} trade(s)${skipped > 0 ? `, ${skipped} skipped (unknown precision)` : ''}`);
@@ -905,8 +765,8 @@ async function main() {
905
765
  if (opts.perFill) {
906
766
  items = trades;
907
767
  } else {
908
- const orders = aggregateByOrder(trades);
909
- console.log(` Aggregated into ${orders.length} order(s) (multi-fill orders collapsed by weighted avg price)`);
768
+ const orders = aggregateByOrder(trades, orderUpdates);
769
+ console.log(` Aggregated into ${orders.length} order epoch(s) (partial fills collapsed; repriced orders kept separate)`);
910
770
  items = orders;
911
771
  }
912
772