dexbot 1.4.10 → 1.4.12
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/README.md +6 -5
- package/dist/analysis/chart_utils.d.ts.map +1 -1
- package/dist/analysis/chart_utils.js +9 -18
- package/dist/analysis/chart_utils.js.map +1 -1
- package/dist/analysis/math_utils.d.ts.map +1 -1
- package/dist/analysis/math_utils.js +6 -16
- package/dist/analysis/math_utils.js.map +1 -1
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts +1 -1
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts.map +1 -1
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js +31 -38
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js.map +1 -1
- package/dist/analysis/trend_detection/hurst_analyzer.js +7 -10
- package/dist/analysis/trend_detection/hurst_analyzer.js.map +1 -1
- package/dist/analysis/trend_detection/kalman_trend_analyzer.js +10 -14
- package/dist/analysis/trend_detection/kalman_trend_analyzer.js.map +1 -1
- package/dist/analysis/trend_detection/kalman_velocity_smoothing.js +10 -15
- package/dist/analysis/trend_detection/kalman_velocity_smoothing.js.map +1 -1
- package/dist/analysis/trend_detection/permutation_entropy_analyzer.js +7 -10
- package/dist/analysis/trend_detection/permutation_entropy_analyzer.js.map +1 -1
- package/dist/bot.d.ts.map +1 -1
- package/dist/bot.js +4 -12
- package/dist/bot.js.map +1 -1
- package/dist/credential-daemon.d.ts +0 -1
- package/dist/credential-daemon.d.ts.map +1 -1
- package/dist/credential-daemon.js +3 -123
- package/dist/credential-daemon.js.map +1 -1
- package/dist/dexbot.d.ts +0 -87
- package/dist/dexbot.d.ts.map +1 -1
- package/dist/dexbot.js +54 -23
- package/dist/dexbot.js.map +1 -1
- package/dist/market_adapter/ama_signal_runner.js +8 -9
- package/dist/market_adapter/ama_signal_runner.js.map +1 -1
- package/dist/market_adapter/candle_utils.js +1 -7
- package/dist/market_adapter/candle_utils.js.map +1 -1
- package/dist/market_adapter/core/asymmetric_bounds.js +1 -4
- package/dist/market_adapter/core/asymmetric_bounds.js.map +1 -1
- package/dist/market_adapter/core/config_normalizers.js +5 -9
- package/dist/market_adapter/core/config_normalizers.js.map +1 -1
- package/dist/market_adapter/core/kibana_candles.js +8 -15
- package/dist/market_adapter/core/kibana_candles.js.map +1 -1
- package/dist/market_adapter/core/kibana_client.js +7 -13
- package/dist/market_adapter/core/kibana_client.js.map +1 -1
- package/dist/market_adapter/core/kibana_market_candles.js +6 -12
- package/dist/market_adapter/core/kibana_market_candles.js.map +1 -1
- package/dist/market_adapter/core/market_adapter_service.d.ts +2 -0
- package/dist/market_adapter/core/market_adapter_service.d.ts.map +1 -1
- package/dist/market_adapter/core/market_adapter_service.js +100 -101
- package/dist/market_adapter/core/market_adapter_service.js.map +1 -1
- package/dist/market_adapter/core/strategies/ama.js +5 -10
- package/dist/market_adapter/core/strategies/ama.js.map +1 -1
- package/dist/market_adapter/core/strategies/ama_slope_model.js +21 -24
- package/dist/market_adapter/core/strategies/ama_slope_model.js.map +1 -1
- package/dist/market_adapter/core/strategies/atr/calculator.js +3 -5
- package/dist/market_adapter/core/strategies/atr/calculator.js.map +1 -1
- package/dist/market_adapter/core/strategies/collateral_manager.js +4 -6
- package/dist/market_adapter/core/strategies/collateral_manager.js.map +1 -1
- package/dist/market_adapter/core/strategies/regime_gate.js +15 -22
- package/dist/market_adapter/core/strategies/regime_gate.js.map +1 -1
- package/dist/market_adapter/index.d.ts +23 -23
- package/dist/market_adapter/index.d.ts.map +1 -1
- package/dist/market_adapter/index.js +24 -82
- package/dist/market_adapter/index.js.map +1 -1
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js +2 -1
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js.map +1 -1
- package/dist/market_adapter/inputs/fetch_lp_data.d.ts.map +1 -1
- package/dist/market_adapter/inputs/fetch_lp_data.js +59 -98
- package/dist/market_adapter/inputs/fetch_lp_data.js.map +1 -1
- package/dist/market_adapter/inputs/kibana_source.js +11 -17
- package/dist/market_adapter/inputs/kibana_source.js.map +1 -1
- package/dist/market_adapter/interval_utils.js +1 -2
- package/dist/market_adapter/interval_utils.js.map +1 -1
- package/dist/market_adapter/log_format.js +1 -7
- package/dist/market_adapter/log_format.js.map +1 -1
- package/dist/market_adapter/lp_chart_core.js +20 -22
- package/dist/market_adapter/lp_chart_core.js.map +1 -1
- package/dist/market_adapter/lp_chart_runner.d.ts +1 -1
- package/dist/market_adapter/lp_chart_runner.d.ts.map +1 -1
- package/dist/market_adapter/lp_chart_runner.js +41 -57
- package/dist/market_adapter/lp_chart_runner.js.map +1 -1
- package/dist/market_adapter/lp_chart_strategy_loader.js +30 -34
- package/dist/market_adapter/lp_chart_strategy_loader.js.map +1 -1
- package/dist/market_adapter/market_adapter.d.ts +2 -2
- package/dist/market_adapter/market_adapter.d.ts.map +1 -1
- package/dist/market_adapter/market_adapter.js +163 -222
- package/dist/market_adapter/market_adapter.js.map +1 -1
- package/dist/market_adapter/merge_lp_data.js +17 -18
- package/dist/market_adapter/merge_lp_data.js.map +1 -1
- package/dist/market_adapter/test_helpers.js +3 -14
- package/dist/market_adapter/test_helpers.js.map +1 -1
- package/dist/market_adapter/utils/adapter_client.js +7 -16
- package/dist/market_adapter/utils/adapter_client.js.map +1 -1
- package/dist/market_adapter/utils/atomic_write.js +3 -5
- package/dist/market_adapter/utils/atomic_write.js.map +1 -1
- package/dist/market_adapter/utils/chain.d.ts.map +1 -1
- package/dist/market_adapter/utils/chain.js +9 -17
- package/dist/market_adapter/utils/chain.js.map +1 -1
- package/dist/market_adapter/utils/data_discovery.js +8 -10
- package/dist/market_adapter/utils/data_discovery.js.map +1 -1
- package/dist/market_adapter/utils/dynamic_grid_snapshot.d.ts.map +1 -1
- package/dist/market_adapter/utils/dynamic_grid_snapshot.js +11 -14
- package/dist/market_adapter/utils/dynamic_grid_snapshot.js.map +1 -1
- package/dist/market_adapter/utils/file_lock.js +14 -19
- package/dist/market_adapter/utils/file_lock.js.map +1 -1
- package/dist/market_adapter/utils/native_history.js +8 -14
- package/dist/market_adapter/utils/native_history.js.map +1 -1
- package/dist/market_adapter/utils/paths.js +3 -6
- package/dist/market_adapter/utils/paths.js.map +1 -1
- package/dist/modules/account_bots.d.ts +1 -1
- package/dist/modules/account_bots.d.ts.map +1 -1
- package/dist/modules/account_bots.js +90 -97
- package/dist/modules/account_bots.js.map +1 -1
- package/dist/modules/account_orders.d.ts +0 -9
- package/dist/modules/account_orders.d.ts.map +1 -1
- package/dist/modules/account_orders.js +26 -121
- package/dist/modules/account_orders.js.map +1 -1
- package/dist/modules/authority_resolver.js +12 -18
- package/dist/modules/authority_resolver.js.map +1 -1
- package/dist/modules/bitshares-native/chain_client.js +16 -21
- package/dist/modules/bitshares-native/chain_client.js.map +1 -1
- package/dist/modules/bitshares-native/crypto/ecc.browser.js +12 -72
- package/dist/modules/bitshares-native/crypto/ecc.browser.js.map +1 -1
- package/dist/modules/bitshares-native/crypto/ecc.js +13 -40
- package/dist/modules/bitshares-native/crypto/ecc.js.map +1 -1
- package/dist/modules/bitshares-native/crypto/ecc_selector.d.ts.map +1 -1
- package/dist/modules/bitshares-native/crypto/ecc_selector.js +5 -6
- package/dist/modules/bitshares-native/crypto/ecc_selector.js.map +1 -1
- package/dist/modules/bitshares-native/index.d.ts +8 -8
- package/dist/modules/bitshares-native/index.d.ts.map +1 -1
- package/dist/modules/bitshares-native/index.js +11 -68
- package/dist/modules/bitshares-native/index.js.map +1 -1
- package/dist/modules/bitshares-native/lru_cache.js +3 -6
- package/dist/modules/bitshares-native/lru_cache.js.map +1 -1
- package/dist/modules/bitshares-native/resolvers.js +9 -11
- package/dist/modules/bitshares-native/resolvers.js.map +1 -1
- package/dist/modules/bitshares-native/serial/chain_constants.js +3 -28
- package/dist/modules/bitshares-native/serial/chain_constants.js.map +1 -1
- package/dist/modules/bitshares-native/serial/index.d.ts +4 -4
- package/dist/modules/bitshares-native/serial/index.d.ts.map +1 -1
- package/dist/modules/bitshares-native/serial/index.js +5 -46
- package/dist/modules/bitshares-native/serial/index.js.map +1 -1
- package/dist/modules/bitshares-native/serial/operations.d.ts +98 -98
- package/dist/modules/bitshares-native/serial/operations.d.ts.map +1 -1
- package/dist/modules/bitshares-native/serial/operations.js +372 -473
- package/dist/modules/bitshares-native/serial/operations.js.map +1 -1
- package/dist/modules/bitshares-native/serial/serializer.js +3 -8
- package/dist/modules/bitshares-native/serial/serializer.js.map +1 -1
- package/dist/modules/bitshares-native/serial/types.js +9 -74
- package/dist/modules/bitshares-native/serial/types.js.map +1 -1
- package/dist/modules/bitshares-native/signing_client.d.ts.map +1 -1
- package/dist/modules/bitshares-native/signing_client.js +11 -48
- package/dist/modules/bitshares-native/signing_client.js.map +1 -1
- package/dist/modules/bitshares-native/subscriptions.d.ts.map +1 -1
- package/dist/modules/bitshares-native/subscriptions.js +24 -27
- package/dist/modules/bitshares-native/subscriptions.js.map +1 -1
- package/dist/modules/bitshares-native/transport.d.ts.map +1 -1
- package/dist/modules/bitshares-native/transport.js +14 -26
- package/dist/modules/bitshares-native/transport.js.map +1 -1
- package/dist/modules/bitshares-native/tx/builder.js +29 -72
- package/dist/modules/bitshares-native/tx/builder.js.map +1 -1
- package/dist/modules/bitshares-native/tx/tx_cache.js +12 -21
- package/dist/modules/bitshares-native/tx/tx_cache.js.map +1 -1
- package/dist/modules/bitshares_client.d.ts +21 -0
- package/dist/modules/bitshares_client.d.ts.map +1 -1
- package/dist/modules/bitshares_client.js +32 -86
- package/dist/modules/bitshares_client.js.map +1 -1
- package/dist/modules/bot_settings.d.ts.map +1 -1
- package/dist/modules/bot_settings.js +30 -49
- package/dist/modules/bot_settings.js.map +1 -1
- package/dist/modules/bots_file_lock.d.ts.map +1 -1
- package/dist/modules/bots_file_lock.js +5 -13
- package/dist/modules/bots_file_lock.js.map +1 -1
- package/dist/modules/broadcast_failure.js +1 -3
- package/dist/modules/broadcast_failure.js.map +1 -1
- package/dist/modules/chain_keys.d.ts +4 -9
- package/dist/modules/chain_keys.d.ts.map +1 -1
- package/dist/modules/chain_keys.js +77 -182
- package/dist/modules/chain_keys.js.map +1 -1
- package/dist/modules/chain_orders.d.ts +2 -2
- package/dist/modules/chain_orders.d.ts.map +1 -1
- package/dist/modules/chain_orders.js +81 -144
- package/dist/modules/chain_orders.js.map +1 -1
- package/dist/modules/config.d.ts.map +1 -1
- package/dist/modules/config.js +23 -28
- package/dist/modules/config.js.map +1 -1
- package/dist/modules/constants.d.ts +4 -14
- package/dist/modules/constants.d.ts.map +1 -1
- package/dist/modules/constants.js +46 -78
- package/dist/modules/constants.js.map +1 -1
- package/dist/modules/cr_planner.js +15 -26
- package/dist/modules/cr_planner.js.map +1 -1
- package/dist/modules/credential_policy.d.ts +3 -6
- package/dist/modules/credential_policy.d.ts.map +1 -1
- package/dist/modules/credential_policy.js +62 -153
- package/dist/modules/credential_policy.js.map +1 -1
- package/dist/modules/credential_runtime.d.ts +1 -1
- package/dist/modules/credential_runtime.d.ts.map +1 -1
- package/dist/modules/credential_runtime.js +34 -41
- package/dist/modules/credential_runtime.js.map +1 -1
- package/dist/modules/credential_session_cache.d.ts +1 -1
- package/dist/modules/credential_session_cache.d.ts.map +1 -1
- package/dist/modules/credential_session_cache.js +5 -42
- package/dist/modules/credential_session_cache.js.map +1 -1
- package/dist/modules/credit_runtime.d.ts.map +1 -1
- package/dist/modules/credit_runtime.js +126 -162
- package/dist/modules/credit_runtime.js.map +1 -1
- package/dist/modules/crypto/browser_provider.d.ts +1 -1
- package/dist/modules/crypto/browser_provider.d.ts.map +1 -1
- package/dist/modules/crypto/browser_provider.js +7 -11
- package/dist/modules/crypto/browser_provider.js.map +1 -1
- package/dist/modules/crypto/index.d.ts +6 -6
- package/dist/modules/crypto/index.d.ts.map +1 -1
- package/dist/modules/crypto/index.js +12 -30
- package/dist/modules/crypto/index.js.map +1 -1
- package/dist/modules/crypto/node_provider.d.ts +1 -1
- package/dist/modules/crypto/node_provider.d.ts.map +1 -1
- package/dist/modules/crypto/node_provider.js +3 -5
- package/dist/modules/crypto/node_provider.js.map +1 -1
- package/dist/modules/crypto/provider.js +1 -2
- package/dist/modules/crypto/pure_ripemd160.js +1 -3
- package/dist/modules/crypto/pure_ripemd160.js.map +1 -1
- package/dist/modules/crypto/pure_scrypt.js +1 -4
- package/dist/modules/crypto/pure_scrypt.js.map +1 -1
- package/dist/modules/crypto/pure_secp256k1.d.ts +1 -1
- package/dist/modules/crypto/pure_secp256k1.d.ts.map +1 -1
- package/dist/modules/crypto/pure_secp256k1.js +1 -16
- package/dist/modules/crypto/pure_secp256k1.js.map +1 -1
- package/dist/modules/crypto/sync.d.ts.map +1 -1
- package/dist/modules/crypto/sync.js +16 -17
- package/dist/modules/crypto/sync.js.map +1 -1
- package/dist/modules/daemon_node_health.js +14 -16
- package/dist/modules/daemon_node_health.js.map +1 -1
- package/dist/modules/dexbot_class.d.ts +19 -14
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_class.js +127 -164
- package/dist/modules/dexbot_class.js.map +1 -1
- package/dist/modules/dexbot_cow_runtime.d.ts +28 -27
- package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.js +55 -45
- package/dist/modules/dexbot_cow_runtime.js.map +1 -1
- package/dist/modules/dexbot_credential_client.d.ts +6 -2
- package/dist/modules/dexbot_credential_client.d.ts.map +1 -1
- package/dist/modules/dexbot_credential_client.js +21 -31
- package/dist/modules/dexbot_credential_client.js.map +1 -1
- package/dist/modules/dexbot_fill_runtime.d.ts +23 -23
- package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_fill_runtime.js +119 -77
- package/dist/modules/dexbot_fill_runtime.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts +46 -38
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +81 -81
- package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
- package/dist/modules/dexbot_startup_runtime.d.ts +3 -3
- package/dist/modules/dexbot_startup_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_startup_runtime.js +43 -81
- package/dist/modules/dexbot_startup_runtime.js.map +1 -1
- package/dist/modules/dexbot_state_recovery.d.ts +11 -11
- package/dist/modules/dexbot_state_recovery.js +32 -78
- package/dist/modules/dexbot_state_recovery.js.map +1 -1
- package/dist/modules/env.js +3 -7
- package/dist/modules/env.js.map +1 -1
- package/dist/modules/fund_registry.d.ts.map +1 -1
- package/dist/modules/fund_registry.js +12 -24
- package/dist/modules/fund_registry.js.map +1 -1
- package/dist/modules/general_settings.js +10 -16
- package/dist/modules/general_settings.js.map +1 -1
- package/dist/modules/graceful_shutdown.d.ts.map +1 -1
- package/dist/modules/graceful_shutdown.js +12 -19
- package/dist/modules/graceful_shutdown.js.map +1 -1
- package/dist/modules/key_store.d.ts.map +1 -1
- package/dist/modules/key_store.js +27 -65
- package/dist/modules/key_store.js.map +1 -1
- package/dist/modules/launcher/bot_supervisor.d.ts +1 -1
- package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -1
- package/dist/modules/launcher/bot_supervisor.js +89 -118
- package/dist/modules/launcher/bot_supervisor.js.map +1 -1
- package/dist/modules/launcher/child_env.js +1 -5
- package/dist/modules/launcher/child_env.js.map +1 -1
- package/dist/modules/launcher/credential_bootstrap.js +22 -31
- package/dist/modules/launcher/credential_bootstrap.js.map +1 -1
- package/dist/modules/launcher/credential_daemon.js +37 -75
- package/dist/modules/launcher/credential_daemon.js.map +1 -1
- package/dist/modules/launcher/credential_secret.js +2 -37
- package/dist/modules/launcher/credential_secret.js.map +1 -1
- package/dist/modules/launcher/foreign_cred_daemon.js +25 -31
- package/dist/modules/launcher/foreign_cred_daemon.js.map +1 -1
- package/dist/modules/launcher/headless_password.js +10 -12
- package/dist/modules/launcher/headless_password.js.map +1 -1
- package/dist/modules/launcher/launch_modes.js +3 -9
- package/dist/modules/launcher/launch_modes.js.map +1 -1
- package/dist/modules/launcher/market_adapter_runtime.d.ts +1 -1
- package/dist/modules/launcher/market_adapter_runtime.d.ts.map +1 -1
- package/dist/modules/launcher/market_adapter_runtime.js +25 -35
- package/dist/modules/launcher/market_adapter_runtime.js.map +1 -1
- package/dist/modules/launcher/market_adapter_watchdog.d.ts.map +1 -1
- package/dist/modules/launcher/market_adapter_watchdog.js +42 -45
- package/dist/modules/launcher/market_adapter_watchdog.js.map +1 -1
- package/dist/modules/launcher/monolithic_runtime.d.ts +3 -2
- package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -1
- package/dist/modules/launcher/monolithic_runtime.js +97 -138
- package/dist/modules/launcher/monolithic_runtime.js.map +1 -1
- package/dist/modules/launcher/runtime_entry.d.ts +1 -11
- package/dist/modules/launcher/runtime_entry.d.ts.map +1 -1
- package/dist/modules/launcher/runtime_entry.js +12 -15
- package/dist/modules/launcher/runtime_entry.js.map +1 -1
- package/dist/modules/launcher/status_reporting.d.ts +1 -1
- package/dist/modules/launcher/status_reporting.d.ts.map +1 -1
- package/dist/modules/launcher/status_reporting.js +15 -36
- package/dist/modules/launcher/status_reporting.js.map +1 -1
- package/dist/modules/launcher/supervisor_control.js +4 -9
- package/dist/modules/launcher/supervisor_control.js.map +1 -1
- package/dist/modules/logger.d.ts +1 -1
- package/dist/modules/logger.d.ts.map +1 -1
- package/dist/modules/logger.js +3 -38
- package/dist/modules/logger.js.map +1 -1
- package/dist/modules/market_adapter_whitelist.js +9 -18
- package/dist/modules/market_adapter_whitelist.js.map +1 -1
- package/dist/modules/node_failure_ledger.js +1 -3
- package/dist/modules/node_failure_ledger.js.map +1 -1
- package/dist/modules/node_health_cache.d.ts.map +1 -1
- package/dist/modules/node_health_cache.js +15 -25
- package/dist/modules/node_health_cache.js.map +1 -1
- package/dist/modules/node_manager.d.ts +1 -1
- package/dist/modules/node_manager.d.ts.map +1 -1
- package/dist/modules/node_manager.js +44 -43
- package/dist/modules/node_manager.js.map +1 -1
- package/dist/modules/order/accounting.d.ts.map +1 -1
- package/dist/modules/order/accounting.js +102 -118
- package/dist/modules/order/accounting.js.map +1 -1
- package/dist/modules/order/async_lock.d.ts +3 -15
- package/dist/modules/order/async_lock.d.ts.map +1 -1
- package/dist/modules/order/async_lock.js +6 -26
- package/dist/modules/order/async_lock.js.map +1 -1
- package/dist/modules/order/export.js +34 -75
- package/dist/modules/order/export.js.map +1 -1
- package/dist/modules/order/format.d.ts +1 -3
- package/dist/modules/order/format.d.ts.map +1 -1
- package/dist/modules/order/format.js +4 -24
- package/dist/modules/order/format.js.map +1 -1
- package/dist/modules/order/grid.d.ts +8 -8
- package/dist/modules/order/grid.d.ts.map +1 -1
- package/dist/modules/order/grid.js +254 -266
- package/dist/modules/order/grid.js.map +1 -1
- package/dist/modules/order/grid_reconcile.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile.js +77 -94
- package/dist/modules/order/grid_reconcile.js.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.js +117 -149
- package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
- package/dist/modules/order/index.d.ts.map +1 -1
- package/dist/modules/order/index.js +11 -45
- package/dist/modules/order/index.js.map +1 -1
- package/dist/modules/order/logger.d.ts +0 -8
- package/dist/modules/order/logger.d.ts.map +1 -1
- package/dist/modules/order/logger.js +28 -141
- package/dist/modules/order/logger.js.map +1 -1
- package/dist/modules/order/logger_state.d.ts.map +1 -1
- package/dist/modules/order/logger_state.js +3 -4
- package/dist/modules/order/logger_state.js.map +1 -1
- package/dist/modules/order/manager.d.ts +10 -79
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +131 -247
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/processed_fill_store.js +2 -7
- package/dist/modules/order/processed_fill_store.js.map +1 -1
- package/dist/modules/order/strategy.js +23 -25
- package/dist/modules/order/strategy.js.map +1 -1
- package/dist/modules/order/sync_engine.d.ts +27 -0
- package/dist/modules/order/sync_engine.d.ts.map +1 -1
- package/dist/modules/order/sync_engine.js +232 -181
- package/dist/modules/order/sync_engine.js.map +1 -1
- package/dist/modules/order/utils/math.d.ts +1 -25
- package/dist/modules/order/utils/math.d.ts.map +1 -1
- package/dist/modules/order/utils/math.js +49 -172
- package/dist/modules/order/utils/math.js.map +1 -1
- package/dist/modules/order/utils/order.d.ts +46 -2
- package/dist/modules/order/utils/order.d.ts.map +1 -1
- package/dist/modules/order/utils/order.js +165 -143
- package/dist/modules/order/utils/order.js.map +1 -1
- package/dist/modules/order/utils/system.d.ts +3 -1
- package/dist/modules/order/utils/system.d.ts.map +1 -1
- package/dist/modules/order/utils/system.js +152 -211
- package/dist/modules/order/utils/system.js.map +1 -1
- package/dist/modules/order/utils/timeout.js +1 -4
- package/dist/modules/order/utils/timeout.js.map +1 -1
- package/dist/modules/order/utils/validate.js +91 -142
- package/dist/modules/order/utils/validate.js.map +1 -1
- package/dist/modules/order/utils/withPoolRef.js +21 -61
- package/dist/modules/order/utils/withPoolRef.js.map +1 -1
- package/dist/modules/order/working_grid.js +6 -9
- package/dist/modules/order/working_grid.js.map +1 -1
- package/dist/modules/path_api.d.ts.map +1 -1
- package/dist/modules/path_api.js +8 -11
- package/dist/modules/path_api.js.map +1 -1
- package/dist/modules/paths.d.ts.map +1 -1
- package/dist/modules/paths.js +61 -67
- package/dist/modules/paths.js.map +1 -1
- package/dist/modules/process_discovery.d.ts.map +1 -1
- package/dist/modules/process_discovery.js +14 -23
- package/dist/modules/process_discovery.js.map +1 -1
- package/dist/modules/runtime.js +7 -11
- package/dist/modules/runtime.js.map +1 -1
- package/dist/modules/runtime_settings.d.ts.map +1 -1
- package/dist/modules/runtime_settings.js +14 -16
- package/dist/modules/runtime_settings.js.map +1 -1
- package/dist/modules/settings_merge.js +1 -3
- package/dist/modules/settings_merge.js.map +1 -1
- package/dist/modules/socket_json_client.js +3 -5
- package/dist/modules/socket_json_client.js.map +1 -1
- package/dist/modules/storage/browser_adapter.js +1 -4
- package/dist/modules/storage/browser_adapter.js.map +1 -1
- package/dist/modules/storage/index.d.ts +3 -2
- package/dist/modules/storage/index.d.ts.map +1 -1
- package/dist/modules/storage/index.js +5 -8
- package/dist/modules/storage/index.js.map +1 -1
- package/dist/modules/storage/node_adapter.d.ts +3 -4
- package/dist/modules/storage/node_adapter.d.ts.map +1 -1
- package/dist/modules/storage/node_adapter.js +12 -16
- package/dist/modules/storage/node_adapter.js.map +1 -1
- package/dist/modules/storage/types.js +1 -2
- package/dist/modules/types.d.ts +0 -6
- package/dist/modules/types.d.ts.map +1 -1
- package/dist/modules/types.js +1 -2
- package/dist/modules/types.js.map +1 -1
- package/dist/modules/utils/base58check.js +8 -15
- package/dist/modules/utils/base58check.js.map +1 -1
- package/dist/modules/utils/build_dir.js +3 -7
- package/dist/modules/utils/build_dir.js.map +1 -1
- package/dist/modules/utils/errors.js +1 -5
- package/dist/modules/utils/errors.js.map +1 -1
- package/dist/modules/utils/math_utils.d.ts +1 -1
- package/dist/modules/utils/math_utils.d.ts.map +1 -1
- package/dist/modules/utils/math_utils.js +2 -7
- package/dist/modules/utils/math_utils.js.map +1 -1
- package/dist/modules/validate_profiles.d.ts.map +1 -1
- package/dist/modules/validate_profiles.js +18 -25
- package/dist/modules/validate_profiles.js.map +1 -1
- package/dist/pm2.d.ts +4 -70
- package/dist/pm2.d.ts.map +1 -1
- package/dist/pm2.js +106 -132
- package/dist/pm2.js.map +1 -1
- package/dist/scripts/analyze-git.js +13 -17
- package/dist/scripts/analyze-git.js.map +1 -1
- package/dist/scripts/analyze-orders.d.ts.map +1 -1
- package/dist/scripts/analyze-orders.js +48 -63
- package/dist/scripts/analyze-orders.js.map +1 -1
- package/dist/scripts/diagnose-kibana-candles.d.ts.map +1 -1
- package/dist/scripts/diagnose-kibana-candles.js +2 -1
- package/dist/scripts/diagnose-kibana-candles.js.map +1 -1
- package/dist/scripts/diagnose-pool-history.d.ts.map +1 -1
- package/dist/scripts/diagnose-pool-history.js +2 -1
- package/dist/scripts/diagnose-pool-history.js.map +1 -1
- package/dist/scripts/divergence-calc.d.ts.map +1 -1
- package/dist/scripts/divergence-calc.js +2 -2
- package/dist/scripts/divergence-calc.js.map +1 -1
- package/dist/scripts/fix-err-message.js +17 -22
- package/dist/scripts/fix-err-message.js.map +1 -1
- package/dist/scripts/generate_lp_chart.d.ts.map +1 -1
- package/dist/scripts/generate_lp_chart.js +7 -15
- package/dist/scripts/generate_lp_chart.js.map +1 -1
- package/dist/scripts/generate_mainnet_corpus_report.d.ts.map +1 -1
- package/dist/scripts/generate_mainnet_corpus_report.js +3 -2
- package/dist/scripts/generate_mainnet_corpus_report.js.map +1 -1
- package/dist/scripts/generate_market_adapter_whitelist.d.ts.map +1 -1
- package/dist/scripts/generate_market_adapter_whitelist.js +18 -24
- package/dist/scripts/generate_market_adapter_whitelist.js.map +1 -1
- package/dist/scripts/native_release_gates.js +15 -19
- package/dist/scripts/native_release_gates.js.map +1 -1
- package/dist/scripts/print_grid.d.ts.map +1 -1
- package/dist/scripts/print_grid.js +2 -2
- package/dist/scripts/print_grid.js.map +1 -1
- package/dist/scripts/run-tests.js +12 -17
- package/dist/scripts/run-tests.js.map +1 -1
- package/dist/scripts/runner.d.ts.map +1 -1
- package/dist/scripts/runner.js +18 -20
- package/dist/scripts/runner.js.map +1 -1
- package/dist/scripts/sync-version.js +2 -2
- package/dist/scripts/sync-version.js.map +1 -1
- package/dist/scripts/test-credit-renewal.d.ts.map +1 -1
- package/dist/scripts/test-credit-renewal.js +4 -3
- package/dist/scripts/test-credit-renewal.js.map +1 -1
- package/dist/scripts/update.js +93 -95
- package/dist/scripts/update.js.map +1 -1
- package/dist/scripts/validate_bots.d.ts.map +1 -1
- package/dist/scripts/validate_bots.js +2 -2
- package/dist/scripts/validate_bots.js.map +1 -1
- package/dist/scripts/verify-browser-bundle.js +12 -17
- package/dist/scripts/verify-browser-bundle.js.map +1 -1
- package/dist/unlock.d.ts +0 -30
- package/dist/unlock.d.ts.map +1 -1
- package/dist/unlock.js +258 -222
- package/dist/unlock.js.map +1 -1
- package/package.json +5 -8
- package/scripts/README.md +1 -1
- package/scripts/create-bot-symlinks.sh +2 -2
- package/dist/modules/utils/fs_utils.d.ts +0 -18
- package/dist/modules/utils/fs_utils.d.ts.map +0 -1
- package/dist/modules/utils/fs_utils.js +0 -32
- package/dist/modules/utils/fs_utils.js.map +0 -1
- package/dist/scripts/migrate_bot_keys.d.ts +0 -3
- package/dist/scripts/migrate_bot_keys.d.ts.map +0 -1
- package/dist/scripts/migrate_bot_keys.js +0 -225
- package/dist/scripts/migrate_bot_keys.js.map +0 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* modules/order/grid.ts - Grid Engine
|
|
4
3
|
*
|
|
@@ -94,67 +93,12 @@
|
|
|
94
93
|
*
|
|
95
94
|
* ===============================================================================
|
|
96
95
|
*/
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
Object.defineProperty(o, k2, desc);
|
|
104
|
-
}) : (function(o, m, k, k2) {
|
|
105
|
-
if (k2 === undefined) k2 = k;
|
|
106
|
-
o[k2] = m[k];
|
|
107
|
-
}));
|
|
108
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
109
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
110
|
-
}) : function(o, v) {
|
|
111
|
-
o["default"] = v;
|
|
112
|
-
});
|
|
113
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
114
|
-
var ownKeys = function(o) {
|
|
115
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
116
|
-
var ar = [];
|
|
117
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
118
|
-
return ar;
|
|
119
|
-
};
|
|
120
|
-
return ownKeys(o);
|
|
121
|
-
};
|
|
122
|
-
return function (mod) {
|
|
123
|
-
if (mod && mod.__esModule) return mod;
|
|
124
|
-
var result = {};
|
|
125
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
126
|
-
__setModuleDefault(result, mod);
|
|
127
|
-
return result;
|
|
128
|
-
};
|
|
129
|
-
})();
|
|
130
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
131
|
-
exports.calculateGapSlots = calculateGapSlots;
|
|
132
|
-
exports.isGridBloated = isGridBloated;
|
|
133
|
-
exports.isGridBloatGraceActive = isGridBloatGraceActive;
|
|
134
|
-
exports.clearGridBloatFlag = clearGridBloatFlag;
|
|
135
|
-
exports.getSizingContext = getSizingContext;
|
|
136
|
-
exports._getSizingContext = _getSizingContext;
|
|
137
|
-
exports.createOrderGrid = createOrderGrid;
|
|
138
|
-
exports.loadGrid = loadGrid;
|
|
139
|
-
exports.initializeGrid = initializeGrid;
|
|
140
|
-
exports.recalculateGrid = recalculateGrid;
|
|
141
|
-
exports.checkAndUpdateGridIfNeeded = checkAndUpdateGridIfNeeded;
|
|
142
|
-
exports._recalculateGridOrderSizesFromBlockchain = _recalculateGridOrderSizesFromBlockchain;
|
|
143
|
-
exports.updateGridFromBlockchainSnapshot = updateGridFromBlockchainSnapshot;
|
|
144
|
-
exports.compareGrids = compareGrids;
|
|
145
|
-
exports.monitorDivergence = monitorDivergence;
|
|
146
|
-
exports.calculateCurrentSpread = calculateCurrentSpread;
|
|
147
|
-
exports.checkSpreadCondition = checkSpreadCondition;
|
|
148
|
-
exports.checkGridHealth = checkGridHealth;
|
|
149
|
-
exports.checkWindowDust = checkWindowDust;
|
|
150
|
-
exports.hasAnyDust = hasAnyDust;
|
|
151
|
-
exports.getDustOrders = getDustOrders;
|
|
152
|
-
exports.determineOrderSideByFunds = determineOrderSideByFunds;
|
|
153
|
-
exports.prepareSpreadCorrectionOrders = prepareSpreadCorrectionOrders;
|
|
154
|
-
const constants_1 = require("../constants");
|
|
155
|
-
const { GRID_COMPARISON } = constants_1.GRID_LIMITS;
|
|
156
|
-
const Format = __importStar(require("./format"));
|
|
157
|
-
const asymmetric_bounds_1 = require("../../market_adapter/core/asymmetric_bounds");
|
|
96
|
+
import { createRequire } from 'node:module';
|
|
97
|
+
const require = createRequire(import.meta.url);
|
|
98
|
+
import { ORDER_TYPES, ORDER_STATES, COW_ACTIONS, DEFAULT_CONFIG, GRID_LIMITS, TIMING, PIPELINE_TIMING, MARKET_ADAPTER, INCREMENT_BOUNDS } from '../constants.js';
|
|
99
|
+
const { GRID_COMPARISON } = GRID_LIMITS;
|
|
100
|
+
import * as Format from './format.js';
|
|
101
|
+
import { resolveMaxAsymmetryFactor, applyAsymmetricBounds, } from '../../market_adapter/core/asymmetric_bounds.js';
|
|
158
102
|
// FIX: Extract magic numbers to named constants for maintainability
|
|
159
103
|
const GRID_CONSTANTS = {
|
|
160
104
|
RMS_PERCENTAGE_SCALE: 100, // Convert RMS percentage threshold from percent to decimal
|
|
@@ -167,16 +111,16 @@ function _snapshotFundState(manager) {
|
|
|
167
111
|
sellLocked: Number(manager.accountTotals?.sellLocked || 0),
|
|
168
112
|
};
|
|
169
113
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
function calculateGapSlots(incrementPercent, targetSpreadPercent, gridLimitsOverride) {
|
|
177
|
-
return (
|
|
114
|
+
import { floatToBlockchainInt, getPrecisionByOrderType, getPrecisionsForManager, calculateOrderCreationFees, calculateOrderSizes, calculateRotationOrderSizes, calculateAvailableFundsValue, calculateGridSideDivergenceMetric, getPrecisionSlack, getMinAbsoluteOrderSize, getSingleDustThreshold, getGridBestPrices, calculateSpreadFromOrders, allocateFundsByWeights, calculateGapSlots as _mathGapSlots, findPriceCollision, getBtsSide, getSellStartIdx, resolveGapBand, countGapBandSpread, adjustBudgetForBtsFees, } from './utils/math.js';
|
|
115
|
+
import { filterOrdersByType, checkSizesBeforeMinimum, checkSizeThreshold, resolveConfiguredPriceBound, shouldFlagOutOfSpread, isOrderHealthy, isPhantomOrder, isSlotAvailable, isOrderOnChain, isOrderPlaced, hasOnChainId, isEmptyGridSlot, parseSlotIndex, calculateIdealBoundary, assignGridRoles, resolveOnChainRetypeType } from './utils/order.js';
|
|
116
|
+
import { loadAmaCenterPrice, loadAmaCenterSnapshot, withBlockchainRetry } from './utils/system.js';
|
|
117
|
+
import { derivePriceWithPoolRef } from './utils/withPoolRef.js';
|
|
118
|
+
import { getWhitelistFlags } from '../market_adapter_whitelist.js';
|
|
119
|
+
import { getErrorMessage } from '../utils/errors.js';
|
|
120
|
+
export function calculateGapSlots(incrementPercent, targetSpreadPercent, gridLimitsOverride) {
|
|
121
|
+
return _mathGapSlots(incrementPercent, targetSpreadPercent, gridLimitsOverride ?? GRID_LIMITS);
|
|
178
122
|
}
|
|
179
|
-
function isGridBloated(manager, orders) {
|
|
123
|
+
export function isGridBloated(manager, orders) {
|
|
180
124
|
const gridSize = Array.isArray(orders) ? orders.length : orders.size;
|
|
181
125
|
if (!gridSize || !manager?.config)
|
|
182
126
|
return { bloated: false };
|
|
@@ -186,11 +130,11 @@ function isGridBloated(manager, orders) {
|
|
|
186
130
|
return { bloated: false };
|
|
187
131
|
const targetSpreadPct = config.targetSpreadPercent || incPct * 2;
|
|
188
132
|
const orderList = Array.isArray(orders) ? orders : Array.from(orders.values());
|
|
189
|
-
const numBuyActive = orderList.filter((o) => o.type ===
|
|
190
|
-
(o.state ===
|
|
133
|
+
const numBuyActive = orderList.filter((o) => o.type === ORDER_TYPES.BUY &&
|
|
134
|
+
(o.state === ORDER_STATES.ACTIVE || o.state === ORDER_STATES.PARTIAL) &&
|
|
191
135
|
o.orderId).length;
|
|
192
|
-
const numSellActive = orderList.filter((o) => o.type ===
|
|
193
|
-
(o.state ===
|
|
136
|
+
const numSellActive = orderList.filter((o) => o.type === ORDER_TYPES.SELL &&
|
|
137
|
+
(o.state === ORDER_STATES.ACTIVE || o.state === ORDER_STATES.PARTIAL) &&
|
|
194
138
|
o.orderId).length;
|
|
195
139
|
const placedCount = numBuyActive + numSellActive;
|
|
196
140
|
if (!placedCount)
|
|
@@ -221,7 +165,7 @@ function isGridBloated(manager, orders) {
|
|
|
221
165
|
}
|
|
222
166
|
}
|
|
223
167
|
const railEstimate = Math.max(expectedTotal, placedCount);
|
|
224
|
-
const buffer = (config.gridLimits?.MIN_SPREAD_ORDERS ??
|
|
168
|
+
const buffer = (config.gridLimits?.MIN_SPREAD_ORDERS ?? GRID_LIMITS.MIN_SPREAD_ORDERS);
|
|
225
169
|
const maxAllowed = railEstimate + gapSlots + buffer;
|
|
226
170
|
return {
|
|
227
171
|
bloated: gridSize > maxAllowed,
|
|
@@ -241,8 +185,8 @@ function isGridBloated(manager, orders) {
|
|
|
241
185
|
* @param {Object} manager - OrderManager instance.
|
|
242
186
|
* @returns {{active: boolean, elapsed: number, graceMs: number}}
|
|
243
187
|
*/
|
|
244
|
-
function isGridBloatGraceActive(manager) {
|
|
245
|
-
const graceMs = Number(
|
|
188
|
+
export function isGridBloatGraceActive(manager) {
|
|
189
|
+
const graceMs = Number(TIMING?.GRID_BLOAT_RESYNC_GRACE_MS) || TIMING.GRID_BLOAT_RESYNC_GRACE_MS;
|
|
246
190
|
if (!manager._gridBloatDetectedAt) {
|
|
247
191
|
return { active: false, elapsed: 0, graceMs };
|
|
248
192
|
}
|
|
@@ -254,7 +198,7 @@ function isGridBloatGraceActive(manager) {
|
|
|
254
198
|
* returned to normal. Shared so both call sites use the same key.
|
|
255
199
|
* @param {Object} manager - OrderManager instance.
|
|
256
200
|
*/
|
|
257
|
-
function clearGridBloatFlag(manager) {
|
|
201
|
+
export function clearGridBloatFlag(manager) {
|
|
258
202
|
delete manager._gridBloatDetectedAt;
|
|
259
203
|
}
|
|
260
204
|
/**
|
|
@@ -265,7 +209,7 @@ function clearGridBloatFlag(manager) {
|
|
|
265
209
|
* @param {'buy'|'sell'} side
|
|
266
210
|
* @returns {Promise<import('./types').SizingContext|null>}
|
|
267
211
|
*/
|
|
268
|
-
async function getSizingContext(manager, side) {
|
|
212
|
+
export async function getSizingContext(manager, side) {
|
|
269
213
|
return await _getSizingContext(manager, side);
|
|
270
214
|
}
|
|
271
215
|
/**
|
|
@@ -277,7 +221,7 @@ async function getSizingContext(manager, side) {
|
|
|
277
221
|
* @returns {Promise<import('./types').SizingContext|null>}
|
|
278
222
|
* @private
|
|
279
223
|
*/
|
|
280
|
-
async function _getSizingContext(manager, side, { skipRecalc = false } = {}) {
|
|
224
|
+
export async function _getSizingContext(manager, side, { skipRecalc = false } = {}) {
|
|
281
225
|
if (!manager || !manager.assets)
|
|
282
226
|
return null;
|
|
283
227
|
// 1. Ensure fund state is fresh before sizing
|
|
@@ -286,7 +230,7 @@ async function _getSizingContext(manager, side, { skipRecalc = false } = {}) {
|
|
|
286
230
|
}
|
|
287
231
|
const snap = manager.getChainFundsSnapshot ? manager.getChainFundsSnapshot() : {};
|
|
288
232
|
const isBuy = side === 'buy';
|
|
289
|
-
const type = isBuy ?
|
|
233
|
+
const type = isBuy ? ORDER_TYPES.BUY : ORDER_TYPES.SELL;
|
|
290
234
|
// 2. Determine base budget: Always use ALLOCATED funds (respects botFunds %)
|
|
291
235
|
// This ensures the bot only "thinks" about the capital it is allowed to use.
|
|
292
236
|
let budget = isBuy ? (snap.allocatedBuy || 0) : (snap.allocatedSell || 0);
|
|
@@ -297,15 +241,15 @@ async function _getSizingContext(manager, side, { skipRecalc = false } = {}) {
|
|
|
297
241
|
const targetBuy = Math.max(0, manager.config.activeOrders?.buy ?? 1);
|
|
298
242
|
const targetSell = Math.max(0, manager.config.activeOrders?.sell ?? 1);
|
|
299
243
|
const totalTarget = targetBuy + targetSell;
|
|
300
|
-
const btsOrderType =
|
|
301
|
-
const isBtsSide = isBuy ? (btsOrderType ===
|
|
302
|
-
const formulaBudget =
|
|
303
|
-
budget =
|
|
244
|
+
const btsOrderType = getBtsSide(manager.config?.assetA, manager.config?.assetB);
|
|
245
|
+
const isBtsSide = isBuy ? (btsOrderType === ORDER_TYPES.BUY) : (btsOrderType === ORDER_TYPES.SELL);
|
|
246
|
+
const formulaBudget = calculateOrderCreationFees(manager.config.assetA, manager.config.assetB, totalTarget, manager.config?.feeParams?.BTS_RESERVATION_MULTIPLIER);
|
|
247
|
+
budget = adjustBudgetForBtsFees(budget, isBtsSide, formulaBudget, manager.config.min_BTS_value || 0, Format.toFiniteNumber(manager.funds?.btsBalance?.free, 0), Format.toFiniteNumber(isBuy ? (snap.allocatedBuy || 0) : (snap.allocatedSell || 0)), Format.toFiniteNumber(snap.allocatedBuy || 0)
|
|
304
248
|
+ Format.toFiniteNumber(snap.allocatedSell || 0));
|
|
305
249
|
}
|
|
306
250
|
return {
|
|
307
251
|
budget,
|
|
308
|
-
precision:
|
|
252
|
+
precision: getPrecisionByOrderType(manager.assets, type),
|
|
309
253
|
config: manager.config
|
|
310
254
|
};
|
|
311
255
|
}
|
|
@@ -358,7 +302,7 @@ async function _getSizingContext(manager, side, { skipRecalc = false } = {}) {
|
|
|
358
302
|
* @param {import('./types').GridConfig} config - Grid configuration
|
|
359
303
|
* @returns {import('./types').GridCreationResult}
|
|
360
304
|
*/
|
|
361
|
-
function createOrderGrid(config) {
|
|
305
|
+
export function createOrderGrid(config) {
|
|
362
306
|
const { startPrice, minPrice, maxPrice, incrementPercent } = config;
|
|
363
307
|
// FIX: Add comprehensive input validation to prevent silent grid creation failures
|
|
364
308
|
if (!Number.isFinite(startPrice)) {
|
|
@@ -388,7 +332,7 @@ function createOrderGrid(config) {
|
|
|
388
332
|
// Fall back to the canonical INCREMENT_BOUNDS when the config omits
|
|
389
333
|
// incrementBounds. Without this, a non-positive incrementPercent (e.g. 0)
|
|
390
334
|
// silently passes validation and the geometric loop below spins forever.
|
|
391
|
-
const incrementBounds = config.incrementBounds ||
|
|
335
|
+
const incrementBounds = config.incrementBounds || INCREMENT_BOUNDS;
|
|
392
336
|
const minPercent = incrementBounds.MIN_PERCENT;
|
|
393
337
|
const maxPercent = incrementBounds.MAX_PERCENT;
|
|
394
338
|
if (incrementPercent <= 0 || incrementPercent < minPercent || incrementPercent > maxPercent) {
|
|
@@ -439,7 +383,7 @@ function createOrderGrid(config) {
|
|
|
439
383
|
// Determine the boundary and assign roles (BUY/SPREAD/SELL) to each slot.
|
|
440
384
|
//
|
|
441
385
|
// STRATEGY: Center the spread gap around startPrice
|
|
442
|
-
const boundaryIdx =
|
|
386
|
+
const boundaryIdx = calculateIdealBoundary(priceLevels.map((p) => ({ price: p })), startPrice, gapSlots);
|
|
443
387
|
// ================================================================================
|
|
444
388
|
// STEP 4: CREATE ORDER OBJECTS
|
|
445
389
|
// ================================================================================
|
|
@@ -448,12 +392,12 @@ function createOrderGrid(config) {
|
|
|
448
392
|
id: `slot-${i}`,
|
|
449
393
|
price,
|
|
450
394
|
type: null, // assigned below
|
|
451
|
-
state:
|
|
395
|
+
state: ORDER_STATES.VIRTUAL,
|
|
452
396
|
size: 0
|
|
453
397
|
}));
|
|
454
|
-
const updatedOrders =
|
|
455
|
-
const buyCount = updatedOrders.filter((o) => o.type ===
|
|
456
|
-
const sellCount = updatedOrders.filter((o) => o.type ===
|
|
398
|
+
const updatedOrders = assignGridRoles(orders, boundaryIdx, gapSlots, ORDER_TYPES, ORDER_STATES);
|
|
399
|
+
const buyCount = updatedOrders.filter((o) => o.type === ORDER_TYPES.BUY).length;
|
|
400
|
+
const sellCount = updatedOrders.filter((o) => o.type === ORDER_TYPES.SELL).length;
|
|
457
401
|
if (buyCount === 0 || sellCount === 0) {
|
|
458
402
|
throw new Error(`Grid generation produced an imbalanced rail (buy=${buyCount}, sell=${sellCount}) for ` +
|
|
459
403
|
`startPrice=${startPrice}, bounds=[${minPrice}, ${maxPrice}], incrementPercent=${incrementPercent}, ` +
|
|
@@ -503,15 +447,15 @@ function _clearOrderCachesLogic(manager) {
|
|
|
503
447
|
* @param {number|null} [boundaryIdx=null] - The master boundary index.
|
|
504
448
|
* @returns {Promise<void>}
|
|
505
449
|
*/
|
|
506
|
-
async function loadGrid(manager, grid, boundaryIdx = null) {
|
|
450
|
+
export async function loadGrid(manager, grid, boundaryIdx = null) {
|
|
507
451
|
if (!Array.isArray(grid))
|
|
508
452
|
return;
|
|
509
453
|
return await manager._gridLock.acquire(async () => {
|
|
510
454
|
try {
|
|
511
|
-
await
|
|
455
|
+
await withBlockchainRetry(() => manager._initializeAssets(), 'initializeAssets', { logger: manager.logger });
|
|
512
456
|
}
|
|
513
457
|
catch (e) {
|
|
514
|
-
manager.logger?.log?.(`Asset initialization failed during grid load: ${
|
|
458
|
+
manager.logger?.log?.(`Asset initialization failed during grid load: ${getErrorMessage(e)}`, 'warn');
|
|
515
459
|
}
|
|
516
460
|
// RC-2: Use logic helper
|
|
517
461
|
_clearOrderCachesLogic(manager);
|
|
@@ -540,14 +484,14 @@ async function loadGrid(manager, grid, boundaryIdx = null) {
|
|
|
540
484
|
const gapSlots = calculateGapSlots(manager.config?.incrementPercent, manager.config?.targetSpreadPercent, manager.config?.gridLimits);
|
|
541
485
|
manager._gapSlots = gapSlots;
|
|
542
486
|
const buyEndIdx = boundaryIdx;
|
|
543
|
-
const sellStartIdx =
|
|
487
|
+
const sellStartIdx = getSellStartIdx(boundaryIdx, gapSlots);
|
|
544
488
|
let reassignCount = 0;
|
|
545
489
|
grid = grid.map((slot, i) => {
|
|
546
490
|
const correctType = (i <= buyEndIdx)
|
|
547
|
-
?
|
|
491
|
+
? ORDER_TYPES.BUY
|
|
548
492
|
: (i >= sellStartIdx)
|
|
549
|
-
?
|
|
550
|
-
:
|
|
493
|
+
? ORDER_TYPES.SELL
|
|
494
|
+
: ORDER_TYPES.SPREAD;
|
|
551
495
|
// DEFENSIVE BACKSTOP: a VIRTUAL slot with no orderId and
|
|
552
496
|
// zero size is side-neutral — it is a reusable placeholder
|
|
553
497
|
// that may be activated on either rail. Storing a stale
|
|
@@ -562,10 +506,10 @@ async function loadGrid(manager, grid, boundaryIdx = null) {
|
|
|
562
506
|
// boundary-shift and strategy re-plan paths use
|
|
563
507
|
// assignGridRoles (order.ts) with assignOnChain, where
|
|
564
508
|
// geometry-based typing wins.
|
|
565
|
-
if (
|
|
566
|
-
if (slot.type !==
|
|
509
|
+
if (isEmptyGridSlot(slot, slot, { allowNullType: true })) {
|
|
510
|
+
if (slot.type !== ORDER_TYPES.SPREAD)
|
|
567
511
|
reassignCount++;
|
|
568
|
-
return { ...slot, type:
|
|
512
|
+
return { ...slot, type: ORDER_TYPES.SPREAD };
|
|
569
513
|
}
|
|
570
514
|
if (slot.type !== correctType) {
|
|
571
515
|
let newType = correctType;
|
|
@@ -583,8 +527,8 @@ async function loadGrid(manager, grid, boundaryIdx = null) {
|
|
|
583
527
|
// to a number by initializeGrid), which makes every
|
|
584
528
|
// comparison false and wrongly resolves below-center
|
|
585
529
|
// slots to SELL.
|
|
586
|
-
if (newType ===
|
|
587
|
-
newType =
|
|
530
|
+
if (newType === ORDER_TYPES.SPREAD && isOrderOnChain(slot)) {
|
|
531
|
+
newType = resolveOnChainRetypeType(slot, i, buyEndIdx, ORDER_TYPES);
|
|
588
532
|
}
|
|
589
533
|
reassignCount++;
|
|
590
534
|
return { ...slot, type: newType };
|
|
@@ -612,7 +556,7 @@ async function loadGrid(manager, grid, boundaryIdx = null) {
|
|
|
612
556
|
manager._gridBloatDetectedAt = Date.now();
|
|
613
557
|
if (typeof manager.requestStructuralGridResync === 'function') {
|
|
614
558
|
manager.requestStructuralGridResync('grid-bloat-detected', { reason: `Grid size ${d.gridSize} exceeds maximum ${d.maxAllowed}` }).catch((err) => {
|
|
615
|
-
manager.logger?.log?.(`[GRID-BLOAT] Structural resync request failed: ${
|
|
559
|
+
manager.logger?.log?.(`[GRID-BLOAT] Structural resync request failed: ${getErrorMessage(err)}`, 'error');
|
|
616
560
|
});
|
|
617
561
|
}
|
|
618
562
|
}
|
|
@@ -623,9 +567,9 @@ async function loadGrid(manager, grid, boundaryIdx = null) {
|
|
|
623
567
|
// RC-2: Use applyOrderUpdate (PRIVATE/UNLOCKED)
|
|
624
568
|
for (const order of grid) {
|
|
625
569
|
let currentOrder = order;
|
|
626
|
-
if (
|
|
570
|
+
if (isPhantomOrder(order)) {
|
|
627
571
|
manager.logger?.log?.(`Sanitizing corrupted order ${order.id}: ACTIVE/PARTIAL without orderId -> VIRTUAL`, 'warn');
|
|
628
|
-
currentOrder = { ...order, state:
|
|
572
|
+
currentOrder = { ...order, state: ORDER_STATES.VIRTUAL };
|
|
629
573
|
}
|
|
630
574
|
await manager._applyOrderUpdate(currentOrder, 'grid-load', { skipAccounting: true });
|
|
631
575
|
}
|
|
@@ -638,8 +582,8 @@ async function loadGrid(manager, grid, boundaryIdx = null) {
|
|
|
638
582
|
// at creation) so the count is order-independent, matching the
|
|
639
583
|
// accountant (accounting.ts). Fall back to array position only
|
|
640
584
|
// for ids that are not grid slot ids.
|
|
641
|
-
const spreadCount =
|
|
642
|
-
const idx =
|
|
585
|
+
const spreadCount = countGapBandSpread(manager, grid, (o, i) => {
|
|
586
|
+
const idx = parseSlotIndex(o?.id);
|
|
643
587
|
return idx === null ? i : idx;
|
|
644
588
|
});
|
|
645
589
|
manager.initialSpreadCount = spreadCount;
|
|
@@ -652,20 +596,29 @@ async function loadGrid(manager, grid, boundaryIdx = null) {
|
|
|
652
596
|
manager.logger?.log?.(`Loaded ${manager.orders.size} orders from persisted grid.`, 'info');
|
|
653
597
|
});
|
|
654
598
|
}
|
|
599
|
+
function resolveMinScaleSlots(primary, secondary) {
|
|
600
|
+
const p = Number(primary);
|
|
601
|
+
if (Number.isFinite(p))
|
|
602
|
+
return Math.max(0, Math.floor(p));
|
|
603
|
+
const s = Number(secondary);
|
|
604
|
+
if (Number.isFinite(s))
|
|
605
|
+
return Math.max(0, Math.floor(s));
|
|
606
|
+
return MARKET_ADAPTER.ASYMMETRIC_BOUNDS_MIN_SCALE_SLOTS;
|
|
607
|
+
}
|
|
655
608
|
/**
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
async function initializeGrid(manager) {
|
|
609
|
+
* Initialize and orchestrate the order grid.
|
|
610
|
+
*
|
|
611
|
+
* @return {Promise<void>}
|
|
612
|
+
* @throws {Error} If initialization fails or account totals are missing.
|
|
613
|
+
*/
|
|
614
|
+
export async function initializeGrid(manager) {
|
|
662
615
|
if (!manager)
|
|
663
616
|
throw new Error('initializeGrid requires a manager instance');
|
|
664
617
|
try {
|
|
665
|
-
await
|
|
618
|
+
await withBlockchainRetry(() => manager._initializeAssets(), 'initializeAssets', { logger: manager.logger });
|
|
666
619
|
}
|
|
667
620
|
catch (e) {
|
|
668
|
-
manager.logger?.log?.(`Asset initialization failed during grid init: ${
|
|
621
|
+
manager.logger?.log?.(`Asset initialization failed during grid init: ${getErrorMessage(e)}`, 'warn');
|
|
669
622
|
}
|
|
670
623
|
// FIX: Add explicit state validation to prevent cryptic errors later
|
|
671
624
|
if (!manager.assets || !manager.assets.assetA || !manager.assets.assetB) {
|
|
@@ -680,7 +633,7 @@ async function initializeGrid(manager) {
|
|
|
680
633
|
if (typeof mpRaw !== 'number' || isNaN(mpRaw)) {
|
|
681
634
|
try {
|
|
682
635
|
const { BitShares } = require('../bitshares_client');
|
|
683
|
-
const derived = await
|
|
636
|
+
const derived = await derivePriceWithPoolRef(BitShares, manager.config.assetA, manager.config.assetB, manager.config.priceMode || 'auto', manager.config.poolRef);
|
|
684
637
|
if (derived) {
|
|
685
638
|
manager.logger?.log?.(`[DIAGNOSTIC] initializeGrid: Derived new startPrice=${derived.toFixed(8)} (mode=${manager.config.priceMode || 'auto'})`, 'info');
|
|
686
639
|
manager.config.startPrice = Number(derived);
|
|
@@ -690,7 +643,7 @@ async function initializeGrid(manager) {
|
|
|
690
643
|
}
|
|
691
644
|
}
|
|
692
645
|
catch (err) {
|
|
693
|
-
manager.logger?.log?.(`Failed to derive market price: ${
|
|
646
|
+
manager.logger?.log?.(`Failed to derive market price: ${getErrorMessage(err)}`, 'warn');
|
|
694
647
|
throw err; // Re-throw to prevent "pool" string reaching numeric math
|
|
695
648
|
}
|
|
696
649
|
}
|
|
@@ -706,7 +659,7 @@ async function initializeGrid(manager) {
|
|
|
706
659
|
let gp = mp;
|
|
707
660
|
let gpSource = 'startPrice';
|
|
708
661
|
let amaSnapshot = null;
|
|
709
|
-
const whitelistFlags =
|
|
662
|
+
const whitelistFlags = getWhitelistFlags(manager.config.botKey);
|
|
710
663
|
const isGridRangeScalingWhitelisted = whitelistFlags.asymmetricBounds === true;
|
|
711
664
|
let gridPriceOffsetPct = 0;
|
|
712
665
|
const gpRaw = manager.config.gridPrice;
|
|
@@ -719,7 +672,7 @@ async function initializeGrid(manager) {
|
|
|
719
672
|
else if (gpMode === 'pool' || gpMode === 'book') {
|
|
720
673
|
try {
|
|
721
674
|
const { BitShares } = require('../bitshares_client');
|
|
722
|
-
const derived = await
|
|
675
|
+
const derived = await derivePriceWithPoolRef(BitShares, manager.config.assetA, manager.config.assetB, gpMode, manager.config.poolRef);
|
|
723
676
|
if (derived) {
|
|
724
677
|
gp = Number(derived);
|
|
725
678
|
gpSource = gpMode;
|
|
@@ -730,12 +683,12 @@ async function initializeGrid(manager) {
|
|
|
730
683
|
}
|
|
731
684
|
}
|
|
732
685
|
catch (err) {
|
|
733
|
-
manager.logger?.log?.(`initializeGrid: ${gpMode} gridPrice derivation failed: ${
|
|
686
|
+
manager.logger?.log?.(`initializeGrid: ${gpMode} gridPrice derivation failed: ${getErrorMessage(err)}`, 'warn');
|
|
734
687
|
}
|
|
735
688
|
}
|
|
736
689
|
else if (/^ama(?:[1-4])?$/.test(gpMode || '')) {
|
|
737
|
-
amaSnapshot =
|
|
738
|
-
const amaCenter = amaSnapshot?.gridCenterPrice ??
|
|
690
|
+
amaSnapshot = loadAmaCenterSnapshot(manager.config.botKey);
|
|
691
|
+
const amaCenter = amaSnapshot?.gridCenterPrice ?? loadAmaCenterPrice(manager.config.botKey);
|
|
739
692
|
if (Number.isFinite(amaCenter) && amaCenter > 0) {
|
|
740
693
|
gp = amaCenter;
|
|
741
694
|
gpSource = 'ama';
|
|
@@ -750,14 +703,16 @@ async function initializeGrid(manager) {
|
|
|
750
703
|
manager.logger?.log?.(`initializeGrid: AMA center unavailable for gridPrice, falling back to startPrice`, 'warn');
|
|
751
704
|
}
|
|
752
705
|
}
|
|
753
|
-
const minP =
|
|
754
|
-
const maxP =
|
|
706
|
+
const minP = resolveConfiguredPriceBound(manager.config.minPrice, DEFAULT_CONFIG.minPrice, gp, 'min');
|
|
707
|
+
const maxP = resolveConfiguredPriceBound(manager.config.maxPrice, DEFAULT_CONFIG.maxPrice, gp, 'max');
|
|
755
708
|
// Asymmetric bound adjustment: widen the bound in the AMA trend direction
|
|
756
709
|
// and tighten the opposite side, giving the grid more room when the center
|
|
757
710
|
// trails price. Uses slope data from the dynamicgrid.json snapshot.
|
|
758
711
|
let resolvedMinP = minP;
|
|
759
712
|
let resolvedMaxP = maxP;
|
|
760
713
|
let rangeScalingFactor = null;
|
|
714
|
+
let appliedTrend = null;
|
|
715
|
+
let minScaleSlots = null;
|
|
761
716
|
if (gpSource === 'ama' && Number.isFinite(minP) && Number.isFinite(maxP)
|
|
762
717
|
&& isGridRangeScalingWhitelisted) {
|
|
763
718
|
const dw = amaSnapshot?.dynamicWeights;
|
|
@@ -768,8 +723,8 @@ async function initializeGrid(manager) {
|
|
|
768
723
|
? amaSnapshot.asymmetricBounds
|
|
769
724
|
: null;
|
|
770
725
|
if (dw) {
|
|
771
|
-
const maxAsymmetryFactor =
|
|
772
|
-
const adjustment =
|
|
726
|
+
const maxAsymmetryFactor = resolveMaxAsymmetryFactor(manager.config.asymmetricBounds?.maxAsymmetryFactor, dw?.maxAsymmetryFactor, MARKET_ADAPTER.ASYMMETRIC_BOUNDS_MAX_ASYMMETRY_FACTOR);
|
|
727
|
+
const adjustment = applyAsymmetricBounds({
|
|
773
728
|
centerPrice: gp,
|
|
774
729
|
minPrice: minP,
|
|
775
730
|
maxPrice: maxP,
|
|
@@ -782,6 +737,8 @@ async function initializeGrid(manager) {
|
|
|
782
737
|
resolvedMinP = adjustment.resolvedMinPrice;
|
|
783
738
|
resolvedMaxP = adjustment.resolvedMaxPrice;
|
|
784
739
|
rangeScalingFactor = Number(adjustment.appliedAsymmetryFactor);
|
|
740
|
+
appliedTrend = (dw?.trend === 'UP' || dw?.trend === 'DOWN') ? dw.trend : null;
|
|
741
|
+
minScaleSlots = resolveMinScaleSlots(manager.config.asymmetricBounds?.minScaleSlots, dw?.minScaleSlots);
|
|
785
742
|
manager.logger?.log?.(`[BOUND-ASYMMETRY] trend=${dw.trend} slopeOffset=${dw.slopeOffset.toFixed(4)} `
|
|
786
743
|
+ `raw=${((adjustment.rawAsymmetryFactor ?? 0) * 100).toFixed(1)}% `
|
|
787
744
|
+ `cap=${((maxAsymmetryFactor ?? 0) * 100).toFixed(0)}% `
|
|
@@ -803,6 +760,8 @@ async function initializeGrid(manager) {
|
|
|
803
760
|
resolvedMaxP = gp * ((maxP / gp) * (1 + asymmetry));
|
|
804
761
|
}
|
|
805
762
|
rangeScalingFactor = asymmetry;
|
|
763
|
+
appliedTrend = rootTrend;
|
|
764
|
+
minScaleSlots = resolveMinScaleSlots(manager.config.asymmetricBounds?.minScaleSlots, rootBounds.minScaleSlots);
|
|
806
765
|
manager.logger?.log?.(`[BOUND-ASYMMETRY] trend=${rootTrend} `
|
|
807
766
|
+ `asymmetry=${(asymmetry * 100).toFixed(1)}% `
|
|
808
767
|
+ `(root-level) min ${(minP ?? 0).toFixed(8)}→${(resolvedMinP ?? 0).toFixed(8)} `
|
|
@@ -831,6 +790,35 @@ async function initializeGrid(manager) {
|
|
|
831
790
|
gridStartPrice = clamped;
|
|
832
791
|
}
|
|
833
792
|
}
|
|
793
|
+
// Narrowing-side slot guard: range scaling tightens one bound toward the
|
|
794
|
+
// center. Without a floor this can collapse that side into a near-center
|
|
795
|
+
// sliver holding few or zero active orders. Guarantee at least
|
|
796
|
+
// minScaleSlots price levels remain between the grid center and the
|
|
797
|
+
// tightened bound (in multiples of incrementPercent). The widened side
|
|
798
|
+
// still extends freely.
|
|
799
|
+
if (appliedTrend && Number.isFinite(gridStartPrice) && gridStartPrice > 0) {
|
|
800
|
+
const inc = Number(manager.config.incrementPercent);
|
|
801
|
+
const mss = Number.isFinite(minScaleSlots) ? Math.floor(Number(minScaleSlots)) : 0;
|
|
802
|
+
if (Number.isFinite(inc) && inc > 0 && mss > 0) {
|
|
803
|
+
const stepMult = 1 + (inc / 100);
|
|
804
|
+
if (appliedTrend === 'DOWN' && resolvedMaxP != null) {
|
|
805
|
+
const keepAbove = gridStartPrice * Math.pow(stepMult, mss);
|
|
806
|
+
if (resolvedMaxP < keepAbove) {
|
|
807
|
+
manager.logger?.log?.(`[BOUND-ASYMMETRY] narrowing-side guard: max ${resolvedMaxP.toFixed(8)} collapses ` +
|
|
808
|
+
`${mss} levels; holding at ${keepAbove.toFixed(8)}`, 'info');
|
|
809
|
+
resolvedMaxP = keepAbove;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
else if (appliedTrend === 'UP' && resolvedMinP != null) {
|
|
813
|
+
const belowMin = gridStartPrice * Math.pow(1 - (inc / 100), mss);
|
|
814
|
+
if (resolvedMinP > belowMin) {
|
|
815
|
+
manager.logger?.log?.(`[BOUND-ASYMMETRY] narrowing-side guard would pull min ${resolvedMinP.toFixed(8)} ` +
|
|
816
|
+
`short of ${mss} levels; holding at ${belowMin.toFixed(8)}`, 'info');
|
|
817
|
+
resolvedMinP = belowMin;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
834
822
|
manager.config.minPrice = resolvedMinP;
|
|
835
823
|
manager.config.maxPrice = resolvedMaxP;
|
|
836
824
|
manager._lastGridPricingContext = {
|
|
@@ -845,14 +833,14 @@ async function initializeGrid(manager) {
|
|
|
845
833
|
// Ensure percentage-based funds are resolved before sizing
|
|
846
834
|
try {
|
|
847
835
|
if (manager.accountId && !manager.accountTotals) {
|
|
848
|
-
await manager.waitForAccountTotals(
|
|
836
|
+
await manager.waitForAccountTotals(TIMING.ACCOUNT_TOTALS_TIMEOUT_MS);
|
|
849
837
|
}
|
|
850
838
|
}
|
|
851
839
|
catch (e) {
|
|
852
|
-
manager.logger?.log?.(`Failed to load account totals: ${
|
|
840
|
+
manager.logger?.log?.(`Failed to load account totals: ${getErrorMessage(e)}`, 'warn');
|
|
853
841
|
// FIX: Add error handling - cannot proceed with grid initialization without account totals
|
|
854
842
|
// Continuing would create grid with 0 fund allocation, rendering it non-functional
|
|
855
|
-
throw new Error(`Cannot initialize grid without account totals: ${
|
|
843
|
+
throw new Error(`Cannot initialize grid without account totals: ${getErrorMessage(e)}`);
|
|
856
844
|
}
|
|
857
845
|
const { orders, boundaryIdx, initialSpreadCount, gapSlots } = createOrderGrid({
|
|
858
846
|
...manager.config,
|
|
@@ -871,13 +859,13 @@ async function initializeGrid(manager) {
|
|
|
871
859
|
manager.notifyBoundaryUpdate(boundaryIdx);
|
|
872
860
|
}
|
|
873
861
|
catch (err) {
|
|
874
|
-
manager.logger?.log?.(`Error notifying boundary update: ${
|
|
862
|
+
manager.logger?.log?.(`Error notifying boundary update: ${getErrorMessage(err)}`, 'warn');
|
|
875
863
|
}
|
|
876
864
|
}
|
|
877
865
|
}
|
|
878
|
-
const minSellSize =
|
|
879
|
-
const minBuySize =
|
|
880
|
-
const { A: precA, B: precB } =
|
|
866
|
+
const minSellSize = getMinAbsoluteOrderSize(ORDER_TYPES.SELL, manager.assets);
|
|
867
|
+
const minBuySize = getMinAbsoluteOrderSize(ORDER_TYPES.BUY, manager.assets);
|
|
868
|
+
const { A: precA, B: precB } = getPrecisionsForManager(manager.assets);
|
|
881
869
|
// Use centralized sizing context for both sides.
|
|
882
870
|
// Resolve funds once upfront so both contexts share the same snapshot,
|
|
883
871
|
// avoiding a redundant recalculateFunds inside the second _getSizingContext call.
|
|
@@ -886,17 +874,17 @@ async function initializeGrid(manager) {
|
|
|
886
874
|
const buyCtx = await _getSizingContext(manager, 'buy', { skipRecalc: true });
|
|
887
875
|
if (!sellCtx || !buyCtx)
|
|
888
876
|
throw new Error('Failed to retrieve sizing context for grid initialization');
|
|
889
|
-
let sizedOrders =
|
|
877
|
+
let sizedOrders = calculateOrderSizes(orders, manager.config, sellCtx.budget, buyCtx.budget, minSellSize, minBuySize, precA, precB);
|
|
890
878
|
// Verification of sizes
|
|
891
|
-
const sells =
|
|
892
|
-
const buys =
|
|
893
|
-
if (
|
|
879
|
+
const sells = filterOrdersByType(sizedOrders, ORDER_TYPES.SELL).map((o) => Number(o.size || 0));
|
|
880
|
+
const buys = filterOrdersByType(sizedOrders, ORDER_TYPES.BUY).map((o) => Number(o.size || 0));
|
|
881
|
+
if (checkSizesBeforeMinimum(sells, minSellSize, precA) || checkSizesBeforeMinimum(buys, minBuySize, precB)) {
|
|
894
882
|
throw new Error('Calculated orders fall below minimum allowable size.');
|
|
895
883
|
}
|
|
896
884
|
// Check for warning if orders are near minimal size (regression fix)
|
|
897
|
-
const warningSellSize = minSellSize > 0 ?
|
|
898
|
-
const warningBuySize = minBuySize > 0 ?
|
|
899
|
-
if (
|
|
885
|
+
const warningSellSize = minSellSize > 0 ? getMinAbsoluteOrderSize(ORDER_TYPES.SELL, manager.assets, 100) : 0;
|
|
886
|
+
const warningBuySize = minBuySize > 0 ? getMinAbsoluteOrderSize(ORDER_TYPES.BUY, manager.assets, 100) : 0;
|
|
887
|
+
if (checkSizeThreshold(sells, warningSellSize, precA, false) || checkSizeThreshold(buys, warningBuySize, precB, false)) {
|
|
900
888
|
manager.logger?.log?.("WARNING: Order grid contains orders near minimum size. To ensure the bot runs properly, consider increasing the funds of your bot.", "warn");
|
|
901
889
|
}
|
|
902
890
|
// RC-2: Wrap atomic changes in grid lock
|
|
@@ -941,13 +929,13 @@ async function initializeGrid(manager) {
|
|
|
941
929
|
* @param {string} opts.privateKey - Private key.
|
|
942
930
|
* @returns {Promise<void>}
|
|
943
931
|
*/
|
|
944
|
-
async function recalculateGrid(manager, opts) {
|
|
932
|
+
export async function recalculateGrid(manager, opts) {
|
|
945
933
|
const { readOpenOrdersFn, chainOrders, account, privateKey } = opts;
|
|
946
934
|
// Suppress invariant warnings during full resync
|
|
947
935
|
manager.startBootstrap();
|
|
948
936
|
// Total timeout across all steps — prevents indefinite hang even if
|
|
949
937
|
// an individual withBlockchainRetry step pins the event loop.
|
|
950
|
-
const totalTimeoutMs =
|
|
938
|
+
const totalTimeoutMs = PIPELINE_TIMING.TIMEOUT_MS * 2; // 10 min
|
|
951
939
|
let _resyncAborted = false;
|
|
952
940
|
const work = (async () => {
|
|
953
941
|
try {
|
|
@@ -956,24 +944,24 @@ async function recalculateGrid(manager, opts) {
|
|
|
956
944
|
return;
|
|
957
945
|
// #1: Initialize assets with timeout + retry + node failover
|
|
958
946
|
try {
|
|
959
|
-
await
|
|
947
|
+
await withBlockchainRetry(() => manager._initializeAssets(), 'initializeAssets', { logger: manager.logger });
|
|
960
948
|
}
|
|
961
949
|
catch (e) {
|
|
962
|
-
manager.logger?.log?.(`Asset initialization failed during resync: ${
|
|
950
|
+
manager.logger?.log?.(`Asset initialization failed during resync: ${getErrorMessage(e)}`, 'warn');
|
|
963
951
|
}
|
|
964
952
|
if (_resyncAborted)
|
|
965
953
|
return;
|
|
966
954
|
// #2: Fetch account totals with timeout + retry + node failover
|
|
967
|
-
await
|
|
955
|
+
await withBlockchainRetry(() => manager.fetchAccountTotals(), 'fetchAccountTotals', { logger: manager.logger });
|
|
968
956
|
if (_resyncAborted)
|
|
969
957
|
return;
|
|
970
958
|
// #3: Read open orders with timeout + retry + node failover
|
|
971
|
-
const chainOpenOrders = await
|
|
959
|
+
const chainOpenOrders = await withBlockchainRetry(() => readOpenOrdersFn(), 'readOpenOrders', { logger: manager.logger });
|
|
972
960
|
if (_resyncAborted)
|
|
973
961
|
return;
|
|
974
962
|
if (!Array.isArray(chainOpenOrders))
|
|
975
963
|
return;
|
|
976
|
-
await
|
|
964
|
+
await withBlockchainRetry(() => manager.syncFromOpenOrders(chainOpenOrders, { skipAccounting: true }), 'syncFromOpenOrders', { logger: manager.logger });
|
|
977
965
|
if (_resyncAborted)
|
|
978
966
|
return;
|
|
979
967
|
// resetFunds under _fundLock + snapshot; persistGrid outside lock to
|
|
@@ -1013,8 +1001,8 @@ async function recalculateGrid(manager, opts) {
|
|
|
1013
1001
|
await reconcileGridOrders({ manager, config: manager.config, account, privateKey, chainOrders, chainOpenOrders });
|
|
1014
1002
|
}
|
|
1015
1003
|
catch (err) {
|
|
1016
|
-
manager.logger?.log?.(`Error during startup order reconciliation: ${
|
|
1017
|
-
throw new Error(`Grid recalculation failed during order reconciliation: ${
|
|
1004
|
+
manager.logger?.log?.(`Error during startup order reconciliation: ${getErrorMessage(err)}`, 'error');
|
|
1005
|
+
throw new Error(`Grid recalculation failed during order reconciliation: ${getErrorMessage(err)}`);
|
|
1018
1006
|
}
|
|
1019
1007
|
if (_resyncAborted)
|
|
1020
1008
|
return;
|
|
@@ -1045,20 +1033,20 @@ async function recalculateGrid(manager, opts) {
|
|
|
1045
1033
|
* @param {import('./types').OrderManager} manager - Manager instance with order state
|
|
1046
1034
|
* @returns {import('./types').SideUpdateFlags}
|
|
1047
1035
|
*/
|
|
1048
|
-
function checkAndUpdateGridIfNeeded(manager) {
|
|
1036
|
+
export function checkAndUpdateGridIfNeeded(manager) {
|
|
1049
1037
|
const threshold = manager.config?.gridLimits?.GRID_REGENERATION_PERCENTAGE;
|
|
1050
1038
|
const chainSnap = manager.getChainFundsSnapshot();
|
|
1051
1039
|
const gridBuy = Number(manager.funds?.total?.grid?.buy || 0);
|
|
1052
1040
|
const gridSell = Number(manager.funds?.total?.grid?.sell || 0);
|
|
1053
1041
|
const result = { buyUpdated: false, sellUpdated: false };
|
|
1054
1042
|
const sides = [
|
|
1055
|
-
{ name: 'buy', grid: gridBuy, orderType:
|
|
1056
|
-
{ name: 'sell', grid: gridSell, orderType:
|
|
1043
|
+
{ name: 'buy', grid: gridBuy, orderType: ORDER_TYPES.BUY },
|
|
1044
|
+
{ name: 'sell', grid: gridSell, orderType: ORDER_TYPES.SELL }
|
|
1057
1045
|
];
|
|
1058
1046
|
for (const s of sides) {
|
|
1059
1047
|
if (s.grid <= 0)
|
|
1060
1048
|
continue;
|
|
1061
|
-
const availableFunds =
|
|
1049
|
+
const availableFunds = calculateAvailableFundsValue(s.name, manager.accountTotals, manager.funds, manager.config.assetA, manager.config.assetB, manager.config.activeOrders, manager.config.min_BTS_value, manager.config.feeParams ?? null);
|
|
1062
1050
|
// Denominator: side's allocated capital (or chain total fallback).
|
|
1063
1051
|
const allocated = s.name === 'buy' ? chainSnap.allocatedBuy : chainSnap.allocatedSell;
|
|
1064
1052
|
const denominator = (allocated > 0) ? allocated : (s.grid + availableFunds);
|
|
@@ -1128,16 +1116,16 @@ function checkAndUpdateGridIfNeeded(manager) {
|
|
|
1128
1116
|
* @param {import('./types').OrderManager} manager - OrderManager instance
|
|
1129
1117
|
* @param {string} orderType - ORDER_TYPES.BUY or ORDER_TYPES.SELL
|
|
1130
1118
|
* @param {Object} [options] - Options object
|
|
1131
|
-
* @param {import('./working_grid')} [options.workingGrid] - Working grid for COW pattern
|
|
1119
|
+
* @param {import('./working_grid.js')} [options.workingGrid] - Working grid for COW pattern
|
|
1132
1120
|
* @returns {Promise<{actions: Array, changed: boolean}|undefined>} - COW result or undefined
|
|
1133
1121
|
* @private
|
|
1134
1122
|
*/
|
|
1135
|
-
async function _recalculateGridOrderSizesFromBlockchain(manager, orderType, options = {}) {
|
|
1123
|
+
export async function _recalculateGridOrderSizesFromBlockchain(manager, orderType, options = {}) {
|
|
1136
1124
|
if (!manager.assets)
|
|
1137
1125
|
return options?.workingGrid ? { actions: [], changed: false } : undefined;
|
|
1138
1126
|
const workingGrid = options?.workingGrid || null;
|
|
1139
1127
|
const collectActions = !!workingGrid;
|
|
1140
|
-
const isBuy = orderType ===
|
|
1128
|
+
const isBuy = orderType === ORDER_TYPES.BUY;
|
|
1141
1129
|
const sideName = isBuy ? 'buy' : 'sell';
|
|
1142
1130
|
// Use centralized sizing context (respects botFunds % allocation)
|
|
1143
1131
|
const ctx = await _getSizingContext(manager, sideName);
|
|
@@ -1173,7 +1161,7 @@ async function _recalculateGridOrderSizesFromBlockchain(manager, orderType, opti
|
|
|
1173
1161
|
if (allSideSlots.length === 0)
|
|
1174
1162
|
return collectActions ? { actions: [], changed: false } : undefined;
|
|
1175
1163
|
// Calculate geometric sizes for the ENTIRE rail
|
|
1176
|
-
const newSizes =
|
|
1164
|
+
const newSizes = calculateRotationOrderSizes(ctx.budget, 0, allSideSlots.length, orderType, manager.config, 0, ctx.precision);
|
|
1177
1165
|
const actions = [];
|
|
1178
1166
|
let changed = false;
|
|
1179
1167
|
const freeKey = isBuy ? 'buyFree' : 'sellFree';
|
|
@@ -1192,7 +1180,7 @@ async function _recalculateGridOrderSizesFromBlockchain(manager, orderType, opti
|
|
|
1192
1180
|
// NOTE: BTS update fees are paid from BTS balance (separate from asset balance),
|
|
1193
1181
|
// so they don't affect this asset-side size cap. Fee budgets are tracked in
|
|
1194
1182
|
// funds.btsFeesOwed and reserved separately via btsFeesReservation.
|
|
1195
|
-
const isCommitted =
|
|
1183
|
+
const isCommitted = isOrderOnChain(slot);
|
|
1196
1184
|
if (isCommitted) {
|
|
1197
1185
|
const currentSize = Number(slot.size || 0);
|
|
1198
1186
|
const delta = newSize - currentSize;
|
|
@@ -1212,8 +1200,8 @@ async function _recalculateGridOrderSizesFromBlockchain(manager, orderType, opti
|
|
|
1212
1200
|
}
|
|
1213
1201
|
}
|
|
1214
1202
|
// Use integer comparison to avoid redundant updates from float noise
|
|
1215
|
-
const currentSizeInt =
|
|
1216
|
-
const newSizeInt =
|
|
1203
|
+
const currentSizeInt = floatToBlockchainInt(slot.size || 0, ctx.precision);
|
|
1204
|
+
const newSizeInt = floatToBlockchainInt(newSize, ctx.precision);
|
|
1217
1205
|
if (slot.size === undefined || currentSizeInt !== newSizeInt) {
|
|
1218
1206
|
changed = true;
|
|
1219
1207
|
if (collectActions) {
|
|
@@ -1221,9 +1209,9 @@ async function _recalculateGridOrderSizesFromBlockchain(manager, orderType, opti
|
|
|
1221
1209
|
...slot,
|
|
1222
1210
|
size: newSize
|
|
1223
1211
|
});
|
|
1224
|
-
if (isCommitted &&
|
|
1212
|
+
if (isCommitted && hasOnChainId(slot)) {
|
|
1225
1213
|
actions.push({
|
|
1226
|
-
type:
|
|
1214
|
+
type: COW_ACTIONS.UPDATE,
|
|
1227
1215
|
id: slot.id,
|
|
1228
1216
|
orderId: slot.orderId,
|
|
1229
1217
|
newGridId: slot.id,
|
|
@@ -1269,9 +1257,9 @@ async function _recalculateGridOrderSizesFromBlockchain(manager, orderType, opti
|
|
|
1269
1257
|
* @param {string} orderType - 'buy', 'sell', or 'both' - which sides to update
|
|
1270
1258
|
* @param {boolean} [fromBlockchainTimer=false] - If true, skip refetch of account totals (already current)
|
|
1271
1259
|
* @param {number|null} [overrideBoundaryIdx=null] - Optional override for boundary index
|
|
1272
|
-
* @returns {Promise<{actions: Array, workingGrid: import('./working_grid'), workingIndexes: Object, workingBoundary: number, hasWorkingChanges: boolean, aborted: boolean}|null>}
|
|
1260
|
+
* @returns {Promise<{actions: Array, workingGrid: import('./working_grid.js'), workingIndexes: Object, workingBoundary: number, hasWorkingChanges: boolean, aborted: boolean}|null>}
|
|
1273
1261
|
*/
|
|
1274
|
-
async function updateGridFromBlockchainSnapshot(manager, orderType = 'both', fromBlockchainTimer = false, overrideBoundaryIdx = null) {
|
|
1262
|
+
export async function updateGridFromBlockchainSnapshot(manager, orderType = 'both', fromBlockchainTimer = false, overrideBoundaryIdx = null) {
|
|
1275
1263
|
if (!fromBlockchainTimer && manager.config?.accountId) {
|
|
1276
1264
|
await manager.fetchAccountTotals(manager.config.accountId);
|
|
1277
1265
|
}
|
|
@@ -1297,7 +1285,7 @@ async function updateGridFromBlockchainSnapshot(manager, orderType = 'both', fro
|
|
|
1297
1285
|
const allSlots = Array.from(workingGrid.values())
|
|
1298
1286
|
.filter((s) => s.price != null)
|
|
1299
1287
|
.sort((a, b) => a.price - b.price);
|
|
1300
|
-
const updatedSlots =
|
|
1288
|
+
const updatedSlots = assignGridRoles(allSlots, newBoundary, gapSlots, ORDER_TYPES, ORDER_STATES, { assignOnChain: true });
|
|
1301
1289
|
for (const slot of updatedSlots) {
|
|
1302
1290
|
workingGrid.set(slot.id, slot);
|
|
1303
1291
|
}
|
|
@@ -1306,13 +1294,13 @@ async function updateGridFromBlockchainSnapshot(manager, orderType = 'both', fro
|
|
|
1306
1294
|
// Calculate size updates for each side (via existing sizing function in COW mode).
|
|
1307
1295
|
// _recalculateGridOrderSizesFromBlockchain reads types from the working grid when
|
|
1308
1296
|
// one is passed, so boundary-crossing slots are now correctly classified.
|
|
1309
|
-
if (orderType ===
|
|
1310
|
-
const buyResult = await _recalculateGridOrderSizesFromBlockchain(manager,
|
|
1297
|
+
if (orderType === ORDER_TYPES.BUY || orderType === 'both') {
|
|
1298
|
+
const buyResult = await _recalculateGridOrderSizesFromBlockchain(manager, ORDER_TYPES.BUY, { workingGrid });
|
|
1311
1299
|
allActions.push(...buyResult.actions);
|
|
1312
1300
|
hasWorkingChanges = hasWorkingChanges || buyResult.changed;
|
|
1313
1301
|
}
|
|
1314
|
-
if (orderType ===
|
|
1315
|
-
const sellResult = await _recalculateGridOrderSizesFromBlockchain(manager,
|
|
1302
|
+
if (orderType === ORDER_TYPES.SELL || orderType === 'both') {
|
|
1303
|
+
const sellResult = await _recalculateGridOrderSizesFromBlockchain(manager, ORDER_TYPES.SELL, { workingGrid });
|
|
1316
1304
|
allActions.push(...sellResult.actions);
|
|
1317
1305
|
hasWorkingChanges = hasWorkingChanges || sellResult.changed;
|
|
1318
1306
|
}
|
|
@@ -1358,7 +1346,7 @@ async function updateGridFromBlockchainSnapshot(manager, orderType = 'both', fro
|
|
|
1358
1346
|
* @param {import('./types').OrderManager|null} [manager=null] - Manager instance (for grid lock access)
|
|
1359
1347
|
* @returns {Promise<import('./types').GridComparisonResult>}
|
|
1360
1348
|
*/
|
|
1361
|
-
async function compareGrids(calculatedGrid, persistedGrid, manager = null) {
|
|
1349
|
+
export async function compareGrids(calculatedGrid, persistedGrid, manager = null) {
|
|
1362
1350
|
if (!Array.isArray(calculatedGrid) || !Array.isArray(persistedGrid)) {
|
|
1363
1351
|
return { buy: { metric: 0, updated: false }, sell: { metric: 0, updated: false } };
|
|
1364
1352
|
}
|
|
@@ -1381,14 +1369,14 @@ async function compareGrids(calculatedGrid, persistedGrid, manager = null) {
|
|
|
1381
1369
|
// they are instead handled by the available-funds ratio check or follow-up correction.
|
|
1382
1370
|
// Must be sorted ASC for calculateRotationOrderSizes to match geometric weight distribution
|
|
1383
1371
|
const filterForRms = (orders, type) => {
|
|
1384
|
-
const result = Array.isArray(orders) ? orders.filter((o) => o && o.type === type && o.state ===
|
|
1372
|
+
const result = Array.isArray(orders) ? orders.filter((o) => o && o.type === type && o.state === ORDER_STATES.ACTIVE) : [];
|
|
1385
1373
|
return result
|
|
1386
1374
|
.sort((a, b) => (a.price ?? 0) - (b.price ?? 0));
|
|
1387
1375
|
};
|
|
1388
|
-
const calculatedBuys = filterForRms(calculatedSnap,
|
|
1389
|
-
const calculatedSells = filterForRms(calculatedSnap,
|
|
1390
|
-
const persistedBuys = filterForRms(persistedSnap,
|
|
1391
|
-
const persistedSells = filterForRms(persistedSnap,
|
|
1376
|
+
const calculatedBuys = filterForRms(calculatedSnap, ORDER_TYPES.BUY);
|
|
1377
|
+
const calculatedSells = filterForRms(calculatedSnap, ORDER_TYPES.SELL);
|
|
1378
|
+
const persistedBuys = filterForRms(persistedSnap, ORDER_TYPES.BUY);
|
|
1379
|
+
const persistedSells = filterForRms(persistedSnap, ORDER_TYPES.SELL);
|
|
1392
1380
|
// Calculate ideal sizes for each order based on current available budget.
|
|
1393
1381
|
// The sizing context (which includes recalculateFunds) is resolved once per side up front
|
|
1394
1382
|
// so both buy and sell metrics share a single fund snapshot. This avoids the previous
|
|
@@ -1411,7 +1399,7 @@ async function compareGrids(calculatedGrid, persistedGrid, manager = null) {
|
|
|
1411
1399
|
return activeOrders;
|
|
1412
1400
|
// Calculate geometric ideals for the ENTIRE side (all slots)
|
|
1413
1401
|
try {
|
|
1414
|
-
const allIdealSizes =
|
|
1402
|
+
const allIdealSizes = calculateRotationOrderSizes(ctx.budget, 0, sideSlots.length, type, manager.config, 0, ctx.precision);
|
|
1415
1403
|
// Map Ideal sizes to IDs for quick lookup
|
|
1416
1404
|
const idealMap = new Map();
|
|
1417
1405
|
sideSlots.forEach((slot, i) => idealMap.set(slot.id, allIdealSizes[i]));
|
|
@@ -1433,11 +1421,11 @@ async function compareGrids(calculatedGrid, persistedGrid, manager = null) {
|
|
|
1433
1421
|
const sellCtx = needsSell
|
|
1434
1422
|
? await _getSizingContext(manager, 'sell', { skipRecalc: true })
|
|
1435
1423
|
: null;
|
|
1436
|
-
const buyIdeals = computeSideIdeals(calculatedBuys,
|
|
1437
|
-
const sellIdeals = computeSideIdeals(calculatedSells,
|
|
1424
|
+
const buyIdeals = computeSideIdeals(calculatedBuys, ORDER_TYPES.BUY, buyCtx);
|
|
1425
|
+
const sellIdeals = computeSideIdeals(calculatedSells, ORDER_TYPES.SELL, sellCtx);
|
|
1438
1426
|
// Calculate RMS divergence metric for each side
|
|
1439
|
-
const buyMetric =
|
|
1440
|
-
const sellMetric =
|
|
1427
|
+
const buyMetric = calculateGridSideDivergenceMetric(buyIdeals, persistedBuys, 'buy');
|
|
1428
|
+
const sellMetric = calculateGridSideDivergenceMetric(sellIdeals, persistedSells, 'sell');
|
|
1441
1429
|
// Check if metrics exceed threshold and flag sides for regeneration
|
|
1442
1430
|
// Set RMS_PERCENTAGE to 0 to disable RMS divergence checks
|
|
1443
1431
|
let buyUpdated = false, sellUpdated = false;
|
|
@@ -1447,14 +1435,14 @@ async function compareGrids(calculatedGrid, persistedGrid, manager = null) {
|
|
|
1447
1435
|
// RC-3: Use Set for automatic duplicate prevention
|
|
1448
1436
|
if (!(manager._gridSidesUpdated instanceof Set))
|
|
1449
1437
|
manager._gridSidesUpdated = new Set();
|
|
1450
|
-
manager._gridSidesUpdated.add(
|
|
1438
|
+
manager._gridSidesUpdated.add(ORDER_TYPES.BUY);
|
|
1451
1439
|
buyUpdated = true;
|
|
1452
1440
|
}
|
|
1453
1441
|
if (sellMetric > limit) {
|
|
1454
1442
|
// RC-3: Use Set for automatic duplicate prevention
|
|
1455
1443
|
if (!(manager._gridSidesUpdated instanceof Set))
|
|
1456
1444
|
manager._gridSidesUpdated = new Set();
|
|
1457
|
-
manager._gridSidesUpdated.add(
|
|
1445
|
+
manager._gridSidesUpdated.add(ORDER_TYPES.SELL);
|
|
1458
1446
|
sellUpdated = true;
|
|
1459
1447
|
}
|
|
1460
1448
|
}
|
|
@@ -1473,7 +1461,7 @@ async function compareGrids(calculatedGrid, persistedGrid, manager = null) {
|
|
|
1473
1461
|
* @param {Array<import('./types').GridOrderSlot>} persistedGrid - Current/persisted grid
|
|
1474
1462
|
* @returns {Promise<import('./types').DivergenceResult>}
|
|
1475
1463
|
*/
|
|
1476
|
-
async function monitorDivergence(manager, calculatedGrid, persistedGrid) {
|
|
1464
|
+
export async function monitorDivergence(manager, calculatedGrid, persistedGrid) {
|
|
1477
1465
|
// 1. Check ratio-based divergence (available funds vs allocated)
|
|
1478
1466
|
const ratioResult = checkAndUpdateGridIfNeeded(manager);
|
|
1479
1467
|
if (ratioResult.buyUpdated || ratioResult.sellUpdated) {
|
|
@@ -1505,12 +1493,12 @@ async function monitorDivergence(manager, calculatedGrid, persistedGrid) {
|
|
|
1505
1493
|
*/
|
|
1506
1494
|
function _getOnChainOrders(manager) {
|
|
1507
1495
|
const onChainBuys = [
|
|
1508
|
-
...manager.getOrdersByTypeAndState(
|
|
1509
|
-
...manager.getOrdersByTypeAndState(
|
|
1496
|
+
...manager.getOrdersByTypeAndState(ORDER_TYPES.BUY, ORDER_STATES.ACTIVE),
|
|
1497
|
+
...manager.getOrdersByTypeAndState(ORDER_TYPES.BUY, ORDER_STATES.PARTIAL)
|
|
1510
1498
|
].filter((o) => o?.orderId && Number(o?.size || 0) > 0);
|
|
1511
1499
|
const onChainSells = [
|
|
1512
|
-
...manager.getOrdersByTypeAndState(
|
|
1513
|
-
...manager.getOrdersByTypeAndState(
|
|
1500
|
+
...manager.getOrdersByTypeAndState(ORDER_TYPES.SELL, ORDER_STATES.ACTIVE),
|
|
1501
|
+
...manager.getOrdersByTypeAndState(ORDER_TYPES.SELL, ORDER_STATES.PARTIAL)
|
|
1514
1502
|
].filter((o) => o?.orderId && Number(o?.size || 0) > 0);
|
|
1515
1503
|
return { onChainBuys, onChainSells };
|
|
1516
1504
|
}
|
|
@@ -1519,9 +1507,9 @@ function _getOnChainOrders(manager) {
|
|
|
1519
1507
|
* @param {import('./types').OrderManager} manager - The manager instance.
|
|
1520
1508
|
* @returns {number} The calculated spread percentage.
|
|
1521
1509
|
*/
|
|
1522
|
-
function calculateCurrentSpread(manager) {
|
|
1510
|
+
export function calculateCurrentSpread(manager) {
|
|
1523
1511
|
const { onChainBuys, onChainSells } = _getOnChainOrders(manager);
|
|
1524
|
-
return
|
|
1512
|
+
return calculateSpreadFromOrders(onChainBuys, onChainSells);
|
|
1525
1513
|
}
|
|
1526
1514
|
/**
|
|
1527
1515
|
* Proactive spread correction check.
|
|
@@ -1546,7 +1534,7 @@ function calculateCurrentSpread(manager) {
|
|
|
1546
1534
|
* @param {Function|null} [updateOrdersOnChainBatch=null] - Optional batch update function
|
|
1547
1535
|
* @returns {Promise<import('./types').SpreadCheckResult>}
|
|
1548
1536
|
*/
|
|
1549
|
-
async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatch = null) {
|
|
1537
|
+
export async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatch = null) {
|
|
1550
1538
|
// CRITICAL: Acquire corrections lock to serialize spread correction operations
|
|
1551
1539
|
// This prevents concurrent fill processing from modifying funds while we're making decisions
|
|
1552
1540
|
let correction = null;
|
|
@@ -1560,7 +1548,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1560
1548
|
// Mid between best bid and best ask is the most current price the bot has.
|
|
1561
1549
|
// Falls back to config.startPrice when either side is empty (e.g. at startup).
|
|
1562
1550
|
const { onChainBuys, onChainSells } = _getOnChainOrders(manager);
|
|
1563
|
-
const { bestBuy, bestSell } =
|
|
1551
|
+
const { bestBuy, bestSell } = getGridBestPrices(onChainBuys, onChainSells);
|
|
1564
1552
|
const lastPrice = (bestBuy !== null && bestSell !== null)
|
|
1565
1553
|
? (bestBuy + bestSell) / 2
|
|
1566
1554
|
: Number(manager.config.startPrice) || 0;
|
|
@@ -1578,18 +1566,18 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1578
1566
|
const currentSpread = calculateCurrentSpread(manager);
|
|
1579
1567
|
// Nominal spread is the configured target spread percentage.
|
|
1580
1568
|
// Keep this fixed: doubled-side flags are fill/replacement mechanics only.
|
|
1581
|
-
const nominalSpread = manager.config.targetSpreadPercent ??
|
|
1569
|
+
const nominalSpread = manager.config.targetSpreadPercent ?? DEFAULT_CONFIG.targetSpreadPercent;
|
|
1582
1570
|
// Fixed tolerance: 0.5 steps = half increment (tighter spread check).
|
|
1583
1571
|
const toleranceSteps = 0.5;
|
|
1584
|
-
const buyCount = manager.getOrdersByTypeAndState(
|
|
1585
|
-
.concat(manager.getOrdersByTypeAndState(
|
|
1572
|
+
const buyCount = manager.getOrdersByTypeAndState(ORDER_TYPES.BUY, ORDER_STATES.ACTIVE)
|
|
1573
|
+
.concat(manager.getOrdersByTypeAndState(ORDER_TYPES.BUY, ORDER_STATES.PARTIAL))
|
|
1586
1574
|
.filter((o) => o?.orderId && Number(o?.size || 0) > 0)
|
|
1587
1575
|
.length;
|
|
1588
|
-
const sellCount = manager.getOrdersByTypeAndState(
|
|
1589
|
-
.concat(manager.getOrdersByTypeAndState(
|
|
1576
|
+
const sellCount = manager.getOrdersByTypeAndState(ORDER_TYPES.SELL, ORDER_STATES.ACTIVE)
|
|
1577
|
+
.concat(manager.getOrdersByTypeAndState(ORDER_TYPES.SELL, ORDER_STATES.PARTIAL))
|
|
1590
1578
|
.filter((o) => o?.orderId && Number(o?.size || 0) > 0)
|
|
1591
1579
|
.length;
|
|
1592
|
-
manager.outOfSpread =
|
|
1580
|
+
manager.outOfSpread = shouldFlagOutOfSpread(currentSpread, nominalSpread, toleranceSteps, buyCount, sellCount, manager.config.incrementPercent);
|
|
1593
1581
|
if (manager.outOfSpread === 0)
|
|
1594
1582
|
return false;
|
|
1595
1583
|
// Check whether the empty side is caused by boundary-at-rail-edge.
|
|
@@ -1605,7 +1593,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1605
1593
|
const gapSlots = manager._gapSlots ?? calculateGapSlots(manager.config.incrementPercent, manager.config.targetSpreadPercent, manager.config.gridLimits);
|
|
1606
1594
|
const railLen = allSlots.length;
|
|
1607
1595
|
const buyEndIdx = manager.boundaryIdx;
|
|
1608
|
-
const sellStartIdx =
|
|
1596
|
+
const sellStartIdx = getSellStartIdx(manager.boundaryIdx, gapSlots);
|
|
1609
1597
|
const buySideCount = Math.max(0, Math.min(railLen, buyEndIdx + 1));
|
|
1610
1598
|
const sellSideCount = Math.max(0, railLen - sellStartIdx);
|
|
1611
1599
|
// Structural resync: both sides have room but one is empty.
|
|
@@ -1619,7 +1607,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1619
1607
|
`Requesting structural grid resync to re-center.`, 'warn');
|
|
1620
1608
|
if (typeof manager.requestStructuralGridResync === 'function') {
|
|
1621
1609
|
manager.requestStructuralGridResync('boundary-at-rail-edge', { reason: `Boundary ${manager.boundaryIdx} leaves ${buySideCount} buy / ${sellSideCount} sell slots` }).catch((err) => {
|
|
1622
|
-
manager.logger?.log?.(`[SPREAD] Structural resync request failed: ${
|
|
1610
|
+
manager.logger?.log?.(`[SPREAD] Structural resync request failed: ${getErrorMessage(err)}`, 'error');
|
|
1623
1611
|
});
|
|
1624
1612
|
}
|
|
1625
1613
|
}
|
|
@@ -1639,7 +1627,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1639
1627
|
// STARVATION FALLBACK: If the selected side has no correctable slots (e.g.
|
|
1640
1628
|
// all SPREAD slots already filled or misaligned), try the opposite side.
|
|
1641
1629
|
if ((placeCount + updateCount) === 0) {
|
|
1642
|
-
const oppositeSide = decision.side ===
|
|
1630
|
+
const oppositeSide = decision.side === ORDER_TYPES.BUY ? ORDER_TYPES.SELL : ORDER_TYPES.BUY;
|
|
1643
1631
|
manager.logger?.log?.(`[SPREAD] Side ${decision.side} produced zero candidates; ` +
|
|
1644
1632
|
`trying opposite side ${oppositeSide}.`, 'debug');
|
|
1645
1633
|
const oppositeCorrection = await prepareSpreadCorrectionOrders(manager, oppositeSide, manager.outOfSpread);
|
|
@@ -1657,7 +1645,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1657
1645
|
shouldApplyCorrection = await manager._gridLock.acquire(executeSpreadCheck);
|
|
1658
1646
|
}
|
|
1659
1647
|
catch (err) {
|
|
1660
|
-
manager.logger?.log?.(`Error checking spread condition: ${
|
|
1648
|
+
manager.logger?.log?.(`Error checking spread condition: ${getErrorMessage(err)}`, 'error');
|
|
1661
1649
|
// Track failure in recovery state for external monitoring.
|
|
1662
1650
|
// Do NOT throw — the startup runtime path lacks a try/catch and
|
|
1663
1651
|
// a throw would crash startup. The error is symptom of a deeper
|
|
@@ -1688,7 +1676,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1688
1676
|
// Refresh lastPrice from current grid state — the grid may
|
|
1689
1677
|
// have changed since function entry (TOCTOU).
|
|
1690
1678
|
const freshOnChain = _getOnChainOrders(manager);
|
|
1691
|
-
const freshBest =
|
|
1679
|
+
const freshBest = getGridBestPrices(freshOnChain.onChainBuys, freshOnChain.onChainSells);
|
|
1692
1680
|
const freshPrice = (freshBest.bestBuy !== null && freshBest.bestSell !== null)
|
|
1693
1681
|
? (freshBest.bestBuy + freshBest.bestSell) / 2
|
|
1694
1682
|
: Number(manager.config.startPrice) || 0;
|
|
@@ -1722,7 +1710,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1722
1710
|
return { ordersPlaced: placed + updated, partialsMoved: updated };
|
|
1723
1711
|
}
|
|
1724
1712
|
catch (err) {
|
|
1725
|
-
manager.logger?.log?.(`Error applying spread correction on-chain: ${
|
|
1713
|
+
manager.logger?.log?.(`Error applying spread correction on-chain: ${getErrorMessage(err)}`, 'warn');
|
|
1726
1714
|
return { ordersPlaced: 0, partialsMoved: 0 };
|
|
1727
1715
|
}
|
|
1728
1716
|
}
|
|
@@ -1741,7 +1729,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1741
1729
|
* @param {Function|null} [updateOrdersOnChainBatch=null] - Optional batch update function.
|
|
1742
1730
|
* @returns {Promise<import('./types').DustCheckResult>}
|
|
1743
1731
|
*/
|
|
1744
|
-
async function checkGridHealth(manager, _updateOrdersOnChainBatch = null) {
|
|
1732
|
+
export async function checkGridHealth(manager, _updateOrdersOnChainBatch = null) {
|
|
1745
1733
|
if (!manager)
|
|
1746
1734
|
return { buyDust: false, sellDust: false, buyDustOrders: [], sellDustOrders: [] };
|
|
1747
1735
|
// Skip health checks during bootstrap to prevent spamming warnings
|
|
@@ -1767,25 +1755,25 @@ async function checkGridHealth(manager, _updateOrdersOnChainBatch = null) {
|
|
|
1767
1755
|
* the sibling active order already covers that price level.
|
|
1768
1756
|
*
|
|
1769
1757
|
* Returns boolean flags plus the actual dust order objects so callers can act
|
|
1770
|
-
* on individual orders (
|
|
1758
|
+
* on individual orders (dust is cancelled immediately on detection).
|
|
1771
1759
|
*
|
|
1772
1760
|
* @param {import('./types').OrderManager} manager
|
|
1773
1761
|
* @returns {Promise<import('./types').DustCheckResult>}
|
|
1774
1762
|
*/
|
|
1775
|
-
async function checkWindowDust(manager) {
|
|
1763
|
+
export async function checkWindowDust(manager) {
|
|
1776
1764
|
if (!manager)
|
|
1777
1765
|
return { buyDust: false, sellDust: false, buyDustOrders: [], sellDustOrders: [] };
|
|
1778
1766
|
const allOrders = Array.from(manager.orders.values());
|
|
1779
1767
|
const isLiveOrder = (order) => order &&
|
|
1780
1768
|
order.orderId &&
|
|
1781
1769
|
order.price != null &&
|
|
1782
|
-
(order.state ===
|
|
1770
|
+
(order.state === ORDER_STATES.ACTIVE || order.state === ORDER_STATES.PARTIAL);
|
|
1783
1771
|
// Identify top-of-window orders (closest to market per side).
|
|
1784
1772
|
const topBuyOrder = allOrders
|
|
1785
|
-
.filter((o) => o.type ===
|
|
1773
|
+
.filter((o) => o.type === ORDER_TYPES.BUY && isLiveOrder(o))
|
|
1786
1774
|
.sort((a, b) => b.price - a.price)[0];
|
|
1787
1775
|
const topSellOrder = allOrders
|
|
1788
|
-
.filter((o) => o.type ===
|
|
1776
|
+
.filter((o) => o.type === ORDER_TYPES.SELL && isLiveOrder(o))
|
|
1789
1777
|
.sort((a, b) => a.price - b.price)[0];
|
|
1790
1778
|
// Check if an order has a duplicate price level — an active sibling at the
|
|
1791
1779
|
// same price within tolerance. If so, cancelling won't create a grid gap.
|
|
@@ -1793,17 +1781,17 @@ async function checkWindowDust(manager) {
|
|
|
1793
1781
|
// sibling, neither qualifies and the gap is left to the rebalancer.
|
|
1794
1782
|
// Uses the LARGER size of the two orders for tolerance calculation to prevent
|
|
1795
1783
|
// a tiny dust order from inflating the tolerance window.
|
|
1796
|
-
const hasDuplicatePriceLevel = (order, assets) =>
|
|
1784
|
+
const hasDuplicatePriceLevel = (order, assets) => findPriceCollision(allOrders, order.id, order.price, order.size, order.type, assets, (o) => o.type === order.type && o.state === ORDER_STATES.ACTIVE && !!o.orderId && o.price != null) != null;
|
|
1797
1785
|
const assets = manager.assets;
|
|
1798
|
-
const allPartials = allOrders.filter((o) => isLiveOrder(o) && o.state ===
|
|
1786
|
+
const allPartials = allOrders.filter((o) => isLiveOrder(o) && o.state === ORDER_STATES.PARTIAL);
|
|
1799
1787
|
const isTopBuy = (o) => topBuyOrder && o.id === topBuyOrder.id;
|
|
1800
1788
|
const isTopSell = (o) => topSellOrder && o.id === topSellOrder.id;
|
|
1801
1789
|
// Safety filter: top-of-window partials always qualify; interior partials
|
|
1802
1790
|
// only qualify if they have a duplicate price level (no gap risk).
|
|
1803
|
-
const eligibleBuyPartials = allPartials.filter((o) => o.type ===
|
|
1804
|
-
const eligibleSellPartials = allPartials.filter((o) => o.type ===
|
|
1805
|
-
const buyDustOrders = await _getDustOrders(manager, eligibleBuyPartials,
|
|
1806
|
-
const sellDustOrders = await _getDustOrders(manager, eligibleSellPartials,
|
|
1791
|
+
const eligibleBuyPartials = allPartials.filter((o) => o.type === ORDER_TYPES.BUY && (isTopBuy(o) || hasDuplicatePriceLevel(o, assets)));
|
|
1792
|
+
const eligibleSellPartials = allPartials.filter((o) => o.type === ORDER_TYPES.SELL && (isTopSell(o) || hasDuplicatePriceLevel(o, assets)));
|
|
1793
|
+
const buyDustOrders = await _getDustOrders(manager, eligibleBuyPartials, ORDER_TYPES.BUY);
|
|
1794
|
+
const sellDustOrders = await _getDustOrders(manager, eligibleSellPartials, ORDER_TYPES.SELL);
|
|
1807
1795
|
return {
|
|
1808
1796
|
buyDust: buyDustOrders.length > 0,
|
|
1809
1797
|
sellDust: sellDustOrders.length > 0,
|
|
@@ -1824,7 +1812,7 @@ async function checkWindowDust(manager) {
|
|
|
1824
1812
|
async function _getDustOrders(manager, partials, type) {
|
|
1825
1813
|
if (!partials || partials.length === 0)
|
|
1826
1814
|
return [];
|
|
1827
|
-
const side = type ===
|
|
1815
|
+
const side = type === ORDER_TYPES.BUY ? 'buy' : 'sell';
|
|
1828
1816
|
const ctx = await _getSizingContext(manager, side);
|
|
1829
1817
|
const dustThresholdPercent = manager.config?.gridLimits?.PARTIAL_DUST_THRESHOLD_PERCENTAGE;
|
|
1830
1818
|
const sideSlots = Array.from(manager.orders.values())
|
|
@@ -1833,7 +1821,7 @@ async function _getDustOrders(manager, partials, type) {
|
|
|
1833
1821
|
if (sideSlots.length === 0)
|
|
1834
1822
|
return [];
|
|
1835
1823
|
const idealSizes = ctx && ctx.budget > 0
|
|
1836
|
-
?
|
|
1824
|
+
? allocateFundsByWeights(ctx.budget, sideSlots.length, manager.config.weightDistribution[side], manager.config.incrementPercent / 100, type === ORDER_TYPES.BUY, 0, ctx.precision)
|
|
1837
1825
|
: [];
|
|
1838
1826
|
// When no budget is available, idealSizes becomes [] so every
|
|
1839
1827
|
// partial's threshold collapses to 0 — no order qualifies as dust.
|
|
@@ -1844,7 +1832,7 @@ async function _getDustOrders(manager, partials, type) {
|
|
|
1844
1832
|
if (idx === -1)
|
|
1845
1833
|
return false;
|
|
1846
1834
|
const threshold = idealSizes.length > idx && idealSizes[idx] > 0
|
|
1847
|
-
?
|
|
1835
|
+
? getSingleDustThreshold(idealSizes[idx], dustThresholdPercent)
|
|
1848
1836
|
: 0;
|
|
1849
1837
|
return p.size < threshold;
|
|
1850
1838
|
});
|
|
@@ -1867,8 +1855,8 @@ async function _hasAnyDust(manager, partials, type) {
|
|
|
1867
1855
|
* @param {'buy'|'sell'} side
|
|
1868
1856
|
* @returns {Promise<boolean>}
|
|
1869
1857
|
*/
|
|
1870
|
-
async function hasAnyDust(manager, partials, side) {
|
|
1871
|
-
const type = side === 'buy' ?
|
|
1858
|
+
export async function hasAnyDust(manager, partials, side) {
|
|
1859
|
+
const type = side === 'buy' ? ORDER_TYPES.BUY : side === 'sell' ? ORDER_TYPES.SELL : null;
|
|
1872
1860
|
if (!type)
|
|
1873
1861
|
return false;
|
|
1874
1862
|
return await _hasAnyDust(manager, partials, type);
|
|
@@ -1881,8 +1869,8 @@ async function hasAnyDust(manager, partials, side) {
|
|
|
1881
1869
|
* @param {'buy'|'sell'} side
|
|
1882
1870
|
* @returns {Promise<Array<import('./types').GridOrderSlot>>}
|
|
1883
1871
|
*/
|
|
1884
|
-
async function getDustOrders(manager, partials, side) {
|
|
1885
|
-
const type = side === 'buy' ?
|
|
1872
|
+
export async function getDustOrders(manager, partials, side) {
|
|
1873
|
+
const type = side === 'buy' ? ORDER_TYPES.BUY : side === 'sell' ? ORDER_TYPES.SELL : null;
|
|
1886
1874
|
if (!type)
|
|
1887
1875
|
return [];
|
|
1888
1876
|
return await _getDustOrders(manager, partials, type);
|
|
@@ -1894,7 +1882,7 @@ async function getDustOrders(manager, partials, side) {
|
|
|
1894
1882
|
* normalize sell-side funds into buy-side units for a fair cross-asset comparison.
|
|
1895
1883
|
* @returns {{ side: import('./types').OrderType|null, reason: string }} The side to correct on, or null if insufficient funds.
|
|
1896
1884
|
*/
|
|
1897
|
-
function determineOrderSideByFunds(manager, currentMarketPrice) {
|
|
1885
|
+
export function determineOrderSideByFunds(manager, currentMarketPrice) {
|
|
1898
1886
|
const buyAvailable = Math.min(Number(manager.funds?.available?.buy || 0), Number(manager.accountTotals?.buyFree || 0));
|
|
1899
1887
|
const sellAvailable = Math.min(Number(manager.funds?.available?.sell || 0), Number(manager.accountTotals?.sellFree || 0));
|
|
1900
1888
|
// Need at least some funds on a side to justify correction
|
|
@@ -1916,13 +1904,13 @@ function determineOrderSideByFunds(manager, currentMarketPrice) {
|
|
|
1916
1904
|
const sellInBuyUnits = (Number.isFinite(marketPrice) && marketPrice > 0)
|
|
1917
1905
|
? sellAvailable * marketPrice
|
|
1918
1906
|
: sellAvailable;
|
|
1919
|
-
side = buyAvailable >= sellInBuyUnits ?
|
|
1907
|
+
side = buyAvailable >= sellInBuyUnits ? ORDER_TYPES.BUY : ORDER_TYPES.SELL;
|
|
1920
1908
|
}
|
|
1921
1909
|
else if (buyViable) {
|
|
1922
|
-
side =
|
|
1910
|
+
side = ORDER_TYPES.BUY;
|
|
1923
1911
|
}
|
|
1924
1912
|
else if (sellViable) {
|
|
1925
|
-
side =
|
|
1913
|
+
side = ORDER_TYPES.SELL;
|
|
1926
1914
|
}
|
|
1927
1915
|
if (!side) {
|
|
1928
1916
|
const committedBuy = Math.max(0, Number(manager.funds?.committed?.chain?.buy || 0));
|
|
@@ -1933,17 +1921,17 @@ function determineOrderSideByFunds(manager, currentMarketPrice) {
|
|
|
1933
1921
|
if (hasValidPrice) {
|
|
1934
1922
|
const buyComparable = committedBuy;
|
|
1935
1923
|
const sellComparable = committedSell * marketPrice;
|
|
1936
|
-
side = buyComparable >= sellComparable ?
|
|
1924
|
+
side = buyComparable >= sellComparable ? ORDER_TYPES.BUY : ORDER_TYPES.SELL;
|
|
1937
1925
|
}
|
|
1938
1926
|
else if (committedBuy > buyMinUnit && committedSell <= sellMinUnit) {
|
|
1939
|
-
side =
|
|
1927
|
+
side = ORDER_TYPES.BUY;
|
|
1940
1928
|
}
|
|
1941
1929
|
else if (committedSell > sellMinUnit && committedBuy <= buyMinUnit) {
|
|
1942
|
-
side =
|
|
1930
|
+
side = ORDER_TYPES.SELL;
|
|
1943
1931
|
}
|
|
1944
1932
|
else {
|
|
1945
1933
|
// Deterministic fallback when both sides hold inventory but market valuation is unavailable.
|
|
1946
|
-
side =
|
|
1934
|
+
side = ORDER_TYPES.BUY;
|
|
1947
1935
|
}
|
|
1948
1936
|
manager.logger?.log?.(`Spread correction using redistribution fallback on ${side} ` +
|
|
1949
1937
|
`(free buy=${Format.formatAmount8(buyAvailable)}, free sell=${Format.formatAmount8(sellAvailable)}, ` +
|
|
@@ -1970,7 +1958,7 @@ function determineOrderSideByFunds(manager, currentMarketPrice) {
|
|
|
1970
1958
|
// the band, so this cap only binds when the opposite rail is smaller than the
|
|
1971
1959
|
// band — i.e. an empty opposite rail (cap 0) blocks promotion entirely.
|
|
1972
1960
|
function _collectPromotableBoundarySlots(allSlotsByPrice, railType, buyEndIdx, sellStartIdx, quota) {
|
|
1973
|
-
const isBuy = railType ===
|
|
1961
|
+
const isBuy = railType === ORDER_TYPES.BUY;
|
|
1974
1962
|
const maxIdx = allSlotsByPrice.length - 1;
|
|
1975
1963
|
const maxPromotable = isBuy
|
|
1976
1964
|
? Math.max(0, maxIdx + 1 - sellStartIdx)
|
|
@@ -1980,7 +1968,7 @@ function _collectPromotableBoundarySlots(allSlotsByPrice, railType, buyEndIdx, s
|
|
|
1980
1968
|
const step = isBuy ? 1 : -1;
|
|
1981
1969
|
for (let idx = isBuy ? buyEndIdx + 1 : sellStartIdx - 1; promoted.length < promotionQuota && (isBuy ? idx < sellStartIdx : idx > buyEndIdx); idx += step) {
|
|
1982
1970
|
const slot = allSlotsByPrice[idx];
|
|
1983
|
-
if (!slot || !
|
|
1971
|
+
if (!slot || !isSlotAvailable(slot))
|
|
1984
1972
|
break;
|
|
1985
1973
|
promoted.push(slot);
|
|
1986
1974
|
}
|
|
@@ -1993,15 +1981,15 @@ function _collectPromotableBoundarySlots(allSlotsByPrice, railType, buyEndIdx, s
|
|
|
1993
1981
|
* @returns {Promise<import('./types').SpreadCorrectionResult>}
|
|
1994
1982
|
* @throws {Error} If preferredSide is invalid.
|
|
1995
1983
|
*/
|
|
1996
|
-
async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread = 0) {
|
|
1984
|
+
export async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread = 0) {
|
|
1997
1985
|
// FIX: Validate preferredSide parameter to prevent silent logic errors
|
|
1998
|
-
if (preferredSide !==
|
|
1999
|
-
throw new Error(`Invalid preferredSide: ${preferredSide}. Must be '${
|
|
1986
|
+
if (preferredSide !== ORDER_TYPES.BUY && preferredSide !== ORDER_TYPES.SELL) {
|
|
1987
|
+
throw new Error(`Invalid preferredSide: ${preferredSide}. Must be '${ORDER_TYPES.BUY}' or '${ORDER_TYPES.SELL}'.`);
|
|
2000
1988
|
}
|
|
2001
1989
|
const ordersToPlace = [];
|
|
2002
1990
|
const ordersToUpdate = [];
|
|
2003
1991
|
const railType = preferredSide;
|
|
2004
|
-
const sideName = railType ===
|
|
1992
|
+
const sideName = railType === ORDER_TYPES.BUY ? 'buy' : 'sell';
|
|
2005
1993
|
const configuredMissingSlots = Number(outOfSpread || 0);
|
|
2006
1994
|
const missingSlots = configuredMissingSlots > 0
|
|
2007
1995
|
? Math.floor(configuredMissingSlots)
|
|
@@ -2030,7 +2018,7 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread
|
|
|
2030
2018
|
// spread correction cycle will re-classify with the updated committed value.
|
|
2031
2019
|
// This prevents TOCTOU-style inconsistency where slot types are chosen
|
|
2032
2020
|
// against a boundary that was never atomically committed to manager.orders.
|
|
2033
|
-
const resolved =
|
|
2021
|
+
const resolved = resolveGapBand(manager);
|
|
2034
2022
|
const gapSlots = resolved.gapSlots;
|
|
2035
2023
|
const boundaryKnown = resolved.boundaryIdx !== null && resolved.sellStartIdx !== null;
|
|
2036
2024
|
// NOTE: `?? 0` keeps the legacy classification fallback (boundary not
|
|
@@ -2039,22 +2027,22 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread
|
|
|
2039
2027
|
// `boundaryKnown` below: deriving a new boundary from a fabricated 0 would
|
|
2040
2028
|
// silently commit a boundary that was never real.
|
|
2041
2029
|
const buyEndIdx = resolved.boundaryIdx ?? 0;
|
|
2042
|
-
const sellStartIdx = resolved.sellStartIdx ??
|
|
2030
|
+
const sellStartIdx = resolved.sellStartIdx ?? getSellStartIdx(buyEndIdx, gapSlots);
|
|
2043
2031
|
const getSlotCorrectType = (slot) => {
|
|
2044
2032
|
const idx = slotIndexMap.get(slot.id);
|
|
2045
2033
|
if (idx === undefined)
|
|
2046
2034
|
return slot.type;
|
|
2047
2035
|
if (idx <= buyEndIdx)
|
|
2048
|
-
return
|
|
2036
|
+
return ORDER_TYPES.BUY;
|
|
2049
2037
|
if (idx >= sellStartIdx)
|
|
2050
|
-
return
|
|
2051
|
-
return
|
|
2038
|
+
return ORDER_TYPES.SELL;
|
|
2039
|
+
return ORDER_TYPES.SPREAD;
|
|
2052
2040
|
};
|
|
2053
2041
|
let edgePartial = null;
|
|
2054
2042
|
const partials = allOrders
|
|
2055
2043
|
.filter((o) => getSlotCorrectType(o) === railType
|
|
2056
|
-
&& o.state ===
|
|
2057
|
-
.sort((a, b) => railType ===
|
|
2044
|
+
&& o.state === ORDER_STATES.PARTIAL)
|
|
2045
|
+
.sort((a, b) => railType === ORDER_TYPES.BUY ? b.price - a.price : a.price - b.price);
|
|
2058
2046
|
if (partials.length > 0) {
|
|
2059
2047
|
edgePartial = partials[0];
|
|
2060
2048
|
manager.logger?.log?.(`[SPREAD-CORRECTION] Identified partial order at ${edgePartial.price} for update`, 'debug');
|
|
@@ -2064,10 +2052,10 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread
|
|
|
2064
2052
|
// in the BUY or SELL zone is excluded — it would otherwise be placed on the
|
|
2065
2053
|
// correction side at a price the grid already considers the opposite side.
|
|
2066
2054
|
const typedSpreadCandidates = allOrders
|
|
2067
|
-
.filter((o) => o.type ===
|
|
2068
|
-
&&
|
|
2055
|
+
.filter((o) => o.type === ORDER_TYPES.SPREAD
|
|
2056
|
+
&& isSlotAvailable(o)
|
|
2069
2057
|
&& getSlotCorrectType(o) === railType)
|
|
2070
|
-
.sort((a, b) => railType ===
|
|
2058
|
+
.sort((a, b) => railType === ORDER_TYPES.BUY ? a.price - b.price : b.price - a.price)
|
|
2071
2059
|
.slice(0, missingSlots);
|
|
2072
2060
|
// Secondary candidates: orphaned virtual slots that have lost their
|
|
2073
2061
|
// order (e.g. stale-cleaned after a race condition during a crash).
|
|
@@ -2082,12 +2070,12 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread
|
|
|
2082
2070
|
// NOT re-activated on the stale side — doing so would compound inventory
|
|
2083
2071
|
// at prices where the bot already traded.
|
|
2084
2072
|
const orphanedVirtualCandidates = allOrders
|
|
2085
|
-
.filter((o) => (o.type === railType || o.type ===
|
|
2086
|
-
&& o.state ===
|
|
2073
|
+
.filter((o) => (o.type === railType || o.type === ORDER_TYPES.SPREAD)
|
|
2074
|
+
&& o.state === ORDER_STATES.VIRTUAL
|
|
2087
2075
|
&& !o.orderId
|
|
2088
2076
|
&& Number(o.size || 0) === 0
|
|
2089
2077
|
&& getSlotCorrectType(o) === railType)
|
|
2090
|
-
.sort((a, b) => railType ===
|
|
2078
|
+
.sort((a, b) => railType === ORDER_TYPES.BUY ? b.price - a.price : a.price - b.price)
|
|
2091
2079
|
.slice(0, missingSlots);
|
|
2092
2080
|
// If the funded rail is full, the spread itself may be stale: the
|
|
2093
2081
|
// nearest empty slots are still in the gap band. Promote contiguous
|
|
@@ -2141,8 +2129,8 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread
|
|
|
2141
2129
|
return false;
|
|
2142
2130
|
// Resolve candidate size: if zero/missing, use minimum so tolerance
|
|
2143
2131
|
// doesn't collapse to zero (calculatePriceTolerance returns null for size <= 0).
|
|
2144
|
-
const cs = (c.size && c.size > 0) ? c.size :
|
|
2145
|
-
return !
|
|
2132
|
+
const cs = (c.size && c.size > 0) ? c.size : getMinAbsoluteOrderSize(railType, manager.assets);
|
|
2133
|
+
return !findPriceCollision(allOrders, c.id, c.price, cs, railType, manager.assets, (o) => isOrderPlaced(o) && o.price != null);
|
|
2146
2134
|
});
|
|
2147
2135
|
const filteredCount = preFilter - spreadCandidates.length;
|
|
2148
2136
|
if (filteredCount > 0) {
|
|
@@ -2168,14 +2156,14 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread
|
|
|
2168
2156
|
if (!ctx || ctx.budget <= 0 || syntheticSideSlots.length === 0) {
|
|
2169
2157
|
return { ordersToPlace: [], ordersToUpdate: [] };
|
|
2170
2158
|
}
|
|
2171
|
-
const precisionEpsilon =
|
|
2172
|
-
const idealSizes =
|
|
2159
|
+
const precisionEpsilon = getPrecisionSlack(ctx.precision, 1);
|
|
2160
|
+
const idealSizes = allocateFundsByWeights(ctx.budget, syntheticSideSlots.length, manager.config.weightDistribution[sideName], manager.config.incrementPercent / 100, railType === ORDER_TYPES.BUY, 0, ctx.precision);
|
|
2173
2161
|
const idealById = new Map();
|
|
2174
2162
|
syntheticSideSlots.forEach((slot, idx) => {
|
|
2175
2163
|
idealById.set(slot.id, Number(idealSizes[idx] || 0));
|
|
2176
2164
|
});
|
|
2177
2165
|
const availableFund = Math.max(0, Math.min(Number(manager.funds?.available?.[sideName] || 0), Number(sideName === 'buy' ? manager.accountTotals?.buyFree : manager.accountTotals?.sellFree) || 0));
|
|
2178
|
-
const minAbsoluteSize =
|
|
2166
|
+
const minAbsoluteSize = getMinAbsoluteOrderSize(railType, manager.assets);
|
|
2179
2167
|
const prioritizedTargets = [];
|
|
2180
2168
|
if (edgePartial && edgePartial.id) {
|
|
2181
2169
|
const ideal = Number(idealById.get(edgePartial.id) || 0);
|
|
@@ -2211,9 +2199,9 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread
|
|
|
2211
2199
|
if (totalNeeded > availableFund + precisionEpsilon) {
|
|
2212
2200
|
let shortfall = totalNeeded - availableFund;
|
|
2213
2201
|
const donors = sideSlots
|
|
2214
|
-
.filter((o) =>
|
|
2202
|
+
.filter((o) => hasOnChainId(o) && (o.state === ORDER_STATES.ACTIVE || o.state === ORDER_STATES.PARTIAL))
|
|
2215
2203
|
.filter((o) => !edgePartial || o.id !== edgePartial.id)
|
|
2216
|
-
.sort((a, b) => railType ===
|
|
2204
|
+
.sort((a, b) => railType === ORDER_TYPES.BUY ? a.price - b.price : b.price - a.price);
|
|
2217
2205
|
for (const donor of donors) {
|
|
2218
2206
|
if (shortfall <= precisionEpsilon)
|
|
2219
2207
|
break;
|
|
@@ -2227,7 +2215,7 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread
|
|
|
2227
2215
|
const donorNext = donorCurrent - reduction;
|
|
2228
2216
|
if (donorNext <= precisionEpsilon)
|
|
2229
2217
|
continue;
|
|
2230
|
-
if (!
|
|
2218
|
+
if (!isOrderHealthy(donorNext, railType, manager.assets, donorIdeal || donorNext))
|
|
2231
2219
|
continue;
|
|
2232
2220
|
redistributionUpdates.push({ partialOrder: { ...donor }, newSize: donorNext });
|
|
2233
2221
|
recoveredBudget += reduction;
|
|
@@ -2244,7 +2232,7 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread
|
|
|
2244
2232
|
if (target.kind === 'partial-topup') {
|
|
2245
2233
|
const topUp = Math.min(target.needed, remainingBudget);
|
|
2246
2234
|
const newSize = target.current + topUp;
|
|
2247
|
-
if (newSize > target.current + precisionEpsilon &&
|
|
2235
|
+
if (newSize > target.current + precisionEpsilon && isOrderHealthy(newSize, railType, manager.assets, target.ideal)) {
|
|
2248
2236
|
ordersToUpdate.push({ partialOrder: { ...target.order }, newSize });
|
|
2249
2237
|
remainingBudget -= topUp;
|
|
2250
2238
|
}
|
|
@@ -2253,13 +2241,13 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread
|
|
|
2253
2241
|
const createSize = Math.min(target.ideal, remainingBudget);
|
|
2254
2242
|
if (createSize <= precisionEpsilon)
|
|
2255
2243
|
continue;
|
|
2256
|
-
if (!
|
|
2244
|
+
if (!isOrderHealthy(createSize, railType, manager.assets, target.ideal))
|
|
2257
2245
|
continue;
|
|
2258
2246
|
ordersToPlace.push({
|
|
2259
2247
|
...target.order,
|
|
2260
2248
|
type: railType,
|
|
2261
2249
|
size: createSize,
|
|
2262
|
-
state:
|
|
2250
|
+
state: ORDER_STATES.VIRTUAL
|
|
2263
2251
|
});
|
|
2264
2252
|
remainingBudget -= createSize;
|
|
2265
2253
|
}
|
|
@@ -2276,14 +2264,14 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread
|
|
|
2276
2264
|
const idx = slotIndexMap.get(id);
|
|
2277
2265
|
if (idx === undefined)
|
|
2278
2266
|
continue;
|
|
2279
|
-
const dist = railType ===
|
|
2267
|
+
const dist = railType === ORDER_TYPES.BUY
|
|
2280
2268
|
? idx - buyEndIdx
|
|
2281
2269
|
: sellStartIdx - idx;
|
|
2282
2270
|
if (dist > maxDist)
|
|
2283
2271
|
maxDist = dist;
|
|
2284
2272
|
}
|
|
2285
2273
|
if (maxDist > 0) {
|
|
2286
|
-
boundaryIdx = railType ===
|
|
2274
|
+
boundaryIdx = railType === ORDER_TYPES.BUY
|
|
2287
2275
|
? buyEndIdx + maxDist
|
|
2288
2276
|
: buyEndIdx - maxDist;
|
|
2289
2277
|
const maxIdx = allSlotsByPrice.length - 1;
|