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,1613 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Maintenance runtime - periodic sync loops, grid health checks, rebalance */
|
|
3
|
+
const { createHash } = require('./crypto/sync');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const { path } = require('./path_api');
|
|
6
|
+
const { spawn } = require('child_process');
|
|
7
|
+
const { BitShares } = require('./bitshares_client');
|
|
8
|
+
const chainOrders = require('./chain_orders');
|
|
9
|
+
const { Config, hasOpenOrdersSyncLoopMsSet, getOpenOrdersSyncLoopMs } = require('./config');
|
|
10
|
+
const Grid = require('./order/grid');
|
|
11
|
+
const { ORDER_STATES, ORDER_TYPES, TIMING, BTS_PRECISION, NATIVE_CLIENT } = require('./constants');
|
|
12
|
+
const { PATHS } = require('./paths');
|
|
13
|
+
const { buildRuntimeScriptPath, isDistCodeRoot } = require('./launcher/runtime_entry');
|
|
14
|
+
const { applyGridDivergenceCorrections, loadAmaCenterSnapshot, sleep } = require('./order/utils/system');
|
|
15
|
+
const { isPm2Runtime } = require('./order/logger');
|
|
16
|
+
const { getSharedMarketAdapterRuntime } = require('./launcher/market_adapter_runtime');
|
|
17
|
+
const { resetMarketAdapterWhitelistCache, isBotDynamicWeightWhitelisted, } = require('./market_adapter_whitelist');
|
|
18
|
+
const Format = require('./order/format');
|
|
19
|
+
const { parseJsonWithComments } = require('./order/utils/system');
|
|
20
|
+
const { cloneWeightDistribution, calculateOrderCreationFees, calculateSwapInAmount, floatToBlockchainInt, blockchainToFloat } = require('./order/utils/math');
|
|
21
|
+
const { updateDynamicGridSnapshotSync } = require('../market_adapter/utils/dynamic_grid_snapshot');
|
|
22
|
+
const { reconcileGridOrders } = require('./order/grid_reconcile');
|
|
23
|
+
const { formatUnmatchedChainOrder, getSideBudget } = require('./order/utils/order');
|
|
24
|
+
const { getStorage } = require('./storage');
|
|
25
|
+
const storage = getStorage();
|
|
26
|
+
const { ensureDir, safeUnlink } = require('./utils/fs_utils');
|
|
27
|
+
const fundRegistry = require('./fund_registry');
|
|
28
|
+
const CODE_ROOT = path.join(__dirname, '..');
|
|
29
|
+
const PROFILES_DIR = PATHS.PROFILES_DIR;
|
|
30
|
+
const PROFILES_BOTS_FILE = PATHS.PROFILES.BOTS_JSON;
|
|
31
|
+
const LOGS_DIR = PATHS.LOGS_DIR;
|
|
32
|
+
const MARKET_ADAPTER_APP_NAME = 'dexbot-adapter';
|
|
33
|
+
const MARKET_ADAPTER_SCRIPT = buildRuntimeScriptPath(CODE_ROOT, ['market_adapter', 'market_adapter']);
|
|
34
|
+
const MARKET_ADAPTER_ERROR_FILE = path.join(LOGS_DIR, 'dexbot-adapter-error.log');
|
|
35
|
+
const MARKET_ADAPTER_OUT_FILE = path.join(LOGS_DIR, 'dexbot-adapter.log');
|
|
36
|
+
const MARKET_ADAPTER_TRIGGER_SOURCE = 'market_adapter/market_adapter' + (isDistCodeRoot(CODE_ROOT) ? '.js' : '.ts');
|
|
37
|
+
const MANUAL_TRIGGER_METADATA = {
|
|
38
|
+
shouldRefreshCenterPrice: true,
|
|
39
|
+
centerRefreshContext: 'manual grid resync',
|
|
40
|
+
centerRefreshLabel: 'manual grid reset',
|
|
41
|
+
resetSource: 'manual_grid_resync',
|
|
42
|
+
};
|
|
43
|
+
const MARKET_ADAPTER_TRIGGER_RESETS = Object.freeze({
|
|
44
|
+
market_adapter_bootstrap: {
|
|
45
|
+
shouldRefreshCenterPrice: true,
|
|
46
|
+
centerRefreshContext: 'AMA bootstrap grid resync',
|
|
47
|
+
centerRefreshLabel: 'AMA bootstrap grid reset',
|
|
48
|
+
},
|
|
49
|
+
market_adapter_ama_slope_delta_threshold: {
|
|
50
|
+
shouldRefreshCenterPrice: true,
|
|
51
|
+
centerRefreshContext: 'AMA slope grid resync',
|
|
52
|
+
centerRefreshLabel: 'AMA slope grid reset',
|
|
53
|
+
},
|
|
54
|
+
market_adapter_delta_threshold: {
|
|
55
|
+
shouldRefreshCenterPrice: true,
|
|
56
|
+
centerRefreshContext: 'AMA center grid resync',
|
|
57
|
+
centerRefreshLabel: 'AMA center grid reset',
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
const GRID_RESYNC_REASONS = Object.freeze({
|
|
61
|
+
...MARKET_ADAPTER_TRIGGER_RESETS,
|
|
62
|
+
manual_grid_resync: MANUAL_TRIGGER_METADATA,
|
|
63
|
+
rms_structural_grid_resync: {
|
|
64
|
+
shouldRefreshCenterPrice: true,
|
|
65
|
+
centerRefreshContext: 'RMS structural grid resync',
|
|
66
|
+
centerRefreshLabel: 'RMS structural grid resync',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
70
|
+
* Check if a bot configuration uses an AMA grid price source.
|
|
71
|
+
* @param {Object} bot - Bot configuration object
|
|
72
|
+
* @returns {boolean} True if gridPrice starts with 'ama' (ama, ama1..ama4)
|
|
73
|
+
*/
|
|
74
|
+
function usesAmaGridPrice(bot) {
|
|
75
|
+
const gridPrice = String(bot?.gridPrice || '').trim().toLowerCase();
|
|
76
|
+
return /^ama(?:[1-4])?$/.test(gridPrice);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Find a bot entry in the bots config snapshot that matches a runtime config.
|
|
80
|
+
* Matches by botKey or name.
|
|
81
|
+
* @param {import('./types').BotsConfigSnapshot} snapshot - Bots configuration snapshot
|
|
82
|
+
* @param {Object} config - Runtime bot configuration
|
|
83
|
+
* @returns {Object|null} Matched bot entry or null
|
|
84
|
+
*/
|
|
85
|
+
function findSnapshotBotForRuntimeConfig(snapshot, config) {
|
|
86
|
+
if (!snapshot || !Array.isArray(snapshot.activeBots) || !config) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const botKey = config.botKey ? String(config.botKey) : null;
|
|
90
|
+
const name = config.name ? String(config.name) : null;
|
|
91
|
+
return snapshot.activeBots.find((bot) => {
|
|
92
|
+
if (!bot)
|
|
93
|
+
return false;
|
|
94
|
+
if (botKey && String(bot.botKey || '') === botKey)
|
|
95
|
+
return true;
|
|
96
|
+
if (name && String(bot.name || '') === name)
|
|
97
|
+
return true;
|
|
98
|
+
return false;
|
|
99
|
+
}) || null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Check if a runtime bot configuration requires the market adapter.
|
|
103
|
+
* @param {import('./types').BotsConfigSnapshot} snapshot - Bots configuration snapshot
|
|
104
|
+
* @param {Object} config - Runtime bot configuration
|
|
105
|
+
* @returns {boolean} True if the bot uses AMA grid pricing
|
|
106
|
+
*/
|
|
107
|
+
function runtimeConfigNeedsMarketAdapter(snapshot, config) {
|
|
108
|
+
const snapshotBot = findSnapshotBotForRuntimeConfig(snapshot, config);
|
|
109
|
+
if (snapshotBot) {
|
|
110
|
+
return usesAmaGridPrice(snapshotBot);
|
|
111
|
+
}
|
|
112
|
+
return usesAmaGridPrice(config);
|
|
113
|
+
}
|
|
114
|
+
function countLiveGridOrders(manager, type) {
|
|
115
|
+
if (!manager)
|
|
116
|
+
return 0;
|
|
117
|
+
const active = manager.getOrdersByTypeAndState?.(type, ORDER_STATES.ACTIVE) || [];
|
|
118
|
+
const partial = manager.getOrdersByTypeAndState?.(type, ORDER_STATES.PARTIAL) || [];
|
|
119
|
+
return active.concat(partial).filter(o => o?.orderId).length;
|
|
120
|
+
}
|
|
121
|
+
function getTargetActiveOrders(config, side) {
|
|
122
|
+
const configured = Number(config?.activeOrders?.[side]);
|
|
123
|
+
return Math.max(0, Number.isFinite(configured) ? configured : 1);
|
|
124
|
+
}
|
|
125
|
+
function _hasBudgetForSide(manager, config, side) {
|
|
126
|
+
try {
|
|
127
|
+
const funds = manager?.getChainFundsSnapshot?.();
|
|
128
|
+
if (!funds)
|
|
129
|
+
return true;
|
|
130
|
+
const allocated = side === 'buy' ? (funds.allocatedBuy || 0) : (funds.allocatedSell || 0);
|
|
131
|
+
if (allocated <= 0)
|
|
132
|
+
return false;
|
|
133
|
+
const targetBuy = Math.max(0, config?.activeOrders?.buy ?? 1);
|
|
134
|
+
const targetSell = Math.max(0, config?.activeOrders?.sell ?? 1);
|
|
135
|
+
const totalTarget = targetBuy + targetSell;
|
|
136
|
+
const budget = getSideBudget(side, funds, config, totalTarget);
|
|
137
|
+
return budget > 0;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function getTargetedSyncReason(bot) {
|
|
144
|
+
if (!bot.manager || bot.config?.dryRun)
|
|
145
|
+
return null;
|
|
146
|
+
const targetBuy = getTargetActiveOrders(bot.config, 'buy');
|
|
147
|
+
const targetSell = getTargetActiveOrders(bot.config, 'sell');
|
|
148
|
+
const liveBuy = countLiveGridOrders(bot.manager, ORDER_TYPES.BUY);
|
|
149
|
+
const liveSell = countLiveGridOrders(bot.manager, ORDER_TYPES.SELL);
|
|
150
|
+
const shortfalls = [];
|
|
151
|
+
if (liveBuy < targetBuy) {
|
|
152
|
+
if (_hasBudgetForSide(bot.manager, bot.config, 'buy')) {
|
|
153
|
+
shortfalls.push(`buy ${liveBuy}/${targetBuy}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (liveSell < targetSell) {
|
|
157
|
+
if (_hasBudgetForSide(bot.manager, bot.config, 'sell')) {
|
|
158
|
+
shortfalls.push(`sell ${liveSell}/${targetSell}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const drift = bot.manager.checkFundDriftAfterFills?.();
|
|
162
|
+
if (drift && drift.isValid === false) {
|
|
163
|
+
return { reason: `fund drift: ${drift.reason}`, targetBuy, targetSell, liveBuy, liveSell, drift };
|
|
164
|
+
}
|
|
165
|
+
if (shortfalls.length > 0) {
|
|
166
|
+
return { reason: `active order shortfall: ${shortfalls.join(', ')}`, targetBuy, targetSell, liveBuy, liveSell, drift };
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
async function maybeRunTargetedDriftReconciliation(bot, context) {
|
|
171
|
+
const trigger = getTargetedSyncReason(bot);
|
|
172
|
+
if (!trigger)
|
|
173
|
+
return false;
|
|
174
|
+
const now = Date.now();
|
|
175
|
+
const cooldownMs = Number.isFinite(Number(bot._targetedDriftSyncCooldownMs))
|
|
176
|
+
? Number(bot._targetedDriftSyncCooldownMs)
|
|
177
|
+
: 60_000;
|
|
178
|
+
const lastSyncAt = Number(bot._lastTargetedDriftSyncAt || 0);
|
|
179
|
+
if (lastSyncAt > 0 && now - lastSyncAt < cooldownMs) {
|
|
180
|
+
bot._log(`[TARGETED-SYNC] Deferring ${context} reconciliation for ${Math.ceil((cooldownMs - (now - lastSyncAt)) / TIMING.MILLISECONDS_PER_SECOND)}s: ${trigger.reason}`, 'debug');
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
if (!bot.accountId || typeof chainOrders.readOpenOrders !== 'function') {
|
|
184
|
+
bot._warn(`[TARGETED-SYNC] Cannot reconcile ${context}: missing account id or readOpenOrders`);
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
bot._log(`[TARGETED-SYNC] Fetching open orders during ${context}: ${trigger.reason}`, 'warn');
|
|
188
|
+
try {
|
|
189
|
+
await bot.manager.fetchAccountTotals?.(bot.accountId);
|
|
190
|
+
const { syncResult, openOrders, aborted } = await bot._syncOpenOrdersAndProcessFills(`targeted ${context} reconciliation`);
|
|
191
|
+
if (aborted) {
|
|
192
|
+
bot._warn(`[TARGETED-SYNC] Chain sync failed during ${context}, skipping reconciliation`);
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
const remaining = getTargetedSyncReason(bot);
|
|
196
|
+
const unmatchedCount = Number(syncResult?.unmatchedChainOrders?.length || 0);
|
|
197
|
+
if (remaining || unmatchedCount > 0) {
|
|
198
|
+
bot._log(`[TARGETED-SYNC] Running startup-style reconcile during ${context}: ` +
|
|
199
|
+
`${remaining ? remaining.reason : `${unmatchedCount} unmatched chain order(s)`}`, 'warn');
|
|
200
|
+
const reconcileResult = await reconcileGridOrders({
|
|
201
|
+
manager: bot.manager,
|
|
202
|
+
config: bot.config,
|
|
203
|
+
account: bot.account,
|
|
204
|
+
privateKey: bot.privateKey,
|
|
205
|
+
chainOrders,
|
|
206
|
+
chainOpenOrders: openOrders,
|
|
207
|
+
fillLockAlreadyHeld: true,
|
|
208
|
+
});
|
|
209
|
+
await bot._executeBatchIfNeeded(reconcileResult, `targeted ${context} reconcile`);
|
|
210
|
+
}
|
|
211
|
+
// Advance cooldown only after the sync (and optional reconcile) succeeds.
|
|
212
|
+
// Previously this was set before the work, which meant a network blip
|
|
213
|
+
// would lock out the next drift for the full cooldown even though no
|
|
214
|
+
// useful work happened. With post-sync stamping, transient failures
|
|
215
|
+
// retry on the next maintenance tick.
|
|
216
|
+
bot._lastTargetedDriftSyncAt = Date.now();
|
|
217
|
+
await bot.manager.persistGrid?.();
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
bot._warn(`[TARGETED-SYNC] Failed during ${context}: ${err.message}`);
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Load and fingerprint the bots.json configuration file.
|
|
227
|
+
* @returns {import('./types').BotsConfigSnapshot} Snapshot with exists flag, fingerprint, active bots list, and adapter requirement
|
|
228
|
+
*/
|
|
229
|
+
function loadBotsConfigSnapshot() {
|
|
230
|
+
if (!storage.exists(PROFILES_BOTS_FILE)) {
|
|
231
|
+
return {
|
|
232
|
+
exists: false,
|
|
233
|
+
fingerprint: null,
|
|
234
|
+
activeBots: [],
|
|
235
|
+
needsMarketAdapter: false,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
const raw = storage.readFile(PROFILES_BOTS_FILE);
|
|
239
|
+
if (!raw || !raw.trim()) {
|
|
240
|
+
return {
|
|
241
|
+
exists: false,
|
|
242
|
+
fingerprint: null,
|
|
243
|
+
activeBots: [],
|
|
244
|
+
needsMarketAdapter: false,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
const fingerprint = createHash('sha1').update(raw).digest('hex');
|
|
248
|
+
const parsed = parseJsonWithComments(raw);
|
|
249
|
+
const bots = Array.isArray(parsed?.bots) ? parsed.bots.filter(Boolean) : [];
|
|
250
|
+
const activeBots = bots.filter((bot) => bot.active !== false);
|
|
251
|
+
return {
|
|
252
|
+
exists: true,
|
|
253
|
+
fingerprint,
|
|
254
|
+
config: parsed,
|
|
255
|
+
activeBots,
|
|
256
|
+
needsMarketAdapter: activeBots.some(usesAmaGridPrice),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Parse PM2 jlist command output to extract process names.
|
|
261
|
+
* @param {string} stdout - Raw stdout from pm2 jlist
|
|
262
|
+
* @returns {string[]} Array of process names
|
|
263
|
+
* @throws {Error} If output cannot be parsed
|
|
264
|
+
*/
|
|
265
|
+
function parsePm2JlistOutput(stdout) {
|
|
266
|
+
const output = String(stdout || '').trim();
|
|
267
|
+
if (!output)
|
|
268
|
+
return [];
|
|
269
|
+
const jsonStart = output.indexOf('[');
|
|
270
|
+
if (jsonStart === -1) {
|
|
271
|
+
throw new Error('pm2 jlist output did not contain JSON');
|
|
272
|
+
}
|
|
273
|
+
const parsed = JSON.parse(output.slice(jsonStart));
|
|
274
|
+
if (!Array.isArray(parsed)) {
|
|
275
|
+
throw new Error('pm2 jlist output was not an array');
|
|
276
|
+
}
|
|
277
|
+
return parsed.map((proc) => String(proc?.name || '')).filter(Boolean);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Run a PM2 CLI command and return stdout/stderr.
|
|
281
|
+
* @param {string[]} args - PM2 command arguments
|
|
282
|
+
* @returns {Promise<{stdout: string, stderr: string}>} Command output
|
|
283
|
+
* @throws {Error} If the command exits with non-zero code
|
|
284
|
+
*/
|
|
285
|
+
function runPm2Command(args) {
|
|
286
|
+
return new Promise((resolve, reject) => {
|
|
287
|
+
const child = spawn('pm2', args, {
|
|
288
|
+
stdio: 'pipe',
|
|
289
|
+
shell: Config.PLATFORM === 'win32',
|
|
290
|
+
});
|
|
291
|
+
let stdout = '';
|
|
292
|
+
let stderr = '';
|
|
293
|
+
child.stdout.on('data', (data) => {
|
|
294
|
+
stdout += data.toString();
|
|
295
|
+
});
|
|
296
|
+
child.stderr.on('data', (data) => {
|
|
297
|
+
stderr += data.toString();
|
|
298
|
+
});
|
|
299
|
+
child.on('close', (code) => {
|
|
300
|
+
if (code === 0) {
|
|
301
|
+
resolve({ stdout, stderr });
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
reject(new Error(stderr || stdout || `pm2 exited with code ${code}`));
|
|
305
|
+
});
|
|
306
|
+
child.on('error', reject);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Get list of running PM2 process names.
|
|
311
|
+
* @returns {Promise<string[]>} Array of process names
|
|
312
|
+
*/
|
|
313
|
+
async function getPm2ProcessNames() {
|
|
314
|
+
const { stdout } = await runPm2Command(['jlist']);
|
|
315
|
+
return parsePm2JlistOutput(stdout);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Start the market adapter process under PM2.
|
|
319
|
+
* @returns {Promise<void>}
|
|
320
|
+
*/
|
|
321
|
+
async function startMarketAdapterPm2() {
|
|
322
|
+
if (!storage.exists(LOGS_DIR)) {
|
|
323
|
+
ensureDir(LOGS_DIR);
|
|
324
|
+
}
|
|
325
|
+
const pm2Args = [
|
|
326
|
+
'start',
|
|
327
|
+
MARKET_ADAPTER_SCRIPT,
|
|
328
|
+
];
|
|
329
|
+
if (!isDistCodeRoot(CODE_ROOT)) {
|
|
330
|
+
pm2Args.push('--node-args', '--import', '--node-args', 'tsx');
|
|
331
|
+
}
|
|
332
|
+
pm2Args.push('--name', MARKET_ADAPTER_APP_NAME, '--cwd', PATHS.PROJECT_ROOT, '--output', MARKET_ADAPTER_OUT_FILE, '--error', MARKET_ADAPTER_ERROR_FILE, '--max-memory-restart', '150M', '--log-date-format', 'YY-MM-DD HH:mm:ss.SSS');
|
|
333
|
+
await runPm2Command(pm2Args);
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Stop and delete the market adapter process from PM2.
|
|
337
|
+
* @returns {Promise<void>}
|
|
338
|
+
*/
|
|
339
|
+
async function stopMarketAdapterPm2() {
|
|
340
|
+
await runPm2Command(['delete', MARKET_ADAPTER_APP_NAME]);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Synchronize market adapter state based on periodic config checks.
|
|
344
|
+
* Starts or stops the market adapter based on whether any active bot uses AMA grid pricing.
|
|
345
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
346
|
+
* @param {string} [context='periodic'] - Context label for logging
|
|
347
|
+
* @returns {Promise<import('./types').MarketAdapterSyncResult>}
|
|
348
|
+
*/
|
|
349
|
+
async function syncMarketAdapterOnPeriodicConfigCheck(bot, context = 'periodic') {
|
|
350
|
+
if (bot._marketAdapterWatchdogInFlight) {
|
|
351
|
+
return { skipped: true, reason: 'in-flight' };
|
|
352
|
+
}
|
|
353
|
+
bot._marketAdapterWatchdogInFlight = true;
|
|
354
|
+
try {
|
|
355
|
+
const snapshot = typeof bot._loadBotsConfigSnapshot === 'function'
|
|
356
|
+
? await bot._loadBotsConfigSnapshot()
|
|
357
|
+
: loadBotsConfigSnapshot();
|
|
358
|
+
const previousFingerprint = bot._marketAdapterWatchdogFingerprint || null;
|
|
359
|
+
const changed = snapshot.fingerprint !== previousFingerprint;
|
|
360
|
+
bot._marketAdapterWatchdogFingerprint = snapshot.fingerprint;
|
|
361
|
+
if (changed) {
|
|
362
|
+
bot._log(`Detected bots.json changes during ${context}; re-evaluating market adapter requirements.`);
|
|
363
|
+
}
|
|
364
|
+
if (!isPm2Runtime()) {
|
|
365
|
+
const runtime = getSharedMarketAdapterRuntime({ root: PATHS.PROJECT_ROOT });
|
|
366
|
+
const botId = String(bot.config?.botKey || bot.config?.name || bot.config?.preferredAccount || bot.config?.assetA || 'dexbot');
|
|
367
|
+
const botNeedsMarketAdapter = !!snapshot.exists && runtimeConfigNeedsMarketAdapter(snapshot, bot.config);
|
|
368
|
+
const required = !!snapshot.needsMarketAdapter || botNeedsMarketAdapter;
|
|
369
|
+
const result = await runtime.syncBot(botId, botNeedsMarketAdapter);
|
|
370
|
+
if (!snapshot.exists || !required) {
|
|
371
|
+
if (result?.stopped) {
|
|
372
|
+
bot._log(`Stopped ${MARKET_ADAPTER_APP_NAME} because no AMA grid bots are active.`, 'info');
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
changed,
|
|
376
|
+
required: false,
|
|
377
|
+
running: !!result?.running,
|
|
378
|
+
started: false,
|
|
379
|
+
stopped: !!result?.stopped,
|
|
380
|
+
mode: 'direct',
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
if (result?.started) {
|
|
384
|
+
bot._log(`Started ${MARKET_ADAPTER_APP_NAME} because AMA grid pricing is active.`, 'info');
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
changed,
|
|
388
|
+
required,
|
|
389
|
+
running: !!result?.running,
|
|
390
|
+
started: !!result?.started,
|
|
391
|
+
stopped: false,
|
|
392
|
+
mode: 'direct',
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
const getPm2ProcessNamesFn = typeof bot._getPm2ProcessNames === 'function'
|
|
396
|
+
? bot._getPm2ProcessNames.bind(bot)
|
|
397
|
+
: getPm2ProcessNames;
|
|
398
|
+
const startMarketAdapterFn = typeof bot._startMarketAdapterPm2 === 'function'
|
|
399
|
+
? bot._startMarketAdapterPm2.bind(bot)
|
|
400
|
+
: startMarketAdapterPm2;
|
|
401
|
+
const stopMarketAdapterFn = typeof bot._stopMarketAdapterPm2 === 'function'
|
|
402
|
+
? bot._stopMarketAdapterPm2.bind(bot)
|
|
403
|
+
: stopMarketAdapterPm2;
|
|
404
|
+
let processNames = [];
|
|
405
|
+
let pm2QueryFailed = false;
|
|
406
|
+
try {
|
|
407
|
+
processNames = await getPm2ProcessNamesFn();
|
|
408
|
+
}
|
|
409
|
+
catch (err) {
|
|
410
|
+
pm2QueryFailed = true;
|
|
411
|
+
bot._warn(`Could not query PM2 for ${MARKET_ADAPTER_APP_NAME}: ${err.message}. Using a direct PM2 action.`);
|
|
412
|
+
}
|
|
413
|
+
// Cross-reference config-active bots against actually running PM2 processes
|
|
414
|
+
// so we don't start the adapter for configured AMA bots that aren't running.
|
|
415
|
+
const runningActiveBots = pm2QueryFailed
|
|
416
|
+
? snapshot.activeBots
|
|
417
|
+
: snapshot.activeBots.filter((b) => processNames.includes(b.name));
|
|
418
|
+
const needsAdapterForRunningBots = runningActiveBots.some(usesAmaGridPrice);
|
|
419
|
+
if (!snapshot.exists || !needsAdapterForRunningBots) {
|
|
420
|
+
const shouldStop = pm2QueryFailed || processNames.includes(MARKET_ADAPTER_APP_NAME);
|
|
421
|
+
if (!shouldStop) {
|
|
422
|
+
return {
|
|
423
|
+
changed,
|
|
424
|
+
required: false,
|
|
425
|
+
running: false,
|
|
426
|
+
started: false,
|
|
427
|
+
stopped: false,
|
|
428
|
+
mode: 'pm2',
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
await stopMarketAdapterFn();
|
|
432
|
+
bot._log(`Stopped ${MARKET_ADAPTER_APP_NAME} because no AMA grid bots are running.`, 'info');
|
|
433
|
+
return {
|
|
434
|
+
changed,
|
|
435
|
+
required: false,
|
|
436
|
+
running: false,
|
|
437
|
+
started: false,
|
|
438
|
+
stopped: true,
|
|
439
|
+
mode: 'pm2',
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
if (processNames.includes(MARKET_ADAPTER_APP_NAME)) {
|
|
443
|
+
return {
|
|
444
|
+
changed,
|
|
445
|
+
required: true,
|
|
446
|
+
running: true,
|
|
447
|
+
started: false,
|
|
448
|
+
stopped: false,
|
|
449
|
+
mode: 'pm2',
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
await startMarketAdapterFn();
|
|
453
|
+
bot._log(`Started ${MARKET_ADAPTER_APP_NAME} because AMA grid pricing is active.`, 'info');
|
|
454
|
+
return {
|
|
455
|
+
changed,
|
|
456
|
+
required: true,
|
|
457
|
+
running: false,
|
|
458
|
+
started: true,
|
|
459
|
+
stopped: false,
|
|
460
|
+
mode: 'pm2',
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
catch (err) {
|
|
464
|
+
bot._warn(`Market adapter watchdog failed during ${context}: ${err.message}`);
|
|
465
|
+
return {
|
|
466
|
+
changed: false,
|
|
467
|
+
required: false,
|
|
468
|
+
running: false,
|
|
469
|
+
started: false,
|
|
470
|
+
stopped: false,
|
|
471
|
+
error: err.message,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
finally {
|
|
475
|
+
bot._marketAdapterWatchdogInFlight = false;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Refresh the dynamic weight distribution from the AMA center snapshot.
|
|
480
|
+
* Applies live dynamic weights if the bot is whitelisted and weights are ready.
|
|
481
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
482
|
+
* @param {string} [context='runtime'] - Context label for logging
|
|
483
|
+
* @returns {import('./types').DynamicWeightRefreshResult}
|
|
484
|
+
*/
|
|
485
|
+
function refreshDynamicWeightDistribution(bot, context = 'runtime') {
|
|
486
|
+
const baseWeights = cloneWeightDistribution(bot._baseWeightDistribution, bot.config?.weightDistribution || bot.manager?.config?.weightDistribution);
|
|
487
|
+
if (!bot.config || !bot.manager || !bot.config.botKey || !baseWeights) {
|
|
488
|
+
return {
|
|
489
|
+
applied: false,
|
|
490
|
+
source: 'static',
|
|
491
|
+
weightDistribution: baseWeights,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
const botKey = bot.config.botKey;
|
|
495
|
+
let nextWeights = baseWeights;
|
|
496
|
+
let source = 'static';
|
|
497
|
+
let snapshot = null;
|
|
498
|
+
// Re-read the shared whitelist on every refresh so live flag changes apply
|
|
499
|
+
// without requiring a bot restart.
|
|
500
|
+
resetMarketAdapterWhitelistCache();
|
|
501
|
+
if (isBotDynamicWeightWhitelisted(botKey)) {
|
|
502
|
+
snapshot = loadAmaCenterSnapshot(botKey);
|
|
503
|
+
const dw = snapshot?.dynamicWeights;
|
|
504
|
+
const liveWeights = cloneWeightDistribution(dw?.effectiveWeights);
|
|
505
|
+
if (dw?.isReady && liveWeights) {
|
|
506
|
+
const snapshotBase = cloneWeightDistribution(dw?.baseWeights);
|
|
507
|
+
const baseChanged = !snapshotBase
|
|
508
|
+
|| snapshotBase.sell !== baseWeights.sell
|
|
509
|
+
|| snapshotBase.buy !== baseWeights.buy;
|
|
510
|
+
if (baseChanged) {
|
|
511
|
+
bot._log(`Skipping stale dynamic weights (${context}): ` +
|
|
512
|
+
`snapshot base (sell=${snapshotBase?.sell}, buy=${snapshotBase?.buy}) ` +
|
|
513
|
+
`!= config (sell=${baseWeights.sell}, buy=${baseWeights.buy})`, 'warn');
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
nextWeights = liveWeights;
|
|
517
|
+
source = 'dynamic';
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
bot.config.weightDistribution = { ...nextWeights };
|
|
522
|
+
if (bot.manager?.config) {
|
|
523
|
+
bot.manager.config.weightDistribution = { ...nextWeights };
|
|
524
|
+
}
|
|
525
|
+
if (source === 'dynamic') {
|
|
526
|
+
bot._log(`Applied live dynamic weights (${context}): sell=${nextWeights.sell} buy=${nextWeights.buy}`, 'info');
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
applied: source === 'dynamic',
|
|
530
|
+
source,
|
|
531
|
+
weightDistribution: nextWeights,
|
|
532
|
+
snapshotUpdatedAt: snapshot?.updatedAt || null,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Read and parse a trigger file's metadata payload.
|
|
537
|
+
* Determines whether the trigger originated from the market adapter or was manual.
|
|
538
|
+
* @param {string} triggerFile - Path to the trigger file
|
|
539
|
+
* @returns {import('./types').GridResyncMetadata} Parsed trigger metadata
|
|
540
|
+
*/
|
|
541
|
+
function readTriggerMetadata(triggerFile) {
|
|
542
|
+
const manualTriggerMetadata = (payload = null) => ({
|
|
543
|
+
...buildGridResyncMetadata('manual_grid_resync'),
|
|
544
|
+
payload,
|
|
545
|
+
});
|
|
546
|
+
const marketAdapterTriggerMetadata = (payload) => {
|
|
547
|
+
const reason = String(payload?.reason || '').trim();
|
|
548
|
+
return {
|
|
549
|
+
...buildGridResyncMetadata(reason || 'market_adapter_grid_resync'),
|
|
550
|
+
payload,
|
|
551
|
+
};
|
|
552
|
+
};
|
|
553
|
+
try {
|
|
554
|
+
const raw = storage.readFile(triggerFile).trim();
|
|
555
|
+
if (!raw) {
|
|
556
|
+
// An empty trigger is the legacy/manual CLI reset signal.
|
|
557
|
+
return manualTriggerMetadata();
|
|
558
|
+
}
|
|
559
|
+
const payload = JSON.parse(raw);
|
|
560
|
+
const source = String(payload?.source || '').trim();
|
|
561
|
+
return source === MARKET_ADAPTER_TRIGGER_SOURCE
|
|
562
|
+
? marketAdapterTriggerMetadata(payload)
|
|
563
|
+
: manualTriggerMetadata(payload);
|
|
564
|
+
}
|
|
565
|
+
catch (_) {
|
|
566
|
+
return manualTriggerMetadata();
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Build grid resync metadata from a reason string.
|
|
571
|
+
* Maps known reason strings to structured metadata with refresh flags.
|
|
572
|
+
* @param {string} reason - Resync reason identifier (e.g. 'manual_grid_resync', 'rms_structural_grid_resync')
|
|
573
|
+
* @returns {import('./types').GridResyncMetadata}
|
|
574
|
+
*/
|
|
575
|
+
function buildGridResyncMetadata(reason) {
|
|
576
|
+
const resetSource = String(reason || '').trim() || 'dexbot_grid_resync';
|
|
577
|
+
const defaults = {
|
|
578
|
+
shouldRefreshCenterPrice: false,
|
|
579
|
+
centerRefreshContext: 'grid resync',
|
|
580
|
+
centerRefreshLabel: 'grid resync',
|
|
581
|
+
};
|
|
582
|
+
const marketAdapterUnknown = resetSource === 'market_adapter_grid_resync'
|
|
583
|
+
? {
|
|
584
|
+
centerRefreshContext: 'market adapter grid resync',
|
|
585
|
+
centerRefreshLabel: 'market adapter grid reset',
|
|
586
|
+
}
|
|
587
|
+
: null;
|
|
588
|
+
return {
|
|
589
|
+
...defaults,
|
|
590
|
+
...marketAdapterUnknown,
|
|
591
|
+
...GRID_RESYNC_REASONS[resetSource],
|
|
592
|
+
resetSource,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Build grid resync options from a reason string or metadata object.
|
|
597
|
+
* @param {string|import('./types').GridResyncMetadata} reasonOrMetadata - Reason string or metadata object
|
|
598
|
+
* @returns {import('./types').GridResyncOptions}
|
|
599
|
+
*/
|
|
600
|
+
function buildGridResyncOptions(reasonOrMetadata) {
|
|
601
|
+
const metadata = typeof reasonOrMetadata === 'string'
|
|
602
|
+
? buildGridResyncMetadata(reasonOrMetadata)
|
|
603
|
+
: reasonOrMetadata;
|
|
604
|
+
return {
|
|
605
|
+
refreshCenterPrice: !!metadata?.shouldRefreshCenterPrice,
|
|
606
|
+
centerRefreshContext: metadata?.centerRefreshContext,
|
|
607
|
+
centerRefreshLabel: metadata?.centerRefreshLabel,
|
|
608
|
+
resetSource: metadata?.resetSource,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Promote the AMA center price to the grid center price in the dynamic grid snapshot.
|
|
613
|
+
* Used during grid resets to align the grid center with the latest AMA calculation.
|
|
614
|
+
* @param {string} botKey - Bot identifier key
|
|
615
|
+
* @returns {boolean} True if promotion succeeded
|
|
616
|
+
*/
|
|
617
|
+
function promoteAmaCenterSnapshotForGridReset(botKey) {
|
|
618
|
+
if (!botKey)
|
|
619
|
+
return false;
|
|
620
|
+
// Full grid resets rebuild from the latest AMA center. The active grid
|
|
621
|
+
// baseline is promoted to that value before recalculation, while the raw
|
|
622
|
+
// AMA output remains intact in amaCenterPrice for diagnostics.
|
|
623
|
+
const snapshotPath = path.join(PATHS.ORDERS_DIR, `${botKey}.dynamicgrid.json`);
|
|
624
|
+
try {
|
|
625
|
+
const result = updateDynamicGridSnapshotSync(snapshotPath, (snapshot) => {
|
|
626
|
+
const amaCenterPrice = Number(snapshot?.amaCenterPrice);
|
|
627
|
+
if (!Number.isFinite(amaCenterPrice) || amaCenterPrice <= 0) {
|
|
628
|
+
return { ok: false, write: false };
|
|
629
|
+
}
|
|
630
|
+
const currentCenterPrice = Number(snapshot?.gridCenterPrice ?? snapshot?.centerPrice);
|
|
631
|
+
if (Number.isFinite(currentCenterPrice) && currentCenterPrice === amaCenterPrice) {
|
|
632
|
+
return { write: false };
|
|
633
|
+
}
|
|
634
|
+
return {
|
|
635
|
+
...snapshot,
|
|
636
|
+
gridCenterPrice: amaCenterPrice,
|
|
637
|
+
centerPrice: amaCenterPrice,
|
|
638
|
+
updatedAt: new Date().toISOString(),
|
|
639
|
+
};
|
|
640
|
+
});
|
|
641
|
+
return result.ok;
|
|
642
|
+
}
|
|
643
|
+
catch (_) {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Update the grid reset metadata (last reset timestamp and source) in the dynamic grid snapshot.
|
|
649
|
+
* @param {string} botKey - Bot identifier key
|
|
650
|
+
* @param {Object} [options] - Reset metadata options
|
|
651
|
+
* @param {string} [options.resetAt] - ISO timestamp for the reset (defaults to now)
|
|
652
|
+
* @param {string} [options.resetSource] - Source label for the reset (defaults to 'dexbot_grid_resync')
|
|
653
|
+
* @returns {boolean} True if metadata was written
|
|
654
|
+
*/
|
|
655
|
+
function updateBotGridResetMetadata(botKey, options = {}) {
|
|
656
|
+
if (!botKey)
|
|
657
|
+
return false;
|
|
658
|
+
const resetAt = options.resetAt || new Date().toISOString();
|
|
659
|
+
const resetSource = options.resetSource || 'dexbot_grid_resync';
|
|
660
|
+
const snapshotPath = path.join(PATHS.ORDERS_DIR, `${botKey}.dynamicgrid.json`);
|
|
661
|
+
try {
|
|
662
|
+
const result = updateDynamicGridSnapshotSync(snapshotPath, (snapshot) => {
|
|
663
|
+
const gridCenterPrice = Number(snapshot?.gridCenterPrice ?? snapshot?.centerPrice);
|
|
664
|
+
if (!Number.isFinite(gridCenterPrice) || gridCenterPrice <= 0) {
|
|
665
|
+
return { ok: false, write: false };
|
|
666
|
+
}
|
|
667
|
+
return {
|
|
668
|
+
...snapshot,
|
|
669
|
+
gridCenterPrice,
|
|
670
|
+
centerPrice: gridCenterPrice,
|
|
671
|
+
lastGridResetAt: resetAt,
|
|
672
|
+
lastGridResetSource: resetSource,
|
|
673
|
+
updatedAt: resetAt,
|
|
674
|
+
};
|
|
675
|
+
});
|
|
676
|
+
return result.ok && result.written;
|
|
677
|
+
}
|
|
678
|
+
catch (_) {
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Perform a full grid resync: reload config, optionally refresh center price,
|
|
684
|
+
* recalculate the grid, persist, and record reset metadata.
|
|
685
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
686
|
+
* @param {import('./types').GridResyncOptions} [options] - Grid resync options
|
|
687
|
+
* @returns {Promise<boolean>} True if resync succeeded
|
|
688
|
+
*/
|
|
689
|
+
function performGridResync(bot, options = {}) {
|
|
690
|
+
const self = bot;
|
|
691
|
+
let success = false;
|
|
692
|
+
const refreshCenterPrice = !!options.refreshCenterPrice;
|
|
693
|
+
const centerRefreshContext = options.centerRefreshContext || (refreshCenterPrice ? 'grid reset recenter' : 'grid resync');
|
|
694
|
+
const centerRefreshLabel = options.centerRefreshLabel || (refreshCenterPrice ? 'grid reset' : 'grid resync');
|
|
695
|
+
const resetSource = options.resetSource || (refreshCenterPrice ? 'manual_grid_resync' : 'dexbot_grid_resync');
|
|
696
|
+
const idleDelayMs = getMaintenanceIdleDelayMs(self);
|
|
697
|
+
if (idleDelayMs > 0) {
|
|
698
|
+
self._log(`[MAINT-IDLE] Deferring grid resync until bot is idle` +
|
|
699
|
+
` (next check in ${Math.ceil(idleDelayMs / TIMING.MILLISECONDS_PER_SECOND)}s)`, 'info');
|
|
700
|
+
scheduleDeferredGridResync(self, options);
|
|
701
|
+
return Promise.resolve(false);
|
|
702
|
+
}
|
|
703
|
+
self.manager.startBootstrap();
|
|
704
|
+
self._log('Grid regeneration triggered. Performing full grid resync...');
|
|
705
|
+
return (async () => {
|
|
706
|
+
try {
|
|
707
|
+
try {
|
|
708
|
+
const content = storage.readFile(PROFILES_BOTS_FILE);
|
|
709
|
+
const allBotsConfig = parseJsonWithComments(content).bots || [];
|
|
710
|
+
const myName = self.config.name;
|
|
711
|
+
const updatedBot = allBotsConfig.find(b => b.name === myName);
|
|
712
|
+
if (updatedBot) {
|
|
713
|
+
self._log(`Reloaded configuration for bot '${myName}'`);
|
|
714
|
+
const oldKey = self.config.botKey;
|
|
715
|
+
const oldIndex = self.config.botIndex;
|
|
716
|
+
self.config = { ...updatedBot, botKey: oldKey, botIndex: oldIndex };
|
|
717
|
+
self.manager.config = { ...self.manager.config, ...self.config };
|
|
718
|
+
self._baseWeightDistribution = cloneWeightDistribution(updatedBot.weightDistribution, self._baseWeightDistribution);
|
|
719
|
+
refreshDynamicWeightDistribution(self, 'grid resync');
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
catch (e) {
|
|
723
|
+
self._warn(`Failed to reload config during resync (using current settings): ${e.message}`);
|
|
724
|
+
}
|
|
725
|
+
if (refreshCenterPrice) {
|
|
726
|
+
if (promoteAmaCenterSnapshotForGridReset(self.config?.botKey)) {
|
|
727
|
+
self._log(`Refreshed AMA center snapshot for ${centerRefreshLabel}.`, 'info');
|
|
728
|
+
refreshDynamicWeightDistribution(self, centerRefreshContext);
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
self._warn(`${centerRefreshLabel} requested but AMA center snapshot could not be refreshed.`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
// Config was reloaded above but center price didn't change — still need
|
|
736
|
+
// fresh weights so cancelDustOrders uses live distribution (Issue M).
|
|
737
|
+
refreshDynamicWeightDistribution(self, 'grid resync');
|
|
738
|
+
}
|
|
739
|
+
const readFn = () => chainOrders.readOpenOrders(self.accountId);
|
|
740
|
+
await Grid.recalculateGrid(self.manager, {
|
|
741
|
+
readOpenOrdersFn: readFn,
|
|
742
|
+
chainOrders,
|
|
743
|
+
account: self.account,
|
|
744
|
+
privateKey: self.privateKey,
|
|
745
|
+
config: self.config,
|
|
746
|
+
});
|
|
747
|
+
self.manager.funds.btsFeesOwed = 0;
|
|
748
|
+
await self.manager.persistGrid();
|
|
749
|
+
success = true;
|
|
750
|
+
if (updateBotGridResetMetadata(self.config?.botKey, {
|
|
751
|
+
resetAt: new Date().toISOString(),
|
|
752
|
+
resetSource,
|
|
753
|
+
})) {
|
|
754
|
+
self._log('Recorded grid reset metadata for dynamic grid state.', 'info');
|
|
755
|
+
}
|
|
756
|
+
safeUnlink(self.triggerFile);
|
|
757
|
+
self._log('Removed trigger file.');
|
|
758
|
+
// Re-detect and cancel dust immediately after full resync.
|
|
759
|
+
if (!self._shuttingDown) {
|
|
760
|
+
try {
|
|
761
|
+
const resyncHealth = await self.manager.checkGridHealth(self.updateOrdersOnChainPlan?.bind(self));
|
|
762
|
+
await cancelDustOrders(self, {
|
|
763
|
+
buy: resyncHealth.buyDustOrders,
|
|
764
|
+
sell: resyncHealth.sellDustOrders,
|
|
765
|
+
fillLockAlreadyHeld: true,
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
catch (_dustErr) {
|
|
769
|
+
self._warn(`[DUST] Post-resync dust cancel failed: ${_dustErr.message}`);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
catch (err) {
|
|
774
|
+
self._log(`Error during triggered resync: ${err.message}`, 'error');
|
|
775
|
+
}
|
|
776
|
+
finally {
|
|
777
|
+
self.manager.finishBootstrap();
|
|
778
|
+
}
|
|
779
|
+
return success;
|
|
780
|
+
})();
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Handle a pending trigger file detected at startup or during runtime.
|
|
784
|
+
* Processes the trigger and performs a grid resync if the trigger file exists.
|
|
785
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
786
|
+
* @returns {Promise<boolean>} True if reset was handled successfully
|
|
787
|
+
*/
|
|
788
|
+
async function handlePendingTriggerReset(bot) {
|
|
789
|
+
if (!storage.exists(bot.triggerFile)) {
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
bot._log('Pending trigger file detected. Processing reset before startup...');
|
|
793
|
+
const triggerInfo = readTriggerMetadata(bot.triggerFile);
|
|
794
|
+
let resetSucceeded = false;
|
|
795
|
+
await bot.manager._fillProcessingLock.acquire(async () => {
|
|
796
|
+
resetSucceeded = await performGridResync(bot, buildGridResyncOptions(triggerInfo));
|
|
797
|
+
});
|
|
798
|
+
if (!resetSucceeded) {
|
|
799
|
+
bot._warn('Pending trigger reset failed. Continuing with normal startup path.');
|
|
800
|
+
}
|
|
801
|
+
return resetSucceeded;
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Set up a file watcher on the profiles directory to detect trigger file creation.
|
|
805
|
+
* When a trigger file appears, debounces and processes the grid resync.
|
|
806
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
807
|
+
* @returns {Promise<void>}
|
|
808
|
+
*/
|
|
809
|
+
async function setupTriggerFileDetection(bot) {
|
|
810
|
+
if (bot._triggerWatcher && typeof bot._triggerWatcher.close === 'function') {
|
|
811
|
+
bot._triggerWatcher.close();
|
|
812
|
+
bot._triggerWatcher = null;
|
|
813
|
+
}
|
|
814
|
+
if (bot._triggerDebounceTimer) {
|
|
815
|
+
clearTimeout(bot._triggerDebounceTimer);
|
|
816
|
+
bot._triggerDebounceTimer = null;
|
|
817
|
+
}
|
|
818
|
+
try {
|
|
819
|
+
bot._triggerWatcher = fs.watch(PROFILES_DIR, (eventType, filename) => {
|
|
820
|
+
try {
|
|
821
|
+
if (bot._shuttingDown)
|
|
822
|
+
return;
|
|
823
|
+
if (filename === path.basename(bot.triggerFile)) {
|
|
824
|
+
if ((eventType === 'rename' || eventType === 'change') && storage.exists(bot.triggerFile)) {
|
|
825
|
+
if (bot._triggerDebounceTimer)
|
|
826
|
+
clearTimeout(bot._triggerDebounceTimer);
|
|
827
|
+
bot._triggerDebounceTimer = setTimeout(() => {
|
|
828
|
+
bot._triggerDebounceTimer = null;
|
|
829
|
+
// Re-check shutdown: the fs.watch callback checked
|
|
830
|
+
// _shuttingDown at debounce-schedule time, but the
|
|
831
|
+
// 200ms delay can outlive the start of shutdown.
|
|
832
|
+
// Acquiring the fill lock with a torn-down manager
|
|
833
|
+
// would be a no-op-or-error at best and a use-after-
|
|
834
|
+
// free at worst.
|
|
835
|
+
if (bot._shuttingDown || !bot.manager?._fillProcessingLock)
|
|
836
|
+
return;
|
|
837
|
+
const triggerInfo = readTriggerMetadata(bot.triggerFile);
|
|
838
|
+
bot.manager._fillProcessingLock.acquire(async () => {
|
|
839
|
+
if (bot._shuttingDown)
|
|
840
|
+
return;
|
|
841
|
+
const ok = await performGridResync(bot, buildGridResyncOptions(triggerInfo));
|
|
842
|
+
if (!ok) {
|
|
843
|
+
bot._warn('Runtime trigger reset failed; retaining existing grid state.');
|
|
844
|
+
}
|
|
845
|
+
}).catch(err => {
|
|
846
|
+
bot._warn(`Trigger reset lock error: ${err.message}`);
|
|
847
|
+
bot.manager._recoveryState = bot.manager._recoveryState || {};
|
|
848
|
+
bot.manager._recoveryState.lastFailureAt = Date.now();
|
|
849
|
+
});
|
|
850
|
+
}, 200);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
catch (err) {
|
|
855
|
+
bot._warn(`fs.watch handler error: ${err && err.message ? err.message : err}`);
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
catch (err) {
|
|
860
|
+
bot._warn(`Failed to setup file watcher: ${err.message}`);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Perform periodic grid health checks (divergence, spread condition, dust detection).
|
|
865
|
+
* Called as part of the periodic blockchain fetch interval.
|
|
866
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
867
|
+
* @returns {Promise<void>}
|
|
868
|
+
*/
|
|
869
|
+
async function performPeriodicGridChecks(bot) {
|
|
870
|
+
if (typeof bot._runGridMaintenance === 'function') {
|
|
871
|
+
await bot._runGridMaintenance('periodic', { fillLockAlreadyHeld: true });
|
|
872
|
+
}
|
|
873
|
+
else {
|
|
874
|
+
await runGridMaintenance(bot, 'periodic', { fillLockAlreadyHeld: true });
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Check if the continuous open-orders sync loop is enabled.
|
|
879
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
880
|
+
* @returns {boolean} True if the sync loop is enabled in TIMING config
|
|
881
|
+
*/
|
|
882
|
+
function isOpenOrdersSyncLoopEnabled(bot) {
|
|
883
|
+
if (bot.config?.timing?.openOrdersSyncLoopEnabled !== undefined) {
|
|
884
|
+
return !!bot.config.timing.openOrdersSyncLoopEnabled;
|
|
885
|
+
}
|
|
886
|
+
return !!TIMING.OPEN_ORDERS_SYNC_LOOP_ENABLED;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Start the continuous open-orders sync loop.
|
|
890
|
+
* Periodically reads on-chain orders and synchronizes with the grid manager.
|
|
891
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
892
|
+
*/
|
|
893
|
+
function startOpenOrdersSyncLoop(bot) {
|
|
894
|
+
if (bot._mainLoopPromise)
|
|
895
|
+
return;
|
|
896
|
+
const hasEnvLoopDelay = hasOpenOrdersSyncLoopMsSet();
|
|
897
|
+
const loopDelayRaw = getOpenOrdersSyncLoopMs();
|
|
898
|
+
const configuredLoopDelayMs = hasEnvLoopDelay && loopDelayRaw !== undefined ? loopDelayRaw : Number(TIMING.RUN_LOOP_DEFAULT_MS);
|
|
899
|
+
const loopDelayMs = Number.isFinite(configuredLoopDelayMs) && configuredLoopDelayMs > 0
|
|
900
|
+
? configuredLoopDelayMs
|
|
901
|
+
: Number(TIMING.RUN_LOOP_DEFAULT_MS);
|
|
902
|
+
if (hasEnvLoopDelay && loopDelayMs !== configuredLoopDelayMs) {
|
|
903
|
+
bot._warn(`Invalid OPEN_ORDERS_SYNC_LOOP_MS='${Config._OPEN_ORDERS_SYNC_LOOP_MS_RAW}'. Falling back to default ${TIMING.RUN_LOOP_DEFAULT_MS}ms.`);
|
|
904
|
+
}
|
|
905
|
+
bot._mainLoopActive = true;
|
|
906
|
+
bot._log(`Open-orders sync loop started (every ${loopDelayMs}ms, dryRun=${!!bot.config.dryRun})`);
|
|
907
|
+
const readOpenOrdersFn = chainOrders.readOpenOrders;
|
|
908
|
+
bot._mainLoopPromise = (async () => {
|
|
909
|
+
while (bot._mainLoopActive && !bot._shuttingDown) {
|
|
910
|
+
try {
|
|
911
|
+
if (bot.manager && bot.accountId && !bot.config.dryRun) {
|
|
912
|
+
if (!bot.manager._fillProcessingLock.isLocked() &&
|
|
913
|
+
bot.manager._fillProcessingLock.getQueueLength() === 0) {
|
|
914
|
+
await bot.manager._fillProcessingLock.acquire(async () => {
|
|
915
|
+
const chainOpenOrders = await readOpenOrdersFn.call(chainOrders, bot.accountId);
|
|
916
|
+
const syncResult = await bot.manager.synchronizeWithChain(chainOpenOrders, 'readOpenOrders', { fillLockAlreadyHeld: true });
|
|
917
|
+
if (syncResult?.filledOrders && syncResult.filledOrders.length > 0) {
|
|
918
|
+
bot._log(`Open-orders sync loop: ${syncResult.filledOrders.length} grid order(s) found filled on-chain. Triggering rebalance.`, 'info');
|
|
919
|
+
bot._markGridActivity?.('open-orders sync fill');
|
|
920
|
+
const batchResult = await bot._processFillsWithBatching(syncResult.filledOrders, new Set(), 'open-orders sync fill rebalance');
|
|
921
|
+
if (!batchResult?.aborted) {
|
|
922
|
+
await bot.manager.persistGrid();
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
// Run grid health / dust detection after every sync tick so
|
|
926
|
+
// partial-only fills that reduced an order below the dust
|
|
927
|
+
// threshold (but did not trigger the full-fill gate in the
|
|
928
|
+
// main processFills path) are caught promptly instead of
|
|
929
|
+
// waiting up to BLOCKCHAIN_FETCH_INTERVAL_MIN.
|
|
930
|
+
await performPeriodicGridChecks(bot);
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
catch (err) {
|
|
936
|
+
bot._warn(`Order manager loop error: ${err.message}`);
|
|
937
|
+
}
|
|
938
|
+
await sleep(loopDelayMs);
|
|
939
|
+
}
|
|
940
|
+
})().catch(err => {
|
|
941
|
+
bot._warn(`Open-orders sync loop failed: ${err && err.message ? err.message : err}`);
|
|
942
|
+
}).finally(() => {
|
|
943
|
+
bot._mainLoopPromise = null;
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Stop the continuous open-orders sync loop.
|
|
948
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
949
|
+
* @returns {Promise<void>}
|
|
950
|
+
*/
|
|
951
|
+
async function stopOpenOrdersSyncLoop(bot) {
|
|
952
|
+
bot._mainLoopActive = false;
|
|
953
|
+
if (bot._mainLoopPromise) {
|
|
954
|
+
await bot._mainLoopPromise;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Set up the periodic blockchain fetch interval.
|
|
959
|
+
* Periodically fetches account totals and syncs open orders from the blockchain.
|
|
960
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
961
|
+
*/
|
|
962
|
+
function setupBlockchainFetchInterval(bot) {
|
|
963
|
+
let intervalMin = bot.config?.timing?.BLOCKCHAIN_FETCH_INTERVAL_MIN;
|
|
964
|
+
// Use the per-instance override if set (e.g., from fund registry shared-account detection)
|
|
965
|
+
if (typeof bot._blockchainFetchIntervalMin === 'number' && Number.isFinite(bot._blockchainFetchIntervalMin) && bot._blockchainFetchIntervalMin > 0) {
|
|
966
|
+
intervalMin = bot._blockchainFetchIntervalMin;
|
|
967
|
+
}
|
|
968
|
+
else if (bot.config?.preferredAccount) {
|
|
969
|
+
// Fallback: check fund registry for shared accounts
|
|
970
|
+
try {
|
|
971
|
+
if (fundRegistry.isSharedAccount(bot.config.preferredAccount)) {
|
|
972
|
+
intervalMin = TIMING.SHARED_ACCOUNT_FETCH_INTERVAL_MIN;
|
|
973
|
+
bot._blockchainFetchIntervalMin = intervalMin;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
catch (_err) {
|
|
977
|
+
bot?._warn?.(`Registry unavailable for shared-account interval check: ${_err.message}`);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
syncMarketAdapterOnPeriodicConfigCheck(bot, 'startup blockchain fetch setup')
|
|
981
|
+
.catch((err) => {
|
|
982
|
+
bot._warn(`Market adapter watchdog failed during startup blockchain fetch setup: ${err.message}`);
|
|
983
|
+
});
|
|
984
|
+
if (bot._blockchainFetchInterval !== null && bot._blockchainFetchInterval !== undefined) {
|
|
985
|
+
stopBlockchainFetchInterval(bot);
|
|
986
|
+
}
|
|
987
|
+
if (!Number.isFinite(intervalMin) || intervalMin <= 0) {
|
|
988
|
+
bot._log(`Blockchain fetch interval disabled (value: ${intervalMin}). Periodic blockchain updates will not run.`);
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
if (!bot.manager || typeof bot.manager.fetchAccountTotals !== 'function') {
|
|
992
|
+
bot._warn('Cannot start blockchain fetch interval: manager or fetchAccountTotals method missing');
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
if (!bot.accountId) {
|
|
996
|
+
bot._warn('Cannot start blockchain fetch interval: account ID not available');
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
const intervalMs = intervalMin * 60 * TIMING.MILLISECONDS_PER_SECOND;
|
|
1000
|
+
bot._blockchainFetchInterval = setInterval(async () => {
|
|
1001
|
+
// Skip if shutdown has begun between the previous tick and now:
|
|
1002
|
+
// there is no point acquiring _fillProcessingLock or making
|
|
1003
|
+
// chain / daemon calls once we are tearing down. The lock would
|
|
1004
|
+
// serialize correctly, but we would still do wasted work
|
|
1005
|
+
// (syncMarketAdapter, fetchAccountTotals, readOpenOrders)
|
|
1006
|
+
// during shutdown.
|
|
1007
|
+
if (bot._shuttingDown)
|
|
1008
|
+
return;
|
|
1009
|
+
// Guard against overlapping ticks: if the previous tick is still in
|
|
1010
|
+
// flight (slow chain / stall), skip rather than queue a second
|
|
1011
|
+
// periodic fetch. The fill lock below would still serialize the
|
|
1012
|
+
// work, but the second tick would waste a syncMarketAdapter call
|
|
1013
|
+
// and a fetchAccountTotals call while waiting.
|
|
1014
|
+
if (bot._blockchainFetchInFlight)
|
|
1015
|
+
return;
|
|
1016
|
+
bot._blockchainFetchInFlight = true;
|
|
1017
|
+
try {
|
|
1018
|
+
try {
|
|
1019
|
+
await syncMarketAdapterOnPeriodicConfigCheck(bot, 'periodic blockchain fetch');
|
|
1020
|
+
await bot.manager._fillProcessingLock.acquire(async () => {
|
|
1021
|
+
if (bot.manager.accountant && typeof bot.manager.accountant.resetRecoveryState === 'function') {
|
|
1022
|
+
bot.manager.accountant.resetRecoveryState();
|
|
1023
|
+
}
|
|
1024
|
+
else {
|
|
1025
|
+
bot.manager._recoveryAttempted = false;
|
|
1026
|
+
}
|
|
1027
|
+
bot._log(`Fetching blockchain account values (interval: every ${intervalMin}min)`);
|
|
1028
|
+
await bot.manager.fetchAccountTotals(bot.accountId);
|
|
1029
|
+
let chainOpenOrders = [];
|
|
1030
|
+
if (!bot.config.dryRun) {
|
|
1031
|
+
try {
|
|
1032
|
+
chainOpenOrders = await chainOrders.readOpenOrders(bot.accountId);
|
|
1033
|
+
const syncResult = await bot.manager.synchronizeWithChain(chainOpenOrders, 'periodicBlockchainFetch', { fillLockAlreadyHeld: true });
|
|
1034
|
+
if (syncResult.filledOrders && syncResult.filledOrders.length > 0) {
|
|
1035
|
+
bot._log(`Periodic sync: ${syncResult.filledOrders.length} grid order(s) found filled on-chain. Triggering rebalance.`, 'info');
|
|
1036
|
+
bot._markGridActivity?.('periodic sync fill rebalance');
|
|
1037
|
+
const batchResult = await bot._processFillsWithBatching(syncResult.filledOrders, new Set(), 'periodic sync fill rebalance');
|
|
1038
|
+
if (!batchResult?.aborted) {
|
|
1039
|
+
await bot.manager.persistGrid();
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
if (syncResult.unmatchedChainOrders && syncResult.unmatchedChainOrders.length > 0) {
|
|
1043
|
+
const sample = syncResult.unmatchedChainOrders
|
|
1044
|
+
.slice(0, 3)
|
|
1045
|
+
.map(formatUnmatchedChainOrder)
|
|
1046
|
+
.join(' | ');
|
|
1047
|
+
bot._log(`Periodic sync: ${syncResult.unmatchedChainOrders.length} chain order(s) not in grid ` +
|
|
1048
|
+
`(surplus/divergence)${sample ? `: ${sample}` : ''}`, 'warn');
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
catch (err) {
|
|
1052
|
+
bot._warn(`Error reading open orders during periodic fetch: ${err.message}`);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
await performPeriodicGridChecks(bot);
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
catch (err) {
|
|
1059
|
+
bot._warn(`Error during periodic blockchain fetch: ${err && err.message ? err.message : err}`);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
finally {
|
|
1063
|
+
bot._blockchainFetchInFlight = false;
|
|
1064
|
+
}
|
|
1065
|
+
}, intervalMs);
|
|
1066
|
+
if (typeof bot._blockchainFetchInterval.unref === 'function') {
|
|
1067
|
+
bot._blockchainFetchInterval.unref();
|
|
1068
|
+
}
|
|
1069
|
+
bot._log(`Started periodic blockchain fetch interval: every ${intervalMin} minute(s)`);
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Stop the periodic blockchain fetch interval.
|
|
1073
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
1074
|
+
*/
|
|
1075
|
+
function stopBlockchainFetchInterval(bot) {
|
|
1076
|
+
if (bot._blockchainFetchInterval !== null && bot._blockchainFetchInterval !== undefined) {
|
|
1077
|
+
clearInterval(bot._blockchainFetchInterval);
|
|
1078
|
+
bot._blockchainFetchInterval = null;
|
|
1079
|
+
bot._log('Stopped periodic blockchain fetch interval');
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Release the market adapter runtime for a bot.
|
|
1084
|
+
* In PM2 mode this is a no-op; in direct mode it calls the shared runtime's releaseBot.
|
|
1085
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
1086
|
+
* @param {string} botId - Bot identifier
|
|
1087
|
+
* @param {string} [context='shutdown'] - Context label for logging
|
|
1088
|
+
* @returns {Promise<import('./types').MarketAdapterReleaseResult>}
|
|
1089
|
+
*/
|
|
1090
|
+
async function releaseMarketAdapterRuntime(bot, botId, context = 'shutdown') {
|
|
1091
|
+
if (isPm2Runtime()) {
|
|
1092
|
+
return { released: false, mode: 'pm2' };
|
|
1093
|
+
}
|
|
1094
|
+
if (!botId) {
|
|
1095
|
+
return { released: false, mode: 'direct', reason: 'missing-bot-id' };
|
|
1096
|
+
}
|
|
1097
|
+
const runtime = getSharedMarketAdapterRuntime({ root: PATHS.PROJECT_ROOT });
|
|
1098
|
+
const result = await runtime.releaseBot(botId);
|
|
1099
|
+
return {
|
|
1100
|
+
released: true,
|
|
1101
|
+
context,
|
|
1102
|
+
mode: 'direct',
|
|
1103
|
+
...result,
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
// Dust is cancelled immediately on detection — no timer infrastructure needed.
|
|
1107
|
+
/**
|
|
1108
|
+
* Check if an error message indicates that an order does not exist on the blockchain.
|
|
1109
|
+
* @param {string} message - Error message to check
|
|
1110
|
+
* @param {string} [orderId] - Optional order ID for context-aware matching
|
|
1111
|
+
* @returns {boolean} True if the message indicates a nonexistent order
|
|
1112
|
+
*/
|
|
1113
|
+
function isOrderDoesNotExistError(message, orderId) {
|
|
1114
|
+
if (typeof message !== 'string' || message.length === 0)
|
|
1115
|
+
return false;
|
|
1116
|
+
const normalized = message.toLowerCase();
|
|
1117
|
+
if (/\border\b.*\bdoes not exist\b/i.test(message))
|
|
1118
|
+
return true;
|
|
1119
|
+
if (/\bdoes not exist\b.*\border\b/i.test(message))
|
|
1120
|
+
return true;
|
|
1121
|
+
if (orderId && normalized.includes(String(orderId).toLowerCase())) {
|
|
1122
|
+
return /\bdoes not exist\b/i.test(message)
|
|
1123
|
+
|| /\bcould not find object\b/i.test(message)
|
|
1124
|
+
|| /\bunable to find object\b/i.test(message)
|
|
1125
|
+
|| /\bobject\b.*\bnot found\b/i.test(message);
|
|
1126
|
+
}
|
|
1127
|
+
return false;
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Calculate the remaining idle delay (ms) before grid maintenance can proceed.
|
|
1131
|
+
* Waits for fill queue to drain and for recent grid activity to settle.
|
|
1132
|
+
* @param {Object} ctx - Bot context with _lastGridActivityAt and _incomingFillQueue
|
|
1133
|
+
* @returns {number} Remaining idle delay in ms (0 if bot is idle)
|
|
1134
|
+
*/
|
|
1135
|
+
function getMaintenanceIdleDelayMs(ctx) {
|
|
1136
|
+
const settleDelayMs = Number.isFinite(TIMING.BLOCKCHAIN_SETTLE_DELAY_MS)
|
|
1137
|
+
? Math.max(0, TIMING.BLOCKCHAIN_SETTLE_DELAY_MS)
|
|
1138
|
+
: 6_000;
|
|
1139
|
+
if (settleDelayMs <= 0)
|
|
1140
|
+
return 0;
|
|
1141
|
+
if (ctx?._incomingFillQueue?.length > 0)
|
|
1142
|
+
return settleDelayMs;
|
|
1143
|
+
const lastActivityAt = Number(ctx?._lastGridActivityAt || 0);
|
|
1144
|
+
if (!Number.isFinite(lastActivityAt) || lastActivityAt <= 0)
|
|
1145
|
+
return 0;
|
|
1146
|
+
return Math.max(0, settleDelayMs - (Date.now() - lastActivityAt));
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Schedule grid maintenance to run after the bot becomes idle.
|
|
1150
|
+
* @param {Object} ctx - Bot context
|
|
1151
|
+
* @param {string} context - Context label for logging
|
|
1152
|
+
* @param {Object} [options] - Maintenance options forwarded to runGridMaintenance
|
|
1153
|
+
*/
|
|
1154
|
+
function scheduleMaintenanceAfterIdle(ctx, context, options = {}) {
|
|
1155
|
+
if (!ctx || ctx._shuttingDown || ctx._maintenanceIdleTimer || !ctx.manager?._fillProcessingLock)
|
|
1156
|
+
return;
|
|
1157
|
+
const delayMs = getMaintenanceIdleDelayMs(ctx);
|
|
1158
|
+
if (!(delayMs > 0))
|
|
1159
|
+
return;
|
|
1160
|
+
// The caller will have returned (and released _fillProcessingLock, if it
|
|
1161
|
+
// was held) by the time this deferred callback fires, so the deferred
|
|
1162
|
+
// run must always acquire the lock itself. Override any caller-supplied
|
|
1163
|
+
// fillLockAlreadyHeld=true so the deferred path can't deadlock against
|
|
1164
|
+
// a lock the caller no longer owns.
|
|
1165
|
+
const timerOptions = {
|
|
1166
|
+
...(options || {}),
|
|
1167
|
+
fillLockAlreadyHeld: false,
|
|
1168
|
+
};
|
|
1169
|
+
ctx._maintenanceIdleTimer = setTimeout(() => {
|
|
1170
|
+
ctx._maintenanceIdleTimer = null;
|
|
1171
|
+
if (ctx._shuttingDown)
|
|
1172
|
+
return;
|
|
1173
|
+
ctx._runGridMaintenance(context, timerOptions)
|
|
1174
|
+
.catch(err => {
|
|
1175
|
+
ctx._warn(`Deferred ${context} grid maintenance failed: ${err.message}`);
|
|
1176
|
+
if (ctx.manager) {
|
|
1177
|
+
ctx.manager._recoveryState = ctx.manager._recoveryState || {};
|
|
1178
|
+
ctx.manager._recoveryState.lastFailureAt = Date.now();
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
}, delayMs);
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Schedule a deferred grid resync after idle delay elapses.
|
|
1185
|
+
* @param {Object} ctx - Bot context
|
|
1186
|
+
* @param {import('./types').GridResyncOptions} [options] - Grid resync options
|
|
1187
|
+
*/
|
|
1188
|
+
function scheduleDeferredGridResync(ctx, options = {}) {
|
|
1189
|
+
if (!ctx ||
|
|
1190
|
+
ctx._shuttingDown ||
|
|
1191
|
+
ctx._deferredGridResyncTimer ||
|
|
1192
|
+
!ctx.manager?._fillProcessingLock) {
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
const idleDelayMs = getMaintenanceIdleDelayMs(ctx);
|
|
1196
|
+
const triggerFileWasPresent = !!(ctx.triggerFile && storage.exists(ctx.triggerFile));
|
|
1197
|
+
const settleDelayMs = Number.isFinite(TIMING.BLOCKCHAIN_SETTLE_DELAY_MS)
|
|
1198
|
+
? Math.max(0, TIMING.BLOCKCHAIN_SETTLE_DELAY_MS)
|
|
1199
|
+
: 6_000;
|
|
1200
|
+
const delayMs = Math.max(idleDelayMs + settleDelayMs, idleDelayMs);
|
|
1201
|
+
if (!(delayMs > 0))
|
|
1202
|
+
return;
|
|
1203
|
+
ctx._deferredGridResyncTimer = setTimeout(() => {
|
|
1204
|
+
ctx._deferredGridResyncTimer = null;
|
|
1205
|
+
if (ctx._shuttingDown)
|
|
1206
|
+
return;
|
|
1207
|
+
if (triggerFileWasPresent && !storage.exists(ctx.triggerFile))
|
|
1208
|
+
return;
|
|
1209
|
+
ctx.manager._fillProcessingLock.acquire(async () => {
|
|
1210
|
+
const ok = await ctx._performGridResync(options);
|
|
1211
|
+
if (!ok && !ctx._shuttingDown) {
|
|
1212
|
+
const curIdleMs = getMaintenanceIdleDelayMs(ctx);
|
|
1213
|
+
const reason = curIdleMs > 0
|
|
1214
|
+
? `idle cooldown (${Math.ceil(curIdleMs / TIMING.MILLISECONDS_PER_SECOND)}s)`
|
|
1215
|
+
: 'grid resync rejected or failed';
|
|
1216
|
+
ctx._warn(`Deferred trigger reset blocked: ${reason}; retaining existing grid state.`);
|
|
1217
|
+
}
|
|
1218
|
+
}).catch(err => {
|
|
1219
|
+
ctx._warn(`Deferred trigger reset lock error: ${err.message}`);
|
|
1220
|
+
if (ctx.manager) {
|
|
1221
|
+
ctx.manager._recoveryState = ctx.manager._recoveryState || {};
|
|
1222
|
+
ctx.manager._recoveryState.lastFailureAt = Date.now();
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
}, delayMs);
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Execute the core maintenance logic: recalculate funds, check pipeline,
|
|
1229
|
+
* refresh dynamic weights, check grid health, cancel dust orders,
|
|
1230
|
+
* apply divergence corrections, and fix spread conditions.
|
|
1231
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
1232
|
+
* @param {string} context - Context label for logging (e.g. 'periodic', 'dust-timer')
|
|
1233
|
+
* @returns {Promise<void>}
|
|
1234
|
+
*/
|
|
1235
|
+
async function executeMaintenanceLogic(bot, context) {
|
|
1236
|
+
await bot.manager.recalculateFunds();
|
|
1237
|
+
await checkBtsBalanceAndAcquire(bot);
|
|
1238
|
+
bot.manager.clearStalePipelineOperations();
|
|
1239
|
+
// Clear stale divergence flags before the pipeline check to break a self-blocking loop:
|
|
1240
|
+
// checkAndUpdateGridIfNeeded / compareGrids may have set _gridSidesUpdated earlier in this
|
|
1241
|
+
// tick (or in a previous tick that aborted before corrections ran), and applyGridDivergenceCorrections
|
|
1242
|
+
// is the only consumer that clears it. If a prior tick set the flag but never reached the
|
|
1243
|
+
// correction path, the flag persists and the next isPipelineEmpty sees it as a blockage,
|
|
1244
|
+
// preventing the divergence section from running. Stale flags must be cleared here, BEFORE
|
|
1245
|
+
// the pipeline check, so the divergence section can be entered.
|
|
1246
|
+
const staleFlags = bot.manager._gridSidesUpdated?.size || 0;
|
|
1247
|
+
if (staleFlags > 0) {
|
|
1248
|
+
bot.manager._gridSidesUpdated.clear();
|
|
1249
|
+
bot._log(`[PIPELINE-CLEAR] Cleared ${staleFlags} stale _gridSidesUpdated flag(s) before ${context} pipeline check`, 'info');
|
|
1250
|
+
}
|
|
1251
|
+
if (bot._maintenanceCooldownCycles > 0) {
|
|
1252
|
+
bot._maintenanceCooldownCycles--;
|
|
1253
|
+
bot._log(`[MAINT-COOLDOWN] Skipping ${context} maintenance after hard-abort recovery sync (remaining=${bot._maintenanceCooldownCycles})`, 'warn');
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
// Dust detection runs before the pipeline gate. Cancellation is immediate
|
|
1257
|
+
// (no 30s delay) and stays inside the empty-pipeline branch to avoid racing.
|
|
1258
|
+
let healthResult = await bot.manager.checkGridHealth(bot.updateOrdersOnChainPlan.bind(bot));
|
|
1259
|
+
if (await bot._abortFlowIfIllegalState(`${context} health check`))
|
|
1260
|
+
return;
|
|
1261
|
+
const pipelineStatus = bot.manager.isPipelineEmpty(bot._getPipelineSignals());
|
|
1262
|
+
if (pipelineStatus.isEmpty) {
|
|
1263
|
+
const repairedFromChain = await maybeRunTargetedDriftReconciliation(bot, context);
|
|
1264
|
+
if (repairedFromChain) {
|
|
1265
|
+
const freshHealth = await bot.manager.checkGridHealth(bot.updateOrdersOnChainPlan.bind(bot));
|
|
1266
|
+
if (await bot._abortFlowIfIllegalState(`${context} post-reconcile health check`))
|
|
1267
|
+
return;
|
|
1268
|
+
healthResult = freshHealth;
|
|
1269
|
+
}
|
|
1270
|
+
refreshDynamicWeightDistribution(bot, context);
|
|
1271
|
+
const dustCancelResult = await cancelDustOrders(bot, {
|
|
1272
|
+
buy: healthResult.buyDustOrders,
|
|
1273
|
+
sell: healthResult.sellDustOrders,
|
|
1274
|
+
fillLockAlreadyHeld: true,
|
|
1275
|
+
});
|
|
1276
|
+
if (dustCancelResult?.batchResult?.aborted) {
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
try {
|
|
1280
|
+
const persistedGridData = bot.accountOrders.loadGrid(true) || [];
|
|
1281
|
+
const calculatedGrid = Array.from(bot.manager.orders.values());
|
|
1282
|
+
const divergence = await Grid.monitorDivergence(bot.manager, calculatedGrid, persistedGridData);
|
|
1283
|
+
if (divergence.needsUpdate) {
|
|
1284
|
+
const hasRmsDivergence = !!(divergence.buy.rms || divergence.sell.rms);
|
|
1285
|
+
if (divergence.buy.ratio || divergence.sell.ratio) {
|
|
1286
|
+
bot._log(`Grid update triggered by funds during ${context} (buy: ${divergence.buy.ratio}, sell: ${divergence.sell.ratio})`);
|
|
1287
|
+
}
|
|
1288
|
+
if (hasRmsDivergence) {
|
|
1289
|
+
bot._log(`Grid update triggered by structural divergence during ${context}: buy=${Format.formatPrice6(divergence.buy.metric)}, sell=${Format.formatPrice6(divergence.sell.metric)}`);
|
|
1290
|
+
let ok;
|
|
1291
|
+
if (typeof bot._performGridResync === 'function') {
|
|
1292
|
+
ok = await bot._performGridResync(buildGridResyncOptions('rms_structural_grid_resync'));
|
|
1293
|
+
}
|
|
1294
|
+
else {
|
|
1295
|
+
ok = await performGridResync(bot, buildGridResyncOptions('rms_structural_grid_resync'));
|
|
1296
|
+
}
|
|
1297
|
+
if (!ok) {
|
|
1298
|
+
bot._warn(`RMS structural divergence full grid resync failed during ${context}; retaining existing grid state.`);
|
|
1299
|
+
}
|
|
1300
|
+
// Clear any ratio flags set by checkAndUpdateGridIfNeeded earlier in this tick,
|
|
1301
|
+
// since the resync already rebuilt the full grid.
|
|
1302
|
+
if (bot.manager._gridSidesUpdated?.size > 0) {
|
|
1303
|
+
bot.manager._gridSidesUpdated.clear();
|
|
1304
|
+
}
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
try {
|
|
1308
|
+
await applyGridDivergenceCorrections(bot.manager, bot.accountOrders, bot.config.botKey, bot.updateOrdersOnChainBatch.bind(bot));
|
|
1309
|
+
if (await bot._abortFlowIfIllegalState(`${context} divergence correction`))
|
|
1310
|
+
return;
|
|
1311
|
+
bot._log(`Grid divergence corrections applied during ${context}`);
|
|
1312
|
+
}
|
|
1313
|
+
catch (err) {
|
|
1314
|
+
bot._warn(`Error applying divergence corrections during ${context}: ${err.message}`);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
catch (err) {
|
|
1319
|
+
bot._warn(`Error running divergence check during ${context}: ${err.message}`);
|
|
1320
|
+
}
|
|
1321
|
+
const spreadResult = await bot.manager.checkSpreadCondition(BitShares, bot.updateOrdersOnChainPlan.bind(bot));
|
|
1322
|
+
if (await bot._abortFlowIfIllegalState(`${context} spread check`))
|
|
1323
|
+
return;
|
|
1324
|
+
if (spreadResult && spreadResult.ordersPlaced > 0) {
|
|
1325
|
+
bot._log(`✓ Spread correction during ${context}: ${spreadResult.ordersPlaced} order(s) placed`);
|
|
1326
|
+
await bot._persistAndRecoverIfNeeded();
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Cancel dust orders immediately — no delay, no timer, no maps.
|
|
1332
|
+
* Each dust order is cancelled on chain and its slot is rotated through
|
|
1333
|
+
* the normal synthetic-fill pipeline. Failures are logged and retried on
|
|
1334
|
+
* the next detection cycle (next fill batch or 5-min health check).
|
|
1335
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
1336
|
+
* @param {Object} [options] - Dust cancellation options
|
|
1337
|
+
* @param {import('./types').Order[]} [options.buy=[]] - Buy-side dust orders
|
|
1338
|
+
* @param {import('./types').Order[]} [options.sell=[]] - Sell-side dust orders
|
|
1339
|
+
* @param {boolean} [options.fillLockAlreadyHeld=false] - Skip fill-processing lock re-entry (caller already holds it)
|
|
1340
|
+
* @returns {Promise<{cancelledCount: number, batchResult: {aborted: boolean}|null}>}
|
|
1341
|
+
*/
|
|
1342
|
+
async function cancelDustOrders(bot, { buy: buyDust = [], sell: sellDust = [], fillLockAlreadyHeld = false } = {}) {
|
|
1343
|
+
const allDust = [...buyDust, ...sellDust];
|
|
1344
|
+
if (allDust.length === 0)
|
|
1345
|
+
return { cancelledCount: 0, batchResult: null };
|
|
1346
|
+
const syntheticFills = [];
|
|
1347
|
+
for (const order of allDust) {
|
|
1348
|
+
if (!order.orderId)
|
|
1349
|
+
continue;
|
|
1350
|
+
try {
|
|
1351
|
+
const cancelResult = await chainOrders.cancelOrder(bot.account, bot.privateKey, order.orderId);
|
|
1352
|
+
try {
|
|
1353
|
+
if (cancelResult?.verifiedAfterFailure) {
|
|
1354
|
+
const accountRef = bot.accountId || bot.account;
|
|
1355
|
+
const chainOpenOrders = await chainOrders.readOpenOrders(accountRef);
|
|
1356
|
+
await bot.manager.synchronizeWithChain(chainOpenOrders, 'readOpenOrders', { fillLockAlreadyHeld: true });
|
|
1357
|
+
}
|
|
1358
|
+
else {
|
|
1359
|
+
await bot.manager.synchronizeWithChain({ orderId: order.orderId, clearSize: true }, 'cancelOrder', { fillLockAlreadyHeld });
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
catch (refetchErr) {
|
|
1363
|
+
bot._warn(`[DUST] Cancel succeeded but refetch failed for ${order.id} (${order.orderId}): ${refetchErr.message}`);
|
|
1364
|
+
}
|
|
1365
|
+
syntheticFills.push({ ...order, isPartial: true, isDelayedRotationTrigger: true });
|
|
1366
|
+
bot._log(`[DUST] Cancelled ${order.id} (${order.orderId}) size=${order.size}`, 'info');
|
|
1367
|
+
}
|
|
1368
|
+
catch (err) {
|
|
1369
|
+
const errMsg = err?.message || '';
|
|
1370
|
+
if (isOrderDoesNotExistError(errMsg, order.orderId)) {
|
|
1371
|
+
syntheticFills.push({ ...order, isPartial: true, isDelayedRotationTrigger: true });
|
|
1372
|
+
bot._log(`[DUST] Order ${order.id} (${order.orderId}) already gone from chain`, 'info');
|
|
1373
|
+
}
|
|
1374
|
+
else {
|
|
1375
|
+
bot._warn(`[DUST] Failed to cancel ${order.id} (${order.orderId}): ${errMsg}`);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
if (syntheticFills.length === 0)
|
|
1380
|
+
return { cancelledCount: 0, batchResult: null };
|
|
1381
|
+
const result = await bot._processFillsWithBatching(syntheticFills, new Set(), `dust cancel [${syntheticFills.map(o => o.id).join(', ')}]`);
|
|
1382
|
+
if (!result.aborted) {
|
|
1383
|
+
await bot.manager.persistGrid();
|
|
1384
|
+
}
|
|
1385
|
+
return { cancelledCount: syntheticFills.length, batchResult: { aborted: result.aborted } };
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Run grid maintenance with idle detection and lock acquisition.
|
|
1389
|
+
* Checks if the bot is idle before proceeding, and acquires the fill processing lock.
|
|
1390
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
1391
|
+
* @param {string} [context='periodic'] - Context label for logging
|
|
1392
|
+
* @param {Object} [options] - Maintenance options
|
|
1393
|
+
* @param {boolean} [options.fillLockAlreadyHeld=false] - Skip fill lock acquisition if already held
|
|
1394
|
+
* @param {boolean} [options.skipIdle=false] - Skip idle delay check
|
|
1395
|
+
* @returns {Promise<void>}
|
|
1396
|
+
*/
|
|
1397
|
+
async function runGridMaintenance(bot, context = 'periodic', options = {}) {
|
|
1398
|
+
if (options === null || typeof options !== 'object' || Array.isArray(options)) {
|
|
1399
|
+
throw new TypeError('Grid maintenance options must be an object');
|
|
1400
|
+
}
|
|
1401
|
+
const fillLockAlreadyHeld = options.fillLockAlreadyHeld === true;
|
|
1402
|
+
const skipIdle = options.skipIdle === true;
|
|
1403
|
+
if (!skipIdle) {
|
|
1404
|
+
const idleDelayMs = getMaintenanceIdleDelayMs(bot);
|
|
1405
|
+
if (idleDelayMs > 0) {
|
|
1406
|
+
bot._log(`[MAINT-IDLE] Deferring ${context} grid maintenance until ` +
|
|
1407
|
+
`${Math.ceil(idleDelayMs / TIMING.MILLISECONDS_PER_SECOND)}s of inactivity has passed`, 'debug');
|
|
1408
|
+
scheduleMaintenanceAfterIdle(bot, context, options);
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
try {
|
|
1413
|
+
if (!bot.manager)
|
|
1414
|
+
return;
|
|
1415
|
+
// Lock-ordering contract:
|
|
1416
|
+
// - When fillLockAlreadyHeld=false this function acquires
|
|
1417
|
+
// _fillProcessingLock first, then _divergenceLock (canonical order).
|
|
1418
|
+
// - When fillLockAlreadyHeld=true the caller must already hold
|
|
1419
|
+
// _fillProcessingLock; only _divergenceLock is acquired here.
|
|
1420
|
+
// AsyncLock is NOT reentrant — acquiring in the reverse order anywhere
|
|
1421
|
+
// else in the bot would deadlock against the path above.
|
|
1422
|
+
const runWithDivergenceLock = async () => {
|
|
1423
|
+
// Re-check orders size under the divergence lock to avoid a TOCTOU
|
|
1424
|
+
// race with concurrent order mutations. The previous placement
|
|
1425
|
+
// (before any lock acquisition) could observe a stale empty
|
|
1426
|
+
// grid and silently skip maintenance while fills were in flight.
|
|
1427
|
+
if (!bot.manager.orders || bot.manager.orders.size === 0)
|
|
1428
|
+
return;
|
|
1429
|
+
await executeMaintenanceLogic(bot, context);
|
|
1430
|
+
};
|
|
1431
|
+
if (fillLockAlreadyHeld) {
|
|
1432
|
+
await bot.manager._divergenceLock.acquire(runWithDivergenceLock);
|
|
1433
|
+
}
|
|
1434
|
+
else {
|
|
1435
|
+
await bot.manager._fillProcessingLock.acquire(async () => {
|
|
1436
|
+
await bot.manager._divergenceLock.acquire(runWithDivergenceLock);
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
catch (err) {
|
|
1441
|
+
bot._warn(`Error during ${context} grid maintenance: ${err.message}`);
|
|
1442
|
+
throw err;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
const _lastBtsAcquisitionTimestamps = new Map();
|
|
1446
|
+
/**
|
|
1447
|
+
* Check if the bot's BTS balance is below the minimum threshold and trigger acquisition.
|
|
1448
|
+
* Only applies to non-BTS pairs. Uses hysteresis: triggers at 1× min_BTS_value,
|
|
1449
|
+
* fills to BTS_ACQUIRE_TARGET_MULTIPLIER × min_BTS_value.
|
|
1450
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
1451
|
+
* @returns {Promise<void>}
|
|
1452
|
+
*/
|
|
1453
|
+
async function checkBtsBalanceAndAcquire(bot) {
|
|
1454
|
+
if (bot.config.dryRun)
|
|
1455
|
+
return;
|
|
1456
|
+
if (bot.config.assetA === 'BTS' || bot.config.assetB === 'BTS')
|
|
1457
|
+
return;
|
|
1458
|
+
const cooldownMin = bot.config?.timing?.BTS_ACQUIRE_COOLDOWN_MIN;
|
|
1459
|
+
const cooldownMs = cooldownMin * 60 * 1000;
|
|
1460
|
+
const now = Date.now();
|
|
1461
|
+
// Prune every expired entry in the map, not just the current bot's.
|
|
1462
|
+
// Otherwise entries for bots that acquired BTS and then stopped calling
|
|
1463
|
+
// (removed from bots.json, supervisor restart with a different roster)
|
|
1464
|
+
// would persist forever. The map is bounded by the number of unique bot
|
|
1465
|
+
// keys that ever acquired BTS, so this O(n) sweep is cheap.
|
|
1466
|
+
for (const [key, ts] of _lastBtsAcquisitionTimestamps) {
|
|
1467
|
+
if ((now - ts) >= cooldownMs) {
|
|
1468
|
+
_lastBtsAcquisitionTimestamps.delete(key);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
const botKey = bot.config.botKey || bot.config.name;
|
|
1472
|
+
const lastAcq = _lastBtsAcquisitionTimestamps.get(botKey);
|
|
1473
|
+
if (lastAcq && (now - lastAcq) < cooldownMs)
|
|
1474
|
+
return;
|
|
1475
|
+
if (!bot.manager || !bot.manager.btsBalance)
|
|
1476
|
+
return;
|
|
1477
|
+
const targetBuy = Math.max(0, bot.config.activeOrders?.buy ?? 1);
|
|
1478
|
+
const targetSell = Math.max(0, bot.config.activeOrders?.sell ?? 1);
|
|
1479
|
+
const totalTarget = targetBuy + targetSell;
|
|
1480
|
+
const btsReservationMultiplier = bot.config?.feeParams?.BTS_RESERVATION_MULTIPLIER;
|
|
1481
|
+
const minBtsVal = calculateOrderCreationFees(bot.config.assetA, bot.config.assetB, totalTarget, btsReservationMultiplier);
|
|
1482
|
+
if (minBtsVal <= 0)
|
|
1483
|
+
return;
|
|
1484
|
+
const effectiveMin = (bot.config.min_BTS_value > 0) ? bot.config.min_BTS_value : minBtsVal;
|
|
1485
|
+
const btsFree = bot.manager.btsBalance.free || 0;
|
|
1486
|
+
const btsAcquireThreshold = bot.config?.feeParams?.BTS_ACQUIRE_THRESHOLD;
|
|
1487
|
+
const triggerAt = effectiveMin * btsAcquireThreshold;
|
|
1488
|
+
if (btsFree >= triggerAt)
|
|
1489
|
+
return;
|
|
1490
|
+
const btsAcquireTargetMultiplier = bot.config?.feeParams?.BTS_ACQUIRE_TARGET_MULTIPLIER;
|
|
1491
|
+
const target = effectiveMin * btsAcquireTargetMultiplier;
|
|
1492
|
+
const deficit = Math.max(0, target - btsFree);
|
|
1493
|
+
bot._log(`[BTS-ACQ] BTS balance ${Format.formatAmount8(btsFree)} below threshold ${Format.formatAmount8(triggerAt)}. ` +
|
|
1494
|
+
`Acquiring ${Format.formatAmount8(deficit)} BTS (target: ${Format.formatAmount8(target)})`, 'info');
|
|
1495
|
+
_lastBtsAcquisitionTimestamps.set(botKey, Date.now());
|
|
1496
|
+
await acquireBts(bot, deficit);
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Acquire BTS by swapping one of the trading pair assets through an AMM pool.
|
|
1500
|
+
* Tries both assets for a BTS pool, picks the best (lowest price impact).
|
|
1501
|
+
* @param {import('./dexbot_class').DEXBot} bot
|
|
1502
|
+
* @param {number} deficit - Amount of BTS needed (float)
|
|
1503
|
+
* @returns {Promise<void>}
|
|
1504
|
+
*/
|
|
1505
|
+
async function acquireBts(bot, deficit) {
|
|
1506
|
+
if (deficit <= 0)
|
|
1507
|
+
return;
|
|
1508
|
+
const { BitShares } = require('./bitshares_client');
|
|
1509
|
+
if (!BitShares || !BitShares.db)
|
|
1510
|
+
return;
|
|
1511
|
+
const coreAssetId = NATIVE_CLIENT.CHAIN.CORE_ASSET_ID;
|
|
1512
|
+
const assets = [
|
|
1513
|
+
{ id: bot.assets?.assetA?.id, free: bot.manager.accountTotals?.sellFree || 0, precision: bot.assets?.assetA?.precision, symbol: bot.config.assetA },
|
|
1514
|
+
{ id: bot.assets?.assetB?.id, free: bot.manager.accountTotals?.buyFree || 0, precision: bot.assets?.assetB?.precision, symbol: bot.config.assetB }
|
|
1515
|
+
];
|
|
1516
|
+
const candidates = [];
|
|
1517
|
+
for (const asset of assets) {
|
|
1518
|
+
if (!asset.id || asset.free <= 0)
|
|
1519
|
+
continue;
|
|
1520
|
+
try {
|
|
1521
|
+
const pools = await BitShares.db.get_liquidity_pools_by_both_assets(asset.id, coreAssetId);
|
|
1522
|
+
const validPools = Array.isArray(pools) ? pools.filter(p => p?.id) : [];
|
|
1523
|
+
const poolData = validPools.length
|
|
1524
|
+
? validPools.sort((a, b) => {
|
|
1525
|
+
const getBtsBal = (p) => {
|
|
1526
|
+
const isBts = String(p.asset_a ?? p.asset_ids?.[0] ?? '') === String(coreAssetId);
|
|
1527
|
+
return Number(isBts ? (p.balance_a ?? 0) : (p.balance_b ?? 0));
|
|
1528
|
+
};
|
|
1529
|
+
return getBtsBal(b) - getBtsBal(a);
|
|
1530
|
+
})[0]
|
|
1531
|
+
: null;
|
|
1532
|
+
if (!poolData)
|
|
1533
|
+
continue;
|
|
1534
|
+
const isAssetA = String(poolData.asset_a) === String(asset.id) || String(poolData.asset_ids?.[0]) === String(asset.id);
|
|
1535
|
+
const assetReserveRaw = isAssetA ? (poolData.balance_a || poolData.reserves?.[0]?.amount) : (poolData.balance_b || poolData.reserves?.[1]?.amount);
|
|
1536
|
+
const btsReserveRaw = isAssetA ? (poolData.balance_b || poolData.reserves?.[1]?.amount) : (poolData.balance_a || poolData.reserves?.[0]?.amount);
|
|
1537
|
+
if (!assetReserveRaw || !btsReserveRaw)
|
|
1538
|
+
continue;
|
|
1539
|
+
const assetReserve = blockchainToFloat(assetReserveRaw, asset.precision);
|
|
1540
|
+
const btsReserve = blockchainToFloat(btsReserveRaw, BTS_PRECISION);
|
|
1541
|
+
const expectedReceive = Math.min(deficit, btsReserve * 0.5);
|
|
1542
|
+
const sellAmount = calculateSwapInAmount(deficit, btsReserve, assetReserve);
|
|
1543
|
+
if (sellAmount <= 0 || sellAmount > asset.free)
|
|
1544
|
+
continue;
|
|
1545
|
+
candidates.push({ asset, poolId: poolData.id, sellAmount, expectedReceive, priceImpact: sellAmount / assetReserve });
|
|
1546
|
+
}
|
|
1547
|
+
catch (e) {
|
|
1548
|
+
bot._log(`[BTS-ACQ] Pool lookup failed for ${asset?.symbol}: ${e.message}`, 'debug');
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
if (candidates.length === 0) {
|
|
1552
|
+
bot._log(`[BTS-ACQ] CRITICAL: No BTS pool with sufficient liquidity for ${bot.config.assetA} or ${bot.config.assetB}`, 'error');
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
candidates.sort((a, b) => a.priceImpact - b.priceImpact);
|
|
1556
|
+
const best = candidates[0];
|
|
1557
|
+
const poolSlippageTolerance = bot.config?.feeParams?.POOL_SLIPPAGE_TOLERANCE;
|
|
1558
|
+
const minReceive = best.expectedReceive * (1 - poolSlippageTolerance);
|
|
1559
|
+
const sellInt = floatToBlockchainInt(best.sellAmount, best.asset.precision);
|
|
1560
|
+
const minReceiveInt = floatToBlockchainInt(minReceive, BTS_PRECISION);
|
|
1561
|
+
const op = chainOrders.buildLiquidityPoolExchangeOp(bot.accountId, best.poolId, sellInt, best.asset.id, minReceiveInt, coreAssetId);
|
|
1562
|
+
try {
|
|
1563
|
+
if (bot.privateKey) {
|
|
1564
|
+
await chainOrders.executeBatch(bot.account, bot.privateKey, [op]);
|
|
1565
|
+
}
|
|
1566
|
+
else {
|
|
1567
|
+
bot._log('[BTS-ACQ] CRITICAL: No signing method available', 'error');
|
|
1568
|
+
return;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
catch (err) {
|
|
1572
|
+
bot._log(`[BTS-ACQ] Swap broadcast failed: ${err.message}`, 'error');
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
const orderType = (best.asset.id === bot.assets?.assetA?.id) ? 'sell' : 'buy';
|
|
1576
|
+
if (bot.manager.accountant) {
|
|
1577
|
+
bot.manager.accountant.adjustTotalBalance(orderType, -best.sellAmount, 'bts-acquisition-swap-sell');
|
|
1578
|
+
}
|
|
1579
|
+
// Do NOT optimistically bump btsBalance.free/total here. expectedReceive is
|
|
1580
|
+
// a pre-swap estimate and may diverge from the actual fill (slippage, fees,
|
|
1581
|
+
// partial fills, broadcast/confirm failures). The next periodic
|
|
1582
|
+
// fetchAccountTotals() reconciles from chain truth. The bts-acquisition
|
|
1583
|
+
// cooldown in checkBtsBalanceAndAcquire prevents immediate re-trigger even
|
|
1584
|
+
// if the chain balance is still below the trigger threshold.
|
|
1585
|
+
bot._log(`[BTS-ACQ] Acquired ~${Format.formatAmount8(best.expectedReceive)} BTS: sold ${Format.formatAmount8(best.sellAmount)} ${best.asset.symbol} via pool ${best.poolId}`, 'info');
|
|
1586
|
+
}
|
|
1587
|
+
module.exports = {
|
|
1588
|
+
loadBotsConfigSnapshot,
|
|
1589
|
+
refreshDynamicWeightDistribution,
|
|
1590
|
+
performGridResync,
|
|
1591
|
+
updateBotGridResetMetadata,
|
|
1592
|
+
handlePendingTriggerReset,
|
|
1593
|
+
setupTriggerFileDetection,
|
|
1594
|
+
performPeriodicGridChecks,
|
|
1595
|
+
isOpenOrdersSyncLoopEnabled,
|
|
1596
|
+
startOpenOrdersSyncLoop,
|
|
1597
|
+
stopOpenOrdersSyncLoop,
|
|
1598
|
+
setupBlockchainFetchInterval,
|
|
1599
|
+
stopBlockchainFetchInterval,
|
|
1600
|
+
executeMaintenanceLogic,
|
|
1601
|
+
cancelDustOrders,
|
|
1602
|
+
isOrderDoesNotExistError,
|
|
1603
|
+
runGridMaintenance,
|
|
1604
|
+
stopMarketAdapterPm2,
|
|
1605
|
+
releaseMarketAdapterRuntime,
|
|
1606
|
+
syncMarketAdapterOnPeriodicConfigCheck,
|
|
1607
|
+
findSnapshotBotForRuntimeConfig,
|
|
1608
|
+
runtimeConfigNeedsMarketAdapter,
|
|
1609
|
+
usesAmaGridPrice,
|
|
1610
|
+
checkBtsBalanceAndAcquire,
|
|
1611
|
+
acquireBts,
|
|
1612
|
+
};
|
|
1613
|
+
//# sourceMappingURL=dexbot_maintenance_runtime.js.map
|