@reefclaw/connect 0.1.12 → 0.1.14
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/assets/plugin/audit/mode-transition-audit.d.ts +11 -0
- package/assets/plugin/audit/mode-transition-audit.js +29 -0
- package/assets/plugin/balance-utils.d.ts +36 -0
- package/assets/plugin/balance-utils.js +98 -0
- package/assets/plugin/ccxt/binance-ban-gate.d.ts +47 -0
- package/assets/plugin/ccxt/binance-ban-gate.js +420 -0
- package/assets/plugin/ccxt/binance-private.d.ts +346 -0
- package/assets/plugin/ccxt/binance-private.js +1525 -0
- package/assets/plugin/ccxt/binance-public.d.ts +34 -0
- package/assets/plugin/ccxt/binance-public.js +180 -0
- package/assets/plugin/ccxt/intel-public.d.ts +25 -0
- package/assets/plugin/ccxt/intel-public.js +80 -0
- package/assets/plugin/ccxt/public-market-data-api.d.ts +26 -0
- package/assets/plugin/ccxt/public-market-data-api.js +23 -0
- package/assets/plugin/config/agent-config-client.d.ts +77 -0
- package/assets/plugin/config/agent-config-client.js +188 -0
- package/assets/plugin/config/agent-config-poller.d.ts +32 -0
- package/assets/plugin/config/agent-config-poller.js +101 -0
- package/assets/plugin/config/brackets-config.d.ts +22 -0
- package/assets/plugin/config/brackets-config.js +58 -0
- package/assets/plugin/config/entitlement-gate.d.ts +51 -0
- package/assets/plugin/config/entitlement-gate.js +137 -0
- package/assets/plugin/config/gate-store.d.ts +18 -0
- package/assets/plugin/config/gate-store.js +61 -0
- package/assets/plugin/config/plugin-config-io.d.ts +222 -0
- package/assets/plugin/config/plugin-config-io.js +126 -0
- package/assets/plugin/config/position-review-config.d.ts +35 -0
- package/assets/plugin/config/position-review-config.js +105 -0
- package/assets/plugin/config/tool-gate.d.ts +53 -0
- package/assets/plugin/config/tool-gate.js +128 -0
- package/assets/plugin/config/user-data-stream-config.d.ts +89 -0
- package/assets/plugin/config/user-data-stream-config.js +239 -0
- package/assets/plugin/connector-supervisor.d.ts +36 -0
- package/assets/plugin/connector-supervisor.js +149 -0
- package/assets/plugin/exchange-adapter.d.ts +65 -0
- package/assets/plugin/exchange-adapter.js +4 -0
- package/assets/plugin/index.d.ts +30 -0
- package/assets/plugin/index.js +2763 -0
- package/assets/plugin/ingest/pending-entry-metadata.d.ts +74 -0
- package/assets/plugin/ingest/pending-entry-metadata.js +236 -0
- package/assets/plugin/ingest/position-auto-capture.d.ts +171 -0
- package/assets/plugin/ingest/position-auto-capture.js +709 -0
- package/assets/plugin/ingest/position-decisions-client.d.ts +327 -0
- package/assets/plugin/ingest/position-decisions-client.js +306 -0
- package/assets/plugin/ingest/readiness-reporter.d.ts +42 -0
- package/assets/plugin/ingest/readiness-reporter.js +163 -0
- package/assets/plugin/ingest/reconcile-db-vs-exchange.d.ts +51 -0
- package/assets/plugin/ingest/reconcile-db-vs-exchange.js +170 -0
- package/assets/plugin/ingest/reconciler-cleanup.d.ts +37 -0
- package/assets/plugin/ingest/reconciler-cleanup.js +147 -0
- package/assets/plugin/ingest/rest-gap-filler.d.ts +191 -0
- package/assets/plugin/ingest/rest-gap-filler.js +565 -0
- package/assets/plugin/ingest/touched-symbols-store.d.ts +25 -0
- package/assets/plugin/ingest/touched-symbols-store.js +96 -0
- package/assets/plugin/ingest/trade-store-client.d.ts +40 -0
- package/assets/plugin/ingest/trade-store-client.js +125 -0
- package/assets/plugin/ingest/ws-ingest.d.ts +43 -0
- package/assets/plugin/ingest/ws-ingest.js +126 -0
- package/assets/plugin/learning/setup-family.d.ts +21 -0
- package/assets/plugin/learning/setup-family.js +103 -0
- package/assets/plugin/lifecycle/install-signal-handlers.d.ts +33 -0
- package/assets/plugin/lifecycle/install-signal-handlers.js +112 -0
- package/assets/plugin/lifecycle/shutdown-coordinator.d.ts +43 -0
- package/assets/plugin/lifecycle/shutdown-coordinator.js +131 -0
- package/assets/plugin/lifecycle/trading-operation-lock.d.ts +17 -0
- package/assets/plugin/lifecycle/trading-operation-lock.js +14 -0
- package/assets/plugin/live/bracket-id.d.ts +26 -0
- package/assets/plugin/live/bracket-id.js +112 -0
- package/assets/plugin/live/bracket-ledger.d.ts +54 -0
- package/assets/plugin/live/bracket-ledger.js +267 -0
- package/assets/plugin/live/bracket-manager.d.ts +82 -0
- package/assets/plugin/live/bracket-manager.js +478 -0
- package/assets/plugin/live/bracket-params.d.ts +22 -0
- package/assets/plugin/live/bracket-params.js +124 -0
- package/assets/plugin/live/bracket-reconciler.d.ts +95 -0
- package/assets/plugin/live/bracket-reconciler.js +573 -0
- package/assets/plugin/live/bracket-types.d.ts +102 -0
- package/assets/plugin/live/bracket-types.js +8 -0
- package/assets/plugin/live/deposit-tracker.d.ts +62 -0
- package/assets/plugin/live/deposit-tracker.js +97 -0
- package/assets/plugin/live/emergency-controls.d.ts +32 -0
- package/assets/plugin/live/emergency-controls.js +226 -0
- package/assets/plugin/live/exchange-errors.d.ts +12 -0
- package/assets/plugin/live/exchange-errors.js +130 -0
- package/assets/plugin/live/exchange-info-cache.d.ts +37 -0
- package/assets/plugin/live/exchange-info-cache.js +134 -0
- package/assets/plugin/live/fact-subscriber.d.ts +78 -0
- package/assets/plugin/live/fact-subscriber.js +182 -0
- package/assets/plugin/live/fill-price.d.ts +13 -0
- package/assets/plugin/live/fill-price.js +37 -0
- package/assets/plugin/live/intent-journal.d.ts +42 -0
- package/assets/plugin/live/intent-journal.js +122 -0
- package/assets/plugin/live/listen-key-manager.d.ts +70 -0
- package/assets/plugin/live/listen-key-manager.js +169 -0
- package/assets/plugin/live/live-adapter.d.ts +319 -0
- package/assets/plugin/live/live-adapter.js +1906 -0
- package/assets/plugin/live/live-balance-enricher.d.ts +32 -0
- package/assets/plugin/live/live-balance-enricher.js +104 -0
- package/assets/plugin/live/live-bracket-api.d.ts +13 -0
- package/assets/plugin/live/live-bracket-api.js +20 -0
- package/assets/plugin/live/live-state-store.d.ts +194 -0
- package/assets/plugin/live/live-state-store.js +450 -0
- package/assets/plugin/live/local-signal-service.d.ts +57 -0
- package/assets/plugin/live/local-signal-service.js +151 -0
- package/assets/plugin/live/local-strategy-evaluator.d.ts +62 -0
- package/assets/plugin/live/local-strategy-evaluator.js +131 -0
- package/assets/plugin/live/microstructure-assembler.d.ts +54 -0
- package/assets/plugin/live/microstructure-assembler.js +148 -0
- package/assets/plugin/live/order-poller.d.ts +29 -0
- package/assets/plugin/live/order-poller.js +125 -0
- package/assets/plugin/live/position-state-store.d.ts +87 -0
- package/assets/plugin/live/position-state-store.js +237 -0
- package/assets/plugin/live/proposal-decision-listener.d.ts +70 -0
- package/assets/plugin/live/proposal-decision-listener.js +292 -0
- package/assets/plugin/live/proposal-manager.d.ts +76 -0
- package/assets/plugin/live/proposal-manager.js +140 -0
- package/assets/plugin/live/rate-limiter.d.ts +47 -0
- package/assets/plugin/live/rate-limiter.js +159 -0
- package/assets/plugin/live/reconciler.d.ts +39 -0
- package/assets/plugin/live/reconciler.js +175 -0
- package/assets/plugin/live/setup-buckets.d.ts +7 -0
- package/assets/plugin/live/setup-buckets.js +33 -0
- package/assets/plugin/live/slippage-tracker.d.ts +45 -0
- package/assets/plugin/live/slippage-tracker.js +78 -0
- package/assets/plugin/live/stop-watcher.d.ts +67 -0
- package/assets/plugin/live/stop-watcher.js +218 -0
- package/assets/plugin/live/user-data-active-probe.d.ts +54 -0
- package/assets/plugin/live/user-data-active-probe.js +180 -0
- package/assets/plugin/live/user-data-stream-controller.d.ts +200 -0
- package/assets/plugin/live/user-data-stream-controller.js +579 -0
- package/assets/plugin/live/user-data-stream-ws.d.ts +22 -0
- package/assets/plugin/live/user-data-stream-ws.js +63 -0
- package/assets/plugin/live/user-data-stream.d.ts +243 -0
- package/assets/plugin/live/user-data-stream.js +704 -0
- package/assets/plugin/logger.d.ts +2 -0
- package/assets/plugin/logger.js +2 -0
- package/assets/plugin/mfe.d.ts +21 -0
- package/assets/plugin/mfe.js +68 -0
- package/assets/plugin/onboarding/mode-ladder.d.ts +1 -0
- package/assets/plugin/onboarding/mode-ladder.js +3 -0
- package/assets/plugin/onboarding/runtime.d.ts +102 -0
- package/assets/plugin/onboarding/runtime.js +215 -0
- package/assets/plugin/openclaw.plugin.json +92 -0
- package/assets/plugin/paper-adapter.d.ts +24 -0
- package/assets/plugin/paper-adapter.js +91 -0
- package/assets/plugin/persistence/state-manager.d.ts +66 -0
- package/assets/plugin/persistence/state-manager.js +222 -0
- package/assets/plugin/pinned-plan.d.ts +9 -0
- package/assets/plugin/pinned-plan.js +23 -0
- package/assets/plugin/portfolio/reentry-tracker.d.ts +36 -0
- package/assets/plugin/portfolio/reentry-tracker.js +127 -0
- package/assets/plugin/portfolio/wave9-admission.d.ts +67 -0
- package/assets/plugin/portfolio/wave9-admission.js +262 -0
- package/assets/plugin/portfolio/wave9-policy.d.ts +36 -0
- package/assets/plugin/portfolio/wave9-policy.js +183 -0
- package/assets/plugin/risk/pre-trade-check.d.ts +38 -0
- package/assets/plugin/risk/pre-trade-check.js +345 -0
- package/assets/plugin/risk/pre-trade-types.d.ts +60 -0
- package/assets/plugin/risk/pre-trade-types.js +3 -0
- package/assets/plugin/shadow/shadow-tracker.d.ts +36 -0
- package/assets/plugin/shadow/shadow-tracker.js +151 -0
- package/assets/plugin/shadow/types.d.ts +42 -0
- package/assets/plugin/shadow/types.js +20 -0
- package/assets/plugin/shared/indicators-extended.d.ts +52 -0
- package/assets/plugin/shared/indicators-extended.js +291 -0
- package/assets/plugin/shared/indicators.d.ts +15 -0
- package/assets/plugin/shared/indicators.js +114 -0
- package/assets/plugin/signals/conditions/registry.d.ts +16 -0
- package/assets/plugin/signals/conditions/registry.js +1333 -0
- package/assets/plugin/signals/conditions/types.d.ts +1 -0
- package/assets/plugin/signals/conditions/types.js +4 -0
- package/assets/plugin/signals/direction-rules.d.ts +3 -0
- package/assets/plugin/signals/direction-rules.js +24 -0
- package/assets/plugin/signals/entry-rules.d.ts +6 -0
- package/assets/plugin/signals/entry-rules.js +33 -0
- package/assets/plugin/signals/serialize-context.d.ts +4 -0
- package/assets/plugin/signals/serialize-context.js +39 -0
- package/assets/plugin/signals/stop-rules.d.ts +3 -0
- package/assets/plugin/signals/stop-rules.js +48 -0
- package/assets/plugin/signals/strategy-adapter.d.ts +47 -0
- package/assets/plugin/signals/strategy-adapter.js +209 -0
- package/assets/plugin/signals/types.d.ts +1 -0
- package/assets/plugin/signals/types.js +8 -0
- package/assets/plugin/simulator/exchange-simulator.d.ts +105 -0
- package/assets/plugin/simulator/exchange-simulator.js +759 -0
- package/assets/plugin/simulator/fill-engine.d.ts +53 -0
- package/assets/plugin/simulator/fill-engine.js +280 -0
- package/assets/plugin/simulator/paper-market-feed.d.ts +26 -0
- package/assets/plugin/simulator/paper-market-feed.js +104 -0
- package/assets/plugin/simulator/realistic-fills.d.ts +59 -0
- package/assets/plugin/simulator/realistic-fills.js +175 -0
- package/assets/plugin/simulator/types.d.ts +228 -0
- package/assets/plugin/simulator/types.js +43 -0
- package/assets/plugin/strategy/builtin-strategies.d.ts +2 -0
- package/assets/plugin/strategy/builtin-strategies.js +113 -0
- package/assets/plugin/strategy/condition-registry.d.ts +3 -0
- package/assets/plugin/strategy/condition-registry.js +153 -0
- package/assets/plugin/strategy/evaluator.d.ts +70 -0
- package/assets/plugin/strategy/evaluator.js +98 -0
- package/assets/plugin/tools/assessment-validation.d.ts +141 -0
- package/assets/plugin/tools/assessment-validation.js +473 -0
- package/assets/plugin/tools/attach-brackets.d.ts +39 -0
- package/assets/plugin/tools/attach-brackets.js +564 -0
- package/assets/plugin/tools/audit-bracket-protection.d.ts +49 -0
- package/assets/plugin/tools/audit-bracket-protection.js +683 -0
- package/assets/plugin/tools/bracket-control.d.ts +12 -0
- package/assets/plugin/tools/bracket-control.js +35 -0
- package/assets/plugin/tools/cancel-all-orders.d.ts +9 -0
- package/assets/plugin/tools/cancel-all-orders.js +8 -0
- package/assets/plugin/tools/cancel-order.d.ts +14 -0
- package/assets/plugin/tools/cancel-order.js +60 -0
- package/assets/plugin/tools/check-position-health.d.ts +46 -0
- package/assets/plugin/tools/check-position-health.js +194 -0
- package/assets/plugin/tools/clear-exchange-credentials.d.ts +24 -0
- package/assets/plugin/tools/clear-exchange-credentials.js +70 -0
- package/assets/plugin/tools/close-position.d.ts +45 -0
- package/assets/plugin/tools/close-position.js +722 -0
- package/assets/plugin/tools/create-order.d.ts +89 -0
- package/assets/plugin/tools/create-order.js +1555 -0
- package/assets/plugin/tools/exit-gate.d.ts +58 -0
- package/assets/plugin/tools/exit-gate.js +162 -0
- package/assets/plugin/tools/fetch-balance.d.ts +5 -0
- package/assets/plugin/tools/fetch-balance.js +4 -0
- package/assets/plugin/tools/fetch-ohlcv.d.ts +11 -0
- package/assets/plugin/tools/fetch-ohlcv.js +8 -0
- package/assets/plugin/tools/fetch-open-orders.d.ts +7 -0
- package/assets/plugin/tools/fetch-open-orders.js +4 -0
- package/assets/plugin/tools/fetch-positions.d.ts +7 -0
- package/assets/plugin/tools/fetch-positions.js +4 -0
- package/assets/plugin/tools/fetch-ticker.d.ts +11 -0
- package/assets/plugin/tools/fetch-ticker.js +5 -0
- package/assets/plugin/tools/get-agent-profile.d.ts +4 -0
- package/assets/plugin/tools/get-agent-profile.js +6 -0
- package/assets/plugin/tools/get-analytics.d.ts +6 -0
- package/assets/plugin/tools/get-analytics.js +7 -0
- package/assets/plugin/tools/get-backtest.d.ts +12 -0
- package/assets/plugin/tools/get-backtest.js +91 -0
- package/assets/plugin/tools/get-basis.d.ts +7 -0
- package/assets/plugin/tools/get-basis.js +7 -0
- package/assets/plugin/tools/get-bracket-config.d.ts +11 -0
- package/assets/plugin/tools/get-bracket-config.js +24 -0
- package/assets/plugin/tools/get-cascade-risk.d.ts +7 -0
- package/assets/plugin/tools/get-cascade-risk.js +8 -0
- package/assets/plugin/tools/get-crypto-metrics.d.ts +18 -0
- package/assets/plugin/tools/get-crypto-metrics.js +62 -0
- package/assets/plugin/tools/get-cvd.d.ts +6 -0
- package/assets/plugin/tools/get-cvd.js +6 -0
- package/assets/plugin/tools/get-divergences.d.ts +6 -0
- package/assets/plugin/tools/get-divergences.js +6 -0
- package/assets/plugin/tools/get-funding-context.d.ts +6 -0
- package/assets/plugin/tools/get-funding-context.js +16 -0
- package/assets/plugin/tools/get-liquidation-levels.d.ts +7 -0
- package/assets/plugin/tools/get-liquidation-levels.js +7 -0
- package/assets/plugin/tools/get-liquidation-pulse.d.ts +9 -0
- package/assets/plugin/tools/get-liquidation-pulse.js +22 -0
- package/assets/plugin/tools/get-market-breadth.d.ts +6 -0
- package/assets/plugin/tools/get-market-breadth.js +8 -0
- package/assets/plugin/tools/get-market-intel.d.ts +19 -0
- package/assets/plugin/tools/get-market-intel.js +116 -0
- package/assets/plugin/tools/get-market-structure.d.ts +47 -0
- package/assets/plugin/tools/get-market-structure.js +198 -0
- package/assets/plugin/tools/get-my-mined-patterns.d.ts +20 -0
- package/assets/plugin/tools/get-my-mined-patterns.js +61 -0
- package/assets/plugin/tools/get-my-proposed-learnings.d.ts +20 -0
- package/assets/plugin/tools/get-my-proposed-learnings.js +55 -0
- package/assets/plugin/tools/get-my-recent-reviews.d.ts +22 -0
- package/assets/plugin/tools/get-my-recent-reviews.js +66 -0
- package/assets/plugin/tools/get-orderbook.d.ts +21 -0
- package/assets/plugin/tools/get-orderbook.js +32 -0
- package/assets/plugin/tools/get-pattern-scan.d.ts +7 -0
- package/assets/plugin/tools/get-pattern-scan.js +8 -0
- package/assets/plugin/tools/get-regime.d.ts +6 -0
- package/assets/plugin/tools/get-regime.js +7 -0
- package/assets/plugin/tools/get-relevant-learnings.d.ts +21 -0
- package/assets/plugin/tools/get-relevant-learnings.js +65 -0
- package/assets/plugin/tools/get-resting-liquidity.d.ts +6 -0
- package/assets/plugin/tools/get-resting-liquidity.js +11 -0
- package/assets/plugin/tools/get-risk-scenario.d.ts +29 -0
- package/assets/plugin/tools/get-risk-scenario.js +47 -0
- package/assets/plugin/tools/get-risk-summary.d.ts +51 -0
- package/assets/plugin/tools/get-risk-summary.js +118 -0
- package/assets/plugin/tools/get-sentiment.d.ts +4 -0
- package/assets/plugin/tools/get-sentiment.js +6 -0
- package/assets/plugin/tools/get-session-review.d.ts +7 -0
- package/assets/plugin/tools/get-session-review.js +8 -0
- package/assets/plugin/tools/get-setup-detail.d.ts +7 -0
- package/assets/plugin/tools/get-setup-detail.js +322 -0
- package/assets/plugin/tools/get-signals.d.ts +15 -0
- package/assets/plugin/tools/get-signals.js +54 -0
- package/assets/plugin/tools/get-sizing.d.ts +6 -0
- package/assets/plugin/tools/get-sizing.js +6 -0
- package/assets/plugin/tools/get-trade-feedback.d.ts +7 -0
- package/assets/plugin/tools/get-trade-feedback.js +8 -0
- package/assets/plugin/tools/get-trade-flow.d.ts +7 -0
- package/assets/plugin/tools/get-trade-flow.js +7 -0
- package/assets/plugin/tools/get-volume-analysis.d.ts +21 -0
- package/assets/plugin/tools/get-volume-analysis.js +74 -0
- package/assets/plugin/tools/get-volume-profile.d.ts +7 -0
- package/assets/plugin/tools/get-volume-profile.js +7 -0
- package/assets/plugin/tools/get-wave9-status.d.ts +127 -0
- package/assets/plugin/tools/get-wave9-status.js +796 -0
- package/assets/plugin/tools/helpers.d.ts +26 -0
- package/assets/plugin/tools/helpers.js +39 -0
- package/assets/plugin/tools/intel-api.d.ts +34 -0
- package/assets/plugin/tools/intel-api.js +119 -0
- package/assets/plugin/tools/intel-cache.d.ts +25 -0
- package/assets/plugin/tools/intel-cache.js +148 -0
- package/assets/plugin/tools/list-strategies.d.ts +17 -0
- package/assets/plugin/tools/list-strategies.js +23 -0
- package/assets/plugin/tools/modify-stop.d.ts +21 -0
- package/assets/plugin/tools/modify-stop.js +120 -0
- package/assets/plugin/tools/modify-target.d.ts +21 -0
- package/assets/plugin/tools/modify-target.js +110 -0
- package/assets/plugin/tools/propose-learning.d.ts +22 -0
- package/assets/plugin/tools/propose-learning.js +65 -0
- package/assets/plugin/tools/query-review-outcomes.d.ts +30 -0
- package/assets/plugin/tools/query-review-outcomes.js +64 -0
- package/assets/plugin/tools/query-trades.d.ts +21 -0
- package/assets/plugin/tools/query-trades.js +37 -0
- package/assets/plugin/tools/record-position-reviews.d.ts +38 -0
- package/assets/plugin/tools/record-position-reviews.js +147 -0
- package/assets/plugin/tools/save-strategy.d.ts +16 -0
- package/assets/plugin/tools/save-strategy.js +46 -0
- package/assets/plugin/tools/scan-pairs.d.ts +22 -0
- package/assets/plugin/tools/scan-pairs.js +234 -0
- package/assets/plugin/tools/score-setup.d.ts +31 -0
- package/assets/plugin/tools/score-setup.js +268 -0
- package/assets/plugin/tools/set-bracket-requirement.d.ts +18 -0
- package/assets/plugin/tools/set-bracket-requirement.js +81 -0
- package/assets/plugin/tools/set-exchange-credentials.d.ts +25 -0
- package/assets/plugin/tools/set-exchange-credentials.js +80 -0
- package/assets/plugin/tools/set-trading-mode.d.ts +26 -0
- package/assets/plugin/tools/set-trading-mode.js +152 -0
- package/assets/plugin/tools/test-exchange-credentials.d.ts +16 -0
- package/assets/plugin/tools/test-exchange-credentials.js +100 -0
- package/assets/plugin/tools/toggle-strategy.d.ts +8 -0
- package/assets/plugin/tools/toggle-strategy.js +15 -0
- package/assets/plugin/trading-params-cache.d.ts +26 -0
- package/assets/plugin/trading-params-cache.js +52 -0
- package/assets/plugin/types.d.ts +134 -0
- package/assets/plugin/types.js +7 -0
- package/assets/plugin/util/plugin-paths.d.ts +3 -0
- package/assets/plugin/util/plugin-paths.js +15 -0
- package/assets/plugin/venues/hyperliquid/hl-balance.d.ts +116 -0
- package/assets/plugin/venues/hyperliquid/hl-balance.js +145 -0
- package/assets/plugin/venues/hyperliquid/hl-bracket-coordinator.d.ts +123 -0
- package/assets/plugin/venues/hyperliquid/hl-bracket-coordinator.js +533 -0
- package/assets/plugin/venues/hyperliquid/hl-brackets.d.ts +102 -0
- package/assets/plugin/venues/hyperliquid/hl-brackets.js +172 -0
- package/assets/plugin/venues/hyperliquid/hl-cloid.d.ts +22 -0
- package/assets/plugin/venues/hyperliquid/hl-cloid.js +82 -0
- package/assets/plugin/venues/hyperliquid/hl-info-cache.d.ts +46 -0
- package/assets/plugin/venues/hyperliquid/hl-info-cache.js +125 -0
- package/assets/plugin/venues/hyperliquid/hl-live-adapter.d.ts +146 -0
- package/assets/plugin/venues/hyperliquid/hl-live-adapter.js +728 -0
- package/assets/plugin/venues/hyperliquid/hl-precision.d.ts +61 -0
- package/assets/plugin/venues/hyperliquid/hl-precision.js +176 -0
- package/assets/plugin/venues/hyperliquid/hl-private.d.ts +88 -0
- package/assets/plugin/venues/hyperliquid/hl-private.js +357 -0
- package/assets/plugin/venues/hyperliquid/hl-public.d.ts +79 -0
- package/assets/plugin/venues/hyperliquid/hl-public.js +436 -0
- package/assets/plugin/venues/hyperliquid/hl-rate-gate.d.ts +57 -0
- package/assets/plugin/venues/hyperliquid/hl-rate-gate.js +220 -0
- package/assets/plugin/venues/hyperliquid/hl-user-stream.d.ts +90 -0
- package/assets/plugin/venues/hyperliquid/hl-user-stream.js +220 -0
- package/assets/plugin/venues/registry.d.ts +38 -0
- package/assets/plugin/venues/registry.js +46 -0
- package/assets/plugin/venues/symbols.d.ts +43 -0
- package/assets/plugin/venues/symbols.js +107 -0
- package/assets/plugin/wave9/live-account-capture.d.ts +67 -0
- package/assets/plugin/wave9/live-account-capture.js +435 -0
- package/assets/plugin/wave9/live-autonomous-protection.d.ts +39 -0
- package/assets/plugin/wave9/live-autonomous-protection.js +112 -0
- package/assets/plugin/wave9/live-durable-reconciliation-scheduler.d.ts +33 -0
- package/assets/plugin/wave9/live-durable-reconciliation-scheduler.js +115 -0
- package/assets/plugin/wave9/live-execution-ledger.d.ts +107 -0
- package/assets/plugin/wave9/live-execution-ledger.js +498 -0
- package/assets/plugin/wave9/live-position-confirmation.d.ts +18 -0
- package/assets/plugin/wave9/live-position-confirmation.js +111 -0
- package/assets/plugin/wave9/live-residual-protection.d.ts +18 -0
- package/assets/plugin/wave9/live-residual-protection.js +250 -0
- package/assets/plugin/wave9/live-startup-reconciliation.d.ts +38 -0
- package/assets/plugin/wave9/live-startup-reconciliation.js +454 -0
- package/assets/plugin/wave9/live-symbol-ownership.d.ts +20 -0
- package/assets/plugin/wave9/live-symbol-ownership.js +132 -0
- package/assets/plugin/wave9/paper-admission-guard.d.ts +199 -0
- package/assets/plugin/wave9/paper-admission-guard.js +650 -0
- package/assets/plugin/wave9/usdm-evidence-provider.d.ts +42 -0
- package/assets/plugin/wave9/usdm-evidence-provider.js +133 -0
- package/dist/plugin.js +24 -15
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface SymbolRules {
|
|
2
|
+
minNotional: number;
|
|
3
|
+
minQty: number;
|
|
4
|
+
stepSize: number;
|
|
5
|
+
tickSize: number;
|
|
6
|
+
maxQty: number;
|
|
7
|
+
pricePrecision: number;
|
|
8
|
+
amountPrecision: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ValidationResult {
|
|
11
|
+
valid: boolean;
|
|
12
|
+
error?: string;
|
|
13
|
+
roundedAmount: number;
|
|
14
|
+
roundedPrice?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Normalize symbol format for consistent lookups.
|
|
18
|
+
* Tools use "BTC/USDT", CCXT futures markets are "BTC/USDT:USDT".
|
|
19
|
+
* This function tries both formats to find a match.
|
|
20
|
+
*/
|
|
21
|
+
export declare function normalizeSymbol(symbol: string, knownSymbols: Set<string>): string;
|
|
22
|
+
export declare class ExchangeInfoCache {
|
|
23
|
+
private rules;
|
|
24
|
+
/** Set of all known symbols for normalization lookups. */
|
|
25
|
+
private knownSymbols;
|
|
26
|
+
/** Load market info from CCXT exchange instance. Throws on failure (incl.
|
|
27
|
+
* an active ban — the gate short-circuits with zero network so a boot
|
|
28
|
+
* during a 418 can't extend it); callers treat a throw as init failure. */
|
|
29
|
+
load(exchange: any): Promise<void>;
|
|
30
|
+
/** Get rules for a symbol (tries both BTC/USDT and BTC/USDT:USDT formats). */
|
|
31
|
+
getRules(symbol: string): SymbolRules | undefined;
|
|
32
|
+
/** Validate and round order parameters before submission. */
|
|
33
|
+
validate(symbol: string, amount: number, price?: number, lastPrice?: number): ValidationResult;
|
|
34
|
+
/** Round value to step size and precision. */
|
|
35
|
+
private roundToStep;
|
|
36
|
+
get size(): number;
|
|
37
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Exchange info cache — caches per-symbol trading rules (min notional, lot sizes, tick sizes).
|
|
2
|
+
// Loaded once on startup from CCXT loadMarkets(), used to validate and round orders before submission.
|
|
3
|
+
import { logger } from '../logger.js';
|
|
4
|
+
import { assertNotBanned, noteBinanceError, noteSuccess } from '../ccxt/binance-ban-gate.js';
|
|
5
|
+
const TAG = 'exchange-info';
|
|
6
|
+
/**
|
|
7
|
+
* Normalize symbol format for consistent lookups.
|
|
8
|
+
* Tools use "BTC/USDT", CCXT futures markets are "BTC/USDT:USDT".
|
|
9
|
+
* This function tries both formats to find a match.
|
|
10
|
+
*/
|
|
11
|
+
export function normalizeSymbol(symbol, knownSymbols) {
|
|
12
|
+
// Exact match first
|
|
13
|
+
if (knownSymbols.has(symbol))
|
|
14
|
+
return symbol;
|
|
15
|
+
// Try adding :USDT suffix (most common futures pattern)
|
|
16
|
+
const withSuffix = `${symbol}:USDT`;
|
|
17
|
+
if (knownSymbols.has(withSuffix))
|
|
18
|
+
return withSuffix;
|
|
19
|
+
// Try removing :USDT suffix
|
|
20
|
+
const withoutSuffix = symbol.replace(/:USDT$/, '');
|
|
21
|
+
if (knownSymbols.has(withoutSuffix))
|
|
22
|
+
return withoutSuffix;
|
|
23
|
+
// No match — return as-is (will fail validation with "Unknown symbol")
|
|
24
|
+
return symbol;
|
|
25
|
+
}
|
|
26
|
+
export class ExchangeInfoCache {
|
|
27
|
+
rules = new Map();
|
|
28
|
+
/** Set of all known symbols for normalization lookups. */
|
|
29
|
+
knownSymbols = new Set();
|
|
30
|
+
/** Load market info from CCXT exchange instance. Throws on failure (incl.
|
|
31
|
+
* an active ban — the gate short-circuits with zero network so a boot
|
|
32
|
+
* during a 418 can't extend it); callers treat a throw as init failure. */
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
async load(exchange) {
|
|
35
|
+
let info;
|
|
36
|
+
try {
|
|
37
|
+
// GET /fapi/v1/exchangeInfo via ccxt loadMarkets — IP weight 1
|
|
38
|
+
// (doc-verified). Ban-gated but never weight-paced (NEVER_PACE):
|
|
39
|
+
// once-per-boot and required for order validation + emergency close.
|
|
40
|
+
assertNotBanned('loadMarkets');
|
|
41
|
+
info = await exchange.loadMarkets(true); // force refresh
|
|
42
|
+
noteSuccess();
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
noteBinanceError(err);
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
for (const [symbol, market] of Object.entries(info)) {
|
|
49
|
+
const m = market;
|
|
50
|
+
// Only cache futures (swap) markets
|
|
51
|
+
if (m.type !== 'swap')
|
|
52
|
+
continue;
|
|
53
|
+
const limits = m.limits;
|
|
54
|
+
const precision = m.precision;
|
|
55
|
+
const rules = {
|
|
56
|
+
minNotional: limits?.cost?.min ?? 5,
|
|
57
|
+
minQty: limits?.amount?.min ?? 0.001,
|
|
58
|
+
stepSize: precision?.amount != null ? Math.pow(10, -precision.amount) : 0.001,
|
|
59
|
+
tickSize: precision?.price != null ? Math.pow(10, -precision.price) : 0.01,
|
|
60
|
+
maxQty: limits?.amount?.max ?? Infinity,
|
|
61
|
+
pricePrecision: precision?.price ?? 2,
|
|
62
|
+
amountPrecision: precision?.amount ?? 3,
|
|
63
|
+
};
|
|
64
|
+
// Store under both "BTC/USDT:USDT" and "BTC/USDT" keys for lookup flexibility
|
|
65
|
+
this.rules.set(symbol, rules);
|
|
66
|
+
this.knownSymbols.add(symbol);
|
|
67
|
+
const shortSymbol = symbol.replace(/:USDT$/, '');
|
|
68
|
+
if (shortSymbol !== symbol && !this.rules.has(shortSymbol)) {
|
|
69
|
+
this.rules.set(shortSymbol, rules);
|
|
70
|
+
this.knownSymbols.add(shortSymbol);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
logger.info(TAG, `Loaded exchange rules for ${this.knownSymbols.size} symbols`);
|
|
74
|
+
}
|
|
75
|
+
/** Get rules for a symbol (tries both BTC/USDT and BTC/USDT:USDT formats). */
|
|
76
|
+
getRules(symbol) {
|
|
77
|
+
return this.rules.get(symbol) ?? this.rules.get(normalizeSymbol(symbol, this.knownSymbols));
|
|
78
|
+
}
|
|
79
|
+
/** Validate and round order parameters before submission. */
|
|
80
|
+
validate(symbol, amount, price, lastPrice) {
|
|
81
|
+
const normalized = normalizeSymbol(symbol, this.knownSymbols);
|
|
82
|
+
const rules = this.rules.get(normalized);
|
|
83
|
+
if (!rules)
|
|
84
|
+
return { valid: false, error: `Unknown symbol: ${symbol}`, roundedAmount: amount };
|
|
85
|
+
const roundedAmount = this.roundToStep(amount, rules.stepSize, rules.amountPrecision);
|
|
86
|
+
const roundedPrice = price != null
|
|
87
|
+
? this.roundToStep(price, rules.tickSize, rules.pricePrecision)
|
|
88
|
+
: undefined;
|
|
89
|
+
if (roundedAmount < rules.minQty) {
|
|
90
|
+
// Include the requested amount + the rule context. Without this, sub-step
|
|
91
|
+
// requests floor to 0 and the agent reads "Amount 0 below min 0.001"
|
|
92
|
+
// as a validator bug rather than as "your size is below the exchange
|
|
93
|
+
// minimum". Surface the dollar minimum too when we know the price —
|
|
94
|
+
// tells the agent exactly how much to size up.
|
|
95
|
+
const refPrice = roundedPrice ?? price ?? lastPrice;
|
|
96
|
+
const minNotionalAtPrice = refPrice != null && refPrice > 0
|
|
97
|
+
? ` (~$${(rules.minQty * refPrice).toFixed(2)})`
|
|
98
|
+
: '';
|
|
99
|
+
const requestedDesc = roundedAmount === amount
|
|
100
|
+
? `${roundedAmount}`
|
|
101
|
+
: `${amount} (rounded to ${roundedAmount} by step ${rules.stepSize})`;
|
|
102
|
+
return {
|
|
103
|
+
valid: false,
|
|
104
|
+
error: `Amount ${requestedDesc} below min ${rules.minQty}${minNotionalAtPrice} for ${symbol}`,
|
|
105
|
+
roundedAmount,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (roundedAmount > rules.maxQty) {
|
|
109
|
+
return { valid: false, error: `Amount ${roundedAmount} above max ${rules.maxQty} for ${symbol}`, roundedAmount };
|
|
110
|
+
}
|
|
111
|
+
// Check min notional (order value)
|
|
112
|
+
const refPrice = roundedPrice ?? lastPrice;
|
|
113
|
+
if (refPrice != null && refPrice > 0) {
|
|
114
|
+
const notional = roundedAmount * refPrice;
|
|
115
|
+
if (notional < rules.minNotional) {
|
|
116
|
+
return {
|
|
117
|
+
valid: false,
|
|
118
|
+
error: `Order notional $${notional.toFixed(2)} below minimum $${rules.minNotional} for ${symbol}`,
|
|
119
|
+
roundedAmount,
|
|
120
|
+
roundedPrice,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { valid: true, roundedAmount, roundedPrice };
|
|
125
|
+
}
|
|
126
|
+
/** Round value to step size and precision. */
|
|
127
|
+
roundToStep(value, stepSize, precision) {
|
|
128
|
+
const stepped = Math.floor(value / stepSize) * stepSize;
|
|
129
|
+
return Number(stepped.toFixed(precision));
|
|
130
|
+
}
|
|
131
|
+
get size() {
|
|
132
|
+
return this.rules.size;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { MarketContext } from '@reefclaw/shared';
|
|
2
|
+
export interface ParsedEvent {
|
|
3
|
+
id?: number;
|
|
4
|
+
event: string;
|
|
5
|
+
data: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Incremental SSE parser. Feed raw text; returns the complete events plus the
|
|
9
|
+
* unparsed remainder (a partial event still arriving at the buffer tail). Pure.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseSse(buffer: string): {
|
|
12
|
+
events: ParsedEvent[];
|
|
13
|
+
remainder: string;
|
|
14
|
+
};
|
|
15
|
+
export interface FactSubscriberOptions {
|
|
16
|
+
/** Intel base URL, e.g. https://intel.reefclaw.com */
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
/** rc_ connection token. */
|
|
19
|
+
token: string;
|
|
20
|
+
symbols: string[];
|
|
21
|
+
/** Injected for tests; defaults to global fetch. */
|
|
22
|
+
fetchImpl?: typeof fetch;
|
|
23
|
+
/** Base reconnect delay (ms). Doubles per consecutive failure with ±20%
|
|
24
|
+
* jitter up to reconnectCapMs — a fixed delay would hard-loop a 401/404
|
|
25
|
+
* (token misconfig / stream disabled) at max rate forever. */
|
|
26
|
+
reconnectDelayMs?: number;
|
|
27
|
+
/** Reconnect backoff cap (ms, default 60s). */
|
|
28
|
+
reconnectCapMs?: number;
|
|
29
|
+
/** Abort + reconnect when a LIVE connection delivers no bytes for this
|
|
30
|
+
* long (default 30s = 6× the broadcaster's 5s cadence). Without this, a
|
|
31
|
+
* silently stalled stream (no close, no RST) leaves `reader.read()`
|
|
32
|
+
* pending forever while the evaluator keeps serving ever-staler facts —
|
|
33
|
+
* same class as the user-data-stream / routed-WS staleness watchdogs. */
|
|
34
|
+
stallThresholdMs?: number;
|
|
35
|
+
/** Injected for tests. */
|
|
36
|
+
now?: () => number;
|
|
37
|
+
}
|
|
38
|
+
export declare class FactSubscriber {
|
|
39
|
+
private readonly opts;
|
|
40
|
+
private readonly latest;
|
|
41
|
+
/** Per-symbol wall-clock receipt time of the latest fact (now()-based). */
|
|
42
|
+
private readonly latestAt;
|
|
43
|
+
private buffer;
|
|
44
|
+
private lastEventId;
|
|
45
|
+
private running;
|
|
46
|
+
private controller;
|
|
47
|
+
private applied;
|
|
48
|
+
private errors;
|
|
49
|
+
private stalls;
|
|
50
|
+
private consecutiveFailures;
|
|
51
|
+
/** 0 = no live connection; otherwise last byte-activity time. */
|
|
52
|
+
private lastChunkAt;
|
|
53
|
+
private watchdog;
|
|
54
|
+
private readonly now;
|
|
55
|
+
constructor(opts: FactSubscriberOptions);
|
|
56
|
+
/** Latest streamed context for a symbol — what the evaluator reads. */
|
|
57
|
+
getLatest(symbol: string): MarketContext | null;
|
|
58
|
+
/** When the latest fact for a symbol ARRIVED (receipt clock, not the
|
|
59
|
+
* context's own timestamp). Null when no fact has ever arrived. Callers
|
|
60
|
+
* use this to refuse stale facts (LocalSignalService.getSnapshot). */
|
|
61
|
+
getLatestFactAt(symbol: string): number | null;
|
|
62
|
+
getStats(): {
|
|
63
|
+
applied: number;
|
|
64
|
+
errors: number;
|
|
65
|
+
stalls: number;
|
|
66
|
+
lastEventId: number | undefined;
|
|
67
|
+
symbols: number;
|
|
68
|
+
};
|
|
69
|
+
/** Apply one parsed event: deserialize a `fact` and store it as the latest
|
|
70
|
+
* context for its symbol. No I/O — exposed for tests. */
|
|
71
|
+
applyEvent(ev: ParsedEvent): void;
|
|
72
|
+
/** Feed a raw text chunk; handles events split across chunk boundaries. */
|
|
73
|
+
ingest(chunk: string): void;
|
|
74
|
+
start(): void;
|
|
75
|
+
stop(): void;
|
|
76
|
+
private backoffMs;
|
|
77
|
+
private loop;
|
|
78
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// Plugin-side fact stream subscriber — facts-out (docs/FACTS_OUT_ARCHITECTURE.md)
|
|
2
|
+
// step 4. Connects to the intel broadcaster's SSE endpoint
|
|
3
|
+
// (/api/facts/subscribe), maintains the latest MarketContext per symbol, and
|
|
4
|
+
// feeds it to the LocalStrategyEvaluator (PR #45). Reconnects with Last-Event-ID.
|
|
5
|
+
//
|
|
6
|
+
// Uses the SAME matched (de)serializer the broadcaster uses (shared via the sync
|
|
7
|
+
// manifest), so the wire round-trip is byte-identical — the Date fields are
|
|
8
|
+
// rehydrated rather than left as strings.
|
|
9
|
+
//
|
|
10
|
+
// NOT wired into the heartbeat yet — built + tested standalone. The parse /
|
|
11
|
+
// apply core is pure and unit-tested; the fetch loop is thin plumbing.
|
|
12
|
+
import { deserializeContext } from '../signals/serialize-context.js';
|
|
13
|
+
/**
|
|
14
|
+
* Incremental SSE parser. Feed raw text; returns the complete events plus the
|
|
15
|
+
* unparsed remainder (a partial event still arriving at the buffer tail). Pure.
|
|
16
|
+
*/
|
|
17
|
+
export function parseSse(buffer) {
|
|
18
|
+
const events = [];
|
|
19
|
+
let sep;
|
|
20
|
+
// Events are terminated by a blank line ("\n\n").
|
|
21
|
+
while ((sep = buffer.indexOf('\n\n')) !== -1) {
|
|
22
|
+
const raw = buffer.slice(0, sep);
|
|
23
|
+
buffer = buffer.slice(sep + 2);
|
|
24
|
+
let id;
|
|
25
|
+
let event = 'message';
|
|
26
|
+
const dataLines = [];
|
|
27
|
+
for (const line of raw.split('\n')) {
|
|
28
|
+
if (line.startsWith('id:')) {
|
|
29
|
+
const v = Number(line.slice(3).trim());
|
|
30
|
+
if (Number.isFinite(v))
|
|
31
|
+
id = v;
|
|
32
|
+
}
|
|
33
|
+
else if (line.startsWith('event:')) {
|
|
34
|
+
event = line.slice(6).trim();
|
|
35
|
+
}
|
|
36
|
+
else if (line.startsWith('data:')) {
|
|
37
|
+
dataLines.push(line.slice(5).replace(/^ /, ''));
|
|
38
|
+
}
|
|
39
|
+
// ":" comment lines and unknown fields are ignored, per the SSE spec.
|
|
40
|
+
}
|
|
41
|
+
events.push({ id, event, data: dataLines.join('\n') });
|
|
42
|
+
}
|
|
43
|
+
return { events, remainder: buffer };
|
|
44
|
+
}
|
|
45
|
+
export class FactSubscriber {
|
|
46
|
+
opts;
|
|
47
|
+
latest = new Map();
|
|
48
|
+
/** Per-symbol wall-clock receipt time of the latest fact (now()-based). */
|
|
49
|
+
latestAt = new Map();
|
|
50
|
+
buffer = '';
|
|
51
|
+
lastEventId;
|
|
52
|
+
running = false;
|
|
53
|
+
controller = null;
|
|
54
|
+
applied = 0;
|
|
55
|
+
errors = 0;
|
|
56
|
+
stalls = 0;
|
|
57
|
+
consecutiveFailures = 0;
|
|
58
|
+
/** 0 = no live connection; otherwise last byte-activity time. */
|
|
59
|
+
lastChunkAt = 0;
|
|
60
|
+
watchdog = null;
|
|
61
|
+
now;
|
|
62
|
+
constructor(opts) {
|
|
63
|
+
this.opts = opts;
|
|
64
|
+
this.now = opts.now ?? Date.now;
|
|
65
|
+
}
|
|
66
|
+
/** Latest streamed context for a symbol — what the evaluator reads. */
|
|
67
|
+
getLatest(symbol) {
|
|
68
|
+
return this.latest.get(symbol) ?? null;
|
|
69
|
+
}
|
|
70
|
+
/** When the latest fact for a symbol ARRIVED (receipt clock, not the
|
|
71
|
+
* context's own timestamp). Null when no fact has ever arrived. Callers
|
|
72
|
+
* use this to refuse stale facts (LocalSignalService.getSnapshot). */
|
|
73
|
+
getLatestFactAt(symbol) {
|
|
74
|
+
return this.latestAt.get(symbol) ?? null;
|
|
75
|
+
}
|
|
76
|
+
getStats() {
|
|
77
|
+
return {
|
|
78
|
+
applied: this.applied,
|
|
79
|
+
errors: this.errors,
|
|
80
|
+
stalls: this.stalls,
|
|
81
|
+
lastEventId: this.lastEventId,
|
|
82
|
+
symbols: this.latest.size,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** Apply one parsed event: deserialize a `fact` and store it as the latest
|
|
86
|
+
* context for its symbol. No I/O — exposed for tests. */
|
|
87
|
+
applyEvent(ev) {
|
|
88
|
+
if (ev.id !== undefined)
|
|
89
|
+
this.lastEventId = ev.id;
|
|
90
|
+
if (ev.event !== 'fact')
|
|
91
|
+
return;
|
|
92
|
+
try {
|
|
93
|
+
const ctx = deserializeContext(ev.data);
|
|
94
|
+
this.latest.set(ctx.symbol, ctx);
|
|
95
|
+
this.latestAt.set(ctx.symbol, this.now());
|
|
96
|
+
this.applied++;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
this.errors++;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Feed a raw text chunk; handles events split across chunk boundaries. */
|
|
103
|
+
ingest(chunk) {
|
|
104
|
+
this.buffer += chunk;
|
|
105
|
+
const { events, remainder } = parseSse(this.buffer);
|
|
106
|
+
this.buffer = remainder;
|
|
107
|
+
for (const ev of events)
|
|
108
|
+
this.applyEvent(ev);
|
|
109
|
+
}
|
|
110
|
+
start() {
|
|
111
|
+
if (this.running)
|
|
112
|
+
return;
|
|
113
|
+
this.running = true;
|
|
114
|
+
const stallMs = this.opts.stallThresholdMs ?? 30_000;
|
|
115
|
+
this.watchdog = setInterval(() => {
|
|
116
|
+
if (!this.running || this.lastChunkAt === 0)
|
|
117
|
+
return;
|
|
118
|
+
if (this.now() - this.lastChunkAt > stallMs) {
|
|
119
|
+
this.stalls++;
|
|
120
|
+
this.lastChunkAt = 0; // one abort per stall — reset until reconnect
|
|
121
|
+
this.controller?.abort();
|
|
122
|
+
}
|
|
123
|
+
}, Math.max(1_000, Math.floor(stallMs / 3)));
|
|
124
|
+
this.watchdog.unref?.();
|
|
125
|
+
void this.loop();
|
|
126
|
+
}
|
|
127
|
+
stop() {
|
|
128
|
+
this.running = false;
|
|
129
|
+
if (this.watchdog) {
|
|
130
|
+
clearInterval(this.watchdog);
|
|
131
|
+
this.watchdog = null;
|
|
132
|
+
}
|
|
133
|
+
this.controller?.abort();
|
|
134
|
+
}
|
|
135
|
+
backoffMs() {
|
|
136
|
+
const base = this.opts.reconnectDelayMs ?? 3_000;
|
|
137
|
+
const cap = this.opts.reconnectCapMs ?? 60_000;
|
|
138
|
+
const exp = Math.min(base * 2 ** Math.min(this.consecutiveFailures, 10), cap);
|
|
139
|
+
return Math.round(exp * (0.8 + Math.random() * 0.4)); // ±20% jitter
|
|
140
|
+
}
|
|
141
|
+
async loop() {
|
|
142
|
+
const doFetch = this.opts.fetchImpl ?? fetch;
|
|
143
|
+
const url = `${this.opts.baseUrl}/api/facts/subscribe?symbols=${encodeURIComponent(this.opts.symbols.join(','))}`;
|
|
144
|
+
while (this.running) {
|
|
145
|
+
this.controller = new AbortController();
|
|
146
|
+
let gotData = false;
|
|
147
|
+
try {
|
|
148
|
+
const headers = {
|
|
149
|
+
Authorization: `Bearer ${this.opts.token}`,
|
|
150
|
+
Accept: 'text/event-stream',
|
|
151
|
+
};
|
|
152
|
+
if (this.lastEventId !== undefined)
|
|
153
|
+
headers['Last-Event-ID'] = String(this.lastEventId);
|
|
154
|
+
const resp = await doFetch(url, { headers, signal: this.controller.signal });
|
|
155
|
+
if (!resp.ok || !resp.body)
|
|
156
|
+
throw new Error(`fact stream HTTP ${resp.status}`);
|
|
157
|
+
this.buffer = '';
|
|
158
|
+
this.lastChunkAt = this.now(); // arm the stall watchdog
|
|
159
|
+
const reader = resp.body.getReader();
|
|
160
|
+
const decoder = new TextDecoder();
|
|
161
|
+
while (this.running) {
|
|
162
|
+
const { done, value } = await reader.read();
|
|
163
|
+
if (done)
|
|
164
|
+
break;
|
|
165
|
+
this.lastChunkAt = this.now();
|
|
166
|
+
gotData = true;
|
|
167
|
+
this.ingest(decoder.decode(value, { stream: true }));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
if (!this.running)
|
|
172
|
+
return;
|
|
173
|
+
this.errors++;
|
|
174
|
+
}
|
|
175
|
+
this.lastChunkAt = 0; // no live connection — watchdog idle
|
|
176
|
+
if (!this.running)
|
|
177
|
+
return;
|
|
178
|
+
this.consecutiveFailures = gotData ? 0 : this.consecutiveFailures + 1;
|
|
179
|
+
await new Promise((r) => setTimeout(r, this.backoffMs()));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Minimal order shape needed to resolve a fill price. */
|
|
2
|
+
export interface FillPriceOrder {
|
|
3
|
+
average: number | null;
|
|
4
|
+
filled: number;
|
|
5
|
+
cost: number;
|
|
6
|
+
/** Present only to make explicit that we deliberately DO NOT read it. */
|
|
7
|
+
price?: number | null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The average price a filled order actually executed at, or null if it can't
|
|
11
|
+
* be determined from the response. Never returns the limit price.
|
|
12
|
+
*/
|
|
13
|
+
export declare function fillPriceFromOrder(order: FillPriceOrder): number | null;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Resolve the ACTUAL average fill price of a (partially) filled order.
|
|
2
|
+
//
|
|
3
|
+
// Issue #196 (prod LTC/USDT 2026-07-14, TAO/USDT 2026-07-15): for marketable
|
|
4
|
+
// LIMIT orders, Binance's synchronous USD-M order response
|
|
5
|
+
// (newOrderRespType=RESULT) can carry avgPrice=0 even though executedQty>0 —
|
|
6
|
+
// per the Binance doc, RESULT only guarantees the final fill for MARKET orders
|
|
7
|
+
// and LIMIT orders with a special timeInForce, NOT a plain GTC LIMIT. Code that
|
|
8
|
+
// then used `order.price` (the LIMIT price) as the entry proxy validated bracket
|
|
9
|
+
// geometry / journaled the entry against a price the order never filled at —
|
|
10
|
+
// several percent off for a marketable limit that crossed. That produced:
|
|
11
|
+
// - false "bracket direction invalid" auto-flattens (LTC: SELL limit 43 filled
|
|
12
|
+
// 44.49, target 43.21 compared vs 43 → "must be BELOW 43" → needless scratch), and
|
|
13
|
+
// - wrong journal entry prices (TAO: BUY limit 202 filled ~196.23 → journaled 202).
|
|
14
|
+
//
|
|
15
|
+
// The LIMIT price is NEVER a valid substitute for the fill. This resolver
|
|
16
|
+
// returns the true average fill from the response alone (no network), else null;
|
|
17
|
+
// callers must re-query or fall back to a mark price, never to `order.price`.
|
|
18
|
+
/**
|
|
19
|
+
* The average price a filled order actually executed at, or null if it can't
|
|
20
|
+
* be determined from the response. Never returns the limit price.
|
|
21
|
+
*/
|
|
22
|
+
export function fillPriceFromOrder(order) {
|
|
23
|
+
// 1. The exchange-reported average fill price, when present and positive.
|
|
24
|
+
if (typeof order.average === 'number' && Number.isFinite(order.average) && order.average > 0) {
|
|
25
|
+
return order.average;
|
|
26
|
+
}
|
|
27
|
+
// 2. VWAP from cost/filled. For USD-M futures ccxt maps cost = cumQuote =
|
|
28
|
+
// Σ(price×qty), so cost/filled = Σ(price×qty)/Σqty = the exact avgPrice.
|
|
29
|
+
// Covers the case where avgPrice=0 in the response but cumQuote settled.
|
|
30
|
+
if (typeof order.cost === 'number' && Number.isFinite(order.cost) && order.cost > 0 &&
|
|
31
|
+
typeof order.filled === 'number' && Number.isFinite(order.filled) && order.filled > 0) {
|
|
32
|
+
return order.cost / order.filled;
|
|
33
|
+
}
|
|
34
|
+
// 3. Unknown. The caller MUST NOT fall back to order.price (the limit) — that
|
|
35
|
+
// is exactly the marketable-limit bug this helper exists to prevent.
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface OrderIntent {
|
|
2
|
+
clientOrderId: string;
|
|
3
|
+
symbol: string;
|
|
4
|
+
side: 'buy' | 'sell';
|
|
5
|
+
type: 'market' | 'limit';
|
|
6
|
+
amount: number;
|
|
7
|
+
price?: number;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
status: 'pending' | 'submitted' | 'confirmed' | 'failed';
|
|
10
|
+
exchangeOrderId?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
resolvedAt?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class IntentJournal {
|
|
15
|
+
private readonly filePath;
|
|
16
|
+
private intents;
|
|
17
|
+
/** `basePath` overrides the storage root (default `~/.openclaw/plugins`).
|
|
18
|
+
* Tests pass a temp dir to keep runs hermetic; production omits it.
|
|
19
|
+
* `OPENCLAW_HOME` env var, when set, also reroutes the default base. */
|
|
20
|
+
constructor(pluginId?: string, opts?: {
|
|
21
|
+
basePath?: string;
|
|
22
|
+
});
|
|
23
|
+
/** Record a new order intent BEFORE submitting to exchange. */
|
|
24
|
+
recordIntent(intent: Omit<OrderIntent, 'createdAt' | 'status'>): OrderIntent;
|
|
25
|
+
/** Update intent status after exchange response. */
|
|
26
|
+
updateIntent(clientOrderId: string, update: Partial<Pick<OrderIntent, 'status' | 'exchangeOrderId' | 'error'>>): void;
|
|
27
|
+
/** Get all unresolved intents (pending or submitted). */
|
|
28
|
+
getUnresolved(): OrderIntent[];
|
|
29
|
+
/** Get intent by clientOrderId. */
|
|
30
|
+
getIntent(clientOrderId: string): OrderIntent | undefined;
|
|
31
|
+
/** Get the journal file path. */
|
|
32
|
+
getFilePath(): string;
|
|
33
|
+
/** Load existing intents from JSONL file. Later entries override earlier ones (last-write-wins). */
|
|
34
|
+
private loadExisting;
|
|
35
|
+
/** Append a line to the JSONL file. */
|
|
36
|
+
private appendLine;
|
|
37
|
+
/**
|
|
38
|
+
* Compact the journal — rewrite with only the latest state of each intent.
|
|
39
|
+
* Call periodically (e.g., daily) to prevent unbounded file growth.
|
|
40
|
+
*/
|
|
41
|
+
compact(): void;
|
|
42
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Order intent journal — append-only JSONL file for crash recovery and idempotency.
|
|
2
|
+
// Every live order intent is written BEFORE submission. On network timeout,
|
|
3
|
+
// check exchange by clientOrderId before retry. On restart, resolve all pending intents.
|
|
4
|
+
import { appendFileSync, readFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { logger, formatError } from '../logger.js';
|
|
7
|
+
import { resolvePluginsBaseDir } from '../util/plugin-paths.js';
|
|
8
|
+
const TAG = 'intent-journal';
|
|
9
|
+
export class IntentJournal {
|
|
10
|
+
filePath;
|
|
11
|
+
intents = new Map();
|
|
12
|
+
/** `basePath` overrides the storage root (default `~/.openclaw/plugins`).
|
|
13
|
+
* Tests pass a temp dir to keep runs hermetic; production omits it.
|
|
14
|
+
* `OPENCLAW_HOME` env var, when set, also reroutes the default base. */
|
|
15
|
+
constructor(pluginId, opts) {
|
|
16
|
+
const base = resolvePluginsBaseDir(opts?.basePath);
|
|
17
|
+
const dir = join(base, pluginId ?? 'reefclaw-paper-trading');
|
|
18
|
+
if (!existsSync(dir)) {
|
|
19
|
+
mkdirSync(dir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
this.filePath = join(dir, 'order-intents.jsonl');
|
|
22
|
+
this.loadExisting();
|
|
23
|
+
}
|
|
24
|
+
/** Record a new order intent BEFORE submitting to exchange. */
|
|
25
|
+
recordIntent(intent) {
|
|
26
|
+
const full = {
|
|
27
|
+
...intent,
|
|
28
|
+
createdAt: new Date().toISOString(),
|
|
29
|
+
status: 'pending',
|
|
30
|
+
};
|
|
31
|
+
this.intents.set(full.clientOrderId, full);
|
|
32
|
+
this.appendLine(full);
|
|
33
|
+
logger.info(TAG, `Intent recorded: ${full.clientOrderId} ${full.side} ${full.amount} ${full.symbol}`);
|
|
34
|
+
return full;
|
|
35
|
+
}
|
|
36
|
+
/** Update intent status after exchange response. */
|
|
37
|
+
updateIntent(clientOrderId, update) {
|
|
38
|
+
const intent = this.intents.get(clientOrderId);
|
|
39
|
+
if (!intent) {
|
|
40
|
+
logger.warn(TAG, `Intent not found for update: ${clientOrderId}`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (update.status)
|
|
44
|
+
intent.status = update.status;
|
|
45
|
+
if (update.exchangeOrderId)
|
|
46
|
+
intent.exchangeOrderId = update.exchangeOrderId;
|
|
47
|
+
if (update.error)
|
|
48
|
+
intent.error = update.error;
|
|
49
|
+
if (update.status === 'confirmed' || update.status === 'failed') {
|
|
50
|
+
intent.resolvedAt = new Date().toISOString();
|
|
51
|
+
}
|
|
52
|
+
this.appendLine(intent);
|
|
53
|
+
}
|
|
54
|
+
/** Get all unresolved intents (pending or submitted). */
|
|
55
|
+
getUnresolved() {
|
|
56
|
+
return Array.from(this.intents.values()).filter(i => i.status === 'pending' || i.status === 'submitted');
|
|
57
|
+
}
|
|
58
|
+
/** Get intent by clientOrderId. */
|
|
59
|
+
getIntent(clientOrderId) {
|
|
60
|
+
return this.intents.get(clientOrderId);
|
|
61
|
+
}
|
|
62
|
+
/** Get the journal file path. */
|
|
63
|
+
getFilePath() {
|
|
64
|
+
return this.filePath;
|
|
65
|
+
}
|
|
66
|
+
// ---- Internal ----
|
|
67
|
+
/** Load existing intents from JSONL file. Later entries override earlier ones (last-write-wins). */
|
|
68
|
+
loadExisting() {
|
|
69
|
+
if (!existsSync(this.filePath))
|
|
70
|
+
return;
|
|
71
|
+
try {
|
|
72
|
+
const content = readFileSync(this.filePath, 'utf-8');
|
|
73
|
+
const lines = content.split('\n').filter(l => l.trim());
|
|
74
|
+
for (const line of lines) {
|
|
75
|
+
try {
|
|
76
|
+
const intent = JSON.parse(line);
|
|
77
|
+
if (intent.clientOrderId) {
|
|
78
|
+
this.intents.set(intent.clientOrderId, intent);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Skip malformed lines
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const unresolved = this.getUnresolved();
|
|
86
|
+
if (unresolved.length > 0) {
|
|
87
|
+
logger.warn(TAG, `Loaded ${this.intents.size} intents, ${unresolved.length} unresolved`);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
logger.info(TAG, `Loaded ${this.intents.size} intents, all resolved`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
logger.error(TAG, `Failed to load intent journal: ${formatError(err)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Append a line to the JSONL file. */
|
|
98
|
+
appendLine(intent) {
|
|
99
|
+
try {
|
|
100
|
+
appendFileSync(this.filePath, JSON.stringify(intent) + '\n');
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
logger.error(TAG, `Failed to write intent: ${formatError(err)}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Compact the journal — rewrite with only the latest state of each intent.
|
|
108
|
+
* Call periodically (e.g., daily) to prevent unbounded file growth.
|
|
109
|
+
*/
|
|
110
|
+
compact() {
|
|
111
|
+
try {
|
|
112
|
+
const lines = Array.from(this.intents.values())
|
|
113
|
+
.map(i => JSON.stringify(i))
|
|
114
|
+
.join('\n') + '\n';
|
|
115
|
+
writeFileSync(this.filePath, lines);
|
|
116
|
+
logger.info(TAG, `Compacted journal: ${this.intents.size} intents`);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
logger.error(TAG, `Failed to compact journal: ${formatError(err)}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|