dexbot 1.1.10
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/LICENSE +21 -0
- package/README.md +287 -0
- package/claw/README.md +513 -0
- package/claw/scripts/memu_runner.py +385 -0
- package/claw/skills/bitshares-guide/SKILL.md +14 -0
- package/claw/skills/bitshares-guide/references/example-skill-shells.md +18 -0
- package/claw/skills/bitshares-guide/references/presentation-patterns.md +19 -0
- package/claw/skills/bitshares-guide/references/scope-guardrails.md +9 -0
- package/claw/skills/launcher-ops/SKILL.md +18 -0
- package/claw/skills/launcher-ops/references/launcher-workflow.md +23 -0
- package/claw/skills/margin-trading/SKILL.md +23 -0
- package/claw/skills/margin-trading/references/honest-asset-list.md +74 -0
- package/claw/skills/margin-trading/references/honest-assets.md +44 -0
- package/claw/skills/margin-trading/references/position-management.md +203 -0
- package/claw/skills/margin-trading/references/trading-concepts.md +80 -0
- package/claw/skills/memu-memory/SKILL.md +218 -0
- package/claw/skills/shared/references/js-automation-overview.md +24 -0
- package/claw/skills/shared/references/safety-and-staleness.md +11 -0
- package/claw/skills/shared/references/skill-boundaries.md +17 -0
- package/claw/skills/trend-detection/SKILL.md +33 -0
- package/claw/skills/trend-detection/agents/openai.yaml +3 -0
- package/claw/skills/trend-detection/references/service.md +45 -0
- package/dist/analysis/ama_fitting/analyze_ama_price_changes.d.ts +3 -0
- package/dist/analysis/ama_fitting/analyze_ama_price_changes.d.ts.map +1 -0
- package/dist/analysis/ama_fitting/analyze_ama_price_changes.js +149 -0
- package/dist/analysis/ama_fitting/analyze_ama_price_changes.js.map +1 -0
- package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.d.ts +3 -0
- package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.d.ts.map +1 -0
- package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js +387 -0
- package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js.map +1 -0
- package/dist/analysis/ama_fitting/calibrate_convergence_er.d.ts +3 -0
- package/dist/analysis/ama_fitting/calibrate_convergence_er.d.ts.map +1 -0
- package/dist/analysis/ama_fitting/calibrate_convergence_er.js +200 -0
- package/dist/analysis/ama_fitting/calibrate_convergence_er.js.map +1 -0
- package/dist/analysis/ama_fitting/fetch_lp_candles.d.ts +3 -0
- package/dist/analysis/ama_fitting/fetch_lp_candles.d.ts.map +1 -0
- package/dist/analysis/ama_fitting/fetch_lp_candles.js +212 -0
- package/dist/analysis/ama_fitting/fetch_lp_candles.js.map +1 -0
- package/dist/analysis/ama_fitting/generate_unified_comparison_chart.d.ts +31 -0
- package/dist/analysis/ama_fitting/generate_unified_comparison_chart.d.ts.map +1 -0
- package/dist/analysis/ama_fitting/generate_unified_comparison_chart.js +249 -0
- package/dist/analysis/ama_fitting/generate_unified_comparison_chart.js.map +1 -0
- package/dist/analysis/ama_fitting/optimizer_high_resolution.d.ts +2 -0
- package/dist/analysis/ama_fitting/optimizer_high_resolution.d.ts.map +1 -0
- package/dist/analysis/ama_fitting/optimizer_high_resolution.js +782 -0
- package/dist/analysis/ama_fitting/optimizer_high_resolution.js.map +1 -0
- package/dist/analysis/analyze_derivatives.d.ts +95 -0
- package/dist/analysis/analyze_derivatives.d.ts.map +1 -0
- package/dist/analysis/analyze_derivatives.js +295 -0
- package/dist/analysis/analyze_derivatives.js.map +1 -0
- package/dist/analysis/analyze_dynamic_weight.d.ts +14 -0
- package/dist/analysis/analyze_dynamic_weight.d.ts.map +1 -0
- package/dist/analysis/analyze_dynamic_weight.js +208 -0
- package/dist/analysis/analyze_dynamic_weight.js.map +1 -0
- package/dist/analysis/analyze_kalman.d.ts +13 -0
- package/dist/analysis/analyze_kalman.d.ts.map +1 -0
- package/dist/analysis/analyze_kalman.js +123 -0
- package/dist/analysis/analyze_kalman.js.map +1 -0
- package/dist/analysis/analyze_regime.d.ts +20 -0
- package/dist/analysis/analyze_regime.d.ts.map +1 -0
- package/dist/analysis/analyze_regime.js +139 -0
- package/dist/analysis/analyze_regime.js.map +1 -0
- package/dist/analysis/analyze_regime_windows.d.ts +19 -0
- package/dist/analysis/analyze_regime_windows.d.ts.map +1 -0
- package/dist/analysis/analyze_regime_windows.js +413 -0
- package/dist/analysis/analyze_regime_windows.js.map +1 -0
- package/dist/analysis/analyze_risk_profile.d.ts +3 -0
- package/dist/analysis/analyze_risk_profile.d.ts.map +1 -0
- package/dist/analysis/analyze_risk_profile.js +159 -0
- package/dist/analysis/analyze_risk_profile.js.map +1 -0
- package/dist/analysis/analyze_trade_heatmap.d.ts +3 -0
- package/dist/analysis/analyze_trade_heatmap.d.ts.map +1 -0
- package/dist/analysis/analyze_trade_heatmap.js +367 -0
- package/dist/analysis/analyze_trade_heatmap.js.map +1 -0
- package/dist/analysis/analyze_volatility.d.ts +22 -0
- package/dist/analysis/analyze_volatility.d.ts.map +1 -0
- package/dist/analysis/analyze_volatility.js +172 -0
- package/dist/analysis/analyze_volatility.js.map +1 -0
- package/dist/analysis/bot_fitting/backtest_ama_sweep.d.ts +99 -0
- package/dist/analysis/bot_fitting/backtest_ama_sweep.d.ts.map +1 -0
- package/dist/analysis/bot_fitting/backtest_ama_sweep.js +804 -0
- package/dist/analysis/bot_fitting/backtest_ama_sweep.js.map +1 -0
- package/dist/analysis/bot_fitting/backtest_bot_fitting.d.ts +2 -0
- package/dist/analysis/bot_fitting/backtest_bot_fitting.d.ts.map +1 -0
- package/dist/analysis/bot_fitting/backtest_bot_fitting.js +347 -0
- package/dist/analysis/bot_fitting/backtest_bot_fitting.js.map +1 -0
- package/dist/analysis/bot_fitting/shared_utils.d.ts +15 -0
- package/dist/analysis/bot_fitting/shared_utils.d.ts.map +1 -0
- package/dist/analysis/bot_fitting/shared_utils.js +48 -0
- package/dist/analysis/bot_fitting/shared_utils.js.map +1 -0
- package/dist/analysis/bot_key_utils.d.ts +16 -0
- package/dist/analysis/bot_key_utils.d.ts.map +1 -0
- package/dist/analysis/bot_key_utils.js +61 -0
- package/dist/analysis/bot_key_utils.js.map +1 -0
- package/dist/analysis/bot_usage/discover_bot_accounts.d.ts +3 -0
- package/dist/analysis/bot_usage/discover_bot_accounts.d.ts.map +1 -0
- package/dist/analysis/bot_usage/discover_bot_accounts.js +498 -0
- package/dist/analysis/bot_usage/discover_bot_accounts.js.map +1 -0
- package/dist/analysis/bot_usage/kibana_bot_queries.d.ts +546 -0
- package/dist/analysis/bot_usage/kibana_bot_queries.d.ts.map +1 -0
- package/dist/analysis/bot_usage/kibana_bot_queries.js +427 -0
- package/dist/analysis/bot_usage/kibana_bot_queries.js.map +1 -0
- package/dist/analysis/chart_utils.d.ts +13 -0
- package/dist/analysis/chart_utils.d.ts.map +1 -0
- package/dist/analysis/chart_utils.js +142 -0
- package/dist/analysis/chart_utils.js.map +1 -0
- package/dist/analysis/derivative_chart_generator.d.ts +18 -0
- package/dist/analysis/derivative_chart_generator.d.ts.map +1 -0
- package/dist/analysis/derivative_chart_generator.js +896 -0
- package/dist/analysis/derivative_chart_generator.js.map +1 -0
- package/dist/analysis/math_utils.d.ts +35 -0
- package/dist/analysis/math_utils.d.ts.map +1 -0
- package/dist/analysis/math_utils.js +121 -0
- package/dist/analysis/math_utils.js.map +1 -0
- package/dist/analysis/price_sources.d.ts +34 -0
- package/dist/analysis/price_sources.d.ts.map +1 -0
- package/dist/analysis/price_sources.js +103 -0
- package/dist/analysis/price_sources.js.map +1 -0
- package/dist/analysis/trade_profitability.d.ts +115 -0
- package/dist/analysis/trade_profitability.d.ts.map +1 -0
- package/dist/analysis/trade_profitability.js +1247 -0
- package/dist/analysis/trade_profitability.js.map +1 -0
- package/dist/analysis/tradingview/analyze_tradingview.d.ts +38 -0
- package/dist/analysis/tradingview/analyze_tradingview.d.ts.map +1 -0
- package/dist/analysis/tradingview/analyze_tradingview.js +212 -0
- package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -0
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts +9 -0
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts.map +1 -0
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js +1717 -0
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js.map +1 -0
- package/dist/analysis/trend_detection/derivative_analyzer.d.ts +250 -0
- package/dist/analysis/trend_detection/derivative_analyzer.d.ts.map +1 -0
- package/dist/analysis/trend_detection/derivative_analyzer.js +903 -0
- package/dist/analysis/trend_detection/derivative_analyzer.js.map +1 -0
- package/dist/analysis/trend_detection/dynamic_weight_chart_generator.d.ts +6 -0
- package/dist/analysis/trend_detection/dynamic_weight_chart_generator.d.ts.map +1 -0
- package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js +1449 -0
- package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js.map +1 -0
- package/dist/analysis/trend_detection/hurst_analyzer.d.ts +42 -0
- package/dist/analysis/trend_detection/hurst_analyzer.d.ts.map +1 -0
- package/dist/analysis/trend_detection/hurst_analyzer.js +159 -0
- package/dist/analysis/trend_detection/hurst_analyzer.js.map +1 -0
- package/dist/analysis/trend_detection/kalman_chart_generator.d.ts +6 -0
- package/dist/analysis/trend_detection/kalman_chart_generator.d.ts.map +1 -0
- package/dist/analysis/trend_detection/kalman_chart_generator.js +408 -0
- package/dist/analysis/trend_detection/kalman_chart_generator.js.map +1 -0
- package/dist/analysis/trend_detection/kalman_trend_analyzer.d.ts +103 -0
- package/dist/analysis/trend_detection/kalman_trend_analyzer.d.ts.map +1 -0
- package/dist/analysis/trend_detection/kalman_trend_analyzer.js +229 -0
- package/dist/analysis/trend_detection/kalman_trend_analyzer.js.map +1 -0
- package/dist/analysis/trend_detection/kalman_velocity_smoothing.d.ts +34 -0
- package/dist/analysis/trend_detection/kalman_velocity_smoothing.d.ts.map +1 -0
- package/dist/analysis/trend_detection/kalman_velocity_smoothing.js +89 -0
- package/dist/analysis/trend_detection/kalman_velocity_smoothing.js.map +1 -0
- package/dist/analysis/trend_detection/permutation_entropy_analyzer.d.ts +39 -0
- package/dist/analysis/trend_detection/permutation_entropy_analyzer.d.ts.map +1 -0
- package/dist/analysis/trend_detection/permutation_entropy_analyzer.js +132 -0
- package/dist/analysis/trend_detection/permutation_entropy_analyzer.js.map +1 -0
- package/dist/analysis/trend_detection/regime_chart_generator.d.ts +6 -0
- package/dist/analysis/trend_detection/regime_chart_generator.d.ts.map +1 -0
- package/dist/analysis/trend_detection/regime_chart_generator.js +345 -0
- package/dist/analysis/trend_detection/regime_chart_generator.js.map +1 -0
- package/dist/analysis/trend_detection/tests/test_kalman_trend.d.ts +2 -0
- package/dist/analysis/trend_detection/tests/test_kalman_trend.d.ts.map +1 -0
- package/dist/analysis/trend_detection/tests/test_kalman_trend.js +130 -0
- package/dist/analysis/trend_detection/tests/test_kalman_trend.js.map +1 -0
- package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.d.ts +2 -0
- package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.d.ts.map +1 -0
- package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.js +38 -0
- package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.js.map +1 -0
- package/dist/analysis/trend_detection/volatility_chart_generator.d.ts +6 -0
- package/dist/analysis/trend_detection/volatility_chart_generator.d.ts.map +1 -0
- package/dist/analysis/trend_detection/volatility_chart_generator.js +732 -0
- package/dist/analysis/trend_detection/volatility_chart_generator.js.map +1 -0
- package/dist/bot.d.ts +49 -0
- package/dist/bot.d.ts.map +1 -0
- package/dist/bot.js +221 -0
- package/dist/bot.js.map +1 -0
- package/dist/credential-daemon.d.ts +64 -0
- package/dist/credential-daemon.d.ts.map +1 -0
- package/dist/credential-daemon.js +987 -0
- package/dist/credential-daemon.js.map +1 -0
- package/dist/dexbot.d.ts +87 -0
- package/dist/dexbot.d.ts.map +1 -0
- package/dist/dexbot.js +1222 -0
- package/dist/dexbot.js.map +1 -0
- package/dist/market_adapter/ama_signal_runner.d.ts +3 -0
- package/dist/market_adapter/ama_signal_runner.d.ts.map +1 -0
- package/dist/market_adapter/ama_signal_runner.js +175 -0
- package/dist/market_adapter/ama_signal_runner.js.map +1 -0
- package/dist/market_adapter/candle_utils.d.ts +57 -0
- package/dist/market_adapter/candle_utils.d.ts.map +1 -0
- package/dist/market_adapter/candle_utils.js +252 -0
- package/dist/market_adapter/candle_utils.js.map +1 -0
- package/dist/market_adapter/core/asymmetric_bounds.d.ts +20 -0
- package/dist/market_adapter/core/asymmetric_bounds.d.ts.map +1 -0
- package/dist/market_adapter/core/asymmetric_bounds.js +88 -0
- package/dist/market_adapter/core/asymmetric_bounds.js.map +1 -0
- package/dist/market_adapter/core/config_normalizers.d.ts +10 -0
- package/dist/market_adapter/core/config_normalizers.d.ts.map +1 -0
- package/dist/market_adapter/core/config_normalizers.js +24 -0
- package/dist/market_adapter/core/config_normalizers.js.map +1 -0
- package/dist/market_adapter/core/kibana_candles.d.ts +74 -0
- package/dist/market_adapter/core/kibana_candles.d.ts.map +1 -0
- package/dist/market_adapter/core/kibana_candles.js +249 -0
- package/dist/market_adapter/core/kibana_candles.js.map +1 -0
- package/dist/market_adapter/core/kibana_client.d.ts +27 -0
- package/dist/market_adapter/core/kibana_client.d.ts.map +1 -0
- package/dist/market_adapter/core/kibana_client.js +120 -0
- package/dist/market_adapter/core/kibana_client.js.map +1 -0
- package/dist/market_adapter/core/kibana_market_candles.d.ts +143 -0
- package/dist/market_adapter/core/kibana_market_candles.d.ts.map +1 -0
- package/dist/market_adapter/core/kibana_market_candles.js +170 -0
- package/dist/market_adapter/core/kibana_market_candles.js.map +1 -0
- package/dist/market_adapter/core/market_adapter_service.d.ts +210 -0
- package/dist/market_adapter/core/market_adapter_service.d.ts.map +1 -0
- package/dist/market_adapter/core/market_adapter_service.js +2328 -0
- package/dist/market_adapter/core/market_adapter_service.js.map +1 -0
- package/dist/market_adapter/core/strategies/ama.d.ts +2 -0
- package/dist/market_adapter/core/strategies/ama.d.ts.map +1 -0
- package/dist/market_adapter/core/strategies/ama.js +138 -0
- package/dist/market_adapter/core/strategies/ama.js.map +1 -0
- package/dist/market_adapter/core/strategies/ama_slope_model.d.ts +50 -0
- package/dist/market_adapter/core/strategies/ama_slope_model.d.ts.map +1 -0
- package/dist/market_adapter/core/strategies/ama_slope_model.js +143 -0
- package/dist/market_adapter/core/strategies/ama_slope_model.js.map +1 -0
- package/dist/market_adapter/core/strategies/atr/calculator.d.ts +12 -0
- package/dist/market_adapter/core/strategies/atr/calculator.d.ts.map +1 -0
- package/dist/market_adapter/core/strategies/atr/calculator.js +47 -0
- package/dist/market_adapter/core/strategies/atr/calculator.js.map +1 -0
- package/dist/market_adapter/core/strategies/collateral_manager.d.ts +32 -0
- package/dist/market_adapter/core/strategies/collateral_manager.d.ts.map +1 -0
- package/dist/market_adapter/core/strategies/collateral_manager.js +68 -0
- package/dist/market_adapter/core/strategies/collateral_manager.js.map +1 -0
- package/dist/market_adapter/core/strategies/regime_gate.d.ts +66 -0
- package/dist/market_adapter/core/strategies/regime_gate.d.ts.map +1 -0
- package/dist/market_adapter/core/strategies/regime_gate.js +189 -0
- package/dist/market_adapter/core/strategies/regime_gate.js.map +1 -0
- package/dist/market_adapter/index.d.ts +10 -0
- package/dist/market_adapter/index.d.ts.map +1 -0
- package/dist/market_adapter/index.js +71 -0
- package/dist/market_adapter/index.js.map +1 -0
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.d.ts +3 -0
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.d.ts.map +1 -0
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js +1165 -0
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js.map +1 -0
- package/dist/market_adapter/inputs/fetch_lp_data.d.ts +54 -0
- package/dist/market_adapter/inputs/fetch_lp_data.d.ts.map +1 -0
- package/dist/market_adapter/inputs/fetch_lp_data.js +743 -0
- package/dist/market_adapter/inputs/fetch_lp_data.js.map +1 -0
- package/dist/market_adapter/inputs/kibana_source.d.ts +68 -0
- package/dist/market_adapter/inputs/kibana_source.d.ts.map +1 -0
- package/dist/market_adapter/inputs/kibana_source.js +143 -0
- package/dist/market_adapter/inputs/kibana_source.js.map +1 -0
- package/dist/market_adapter/interval_utils.d.ts +6 -0
- package/dist/market_adapter/interval_utils.d.ts.map +1 -0
- package/dist/market_adapter/interval_utils.js +14 -0
- package/dist/market_adapter/interval_utils.js.map +1 -0
- package/dist/market_adapter/log_format.d.ts +16 -0
- package/dist/market_adapter/log_format.d.ts.map +1 -0
- package/dist/market_adapter/log_format.js +72 -0
- package/dist/market_adapter/log_format.js.map +1 -0
- package/dist/market_adapter/lp_chart_core.d.ts +6 -0
- package/dist/market_adapter/lp_chart_core.d.ts.map +1 -0
- package/dist/market_adapter/lp_chart_core.js +874 -0
- package/dist/market_adapter/lp_chart_core.js.map +1 -0
- package/dist/market_adapter/lp_chart_runner.d.ts +135 -0
- package/dist/market_adapter/lp_chart_runner.d.ts.map +1 -0
- package/dist/market_adapter/lp_chart_runner.js +337 -0
- package/dist/market_adapter/lp_chart_runner.js.map +1 -0
- package/dist/market_adapter/lp_chart_strategy_loader.d.ts +23 -0
- package/dist/market_adapter/lp_chart_strategy_loader.d.ts.map +1 -0
- package/dist/market_adapter/lp_chart_strategy_loader.js +169 -0
- package/dist/market_adapter/lp_chart_strategy_loader.js.map +1 -0
- package/dist/market_adapter/market_adapter.d.ts +133 -0
- package/dist/market_adapter/market_adapter.d.ts.map +1 -0
- package/dist/market_adapter/market_adapter.js +1331 -0
- package/dist/market_adapter/market_adapter.js.map +1 -0
- package/dist/market_adapter/merge_lp_data.d.ts +3 -0
- package/dist/market_adapter/merge_lp_data.d.ts.map +1 -0
- package/dist/market_adapter/merge_lp_data.js +125 -0
- package/dist/market_adapter/merge_lp_data.js.map +1 -0
- package/dist/market_adapter/test_helpers.d.ts +14 -0
- package/dist/market_adapter/test_helpers.d.ts.map +1 -0
- package/dist/market_adapter/test_helpers.js +22 -0
- package/dist/market_adapter/test_helpers.js.map +1 -0
- package/dist/market_adapter/utils/adapter_client.d.ts +20 -0
- package/dist/market_adapter/utils/adapter_client.d.ts.map +1 -0
- package/dist/market_adapter/utils/adapter_client.js +88 -0
- package/dist/market_adapter/utils/adapter_client.js.map +1 -0
- package/dist/market_adapter/utils/atomic_write.d.ts +9 -0
- package/dist/market_adapter/utils/atomic_write.d.ts.map +1 -0
- package/dist/market_adapter/utils/atomic_write.js +13 -0
- package/dist/market_adapter/utils/atomic_write.js.map +1 -0
- package/dist/market_adapter/utils/chain.d.ts +50 -0
- package/dist/market_adapter/utils/chain.d.ts.map +1 -0
- package/dist/market_adapter/utils/chain.js +196 -0
- package/dist/market_adapter/utils/chain.js.map +1 -0
- package/dist/market_adapter/utils/data_discovery.d.ts +6 -0
- package/dist/market_adapter/utils/data_discovery.d.ts.map +1 -0
- package/dist/market_adapter/utils/data_discovery.js +35 -0
- package/dist/market_adapter/utils/data_discovery.js.map +1 -0
- package/dist/market_adapter/utils/dynamic_grid_snapshot.d.ts +15 -0
- package/dist/market_adapter/utils/dynamic_grid_snapshot.d.ts.map +1 -0
- package/dist/market_adapter/utils/dynamic_grid_snapshot.js +60 -0
- package/dist/market_adapter/utils/dynamic_grid_snapshot.js.map +1 -0
- package/dist/market_adapter/utils/file_lock.d.ts +19 -0
- package/dist/market_adapter/utils/file_lock.d.ts.map +1 -0
- package/dist/market_adapter/utils/file_lock.js +191 -0
- package/dist/market_adapter/utils/file_lock.js.map +1 -0
- package/dist/market_adapter/utils/native_history.d.ts +19 -0
- package/dist/market_adapter/utils/native_history.d.ts.map +1 -0
- package/dist/market_adapter/utils/native_history.js +172 -0
- package/dist/market_adapter/utils/native_history.js.map +1 -0
- package/dist/market_adapter/utils/paths.d.ts +5 -0
- package/dist/market_adapter/utils/paths.d.ts.map +1 -0
- package/dist/market_adapter/utils/paths.js +7 -0
- package/dist/market_adapter/utils/paths.js.map +1 -0
- package/dist/modules/account_bots.d.ts +112 -0
- package/dist/modules/account_bots.d.ts.map +1 -0
- package/dist/modules/account_bots.js +1259 -0
- package/dist/modules/account_bots.js.map +1 -0
- package/dist/modules/account_orders.d.ts +273 -0
- package/dist/modules/account_orders.d.ts.map +1 -0
- package/dist/modules/account_orders.js +662 -0
- package/dist/modules/account_orders.js.map +1 -0
- package/dist/modules/authority_resolver.d.ts +2 -0
- package/dist/modules/authority_resolver.d.ts.map +1 -0
- package/dist/modules/authority_resolver.js +231 -0
- package/dist/modules/authority_resolver.js.map +1 -0
- package/dist/modules/bitshares-native/chain_client.d.ts +38 -0
- package/dist/modules/bitshares-native/chain_client.d.ts.map +1 -0
- package/dist/modules/bitshares-native/chain_client.js +322 -0
- package/dist/modules/bitshares-native/chain_client.js.map +1 -0
- package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts +59 -0
- package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts.map +1 -0
- package/dist/modules/bitshares-native/crypto/ecc.browser.js +456 -0
- package/dist/modules/bitshares-native/crypto/ecc.browser.js.map +1 -0
- package/dist/modules/bitshares-native/crypto/ecc.d.ts +66 -0
- package/dist/modules/bitshares-native/crypto/ecc.d.ts.map +1 -0
- package/dist/modules/bitshares-native/crypto/ecc.js +557 -0
- package/dist/modules/bitshares-native/crypto/ecc.js.map +1 -0
- package/dist/modules/bitshares-native/crypto/ecc_selector.d.ts +2 -0
- package/dist/modules/bitshares-native/crypto/ecc_selector.d.ts.map +1 -0
- package/dist/modules/bitshares-native/crypto/ecc_selector.js +20 -0
- package/dist/modules/bitshares-native/crypto/ecc_selector.js.map +1 -0
- package/dist/modules/bitshares-native/index.d.ts +3 -0
- package/dist/modules/bitshares-native/index.d.ts.map +1 -0
- package/dist/modules/bitshares-native/index.js +32 -0
- package/dist/modules/bitshares-native/index.js.map +1 -0
- package/dist/modules/bitshares-native/resolvers.d.ts +40 -0
- package/dist/modules/bitshares-native/resolvers.d.ts.map +1 -0
- package/dist/modules/bitshares-native/resolvers.js +161 -0
- package/dist/modules/bitshares-native/resolvers.js.map +1 -0
- package/dist/modules/bitshares-native/serial/chain_constants.d.ts +56 -0
- package/dist/modules/bitshares-native/serial/chain_constants.d.ts.map +1 -0
- package/dist/modules/bitshares-native/serial/chain_constants.js +92 -0
- package/dist/modules/bitshares-native/serial/chain_constants.js.map +1 -0
- package/dist/modules/bitshares-native/serial/index.d.ts +10 -0
- package/dist/modules/bitshares-native/serial/index.d.ts.map +1 -0
- package/dist/modules/bitshares-native/serial/index.js +14 -0
- package/dist/modules/bitshares-native/serial/index.js.map +1 -0
- package/dist/modules/bitshares-native/serial/operations.d.ts +102 -0
- package/dist/modules/bitshares-native/serial/operations.d.ts.map +1 -0
- package/dist/modules/bitshares-native/serial/operations.js +615 -0
- package/dist/modules/bitshares-native/serial/operations.js.map +1 -0
- package/dist/modules/bitshares-native/serial/serializer.d.ts +64 -0
- package/dist/modules/bitshares-native/serial/serializer.d.ts.map +1 -0
- package/dist/modules/bitshares-native/serial/serializer.js +252 -0
- package/dist/modules/bitshares-native/serial/serializer.js.map +1 -0
- package/dist/modules/bitshares-native/serial/types.d.ts +120 -0
- package/dist/modules/bitshares-native/serial/types.d.ts.map +1 -0
- package/dist/modules/bitshares-native/serial/types.js +837 -0
- package/dist/modules/bitshares-native/serial/types.js.map +1 -0
- package/dist/modules/bitshares-native/signing_client.d.ts +6 -0
- package/dist/modules/bitshares-native/signing_client.d.ts.map +1 -0
- package/dist/modules/bitshares-native/signing_client.js +141 -0
- package/dist/modules/bitshares-native/signing_client.js.map +1 -0
- package/dist/modules/bitshares-native/subscriptions.d.ts +6 -0
- package/dist/modules/bitshares-native/subscriptions.d.ts.map +1 -0
- package/dist/modules/bitshares-native/subscriptions.js +865 -0
- package/dist/modules/bitshares-native/subscriptions.js.map +1 -0
- package/dist/modules/bitshares-native/transport.d.ts +51 -0
- package/dist/modules/bitshares-native/transport.d.ts.map +1 -0
- package/dist/modules/bitshares-native/transport.js +447 -0
- package/dist/modules/bitshares-native/transport.js.map +1 -0
- package/dist/modules/bitshares-native/tx/builder.d.ts +30 -0
- package/dist/modules/bitshares-native/tx/builder.d.ts.map +1 -0
- package/dist/modules/bitshares-native/tx/builder.js +262 -0
- package/dist/modules/bitshares-native/tx/builder.js.map +1 -0
- package/dist/modules/bitshares_client.d.ts +129 -0
- package/dist/modules/bitshares_client.d.ts.map +1 -0
- package/dist/modules/bitshares_client.js +627 -0
- package/dist/modules/bitshares_client.js.map +1 -0
- package/dist/modules/bot_settings.d.ts +33 -0
- package/dist/modules/bot_settings.d.ts.map +1 -0
- package/dist/modules/bot_settings.js +328 -0
- package/dist/modules/bot_settings.js.map +1 -0
- package/dist/modules/bots_file_lock.d.ts +88 -0
- package/dist/modules/bots_file_lock.d.ts.map +1 -0
- package/dist/modules/bots_file_lock.js +161 -0
- package/dist/modules/bots_file_lock.js.map +1 -0
- package/dist/modules/chain_keys.d.ts +302 -0
- package/dist/modules/chain_keys.d.ts.map +1 -0
- package/dist/modules/chain_keys.js +1095 -0
- package/dist/modules/chain_keys.js.map +1 -0
- package/dist/modules/chain_orders.d.ts +423 -0
- package/dist/modules/chain_orders.d.ts.map +1 -0
- package/dist/modules/chain_orders.js +1201 -0
- package/dist/modules/chain_orders.js.map +1 -0
- package/dist/modules/cli_whitelist_args.d.ts +6 -0
- package/dist/modules/cli_whitelist_args.d.ts.map +1 -0
- package/dist/modules/cli_whitelist_args.js +13 -0
- package/dist/modules/cli_whitelist_args.js.map +1 -0
- package/dist/modules/config.d.ts +57 -0
- package/dist/modules/config.d.ts.map +1 -0
- package/dist/modules/config.js +107 -0
- package/dist/modules/config.js.map +1 -0
- package/dist/modules/constants.d.ts +635 -0
- package/dist/modules/constants.d.ts.map +1 -0
- package/dist/modules/constants.js +1464 -0
- package/dist/modules/constants.js.map +1 -0
- package/dist/modules/cr_planner.d.ts +51 -0
- package/dist/modules/cr_planner.d.ts.map +1 -0
- package/dist/modules/cr_planner.js +299 -0
- package/dist/modules/cr_planner.js.map +1 -0
- package/dist/modules/credential_policy.d.ts +188 -0
- package/dist/modules/credential_policy.d.ts.map +1 -0
- package/dist/modules/credential_policy.js +1452 -0
- package/dist/modules/credential_policy.js.map +1 -0
- package/dist/modules/credential_runtime.d.ts +42 -0
- package/dist/modules/credential_runtime.d.ts.map +1 -0
- package/dist/modules/credential_runtime.js +148 -0
- package/dist/modules/credential_runtime.js.map +1 -0
- package/dist/modules/credential_session_cache.d.ts +36 -0
- package/dist/modules/credential_session_cache.d.ts.map +1 -0
- package/dist/modules/credential_session_cache.js +106 -0
- package/dist/modules/credential_session_cache.js.map +1 -0
- package/dist/modules/credit_runtime.d.ts +412 -0
- package/dist/modules/credit_runtime.d.ts.map +1 -0
- package/dist/modules/credit_runtime.js +2866 -0
- package/dist/modules/credit_runtime.js.map +1 -0
- package/dist/modules/crypto/browser_provider.d.ts +15 -0
- package/dist/modules/crypto/browser_provider.d.ts.map +1 -0
- package/dist/modules/crypto/browser_provider.js +113 -0
- package/dist/modules/crypto/browser_provider.js.map +1 -0
- package/dist/modules/crypto/index.d.ts +9 -0
- package/dist/modules/crypto/index.d.ts.map +1 -0
- package/dist/modules/crypto/index.js +44 -0
- package/dist/modules/crypto/index.js.map +1 -0
- package/dist/modules/crypto/node_provider.d.ts +15 -0
- package/dist/modules/crypto/node_provider.d.ts.map +1 -0
- package/dist/modules/crypto/node_provider.js +74 -0
- package/dist/modules/crypto/node_provider.js.map +1 -0
- package/dist/modules/crypto/provider.d.ts +34 -0
- package/dist/modules/crypto/provider.d.ts.map +1 -0
- package/dist/modules/crypto/provider.js +3 -0
- package/dist/modules/crypto/provider.js.map +1 -0
- package/dist/modules/crypto/pure_ripemd160.d.ts +8 -0
- package/dist/modules/crypto/pure_ripemd160.d.ts.map +1 -0
- package/dist/modules/crypto/pure_ripemd160.js +142 -0
- package/dist/modules/crypto/pure_ripemd160.js.map +1 -0
- package/dist/modules/crypto/pure_scrypt.d.ts +23 -0
- package/dist/modules/crypto/pure_scrypt.d.ts.map +1 -0
- package/dist/modules/crypto/pure_scrypt.js +161 -0
- package/dist/modules/crypto/pure_scrypt.js.map +1 -0
- package/dist/modules/crypto/pure_secp256k1.d.ts +29 -0
- package/dist/modules/crypto/pure_secp256k1.d.ts.map +1 -0
- package/dist/modules/crypto/pure_secp256k1.js +195 -0
- package/dist/modules/crypto/pure_secp256k1.js.map +1 -0
- package/dist/modules/crypto/sync.d.ts +13 -0
- package/dist/modules/crypto/sync.d.ts.map +1 -0
- package/dist/modules/crypto/sync.js +37 -0
- package/dist/modules/crypto/sync.js.map +1 -0
- package/dist/modules/dexbot_class.d.ts +1074 -0
- package/dist/modules/dexbot_class.d.ts.map +1 -0
- package/dist/modules/dexbot_class.js +4613 -0
- package/dist/modules/dexbot_class.js.map +1 -0
- package/dist/modules/dexbot_credential_client.d.ts +71 -0
- package/dist/modules/dexbot_credential_client.d.ts.map +1 -0
- package/dist/modules/dexbot_credential_client.js +224 -0
- package/dist/modules/dexbot_credential_client.js.map +1 -0
- package/dist/modules/dexbot_fill_runtime.d.ts +173 -0
- package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -0
- package/dist/modules/dexbot_fill_runtime.js +217 -0
- package/dist/modules/dexbot_fill_runtime.js.map +1 -0
- package/dist/modules/dexbot_maintenance_runtime.d.ts +279 -0
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -0
- package/dist/modules/dexbot_maintenance_runtime.js +1613 -0
- package/dist/modules/dexbot_maintenance_runtime.js.map +1 -0
- package/dist/modules/env.d.ts +12 -0
- package/dist/modules/env.d.ts.map +1 -0
- package/dist/modules/env.js +57 -0
- package/dist/modules/env.js.map +1 -0
- package/dist/modules/fund_registry.d.ts +96 -0
- package/dist/modules/fund_registry.d.ts.map +1 -0
- package/dist/modules/fund_registry.js +261 -0
- package/dist/modules/fund_registry.js.map +1 -0
- package/dist/modules/general_settings.d.ts +36 -0
- package/dist/modules/general_settings.d.ts.map +1 -0
- package/dist/modules/general_settings.js +63 -0
- package/dist/modules/general_settings.js.map +1 -0
- package/dist/modules/graceful_shutdown.d.ts +86 -0
- package/dist/modules/graceful_shutdown.d.ts.map +1 -0
- package/dist/modules/graceful_shutdown.js +164 -0
- package/dist/modules/graceful_shutdown.js.map +1 -0
- package/dist/modules/key_store.d.ts +60 -0
- package/dist/modules/key_store.d.ts.map +1 -0
- package/dist/modules/key_store.js +215 -0
- package/dist/modules/key_store.js.map +1 -0
- package/dist/modules/launcher/bot_supervisor.d.ts +84 -0
- package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -0
- package/dist/modules/launcher/bot_supervisor.js +1067 -0
- package/dist/modules/launcher/bot_supervisor.js.map +1 -0
- package/dist/modules/launcher/child_env.d.ts +9 -0
- package/dist/modules/launcher/child_env.d.ts.map +1 -0
- package/dist/modules/launcher/child_env.js +78 -0
- package/dist/modules/launcher/child_env.js.map +1 -0
- package/dist/modules/launcher/credential_bootstrap.d.ts +22 -0
- package/dist/modules/launcher/credential_bootstrap.d.ts.map +1 -0
- package/dist/modules/launcher/credential_bootstrap.js +303 -0
- package/dist/modules/launcher/credential_bootstrap.js.map +1 -0
- package/dist/modules/launcher/credential_daemon.d.ts +32 -0
- package/dist/modules/launcher/credential_daemon.d.ts.map +1 -0
- package/dist/modules/launcher/credential_daemon.js +173 -0
- package/dist/modules/launcher/credential_daemon.js.map +1 -0
- package/dist/modules/launcher/credential_secret.d.ts +6 -0
- package/dist/modules/launcher/credential_secret.d.ts.map +1 -0
- package/dist/modules/launcher/credential_secret.js +15 -0
- package/dist/modules/launcher/credential_secret.js.map +1 -0
- package/dist/modules/launcher/foreign_cred_daemon.d.ts +2 -0
- package/dist/modules/launcher/foreign_cred_daemon.d.ts.map +1 -0
- package/dist/modules/launcher/foreign_cred_daemon.js +166 -0
- package/dist/modules/launcher/foreign_cred_daemon.js.map +1 -0
- package/dist/modules/launcher/headless_password.d.ts +8 -0
- package/dist/modules/launcher/headless_password.d.ts.map +1 -0
- package/dist/modules/launcher/headless_password.js +33 -0
- package/dist/modules/launcher/headless_password.js.map +1 -0
- package/dist/modules/launcher/launch_modes.d.ts +36 -0
- package/dist/modules/launcher/launch_modes.d.ts.map +1 -0
- package/dist/modules/launcher/launch_modes.js +129 -0
- package/dist/modules/launcher/launch_modes.js.map +1 -0
- package/dist/modules/launcher/market_adapter_runtime.d.ts +43 -0
- package/dist/modules/launcher/market_adapter_runtime.d.ts.map +1 -0
- package/dist/modules/launcher/market_adapter_runtime.js +217 -0
- package/dist/modules/launcher/market_adapter_runtime.js.map +1 -0
- package/dist/modules/launcher/market_adapter_watchdog.d.ts +26 -0
- package/dist/modules/launcher/market_adapter_watchdog.d.ts.map +1 -0
- package/dist/modules/launcher/market_adapter_watchdog.js +214 -0
- package/dist/modules/launcher/market_adapter_watchdog.js.map +1 -0
- package/dist/modules/launcher/monolithic_runtime.d.ts +89 -0
- package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -0
- package/dist/modules/launcher/monolithic_runtime.js +425 -0
- package/dist/modules/launcher/monolithic_runtime.js.map +1 -0
- package/dist/modules/launcher/runtime_entry.d.ts +26 -0
- package/dist/modules/launcher/runtime_entry.d.ts.map +1 -0
- package/dist/modules/launcher/runtime_entry.js +49 -0
- package/dist/modules/launcher/runtime_entry.js.map +1 -0
- package/dist/modules/launcher/status_reporting.d.ts +48 -0
- package/dist/modules/launcher/status_reporting.d.ts.map +1 -0
- package/dist/modules/launcher/status_reporting.js +113 -0
- package/dist/modules/launcher/status_reporting.js.map +1 -0
- package/dist/modules/launcher/supervisor_control.d.ts +6 -0
- package/dist/modules/launcher/supervisor_control.d.ts.map +1 -0
- package/dist/modules/launcher/supervisor_control.js +59 -0
- package/dist/modules/launcher/supervisor_control.js.map +1 -0
- package/dist/modules/logger.d.ts +2 -0
- package/dist/modules/logger.d.ts.map +1 -0
- package/dist/modules/logger.js +71 -0
- package/dist/modules/logger.js.map +1 -0
- package/dist/modules/market_adapter_whitelist.d.ts +22 -0
- package/dist/modules/market_adapter_whitelist.d.ts.map +1 -0
- package/dist/modules/market_adapter_whitelist.js +82 -0
- package/dist/modules/market_adapter_whitelist.js.map +1 -0
- package/dist/modules/node_health_cache.d.ts +55 -0
- package/dist/modules/node_health_cache.d.ts.map +1 -0
- package/dist/modules/node_health_cache.js +134 -0
- package/dist/modules/node_health_cache.js.map +1 -0
- package/dist/modules/node_manager.d.ts +215 -0
- package/dist/modules/node_manager.d.ts.map +1 -0
- package/dist/modules/node_manager.js +628 -0
- package/dist/modules/node_manager.js.map +1 -0
- package/dist/modules/order/accounting.d.ts +349 -0
- package/dist/modules/order/accounting.d.ts.map +1 -0
- package/dist/modules/order/accounting.js +1191 -0
- package/dist/modules/order/accounting.js.map +1 -0
- package/dist/modules/order/async_lock.d.ts +126 -0
- package/dist/modules/order/async_lock.d.ts.map +1 -0
- package/dist/modules/order/async_lock.js +195 -0
- package/dist/modules/order/async_lock.js.map +1 -0
- package/dist/modules/order/export.d.ts +129 -0
- package/dist/modules/order/export.d.ts.map +1 -0
- package/dist/modules/order/export.js +316 -0
- package/dist/modules/order/export.js.map +1 -0
- package/dist/modules/order/format.d.ts +158 -0
- package/dist/modules/order/format.d.ts.map +1 -0
- package/dist/modules/order/format.js +232 -0
- package/dist/modules/order/format.js.map +1 -0
- package/dist/modules/order/grid.d.ts +549 -0
- package/dist/modules/order/grid.d.ts.map +1 -0
- package/dist/modules/order/grid.js +1746 -0
- package/dist/modules/order/grid.js.map +1 -0
- package/dist/modules/order/grid_reconcile.d.ts +154 -0
- package/dist/modules/order/grid_reconcile.d.ts.map +1 -0
- package/dist/modules/order/grid_reconcile.js +1192 -0
- package/dist/modules/order/grid_reconcile.js.map +1 -0
- package/dist/modules/order/index.d.ts +55 -0
- package/dist/modules/order/index.d.ts.map +1 -0
- package/dist/modules/order/index.js +79 -0
- package/dist/modules/order/index.js.map +1 -0
- package/dist/modules/order/logger.d.ts +136 -0
- package/dist/modules/order/logger.d.ts.map +1 -0
- package/dist/modules/order/logger.js +614 -0
- package/dist/modules/order/logger.js.map +1 -0
- package/dist/modules/order/logger_state.d.ts +133 -0
- package/dist/modules/order/logger_state.d.ts.map +1 -0
- package/dist/modules/order/logger_state.js +170 -0
- package/dist/modules/order/logger_state.js.map +1 -0
- package/dist/modules/order/manager.d.ts +413 -0
- package/dist/modules/order/manager.d.ts.map +1 -0
- package/dist/modules/order/manager.js +1605 -0
- package/dist/modules/order/manager.js.map +1 -0
- package/dist/modules/order/processed_fill_store.d.ts +135 -0
- package/dist/modules/order/processed_fill_store.d.ts.map +1 -0
- package/dist/modules/order/processed_fill_store.js +288 -0
- package/dist/modules/order/processed_fill_store.js.map +1 -0
- package/dist/modules/order/runner.d.ts +70 -0
- package/dist/modules/order/runner.d.ts.map +1 -0
- package/dist/modules/order/runner.js +140 -0
- package/dist/modules/order/runner.js.map +1 -0
- package/dist/modules/order/strategy.d.ts +196 -0
- package/dist/modules/order/strategy.d.ts.map +1 -0
- package/dist/modules/order/strategy.js +398 -0
- package/dist/modules/order/strategy.js.map +1 -0
- package/dist/modules/order/sync_engine.d.ts +386 -0
- package/dist/modules/order/sync_engine.d.ts.map +1 -0
- package/dist/modules/order/sync_engine.js +1459 -0
- package/dist/modules/order/sync_engine.js.map +1 -0
- package/dist/modules/order/utils/math.d.ts +677 -0
- package/dist/modules/order/utils/math.d.ts.map +1 -0
- package/dist/modules/order/utils/math.js +1172 -0
- package/dist/modules/order/utils/math.js.map +1 -0
- package/dist/modules/order/utils/order.d.ts +572 -0
- package/dist/modules/order/utils/order.d.ts.map +1 -0
- package/dist/modules/order/utils/order.js +1145 -0
- package/dist/modules/order/utils/order.js.map +1 -0
- package/dist/modules/order/utils/system.d.ts +268 -0
- package/dist/modules/order/utils/system.d.ts.map +1 -0
- package/dist/modules/order/utils/system.js +1261 -0
- package/dist/modules/order/utils/system.js.map +1 -0
- package/dist/modules/order/utils/validate.d.ts +280 -0
- package/dist/modules/order/utils/validate.d.ts.map +1 -0
- package/dist/modules/order/utils/validate.js +918 -0
- package/dist/modules/order/utils/validate.js.map +1 -0
- package/dist/modules/order/working_grid.d.ts +212 -0
- package/dist/modules/order/working_grid.d.ts.map +1 -0
- package/dist/modules/order/working_grid.js +279 -0
- package/dist/modules/order/working_grid.js.map +1 -0
- package/dist/modules/path_api.d.ts +32 -0
- package/dist/modules/path_api.d.ts.map +1 -0
- package/dist/modules/path_api.js +148 -0
- package/dist/modules/path_api.js.map +1 -0
- package/dist/modules/paths.d.ts +58 -0
- package/dist/modules/paths.d.ts.map +1 -0
- package/dist/modules/paths.js +82 -0
- package/dist/modules/paths.js.map +1 -0
- package/dist/modules/process_discovery.d.ts +58 -0
- package/dist/modules/process_discovery.d.ts.map +1 -0
- package/dist/modules/process_discovery.js +265 -0
- package/dist/modules/process_discovery.js.map +1 -0
- package/dist/modules/runtime.d.ts +34 -0
- package/dist/modules/runtime.d.ts.map +1 -0
- package/dist/modules/runtime.js +98 -0
- package/dist/modules/runtime.js.map +1 -0
- package/dist/modules/runtime_settings.d.ts +12 -0
- package/dist/modules/runtime_settings.d.ts.map +1 -0
- package/dist/modules/runtime_settings.js +122 -0
- package/dist/modules/runtime_settings.js.map +1 -0
- package/dist/modules/settings_merge.d.ts +38 -0
- package/dist/modules/settings_merge.d.ts.map +1 -0
- package/dist/modules/settings_merge.js +246 -0
- package/dist/modules/settings_merge.js.map +1 -0
- package/dist/modules/storage/browser_adapter.d.ts +56 -0
- package/dist/modules/storage/browser_adapter.d.ts.map +1 -0
- package/dist/modules/storage/browser_adapter.js +231 -0
- package/dist/modules/storage/browser_adapter.js.map +1 -0
- package/dist/modules/storage/index.d.ts +28 -0
- package/dist/modules/storage/index.d.ts.map +1 -0
- package/dist/modules/storage/index.js +52 -0
- package/dist/modules/storage/index.js.map +1 -0
- package/dist/modules/storage/node_adapter.d.ts +49 -0
- package/dist/modules/storage/node_adapter.d.ts.map +1 -0
- package/dist/modules/storage/node_adapter.js +167 -0
- package/dist/modules/storage/node_adapter.js.map +1 -0
- package/dist/modules/storage/types.d.ts +92 -0
- package/dist/modules/storage/types.d.ts.map +1 -0
- package/dist/modules/storage/types.js +3 -0
- package/dist/modules/storage/types.js.map +1 -0
- package/dist/modules/types.d.ts +1382 -0
- package/dist/modules/types.d.ts.map +1 -0
- package/dist/modules/types.js +11 -0
- package/dist/modules/types.js.map +1 -0
- package/dist/modules/utils/base58check.d.ts +12 -0
- package/dist/modules/utils/base58check.d.ts.map +1 -0
- package/dist/modules/utils/base58check.js +112 -0
- package/dist/modules/utils/base58check.js.map +1 -0
- package/dist/modules/utils/build_dir.d.ts +7 -0
- package/dist/modules/utils/build_dir.d.ts.map +1 -0
- package/dist/modules/utils/build_dir.js +8 -0
- package/dist/modules/utils/build_dir.js.map +1 -0
- package/dist/modules/utils/fs_utils.d.ts +25 -0
- package/dist/modules/utils/fs_utils.d.ts.map +1 -0
- package/dist/modules/utils/fs_utils.js +27 -0
- package/dist/modules/utils/fs_utils.js.map +1 -0
- package/dist/modules/utils/math_utils.d.ts +10 -0
- package/dist/modules/utils/math_utils.d.ts.map +1 -0
- package/dist/modules/utils/math_utils.js +17 -0
- package/dist/modules/utils/math_utils.js.map +1 -0
- package/dist/modules/validate_profiles.d.ts +37 -0
- package/dist/modules/validate_profiles.d.ts.map +1 -0
- package/dist/modules/validate_profiles.js +369 -0
- package/dist/modules/validate_profiles.js.map +1 -0
- package/dist/pm2.d.ts +167 -0
- package/dist/pm2.d.ts.map +1 -0
- package/dist/pm2.js +998 -0
- package/dist/pm2.js.map +1 -0
- package/dist/scripts/analyze-git.d.ts +11 -0
- package/dist/scripts/analyze-git.d.ts.map +1 -0
- package/dist/scripts/analyze-git.js +851 -0
- package/dist/scripts/analyze-git.js.map +1 -0
- package/dist/scripts/analyze-orders.d.ts +17 -0
- package/dist/scripts/analyze-orders.d.ts.map +1 -0
- package/dist/scripts/analyze-orders.js +1568 -0
- package/dist/scripts/analyze-orders.js.map +1 -0
- package/dist/scripts/diagnose-kibana-candles.d.ts +3 -0
- package/dist/scripts/diagnose-kibana-candles.d.ts.map +1 -0
- package/dist/scripts/diagnose-kibana-candles.js +82 -0
- package/dist/scripts/diagnose-kibana-candles.js.map +1 -0
- package/dist/scripts/diagnose-pool-history.d.ts +3 -0
- package/dist/scripts/diagnose-pool-history.d.ts.map +1 -0
- package/dist/scripts/diagnose-pool-history.js +296 -0
- package/dist/scripts/diagnose-pool-history.js.map +1 -0
- package/dist/scripts/divergence-calc.d.ts +26 -0
- package/dist/scripts/divergence-calc.d.ts.map +1 -0
- package/dist/scripts/divergence-calc.js +119 -0
- package/dist/scripts/divergence-calc.js.map +1 -0
- package/dist/scripts/generate_lp_chart.d.ts +31 -0
- package/dist/scripts/generate_lp_chart.d.ts.map +1 -0
- package/dist/scripts/generate_lp_chart.js +92 -0
- package/dist/scripts/generate_lp_chart.js.map +1 -0
- package/dist/scripts/generate_mainnet_corpus_report.d.ts +24 -0
- package/dist/scripts/generate_mainnet_corpus_report.d.ts.map +1 -0
- package/dist/scripts/generate_mainnet_corpus_report.js +237 -0
- package/dist/scripts/generate_mainnet_corpus_report.js.map +1 -0
- package/dist/scripts/generate_market_adapter_whitelist.d.ts +2 -0
- package/dist/scripts/generate_market_adapter_whitelist.d.ts.map +1 -0
- package/dist/scripts/generate_market_adapter_whitelist.js +111 -0
- package/dist/scripts/generate_market_adapter_whitelist.js.map +1 -0
- package/dist/scripts/migrate_bot_keys.d.ts +6 -0
- package/dist/scripts/migrate_bot_keys.d.ts.map +1 -0
- package/dist/scripts/migrate_bot_keys.js +301 -0
- package/dist/scripts/migrate_bot_keys.js.map +1 -0
- package/dist/scripts/native_release_gates.d.ts +3 -0
- package/dist/scripts/native_release_gates.d.ts.map +1 -0
- package/dist/scripts/native_release_gates.js +52 -0
- package/dist/scripts/native_release_gates.js.map +1 -0
- package/dist/scripts/print_grid.d.ts +16 -0
- package/dist/scripts/print_grid.d.ts.map +1 -0
- package/dist/scripts/print_grid.js +80 -0
- package/dist/scripts/print_grid.js.map +1 -0
- package/dist/scripts/run-tests.d.ts +2 -0
- package/dist/scripts/run-tests.d.ts.map +1 -0
- package/dist/scripts/run-tests.js +49 -0
- package/dist/scripts/run-tests.js.map +1 -0
- package/dist/scripts/sync-version.d.ts +14 -0
- package/dist/scripts/sync-version.d.ts.map +1 -0
- package/dist/scripts/sync-version.js +158 -0
- package/dist/scripts/sync-version.js.map +1 -0
- package/dist/scripts/test-credit-renewal.d.ts +2 -0
- package/dist/scripts/test-credit-renewal.d.ts.map +1 -0
- package/dist/scripts/test-credit-renewal.js +329 -0
- package/dist/scripts/test-credit-renewal.js.map +1 -0
- package/dist/scripts/update.d.ts +24 -0
- package/dist/scripts/update.d.ts.map +1 -0
- package/dist/scripts/update.js +650 -0
- package/dist/scripts/update.js.map +1 -0
- package/dist/scripts/validate_bots.d.ts +16 -0
- package/dist/scripts/validate_bots.d.ts.map +1 -0
- package/dist/scripts/validate_bots.js +106 -0
- package/dist/scripts/validate_bots.js.map +1 -0
- package/dist/scripts/verify-browser-bundle.d.ts +22 -0
- package/dist/scripts/verify-browser-bundle.d.ts.map +1 -0
- package/dist/scripts/verify-browser-bundle.js +339 -0
- package/dist/scripts/verify-browser-bundle.js.map +1 -0
- package/dist/unlock.d.ts +48 -0
- package/dist/unlock.d.ts.map +1 -0
- package/dist/unlock.js +925 -0
- package/dist/unlock.js.map +1 -0
- package/package.json +112 -0
- package/scripts/README.md +356 -0
- package/scripts/clear-all.sh +195 -0
- package/scripts/clear-logs.sh +95 -0
- package/scripts/clear-market-adapter.sh +196 -0
- package/scripts/clear-orders.sh +113 -0
- package/scripts/create-bot-symlinks.sh +51 -0
- package/scripts/postinstall.js +9 -0
- package/scripts/reset-settings.sh +118 -0
|
@@ -0,0 +1,1568 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* DEXBot Order Analysis Script
|
|
5
|
+
*
|
|
6
|
+
* Analyzes all order files in profiles/orders/ sorted by modified date.
|
|
7
|
+
* Provides compact terminal output checking:
|
|
8
|
+
* - Real spread vs. target spread (including double-sided status)
|
|
9
|
+
* - Increment value % geometric consistency between grid slots
|
|
10
|
+
* - Total funds of AssetA and AssetB in the grid
|
|
11
|
+
* - Grid slot distribution (% near center) vs grid composition
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* tsx scripts/analyze-orders.ts # terminal output
|
|
15
|
+
* tsx scripts/analyze-orders.ts --export # standalone HTML report
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const { formatPrice6 } = require('../modules/order/format');
|
|
21
|
+
const { resolveConfiguredPriceBound } = require('../modules/order/utils/order');
|
|
22
|
+
const { ORDER_TYPES, ORDER_STATES, MARKET_ADAPTER } = require('../modules/constants');
|
|
23
|
+
const { PATHS } = require('../modules/paths');
|
|
24
|
+
const { getWhitelistFlags } = require('../modules/market_adapter_whitelist');
|
|
25
|
+
const { readJSON } = require('../modules/utils/fs_utils');
|
|
26
|
+
const ORDERS_DIR = PATHS.ORDERS_DIR;
|
|
27
|
+
const BOTS_CONFIG = PATHS.PROFILES.BOTS_JSON;
|
|
28
|
+
// Color codes for terminal output
|
|
29
|
+
const colors = {
|
|
30
|
+
reset: '\x1b[0m',
|
|
31
|
+
buy: '\x1b[92m', // green
|
|
32
|
+
sell: '\x1b[91m', // light red
|
|
33
|
+
buyDark: '\x1b[38;5;28m', // even darker green
|
|
34
|
+
sellDark: '\x1b[31m', // dark red
|
|
35
|
+
spread: '\x1b[93m', // yellow
|
|
36
|
+
cyan: '\x1b[38;5;87m', // bright cyan
|
|
37
|
+
gray: '\x1b[38;5;246m' // medium grey (lighter than bright black)
|
|
38
|
+
};
|
|
39
|
+
// Partial block characters for weight visualization (0-8 eighths height)
|
|
40
|
+
const partialBlocks = ['', 'ā', 'ā', 'ā', 'ā', 'ā
', 'ā', 'ā', 'ā'];
|
|
41
|
+
// Maximum age (ms) for a dynamic grid snapshot to be considered "recently available".
|
|
42
|
+
// The market adapter writes a fresh snapshot on every cycle; a gap larger than this
|
|
43
|
+
// typically means the adapter is stopped or stuck for that bot. We allow up to two
|
|
44
|
+
// full cycles of drift (one missed cycle + slack for slow processing) before falling
|
|
45
|
+
// back to the static-only display, so the live colors track the actual adapter
|
|
46
|
+
// cadence rather than a hand-tuned timeout.
|
|
47
|
+
const DYNAMIC_GRID_SNAPSHOT_MAX_AGE_MS = 2 * MARKET_ADAPTER.RUNTIME_DEFAULTS.pollSeconds * 1000;
|
|
48
|
+
// Tolerance (absolute weight delta) for treating the two live weights as equal
|
|
49
|
+
// when picking a color. Half a percentage point avoids noisy green/red flicker
|
|
50
|
+
// when the dynamic weight sits exactly on the static baseline.
|
|
51
|
+
const DYNAMIC_WEIGHT_EPSILON = 0.005;
|
|
52
|
+
// Bar width configuration (single source of truth)
|
|
53
|
+
const BAR_WIDTH = 51;
|
|
54
|
+
// Header width: prefix width (11 chars for " Slots: ") + bar width
|
|
55
|
+
const HEADER_WIDTH = 11 + BAR_WIDTH;
|
|
56
|
+
/**
|
|
57
|
+
* Utility Functions
|
|
58
|
+
* Helper functions for file I/O, formatting, and data retrieval
|
|
59
|
+
*/
|
|
60
|
+
function sanitizeKey(source) {
|
|
61
|
+
if (!source)
|
|
62
|
+
return 'bot';
|
|
63
|
+
return String(source)
|
|
64
|
+
.trim()
|
|
65
|
+
.toLowerCase()
|
|
66
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
67
|
+
.replace(/^-+|-+$/g, '') || 'bot';
|
|
68
|
+
}
|
|
69
|
+
function createBotKey(bot, index) {
|
|
70
|
+
if (bot && bot.name) {
|
|
71
|
+
return sanitizeKey(bot.name);
|
|
72
|
+
}
|
|
73
|
+
const identifier = bot && bot.assetA && bot.assetB
|
|
74
|
+
? `${bot.assetA}/${bot.assetB}`
|
|
75
|
+
: bot && bot.assetAId && bot.assetBId
|
|
76
|
+
? `${bot.assetAId}/${bot.assetBId}`
|
|
77
|
+
: `bot-${index}`;
|
|
78
|
+
return `${sanitizeKey(identifier)}-${index}`;
|
|
79
|
+
}
|
|
80
|
+
function hasOrderGrid(data) {
|
|
81
|
+
return Boolean(data && typeof data === 'object' && !Array.isArray(data) &&
|
|
82
|
+
data.meta && typeof data.meta === 'object' &&
|
|
83
|
+
Array.isArray(data.grid));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* isAmaGridPrice: Detect bots whose grid price follows the AMA (Kaufman) stream.
|
|
87
|
+
*
|
|
88
|
+
* Mirrors the predicate used in market_adapter.ts / unlock.ts / pm2.ts so the
|
|
89
|
+
* analyzer only attempts to load a dynamic grid snapshot for AMA bots. Non-AMA
|
|
90
|
+
* bots never have a meaningful dynamic grid file.
|
|
91
|
+
*/
|
|
92
|
+
function isAmaGridPrice(config) {
|
|
93
|
+
return resolveAmaKey(config) !== null;
|
|
94
|
+
}
|
|
95
|
+
// Mirrors MARKET_ADAPTER.DEFAULT_AMA_KEY in modules/constants.ts ā keep in sync.
|
|
96
|
+
function resolveAmaKey(config) {
|
|
97
|
+
if (!config || typeof config !== 'object')
|
|
98
|
+
return null;
|
|
99
|
+
const gridPrice = typeof config.gridPrice === 'string' ? config.gridPrice.trim().toLowerCase() : '';
|
|
100
|
+
if (!/^ama(?:[1-4])?$/.test(gridPrice))
|
|
101
|
+
return null;
|
|
102
|
+
if (gridPrice === 'ama')
|
|
103
|
+
return MARKET_ADAPTER.DEFAULT_AMA_KEY;
|
|
104
|
+
return gridPrice.toUpperCase();
|
|
105
|
+
}
|
|
106
|
+
function readDynamicGridSnapshot(botKey) {
|
|
107
|
+
if (!botKey)
|
|
108
|
+
return null;
|
|
109
|
+
try {
|
|
110
|
+
const filePath = path.join(ORDERS_DIR, `${botKey}.dynamicgrid.json`);
|
|
111
|
+
if (!fs.existsSync(filePath))
|
|
112
|
+
return null;
|
|
113
|
+
const data = readJSON(filePath);
|
|
114
|
+
return data && typeof data === 'object' ? data : null;
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* computeAsymmetricBoundsPrices: Mirror of applyAsymmetricBounds logic.
|
|
122
|
+
* Computes the resolved min/max prices after grid range scaling adjustment.
|
|
123
|
+
*/
|
|
124
|
+
function computeAsymmetricBoundsPrices(centerPrice, minPrice, maxPrice, trend, appliedAsymmetryFactor) {
|
|
125
|
+
if (!Number.isFinite(centerPrice) || centerPrice <= 0
|
|
126
|
+
|| !Number.isFinite(minPrice) || minPrice <= 0
|
|
127
|
+
|| !Number.isFinite(maxPrice) || maxPrice <= 0
|
|
128
|
+
|| !Number.isFinite(appliedAsymmetryFactor)
|
|
129
|
+
|| (trend !== 'UP' && trend !== 'DOWN')) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const baseMinDiv = centerPrice / minPrice;
|
|
133
|
+
const baseMaxMult = maxPrice / centerPrice;
|
|
134
|
+
let resolvedMinPrice, resolvedMaxPrice;
|
|
135
|
+
if (trend === 'DOWN') {
|
|
136
|
+
resolvedMinPrice = centerPrice / (baseMinDiv * (1 + appliedAsymmetryFactor));
|
|
137
|
+
resolvedMaxPrice = centerPrice * (baseMaxMult * (1 - appliedAsymmetryFactor));
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
resolvedMinPrice = centerPrice / (baseMinDiv * (1 - appliedAsymmetryFactor));
|
|
141
|
+
resolvedMaxPrice = centerPrice * (baseMaxMult * (1 + appliedAsymmetryFactor));
|
|
142
|
+
}
|
|
143
|
+
return { resolvedMinPrice, resolvedMaxPrice };
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* buildDynamicWeightInfo: Extract a display-ready market-adapter payload.
|
|
147
|
+
*
|
|
148
|
+
* Reads the latest AMA dynamic-grid snapshot for AMA-whitelisted bots. Dynamic
|
|
149
|
+
* weight values are attached only when the bot is also dynamic-weight
|
|
150
|
+
* whitelisted and the snapshot contains effective weights. AMA center and
|
|
151
|
+
* freshness status remain available for AMA-only bots.
|
|
152
|
+
*/
|
|
153
|
+
function buildDynamicWeightInfo(botKey, config) {
|
|
154
|
+
if (!isAmaGridPrice(config))
|
|
155
|
+
return null;
|
|
156
|
+
const whitelistFlags = getWhitelistFlags(botKey);
|
|
157
|
+
if (whitelistFlags.ama !== true)
|
|
158
|
+
return null;
|
|
159
|
+
const snapshot = readDynamicGridSnapshot(botKey);
|
|
160
|
+
if (!snapshot)
|
|
161
|
+
return null;
|
|
162
|
+
const dw = snapshot.dynamicWeights && typeof snapshot.dynamicWeights === 'object'
|
|
163
|
+
? snapshot.dynamicWeights
|
|
164
|
+
: null;
|
|
165
|
+
const hasDynamicWeightData = whitelistFlags.dynamicWeight === true
|
|
166
|
+
&& dw
|
|
167
|
+
&& dw.effectiveWeights
|
|
168
|
+
&& typeof dw.effectiveWeights === 'object';
|
|
169
|
+
let live = null;
|
|
170
|
+
let base = null;
|
|
171
|
+
if (hasDynamicWeightData) {
|
|
172
|
+
const effBuy = Number(dw.effectiveWeights.buy);
|
|
173
|
+
const effSell = Number(dw.effectiveWeights.sell);
|
|
174
|
+
if (Number.isFinite(effBuy) && Number.isFinite(effSell)) {
|
|
175
|
+
const baseFromSnapshot = dw.baseWeights && typeof dw.baseWeights === 'object' ? dw.baseWeights : null;
|
|
176
|
+
const baseBuy = baseFromSnapshot && Number.isFinite(Number(baseFromSnapshot.buy))
|
|
177
|
+
? Number(baseFromSnapshot.buy)
|
|
178
|
+
: (config.weightDistribution && Number.isFinite(Number(config.weightDistribution.buy))
|
|
179
|
+
? Number(config.weightDistribution.buy)
|
|
180
|
+
: null);
|
|
181
|
+
const baseSell = baseFromSnapshot && Number.isFinite(Number(baseFromSnapshot.sell))
|
|
182
|
+
? Number(baseFromSnapshot.sell)
|
|
183
|
+
: (config.weightDistribution && Number.isFinite(Number(config.weightDistribution.sell))
|
|
184
|
+
? Number(config.weightDistribution.sell)
|
|
185
|
+
: null);
|
|
186
|
+
if (Number.isFinite(baseBuy) && Number.isFinite(baseSell)) {
|
|
187
|
+
live = { buy: effBuy, sell: effSell };
|
|
188
|
+
base = { buy: baseBuy, sell: baseSell };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const updatedAtMs = Date.parse(String(snapshot.updatedAt || ''));
|
|
193
|
+
const isRecent = Number.isFinite(updatedAtMs)
|
|
194
|
+
&& (Date.now() - updatedAtMs) <= DYNAMIC_GRID_SNAPSHOT_MAX_AGE_MS;
|
|
195
|
+
// Fallback to root-level asymmetricBounds when dynamicWeights is absent.
|
|
196
|
+
// The market adapter writes this when asymmetricBounds: true, regardless of
|
|
197
|
+
// whether dynamicWeight is enabled for this bot.
|
|
198
|
+
const rootBounds = !dw && snapshot?.asymmetricBounds && typeof snapshot.asymmetricBounds === 'object'
|
|
199
|
+
? snapshot.asymmetricBounds
|
|
200
|
+
: null;
|
|
201
|
+
const amaCenterPrice = Number.isFinite(Number(snapshot.amaCenterPrice))
|
|
202
|
+
? Number(snapshot.amaCenterPrice)
|
|
203
|
+
: (Number.isFinite(Number(snapshot.gridCenterPrice)) ? Number(snapshot.gridCenterPrice) : null);
|
|
204
|
+
const rawAsymmetryFactor = dw && Number.isFinite(Number(dw.rawAsymmetryFactor))
|
|
205
|
+
? Number(dw.rawAsymmetryFactor)
|
|
206
|
+
: (rootBounds && Number.isFinite(Number(rootBounds.rawAsymmetryFactor))
|
|
207
|
+
? Number(rootBounds.rawAsymmetryFactor)
|
|
208
|
+
: null);
|
|
209
|
+
const appliedAsymmetryFactor = dw && Number.isFinite(Number(dw.appliedAsymmetryFactor))
|
|
210
|
+
? Number(dw.appliedAsymmetryFactor)
|
|
211
|
+
: (rootBounds && Number.isFinite(Number(rootBounds.appliedAsymmetryFactor))
|
|
212
|
+
? Number(rootBounds.appliedAsymmetryFactor)
|
|
213
|
+
: null);
|
|
214
|
+
const maxAsymmetryFactor = dw && Number.isFinite(Number(dw.maxAsymmetryFactor))
|
|
215
|
+
? Number(dw.maxAsymmetryFactor)
|
|
216
|
+
: null;
|
|
217
|
+
return {
|
|
218
|
+
live,
|
|
219
|
+
base,
|
|
220
|
+
dynamicWeightEnabled: whitelistFlags.dynamicWeight === true,
|
|
221
|
+
isReady: dw ? dw.isReady === true : false,
|
|
222
|
+
trend: dw && typeof dw.trend === 'string' ? dw.trend
|
|
223
|
+
: (rootBounds && typeof rootBounds.trend === 'string' ? rootBounds.trend : null),
|
|
224
|
+
finalOffset: dw && Number.isFinite(Number(dw.finalOffset)) ? Number(dw.finalOffset) : null,
|
|
225
|
+
amaCenterPrice,
|
|
226
|
+
centerPrice: Number.isFinite(Number(snapshot.centerPrice)) ? Number(snapshot.centerPrice) : null,
|
|
227
|
+
isRecent,
|
|
228
|
+
updatedAt: Number.isFinite(updatedAtMs) ? new Date(updatedAtMs) : null,
|
|
229
|
+
rawAsymmetryFactor,
|
|
230
|
+
appliedAsymmetryFactor,
|
|
231
|
+
maxAsymmetryFactor,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function isRealGridOrder(order) {
|
|
235
|
+
if (!order || typeof order !== 'object')
|
|
236
|
+
return false;
|
|
237
|
+
const hasRealState = order.state === ORDER_STATES.ACTIVE || order.state === ORDER_STATES.PARTIAL;
|
|
238
|
+
const hasRealType = order.type === ORDER_TYPES.BUY || order.type === ORDER_TYPES.SELL;
|
|
239
|
+
const hasOrderId = typeof order.orderId === 'string' && order.orderId.trim().length > 0;
|
|
240
|
+
return hasRealState
|
|
241
|
+
&& hasRealType
|
|
242
|
+
&& hasOrderId
|
|
243
|
+
&& Number(order.price) > 0
|
|
244
|
+
&& Number(order.size) > 0;
|
|
245
|
+
}
|
|
246
|
+
function getRealGridOrders(botData) {
|
|
247
|
+
return Array.isArray(botData?.grid) ? botData.grid.filter(isRealGridOrder) : [];
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* getModifiedTime: Get file modification timestamp
|
|
251
|
+
* Used to sort order files by most recently updated
|
|
252
|
+
* @param {string} filePath - Path to file
|
|
253
|
+
* @returns {Date} Modification time
|
|
254
|
+
*/
|
|
255
|
+
function getModifiedTime(filePath) {
|
|
256
|
+
return fs.statSync(filePath).mtime;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* formatPercent: Convert decimal to percentage string
|
|
260
|
+
* Example: 0.05 -> "5.00%"
|
|
261
|
+
* @param {number} value - Decimal value (0-1)
|
|
262
|
+
* @returns {string} Formatted percentage with 2 decimal places
|
|
263
|
+
*/
|
|
264
|
+
function formatPercent(value) {
|
|
265
|
+
return (value * 100).toFixed(2) + '%';
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* formatCurrency: Format a number to 5 significant digits without K/M abbreviation.
|
|
269
|
+
* Very small values (< 0.01) use 6 decimal places via formatPrice6.
|
|
270
|
+
* @param {number} value - Numeric value to format
|
|
271
|
+
* @returns {string} Formatted currency/quantity string
|
|
272
|
+
*/
|
|
273
|
+
function formatCurrency(value) {
|
|
274
|
+
if (value === 0)
|
|
275
|
+
return '0';
|
|
276
|
+
const abs = Math.abs(value);
|
|
277
|
+
if (abs < 0.1) {
|
|
278
|
+
if (abs >= 0.001)
|
|
279
|
+
return formatCurrency(value * 1000) + 'm';
|
|
280
|
+
if (abs >= 0.000001)
|
|
281
|
+
return formatCurrency(value * 1000000) + 'μ';
|
|
282
|
+
if (abs >= 1e-9)
|
|
283
|
+
return formatCurrency(value * 1e9) + 'n';
|
|
284
|
+
if (abs >= 1e-12)
|
|
285
|
+
return formatCurrency(value * 1e12) + 'p';
|
|
286
|
+
if (abs >= 1e-15)
|
|
287
|
+
return formatCurrency(value * 1e15) + 'f';
|
|
288
|
+
if (abs >= 1e-18)
|
|
289
|
+
return formatCurrency(value * 1e18) + 'a';
|
|
290
|
+
return formatPrice6(value);
|
|
291
|
+
}
|
|
292
|
+
let intDigits = Math.floor(Math.log10(abs)) + 1;
|
|
293
|
+
if (abs < 1)
|
|
294
|
+
intDigits = 1;
|
|
295
|
+
const formatted = intDigits >= 5
|
|
296
|
+
? String(Math.round(value))
|
|
297
|
+
: value.toFixed(5 - intDigits);
|
|
298
|
+
return intDigits >= 4
|
|
299
|
+
? formatted.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
|
300
|
+
: formatted;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* formatFundsValue: Format a fund amount with compact notation (K/M for ā„1000)
|
|
304
|
+
* and up to 5 significant figures, trimming uninformative trailing zeros.
|
|
305
|
+
* Examples:
|
|
306
|
+
* 194395 -> "194.4K"
|
|
307
|
+
* 10000 -> "10K"
|
|
308
|
+
* 1000 -> "1K"
|
|
309
|
+
* 332.33 -> "332.33"
|
|
310
|
+
* 10.389 -> "10.389"
|
|
311
|
+
* 1500000 -> "1.5M"
|
|
312
|
+
* @param {number} value
|
|
313
|
+
* @returns {string}
|
|
314
|
+
*/
|
|
315
|
+
function formatFundsValue(value) {
|
|
316
|
+
if (value === 0)
|
|
317
|
+
return '0';
|
|
318
|
+
const absValue = Math.abs(value);
|
|
319
|
+
let quotient;
|
|
320
|
+
let suffix = '';
|
|
321
|
+
if (absValue >= 1000000) {
|
|
322
|
+
quotient = value / 1000000;
|
|
323
|
+
suffix = 'M';
|
|
324
|
+
}
|
|
325
|
+
else if (absValue >= 1000) {
|
|
326
|
+
quotient = value / 1000;
|
|
327
|
+
suffix = 'K';
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
quotient = value;
|
|
331
|
+
}
|
|
332
|
+
const absQ = Math.abs(quotient);
|
|
333
|
+
const intDigits = Math.floor(Math.log10(Math.max(absQ, 1e-10))) + 1;
|
|
334
|
+
let formatted;
|
|
335
|
+
if (intDigits >= 5) {
|
|
336
|
+
formatted = String(Math.round(quotient));
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
const decimalPlaces = Math.max(0, 5 - intDigits);
|
|
340
|
+
formatted = quotient.toFixed(decimalPlaces);
|
|
341
|
+
formatted = formatted.replace(/(\.[0-9]*?)0+$/, '$1').replace(/\.$/, '');
|
|
342
|
+
}
|
|
343
|
+
return formatted + suffix;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* stripColorCodes: Remove ANSI color codes from a string
|
|
347
|
+
* @param {string} str - String that may contain color codes
|
|
348
|
+
* @returns {string} String without color codes
|
|
349
|
+
*/
|
|
350
|
+
function stripColorCodes(str) {
|
|
351
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* padStringCentered: Pad a string to a target visual width, accounting for color codes
|
|
355
|
+
* Centers the string within the specified width with padding
|
|
356
|
+
* @param {string} str - String that may contain color codes
|
|
357
|
+
* @param {number} width - Target visual width
|
|
358
|
+
* @returns {string} Padded string with original colors preserved
|
|
359
|
+
*/
|
|
360
|
+
function padStringCentered(str, width) {
|
|
361
|
+
const visualLen = stripColorCodes(str).length;
|
|
362
|
+
const totalPad = Math.max(0, width - visualLen);
|
|
363
|
+
const padLeft = Math.ceil(totalPad / 2);
|
|
364
|
+
const padRight = Math.floor(totalPad / 2);
|
|
365
|
+
return ' '.repeat(padLeft) + str + ' '.repeat(padRight);
|
|
366
|
+
}
|
|
367
|
+
// Load bot configurations
|
|
368
|
+
const botsConfig = readJSON(BOTS_CONFIG).bots;
|
|
369
|
+
function getConfiguredBotConfig(botKey, botData) {
|
|
370
|
+
const meta = botData?.meta || {};
|
|
371
|
+
return botsConfig.find((bot, index) => {
|
|
372
|
+
if (!bot)
|
|
373
|
+
return false;
|
|
374
|
+
return createBotKey(bot, index) === botKey || (meta.name && bot.name === meta.name);
|
|
375
|
+
}) || null;
|
|
376
|
+
}
|
|
377
|
+
function getOrderFileCandidate(fileName) {
|
|
378
|
+
const filePath = path.join(ORDERS_DIR, fileName);
|
|
379
|
+
if (!fileName.endsWith('.json')) {
|
|
380
|
+
return { include: false, reason: 'not a JSON file', report: false };
|
|
381
|
+
}
|
|
382
|
+
if (fileName.endsWith('.dynamicgrid.json')) {
|
|
383
|
+
return { include: false, reason: 'dynamic grid snapshot', report: false };
|
|
384
|
+
}
|
|
385
|
+
let data;
|
|
386
|
+
try {
|
|
387
|
+
data = readJSON(filePath);
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
return { include: false, reason: `invalid JSON: ${error.message}`, report: true, name: fileName };
|
|
391
|
+
}
|
|
392
|
+
if (!hasOrderGrid(data)) {
|
|
393
|
+
return { include: false, reason: 'not a persisted order grid', report: true, name: fileName };
|
|
394
|
+
}
|
|
395
|
+
// Derive botKey from the file name: profiles/orders/<botKey>.json
|
|
396
|
+
const botKey = fileName.replace(/\.json$/, '');
|
|
397
|
+
const botData = data;
|
|
398
|
+
const config = getConfiguredBotConfig(botKey, botData);
|
|
399
|
+
if (!config) {
|
|
400
|
+
return { include: false, reason: 'no matching bot config', report: true, name: fileName };
|
|
401
|
+
}
|
|
402
|
+
const realOrders = getRealGridOrders(botData);
|
|
403
|
+
if (realOrders.length === 0) {
|
|
404
|
+
return { include: false, reason: 'no real on-chain orders', report: true, name: fileName };
|
|
405
|
+
}
|
|
406
|
+
return { include: true, name: fileName, path: filePath, botKey, config };
|
|
407
|
+
}
|
|
408
|
+
// Get all order files sorted by modified date
|
|
409
|
+
function getOrderFiles() {
|
|
410
|
+
const candidates = fs.readdirSync(ORDERS_DIR).map(getOrderFileCandidate);
|
|
411
|
+
const files = candidates
|
|
412
|
+
.filter(candidate => candidate.include)
|
|
413
|
+
.map(f => ({
|
|
414
|
+
...f,
|
|
415
|
+
mtime: getModifiedTime(f.path)
|
|
416
|
+
}))
|
|
417
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
418
|
+
return {
|
|
419
|
+
files,
|
|
420
|
+
skippedCandidates: candidates.filter(candidate => !candidate.include && candidate.report)
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* analyzeOrder: Comprehensive analysis of a single bot's order grid
|
|
425
|
+
*
|
|
426
|
+
* Examines all aspects of grid health:
|
|
427
|
+
* - Spread: Gap between best buy and best sell vs. target
|
|
428
|
+
* - Increment: Consistency of price steps between grid slots
|
|
429
|
+
* - Funds: Total funds allocated to each side
|
|
430
|
+
* - Distribution: Comparison of slot count vs. fund allocation
|
|
431
|
+
* - Double-sided mode: Whether sides are intentionally unbalanced
|
|
432
|
+
*
|
|
433
|
+
* Terminology:
|
|
434
|
+
* - boundaryIdx: Index of best buy slot (highest buy price)
|
|
435
|
+
* - Slots > boundaryIdx are sells
|
|
436
|
+
* - Slots < boundaryIdx are buys at boundary position
|
|
437
|
+
* - Spread slots are outside normal grid (rare)
|
|
438
|
+
*
|
|
439
|
+
* @param {Object} botData - Order data with grid array and metadata
|
|
440
|
+
* @param {Object} config - Bot configuration (optional) for comparison
|
|
441
|
+
* @param {string} [botKey] - Bot key used to locate the dynamic grid snapshot
|
|
442
|
+
* @returns {Object} Analysis result with spread, increment, funds, distribution
|
|
443
|
+
*/
|
|
444
|
+
function analyzeOrder(botData, config, botKey) {
|
|
445
|
+
const meta = botData.meta;
|
|
446
|
+
const grid = botData.grid;
|
|
447
|
+
// Extract asset pair: prioritize assets object from order data, fall back to meta
|
|
448
|
+
let assetA = meta.assetA;
|
|
449
|
+
let assetB = meta.assetB;
|
|
450
|
+
// If metadata is null/missing, get from assets object in order file
|
|
451
|
+
if (!assetA && botData.assets && botData.assets.assetA) {
|
|
452
|
+
assetA = botData.assets.assetA.symbol;
|
|
453
|
+
}
|
|
454
|
+
if (!assetB && botData.assets && botData.assets.assetB) {
|
|
455
|
+
assetB = botData.assets.assetB.symbol;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Grid Slot Separation
|
|
459
|
+
* The grid contains buy slots (prices below market), sell slots (above market),
|
|
460
|
+
* and optional spread slots. Separation enables independent analysis.
|
|
461
|
+
*/
|
|
462
|
+
const boundaryIdx = botData.boundaryIdx;
|
|
463
|
+
const buySlots = grid.filter((s, i) => i <= boundaryIdx && s.type === ORDER_TYPES.BUY);
|
|
464
|
+
const sellSlots = grid.filter((s, i) => i > boundaryIdx && s.type === ORDER_TYPES.SELL);
|
|
465
|
+
const spreadSlots = grid.filter(s => s.type === ORDER_TYPES.SPREAD);
|
|
466
|
+
const activeBuySlots = buySlots.filter(s => s.state === ORDER_STATES.ACTIVE || s.state === ORDER_STATES.PARTIAL);
|
|
467
|
+
const virtualBuySlots = buySlots.filter(s => s.state === ORDER_STATES.VIRTUAL);
|
|
468
|
+
const activeSellSlots = sellSlots.filter(s => s.state === ORDER_STATES.ACTIVE || s.state === ORDER_STATES.PARTIAL);
|
|
469
|
+
const virtualSellSlots = sellSlots.filter(s => s.state === ORDER_STATES.VIRTUAL);
|
|
470
|
+
/**
|
|
471
|
+
* Best Prices Identification
|
|
472
|
+
* bestBuySlot: Highest buy price (at boundary, closest to market)
|
|
473
|
+
* bestSellSlot: Lowest sell price (first sell after boundary, closest to market)
|
|
474
|
+
* The spread between these is the "real" spread of the grid
|
|
475
|
+
*/
|
|
476
|
+
const bestBuySlot = grid[boundaryIdx];
|
|
477
|
+
const bestSellSlot = grid.slice(boundaryIdx + 1).find(s => s.type === ORDER_TYPES.SELL);
|
|
478
|
+
/**
|
|
479
|
+
* Real Spread Calculation
|
|
480
|
+
* Formula: (bestSellPrice - bestBuyPrice) / bestBuyPrice
|
|
481
|
+
* This is the actual gap in the market, measured as percentage from buy price
|
|
482
|
+
* Example: buy=100, sell=105 -> spread = 5%
|
|
483
|
+
*/
|
|
484
|
+
const realSpread = bestBuySlot && bestSellSlot
|
|
485
|
+
? ((bestSellSlot.price - bestBuySlot.price) / bestBuySlot.price)
|
|
486
|
+
: 0;
|
|
487
|
+
let spreadDiff, targetSpread, incrementCheck;
|
|
488
|
+
/**
|
|
489
|
+
* Config-based Comparisons
|
|
490
|
+
* If bot has configuration, compare actual to target values
|
|
491
|
+
* Otherwise, just report actual values
|
|
492
|
+
*/
|
|
493
|
+
if (config) {
|
|
494
|
+
// Config exists - calculate variance from target
|
|
495
|
+
targetSpread = config.targetSpreadPercent / 100;
|
|
496
|
+
spreadDiff = realSpread - targetSpread;
|
|
497
|
+
incrementCheck = checkGeometricIncrement(grid, config.incrementPercent / 100);
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
// No config - report actual values only
|
|
501
|
+
targetSpread = null;
|
|
502
|
+
spreadDiff = null;
|
|
503
|
+
incrementCheck = checkGeometricIncrement(grid, null);
|
|
504
|
+
}
|
|
505
|
+
// Calculate total funds committed to buy and sell sides
|
|
506
|
+
const gridFunds = calculateGridFunds(buySlots, sellSlots, bestBuySlot, bestSellSlot);
|
|
507
|
+
const activeGridFunds = calculateGridFunds(activeBuySlots, activeSellSlots, bestBuySlot, bestSellSlot);
|
|
508
|
+
const virtualGridFunds = calculateGridFunds(virtualBuySlots, virtualSellSlots, bestBuySlot, bestSellSlot);
|
|
509
|
+
// Analyze how funds are distributed across slots
|
|
510
|
+
const distribution = analyzeDistribution(buySlots, sellSlots, bestBuySlot, bestSellSlot);
|
|
511
|
+
// Calculate grid extremes and market price
|
|
512
|
+
const gridMinPrice = grid.length > 0 ? Math.min(...grid.map(s => s.price)) : null;
|
|
513
|
+
const gridMaxPrice = grid.length > 0 ? Math.max(...grid.map(s => s.price)) : null;
|
|
514
|
+
const marketPrice = bestBuySlot && bestSellSlot
|
|
515
|
+
? (bestBuySlot.price + bestSellSlot.price) / 2
|
|
516
|
+
: null;
|
|
517
|
+
/**
|
|
518
|
+
* Return comprehensive analysis object
|
|
519
|
+
* Includes all metrics needed for health check output
|
|
520
|
+
*/
|
|
521
|
+
const _dynamicWeight = buildDynamicWeightInfo(botKey, config);
|
|
522
|
+
const _ab = computeGridRangeScalingDisplay(config, _dynamicWeight);
|
|
523
|
+
let gridPriceValue = null;
|
|
524
|
+
let gridPriceLabel = null;
|
|
525
|
+
let gridPriceStale = false;
|
|
526
|
+
const _amaKey = resolveAmaKey(config);
|
|
527
|
+
if (_amaKey && _dynamicWeight?.amaCenterPrice != null) {
|
|
528
|
+
gridPriceLabel = _amaKey;
|
|
529
|
+
gridPriceValue = _dynamicWeight.amaCenterPrice;
|
|
530
|
+
gridPriceStale = _dynamicWeight.isRecent === false;
|
|
531
|
+
}
|
|
532
|
+
else if (typeof config?.gridPrice === 'number' && Number.isFinite(config.gridPrice) && config.gridPrice > 0) {
|
|
533
|
+
gridPriceLabel = 'Grid';
|
|
534
|
+
gridPriceValue = config.gridPrice;
|
|
535
|
+
}
|
|
536
|
+
return {
|
|
537
|
+
pair: `${assetA}/${assetB}`,
|
|
538
|
+
botName: config?.name || botKey,
|
|
539
|
+
lastUpdated: new Date(meta.updatedAt || botData.lastUpdated),
|
|
540
|
+
gridMinPrice: gridMinPrice,
|
|
541
|
+
marketPrice: marketPrice,
|
|
542
|
+
gridMaxPrice: gridMaxPrice,
|
|
543
|
+
hasConfig: !!config,
|
|
544
|
+
// Spread metrics
|
|
545
|
+
spread: {
|
|
546
|
+
real: realSpread,
|
|
547
|
+
target: targetSpread,
|
|
548
|
+
diff: spreadDiff,
|
|
549
|
+
// Pass if within 0.1% of target (or null if no config to compare)
|
|
550
|
+
pass: config ? Math.abs(spreadDiff) < 0.001 : null
|
|
551
|
+
},
|
|
552
|
+
// Increment consistency metrics
|
|
553
|
+
increment: incrementCheck,
|
|
554
|
+
// Fund allocation breakdown
|
|
555
|
+
funds: gridFunds,
|
|
556
|
+
activeFunds: activeGridFunds,
|
|
557
|
+
virtualFunds: virtualGridFunds,
|
|
558
|
+
// Slot vs fund distribution analysis
|
|
559
|
+
distribution: distribution,
|
|
560
|
+
// Slot counts for structure overview
|
|
561
|
+
slots: {
|
|
562
|
+
buy: buySlots.length,
|
|
563
|
+
sell: sellSlots.length,
|
|
564
|
+
spread: spreadSlots.length,
|
|
565
|
+
activeBuy: activeBuySlots.length,
|
|
566
|
+
virtualBuy: virtualBuySlots.length,
|
|
567
|
+
activeSell: activeSellSlots.length,
|
|
568
|
+
virtualSell: virtualSellSlots.length,
|
|
569
|
+
partialBuy: buySlots.filter(s => s.state === ORDER_STATES.PARTIAL).length,
|
|
570
|
+
partialSell: sellSlots.filter(s => s.state === ORDER_STATES.PARTIAL).length
|
|
571
|
+
},
|
|
572
|
+
// Slot data for weight visualization
|
|
573
|
+
slotData: {
|
|
574
|
+
buy: buySlots,
|
|
575
|
+
sell: sellSlots
|
|
576
|
+
},
|
|
577
|
+
// Target active orders from config
|
|
578
|
+
activeOrdersTarget: config ? config.activeOrders : null,
|
|
579
|
+
// Bot fund allocation settings from config
|
|
580
|
+
botFunds: config ? config.botFunds : null,
|
|
581
|
+
// Weight distribution from config
|
|
582
|
+
weightDistribution: config ? config.weightDistribution : null,
|
|
583
|
+
// Resolved grid price label, value, and staleness flag
|
|
584
|
+
// (null label/value when gridPrice is pool/book/null)
|
|
585
|
+
gridPriceLabel,
|
|
586
|
+
gridPriceValue,
|
|
587
|
+
gridPriceStale,
|
|
588
|
+
// Latest dynamic weight payload from the market adapter (AMA bots only).
|
|
589
|
+
// Null when the bot is not AMA, or when no fresh snapshot is available.
|
|
590
|
+
dynamicWeight: _dynamicWeight,
|
|
591
|
+
// Grid range scaling (asymmetric bounds) display data, null if not applicable.
|
|
592
|
+
asymmetricBounds: _ab,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* computeGridRangeScalingDisplay: Compute resolved prices from grid range scaling.
|
|
597
|
+
* Returns null when the bot is not whitelisted or data is incomplete.
|
|
598
|
+
*/
|
|
599
|
+
function computeGridRangeScalingDisplay(config, dynamicWeight) {
|
|
600
|
+
if (!dynamicWeight || dynamicWeight.amaCenterPrice == null) {
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
const centerPrice = dynamicWeight.amaCenterPrice;
|
|
604
|
+
let minPrice, maxPrice;
|
|
605
|
+
try {
|
|
606
|
+
minPrice = resolveConfiguredPriceBound(config && config.minPrice, undefined, centerPrice, 'min');
|
|
607
|
+
maxPrice = resolveConfiguredPriceBound(config && config.maxPrice, undefined, centerPrice, 'max');
|
|
608
|
+
}
|
|
609
|
+
catch (_) {
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
if (!Number.isFinite(minPrice) || !Number.isFinite(maxPrice))
|
|
613
|
+
return null;
|
|
614
|
+
const hasAsym = Number.isFinite(dynamicWeight.appliedAsymmetryFactor)
|
|
615
|
+
&& (dynamicWeight.trend === 'UP' || dynamicWeight.trend === 'DOWN');
|
|
616
|
+
let resolvedMinPrice = minPrice;
|
|
617
|
+
let resolvedMaxPrice = maxPrice;
|
|
618
|
+
if (hasAsym) {
|
|
619
|
+
const prices = computeAsymmetricBoundsPrices(centerPrice, minPrice, maxPrice, dynamicWeight.trend, dynamicWeight.appliedAsymmetryFactor);
|
|
620
|
+
if (prices) {
|
|
621
|
+
resolvedMinPrice = prices.resolvedMinPrice;
|
|
622
|
+
resolvedMaxPrice = prices.resolvedMaxPrice;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const trend = hasAsym ? dynamicWeight.trend : null;
|
|
626
|
+
return {
|
|
627
|
+
resolvedMinPrice,
|
|
628
|
+
resolvedMaxPrice,
|
|
629
|
+
configuredMinPrice: minPrice,
|
|
630
|
+
configuredMaxPrice: maxPrice,
|
|
631
|
+
appliedAsymmetryFactor: hasAsym ? dynamicWeight.appliedAsymmetryFactor : 0,
|
|
632
|
+
rawAsymmetryFactor: hasAsym ? dynamicWeight.rawAsymmetryFactor : 0,
|
|
633
|
+
trend,
|
|
634
|
+
isRecent: dynamicWeight.isRecent,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* checkGeometricIncrement: Verify grid uses consistent geometric price progression
|
|
639
|
+
*
|
|
640
|
+
* A proper grid should have constant percentage increments between slots.
|
|
641
|
+
* This function checks if increments are consistent (low standard deviation).
|
|
642
|
+
*
|
|
643
|
+
* Geometric increment formula:
|
|
644
|
+
* increment = (nextPrice - currentPrice) / currentPrice
|
|
645
|
+
* This is a percentage change from one slot to the next.
|
|
646
|
+
*
|
|
647
|
+
* Example with 2% increment:
|
|
648
|
+
* - Slot 1: 100
|
|
649
|
+
* - Slot 2: 102 (increment = 0.02 or 2%)
|
|
650
|
+
* - Slot 3: 104.04 (increment = 0.02 or 2%)
|
|
651
|
+
*
|
|
652
|
+
* Metrics:
|
|
653
|
+
* - avg: Average increment across all slots
|
|
654
|
+
* - stdDev: Standard deviation (should be < 0.1% for good grids)
|
|
655
|
+
* - pass: True if avg matches target and is consistent (for grids with config)
|
|
656
|
+
*
|
|
657
|
+
* @param {Array} grid - All grid slots
|
|
658
|
+
* @param {number} targetIncrement - Target increment ratio (e.g., 0.02 for 2%)
|
|
659
|
+
* @returns {Object} Increment analysis with avg, target, stdDev, consistency
|
|
660
|
+
*/
|
|
661
|
+
function checkGeometricIncrement(grid, targetIncrement) {
|
|
662
|
+
// Filter out spread slots (only analyze regular buy/sell slots)
|
|
663
|
+
const slots = grid.filter(s => s.type !== 'spread');
|
|
664
|
+
// Need at least 2 slots to calculate increment
|
|
665
|
+
if (slots.length < 2) {
|
|
666
|
+
return { pass: null, avgIncrement: 0, consistent: true, target: targetIncrement };
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Calculate increment for each consecutive pair of slots
|
|
670
|
+
* Increment = percentage change from previous to current price
|
|
671
|
+
*/
|
|
672
|
+
const increments = [];
|
|
673
|
+
for (let i = 1; i < slots.length; i++) {
|
|
674
|
+
const prevPrice = slots[i - 1].price;
|
|
675
|
+
const currPrice = slots[i].price;
|
|
676
|
+
// Relative price change as decimal (0.02 = 2%)
|
|
677
|
+
const increment = (currPrice - prevPrice) / prevPrice;
|
|
678
|
+
increments.push(increment);
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Statistical Analysis
|
|
682
|
+
* Average: Mean of all increments
|
|
683
|
+
* Standard deviation: Measure of variability (lower = more consistent)
|
|
684
|
+
*/
|
|
685
|
+
const avgIncrement = increments.reduce((a, b) => a + b) / increments.length;
|
|
686
|
+
// Calculate standard deviation (measure of consistency)
|
|
687
|
+
const stdDev = Math.sqrt(increments.reduce((sum, inc) => sum + Math.pow(inc - avgIncrement, 2), 0) / increments.length);
|
|
688
|
+
return {
|
|
689
|
+
avg: avgIncrement, // Actual average increment
|
|
690
|
+
target: targetIncrement, // Expected increment from config
|
|
691
|
+
// Difference from target (null if no config)
|
|
692
|
+
diff: targetIncrement ? avgIncrement - targetIncrement : null,
|
|
693
|
+
stdDev: stdDev, // Consistency metric
|
|
694
|
+
// Grid is "consistent" if std dev < 0.1%
|
|
695
|
+
consistent: stdDev < 0.001,
|
|
696
|
+
// Pass if matches target AND is consistent (null if no config)
|
|
697
|
+
pass: targetIncrement ? (Math.abs(avgIncrement - targetIncrement) < 0.0001 && stdDev < 0.001) : null
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* calculateGridFunds: Calculate total funds in buy and sell sides
|
|
702
|
+
*
|
|
703
|
+
* Currency Note:
|
|
704
|
+
* - Buy slot sizes: Measured in AssetB (quote currency, e.g., BTS)
|
|
705
|
+
* Each buy slot uses quote currency to purchase base currency
|
|
706
|
+
* - Sell slot sizes: Measured in AssetA (base currency, e.g., XRP)
|
|
707
|
+
* Each sell slot holds base currency ready to sell
|
|
708
|
+
*
|
|
709
|
+
* Conversion logic:
|
|
710
|
+
* - BTS fund in buy side represents potential XRP purchase: totalBTS / marketPrice
|
|
711
|
+
* - XRP fund in sell side converts to BTS equivalent: totalXRP * marketPrice
|
|
712
|
+
* - Uses market price (midpoint between best buy and best sell) for accurate valuation
|
|
713
|
+
*
|
|
714
|
+
* @param {Array} buySlots - Buy order slots
|
|
715
|
+
* @param {Array} sellSlots - Sell order slots
|
|
716
|
+
* @param {Object} bestBuySlot - Best (highest) buy price slot
|
|
717
|
+
* @param {Object} bestSellSlot - Best (lowest) sell price slot
|
|
718
|
+
* @returns {Object} Fund breakdown {buy: {bts, xrp}, sell: {xrp, bts}}
|
|
719
|
+
*/
|
|
720
|
+
function calculateGridFunds(buySlots, sellSlots, bestBuySlot, bestSellSlot) {
|
|
721
|
+
/**
|
|
722
|
+
* Direct Fund Aggregation
|
|
723
|
+
* Sum all slot sizes on each side
|
|
724
|
+
* Buy slots: Total BTS committed
|
|
725
|
+
* Sell slots: Total XRP (or base currency) available
|
|
726
|
+
*/
|
|
727
|
+
const totalBTS = buySlots.reduce((sum, s) => sum + s.size, 0);
|
|
728
|
+
const totalXRP = sellSlots.reduce((sum, s) => sum + s.size, 0);
|
|
729
|
+
/**
|
|
730
|
+
* Market Price Calculation
|
|
731
|
+
* Use the midpoint between best buy and best sell prices
|
|
732
|
+
* This represents the fair market price for fund valuation
|
|
733
|
+
*/
|
|
734
|
+
const marketPrice = bestBuySlot && bestSellSlot
|
|
735
|
+
? (bestBuySlot.price + bestSellSlot.price) / 2
|
|
736
|
+
: 1;
|
|
737
|
+
// How much XRP the buy-side BTS could purchase (at market price)
|
|
738
|
+
const totalXRPFromBuy = totalBTS / marketPrice;
|
|
739
|
+
// How much BTS the sell-side XRP could generate (at market price)
|
|
740
|
+
const totalBTSFromSell = totalXRP * marketPrice;
|
|
741
|
+
return {
|
|
742
|
+
// Buy side: funds dedicated to purchasing base currency
|
|
743
|
+
buy: {
|
|
744
|
+
bts: totalBTS, // Direct BTS allocation
|
|
745
|
+
xrp: totalXRPFromBuy // Equivalent XRP buying power
|
|
746
|
+
},
|
|
747
|
+
// Sell side: funds available to sell base currency
|
|
748
|
+
sell: {
|
|
749
|
+
xrp: totalXRP, // Direct XRP holdings
|
|
750
|
+
bts: totalBTSFromSell // Equivalent BTS revenue potential
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* getDeltaColor: Return color code based on delta percentage value
|
|
756
|
+
* Under 10%: green, 10-20%: yellow, over 20%: red
|
|
757
|
+
* @param {number} deltaValue - The delta percentage value
|
|
758
|
+
* @returns {string} Color code
|
|
759
|
+
*/
|
|
760
|
+
function getDeltaColor(deltaValue) {
|
|
761
|
+
if (deltaValue < 10)
|
|
762
|
+
return colors.buy; // green
|
|
763
|
+
if (deltaValue <= 20)
|
|
764
|
+
return colors.spread; // yellow
|
|
765
|
+
return colors.sell; // red
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* createDistributionBar: Create a horizontal bar chart showing BUY/SELL/spread distribution
|
|
769
|
+
* Differentiates between active (dark) and virtual (light) slots
|
|
770
|
+
* @param {Object} counts - Object containing activeBuy, virtualBuy, activeSell, virtualSell, spread
|
|
771
|
+
* @returns {{bar: string, buyWidth: number}} Colored bar visualization
|
|
772
|
+
*/
|
|
773
|
+
function createDistributionBar(counts) {
|
|
774
|
+
const barWidth = BAR_WIDTH; // total width in characters
|
|
775
|
+
const total = counts.activeBuy + counts.virtualBuy + counts.spread + counts.activeSell + counts.virtualSell;
|
|
776
|
+
if (total === 0)
|
|
777
|
+
return { bar: ' '.repeat(barWidth), buyWidth: 0 };
|
|
778
|
+
// Calculate widths proportionally
|
|
779
|
+
let activeBuyWidth = Math.round((counts.activeBuy / total) * barWidth);
|
|
780
|
+
let virtualBuyWidth = Math.round((counts.virtualBuy / total) * barWidth);
|
|
781
|
+
let spreadWidth = Math.round((counts.spread / total) * barWidth);
|
|
782
|
+
let activeSellWidth = Math.round((counts.activeSell / total) * barWidth);
|
|
783
|
+
let virtualSellWidth = Math.round((counts.virtualSell / total) * barWidth);
|
|
784
|
+
// Ensure spread is visible if it exists
|
|
785
|
+
if (counts.spread > 0 && spreadWidth === 0) {
|
|
786
|
+
spreadWidth = 1;
|
|
787
|
+
// Borrow from largest other section
|
|
788
|
+
const widths = [
|
|
789
|
+
{ name: 'activeBuy', val: activeBuyWidth },
|
|
790
|
+
{ name: 'virtualBuy', val: virtualBuyWidth },
|
|
791
|
+
{ name: 'activeSell', val: activeSellWidth },
|
|
792
|
+
{ name: 'virtualSell', val: virtualSellWidth }
|
|
793
|
+
].sort((a, b) => b.val - a.val);
|
|
794
|
+
if (widths[0].val > 0) {
|
|
795
|
+
if (widths[0].name === 'activeBuy')
|
|
796
|
+
activeBuyWidth--;
|
|
797
|
+
else if (widths[0].name === 'virtualBuy')
|
|
798
|
+
virtualBuyWidth--;
|
|
799
|
+
else if (widths[0].name === 'activeSell')
|
|
800
|
+
activeSellWidth--;
|
|
801
|
+
else if (widths[0].name === 'virtualSell')
|
|
802
|
+
virtualSellWidth--;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
// Adjust to ensure total is exactly barWidth
|
|
806
|
+
const sum = activeBuyWidth + virtualBuyWidth + spreadWidth + activeSellWidth + virtualSellWidth;
|
|
807
|
+
if (sum !== barWidth) {
|
|
808
|
+
let diff = barWidth - sum;
|
|
809
|
+
const sections = [
|
|
810
|
+
{ name: 'activeBuyWidth', get: () => activeBuyWidth, set: v => { activeBuyWidth = v; } },
|
|
811
|
+
{ name: 'virtualBuyWidth', get: () => virtualBuyWidth, set: v => { virtualBuyWidth = v; } },
|
|
812
|
+
{ name: 'spreadWidth', get: () => spreadWidth, set: v => { spreadWidth = v; } },
|
|
813
|
+
{ name: 'activeSellWidth', get: () => activeSellWidth, set: v => { activeSellWidth = v; } },
|
|
814
|
+
{ name: 'virtualSellWidth', get: () => virtualSellWidth, set: v => { virtualSellWidth = v; } }
|
|
815
|
+
];
|
|
816
|
+
while (diff > 0) {
|
|
817
|
+
const target = sections
|
|
818
|
+
.slice()
|
|
819
|
+
.sort((a, b) => b.get() - a.get())[0];
|
|
820
|
+
target.set(target.get() + 1);
|
|
821
|
+
diff--;
|
|
822
|
+
}
|
|
823
|
+
while (diff < 0) {
|
|
824
|
+
const target = sections
|
|
825
|
+
.filter(section => section.get() > 0)
|
|
826
|
+
.sort((a, b) => b.get() - a.get())[0];
|
|
827
|
+
if (!target)
|
|
828
|
+
break;
|
|
829
|
+
target.set(target.get() - 1);
|
|
830
|
+
diff++;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
const buyBar = colors.buy + 'ā'.repeat(virtualBuyWidth) + colors.buyDark + 'ā'.repeat(activeBuyWidth) + colors.reset;
|
|
834
|
+
const spreadBar = '\x1b[97m' + 'ā'.repeat(spreadWidth) + colors.reset; // white
|
|
835
|
+
const sellBar = colors.sellDark + 'ā'.repeat(activeSellWidth) + colors.sell + 'ā'.repeat(virtualSellWidth) + colors.reset;
|
|
836
|
+
return { bar: `${buyBar}${spreadBar}${sellBar}`, buyWidth: activeBuyWidth + virtualBuyWidth };
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* createWeightFactorBar: Visualize capital distribution using actual order sizes
|
|
840
|
+
*
|
|
841
|
+
* Shows how capital is distributed across BUY and SELL orders using arithmetic averages.
|
|
842
|
+
* Buy funds measured in quote currency, sell funds in base currency - converts to common basis.
|
|
843
|
+
* Each side scales independently: highest order on each side = 100% (ā)
|
|
844
|
+
* Character width is proportional to fund amount on each side (not 50/50 split).
|
|
845
|
+
* Full block (ā) = maximum average capital on that side
|
|
846
|
+
* Partial blocks (ā-ā) = progressively lower average capital
|
|
847
|
+
*
|
|
848
|
+
* @param {Array} buyOrders - Buy orders with size property (in quote currency)
|
|
849
|
+
* @param {Array} sellOrders - Sell orders with size property (in base currency)
|
|
850
|
+
* @param {number} barWidth - Target width in characters (default: BAR_WIDTH=51)
|
|
851
|
+
* @param {number} marketPrice - Market price for currency conversion (sell to quote basis)
|
|
852
|
+
* @returns {string} Colored weight visualization with independent scaling
|
|
853
|
+
*/
|
|
854
|
+
function createWeightFactorBar(buyOrders, sellOrders, barWidth = BAR_WIDTH, marketPrice = 1) {
|
|
855
|
+
if ((!buyOrders || buyOrders.length === 0) && (!sellOrders || sellOrders.length === 0)) {
|
|
856
|
+
return '(no orders)';
|
|
857
|
+
}
|
|
858
|
+
// Calculate total fund weight on each side using arithmetic sum
|
|
859
|
+
const buyTotalSize = (buyOrders || []).reduce((sum, o) => sum + (o.size || 0), 0);
|
|
860
|
+
const sellTotalSize = (sellOrders || []).reduce((sum, o) => sum + (o.size || 0), 0);
|
|
861
|
+
// Convert sell side to quote currency equivalent for accurate ratio calculation
|
|
862
|
+
// Buy side is in quote currency, sell side is in base currency
|
|
863
|
+
// Using market price to convert: sell_base * market_price = sell_quote_equivalent
|
|
864
|
+
const sellTotalInQuote = sellTotalSize * marketPrice;
|
|
865
|
+
const totalFunds = buyTotalSize + sellTotalInQuote;
|
|
866
|
+
// Allocate widths proportionally based on fund ratios (in common currency basis)
|
|
867
|
+
// This ensures character length reflects the actual fund distribution
|
|
868
|
+
let buyWidth, sellWidth;
|
|
869
|
+
if (totalFunds > 0) {
|
|
870
|
+
buyWidth = Math.round((buyTotalSize / totalFunds) * barWidth);
|
|
871
|
+
sellWidth = barWidth - buyWidth;
|
|
872
|
+
}
|
|
873
|
+
else {
|
|
874
|
+
// Fallback to 50/50 if no funds
|
|
875
|
+
buyWidth = Math.floor(barWidth / 2);
|
|
876
|
+
sellWidth = barWidth - buyWidth;
|
|
877
|
+
}
|
|
878
|
+
// Create buy side with independent scaling (max on buy side = 100%)
|
|
879
|
+
const buyBar = createWeightSide(buyOrders, colors.buyDark, colors.buy, buyWidth);
|
|
880
|
+
// Create sell side with independent scaling (max on sell side = 100%)
|
|
881
|
+
const sellBar = createWeightSide(sellOrders, colors.sellDark, colors.sell, sellWidth);
|
|
882
|
+
return `${buyBar}${sellBar}`;
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Helper: Create weight visualization for one side with independent scaling
|
|
886
|
+
* Distributes orders evenly across bar width, ensuring all bars are filled
|
|
887
|
+
* @param {Array} orders - Orders on this side
|
|
888
|
+
* @param {string} activeColor - Color for active orders
|
|
889
|
+
* @param {string} virtualColor - Color for virtual orders
|
|
890
|
+
* @param {number} sideWidth - Width allocated to this side
|
|
891
|
+
*/
|
|
892
|
+
function createWeightSide(orders, activeColor, virtualColor, sideWidth) {
|
|
893
|
+
if (!orders || orders.length === 0 || sideWidth === 0) {
|
|
894
|
+
return virtualColor + ' '.repeat(sideWidth) + colors.reset;
|
|
895
|
+
}
|
|
896
|
+
// Get sizes and find max for THIS SIDE only (independent scaling)
|
|
897
|
+
const sizes = orders.map(o => o.size || 0);
|
|
898
|
+
const maxSize = Math.max(...sizes);
|
|
899
|
+
if (maxSize === 0) {
|
|
900
|
+
return virtualColor + 'ā'.repeat(sideWidth) + colors.reset;
|
|
901
|
+
}
|
|
902
|
+
const compressedWeights = [];
|
|
903
|
+
// Distribute orders evenly across all bar positions
|
|
904
|
+
// Each bar maps to a position in the orders array
|
|
905
|
+
for (let barIdx = 0; barIdx < sideWidth; barIdx++) {
|
|
906
|
+
// Map bar position to order range
|
|
907
|
+
// This ensures even distribution even if orders < sideWidth
|
|
908
|
+
const startPos = (barIdx * orders.length) / sideWidth;
|
|
909
|
+
const endPos = ((barIdx + 1) * orders.length) / sideWidth;
|
|
910
|
+
// Get all orders that fall within this bar's range
|
|
911
|
+
const startIdx = Math.floor(startPos);
|
|
912
|
+
const endIdx = Math.ceil(endPos);
|
|
913
|
+
const groupOrders = orders.slice(startIdx, endIdx);
|
|
914
|
+
if (groupOrders.length === 0) {
|
|
915
|
+
// Fallback: if no orders in range, find nearest order
|
|
916
|
+
const nearestIdx = Math.round(startPos);
|
|
917
|
+
groupOrders.push(orders[Math.min(nearestIdx, orders.length - 1)]);
|
|
918
|
+
}
|
|
919
|
+
const groupSizes = groupOrders.map(o => o.size || 0);
|
|
920
|
+
// Calculate arithmetic average of sizes in this group
|
|
921
|
+
const avgSize = groupSizes.reduce((a, b) => a + b, 0) / groupSizes.length;
|
|
922
|
+
// Normalize to max on THIS SIDE (1-8, minimum 1 for visibility)
|
|
923
|
+
const ratio = avgSize / maxSize;
|
|
924
|
+
const blockHeight = Math.max(1, Math.round(ratio * 8));
|
|
925
|
+
// ACTIVE and PARTIAL slots are both real on-chain orders in this analysis.
|
|
926
|
+
const hasLiveOrder = groupOrders.some(o => o.state === ORDER_STATES.ACTIVE || o.state === ORDER_STATES.PARTIAL);
|
|
927
|
+
const color = hasLiveOrder ? activeColor : virtualColor;
|
|
928
|
+
compressedWeights.push(color + partialBlocks[blockHeight] + colors.reset);
|
|
929
|
+
}
|
|
930
|
+
return compressedWeights.join('');
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* analyzeDistribution: Compare slot distribution vs fund distribution
|
|
934
|
+
*
|
|
935
|
+
* Identifies imbalances between:
|
|
936
|
+
* - Slot distribution: How many buy vs sell slots exist
|
|
937
|
+
* - Fund distribution: How much funds are allocated to buy vs sell
|
|
938
|
+
*
|
|
939
|
+
* These should ideally match:
|
|
940
|
+
* - If 50% slots are buy, ~50% of funds should be on buy side
|
|
941
|
+
* - Deviation suggests intentional weighting or uneven fees
|
|
942
|
+
*
|
|
943
|
+
* Example:
|
|
944
|
+
* - 100 total slots: 40 buy + 60 sell = 40% buy slots
|
|
945
|
+
* - Funds: 6000 BTS buy + 4000 BTS sell equivalent = 60% buy funds
|
|
946
|
+
* - Delta buy: |40% - 60%| = 20% (funds weight more toward buy)
|
|
947
|
+
*
|
|
948
|
+
* @param {Array} buySlots - Buy order slots
|
|
949
|
+
* @param {Array} sellSlots - Sell order slots
|
|
950
|
+
* @param {Object} bestBuySlot - Best buy price (used for market price calculation)
|
|
951
|
+
* @param {Object} bestSellSlot - Best sell price (used for market price calculation)
|
|
952
|
+
* @returns {Object} Distribution analysis with slot%, fund%, and deltas
|
|
953
|
+
*/
|
|
954
|
+
function analyzeDistribution(buySlots, sellSlots, bestBuySlot, bestSellSlot) {
|
|
955
|
+
/**
|
|
956
|
+
* Slot Distribution
|
|
957
|
+
* Simple count: what percentage of total slots are buy vs sell
|
|
958
|
+
*/
|
|
959
|
+
const totalSlots = buySlots.length + sellSlots.length;
|
|
960
|
+
const buySlotPercent = totalSlots > 0 ? (buySlots.length / totalSlots) * 100 : 0;
|
|
961
|
+
const sellSlotPercent = totalSlots > 0 ? (sellSlots.length / totalSlots) * 100 : 0;
|
|
962
|
+
/**
|
|
963
|
+
* Fund Distribution
|
|
964
|
+
* Calculate total funds on each side, convert to common currency basis
|
|
965
|
+
* This shows if sides have equal capital or if one is prioritized
|
|
966
|
+
*/
|
|
967
|
+
const totalBuyFunds = buySlots.reduce((sum, s) => sum + s.size, 0);
|
|
968
|
+
const totalSellFunds = sellSlots.reduce((sum, s) => sum + s.size, 0);
|
|
969
|
+
/**
|
|
970
|
+
* Currency Conversion for Comparison
|
|
971
|
+
* Buy side funds: Measured in AssetB (quote)
|
|
972
|
+
* Sell side funds: Measured in AssetA (base)
|
|
973
|
+
* Convert both to common basis using market price (midpoint between best buy/sell)
|
|
974
|
+
*/
|
|
975
|
+
const marketPrice = bestBuySlot && bestSellSlot
|
|
976
|
+
? (bestBuySlot.price + bestSellSlot.price) / 2
|
|
977
|
+
: 1;
|
|
978
|
+
// Convert sell-side funds (XRP) to quote currency equivalent (BTS)
|
|
979
|
+
// This allows apples-to-apples fund comparison using market price
|
|
980
|
+
const sellFundsInBTS = totalSellFunds * marketPrice;
|
|
981
|
+
const totalFunds = totalBuyFunds + sellFundsInBTS;
|
|
982
|
+
/**
|
|
983
|
+
* Fund Percentage
|
|
984
|
+
* What % of total funds are allocated to buy vs sell
|
|
985
|
+
*/
|
|
986
|
+
const buyFundPercent = totalFunds > 0 ? (totalBuyFunds / totalFunds) * 100 : 0;
|
|
987
|
+
const sellFundPercent = totalFunds > 0 ? (sellFundsInBTS / totalFunds) * 100 : 0;
|
|
988
|
+
return {
|
|
989
|
+
// Slot-level breakdown
|
|
990
|
+
slots: {
|
|
991
|
+
buy: buySlots.length,
|
|
992
|
+
sell: sellSlots.length,
|
|
993
|
+
buyPercent: buySlotPercent,
|
|
994
|
+
sellPercent: sellSlotPercent
|
|
995
|
+
},
|
|
996
|
+
// Fund-level breakdown (in common currency)
|
|
997
|
+
funds: {
|
|
998
|
+
buyPercent: buyFundPercent,
|
|
999
|
+
sellPercent: sellFundPercent
|
|
1000
|
+
},
|
|
1001
|
+
// Delta: difference between slot% and fund% (shows imbalance)
|
|
1002
|
+
// If match is 0, slots and funds are perfectly balanced
|
|
1003
|
+
// If match is high, one side is over/under-weighted in funds
|
|
1004
|
+
match: {
|
|
1005
|
+
buyDiff: buyFundPercent - buySlotPercent,
|
|
1006
|
+
sellDiff: sellFundPercent - sellSlotPercent
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* getRawWeightValues: Extract raw buy/sell value strings for column alignment,
|
|
1012
|
+
* without any color codes or formatting artifacts.
|
|
1013
|
+
*
|
|
1014
|
+
* Mirrors the display-mode logic of formatWeightLine but returns plain strings.
|
|
1015
|
+
*
|
|
1016
|
+
* @param {Object|null} weightDistribution
|
|
1017
|
+
* @param {Object|null} dynamicWeight
|
|
1018
|
+
* @returns {{ buy: string, sell: string } | null}
|
|
1019
|
+
*/
|
|
1020
|
+
function getRawWeightValues(weightDistribution, dynamicWeight) {
|
|
1021
|
+
if (!weightDistribution)
|
|
1022
|
+
return null;
|
|
1023
|
+
const staticBuy = Number(weightDistribution.buy);
|
|
1024
|
+
const staticSell = Number(weightDistribution.sell);
|
|
1025
|
+
if (!Number.isFinite(staticBuy) || !Number.isFinite(staticSell))
|
|
1026
|
+
return null;
|
|
1027
|
+
if (dynamicWeight && dynamicWeight.isRecent === false) {
|
|
1028
|
+
return { buy: staticBuy.toFixed(2), sell: staticSell.toFixed(2) };
|
|
1029
|
+
}
|
|
1030
|
+
const useLive = !!(dynamicWeight
|
|
1031
|
+
&& dynamicWeight.isRecent
|
|
1032
|
+
&& dynamicWeight.live
|
|
1033
|
+
&& Number.isFinite(Number(dynamicWeight.live.buy))
|
|
1034
|
+
&& Number.isFinite(Number(dynamicWeight.live.sell)));
|
|
1035
|
+
if (!useLive) {
|
|
1036
|
+
return { buy: staticBuy.toFixed(2), sell: staticSell.toFixed(2) };
|
|
1037
|
+
}
|
|
1038
|
+
const liveBuy = Number(dynamicWeight.live.buy);
|
|
1039
|
+
const liveSell = Number(dynamicWeight.live.sell);
|
|
1040
|
+
return {
|
|
1041
|
+
buy: liveBuy.toFixed(2),
|
|
1042
|
+
sell: liveSell.toFixed(2)
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* formatWeightLine: Render the "Weight:" line, optionally with live dynamic values.
|
|
1047
|
+
*
|
|
1048
|
+
* Has three display modes:
|
|
1049
|
+
* 1. No dynamic snapshot at all (static-only) ā e.g. legacy bots
|
|
1050
|
+
* 2. Stale snapshot ā static values grayed only when dynamic weights are enabled
|
|
1051
|
+
* 3. Live snapshot ā live values colored by delta
|
|
1052
|
+
*
|
|
1053
|
+
* When maxBuyWidth/maxSellWidth are provided, the value portion in each column
|
|
1054
|
+
* is right-padded so it aligns with the widest value across the Active/Weight/Funds block.
|
|
1055
|
+
*
|
|
1056
|
+
* @param {Object|null} weightDistribution - Static weight config from bots.json
|
|
1057
|
+
* @param {Object|null} dynamicWeight - Live weight snapshot from dynamic grid
|
|
1058
|
+
* @param {number} [maxBuyWidth] - Target width for buy-side values (for column alignment)
|
|
1059
|
+
* @param {number} [maxSellWidth] - Target width for sell-side values (for column alignment)
|
|
1060
|
+
* @returns {string|null} Formatted weight line or null if no valid data
|
|
1061
|
+
*/
|
|
1062
|
+
function formatWeightLine(weightDistribution, dynamicWeight, maxBuyWidth, maxSellWidth) {
|
|
1063
|
+
if (!weightDistribution)
|
|
1064
|
+
return null;
|
|
1065
|
+
const staticBuy = Number(weightDistribution.buy);
|
|
1066
|
+
const staticSell = Number(weightDistribution.sell);
|
|
1067
|
+
if (!Number.isFinite(staticBuy) || !Number.isFinite(staticSell))
|
|
1068
|
+
return null;
|
|
1069
|
+
// Stale snapshot: snapshot file exists but updatedAt is older than the
|
|
1070
|
+
// freshness window. Surface a red "(adapter offline)" alert so the operator
|
|
1071
|
+
// knows the live envelope is being withheld, not just absent.
|
|
1072
|
+
if (dynamicWeight && dynamicWeight.isRecent === false) {
|
|
1073
|
+
const buyVal = staticBuy.toFixed(2);
|
|
1074
|
+
const sellVal = staticSell.toFixed(2);
|
|
1075
|
+
const alertStr = `${colors.sell}(adapter offline)${colors.reset}`;
|
|
1076
|
+
const valueColor = dynamicWeight.dynamicWeightEnabled === true ? colors.gray : '';
|
|
1077
|
+
const valueReset = valueColor ? colors.reset : '';
|
|
1078
|
+
return ` Weight: ${valueColor}${maxBuyWidth ? buyVal.padEnd(maxBuyWidth) : buyVal}${valueReset} ${colors.buy}buy${colors.reset} | ${valueColor}${maxSellWidth ? sellVal.padEnd(maxSellWidth) : sellVal}${valueReset} ${colors.sell}sell${colors.reset} ${alertStr}`;
|
|
1079
|
+
}
|
|
1080
|
+
const useLive = !!(dynamicWeight
|
|
1081
|
+
&& dynamicWeight.isRecent
|
|
1082
|
+
&& dynamicWeight.live
|
|
1083
|
+
&& Number.isFinite(Number(dynamicWeight.live.buy))
|
|
1084
|
+
&& Number.isFinite(Number(dynamicWeight.live.sell)));
|
|
1085
|
+
if (!useLive) {
|
|
1086
|
+
const buyVal = staticBuy.toFixed(2);
|
|
1087
|
+
const sellVal = staticSell.toFixed(2);
|
|
1088
|
+
return ` Weight: ${maxBuyWidth ? buyVal.padEnd(maxBuyWidth) : buyVal} ${colors.buy}buy${colors.reset} | ${maxSellWidth ? sellVal.padEnd(maxSellWidth) : sellVal} ${colors.sell}sell${colors.reset}`;
|
|
1089
|
+
}
|
|
1090
|
+
const liveBuy = Number(dynamicWeight.live.buy);
|
|
1091
|
+
const liveSell = Number(dynamicWeight.live.sell);
|
|
1092
|
+
// Compare the two live weights, not their deltas: the side with the larger
|
|
1093
|
+
// live weight is the one the bot is leaning on most heavily (the "losing"
|
|
1094
|
+
// side for that asset). When the two live weights are equal, no side is
|
|
1095
|
+
// being favored, so both fall back to white (default terminal color).
|
|
1096
|
+
const liveDelta = liveBuy - liveSell;
|
|
1097
|
+
let buyColor;
|
|
1098
|
+
let sellColor;
|
|
1099
|
+
if (Math.abs(liveDelta) <= DYNAMIC_WEIGHT_EPSILON) {
|
|
1100
|
+
buyColor = '';
|
|
1101
|
+
sellColor = '';
|
|
1102
|
+
}
|
|
1103
|
+
else if (liveDelta > 0) {
|
|
1104
|
+
// Buy is higher -> losing side (red); sell is lower -> winning side (green)
|
|
1105
|
+
buyColor = colors.sell;
|
|
1106
|
+
sellColor = colors.buy;
|
|
1107
|
+
}
|
|
1108
|
+
else {
|
|
1109
|
+
// Buy is lower -> winning side (green); sell is higher -> losing side (red)
|
|
1110
|
+
buyColor = colors.buy;
|
|
1111
|
+
sellColor = colors.sell;
|
|
1112
|
+
}
|
|
1113
|
+
const liveBuyStr = `${buyColor}${liveBuy.toFixed(2)}${colors.reset}`;
|
|
1114
|
+
const liveSellStr = `${sellColor}${liveSell.toFixed(2)}${colors.reset}`;
|
|
1115
|
+
const buyValVisual = liveBuy.toFixed(2);
|
|
1116
|
+
const sellValVisual = liveSell.toFixed(2);
|
|
1117
|
+
const coloredBuyVal = liveBuyStr;
|
|
1118
|
+
const coloredSellVal = liveSellStr;
|
|
1119
|
+
const paddedBuyVal = maxBuyWidth ? coloredBuyVal + ' '.repeat(Math.max(0, maxBuyWidth - buyValVisual.length)) : coloredBuyVal;
|
|
1120
|
+
const paddedSellVal = maxSellWidth ? coloredSellVal + ' '.repeat(Math.max(0, maxSellWidth - sellValVisual.length)) : coloredSellVal;
|
|
1121
|
+
return ` Weight: ${paddedBuyVal} ${colors.buy}buy${colors.reset} | ${paddedSellVal} ${colors.sell}sell${colors.reset}`;
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* formatAnalysis: Format analysis results into readable console output
|
|
1125
|
+
*
|
|
1126
|
+
* Creates a compact, emoji-enriched display of all analysis metrics.
|
|
1127
|
+
* Each line is designed to fit in typical terminal width.
|
|
1128
|
+
*
|
|
1129
|
+
* Output layout:
|
|
1130
|
+
* š PAIR
|
|
1131
|
+
* Updated: [timestamp]
|
|
1132
|
+
* [warnings if applicable]
|
|
1133
|
+
* Spread: [status] [real]% (target: [target]%) [direction][delta]
|
|
1134
|
+
* Increment: [status] [avg]% (target: [target]%) [direction][delta] Ļ=[stddev]
|
|
1135
|
+
* Slots: [buy] buy + [spread] spread + [sell] sell
|
|
1136
|
+
* Grid: BUY [amount] QUOTE ā [amount] BASE
|
|
1137
|
+
* SELL [amount] BASE ā [amount] QUOTE
|
|
1138
|
+
* Dist: BUY slots [%] vs funds [%] (Ī[diff]%) | SELL slots [%] vs funds [%] (Ī[diff]%)
|
|
1139
|
+
*
|
|
1140
|
+
* Symbols:
|
|
1141
|
+
* ā = passes threshold
|
|
1142
|
+
* ā = exceeds threshold (needs attention)
|
|
1143
|
+
* ā = value is lower than target
|
|
1144
|
+
* ā = value is higher than target
|
|
1145
|
+
* Ļ = standard deviation (consistency)
|
|
1146
|
+
* Ī = delta (difference)
|
|
1147
|
+
*
|
|
1148
|
+
* @param {Object} analysis - Analysis result object from analyzeOrder()
|
|
1149
|
+
* @returns {string} Formatted multi-line output ready for console.log
|
|
1150
|
+
*/
|
|
1151
|
+
function formatAnalysis(analysis) {
|
|
1152
|
+
const lines = [];
|
|
1153
|
+
// Header: Trading pair name
|
|
1154
|
+
lines.push(`\n${colors.cyan}š ${analysis.pair}${colors.reset} (${analysis.botName})`);
|
|
1155
|
+
lines.push(` ${colors.gray}Update: ${analysis.lastUpdated.toLocaleString()}${colors.reset}`);
|
|
1156
|
+
lines.push(``);
|
|
1157
|
+
// Warning: No config available for comparison
|
|
1158
|
+
if (!analysis.hasConfig) {
|
|
1159
|
+
lines.push(` ${colors.gray}ā ļø No config found - showing grid data only${colors.reset}`);
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Spread Analysis
|
|
1163
|
+
* Shows: actual spread vs target spread
|
|
1164
|
+
* Status: ā if within 0.1% of target, ā if not
|
|
1165
|
+
* Direction: ā if above target, ā if below target
|
|
1166
|
+
*/
|
|
1167
|
+
if (analysis.hasConfig) {
|
|
1168
|
+
lines.push(` Spread:${formatPercent(analysis.spread.real).padStart(6)} (${formatPercent(analysis.spread.target)}) | Incr.:${formatPercent(analysis.increment.avg).padStart(6)} (${formatPercent(analysis.increment.target)})`);
|
|
1169
|
+
}
|
|
1170
|
+
else {
|
|
1171
|
+
lines.push(` Spread:${formatPercent(analysis.spread.real).padStart(6)} | Incr.:${formatPercent(analysis.increment.avg).padStart(6)}`);
|
|
1172
|
+
}
|
|
1173
|
+
// Active orders comparison
|
|
1174
|
+
if (analysis.hasConfig && analysis.activeOrdersTarget) {
|
|
1175
|
+
const buyTarget = analysis.activeOrdersTarget.buy;
|
|
1176
|
+
const sellTarget = analysis.activeOrdersTarget.sell;
|
|
1177
|
+
const buyActual = analysis.slots.activeBuy;
|
|
1178
|
+
const sellActual = analysis.slots.activeSell;
|
|
1179
|
+
// Collect raw buy/sell values across Active, Weight, Funds for column alignment
|
|
1180
|
+
const rawWeightVals = getRawWeightValues(analysis.weightDistribution, analysis.dynamicWeight);
|
|
1181
|
+
const buyValues = [`${buyActual}/${buyTarget}`];
|
|
1182
|
+
const sellValues = [`${sellActual}/${sellTarget}`];
|
|
1183
|
+
if (rawWeightVals) {
|
|
1184
|
+
buyValues.push(rawWeightVals.buy);
|
|
1185
|
+
sellValues.push(rawWeightVals.sell);
|
|
1186
|
+
}
|
|
1187
|
+
if (analysis.botFunds) {
|
|
1188
|
+
buyValues.push(analysis.botFunds.buy);
|
|
1189
|
+
sellValues.push(analysis.botFunds.sell);
|
|
1190
|
+
}
|
|
1191
|
+
const maxBuyWidth = Math.max(...buyValues.map(v => stripColorCodes(v).length));
|
|
1192
|
+
const maxSellWidth = Math.max(...sellValues.map(v => stripColorCodes(v).length));
|
|
1193
|
+
lines.push(` Active: ${(buyActual + '/' + buyTarget).padEnd(maxBuyWidth)} ${colors.buy}buy${colors.reset} | ${(sellActual + '/' + sellTarget).padEnd(maxSellWidth)} ${colors.sell}sell${colors.reset}`);
|
|
1194
|
+
lines.push(``);
|
|
1195
|
+
const weightLine = formatWeightLine(analysis.weightDistribution, analysis.dynamicWeight, maxBuyWidth, maxSellWidth);
|
|
1196
|
+
if (weightLine) {
|
|
1197
|
+
lines.push(weightLine);
|
|
1198
|
+
}
|
|
1199
|
+
if (analysis.botFunds) {
|
|
1200
|
+
lines.push(` Funds: ${analysis.botFunds.buy.padEnd(maxBuyWidth)} ${colors.buy}buy${colors.reset} | ${analysis.botFunds.sell.padEnd(maxSellWidth)} ${colors.sell}sell${colors.reset}`);
|
|
1201
|
+
}
|
|
1202
|
+
if (analysis.gridPriceLabel && analysis.gridPriceValue != null) {
|
|
1203
|
+
lines.push(``);
|
|
1204
|
+
const rawPrice = Number(analysis.gridPriceValue);
|
|
1205
|
+
const priceStr = formatCurrency(rawPrice);
|
|
1206
|
+
const priceColor = analysis.gridPriceStale ? colors.gray : '';
|
|
1207
|
+
const priceReset = priceColor ? colors.reset : '';
|
|
1208
|
+
let diffStr = '';
|
|
1209
|
+
if (analysis.marketPrice != null && rawPrice !== 0) {
|
|
1210
|
+
const diff = (analysis.marketPrice - rawPrice) / rawPrice;
|
|
1211
|
+
const diffColor = diff > 0 ? colors.buy : diff < 0 ? colors.sell : '';
|
|
1212
|
+
const sign = diff > 0 ? '+' : '';
|
|
1213
|
+
diffStr = ` ${diffColor}(${sign}${(diff * 100).toFixed(1)}%)${colors.reset}`;
|
|
1214
|
+
}
|
|
1215
|
+
lines.push(` ${analysis.gridPriceLabel}: ${priceColor}${priceStr}${priceReset}${diffStr}`);
|
|
1216
|
+
}
|
|
1217
|
+
if (analysis.asymmetricBounds) {
|
|
1218
|
+
const ab = analysis.asymmetricBounds;
|
|
1219
|
+
const minStr = formatCurrency(ab.resolvedMinPrice);
|
|
1220
|
+
const maxStr = formatCurrency(ab.resolvedMaxPrice);
|
|
1221
|
+
const asymSign = ab.trend === 'DOWN' ? '-' : '+';
|
|
1222
|
+
const pctStr = ab.appliedAsymmetryFactor !== 0
|
|
1223
|
+
? ` (${asymSign}${(ab.appliedAsymmetryFactor * 100).toFixed(1)}%)`
|
|
1224
|
+
: '';
|
|
1225
|
+
lines.push(` Bounds: ${colors.buy}${minStr}${colors.reset} - ${colors.sell}${maxStr}${colors.reset}${pctStr}`);
|
|
1226
|
+
}
|
|
1227
|
+
lines.push(``);
|
|
1228
|
+
}
|
|
1229
|
+
// Calculate bar positioning based on slot distribution
|
|
1230
|
+
const totalSlots = analysis.slots.buy + analysis.slots.sell + analysis.slots.spread;
|
|
1231
|
+
const buyBarWidth = totalSlots > 0 ? Math.floor((analysis.slots.buy / totalSlots) * BAR_WIDTH) : 0;
|
|
1232
|
+
const spreadBarWidth = analysis.slots.spread > 0 ? Math.max(1, Math.floor((analysis.slots.spread / totalSlots) * BAR_WIDTH)) : 0;
|
|
1233
|
+
const sellBarWidth = BAR_WIDTH - buyBarWidth - spreadBarWidth;
|
|
1234
|
+
// Create formatted labels for three-column layout
|
|
1235
|
+
const buyLabel = `${analysis.slots.buy} buy`;
|
|
1236
|
+
const spreadLabel = `${analysis.slots.spread} spread`;
|
|
1237
|
+
const sellLabel = `${analysis.slots.sell} sell`;
|
|
1238
|
+
// Price Range - formatted with three-column alignment
|
|
1239
|
+
const buyPrice = analysis.gridMinPrice ? `${colors.buy}${formatCurrency(analysis.gridMinPrice)}${colors.reset}` : 'N/A';
|
|
1240
|
+
const midPrice = analysis.marketPrice ? formatCurrency(analysis.marketPrice) : 'N/A';
|
|
1241
|
+
const sellPrice = analysis.gridMaxPrice ? `${colors.sell}${formatCurrency(analysis.gridMaxPrice)}${colors.reset}` : 'N/A';
|
|
1242
|
+
const pricePrefix = ` Price: `;
|
|
1243
|
+
const slotsPrefix = ` Slots: `;
|
|
1244
|
+
// Get visual lengths (without color codes)
|
|
1245
|
+
const buyPriceVisualLen = stripColorCodes(buyPrice).length;
|
|
1246
|
+
const midPriceVisualLen = stripColorCodes(midPrice).length;
|
|
1247
|
+
const sellPriceVisualLen = stripColorCodes(sellPrice).length;
|
|
1248
|
+
const buyLabelVisualLen = buyLabel.length;
|
|
1249
|
+
const spreadLabelVisualLen = spreadLabel.length;
|
|
1250
|
+
const sellLabelVisualLen = sellLabel.length;
|
|
1251
|
+
// Calculate positions where items should end to align with bar sections
|
|
1252
|
+
const barStart = 0;
|
|
1253
|
+
const buySection = barStart + buyBarWidth;
|
|
1254
|
+
const spreadSection = buySection + spreadBarWidth;
|
|
1255
|
+
const barEnd = spreadSection + sellBarWidth;
|
|
1256
|
+
// Position items right-aligned at section boundaries, with minimum 1 space gap
|
|
1257
|
+
// Buy item: right-align to buySection
|
|
1258
|
+
const buyPriceSpacing1 = Math.max(1, buySection - buyPriceVisualLen);
|
|
1259
|
+
// Spread item: positioned to center in spread zone
|
|
1260
|
+
const spreadMid = buySection + spreadBarWidth / 2;
|
|
1261
|
+
const midPriceSpacing1 = Math.max(1, Math.round(spreadMid - midPriceVisualLen / 2) - buyPriceVisualLen);
|
|
1262
|
+
// Sell item: right-align to barEnd
|
|
1263
|
+
const sellPriceSpacing2 = Math.max(1, barEnd - sellPriceVisualLen - buyPriceVisualLen - midPriceVisualLen - midPriceSpacing1);
|
|
1264
|
+
lines.push(`${pricePrefix}${buyPrice}${' '.repeat(midPriceSpacing1)}${midPrice}${' '.repeat(Math.max(1, sellPriceSpacing2))}${sellPrice}`);
|
|
1265
|
+
// Same logic for slots line
|
|
1266
|
+
const buyLabelSpacing1 = Math.max(1, buySection - buyLabelVisualLen);
|
|
1267
|
+
const spreadMidLabel = buySection + spreadBarWidth / 2;
|
|
1268
|
+
const spreadLabelSpacing1 = Math.max(1, Math.round(spreadMidLabel - spreadLabelVisualLen / 2) - buyLabelVisualLen);
|
|
1269
|
+
const sellLabelSpacing2 = Math.max(1, barEnd - sellLabelVisualLen - buyLabelVisualLen - spreadLabelVisualLen - spreadLabelSpacing1);
|
|
1270
|
+
lines.push(`${slotsPrefix}${colors.buy}${buyLabel}${colors.reset}${' '.repeat(spreadLabelSpacing1)}${spreadLabel}${' '.repeat(Math.max(1, sellLabelSpacing2))}${colors.sell}${sellLabel}${colors.reset}`);
|
|
1271
|
+
/**
|
|
1272
|
+
* Fund Allocation Breakdown
|
|
1273
|
+
* Shows funds in each currency (quote for buy, base for sell)
|
|
1274
|
+
* Also shows cross-currency equivalent for comparison
|
|
1275
|
+
* Example: BUY 1000 BTS ā 50 XRP (at avg buy price)
|
|
1276
|
+
*/
|
|
1277
|
+
const [assetASymbol, assetBSymbol] = analysis.pair.split('/');
|
|
1278
|
+
// Fallback for null symbols (shouldn't happen after fix, but added for safety)
|
|
1279
|
+
const aSymbol = assetASymbol || 'BASE';
|
|
1280
|
+
const bSymbol = assetBSymbol || 'QUOTE';
|
|
1281
|
+
/**
|
|
1282
|
+
* Distribution Analysis
|
|
1283
|
+
* Compares slot count % with fund allocation %
|
|
1284
|
+
* Shows if one side is over/under-weighted relative to slot count
|
|
1285
|
+
* Ī (delta) = difference between slot % and fund %
|
|
1286
|
+
* Ī 0% = perfectly balanced (slots match funds)
|
|
1287
|
+
* Ī 20% = significant imbalance (e.g., 40% slots but 60% funds)
|
|
1288
|
+
*/
|
|
1289
|
+
const buySlotPct = analysis.distribution.slots.buyPercent.toFixed(1);
|
|
1290
|
+
const buyFundPct = analysis.distribution.funds.buyPercent.toFixed(1);
|
|
1291
|
+
const sellSlotPct = analysis.distribution.slots.sellPercent.toFixed(1);
|
|
1292
|
+
const sellFundPct = analysis.distribution.funds.sellPercent.toFixed(1);
|
|
1293
|
+
const buyMatch = analysis.distribution.match.buyDiff.toFixed(1);
|
|
1294
|
+
const sellMatch = analysis.distribution.match.sellDiff.toFixed(1);
|
|
1295
|
+
// Calculate spread slot percentage (totalSlots already calculated above)
|
|
1296
|
+
const spreadSlotPct = totalSlots > 0 ? ((analysis.slots.spread / totalSlots) * 100).toFixed(1) : '0.0';
|
|
1297
|
+
// Recalculate buy/sell percentages to include spread in total
|
|
1298
|
+
const buySlotPctWithSpread = totalSlots > 0 ? ((analysis.slots.buy / totalSlots) * 100).toFixed(1) : '0.0';
|
|
1299
|
+
const sellSlotPctWithSpread = totalSlots > 0 ? ((analysis.slots.sell / totalSlots) * 100).toFixed(1) : '0.0';
|
|
1300
|
+
const { bar: slotDistBar, buyWidth: slotDistBuyWidth } = createDistributionBar({
|
|
1301
|
+
activeBuy: analysis.slots.activeBuy,
|
|
1302
|
+
virtualBuy: analysis.slots.virtualBuy,
|
|
1303
|
+
spread: analysis.slots.spread,
|
|
1304
|
+
activeSell: analysis.slots.activeSell,
|
|
1305
|
+
virtualSell: analysis.slots.virtualSell
|
|
1306
|
+
});
|
|
1307
|
+
// Weight factor visualization (funds distribution across all orders)
|
|
1308
|
+
const weightBar = createWeightFactorBar(analysis.slotData?.buy, analysis.slotData?.sell, BAR_WIDTH, analysis.marketPrice || 1);
|
|
1309
|
+
lines.push(` ${slotDistBar}`);
|
|
1310
|
+
// Position delta indicator directly under the spread slot character
|
|
1311
|
+
const buyDiffVal = analysis.distribution.match.buyDiff;
|
|
1312
|
+
const signedMatch = (buyDiffVal > 0 ? '+' : '') + buyDiffVal.toFixed(1);
|
|
1313
|
+
const deltaStr = `Ī ${signedMatch}%`;
|
|
1314
|
+
const spreadStart = 11 + slotDistBuyWidth; // Position where spread character starts (11 = prefix length)
|
|
1315
|
+
lines.push(`${' '.repeat(spreadStart)}${deltaStr}`);
|
|
1316
|
+
lines.push(` Funds: ${weightBar}`);
|
|
1317
|
+
// Funds breakdown: BUY on left, SELL on right (right-aligned within its column)
|
|
1318
|
+
const buyValueStr = `${formatFundsValue(analysis.funds.buy.bts)} ${bSymbol}`;
|
|
1319
|
+
const sellValueStr = `${formatFundsValue(analysis.funds.sell.xrp)} ${aSymbol}`;
|
|
1320
|
+
const buyEquivStr = `ā ${formatFundsValue(analysis.funds.buy.xrp)} ${aSymbol}`;
|
|
1321
|
+
const sellEquivStr = `ā ${formatFundsValue(analysis.funds.sell.bts)} ${bSymbol}`;
|
|
1322
|
+
// Right column width is the maximum of sell value or sell equivalent
|
|
1323
|
+
const rightColWidth = Math.max(sellValueStr.length, sellEquivStr.length);
|
|
1324
|
+
// Right-align both sell strings within the right column width
|
|
1325
|
+
const sellValueRight = ' '.repeat(Math.max(0, rightColWidth - sellValueStr.length)) + sellValueStr;
|
|
1326
|
+
const sellEquivRight = ' '.repeat(Math.max(0, rightColWidth - sellEquivStr.length)) + sellEquivStr;
|
|
1327
|
+
// Build prefix strings and calculate their lengths
|
|
1328
|
+
const prefix1 = ` `;
|
|
1329
|
+
const prefix2 = ` `;
|
|
1330
|
+
const prefix1Len = prefix1.length;
|
|
1331
|
+
const prefix2Len = prefix2.length;
|
|
1332
|
+
// Calculate spacing to match the Slots line width (prefix + bar width)
|
|
1333
|
+
// This ensures funds breakdown lines align with the visual bar width
|
|
1334
|
+
const barLinePrefix = ` Funds: `;
|
|
1335
|
+
const targetWidth = barLinePrefix.length + BAR_WIDTH;
|
|
1336
|
+
// Spacing for each line: targetWidth - line prefix - buyValue - sellColumn
|
|
1337
|
+
const spacing1 = Math.max(2, targetWidth - prefix1Len - buyValueStr.length - rightColWidth);
|
|
1338
|
+
const spacing2 = Math.max(2, targetWidth - prefix2Len - buyEquivStr.length - rightColWidth);
|
|
1339
|
+
lines.push(`${prefix1}${colors.buy}${buyValueStr}${colors.reset}${' '.repeat(spacing1)}${colors.sell}${sellValueRight}${colors.reset}`);
|
|
1340
|
+
lines.push(`${prefix2}${buyEquivStr}${' '.repeat(spacing2)}${sellEquivRight}`);
|
|
1341
|
+
// Align pipe separators across all lines so every | sits at the same column
|
|
1342
|
+
let maxPipePos = 0;
|
|
1343
|
+
const pipeLines = [];
|
|
1344
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1345
|
+
const pipeIdx = lines[i].indexOf('|');
|
|
1346
|
+
if (pipeIdx !== -1) {
|
|
1347
|
+
pipeLines.push(i);
|
|
1348
|
+
const visualLen = stripColorCodes(lines[i].substring(0, pipeIdx)).length;
|
|
1349
|
+
if (visualLen > maxPipePos)
|
|
1350
|
+
maxPipePos = visualLen;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
for (const idx of pipeLines) {
|
|
1354
|
+
const pipeIdx = lines[idx].indexOf('|');
|
|
1355
|
+
const visualLen = stripColorCodes(lines[idx].substring(0, pipeIdx)).length;
|
|
1356
|
+
if (visualLen < maxPipePos) {
|
|
1357
|
+
lines[idx] = lines[idx].substring(0, pipeIdx) + ' '.repeat(maxPipePos - visualLen) + lines[idx].substring(pipeIdx);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
return lines.join('\n');
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* main: Entry point - analyze all order files and display results
|
|
1364
|
+
*
|
|
1365
|
+
* Flow:
|
|
1366
|
+
* 1. Get all order files from profiles/orders/ (sorted by modified date)
|
|
1367
|
+
* 2. For each file:
|
|
1368
|
+
* - Parse order data JSON
|
|
1369
|
+
* - Look up bot configuration from profiles/bots.json
|
|
1370
|
+
* - Perform comprehensive analysis
|
|
1371
|
+
* - Format and display results
|
|
1372
|
+
* 3. Handle errors gracefully (skip bad files, continue)
|
|
1373
|
+
* 4. Print summary statistics
|
|
1374
|
+
*
|
|
1375
|
+
* Error handling:
|
|
1376
|
+
* - Invalid JSON: Catch and skip file with error message
|
|
1377
|
+
* - Missing config: Display grid data only (no target comparisons)
|
|
1378
|
+
* - Empty orders directory: Display message and exit
|
|
1379
|
+
*
|
|
1380
|
+
* Output order: Files sorted by modification time (most recent first)
|
|
1381
|
+
* makes it easy to see which bots were most recently updated
|
|
1382
|
+
*/
|
|
1383
|
+
/**
|
|
1384
|
+
* generateHtmlReport: Export all analyses as a standalone HTML report.
|
|
1385
|
+
*
|
|
1386
|
+
* Produces a self-contained HTML file with visually styled cards, colored
|
|
1387
|
+
* distribution bars, and aligned metrics ā matching the terminal output's
|
|
1388
|
+
* information density in a browser-friendly format.
|
|
1389
|
+
*
|
|
1390
|
+
* @param {Array<Object>} analyses - Array of analysis results from analyzeOrder()
|
|
1391
|
+
*/
|
|
1392
|
+
function generateHtmlReport(analyses) {
|
|
1393
|
+
const cssColors = {
|
|
1394
|
+
buy: '#00ff00',
|
|
1395
|
+
buyDark: '#007700',
|
|
1396
|
+
sell: '#ff0000',
|
|
1397
|
+
sellDark: '#990000',
|
|
1398
|
+
spread: '#ffff00',
|
|
1399
|
+
cyan: '#5fffff',
|
|
1400
|
+
gray: '#949494'
|
|
1401
|
+
};
|
|
1402
|
+
const htmlColorMap = {
|
|
1403
|
+
[colors.buy]: `<span style="color:${cssColors.buy}">`,
|
|
1404
|
+
[colors.sell]: `<span style="color:${cssColors.sell}">`,
|
|
1405
|
+
[colors.buyDark]: `<span style="color:${cssColors.buyDark}">`,
|
|
1406
|
+
[colors.sellDark]: `<span style="color:${cssColors.sellDark}">`,
|
|
1407
|
+
[colors.spread]: `<span style="color:${cssColors.spread}">`,
|
|
1408
|
+
[colors.cyan]: `<span style="color:${cssColors.cyan}">`,
|
|
1409
|
+
[colors.gray]: `<span style="color:${cssColors.gray}">`,
|
|
1410
|
+
'\x1b[97m': '<span style="color:#ffffff">' // white for spread bar
|
|
1411
|
+
};
|
|
1412
|
+
function ansiToHtml(str) {
|
|
1413
|
+
let escaped = String(str)
|
|
1414
|
+
.replace(/&/g, '&')
|
|
1415
|
+
.replace(/</g, '<')
|
|
1416
|
+
.replace(/>/g, '>')
|
|
1417
|
+
.replace(/"/g, '"');
|
|
1418
|
+
let colorOpen = false;
|
|
1419
|
+
return escaped.replace(/\x1b\[[0-9;]*m/g, code => {
|
|
1420
|
+
let close = '';
|
|
1421
|
+
if (colorOpen) {
|
|
1422
|
+
colorOpen = false;
|
|
1423
|
+
close = '</span>';
|
|
1424
|
+
}
|
|
1425
|
+
if (code === colors.reset)
|
|
1426
|
+
return close;
|
|
1427
|
+
const html = htmlColorMap[code];
|
|
1428
|
+
if (!html)
|
|
1429
|
+
return close;
|
|
1430
|
+
colorOpen = true;
|
|
1431
|
+
return close + html;
|
|
1432
|
+
}) + (colorOpen ? '</span>' : '');
|
|
1433
|
+
}
|
|
1434
|
+
const reports = analyses.map(a => ansiToHtml(formatAnalysis(a))).join('\n');
|
|
1435
|
+
const html = `<!DOCTYPE html>
|
|
1436
|
+
<html lang="en">
|
|
1437
|
+
<head>
|
|
1438
|
+
<meta charset="UTF-8">
|
|
1439
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1440
|
+
<meta name="darkreader-lock">
|
|
1441
|
+
<title>Order Analysis Report</title>
|
|
1442
|
+
<style>
|
|
1443
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
1444
|
+
body { background: #0d1117; color: #c9d1d9; font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif; padding: 24px; }
|
|
1445
|
+
h1 { color: ${cssColors.cyan}; font-size: 20px; margin-bottom: 8px; }
|
|
1446
|
+
.subtitle { color: ${cssColors.gray}; font-size: 13px; margin-bottom: 24px; }
|
|
1447
|
+
.summary { margin-top: 16px; color: ${cssColors.cyan}; font-size: 13px; }
|
|
1448
|
+
pre {
|
|
1449
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
|
|
1450
|
+
font-size: 14px;
|
|
1451
|
+
line-height: 1.5;
|
|
1452
|
+
background: #000;
|
|
1453
|
+
padding: 16px;
|
|
1454
|
+
border-radius: 8px;
|
|
1455
|
+
border: 1px solid #30363d;
|
|
1456
|
+
overflow-x: auto;
|
|
1457
|
+
}
|
|
1458
|
+
</style>
|
|
1459
|
+
</head>
|
|
1460
|
+
<body>
|
|
1461
|
+
<h1>š Order Analysis</h1>
|
|
1462
|
+
<div class="subtitle">${analyses.length} bot${analyses.length !== 1 ? 's' : ''} analyzed ā ${new Date().toLocaleString()}</div>
|
|
1463
|
+
<pre>${reports}</pre>
|
|
1464
|
+
<div class="summary">Total: ${analyses.length} analyzed</div>
|
|
1465
|
+
</body>
|
|
1466
|
+
</html>`;
|
|
1467
|
+
const outPath = path.resolve('order-analysis.html');
|
|
1468
|
+
fs.writeFileSync(outPath, html, 'utf-8');
|
|
1469
|
+
console.log(`\nš Order Analysis: ${'./' + path.relative(process.cwd(), outPath)}`);
|
|
1470
|
+
}
|
|
1471
|
+
function main() {
|
|
1472
|
+
const exportHtml = process.argv.includes('--export');
|
|
1473
|
+
if (!exportHtml) {
|
|
1474
|
+
console.log(`\n${colors.cyan}š Order Analysis${colors.reset}`);
|
|
1475
|
+
console.log(`${colors.cyan}${'='.repeat(HEADER_WIDTH)}${colors.reset}`);
|
|
1476
|
+
}
|
|
1477
|
+
// Get all order files sorted by modification time (newest first)
|
|
1478
|
+
const { files, skippedCandidates } = getOrderFiles();
|
|
1479
|
+
// Handle fully empty directory case. If files were skipped, report why below.
|
|
1480
|
+
if (files.length === 0 && skippedCandidates.length === 0) {
|
|
1481
|
+
if (!exportHtml) {
|
|
1482
|
+
console.log('No order files found in profiles/orders/');
|
|
1483
|
+
}
|
|
1484
|
+
process.exit(0);
|
|
1485
|
+
}
|
|
1486
|
+
// Counters for summary statistics
|
|
1487
|
+
let analyzed = 0;
|
|
1488
|
+
let skipped = 0;
|
|
1489
|
+
const analyses = [];
|
|
1490
|
+
/**
|
|
1491
|
+
* Process each order file
|
|
1492
|
+
* Try-catch ensures one bad file doesn't stop analysis of others
|
|
1493
|
+
*/
|
|
1494
|
+
files.forEach((file, index) => {
|
|
1495
|
+
try {
|
|
1496
|
+
// Parse order file JSON
|
|
1497
|
+
const orderData = readJSON(file.path);
|
|
1498
|
+
// Per-bot file: data is the bot's entry directly (no bots wrapper)
|
|
1499
|
+
if (!orderData || !orderData.meta || !Array.isArray(orderData.grid)) {
|
|
1500
|
+
throw new Error('Not a persisted order grid');
|
|
1501
|
+
}
|
|
1502
|
+
const botKey = file.botKey;
|
|
1503
|
+
const botData = orderData;
|
|
1504
|
+
// Candidate validation already required a configured bot entry.
|
|
1505
|
+
const config = file.config || getConfiguredBotConfig(botKey, botData);
|
|
1506
|
+
if (!config) {
|
|
1507
|
+
throw new Error(`Missing configured bot entry for ${botKey}`);
|
|
1508
|
+
}
|
|
1509
|
+
// Analyze the order grid (botKey is used to load the dynamic grid snapshot)
|
|
1510
|
+
const analysis = analyzeOrder(botData, config, botKey);
|
|
1511
|
+
analyses.push(analysis);
|
|
1512
|
+
if (!exportHtml) {
|
|
1513
|
+
// Display formatted results
|
|
1514
|
+
let output = formatAnalysis(analysis);
|
|
1515
|
+
// Remove leading newline from first pair to avoid blank line after header
|
|
1516
|
+
if (index === 0) {
|
|
1517
|
+
output = output.replace(/^\n/, '');
|
|
1518
|
+
console.log(output);
|
|
1519
|
+
console.log(''); // Extra blank line after first batch
|
|
1520
|
+
}
|
|
1521
|
+
else {
|
|
1522
|
+
console.log(output);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
analyzed++;
|
|
1526
|
+
}
|
|
1527
|
+
catch (error) {
|
|
1528
|
+
// Log error but continue processing other files
|
|
1529
|
+
console.error(`\nā Error processing ${file.name}: ${error.message}`);
|
|
1530
|
+
skipped++;
|
|
1531
|
+
}
|
|
1532
|
+
});
|
|
1533
|
+
if (exportHtml) {
|
|
1534
|
+
generateHtmlReport(analyses);
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
if (skippedCandidates.length > 0) {
|
|
1538
|
+
console.log('');
|
|
1539
|
+
console.log(`${colors.cyan}${'='.repeat(HEADER_WIDTH)}${colors.reset}`);
|
|
1540
|
+
skippedCandidates.forEach(candidate => {
|
|
1541
|
+
console.log(`${colors.gray}Skipped ${candidate.name}: ${candidate.reason}${colors.reset}`);
|
|
1542
|
+
skipped++;
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1545
|
+
// Summary line
|
|
1546
|
+
console.log(`${colors.cyan}${'='.repeat(HEADER_WIDTH)}${colors.reset}`);
|
|
1547
|
+
console.log(`${colors.cyan}Summary: ${analyzed} analyzed, ${skipped} skipped${colors.reset}\n`);
|
|
1548
|
+
}
|
|
1549
|
+
// Execute analysis only when invoked as a script. When required from a test we
|
|
1550
|
+
// expose the helpers below without triggering the analyzer side effects.
|
|
1551
|
+
if (require.main === module) {
|
|
1552
|
+
main();
|
|
1553
|
+
}
|
|
1554
|
+
module.exports = {
|
|
1555
|
+
resolveAmaKey,
|
|
1556
|
+
isAmaGridPrice,
|
|
1557
|
+
readDynamicGridSnapshot,
|
|
1558
|
+
buildDynamicWeightInfo,
|
|
1559
|
+
formatWeightLine,
|
|
1560
|
+
getRawWeightValues,
|
|
1561
|
+
analyzeOrder,
|
|
1562
|
+
formatAnalysis,
|
|
1563
|
+
generateHtmlReport,
|
|
1564
|
+
colors,
|
|
1565
|
+
DYNAMIC_GRID_SNAPSHOT_MAX_AGE_MS,
|
|
1566
|
+
DYNAMIC_WEIGHT_EPSILON,
|
|
1567
|
+
};
|
|
1568
|
+
//# sourceMappingURL=analyze-orders.js.map
|