@reefclaw/openclaw-plugin 0.1.0
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/.gitignore +8 -0
- package/audit/mode-transition-audit.d.ts +11 -0
- package/audit/mode-transition-audit.js +29 -0
- package/balance-utils.d.ts +36 -0
- package/balance-utils.js +98 -0
- package/bridge/bridge.d.ts +109 -0
- package/bridge/bridge.js +1036 -0
- package/bridge/config.d.ts +43 -0
- package/bridge/config.js +176 -0
- package/bridge/connector.d.ts +52 -0
- package/bridge/connector.js +293 -0
- package/bridge/event-replay-buffer.d.ts +43 -0
- package/bridge/event-replay-buffer.js +109 -0
- package/bridge/gateway/event-parser.d.ts +209 -0
- package/bridge/gateway/event-parser.js +793 -0
- package/bridge/gateway/gateway-config.d.ts +39 -0
- package/bridge/gateway/gateway-config.js +86 -0
- package/bridge/gateway/gateway-http-client.d.ts +50 -0
- package/bridge/gateway/gateway-http-client.js +165 -0
- package/bridge/gateway/gateway-ws-client.d.ts +116 -0
- package/bridge/gateway/gateway-ws-client.js +417 -0
- package/bridge/gateway/poller.d.ts +146 -0
- package/bridge/gateway/poller.js +505 -0
- package/bridge/gateway/tool-discovery.d.ts +25 -0
- package/bridge/gateway/tool-discovery.js +198 -0
- package/bridge/index.d.ts +2 -0
- package/bridge/index.js +253 -0
- package/bridge/logger.d.ts +2 -0
- package/bridge/logger.js +2 -0
- package/bridge/provider.d.ts +156 -0
- package/bridge/provider.js +2 -0
- package/bridge/providers/emergency-commands.d.ts +54 -0
- package/bridge/providers/emergency-commands.js +235 -0
- package/bridge/providers/gateway.d.ts +322 -0
- package/bridge/providers/gateway.js +2302 -0
- package/bridge/providers/mock.d.ts +37 -0
- package/bridge/providers/mock.js +385 -0
- package/bridge/providers/onboarding-commands.d.ts +83 -0
- package/bridge/providers/onboarding-commands.js +213 -0
- package/bridge/providers/risk-calculator.d.ts +96 -0
- package/bridge/providers/risk-calculator.js +369 -0
- package/bridge/setup.d.ts +32 -0
- package/bridge/setup.js +226 -0
- package/bridge/types.d.ts +584 -0
- package/bridge/types.js +50 -0
- package/bridge/utils/reconnect.d.ts +6 -0
- package/bridge/utils/reconnect.js +6 -0
- package/bridge/utils/skill-signing.d.ts +51 -0
- package/bridge/utils/skill-signing.js +138 -0
- package/bridge/utils/skill-version.d.ts +17 -0
- package/bridge/utils/skill-version.js +74 -0
- package/ccxt/binance-ban-gate.d.ts +47 -0
- package/ccxt/binance-ban-gate.js +409 -0
- package/ccxt/binance-private.d.ts +325 -0
- package/ccxt/binance-private.js +1415 -0
- package/ccxt/binance-public.d.ts +18 -0
- package/ccxt/binance-public.js +147 -0
- package/config/agent-config-client.d.ts +55 -0
- package/config/agent-config-client.js +145 -0
- package/config/agent-config-poller.d.ts +25 -0
- package/config/agent-config-poller.js +100 -0
- package/config/brackets-config.d.ts +22 -0
- package/config/brackets-config.js +58 -0
- package/config/gate-store.d.ts +18 -0
- package/config/gate-store.js +61 -0
- package/config/plugin-config-io.d.ts +181 -0
- package/config/plugin-config-io.js +84 -0
- package/config/position-review-config.d.ts +35 -0
- package/config/position-review-config.js +105 -0
- package/config/tool-gate.d.ts +53 -0
- package/config/tool-gate.js +125 -0
- package/config/user-data-stream-config.d.ts +85 -0
- package/config/user-data-stream-config.js +224 -0
- package/connector-supervisor.d.ts +36 -0
- package/connector-supervisor.js +149 -0
- package/exchange-adapter.d.ts +49 -0
- package/exchange-adapter.js +4 -0
- package/index.d.ts +30 -0
- package/index.js +2030 -0
- package/ingest/pending-entry-metadata.d.ts +52 -0
- package/ingest/pending-entry-metadata.js +182 -0
- package/ingest/position-auto-capture.d.ts +98 -0
- package/ingest/position-auto-capture.js +394 -0
- package/ingest/position-decisions-client.d.ts +318 -0
- package/ingest/position-decisions-client.js +296 -0
- package/ingest/reconcile-db-vs-exchange.d.ts +13 -0
- package/ingest/reconcile-db-vs-exchange.js +114 -0
- package/ingest/reconciler-cleanup.d.ts +37 -0
- package/ingest/reconciler-cleanup.js +147 -0
- package/ingest/rest-gap-filler.d.ts +191 -0
- package/ingest/rest-gap-filler.js +565 -0
- package/ingest/touched-symbols-store.d.ts +25 -0
- package/ingest/touched-symbols-store.js +96 -0
- package/ingest/trade-store-client.d.ts +40 -0
- package/ingest/trade-store-client.js +125 -0
- package/ingest/ws-ingest.d.ts +43 -0
- package/ingest/ws-ingest.js +126 -0
- package/learning/setup-family.d.ts +21 -0
- package/learning/setup-family.js +103 -0
- package/lifecycle/install-signal-handlers.d.ts +33 -0
- package/lifecycle/install-signal-handlers.js +112 -0
- package/lifecycle/shutdown-coordinator.d.ts +43 -0
- package/lifecycle/shutdown-coordinator.js +131 -0
- package/live/bracket-id.d.ts +18 -0
- package/live/bracket-id.js +81 -0
- package/live/bracket-ledger.d.ts +54 -0
- package/live/bracket-ledger.js +267 -0
- package/live/bracket-manager.d.ts +82 -0
- package/live/bracket-manager.js +478 -0
- package/live/bracket-params.d.ts +22 -0
- package/live/bracket-params.js +124 -0
- package/live/bracket-reconciler.d.ts +95 -0
- package/live/bracket-reconciler.js +573 -0
- package/live/bracket-types.d.ts +102 -0
- package/live/bracket-types.js +8 -0
- package/live/deposit-tracker.d.ts +62 -0
- package/live/deposit-tracker.js +97 -0
- package/live/emergency-controls.d.ts +32 -0
- package/live/emergency-controls.js +226 -0
- package/live/exchange-errors.d.ts +12 -0
- package/live/exchange-errors.js +130 -0
- package/live/exchange-info-cache.d.ts +35 -0
- package/live/exchange-info-cache.js +119 -0
- package/live/fact-subscriber.d.ts +78 -0
- package/live/fact-subscriber.js +182 -0
- package/live/intent-journal.d.ts +42 -0
- package/live/intent-journal.js +122 -0
- package/live/listen-key-manager.d.ts +70 -0
- package/live/listen-key-manager.js +169 -0
- package/live/live-adapter.d.ts +264 -0
- package/live/live-adapter.js +1665 -0
- package/live/live-balance-enricher.d.ts +32 -0
- package/live/live-balance-enricher.js +104 -0
- package/live/live-bracket-api.d.ts +13 -0
- package/live/live-bracket-api.js +20 -0
- package/live/live-state-store.d.ts +194 -0
- package/live/live-state-store.js +450 -0
- package/live/local-signal-service.d.ts +57 -0
- package/live/local-signal-service.js +146 -0
- package/live/local-strategy-evaluator.d.ts +62 -0
- package/live/local-strategy-evaluator.js +127 -0
- package/live/microstructure-assembler.d.ts +54 -0
- package/live/microstructure-assembler.js +148 -0
- package/live/order-poller.d.ts +29 -0
- package/live/order-poller.js +125 -0
- package/live/position-state-store.d.ts +83 -0
- package/live/position-state-store.js +237 -0
- package/live/proposal-decision-listener.d.ts +64 -0
- package/live/proposal-decision-listener.js +288 -0
- package/live/proposal-manager.d.ts +76 -0
- package/live/proposal-manager.js +140 -0
- package/live/rate-limiter.d.ts +47 -0
- package/live/rate-limiter.js +159 -0
- package/live/reconciler.d.ts +39 -0
- package/live/reconciler.js +175 -0
- package/live/setup-buckets.d.ts +7 -0
- package/live/setup-buckets.js +33 -0
- package/live/slippage-tracker.d.ts +45 -0
- package/live/slippage-tracker.js +78 -0
- package/live/stop-watcher.d.ts +34 -0
- package/live/stop-watcher.js +158 -0
- package/live/user-data-active-probe.d.ts +54 -0
- package/live/user-data-active-probe.js +180 -0
- package/live/user-data-stream-controller.d.ts +200 -0
- package/live/user-data-stream-controller.js +579 -0
- package/live/user-data-stream-ws.d.ts +22 -0
- package/live/user-data-stream-ws.js +63 -0
- package/live/user-data-stream.d.ts +243 -0
- package/live/user-data-stream.js +704 -0
- package/logger.d.ts +2 -0
- package/logger.js +2 -0
- package/mfe.d.ts +21 -0
- package/mfe.js +68 -0
- package/onboarding/mode-ladder.d.ts +1 -0
- package/onboarding/mode-ladder.js +3 -0
- package/onboarding/runtime.d.ts +71 -0
- package/onboarding/runtime.js +153 -0
- package/openclaw.plugin.json +94 -0
- package/package.json +27 -0
- package/paper-adapter.d.ts +24 -0
- package/paper-adapter.js +91 -0
- package/persistence/state-manager.d.ts +42 -0
- package/persistence/state-manager.js +164 -0
- package/pinned-plan.d.ts +9 -0
- package/pinned-plan.js +23 -0
- package/risk/pre-trade-check.d.ts +38 -0
- package/risk/pre-trade-check.js +345 -0
- package/risk/pre-trade-types.d.ts +60 -0
- package/risk/pre-trade-types.js +3 -0
- package/scripts/assemble.mjs +114 -0
- package/shadow/shadow-tracker.d.ts +36 -0
- package/shadow/shadow-tracker.js +151 -0
- package/shadow/types.d.ts +42 -0
- package/shadow/types.js +20 -0
- package/shared/indicators-extended.d.ts +52 -0
- package/shared/indicators-extended.js +291 -0
- package/shared/indicators.d.ts +15 -0
- package/shared/indicators.js +114 -0
- package/signals/conditions/registry.d.ts +16 -0
- package/signals/conditions/registry.js +1274 -0
- package/signals/conditions/types.d.ts +1 -0
- package/signals/conditions/types.js +4 -0
- package/signals/direction-rules.d.ts +3 -0
- package/signals/direction-rules.js +24 -0
- package/signals/entry-rules.d.ts +6 -0
- package/signals/entry-rules.js +33 -0
- package/signals/serialize-context.d.ts +4 -0
- package/signals/serialize-context.js +39 -0
- package/signals/stop-rules.d.ts +3 -0
- package/signals/stop-rules.js +48 -0
- package/signals/strategy-adapter.d.ts +14 -0
- package/signals/strategy-adapter.js +122 -0
- package/signals/types.d.ts +1 -0
- package/signals/types.js +8 -0
- package/simulator/exchange-simulator.d.ts +93 -0
- package/simulator/exchange-simulator.js +684 -0
- package/simulator/fill-engine.d.ts +53 -0
- package/simulator/fill-engine.js +276 -0
- package/simulator/paper-market-feed.d.ts +26 -0
- package/simulator/paper-market-feed.js +104 -0
- package/simulator/realistic-fills.d.ts +59 -0
- package/simulator/realistic-fills.js +175 -0
- package/simulator/types.d.ts +219 -0
- package/simulator/types.js +43 -0
- package/skills/reefclaw/SKILL.md +97 -0
- package/strategy/builtin-strategies.d.ts +2 -0
- package/strategy/builtin-strategies.js +109 -0
- package/strategy/condition-registry.d.ts +3 -0
- package/strategy/condition-registry.js +153 -0
- package/strategy/evaluator.d.ts +67 -0
- package/strategy/evaluator.js +93 -0
- package/tools/assessment-validation.d.ts +118 -0
- package/tools/assessment-validation.js +415 -0
- package/tools/attach-brackets.d.ts +34 -0
- package/tools/attach-brackets.js +363 -0
- package/tools/audit-bracket-protection.d.ts +49 -0
- package/tools/audit-bracket-protection.js +527 -0
- package/tools/cancel-all-orders.d.ts +7 -0
- package/tools/cancel-all-orders.js +5 -0
- package/tools/cancel-order.d.ts +10 -0
- package/tools/cancel-order.js +14 -0
- package/tools/check-position-health.d.ts +46 -0
- package/tools/check-position-health.js +194 -0
- package/tools/clear-exchange-credentials.d.ts +24 -0
- package/tools/clear-exchange-credentials.js +70 -0
- package/tools/close-position.d.ts +22 -0
- package/tools/close-position.js +449 -0
- package/tools/create-order.d.ts +54 -0
- package/tools/create-order.js +338 -0
- package/tools/exit-gate.d.ts +58 -0
- package/tools/exit-gate.js +162 -0
- package/tools/fetch-balance.d.ts +5 -0
- package/tools/fetch-balance.js +4 -0
- package/tools/fetch-ohlcv.d.ts +11 -0
- package/tools/fetch-ohlcv.js +8 -0
- package/tools/fetch-open-orders.d.ts +7 -0
- package/tools/fetch-open-orders.js +4 -0
- package/tools/fetch-positions.d.ts +7 -0
- package/tools/fetch-positions.js +4 -0
- package/tools/fetch-ticker.d.ts +11 -0
- package/tools/fetch-ticker.js +5 -0
- package/tools/get-agent-profile.d.ts +4 -0
- package/tools/get-agent-profile.js +6 -0
- package/tools/get-analytics.d.ts +6 -0
- package/tools/get-analytics.js +7 -0
- package/tools/get-backtest.d.ts +12 -0
- package/tools/get-backtest.js +91 -0
- package/tools/get-basis.d.ts +7 -0
- package/tools/get-basis.js +7 -0
- package/tools/get-bracket-config.d.ts +11 -0
- package/tools/get-bracket-config.js +24 -0
- package/tools/get-cascade-risk.d.ts +7 -0
- package/tools/get-cascade-risk.js +8 -0
- package/tools/get-crypto-metrics.d.ts +18 -0
- package/tools/get-crypto-metrics.js +45 -0
- package/tools/get-cvd.d.ts +6 -0
- package/tools/get-cvd.js +6 -0
- package/tools/get-divergences.d.ts +6 -0
- package/tools/get-divergences.js +6 -0
- package/tools/get-funding-context.d.ts +6 -0
- package/tools/get-funding-context.js +16 -0
- package/tools/get-liquidation-levels.d.ts +7 -0
- package/tools/get-liquidation-levels.js +7 -0
- package/tools/get-liquidation-pulse.d.ts +9 -0
- package/tools/get-liquidation-pulse.js +22 -0
- package/tools/get-market-breadth.d.ts +6 -0
- package/tools/get-market-breadth.js +8 -0
- package/tools/get-market-intel.d.ts +19 -0
- package/tools/get-market-intel.js +116 -0
- package/tools/get-market-structure.d.ts +47 -0
- package/tools/get-market-structure.js +198 -0
- package/tools/get-my-mined-patterns.d.ts +20 -0
- package/tools/get-my-mined-patterns.js +61 -0
- package/tools/get-my-proposed-learnings.d.ts +20 -0
- package/tools/get-my-proposed-learnings.js +55 -0
- package/tools/get-my-recent-reviews.d.ts +22 -0
- package/tools/get-my-recent-reviews.js +66 -0
- package/tools/get-orderbook.d.ts +21 -0
- package/tools/get-orderbook.js +32 -0
- package/tools/get-pattern-scan.d.ts +7 -0
- package/tools/get-pattern-scan.js +8 -0
- package/tools/get-regime.d.ts +6 -0
- package/tools/get-regime.js +7 -0
- package/tools/get-relevant-learnings.d.ts +21 -0
- package/tools/get-relevant-learnings.js +65 -0
- package/tools/get-resting-liquidity.d.ts +6 -0
- package/tools/get-resting-liquidity.js +11 -0
- package/tools/get-risk-scenario.d.ts +29 -0
- package/tools/get-risk-scenario.js +47 -0
- package/tools/get-risk-summary.d.ts +51 -0
- package/tools/get-risk-summary.js +118 -0
- package/tools/get-sentiment.d.ts +4 -0
- package/tools/get-sentiment.js +6 -0
- package/tools/get-session-review.d.ts +7 -0
- package/tools/get-session-review.js +8 -0
- package/tools/get-setup-detail.d.ts +7 -0
- package/tools/get-setup-detail.js +303 -0
- package/tools/get-signals.d.ts +15 -0
- package/tools/get-signals.js +54 -0
- package/tools/get-sizing.d.ts +6 -0
- package/tools/get-sizing.js +6 -0
- package/tools/get-trade-feedback.d.ts +7 -0
- package/tools/get-trade-feedback.js +8 -0
- package/tools/get-trade-flow.d.ts +7 -0
- package/tools/get-trade-flow.js +7 -0
- package/tools/get-volume-analysis.d.ts +21 -0
- package/tools/get-volume-analysis.js +74 -0
- package/tools/get-volume-profile.d.ts +7 -0
- package/tools/get-volume-profile.js +7 -0
- package/tools/helpers.d.ts +25 -0
- package/tools/helpers.js +38 -0
- package/tools/intel-api.d.ts +14 -0
- package/tools/intel-api.js +52 -0
- package/tools/intel-cache.d.ts +25 -0
- package/tools/intel-cache.js +133 -0
- package/tools/list-strategies.d.ts +7 -0
- package/tools/list-strategies.js +6 -0
- package/tools/modify-stop.d.ts +17 -0
- package/tools/modify-stop.js +81 -0
- package/tools/modify-target.d.ts +17 -0
- package/tools/modify-target.js +71 -0
- package/tools/propose-learning.d.ts +22 -0
- package/tools/propose-learning.js +65 -0
- package/tools/query-review-outcomes.d.ts +30 -0
- package/tools/query-review-outcomes.js +64 -0
- package/tools/query-trades.d.ts +21 -0
- package/tools/query-trades.js +37 -0
- package/tools/record-position-reviews.d.ts +38 -0
- package/tools/record-position-reviews.js +147 -0
- package/tools/save-strategy.d.ts +16 -0
- package/tools/save-strategy.js +46 -0
- package/tools/scan-pairs.d.ts +18 -0
- package/tools/scan-pairs.js +220 -0
- package/tools/score-setup.d.ts +31 -0
- package/tools/score-setup.js +268 -0
- package/tools/set-bracket-requirement.d.ts +18 -0
- package/tools/set-bracket-requirement.js +81 -0
- package/tools/set-exchange-credentials.d.ts +25 -0
- package/tools/set-exchange-credentials.js +80 -0
- package/tools/set-trading-mode.d.ts +26 -0
- package/tools/set-trading-mode.js +135 -0
- package/tools/test-exchange-credentials.d.ts +16 -0
- package/tools/test-exchange-credentials.js +100 -0
- package/tools/toggle-strategy.d.ts +8 -0
- package/tools/toggle-strategy.js +8 -0
- package/trading-params-cache.d.ts +26 -0
- package/trading-params-cache.js +52 -0
- package/types.d.ts +110 -0
- package/types.js +7 -0
- package/util/plugin-paths.d.ts +3 -0
- package/util/plugin-paths.js +15 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// Stateless emergency command handlers extracted from GatewayProvider.
|
|
2
|
+
// Functions return results — callers apply state changes (agentMode, emitAgentState).
|
|
3
|
+
import { logger } from '../logger.js';
|
|
4
|
+
const TAG = 'emergency';
|
|
5
|
+
/** Distinct human-readable reasons from the rejected entries of an
|
|
6
|
+
* allSettled batch — surfaced in EmergencyResult.details so the dashboard
|
|
7
|
+
* shows WHY a close/cancel failed, not just that it did. Capped so a
|
|
8
|
+
* many-position failure doesn't produce an unreadable wall. */
|
|
9
|
+
function rejectionReasons(results, max = 3) {
|
|
10
|
+
const reasons = results
|
|
11
|
+
.filter((r) => r.status === 'rejected')
|
|
12
|
+
.map((r) => (r.reason instanceof Error ? r.reason.message : String(r.reason)));
|
|
13
|
+
const distinct = [...new Set(reasons)];
|
|
14
|
+
const shown = distinct.slice(0, max).join('; ');
|
|
15
|
+
return distinct.length > max ? `${shown}; …` : shown;
|
|
16
|
+
}
|
|
17
|
+
// ---- Async commands (need HTTP + tools) ----
|
|
18
|
+
/**
|
|
19
|
+
* Kill: stop the agent and cancel WORKING orders, while PRESERVING protective
|
|
20
|
+
* brackets. A protective order (exchange-side STOP_MARKET / TAKE_PROFIT_MARKET)
|
|
21
|
+
* is the only thing guarding an open position once the agent is halted (nothing
|
|
22
|
+
* will re-attach a stop). Kill cancels only the agent's working orders (pending
|
|
23
|
+
* entries / adds), PORTFOLIO-WIDE, and keeps every protective leg in place. To
|
|
24
|
+
* actually exit a position, use Flatten.
|
|
25
|
+
*
|
|
26
|
+
* Bracket preservation is defence-in-depth: the plugin's `cancelAllOrders`
|
|
27
|
+
* already skips ReefClaw-cid brackets, so the old per-symbol kill never stripped
|
|
28
|
+
* a ReefClaw bracket — this skill-side `protective` filter is BROADER (it also
|
|
29
|
+
* preserves externally-placed / non-ReefClaw-cid stops) and portfolio-wide.
|
|
30
|
+
*
|
|
31
|
+
* Caller must set agentMode = 'STOPPED' after this returns, and should pass a
|
|
32
|
+
* FRESH all-symbols order snapshot (see the gateway wrapper).
|
|
33
|
+
*/
|
|
34
|
+
export async function executeKill(ctx, openOrders) {
|
|
35
|
+
if (!ctx.http) {
|
|
36
|
+
return { action: 'kill', executed: false, timestamp: new Date().toISOString(), details: 'HTTP client not initialized' };
|
|
37
|
+
}
|
|
38
|
+
// Protective = a stop/TP bracket (reduceOnly OR closePosition OR stop-type;
|
|
39
|
+
// see mapCcxtOrder/isProtectiveOrder). Working = everything else (entries /
|
|
40
|
+
// adds). Preserve protective so an open position is never left naked.
|
|
41
|
+
const working = openOrders.filter((o) => o.protective !== true);
|
|
42
|
+
const preserved = openOrders.length - working.length;
|
|
43
|
+
const preservedNote = preserved > 0 ? `; preserved ${preserved} protective bracket order(s)` : '';
|
|
44
|
+
// executed:true ONLY when the agent halt (the caller sets STOPPED
|
|
45
|
+
// unconditionally after this returns) is accompanied by a clean order
|
|
46
|
+
// sweep. Failed/impossible cancels leave live working orders behind an
|
|
47
|
+
// operator who just hit Kill — that must render as Failed + details on the
|
|
48
|
+
// dashboard, never a clean green.
|
|
49
|
+
let executed = true;
|
|
50
|
+
let details;
|
|
51
|
+
const cancelTool = ctx.toolMap.cancel_order;
|
|
52
|
+
if (cancelTool && working.length > 0) {
|
|
53
|
+
// Primary: cancel each working order individually (portfolio-wide), so the
|
|
54
|
+
// protective brackets are never touched.
|
|
55
|
+
const results = await Promise.allSettled(working.map((order) => ctx.http.invokeEmergency(cancelTool, { id: order.id, symbol: order.symbol })));
|
|
56
|
+
const succeeded = results.filter((r) => r.status === 'fulfilled').length;
|
|
57
|
+
const failed = results.filter((r) => r.status === 'rejected').length;
|
|
58
|
+
if (failed > 0) {
|
|
59
|
+
executed = false;
|
|
60
|
+
details = `Agent halted, but cancelled only ${succeeded}/${working.length} working order(s) (${failed} failed: ${rejectionReasons(results)})${preservedNote}`;
|
|
61
|
+
logger.error(TAG, details);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
details = `Cancelled ${succeeded}/${working.length} working order(s)${preservedNote}`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else if (working.length === 0) {
|
|
68
|
+
details = preserved > 0
|
|
69
|
+
? `No working orders to cancel${preservedNote}`
|
|
70
|
+
: 'No open orders to cancel';
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// Degenerate fallback: cancel_order wasn't discovered. cancel_all_orders is
|
|
74
|
+
// per-symbol; the plugin's implementation preserves ReefClaw-cid brackets,
|
|
75
|
+
// but (unlike the skill `protective` filter above) it can't spare an
|
|
76
|
+
// externally-placed / non-ReefClaw-cid stop — so warn.
|
|
77
|
+
const cancelAllTool = ctx.toolMap.cancel_all_orders;
|
|
78
|
+
if (cancelAllTool) {
|
|
79
|
+
const symbols = [...new Set(working.map((o) => o.symbol))];
|
|
80
|
+
const results = await Promise.allSettled(symbols.map((s) => ctx.http.invokeEmergency(cancelAllTool, { symbol: s })));
|
|
81
|
+
const failed = results.filter((r) => r.status === 'rejected').length;
|
|
82
|
+
if (failed > 0) {
|
|
83
|
+
executed = false;
|
|
84
|
+
details = `Agent halted, but cancel_all_orders failed on ${failed}/${symbols.length} symbol(s) (${rejectionReasons(results)}) — some orders may remain live`;
|
|
85
|
+
logger.error(TAG, details);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
details = `cancel_order unavailable — used cancel_all_orders on ${symbols.length} symbol(s) (plugin preserves ReefClaw-cid brackets; a non-cid/external stop is not spared)`;
|
|
89
|
+
logger.warn(TAG, details);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
executed = false;
|
|
94
|
+
details = 'No cancel tool available (neither cancel_order nor cancel_all_orders discovered) — agent halted, orders left in place';
|
|
95
|
+
logger.warn(TAG, details);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { action: 'kill', executed, timestamp: new Date().toISOString(), details };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Flatten: close all open positions at market.
|
|
102
|
+
* Caller must set agentMode = 'STOPPED' after this returns.
|
|
103
|
+
*/
|
|
104
|
+
export async function executeFlatten(ctx, cachedPositions) {
|
|
105
|
+
if (!ctx.http) {
|
|
106
|
+
return { action: 'flatten', executed: false, timestamp: new Date().toISOString(), details: 'HTTP client not initialized' };
|
|
107
|
+
}
|
|
108
|
+
// Try to fetch fresh positions; fall back to cached on failure.
|
|
109
|
+
// Flatten is PORTFOLIO-WIDE — fetch ALL symbols (no symbol filter). Passing
|
|
110
|
+
// ctx.symbol here narrowed the result to a single symbol, so positions on
|
|
111
|
+
// every other symbol were silently skipped and never closed. The cached
|
|
112
|
+
// fallback (this.positions) is already all-symbols.
|
|
113
|
+
let positions = cachedPositions;
|
|
114
|
+
const fetchTool = ctx.toolMap.fetch_positions;
|
|
115
|
+
if (fetchTool) {
|
|
116
|
+
try {
|
|
117
|
+
const result = await ctx.http.invokeEmergency(fetchTool, {});
|
|
118
|
+
if (Array.isArray(result.data)) {
|
|
119
|
+
positions = result.data;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// Mirror gateway.ts fetchFreshPositions: a non-array response must
|
|
123
|
+
// fall back to cached — letting it through would make the .filter
|
|
124
|
+
// below throw and abort the entire flatten.
|
|
125
|
+
logger.warn(TAG, `fetch_positions returned non-array (${typeof result.data}) — using cached positions`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
logger.warn(TAG, `Fresh position fetch failed, using cached: ${err instanceof Error ? err.message : String(err)}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Filter to non-zero positions
|
|
133
|
+
const openPositions = positions.filter((p) => (p.contracts ?? 0) !== 0);
|
|
134
|
+
if (openPositions.length === 0) {
|
|
135
|
+
return { action: 'flatten', executed: true, timestamp: new Date().toISOString(), details: 'No positions to close' };
|
|
136
|
+
}
|
|
137
|
+
// Close each position
|
|
138
|
+
const closeTool = ctx.toolMap.close_position;
|
|
139
|
+
const createTool = ctx.toolMap.create_order;
|
|
140
|
+
const results = await Promise.allSettled(openPositions.map((pos) => {
|
|
141
|
+
if (closeTool) {
|
|
142
|
+
// reason='operator_command' is MANDATORY: close_position's schema gate
|
|
143
|
+
// (validateClosePosition) rejects any close lacking a reason, and only
|
|
144
|
+
// operator_command bypasses the assessment requirement. Without it the
|
|
145
|
+
// operator Flatten is silently rejected at the API boundary and the
|
|
146
|
+
// position never closes. See plugin/src/tools/close-position.ts.
|
|
147
|
+
return ctx.http.invokeEmergency(closeTool, { symbol: pos.symbol, reason: 'operator_command' });
|
|
148
|
+
}
|
|
149
|
+
else if (createTool) {
|
|
150
|
+
// Fallback: opposing market order. reduceOnly is MANDATORY — this
|
|
151
|
+
// order may only shrink the position. Without it, a race (position
|
|
152
|
+
// already closed by a bracket fill / a smaller live quantity than the
|
|
153
|
+
// snapshot) turns the "close" into a NEW opposing position — a flip.
|
|
154
|
+
const side = pos.side === 'long' ? 'sell' : 'buy';
|
|
155
|
+
return ctx.http.invokeEmergency(createTool, {
|
|
156
|
+
symbol: pos.symbol,
|
|
157
|
+
type: 'market',
|
|
158
|
+
side,
|
|
159
|
+
amount: Math.abs(pos.contracts ?? 0),
|
|
160
|
+
reduceOnly: true,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
return Promise.reject(new Error('No close_position or create_order tool available'));
|
|
165
|
+
}
|
|
166
|
+
}));
|
|
167
|
+
const succeeded = results.filter((r) => r.status === 'fulfilled').length;
|
|
168
|
+
const failed = results.filter((r) => r.status === 'rejected').length;
|
|
169
|
+
// Any un-closed position = NOT executed. "Closed 1/5" rendering as a green
|
|
170
|
+
// "Flattened" would leave the operator believing they're flat while 4 real
|
|
171
|
+
// positions stay live — the dashboard must show Failed + which/why.
|
|
172
|
+
if (succeeded === 0 && openPositions.length > 0) {
|
|
173
|
+
const details = `Failed to close any of ${openPositions.length} position(s): ${rejectionReasons(results)}`;
|
|
174
|
+
logger.error(TAG, details);
|
|
175
|
+
return { action: 'flatten', executed: false, timestamp: new Date().toISOString(), details };
|
|
176
|
+
}
|
|
177
|
+
if (failed > 0) {
|
|
178
|
+
const details = `Closed ${succeeded}/${openPositions.length} positions (${failed} failed: ${rejectionReasons(results)})`;
|
|
179
|
+
logger.error(TAG, details);
|
|
180
|
+
return { action: 'flatten', executed: false, timestamp: new Date().toISOString(), details };
|
|
181
|
+
}
|
|
182
|
+
const details = `Closed ${succeeded}/${openPositions.length} positions`;
|
|
183
|
+
return { action: 'flatten', executed: true, timestamp: new Date().toISOString(), details };
|
|
184
|
+
}
|
|
185
|
+
// ---- Sync commands (pure state transitions) ----
|
|
186
|
+
/**
|
|
187
|
+
* Pause: prevent new entries, keep existing positions.
|
|
188
|
+
* Returns the new agent mode if the transition is valid.
|
|
189
|
+
*/
|
|
190
|
+
export function executePause(currentMode) {
|
|
191
|
+
if (currentMode === 'PAUSED') {
|
|
192
|
+
return {
|
|
193
|
+
result: { action: 'pause', executed: true, timestamp: new Date().toISOString(), details: 'Already paused' },
|
|
194
|
+
newMode: 'PAUSED',
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (currentMode === 'STOPPED') {
|
|
198
|
+
return {
|
|
199
|
+
result: { action: 'pause', executed: false, timestamp: new Date().toISOString(), details: 'Cannot pause — agent is stopped' },
|
|
200
|
+
newMode: 'STOPPED',
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
result: { action: 'pause', executed: true, timestamp: new Date().toISOString(), details: 'Trading paused' },
|
|
205
|
+
newMode: 'PAUSED',
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Resume: re-enable entries.
|
|
210
|
+
* Returns the new agent mode if the transition is valid.
|
|
211
|
+
*/
|
|
212
|
+
export function executeResume(currentMode) {
|
|
213
|
+
if (currentMode === 'ACTIVE') {
|
|
214
|
+
return {
|
|
215
|
+
result: { action: 'resume', executed: true, timestamp: new Date().toISOString(), details: 'Already active' },
|
|
216
|
+
newMode: 'ACTIVE',
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
if (currentMode === 'STOPPED') {
|
|
220
|
+
return {
|
|
221
|
+
result: { action: 'resume', executed: true, timestamp: new Date().toISOString(), details: 'Trading resumed from stopped state' },
|
|
222
|
+
newMode: 'ACTIVE',
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (currentMode === 'ERROR') {
|
|
226
|
+
return {
|
|
227
|
+
result: { action: 'resume', executed: false, timestamp: new Date().toISOString(), details: 'Cannot resume — agent is in error state' },
|
|
228
|
+
newMode: 'ERROR',
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
result: { action: 'resume', executed: true, timestamp: new Date().toISOString(), details: 'Trading resumed' },
|
|
233
|
+
newMode: 'ACTIVE',
|
|
234
|
+
};
|
|
235
|
+
}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import type { OpenClawProvider, ProviderEvents, ProviderEventName } from '../provider.js';
|
|
2
|
+
import type { EmergencyAction, ReconciliationSnapshot, TradingMode } from '../types.js';
|
|
3
|
+
import type { GatewayConfig } from '../gateway/gateway-config.js';
|
|
4
|
+
import { type GetBracketConfigOutcome, type SetBracketRequirementOutcome, type BracketRequirementFlag, type SetTradingModeOutcome, type SetExchangeCredentialsOutcome, type TestExchangeCredentialsOutcome, type ClearExchangeCredentialsOutcome } from './onboarding-commands.js';
|
|
5
|
+
/** Decide whether the session-start-NAV anchor needs to be (re)set. Pure so it
|
|
6
|
+
* can be unit-tested without the full provider. Returns true when the anchor
|
|
7
|
+
* has never been set, OR — for PAPER only — when it belongs to a previous UTC
|
|
8
|
+
* day (so Day P&L re-anchors at midnight). Live/shadow anchor once and then
|
|
9
|
+
* sync to the plugin's Binance income-endpoint value, so they never
|
|
10
|
+
* self-re-anchor here. */
|
|
11
|
+
export declare function shouldAnchorSessionNav(args: {
|
|
12
|
+
sessionStartNav: number;
|
|
13
|
+
sessionDate: string | null;
|
|
14
|
+
today: string;
|
|
15
|
+
mode: TradingMode;
|
|
16
|
+
}): boolean;
|
|
17
|
+
export declare class GatewayProvider implements OpenClawProvider {
|
|
18
|
+
private readonly config;
|
|
19
|
+
private http;
|
|
20
|
+
private wsClient;
|
|
21
|
+
private poller;
|
|
22
|
+
private eventParser;
|
|
23
|
+
private toolMap;
|
|
24
|
+
private agentMode;
|
|
25
|
+
private lastKnownStrategy;
|
|
26
|
+
/** Agent display name + live model id, resolved from the gateway (best-effort).
|
|
27
|
+
* Surfaced in agent_state so the dashboard header can show the real agent +
|
|
28
|
+
* model instead of a hardcoded placeholder. Empty until the gateway answers. */
|
|
29
|
+
private agentIdentity;
|
|
30
|
+
/** Emit the one-time rollout probe (gateway response shape) only on the first attempt. */
|
|
31
|
+
private loggedAgentIdentityProbe;
|
|
32
|
+
/** Live heartbeat cadence (seconds), read from the OpenClaw cron store; cached 60s. */
|
|
33
|
+
private heartbeatSeconds?;
|
|
34
|
+
private heartbeatReadAtMs;
|
|
35
|
+
private loggedHeartbeat;
|
|
36
|
+
private lastTicker;
|
|
37
|
+
private positions;
|
|
38
|
+
private balance;
|
|
39
|
+
/** Today's realized PnL (filtered by closedAt) from plugin trade history. */
|
|
40
|
+
private realizedPnlToday;
|
|
41
|
+
/** Cumulative all-time realized PnL — used for the webapp KPI strip. */
|
|
42
|
+
private realizedPnlAllTime;
|
|
43
|
+
/** Paper session execution costs (since plugin boot), forwarded to the
|
|
44
|
+
* dashboard cost cells so "Fees"/"Avg Slip" reflect the drag already in NAV
|
|
45
|
+
* instead of reading $0 (M1). Null in live (LiveAdapter omits them). */
|
|
46
|
+
private paperSessionFees;
|
|
47
|
+
private paperAvgSlippageBps;
|
|
48
|
+
/** Mark-to-market equity from plugin (wallet + collateral + unrealized). */
|
|
49
|
+
private equity;
|
|
50
|
+
private openOrders;
|
|
51
|
+
private startTime;
|
|
52
|
+
/** Session start NAV — -1 means "not yet set", 0 is valid (fresh account). */
|
|
53
|
+
private sessionStartNav;
|
|
54
|
+
/** UTC date (YYYY-MM-DD) the current sessionStartNav anchor belongs to.
|
|
55
|
+
* Null until first anchored. Used to re-anchor Day P&L at UTC midnight —
|
|
56
|
+
* without it a long-running PAPER session kept yesterday's anchor and Day
|
|
57
|
+
* P&L / Realized accumulated across days. */
|
|
58
|
+
private sessionDate;
|
|
59
|
+
/** Track whether we've received at least one positions poll (needed for equity calculation). */
|
|
60
|
+
private hasReceivedPositions;
|
|
61
|
+
/** Track whether we've received at least one balance poll (needed for equity calculation). */
|
|
62
|
+
private hasReceivedBalance;
|
|
63
|
+
private readonly quoteCurrency;
|
|
64
|
+
private orderTimestamps;
|
|
65
|
+
private cancelTimestamps;
|
|
66
|
+
private agentStateInterval;
|
|
67
|
+
private toolRetryInterval;
|
|
68
|
+
private regimeInterval;
|
|
69
|
+
private signalInterval;
|
|
70
|
+
private missionInterval;
|
|
71
|
+
private analyticsInterval;
|
|
72
|
+
private decisionTraceInterval;
|
|
73
|
+
private pendingStartTimers;
|
|
74
|
+
private started;
|
|
75
|
+
private startedAt;
|
|
76
|
+
private lastSeenMissionIds;
|
|
77
|
+
private intelErrors;
|
|
78
|
+
private lastCryptoMetrics;
|
|
79
|
+
private lastVolumeAnalysis;
|
|
80
|
+
private atrData;
|
|
81
|
+
private atrSampleCount;
|
|
82
|
+
private currentRegime;
|
|
83
|
+
/** Timestamp of the most recent trade (fill event) — used in buildAgentState */
|
|
84
|
+
private lastTradeTs;
|
|
85
|
+
/** Tracks open position entries for computing trade results on close.
|
|
86
|
+
* Key: `symbol:side` (e.g. "BTC/USDT:long"), Value: { entryPrice, quantity, side, entryTime, missionId?, setupType?, regime?, confluenceScore? } */
|
|
87
|
+
private openTradeEntries;
|
|
88
|
+
/** Potential closes awaiting 2-poll confirmation. Binance's fetchPositions
|
|
89
|
+
* occasionally returns a held position as missing or zero-contract for a
|
|
90
|
+
* single poll; those transient drops were the root cause of ~350 phantom
|
|
91
|
+
* trade_results rows/day. We only treat a position as closed after it's
|
|
92
|
+
* been absent on TWO consecutive polls. Key: `symbol:side`. */
|
|
93
|
+
private pendingCloses;
|
|
94
|
+
/** Tracks whether the previous poll returned an empty / all-flat positions
|
|
95
|
+
* snapshot while we were holding non-zero positions. A single empty poll is
|
|
96
|
+
* treated as tentative (most often a Binance 429 / partial response). We
|
|
97
|
+
* skip the diff detector and do NOT overwrite this.positions until the
|
|
98
|
+
* next poll confirms — without this guard, the next non-empty poll re-emits
|
|
99
|
+
* synthetic OPEN fills for positions that were there all along (the
|
|
100
|
+
* open-side mirror of the close-side phantom that pendingCloses guards
|
|
101
|
+
* against). See Track 1 verification 2026-04-21 +
|
|
102
|
+
* docs/TRADE_AUDIT_TRAIL_PLAN.md Phase 0. */
|
|
103
|
+
private pendingEmptyPoll;
|
|
104
|
+
/** In-flight trade result reports — drained on stop() to prevent data loss during kill/flatten. */
|
|
105
|
+
private inFlightReports;
|
|
106
|
+
/** File-backed retry queue for failed trade reports */
|
|
107
|
+
private readonly retryQueuePath;
|
|
108
|
+
private retryTimer;
|
|
109
|
+
private tradingMode;
|
|
110
|
+
private readonly listeners;
|
|
111
|
+
constructor(config: GatewayConfig);
|
|
112
|
+
start(): void;
|
|
113
|
+
stop(): void;
|
|
114
|
+
/**
|
|
115
|
+
* Drain in-flight trade result reports. Call during shutdown to prevent
|
|
116
|
+
* data loss during kill/flatten sequences. Returns when all pending
|
|
117
|
+
* reports have completed (or timed out).
|
|
118
|
+
*/
|
|
119
|
+
drainReports(): Promise<void>;
|
|
120
|
+
private loadRetryQueue;
|
|
121
|
+
private saveRetryQueue;
|
|
122
|
+
/** Guards processRetryQueue against overlapping runs — the retry timer fires
|
|
123
|
+
* every 60s and startRetryTimer also kicks an immediate run, so a slow
|
|
124
|
+
* (100-item × 10s-timeout) drain could still be in flight when the next
|
|
125
|
+
* timer ticks. Two concurrent drains would double-POST every queued report
|
|
126
|
+
* AND race their reconcile-saves against each other. */
|
|
127
|
+
private retryInProgress;
|
|
128
|
+
/** Monotonic tag stamped on every queued report so processRetryQueue can
|
|
129
|
+
* remove EXACTLY the reports it delivered when it reconciles the file — a
|
|
130
|
+
* blind `save(remaining)` would clobber any report enqueued DURING the
|
|
131
|
+
* network phase (read-modify-write race with enqueueFailedReport). */
|
|
132
|
+
private nextRetryQid;
|
|
133
|
+
private enqueueFailedReport;
|
|
134
|
+
private processRetryQueue;
|
|
135
|
+
private startRetryTimer;
|
|
136
|
+
private stopRetryTimer;
|
|
137
|
+
/** Dual-fire dedup. Every critical command fires on BOTH WebSocket and
|
|
138
|
+
* HTTP simultaneously (CLAUDE.md: "Agent handles idempotency; first to
|
|
139
|
+
* arrive wins") — so each emergency action normally arrives here TWICE,
|
|
140
|
+
* ~0-200ms apart. Without dedup a second `flatten` re-fetches positions
|
|
141
|
+
* before the first close settles on-exchange and submits a second market
|
|
142
|
+
* close — a position-flip risk. Duplicates of the same action while one
|
|
143
|
+
* is in flight (or within a short post-completion window) share the
|
|
144
|
+
* first invocation's promise/result instead of executing again. */
|
|
145
|
+
private static readonly EMERGENCY_DEDUP_MS;
|
|
146
|
+
private emergencyInFlight;
|
|
147
|
+
executeEmergency(action: EmergencyAction): Promise<{
|
|
148
|
+
action: string;
|
|
149
|
+
executed: boolean;
|
|
150
|
+
timestamp: string;
|
|
151
|
+
details?: string;
|
|
152
|
+
}>;
|
|
153
|
+
private runEmergency;
|
|
154
|
+
private executeKill;
|
|
155
|
+
private executeFlatten;
|
|
156
|
+
private executePause;
|
|
157
|
+
private executeResume;
|
|
158
|
+
handleChat(content: string): Promise<{
|
|
159
|
+
received: boolean;
|
|
160
|
+
error?: string;
|
|
161
|
+
}>;
|
|
162
|
+
closePosition(symbol: string): Promise<{
|
|
163
|
+
closed: boolean;
|
|
164
|
+
symbol: string;
|
|
165
|
+
error?: string;
|
|
166
|
+
}>;
|
|
167
|
+
setTradingMode(mode: TradingMode, acknowledged?: boolean): Promise<SetTradingModeOutcome>;
|
|
168
|
+
setExchangeCredentials(apiKey: string, secret: string, testnet?: boolean): Promise<SetExchangeCredentialsOutcome>;
|
|
169
|
+
testExchangeCredentials(apiKey: string, secret: string, testnet?: boolean): Promise<TestExchangeCredentialsOutcome>;
|
|
170
|
+
clearExchangeCredentials(): Promise<ClearExchangeCredentialsOutcome>;
|
|
171
|
+
/** Operator-only. Read the current bracket-orders config from the plugin. */
|
|
172
|
+
getBracketConfig(): Promise<GetBracketConfigOutcome>;
|
|
173
|
+
/** Operator-only. Flip requireStopLoss or requireTakeProfit. */
|
|
174
|
+
setBracketRequirement(flag: BracketRequirementFlag, value: boolean): Promise<SetBracketRequirementOutcome>;
|
|
175
|
+
getSnapshot(): Promise<ReconciliationSnapshot>;
|
|
176
|
+
on<E extends ProviderEventName>(event: E, listener: ProviderEvents[E]): void;
|
|
177
|
+
off<E extends ProviderEventName>(event: E, listener: ProviderEvents[E]): void;
|
|
178
|
+
private initialize;
|
|
179
|
+
/**
|
|
180
|
+
* Wait for the WS handshake to complete and return the device token.
|
|
181
|
+
* Returns null if handshake fails or times out.
|
|
182
|
+
*/
|
|
183
|
+
private waitForDeviceToken;
|
|
184
|
+
/**
|
|
185
|
+
* Essential optional tools — retry loop keeps running until these are discovered.
|
|
186
|
+
* These are needed for position tracking, emergency controls, and trade display.
|
|
187
|
+
*/
|
|
188
|
+
private static readonly ESSENTIAL_OPTIONAL_TOOLS;
|
|
189
|
+
/** Check if all essential tools are discovered. */
|
|
190
|
+
private hasAllEssentialTools;
|
|
191
|
+
/** Retry tool discovery every 30s until all essential tools are found. */
|
|
192
|
+
private startToolRetry;
|
|
193
|
+
private createPoller;
|
|
194
|
+
private wireWsEvents;
|
|
195
|
+
private onWsConnected;
|
|
196
|
+
private onAgentEvent;
|
|
197
|
+
private onPollerTicker;
|
|
198
|
+
/** Strip the live-Binance settle suffix (`SOL/USDT:USDT` → `SOL/USDT`)
|
|
199
|
+
* to ONE canonical form, matching the webapp/plugin normalizeSymbol
|
|
200
|
+
* convention (chart/DB are unsuffixed). The plugin's getPositions
|
|
201
|
+
* returns the suffixed CCXT form via REST but the unsuffixed form via
|
|
202
|
+
* the WS store; under weight pressure isStoreTrusted() flickers between
|
|
203
|
+
* the two, so the raw symbol alternates poll-to-poll. */
|
|
204
|
+
private canonicalSymbol;
|
|
205
|
+
private onPollerPositions;
|
|
206
|
+
/**
|
|
207
|
+
* Compare previous and current positions from polling.
|
|
208
|
+
* When a position appears, changes size, or disappears,
|
|
209
|
+
* emit synthetic orderUpdate events so chart markers show trades.
|
|
210
|
+
*/
|
|
211
|
+
/** Apply a fresh mark price to a position (mutates in place). */
|
|
212
|
+
private applyMarkPrice;
|
|
213
|
+
private detectPositionChanges;
|
|
214
|
+
/**
|
|
215
|
+
* Emit a synthetic orderUpdate event derived from position polling data.
|
|
216
|
+
* Used when WS agent events are unavailable (gateway event filtering).
|
|
217
|
+
*/
|
|
218
|
+
private emitSyntheticFill;
|
|
219
|
+
private onPollerBalance;
|
|
220
|
+
/**
|
|
221
|
+
* Set session start NAV once both balance AND positions have been received.
|
|
222
|
+
* Checks persisted file first — if today's NAV was already captured in a
|
|
223
|
+
* previous skill run, reuse it so Day P&L is stable across restarts.
|
|
224
|
+
*/
|
|
225
|
+
private trySetSessionStartNav;
|
|
226
|
+
private onPollerOpenOrders;
|
|
227
|
+
private onPollerMarketStructure;
|
|
228
|
+
private onPollerCryptoMetrics;
|
|
229
|
+
private onPollerVolumeAnalysis;
|
|
230
|
+
/**
|
|
231
|
+
* Build and emit a trade journal entry whenever a synthetic fill is detected.
|
|
232
|
+
* Called from emitSyntheticFill — captures market + risk context at trade time.
|
|
233
|
+
*/
|
|
234
|
+
private emitTradeJournal;
|
|
235
|
+
/** Simple heat score (0–100) matching webapp's HeatGauge logic */
|
|
236
|
+
private computeHeatScoreSimple;
|
|
237
|
+
/** Cache last market structure for journal entries */
|
|
238
|
+
private lastMarketStructure;
|
|
239
|
+
private fetchFreshPositions;
|
|
240
|
+
private fetchFreshBalance;
|
|
241
|
+
private fetchFreshOpenOrders;
|
|
242
|
+
private fetchAgentHealth;
|
|
243
|
+
/**
|
|
244
|
+
* Resolve the agent's display name + live model id directly from the gateway.
|
|
245
|
+
* The primary carrier is the `sessions.list` RPC (rows carry agentId + the
|
|
246
|
+
* live model); `health` (above) is a secondary feed. Best-effort and
|
|
247
|
+
* fail-open — the gateway/version may not expose either, in which case the
|
|
248
|
+
* header simply falls back to its placeholder. Re-emits agent_state on a hit
|
|
249
|
+
* so the dashboard updates without waiting for the next periodic tick.
|
|
250
|
+
*/
|
|
251
|
+
private refreshAgentIdentity;
|
|
252
|
+
/** Merge newly-resolved identity fields into the cache. Returns true if anything changed. */
|
|
253
|
+
private mergeAgentIdentity;
|
|
254
|
+
/**
|
|
255
|
+
* Extract the live model id from a `sessions.list` (or `health`) gateway
|
|
256
|
+
* payload. We deliberately do NOT take the agent NAME from here: sessions
|
|
257
|
+
* rows are per-chat and their `name` is a channel/chat id (e.g. a Telegram
|
|
258
|
+
* id), not the agent's identity — the display name comes from
|
|
259
|
+
* resolveAgentNameFromIdentity(). The exact shape isn't contractually stable,
|
|
260
|
+
* so probe the documented paths (row → runtime → defaults → root) and accept
|
|
261
|
+
* either a bare string or a `{ primary }` object.
|
|
262
|
+
*/
|
|
263
|
+
private extractAgentModel;
|
|
264
|
+
/**
|
|
265
|
+
* Resolve the agent's display name from the OpenClaw workspace IDENTITY.md
|
|
266
|
+
* (`- **Name:** <X>`), local to this deployment (per-tenant correct — each
|
|
267
|
+
* trader's own agent has its own IDENTITY.md). Best-effort + fail-open:
|
|
268
|
+
* returns undefined when the file is missing/unreadable, the field is the
|
|
269
|
+
* unfilled placeholder, or the value looks like an id rather than a name.
|
|
270
|
+
*/
|
|
271
|
+
private resolveAgentNameFromIdentity;
|
|
272
|
+
/**
|
|
273
|
+
* Live heartbeat cadence in seconds — the agent's TRUE beat interval, read
|
|
274
|
+
* from the OpenClaw cron store (`~/.openclaw/cron/jobs.json` → the enabled
|
|
275
|
+
* heartbeat job's `schedule.everyMs`). This is the source of truth: the old
|
|
276
|
+
* hardcoded 1800 and `agents.defaults.heartbeat` are both stale/wrong once
|
|
277
|
+
* the operator edits the cron (`openclaw cron ... --every 15m`). Drives both
|
|
278
|
+
* the "beat every Nm" label and the unresponsive (2×) threshold. Cached 60s;
|
|
279
|
+
* returns undefined (caller defaults to 1800) when unreadable.
|
|
280
|
+
*/
|
|
281
|
+
private resolveHeartbeatSeconds;
|
|
282
|
+
/** Build agent state from internal fields (used by both emitAgentState and getSnapshot). */
|
|
283
|
+
private buildAgentState;
|
|
284
|
+
private emitAgentState;
|
|
285
|
+
private emitRiskUpdate;
|
|
286
|
+
/** Compute risk metrics — delegates to pure functions in risk-calculator.ts */
|
|
287
|
+
private computeRiskMetrics;
|
|
288
|
+
/** Compute total equity — delegates to pure function in risk-calculator.ts */
|
|
289
|
+
private computeEquity;
|
|
290
|
+
/** Compute signed notional — delegates to pure function in risk-calculator.ts */
|
|
291
|
+
private computePositionNotional;
|
|
292
|
+
/**
|
|
293
|
+
* Report a completed trade to the Intelligence API for persistent cross-session analytics.
|
|
294
|
+
* Called when a position closes (detected via position polling diffs).
|
|
295
|
+
* Handles partial closes: updates remaining quantity in openTradeEntries instead of deleting.
|
|
296
|
+
*
|
|
297
|
+
* @param key Composite key `symbol:side` into openTradeEntries
|
|
298
|
+
* @param symbol CCXT symbol (e.g. "BTC/USDT")
|
|
299
|
+
* @param exitPrice Fill price for the close
|
|
300
|
+
* @param closedQty Quantity actually closed (may be less than full position for partial closes)
|
|
301
|
+
*/
|
|
302
|
+
private reportTradeResult;
|
|
303
|
+
/**
|
|
304
|
+
* Seed lastTradeTs from Intelligence API on startup. One-shot, best-effort.
|
|
305
|
+
* Prevents "Last Trade: Never" after every restart when the agent is flat.
|
|
306
|
+
*/
|
|
307
|
+
private seedLastTradeTs;
|
|
308
|
+
/** Symbol formatted for intelligence API (BTC/USDT → BTCUSDT). */
|
|
309
|
+
private get intelligenceSymbol();
|
|
310
|
+
/** Shared intelligence poller with circuit-breaker (exponential backoff on errors). */
|
|
311
|
+
private startIntelligencePoller;
|
|
312
|
+
/** Log first error per poller, suppress subsequent repeats. */
|
|
313
|
+
private trackIntelError;
|
|
314
|
+
private startRegimePoller;
|
|
315
|
+
private startSignalPoller;
|
|
316
|
+
private startMissionPoller;
|
|
317
|
+
private startAnalyticsPoller;
|
|
318
|
+
private startDecisionTracePoller;
|
|
319
|
+
private static readonly STRATEGY_LABELS;
|
|
320
|
+
private presentMissionToAgent;
|
|
321
|
+
private fire;
|
|
322
|
+
}
|