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
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * SHARED CHAIN POOL (analysis)
5
+ *
6
+ * Single source of truth for the node list and the ephemeral read-only
7
+ * connection used by analysis tools for small on-chain lookups (account name
8
+ * resolution, asset precision resolution).
9
+ *
10
+ * Analysis tools deliberately do NOT use the production singleton client
11
+ * (modules/bitshares_client.ts): they are short-lived processes and must never
12
+ * disturb a running bot's connection state. Instead each lookup opens a fresh
13
+ * read-only client over the built-in node pool, which the native transport
14
+ * races in parallel and then retries sequentially — so one dead node is not
15
+ * fatal.
16
+ */
17
+
18
+ import * as C from '../modules/constants.js';
19
+
20
+ /** Nodes for ephemeral read-only analysis connections (node management pool). */
21
+ function defaultNodePool(): string[] {
22
+ return [...C.NODE_MANAGEMENT.DEFAULT_NODES];
23
+ }
24
+
25
+ /**
26
+ * Run `fn` against a fresh read-only client connected over the built-in pool.
27
+ * Transport INFO logging is silenced for the duration of the short-lived
28
+ * connection and the client is always disconnected afterwards.
29
+ */
30
+ async function withReadOnlyClient<T>(fn: (client: any) => Promise<T>): Promise<T> {
31
+ const { createReadOnlyClient } = await import('../modules/bitshares-native/index.js');
32
+ const client = createReadOnlyClient({ nodes: defaultNodePool() });
33
+ // Suppress transport INFO logs during ephemeral connection:
34
+ // bitshares-native transport logger (new Logger('Transport')) writes
35
+ // "[timestamp] [INFO] [Transport] ..." — silence by raising log level.
36
+ const prevLevel = process.env.LOG_LEVEL;
37
+ process.env.LOG_LEVEL = 'warn';
38
+ try {
39
+ await client.connect();
40
+ return await fn(client);
41
+ } finally {
42
+ try { client.disconnect(); } catch (_) { /* best-effort cleanup */ }
43
+ process.env.LOG_LEVEL = prevLevel;
44
+ }
45
+ }
46
+
47
+ export { defaultNodePool, withReadOnlyClient };
@@ -10,26 +10,39 @@ const { ensureDir } = getStorage();
10
10
  * Chart utilities for analysis HTML generators.
11
11
  */
12
12
 
13
- // Runtime assets every generated chart references as ../uplot/* relative to
14
- // its own directory (charts live in <root>/charts, assets in <root>/uplot).
15
- const UPLOT_RUNTIME_FILES = ['uPlot.iife.min.js', 'uPlot.min.css'];
13
+ // Vendored uPlot runtime assets are inlined directly into every generated chart
14
+ // (see uplotInlineTags below), so each chart is a single self-contained file
15
+ // that renders anywhere: no DEXBot2 install, no sibling uplot/ dir, no CDN, no
16
+ // network. The vendored originals always live with the code under the analysis
17
+ // assets dir.
18
+ let _uplotRuntime: { css: string; js: string } | null = null;
19
+
20
+ function getUplotRuntime() {
21
+ if (!_uplotRuntime) {
22
+ const dir = PATHS.ANALYSIS.ASSETS_DIR;
23
+ _uplotRuntime = {
24
+ css: fs.readFileSync(path.join(dir, 'uPlot.min.css'), 'utf8'),
25
+ js: fs.readFileSync(path.join(dir, 'uPlot.iife.min.js'), 'utf8'),
26
+ };
27
+ }
28
+ return _uplotRuntime;
29
+ }
16
30
 
17
31
  /**
18
- * Generated HTML loads uPlot from a sibling `uplot/` dir next to the charts
19
- * dir. In a source checkout that dir is the vendored original; anywhere else
20
- * (relocated analysis root under npm/home profiles, or a custom --chart path)
21
- * materialize a copy so the relative refs keep resolving.
32
+ * Inline <style>/<script> tags carrying the vendored uPlot runtime. Embedding
33
+ * the source (not a relative <script src>) makes a chart portable: it keeps
34
+ * rendering after being copied, mailed, or reopened on a machine without a
35
+ * DEXBot2 installation. The inline script must be the first script that runs,
36
+ * placed before any chart-initialization code, since uPlot must exist first.
37
+ * Both the minified JS (no `</script>`) and CSS (no `</style>`) are safe to
38
+ * embed verbatim.
22
39
  */
23
- function ensureSiblingUplotAssets(chartDir: any) {
24
- const targetDir = path.join(path.dirname(path.resolve(chartDir)), 'uplot');
25
- const sourceDir = PATHS.ANALYSIS.ASSETS_DIR;
26
- if (targetDir === path.resolve(sourceDir)) return;
27
- if (UPLOT_RUNTIME_FILES.every((f) => fs.existsSync(path.join(targetDir, f)))) return;
28
- if (!fs.existsSync(sourceDir)) return;
29
- fs.mkdirSync(targetDir, { recursive: true });
30
- for (const f of UPLOT_RUNTIME_FILES) {
31
- fs.copyFileSync(path.join(sourceDir, f), path.join(targetDir, f));
32
- }
40
+ function uplotInlineTags({ css = true, js = true } = {}) {
41
+ const { css: cssCode, js: jsCode } = getUplotRuntime();
42
+ const parts: string[] = [];
43
+ if (css && cssCode) parts.push(`<style>\n${cssCode}\n</style>`);
44
+ if (js && jsCode) parts.push(`<script>\n${jsCode}\n</script>`);
45
+ return parts.join('\n ');
33
46
  }
34
47
 
35
48
 
@@ -58,9 +71,9 @@ function toFileUrl(filePath: any): string {
58
71
  return `file://${path.resolve(String(filePath))}`;
59
72
  }
60
73
 
61
- function writeChartFile(filePath: any, html: any) { const chartDir = path.dirname(filePath);
74
+ function writeChartFile(filePath: any, html: any) {
75
+ const chartDir = path.dirname(filePath);
62
76
  if (!fs.existsSync(chartDir)) ensureDir(chartDir);
63
- ensureSiblingUplotAssets(chartDir);
64
77
  // Atomic write (tmp + rename, matching the production storage adapter
65
78
  // pattern) so a crash mid-write can never leave a truncated chart file.
66
79
  const tmpPath = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 10)}.tmp`;
@@ -193,5 +206,5 @@ function bindPan(chart) {
193
206
  }
194
207
  `;
195
208
 
196
- export { escapeHtml, serializeJsonForScript, toEpochSeconds, writeChartFile, toFileUrl, embedFunctionSources, UPLOT_SHARED_SCRIPT }
209
+ export { escapeHtml, serializeJsonForScript, toEpochSeconds, writeChartFile, toFileUrl, embedFunctionSources, UPLOT_SHARED_SCRIPT, uplotInlineTags }
197
210
 
@@ -4,7 +4,7 @@
4
4
  import fs from 'node:fs';
5
5
  import path from 'node:path';
6
6
  import { pathToFileURL } from 'node:url';
7
- import { escapeHtml, serializeJsonForScript, toEpochSeconds, UPLOT_SHARED_SCRIPT } from './chart_utils.js';
7
+ import { escapeHtml, serializeJsonForScript, toEpochSeconds, UPLOT_SHARED_SCRIPT, uplotInlineTags } from './chart_utils.js';
8
8
  import { getStorage } from '../modules/storage/index.js';
9
9
  const { ensureDir, readJSON } = getStorage();
10
10
  import { fixedTo } from '../modules/order/utils/math.js';
@@ -225,8 +225,7 @@ function generateHTML(data: any, title: string) {
225
225
  <meta name="darkreader-lock">
226
226
  <meta name="color-scheme" content="dark">
227
227
  <title>${escapeHtml(title)}</title>
228
- <link rel="stylesheet" href="../uplot/uPlot.min.css">
229
- <script src="../uplot/uPlot.iife.min.js"></script>
228
+ ${uplotInlineTags()}
230
229
  <style>
231
230
  :root { color-scheme: dark; }
232
231
  * { box-sizing: border-box; margin: 0; padding: 0; }
@@ -0,0 +1,234 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * SHARED FILLS SOURCE (analysis)
5
+ *
6
+ * Single implementation of the Kibana `fill_order` pipeline and the asset
7
+ * precision table/cache used by the fill-based analysis tools. Both
8
+ * trade_profitability.ts and grid_correction_check.ts carried verbatim copies
9
+ * of this (43-entry ASSETS table, toReal/assetPrec helpers, on-chain precision
10
+ * resolution, query builder and paginated fetch); all of it lives here now.
11
+ */
12
+
13
+ import * as KC from '../market_adapter/core/kibana_client.js';
14
+ import { withReadOnlyClient } from './chain_pool.js';
15
+
16
+ const { kibanaSearch, DEFAULT_CONFIG: BASE_CONFIG } = KC;
17
+
18
+ /** operation_type of a fill_order operation in the chain history index. */
19
+ const OP_FILL_ORDER = 4;
20
+ const BTS_ID = '1.3.0';
21
+
22
+ interface AssetInfo {
23
+ symbol: string;
24
+ precision: number;
25
+ }
26
+
27
+ interface AssetAmount {
28
+ amount: number;
29
+ asset_id: string;
30
+ }
31
+
32
+ interface FillRecord {
33
+ time: string;
34
+ blockNum: number;
35
+ opNum: number;
36
+ orderId: string;
37
+ accountId: string;
38
+ pays: AssetAmount;
39
+ receives: AssetAmount;
40
+ fee: AssetAmount;
41
+ isMaker: boolean;
42
+ sort: any[];
43
+ }
44
+
45
+ const ASSETS: Record<string, AssetInfo> = {
46
+ '1.3.0': { symbol: 'BTS', precision: 5 },
47
+ '1.3.118': { symbol: 'GBP', precision: 4 },
48
+ '1.3.119': { symbol: 'JPY', precision: 2 },
49
+ '1.3.120': { symbol: 'EUR', precision: 4 },
50
+ '1.3.1325': { symbol: 'RUBLE', precision: 5 },
51
+ '1.3.2512': { symbol: 'EVRAZ', precision: 4 },
52
+ '1.3.3291': { symbol: 'TWENTIX', precision: 5 },
53
+ '1.3.4099': { symbol: 'XBTSX.STH', precision: 6 },
54
+ '1.3.4156': { symbol: 'XBTSX.DOGE', precision: 5 },
55
+ '1.3.4157': { symbol: 'XBTSX.BTC', precision: 8 },
56
+ '1.3.4159': { symbol: 'XBTSX.LTC', precision: 8 },
57
+ '1.3.4176': { symbol: 'XBTSX.DASH', precision: 8 },
58
+ '1.3.4274': { symbol: 'XBTSX.BCH', precision: 8 },
59
+ '1.3.4760': { symbol: 'XBTSX.ETH', precision: 7 },
60
+ '1.3.5537': { symbol: 'IOB.XRP', precision: 4 },
61
+ '1.3.5541': { symbol: 'XBTSX.BNB', precision: 7 },
62
+ '1.3.5589': { symbol: 'XBTSX.USDT', precision: 6 },
63
+ '1.3.5641': { symbol: 'HONEST.CNY', precision: 4 },
64
+ '1.3.5649': { symbol: 'HONEST.USD', precision: 4 },
65
+ '1.3.5650': { symbol: 'HONEST.BTC', precision: 8 },
66
+ '1.3.5659': { symbol: 'HONEST.ETH', precision: 6 },
67
+ '1.3.5870': { symbol: 'XBTSX.FIL', precision: 6 },
68
+ '1.3.5887': { symbol: 'XBTSX.RUB', precision: 4 },
69
+ '1.3.5902': { symbol: 'XBTSX.USDC', precision: 6 },
70
+ '1.3.6013': { symbol: 'XBTSX.HIVE', precision: 6 },
71
+ '1.3.6124': { symbol: 'XBTSX.AVAX', precision: 6 },
72
+ '1.3.6139': { symbol: 'XBTSX.XAUT', precision: 6 },
73
+ '1.3.6166': { symbol: 'XBTSX.MATIC', precision: 5 },
74
+ '1.3.6241': { symbol: 'XBTSX.ETC', precision: 7 },
75
+ '1.3.6268': { symbol: 'BTWTY.EOS', precision: 4 },
76
+ '1.3.6301': { symbol: 'HONEST.MONEY', precision: 8 },
77
+ '1.3.6304': { symbol: 'HONEST.ADA', precision: 8 },
78
+ '1.3.6305': { symbol: 'HONEST.DOT', precision: 8 },
79
+ '1.3.6309': { symbol: 'HONEST.ATOM', precision: 8 },
80
+ '1.3.6311': { symbol: 'HONEST.ALGO', precision: 8 },
81
+ '1.3.6312': { symbol: 'HONEST.FIL', precision: 8 },
82
+ '1.3.6313': { symbol: 'HONEST.EOS', precision: 8 },
83
+ '1.3.6315': { symbol: 'HONEST.EUR', precision: 4 },
84
+ '1.3.6316': { symbol: 'HONEST.GBP', precision: 4 },
85
+ '1.3.6317': { symbol: 'HONEST.JPY', precision: 4 },
86
+ '1.3.6444': { symbol: 'IOB.XLM', precision: 4 },
87
+ '1.3.6573': { symbol: 'XBTSX.DAI', precision: 6 },
88
+ '1.3.6620': { symbol: 'XBTSX.A', precision: 6 },
89
+ '1.3.6627': { symbol: 'XBTSX.LINK', precision: 6 },
90
+ };
91
+
92
+ /** Precisions learned on-chain this run (populated by resolveAssetPrecisions). */
93
+ const resolvedPrecisions: Record<string, number> = {};
94
+
95
+ function assetSymbol(id: string): string {
96
+ return ASSETS[id]?.symbol ?? id;
97
+ }
98
+
99
+ function assetPrec(id: string): number | undefined {
100
+ return ASSETS[id]?.precision ?? resolvedPrecisions[id];
101
+ }
102
+
103
+ function toReal(amount: number, assetId: string): number {
104
+ const p = assetPrec(assetId);
105
+ if (p === undefined) return NaN;
106
+ return amount / Math.pow(10, p);
107
+ }
108
+
109
+ /**
110
+ * Collect all unique non-BTS asset IDs from fills, resolve unknown precisions
111
+ * from the blockchain, and populate the runtime cache.
112
+ */
113
+ async function resolveAssetPrecisions(fills: FillRecord[]): Promise<void> {
114
+ const unknownIds = new Set<string>();
115
+ for (const f of fills) {
116
+ for (const id of [f.pays.asset_id, f.receives.asset_id, f.fee.asset_id]) {
117
+ if (id !== BTS_ID && !(id in ASSETS) && !(id in resolvedPrecisions)) {
118
+ unknownIds.add(id);
119
+ }
120
+ }
121
+ }
122
+ if (unknownIds.size === 0) return;
123
+
124
+ const ids = [...unknownIds];
125
+ console.log(` Resolving ${ids.length} unknown asset(s) from blockchain...`);
126
+ try {
127
+ await withReadOnlyClient(async (client) => {
128
+ const assets = await client.db('get_assets', [ids]);
129
+ if (Array.isArray(assets)) {
130
+ for (const asset of assets) {
131
+ if (asset?.id && asset.precision != null) {
132
+ resolvedPrecisions[asset.id] = asset.precision;
133
+ console.log(` ${asset.id} → ${asset.symbol || '?'} (precision ${asset.precision})`);
134
+ }
135
+ }
136
+ }
137
+ const missing = ids.filter(id => !(id in resolvedPrecisions));
138
+ if (missing.length > 0) {
139
+ console.warn(` [warn] ${missing.length} asset(s) not found on chain: ${missing.join(', ')}. Fills referencing them will be skipped.`);
140
+ }
141
+ });
142
+ } catch (e: any) {
143
+ console.warn(` [warn] Asset resolution failed: ${e.message}. Fills with unknown assets will be skipped.`);
144
+ }
145
+ }
146
+
147
+ function buildFillQuery(accountId: string, gte: string, lte: string, size: number) {
148
+ return {
149
+ size,
150
+ track_total_hits: false,
151
+ _source: [
152
+ 'block_data.block_time',
153
+ 'block_data.block_num',
154
+ 'operation_id_num',
155
+ 'operation_history.op_object.pays',
156
+ 'operation_history.op_object.receives',
157
+ 'operation_history.op_object.fee',
158
+ 'operation_history.op_object.order_id',
159
+ 'operation_history.op_object.account_id',
160
+ 'operation_history.op_object.is_maker',
161
+ ],
162
+ query: {
163
+ bool: {
164
+ filter: [
165
+ { term: { operation_type: OP_FILL_ORDER } },
166
+ { term: { 'operation_history.op_object.account_id.keyword': accountId } },
167
+ { range: { 'block_data.block_time': { gte, lte } } },
168
+ ],
169
+ },
170
+ },
171
+ sort: [
172
+ { 'block_data.block_time': { order: 'asc' } },
173
+ { operation_id_num: { order: 'asc' } },
174
+ ],
175
+ };
176
+ }
177
+
178
+ /** Fetch every fill_order for an account in [gte, lte], paginated via search_after. */
179
+ async function fetchAllFills(config: any, accountId: string, gte: string, lte: string): Promise<FillRecord[]> {
180
+ const pageSize = 10000;
181
+ const fills: FillRecord[] = [];
182
+ let searchAfter: any[] | null = null;
183
+ const cfg = { ...BASE_CONFIG, timeout: 60000, ...config };
184
+
185
+ while (true) {
186
+ const query = buildFillQuery(accountId, gte, lte, pageSize);
187
+ if (searchAfter) (query as any).search_after = searchAfter;
188
+
189
+ const result: any = await kibanaSearch(cfg, query);
190
+ const hits = result?.hits?.hits ?? [];
191
+ if (!hits.length) break;
192
+
193
+ for (const hit of hits) {
194
+ const src = hit?._source;
195
+ const op = src?.operation_history?.op_object;
196
+ if (!op || !op.pays || !op.receives) continue;
197
+
198
+ fills.push({
199
+ time: src.block_data?.block_time ?? '',
200
+ blockNum: src.block_data?.block_num ?? 0,
201
+ opNum: Number(src.operation_id_num ?? 0),
202
+ orderId: op.order_id ?? '',
203
+ accountId: op.account_id ?? '',
204
+ pays: { amount: Number(op.pays.amount ?? 0), asset_id: op.pays.asset_id ?? '' },
205
+ receives: { amount: Number(op.receives.amount ?? 0), asset_id: op.receives.asset_id ?? '' },
206
+ fee: { amount: Number(op.fee?.amount ?? 0), asset_id: op.fee?.asset_id ?? '' },
207
+ isMaker: op.is_maker ?? false,
208
+ sort: hit.sort,
209
+ });
210
+ }
211
+
212
+ if (hits.length < pageSize) break;
213
+ searchAfter = hits[hits.length - 1].sort;
214
+ if (!Array.isArray(searchAfter)) break;
215
+ }
216
+
217
+ return fills;
218
+ }
219
+
220
+ export {
221
+ BTS_ID,
222
+ OP_FILL_ORDER,
223
+ ASSETS,
224
+ resolvedPrecisions,
225
+ assetSymbol,
226
+ assetPrec,
227
+ toReal,
228
+ resolveAssetPrecisions,
229
+ buildFillQuery,
230
+ fetchAllFills,
231
+ AssetInfo,
232
+ AssetAmount,
233
+ FillRecord,
234
+ };